tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsDSURIContentListener.cpp (10183B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsDocShell.h"
      8 #include "nsDSURIContentListener.h"
      9 #include "nsIChannel.h"
     10 #include "nsServiceManagerUtils.h"
     11 #include "nsDocShellCID.h"
     12 #include "nsIWebNavigationInfo.h"
     13 #include "mozilla/dom/CanonicalBrowsingContext.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/WindowGlobalParent.h"
     16 #include "nsError.h"
     17 #include "nsContentSecurityManager.h"
     18 #include "nsDocShellLoadTypes.h"
     19 #include "nsIInterfaceRequestor.h"
     20 #include "nsIMultiPartChannel.h"
     21 #include "nsWebNavigationInfo.h"
     22 
     23 using namespace mozilla;
     24 using namespace mozilla::dom;
     25 
     26 NS_IMPL_ADDREF(MaybeCloseWindowHelper)
     27 NS_IMPL_RELEASE(MaybeCloseWindowHelper)
     28 
     29 NS_INTERFACE_MAP_BEGIN(MaybeCloseWindowHelper)
     30  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
     31  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
     32  NS_INTERFACE_MAP_ENTRY(nsINamed)
     33 NS_INTERFACE_MAP_END
     34 
     35 MaybeCloseWindowHelper::MaybeCloseWindowHelper(BrowsingContext* aContentContext)
     36    : mBrowsingContext(aContentContext),
     37      mTimer(nullptr),
     38      mShouldCloseWindow(false) {}
     39 
     40 MaybeCloseWindowHelper::~MaybeCloseWindowHelper() {}
     41 
     42 void MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow) {
     43  mShouldCloseWindow = aShouldCloseWindow;
     44 }
     45 
     46 BrowsingContext* MaybeCloseWindowHelper::MaybeCloseWindow() {
     47  if (!mShouldCloseWindow) {
     48    return mBrowsingContext;
     49  }
     50 
     51  // This method should not be called more than once, but it's better to avoid
     52  // closing the current window again.
     53  mShouldCloseWindow = false;
     54 
     55  // Reset the window context to the opener window so that the dependent
     56  // dialogs have a parent
     57  RefPtr<BrowsingContext> newBC = ChooseNewBrowsingContext(mBrowsingContext);
     58 
     59  if (newBC != mBrowsingContext && newBC && !newBC->IsDiscarded()) {
     60    mBCToClose = mBrowsingContext;
     61    mBrowsingContext = newBC;
     62 
     63    // Now close the old window.  Do it on a timer so that we don't run
     64    // into issues trying to close the window before it has fully opened.
     65    NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
     66    NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 0,
     67                            nsITimer::TYPE_ONE_SHOT);
     68  }
     69 
     70  return mBrowsingContext;
     71 }
     72 
     73 already_AddRefed<BrowsingContext>
     74 MaybeCloseWindowHelper::ChooseNewBrowsingContext(BrowsingContext* aBC) {
     75  RefPtr<BrowsingContext> opener = aBC->GetOpener();
     76  if (opener && !opener->IsDiscarded()) {
     77    return opener.forget();
     78  }
     79 
     80  if (!XRE_IsParentProcess()) {
     81    return nullptr;
     82  }
     83 
     84  opener = BrowsingContext::Get(aBC->Canonical()->GetCrossGroupOpenerId());
     85  if (!opener || opener->IsDiscarded()) {
     86    return nullptr;
     87  }
     88  return opener.forget();
     89 }
     90 
     91 NS_IMETHODIMP
     92 MaybeCloseWindowHelper::Notify(nsITimer* timer) {
     93  NS_ASSERTION(mBCToClose, "No window to close after timer fired");
     94 
     95  mBCToClose->Close(CallerType::System, IgnoreErrors());
     96  mBCToClose = nullptr;
     97  mTimer = nullptr;
     98 
     99  return NS_OK;
    100 }
    101 
    102 NS_IMETHODIMP
    103 MaybeCloseWindowHelper::GetName(nsACString& aName) {
    104  aName.AssignLiteral("MaybeCloseWindowHelper");
    105  return NS_OK;
    106 }
    107 
    108 nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
    109    : mDocShell(aDocShell),
    110      mExistingJPEGRequest(nullptr),
    111      mParentContentListener(nullptr) {}
    112 
    113 nsDSURIContentListener::~nsDSURIContentListener() {}
    114 
    115 NS_IMPL_ADDREF(nsDSURIContentListener)
    116 NS_IMPL_RELEASE(nsDSURIContentListener)
    117 
    118 NS_INTERFACE_MAP_BEGIN(nsDSURIContentListener)
    119  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIContentListener)
    120  NS_INTERFACE_MAP_ENTRY(nsIURIContentListener)
    121  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    122 NS_INTERFACE_MAP_END
    123 
    124 NS_IMETHODIMP
    125 nsDSURIContentListener::DoContent(const nsACString& aContentType,
    126                                  bool aIsContentPreferred,
    127                                  nsIRequest* aRequest,
    128                                  nsIStreamListener** aContentHandler,
    129                                  bool* aAbortProcess) {
    130  nsresult rv;
    131  NS_ENSURE_ARG_POINTER(aContentHandler);
    132  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
    133  RefPtr<nsDocShell> docShell = mDocShell.get();
    134 
    135  *aAbortProcess = false;
    136 
    137  // determine if the channel has just been retargeted to us...
    138  nsLoadFlags loadFlags = 0;
    139  if (nsCOMPtr<nsIChannel> openedChannel = do_QueryInterface(aRequest)) {
    140    openedChannel->GetLoadFlags(&loadFlags);
    141  }
    142 
    143  if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
    144    // XXX: Why does this not stop the content too?
    145    docShell->Stop(nsIWebNavigation::STOP_NETWORK);
    146    NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
    147    docShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL);
    148  }
    149 
    150  // In case of multipart jpeg request (mjpeg) we don't really want to
    151  // create new viewer since the one we already have is capable of
    152  // rendering multipart jpeg correctly (see bug 625012)
    153  nsCOMPtr<nsIChannel> baseChannel;
    154  if (nsCOMPtr<nsIMultiPartChannel> mpchan = do_QueryInterface(aRequest)) {
    155    mpchan->GetBaseChannel(getter_AddRefs(baseChannel));
    156  }
    157 
    158  bool reuseCV = baseChannel && baseChannel == mExistingJPEGRequest &&
    159                 aContentType.EqualsLiteral("image/jpeg");
    160 
    161  if (mExistingJPEGStreamListener && reuseCV) {
    162    RefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener);
    163    copy.forget(aContentHandler);
    164    rv = NS_OK;
    165  } else {
    166    rv =
    167        docShell->CreateDocumentViewer(aContentType, aRequest, aContentHandler);
    168    if (NS_SUCCEEDED(rv) && reuseCV) {
    169      mExistingJPEGStreamListener = *aContentHandler;
    170    } else {
    171      mExistingJPEGStreamListener = nullptr;
    172    }
    173    mExistingJPEGRequest = baseChannel;
    174  }
    175 
    176  if (rv == NS_ERROR_DOCSHELL_DYING) {
    177    aRequest->Cancel(rv);
    178    *aAbortProcess = true;
    179    return NS_OK;
    180  }
    181 
    182  if (NS_FAILED(rv)) {
    183    // we don't know how to handle the content
    184    nsCOMPtr<nsIStreamListener> forget = dont_AddRef(*aContentHandler);
    185    *aContentHandler = nullptr;
    186    return rv;
    187  }
    188 
    189  if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
    190    nsCOMPtr<nsPIDOMWindowOuter> domWindow =
    191        mDocShell ? mDocShell->GetWindow() : nullptr;
    192    NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
    193    domWindow->Focus(mozilla::dom::CallerType::System);
    194  }
    195 
    196  return NS_OK;
    197 }
    198 
    199 NS_IMETHODIMP
    200 nsDSURIContentListener::IsPreferred(const char* aContentType,
    201                                    char** aDesiredContentType,
    202                                    bool* aCanHandle) {
    203  NS_ENSURE_ARG_POINTER(aCanHandle);
    204  NS_ENSURE_ARG_POINTER(aDesiredContentType);
    205 
    206  // the docshell has no idea if it is the preferred content provider or not.
    207  // It needs to ask its parent if it is the preferred content handler or not...
    208 
    209  nsCOMPtr<nsIURIContentListener> parentListener;
    210  GetParentContentListener(getter_AddRefs(parentListener));
    211  if (parentListener) {
    212    return parentListener->IsPreferred(aContentType, aDesiredContentType,
    213                                       aCanHandle);
    214  }
    215  // we used to return false here if we didn't have a parent properly registered
    216  // at the top of the docshell hierarchy to dictate what content types this
    217  // docshell should be a preferred handler for. But this really makes it hard
    218  // for developers using iframe or browser tags because then they need to make
    219  // sure they implement nsIURIContentListener otherwise all link clicks would
    220  // get sent to another window because we said we weren't the preferred handler
    221  // type. I'm going to change the default now... if we can handle the content,
    222  // and someone didn't EXPLICITLY set a nsIURIContentListener at the top of our
    223  // docshell chain, then we'll now always attempt to process the content
    224  // ourselves...
    225  return CanHandleContent(aContentType, true, aDesiredContentType, aCanHandle);
    226 }
    227 
    228 NS_IMETHODIMP
    229 nsDSURIContentListener::CanHandleContent(const char* aContentType,
    230                                         bool aIsContentPreferred,
    231                                         char** aDesiredContentType,
    232                                         bool* aCanHandleContent) {
    233  MOZ_ASSERT(aCanHandleContent, "Null out param?");
    234  NS_ENSURE_ARG_POINTER(aDesiredContentType);
    235 
    236  *aCanHandleContent = false;
    237  *aDesiredContentType = nullptr;
    238 
    239  if (aContentType) {
    240    uint32_t canHandle =
    241        nsWebNavigationInfo::IsTypeSupported(nsDependentCString(aContentType));
    242    *aCanHandleContent = (canHandle != nsIWebNavigationInfo::UNSUPPORTED);
    243  }
    244 
    245  return NS_OK;
    246 }
    247 
    248 NS_IMETHODIMP
    249 nsDSURIContentListener::GetLoadCookie(nsISupports** aLoadCookie) {
    250  NS_IF_ADDREF(*aLoadCookie = nsDocShell::GetAsSupports(mDocShell));
    251  return NS_OK;
    252 }
    253 
    254 NS_IMETHODIMP
    255 nsDSURIContentListener::SetLoadCookie(nsISupports* aLoadCookie) {
    256 #ifdef DEBUG
    257  RefPtr<nsDocLoader> cookieAsDocLoader =
    258      nsDocLoader::GetAsDocLoader(aLoadCookie);
    259  NS_ASSERTION(cookieAsDocLoader && cookieAsDocLoader == mDocShell,
    260               "Invalid load cookie being set!");
    261 #endif
    262  return NS_OK;
    263 }
    264 
    265 NS_IMETHODIMP
    266 nsDSURIContentListener::GetParentContentListener(
    267    nsIURIContentListener** aParentListener) {
    268  if (mWeakParentContentListener) {
    269    nsCOMPtr<nsIURIContentListener> tempListener =
    270        do_QueryReferent(mWeakParentContentListener);
    271    *aParentListener = tempListener;
    272    NS_IF_ADDREF(*aParentListener);
    273  } else {
    274    *aParentListener = mParentContentListener;
    275    NS_IF_ADDREF(*aParentListener);
    276  }
    277  return NS_OK;
    278 }
    279 
    280 NS_IMETHODIMP
    281 nsDSURIContentListener::SetParentContentListener(
    282    nsIURIContentListener* aParentListener) {
    283  if (aParentListener) {
    284    // Store the parent listener as a weak ref. Parents not supporting
    285    // nsISupportsWeakReference assert but may still be used.
    286    mParentContentListener = nullptr;
    287    mWeakParentContentListener = do_GetWeakReference(aParentListener);
    288    if (!mWeakParentContentListener) {
    289      mParentContentListener = aParentListener;
    290    }
    291  } else {
    292    mWeakParentContentListener = nullptr;
    293    mParentContentListener = nullptr;
    294  }
    295  return NS_OK;
    296 }