tor-browser

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

nsObjectLoadingContent.cpp (66787B)


      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 * A base class implementing nsIObjectLoadingContent for use by
      8 * various content nodes that want to provide plugin/document/image
      9 * loading functionality (eg <embed>, <object>, etc).
     10 */
     11 
     12 // Interface headers
     13 #include "imgLoader.h"
     14 #include "mozilla/BasePrincipal.h"
     15 #include "mozilla/dom/BindContext.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "nsError.h"
     18 #include "nsIAppShell.h"
     19 #include "nsIAsyncVerifyRedirectCallback.h"
     20 #include "nsIClassOfService.h"
     21 #include "nsIConsoleService.h"
     22 #include "nsIDocShell.h"
     23 #include "nsIExternalProtocolHandler.h"
     24 #include "nsIHttpChannel.h"
     25 #include "nsINestedURI.h"
     26 #include "nsIPermissionManager.h"
     27 #include "nsIScriptChannel.h"
     28 #include "nsIScriptError.h"
     29 #include "nsIURILoader.h"
     30 #include "nsScriptSecurityManager.h"
     31 #include "nsSubDocumentFrame.h"
     32 
     33 // Util headers
     34 #include "mozilla/Logging.h"
     35 #include "mozilla/Preferences.h"
     36 #include "nsContentPolicyUtils.h"
     37 #include "nsContentUtils.h"
     38 #include "nsDocShellLoadState.h"
     39 #include "nsGkAtoms.h"
     40 #include "nsMimeTypes.h"
     41 #include "nsNetUtil.h"
     42 #include "nsQueryObject.h"
     43 #include "nsStyleUtil.h"
     44 #include "nsThreadUtils.h"
     45 
     46 // Concrete classes
     47 #include "ReferrerInfo.h"
     48 #include "mozilla/AsyncEventDispatcher.h"
     49 #include "mozilla/BasicEvents.h"
     50 #include "mozilla/Components.h"
     51 #include "mozilla/EventDispatcher.h"
     52 #include "mozilla/IMEStateManager.h"
     53 #include "mozilla/LoadInfo.h"
     54 #include "mozilla/PresShell.h"
     55 #include "mozilla/ProfilerLabels.h"
     56 #include "mozilla/StaticPrefs_browser.h"
     57 #include "mozilla/StaticPrefs_dom.h"
     58 #include "mozilla/dom/BindingUtils.h"
     59 #include "mozilla/dom/ContentChild.h"
     60 #include "mozilla/dom/Element.h"
     61 #include "mozilla/dom/Event.h"
     62 #include "mozilla/dom/HTMLEmbedElement.h"
     63 #include "mozilla/dom/HTMLObjectElement.h"
     64 #include "mozilla/dom/HTMLObjectElementBinding.h"
     65 #include "mozilla/dom/PolicyContainer.h"
     66 #include "mozilla/dom/ScriptSettings.h"
     67 #include "mozilla/dom/UserActivation.h"
     68 #include "mozilla/dom/nsCSPContext.h"
     69 #include "mozilla/net/DocumentChannel.h"
     70 #include "mozilla/net/UrlClassifierFeatureFactory.h"
     71 #include "mozilla/widget/IMEData.h"
     72 #include "nsChannelClassifier.h"
     73 #include "nsFocusManager.h"
     74 #include "nsFrameLoader.h"
     75 #include "nsIEffectiveTLDService.h"
     76 #include "nsObjectLoadingContent.h"
     77 #include "nsWidgetsCID.h"
     78 
     79 #ifdef XP_WIN
     80 // Thanks so much, Microsoft! :(
     81 #  ifdef CreateEvent
     82 #    undef CreateEvent
     83 #  endif
     84 #endif  // XP_WIN
     85 
     86 static const char kPrefYoutubeRewrite[] = "plugins.rewrite_youtube_embeds";
     87 
     88 using namespace mozilla;
     89 using namespace mozilla::dom;
     90 using namespace mozilla::net;
     91 
     92 static LogModule* GetObjectLog() {
     93  static LazyLogModule sLog("objlc");
     94  return sLog;
     95 }
     96 
     97 #define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
     98 #define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
     99 
    100 static bool IsFlashMIME(const nsACString& aMIMEType) {
    101  return aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
    102         aMIMEType.LowerCaseEqualsASCII("application/futuresplash") ||
    103         aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test");
    104 }
    105 
    106 static bool IsPluginMIME(const nsACString& aMIMEType) {
    107  return IsFlashMIME(aMIMEType) ||
    108         aMIMEType.LowerCaseEqualsASCII("application/x-test");
    109 }
    110 
    111 ///
    112 /// Runnables and helper classes
    113 ///
    114 
    115 // Sets a object's mIsLoading bit to false when destroyed
    116 class AutoSetLoadingToFalse {
    117 public:
    118  explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
    119      : mContent(aContent) {}
    120  ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
    121 
    122 private:
    123  nsObjectLoadingContent* mContent;
    124 };
    125 
    126 ///
    127 /// Helper functions
    128 ///
    129 
    130 bool nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest,
    131                                                 nsresult* aStatus) {
    132  nsresult rv = aRequest->GetStatus(aStatus);
    133  if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
    134    return false;
    135  }
    136 
    137  // This may still be an error page or somesuch
    138  nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
    139  if (httpChan) {
    140    bool success;
    141    rv = httpChan->GetRequestSucceeded(&success);
    142    if (NS_FAILED(rv) || !success) {
    143      return false;
    144    }
    145  }
    146 
    147  // Otherwise, the request is successful
    148  return true;
    149 }
    150 
    151 static bool CanHandleURI(nsIURI* aURI) {
    152  nsAutoCString scheme;
    153  if (NS_FAILED(aURI->GetScheme(scheme))) {
    154    return false;
    155  }
    156 
    157  nsCOMPtr<nsIIOService> ios = mozilla::components::IO::Service();
    158  if (!ios) {
    159    return false;
    160  }
    161 
    162  nsCOMPtr<nsIProtocolHandler> handler;
    163  ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
    164  if (!handler) {
    165    return false;
    166  }
    167 
    168  nsCOMPtr<nsIExternalProtocolHandler> extHandler = do_QueryInterface(handler);
    169  // We can handle this URI if its protocol handler is not the external one
    170  return extHandler == nullptr;
    171 }
    172 
    173 // Helper for tedious URI equality syntax when one or both arguments may be
    174 // null and URIEquals(null, null) should be true
    175 static bool inline URIEquals(nsIURI* a, nsIURI* b) {
    176  bool equal;
    177  return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
    178 }
    179 
    180 ///
    181 /// Member Functions
    182 ///
    183 
    184 // Helper to spawn the frameloader.
    185 void nsObjectLoadingContent::SetupFrameLoader() {
    186  mFrameLoader = nsFrameLoader::Create(AsElement(), mNetworkCreated);
    187  MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed");
    188 }
    189 
    190 // Helper to spawn the frameloader and return a pointer to its docshell.
    191 already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell(
    192    nsIURI* aRecursionCheckURI) {
    193  SetupFrameLoader();
    194  if (!mFrameLoader) {
    195    return nullptr;
    196  }
    197 
    198  nsCOMPtr<nsIDocShell> docShell;
    199 
    200  if (aRecursionCheckURI) {
    201    nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
    202    if (NS_SUCCEEDED(rv)) {
    203      IgnoredErrorResult result;
    204      docShell = mFrameLoader->GetDocShell(result);
    205      if (result.Failed()) {
    206        MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
    207      }
    208    } else {
    209      LOG(("OBJLC [%p]: Aborting recursive load", this));
    210    }
    211  }
    212 
    213  if (!docShell) {
    214    mFrameLoader->Destroy();
    215    mFrameLoader = nullptr;
    216    return nullptr;
    217  }
    218 
    219  return docShell.forget();
    220 }
    221 
    222 void nsObjectLoadingContent::UnbindFromTree() {
    223  // Reset state and clear pending events
    224  /// XXX(johns): The implementation for GenericFrame notes that ideally we
    225  ///             would keep the docshell around, but trash the frameloader
    226  UnloadObject();
    227 }
    228 
    229 nsObjectLoadingContent::nsObjectLoadingContent()
    230    : mType(ObjectType::Loading),
    231      mChannelLoaded(false),
    232      mNetworkCreated(true),
    233      mContentBlockingEnabled(false),
    234      mIsStopping(false),
    235      mIsLoading(false),
    236      mScriptRequested(false),
    237      mRewrittenYoutubeEmbed(false) {}
    238 
    239 nsObjectLoadingContent::~nsObjectLoadingContent() {
    240  // Should have been unbound from the tree at this point, and
    241  // CheckPluginStopEvent keeps us alive
    242  if (mFrameLoader) {
    243    MOZ_ASSERT_UNREACHABLE(
    244        "Should not be tearing down frame loaders at this point");
    245    mFrameLoader->Destroy();
    246  }
    247 }
    248 
    249 // nsIRequestObserver
    250 NS_IMETHODIMP
    251 nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) {
    252  AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
    253 
    254  LOG(("OBJLC [%p]: Channel OnStartRequest", this));
    255 
    256  if (aRequest != mChannel || !aRequest) {
    257    // happens when a new load starts before the previous one got here
    258    return NS_BINDING_ABORTED;
    259  }
    260 
    261  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
    262  NS_ASSERTION(chan, "Why is our request not a channel?");
    263 
    264  nsresult status = NS_OK;
    265  bool success = IsSuccessfulRequest(aRequest, &status);
    266 
    267  // If we have already switched to type document, we're doing a
    268  // process-switching DocumentChannel load. We should be able to pass down the
    269  // load to our inner listener, but should also make sure to update our local
    270  // state.
    271  if (mType == ObjectType::Document) {
    272    if (!mFinalListener) {
    273      MOZ_ASSERT_UNREACHABLE(
    274          "Already is Document, but don't have final listener yet?");
    275      return NS_BINDING_ABORTED;
    276    }
    277 
    278    // If the load looks successful, fix up some of our local state before
    279    // forwarding the request to the final URI loader.
    280    //
    281    // Forward load errors down to the document loader, so we don't tear down
    282    // the nsDocShell ourselves.
    283    if (success) {
    284      LOG(("OBJLC [%p]: OnStartRequest: DocumentChannel request succeeded\n",
    285           this));
    286      nsCString channelType;
    287      MOZ_ALWAYS_SUCCEEDS(mChannel->GetContentType(channelType));
    288 
    289      if (GetTypeOfContent(channelType) != ObjectType::Document) {
    290        MOZ_CRASH("DocumentChannel request with non-document MIME");
    291      }
    292      mContentType = channelType;
    293 
    294      MOZ_ALWAYS_SUCCEEDS(
    295          NS_GetFinalChannelURI(mChannel, getter_AddRefs(mURI)));
    296    }
    297 
    298    return mFinalListener->OnStartRequest(aRequest);
    299  }
    300 
    301  // Otherwise we should be state loading, and call LoadObject with the channel
    302  if (mType != ObjectType::Loading) {
    303    MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
    304    return NS_BINDING_ABORTED;
    305  }
    306  NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
    307  NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
    308 
    309  mChannelLoaded = true;
    310 
    311  if (status == NS_ERROR_BLOCKED_URI) {
    312    nsCOMPtr<nsIConsoleService> console(
    313        do_GetService("@mozilla.org/consoleservice;1"));
    314    if (console) {
    315      nsCOMPtr<nsIURI> uri;
    316      chan->GetURI(getter_AddRefs(uri));
    317      nsString message =
    318          u"Blocking "_ns +
    319          NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
    320          nsLiteralString(
    321              u" since it was found on an internal Firefox blocklist.");
    322      console->LogStringMessage(message.get());
    323    }
    324    mContentBlockingEnabled = true;
    325    return NS_ERROR_FAILURE;
    326  }
    327 
    328  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
    329    mContentBlockingEnabled = true;
    330    return NS_ERROR_FAILURE;
    331  }
    332 
    333  if (!success) {
    334    LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
    335    // If the request fails, we still call LoadObject() to handle fallback
    336    // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
    337    // the bad state.
    338    mChannel = nullptr;
    339    LoadObject(true, false);
    340    return NS_ERROR_FAILURE;
    341  }
    342 
    343  return LoadObject(true, false, aRequest);
    344 }
    345 
    346 NS_IMETHODIMP
    347 nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
    348                                      nsresult aStatusCode) {
    349  AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
    350 
    351  // Handle object not loading error because source was a tracking URL (or
    352  // fingerprinting, cryptomining, etc.).
    353  // We make a note of this object node by including it in a dedicated
    354  // array of blocked tracking nodes under its parent document.
    355  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
    356    nsCOMPtr<nsIContent> thisNode =
    357        do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
    358    if (thisNode && thisNode->IsInComposedDoc()) {
    359      thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
    360    }
    361  }
    362 
    363  if (aRequest != mChannel) {
    364    return NS_BINDING_ABORTED;
    365  }
    366 
    367  mChannel = nullptr;
    368 
    369  if (mFinalListener) {
    370    // This may re-enter in the case of plugin listeners
    371    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
    372    mFinalListener = nullptr;
    373    listenerGrip->OnStopRequest(aRequest, aStatusCode);
    374  }
    375 
    376  // Return value doesn't matter
    377  return NS_OK;
    378 }
    379 
    380 // nsIStreamListener
    381 NS_IMETHODIMP
    382 nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest,
    383                                        nsIInputStream* aInputStream,
    384                                        uint64_t aOffset, uint32_t aCount) {
    385  if (aRequest != mChannel) {
    386    return NS_BINDING_ABORTED;
    387  }
    388 
    389  if (mFinalListener) {
    390    // This may re-enter in the case of plugin listeners
    391    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
    392    return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset,
    393                                         aCount);
    394  }
    395 
    396  // We shouldn't have a connected channel with no final listener
    397  MOZ_ASSERT_UNREACHABLE(
    398      "Got data for channel with no connected final "
    399      "listener");
    400  mChannel = nullptr;
    401 
    402  return NS_ERROR_UNEXPECTED;
    403 }
    404 
    405 NS_IMETHODIMP
    406 nsObjectLoadingContent::GetActualType(nsACString& aType) {
    407  aType = mContentType;
    408  return NS_OK;
    409 }
    410 
    411 NS_IMETHODIMP
    412 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
    413  *aType = DisplayedType();
    414  return NS_OK;
    415 }
    416 
    417 // nsIInterfaceRequestor
    418 // We use a shim class to implement this so that JS consumers still
    419 // see an interface requestor even though WebIDL bindings don't expose
    420 // that stuff.
    421 class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
    422                                           public nsIChannelEventSink,
    423                                           public nsIStreamListener {
    424 public:
    425  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    426  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
    427                                           nsIInterfaceRequestor)
    428  NS_DECL_NSIINTERFACEREQUESTOR
    429  // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
    430  // hence the ugly static cast :(
    431  NS_FORWARD_NSICHANNELEVENTSINK(
    432      static_cast<nsObjectLoadingContent*>(mContent.get())->)
    433  NS_FORWARD_NSISTREAMLISTENER(
    434      static_cast<nsObjectLoadingContent*>(mContent.get())->)
    435  NS_FORWARD_NSIREQUESTOBSERVER(
    436      static_cast<nsObjectLoadingContent*>(mContent.get())->)
    437 
    438  explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
    439      : mContent(aContent) {}
    440 
    441 protected:
    442  ~ObjectInterfaceRequestorShim() = default;
    443  nsCOMPtr<nsIObjectLoadingContent> mContent;
    444 };
    445 
    446 NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
    447 
    448 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
    449  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    450  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
    451  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    452  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    453  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
    454 NS_INTERFACE_MAP_END
    455 
    456 NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
    457 NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
    458 
    459 NS_IMETHODIMP
    460 ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
    461  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
    462    nsIChannelEventSink* sink = this;
    463    *aResult = sink;
    464    NS_ADDREF(sink);
    465    return NS_OK;
    466  }
    467  if (aIID.Equals(NS_GET_IID(nsIObjectLoadingContent))) {
    468    nsIObjectLoadingContent* olc = mContent;
    469    *aResult = olc;
    470    NS_ADDREF(olc);
    471    return NS_OK;
    472  }
    473  return NS_NOINTERFACE;
    474 }
    475 
    476 // nsIChannelEventSink
    477 NS_IMETHODIMP
    478 nsObjectLoadingContent::AsyncOnChannelRedirect(
    479    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
    480    nsIAsyncVerifyRedirectCallback* cb) {
    481  // If we're already busy with a new load, or have no load at all,
    482  // cancel the redirect.
    483  if (!mChannel || aOldChannel != mChannel) {
    484    return NS_BINDING_ABORTED;
    485  }
    486 
    487  mChannel = aNewChannel;
    488 
    489  if (mFinalListener) {
    490    nsCOMPtr<nsIChannelEventSink> sink(do_QueryInterface(mFinalListener));
    491    MOZ_RELEASE_ASSERT(sink, "mFinalListener isn't nsIChannelEventSink?");
    492    if (mType != ObjectType::Document) {
    493      MOZ_ASSERT_UNREACHABLE(
    494          "Not a DocumentChannel load, but we're getting a "
    495          "AsyncOnChannelRedirect with a mFinalListener?");
    496      return NS_BINDING_ABORTED;
    497    }
    498 
    499    return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
    500  }
    501 
    502  cb->OnRedirectVerifyCallback(NS_OK);
    503  return NS_OK;
    504 }
    505 
    506 void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
    507                                                      nsIURI* aBaseURI,
    508                                                      nsIURI** aRewrittenURI) {
    509  nsCOMPtr<nsIEffectiveTLDService> tldService =
    510      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
    511  // If we can't analyze the URL, just pass on through.
    512  if (!tldService) {
    513    NS_WARNING("Could not get TLD service!");
    514    return;
    515  }
    516 
    517  nsAutoCString currentBaseDomain;
    518  bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
    519  if (!ok) {
    520    // Data URIs (commonly used for things like svg embeds) won't parse
    521    // correctly, so just fail silently here.
    522    return;
    523  }
    524 
    525  // See if URL is referencing youtube
    526  if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
    527      !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
    528    return;
    529  }
    530 
    531  // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
    532  // touch object nodes with "/embed/" urls that already do that right thing.
    533  nsAutoCString path;
    534  aURI->GetPathQueryRef(path);
    535  if (!StringBeginsWith(path, "/v/"_ns)) {
    536    return;
    537  }
    538 
    539  // See if requester is planning on using the JS API.
    540  nsAutoCString prePath;
    541  nsresult rv = aURI->GetPrePath(prePath);
    542  if (NS_FAILED(rv)) {
    543    return;
    544  }
    545 
    546  // Some YouTube urls have parameters in path components, e.g.
    547  // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
    548  // but break iframe/object embedding. If this situation occurs with rewritten
    549  // URLs, convert the parameters to query in order to make the video load
    550  // correctly as an iframe. In either case, warn about it in the
    551  // developer console.
    552  int32_t ampIndex = path.FindChar('&', 0);
    553  bool replaceQuery = false;
    554  if (ampIndex != -1) {
    555    int32_t qmIndex = path.FindChar('?', 0);
    556    if (qmIndex == -1 || qmIndex > ampIndex) {
    557      replaceQuery = true;
    558    }
    559  }
    560 
    561  Document* doc = AsElement()->OwnerDoc();
    562  // If we've made it this far, we've got a rewritable embed. Log it in
    563  // telemetry.
    564  doc->SetUseCounter(eUseCounter_custom_YouTubeFlashEmbed);
    565 
    566  // If we're pref'd off, return after telemetry has been logged.
    567  if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
    568    return;
    569  }
    570 
    571  NS_ConvertUTF8toUTF16 utf16OldURI(prePath);
    572  AppendUTF8toUTF16(path, utf16OldURI);
    573  // If we need to convert the URL, it means an ampersand comes first.
    574  // Use the index we found earlier.
    575  if (replaceQuery) {
    576    // Replace question marks with ampersands.
    577    path.ReplaceChar('?', '&');
    578    // Replace the first ampersand with a question mark.
    579    path.SetCharAt('?', ampIndex);
    580  }
    581  // Switch out video access url formats, which should possibly allow HTML5
    582  // video loading.
    583  path.ReplaceSubstring("/v/"_ns, "/embed/"_ns);
    584  NS_ConvertUTF8toUTF16 utf16URI(prePath);
    585  AppendUTF8toUTF16(path, utf16URI);
    586  rv = nsContentUtils::NewURIWithDocumentCharset(aRewrittenURI, utf16URI, doc,
    587                                                 aBaseURI);
    588  if (NS_FAILED(rv)) {
    589    return;
    590  }
    591  AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
    592  const char* msgName;
    593  // If there's no query to rewrite, just notify in the developer console
    594  // that we're changing the embed.
    595  if (!replaceQuery) {
    596    msgName = "RewriteYouTubeEmbed";
    597  } else {
    598    msgName = "RewriteYouTubeEmbedPathParams";
    599  }
    600  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Plugins"_ns,
    601                                  doc, nsContentUtils::eDOM_PROPERTIES, msgName,
    602                                  params);
    603 }
    604 
    605 bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
    606  if (!aContentPolicy || !mURI) {
    607    MOZ_ASSERT_UNREACHABLE("Doing it wrong");
    608    return false;
    609  }
    610 
    611  Element* el = AsElement();
    612  Document* doc = el->OwnerDoc();
    613 
    614  nsContentPolicyType contentPolicyType = GetContentPolicyType();
    615 
    616  Result<RefPtr<LoadInfo>, nsresult> maybeLoadInfo =
    617      LoadInfo::Create(doc->NodePrincipal(),  // loading principal
    618                       doc->NodePrincipal(),  // triggering principal
    619                       el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
    620                       contentPolicyType);
    621  if (NS_WARN_IF(maybeLoadInfo.isErr())) {
    622    return false;
    623  }
    624  RefPtr<LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap();
    625 
    626  *aContentPolicy = nsIContentPolicy::ACCEPT;
    627  nsresult rv =
    628      NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, aContentPolicy,
    629                                nsContentUtils::GetContentPolicy());
    630  NS_ENSURE_SUCCESS(rv, false);
    631  if (NS_CP_REJECTED(*aContentPolicy)) {
    632    LOG(("OBJLC [%p]: Content policy denied load of %s", this,
    633         mURI->GetSpecOrDefault().get()));
    634    return false;
    635  }
    636 
    637  return true;
    638 }
    639 
    640 bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
    641  if (!aContentPolicy) {
    642    MOZ_ASSERT_UNREACHABLE("Null out variable");
    643    return false;
    644  }
    645 
    646  Element* el = AsElement();
    647  Document* doc = el->OwnerDoc();
    648 
    649  nsContentPolicyType objectType;
    650  switch (mType) {
    651    case ObjectType::Document:
    652      objectType = nsIContentPolicy::TYPE_DOCUMENT;
    653      break;
    654    default:
    655      MOZ_ASSERT_UNREACHABLE(
    656          "Calling checkProcessPolicy with an unexpected type");
    657      return false;
    658  }
    659 
    660  Result<RefPtr<LoadInfo>, nsresult> maybeLoadInfo = LoadInfo::Create(
    661      doc->NodePrincipal(),  // loading principal
    662      doc->NodePrincipal(),  // triggering principal
    663      el, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, objectType);
    664  if (NS_WARN_IF(maybeLoadInfo.isErr())) {
    665    return false;
    666  }
    667  RefPtr<LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap();
    668 
    669  *aContentPolicy = nsIContentPolicy::ACCEPT;
    670  nsresult rv = NS_CheckContentProcessPolicy(
    671      mURI ? mURI : mBaseURI, secCheckLoadInfo, aContentPolicy,
    672      nsContentUtils::GetContentPolicy());
    673  NS_ENSURE_SUCCESS(rv, false);
    674 
    675  if (NS_CP_REJECTED(*aContentPolicy)) {
    676    LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
    677    return false;
    678  }
    679 
    680  return true;
    681 }
    682 
    683 bool nsObjectLoadingContent::IsSyntheticImageDocument() const {
    684  if (mType != ObjectType::Document || !mFrameLoader) {
    685    return false;
    686  }
    687 
    688  BrowsingContext* browsingContext = mFrameLoader->GetExtantBrowsingContext();
    689  return browsingContext && browsingContext->GetIsSyntheticDocumentContainer();
    690 }
    691 
    692 nsObjectLoadingContent::ParameterUpdateFlags
    693 nsObjectLoadingContent::UpdateObjectParameters() {
    694  Element* el = AsElement();
    695 
    696  uint32_t caps = GetCapabilities();
    697  LOG(("OBJLC [%p]: Updating object parameters", this));
    698 
    699  nsresult rv;
    700  nsAutoCString newMime;
    701  nsCOMPtr<nsIURI> newURI;
    702  nsCOMPtr<nsIURI> newBaseURI;
    703  ObjectType newType;
    704  // Set if this state can't be used to load anything, forces
    705  // ObjectType::Fallback
    706  bool stateInvalid = false;
    707  // Indicates what parameters changed.
    708  // eParamChannelChanged - means parameters that affect channel opening
    709  //                        decisions changed
    710  // eParamStateChanged -   means anything that affects what content we load
    711  //                        changed, even if the channel we'd open remains the
    712  //                        same.
    713  //
    714  // State changes outside of the channel parameters only matter if we've
    715  // already opened a channel or tried to instantiate content, whereas channel
    716  // parameter changes require re-opening the channel even if we haven't gotten
    717  // that far.
    718  ParameterUpdateFlags retval = eParamNoChange;
    719 
    720  ///
    721  /// Initial MIME Type
    722  ///
    723 
    724  if (caps & eFallbackIfClassIDPresent &&
    725      el->HasNonEmptyAttr(nsGkAtoms::classid)) {
    726    // We don't support class ID plugin references, so we should always treat
    727    // having class Ids as attributes as invalid, and fallback accordingly.
    728    stateInvalid = true;
    729  }
    730 
    731  ///
    732  /// Codebase
    733  ///
    734 
    735  nsIURI* docBaseURI = el->GetBaseURI();
    736 
    737  nsAutoString codebaseStr;
    738  el->GetAttr(nsGkAtoms::codebase, codebaseStr);
    739  if (StaticPrefs::dom_object_embed_codebase_enabled() &&
    740      !codebaseStr.IsEmpty()) {
    741    rv = nsContentUtils::NewURIWithDocumentCharset(
    742        getter_AddRefs(newBaseURI), codebaseStr, el->OwnerDoc(), docBaseURI);
    743    if (NS_FAILED(rv)) {
    744      // Malformed URI
    745      LOG(
    746          ("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
    747           "will use document baseURI instead",
    748           this));
    749    }
    750  }
    751 
    752  // If we failed to build a valid URI, use the document's base URI
    753  if (!newBaseURI) {
    754    newBaseURI = docBaseURI;
    755  }
    756 
    757  ///
    758  /// URI
    759  ///
    760 
    761  nsAutoString uriStr;
    762  // Different elements keep this in various locations
    763  if (el->NodeInfo()->Equals(nsGkAtoms::object)) {
    764    el->GetAttr(nsGkAtoms::data, uriStr);
    765  } else if (el->NodeInfo()->Equals(nsGkAtoms::embed)) {
    766    el->GetAttr(nsGkAtoms::src, uriStr);
    767  } else {
    768    MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
    769  }
    770 
    771  mRewrittenYoutubeEmbed = false;
    772 
    773  // Note that the baseURI changing could affect the newURI, even if uriStr did
    774  // not change.
    775  if (!uriStr.IsEmpty()) {
    776    rv = nsContentUtils::NewURIWithDocumentCharset(
    777        getter_AddRefs(newURI), uriStr, el->OwnerDoc(), newBaseURI);
    778    nsCOMPtr<nsIURI> rewrittenURI;
    779    MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI));
    780    if (rewrittenURI) {
    781      newURI = rewrittenURI;
    782      mRewrittenYoutubeEmbed = true;
    783      newMime = "text/html"_ns;
    784    }
    785 
    786    if (NS_FAILED(rv)) {
    787      stateInvalid = true;
    788    }
    789  }
    790 
    791  ///
    792  /// type
    793  ///
    794  nsAutoString rawTypeAttr;
    795  el->GetAttr(nsGkAtoms::type, rawTypeAttr);
    796  // YouTube embeds might be using type="application/x-shockwave-flash"
    797  // which needs to be allowed, but must not override the text/html MIME set
    798  // above.
    799  if (!mRewrittenYoutubeEmbed && !rawTypeAttr.IsEmpty()) {
    800    nsAutoString params;
    801    nsAutoString mime;
    802    nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
    803 
    804    if (!StaticPrefs::dom_object_embed_type_hint_enabled()) {
    805      NS_ConvertUTF16toUTF8 mimeUTF8(mime);
    806      if (imgLoader::SupportImageWithMimeType(mimeUTF8)) {
    807        // Normally the type attribute should not be used as a hint, but for
    808        // images it does seem to happen in Chrome and Safari. Images generally
    809        // don't lead to code execution and we don't use
    810        // AcceptedMimeTypes::IMAGES_AND_DOCUMENTS above.
    811        newMime = mimeUTF8;
    812      } else if (GetTypeOfContent(mimeUTF8) != ObjectType::Document) {
    813        LOG(
    814            ("OBJLC [%p]: MIME '%s' from type attribute is not supported, "
    815             "forcing fallback.",
    816             this, mimeUTF8.get()));
    817        stateInvalid = true;
    818      }
    819 
    820      // Don't use the type attribute as a Content-Type hint in other cases.
    821    } else {
    822      CopyUTF16toUTF8(mime, newMime);
    823    }
    824  }
    825 
    826  ///
    827  /// Check if the original (pre-channel) content-type or URI changed, and
    828  /// record mOriginal{ContentType,URI}
    829  ///
    830 
    831  if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
    832    // These parameters changing requires re-opening the channel, so don't
    833    // consider the currently-open channel below
    834    // XXX(johns): Changing the mime type might change our decision on whether
    835    //             or not we load a channel, so we count changes to it as a
    836    //             channel parameter change for the sake of simplicity.
    837    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
    838    LOG(("OBJLC [%p]: Channel parameters changed", this));
    839  }
    840  mOriginalContentType = newMime;
    841  mOriginalURI = newURI;
    842 
    843  ///
    844  /// If we have a channel, see if its MIME type should take precendence and
    845  /// check the final (redirected) URL
    846  ///
    847 
    848  // If we have a loaded channel and channel parameters did not change, use it
    849  // to determine what we would load.
    850  bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
    851  // If we have a channel and are type loading, as opposed to having an existing
    852  // channel for a previous load.
    853  bool newChannel = useChannel && mType == ObjectType::Loading;
    854 
    855  RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
    856  if (newChannel && documentChannel) {
    857    // If we've got a DocumentChannel which is marked as loaded using
    858    // `mChannelLoaded`, we are currently in the middle of a
    859    // `UpgradeLoadToDocument`.
    860    //
    861    // As we don't have the real mime-type from the channel, handle this by
    862    // using `newMime`.
    863    newMime = TEXT_HTML;
    864 
    865    MOZ_DIAGNOSTIC_ASSERT(GetTypeOfContent(newMime) == ObjectType::Document,
    866                          "How is text/html not ObjectType::Document?");
    867  } else if (newChannel && mChannel) {
    868    nsCString channelType;
    869    rv = mChannel->GetContentType(channelType);
    870    if (NS_FAILED(rv)) {
    871      MOZ_ASSERT_UNREACHABLE("GetContentType failed");
    872      stateInvalid = true;
    873      channelType.Truncate();
    874    }
    875 
    876    LOG(("OBJLC [%p]: Channel has a content type of %s", this,
    877         channelType.get()));
    878 
    879    bool binaryChannelType = false;
    880    if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
    881      channelType = APPLICATION_OCTET_STREAM;
    882      mChannel->SetContentType(channelType);
    883      binaryChannelType = true;
    884    } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
    885               channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
    886      binaryChannelType = true;
    887    }
    888 
    889    // Channel can change our URI through redirection
    890    rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
    891    if (NS_FAILED(rv)) {
    892      MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
    893      stateInvalid = true;
    894    }
    895 
    896    ObjectType typeHint =
    897        newMime.IsEmpty() ? ObjectType::Fallback : GetTypeOfContent(newMime);
    898 
    899    // In order of preference:
    900    //
    901    // 1) Use our type hint if it matches a plugin
    902    // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
    903    //    it matches a plugin
    904    // 3) If the channel returns a binary stream type:
    905    //    3a) If we have a type non-null non-document type hint, use that
    906    //    3b) If the uri file extension matches a plugin type, use that
    907    // 4) Use the channel type
    908 
    909    bool overrideChannelType = false;
    910    if (IsPluginMIME(newMime)) {
    911      LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
    912           this));
    913      overrideChannelType = true;
    914    } else if (binaryChannelType && typeHint != ObjectType::Fallback) {
    915      if (typeHint == ObjectType::Document) {
    916        if (imgLoader::SupportImageWithMimeType(newMime)) {
    917          LOG(
    918              ("OBJLC [%p]: Using type hint in favor of binary channel type "
    919               "(Image Document)",
    920               this));
    921          overrideChannelType = true;
    922        }
    923      } else {
    924        LOG(
    925            ("OBJLC [%p]: Using type hint in favor of binary channel type "
    926             "(Non-Image Document)",
    927             this));
    928        overrideChannelType = true;
    929      }
    930    }
    931 
    932    if (overrideChannelType) {
    933      // Set the type we'll use for dispatch on the channel.  Otherwise we could
    934      // end up trying to dispatch to a nsFrameLoader, which will complain that
    935      // it couldn't find a way to handle application/octet-stream
    936      nsAutoCString parsedMime, dummy;
    937      NS_ParseResponseContentType(newMime, parsedMime, dummy);
    938      if (!parsedMime.IsEmpty()) {
    939        mChannel->SetContentType(parsedMime);
    940      }
    941    } else {
    942      newMime = channelType;
    943    }
    944  } else if (newChannel) {
    945    LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
    946    stateInvalid = true;
    947  }
    948 
    949  ///
    950  /// Determine final type
    951  ///
    952  // In order of preference:
    953  //  1) If we have attempted channel load, or set stateInvalid above, the type
    954  //     is always null (fallback)
    955  //  2) If we have a loaded channel, we grabbed its mimeType above, use that
    956  //     type.
    957  //  3) If we have a plugin type and no URI, use that type.
    958  //  4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
    959  //  5) if we have a URI, set type to loading to indicate we'd need a channel
    960  //     to proceed.
    961  //  6) Otherwise, type null to indicate unloadable content (fallback)
    962  //
    963 
    964  ObjectType newMime_Type = GetTypeOfContent(newMime);
    965 
    966  if (stateInvalid) {
    967    newType = ObjectType::Fallback;
    968    LOG(("OBJLC [%p]: NewType #0: %s - %u", this, newMime.get(),
    969         uint32_t(newType)));
    970    newMime.Truncate();
    971  } else if (newChannel) {
    972    // If newChannel is set above, we considered it in setting newMime
    973    newType = newMime_Type;
    974    LOG(("OBJLC [%p]: NewType #1: %s - %u", this, newMime.get(),
    975         uint32_t(newType)));
    976    LOG(("OBJLC [%p]: Using channel type", this));
    977  } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
    978             IsPluginMIME(newMime)) {
    979    newType = newMime_Type;
    980    LOG(("OBJLC [%p]: NewType #2: %s - %u", this, newMime.get(),
    981         uint32_t(newType)));
    982    LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
    983  } else if (newURI && (mOriginalContentType.IsEmpty() ||
    984                        newMime_Type != ObjectType::Fallback)) {
    985    // We could potentially load this if we opened a channel on mURI, indicate
    986    // this by leaving type as loading.
    987    //
    988    // If a MIME type was requested in the tag, but we have decided to set load
    989    // type to null, ignore (otherwise we'll default to document type loading).
    990    newType = ObjectType::Loading;
    991    LOG(("OBJLC [%p]: NewType #3: %u", this, uint32_t(newType)));
    992  } else {
    993    // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
    994    // documents) always load with a channel.
    995    newType = ObjectType::Fallback;
    996    LOG(("OBJLC [%p]: NewType #4: %u", this, uint32_t(newType)));
    997  }
    998 
    999  ///
   1000  /// Handle existing channels
   1001  ///
   1002 
   1003  if (useChannel && newType == ObjectType::Loading) {
   1004    // We decided to use a channel, and also that the previous channel is still
   1005    // usable, so re-use the existing values.
   1006    newType = mType;
   1007    LOG(("OBJLC [%p]: NewType #5: %u", this, uint32_t(newType)));
   1008    newMime = mContentType;
   1009    newURI = mURI;
   1010  } else if (useChannel && !newChannel) {
   1011    // We have an existing channel, but did not decide to use one.
   1012    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
   1013    useChannel = false;
   1014  }
   1015 
   1016  ///
   1017  /// Update changed values
   1018  ///
   1019 
   1020  if (newType != mType) {
   1021    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
   1022    LOG(("OBJLC [%p]: Type changed from %u -> %u", this, uint32_t(mType),
   1023         uint32_t(newType)));
   1024    mType = newType;
   1025  }
   1026 
   1027  if (!URIEquals(mBaseURI, newBaseURI)) {
   1028    LOG(("OBJLC [%p]: Object effective baseURI changed", this));
   1029    mBaseURI = newBaseURI;
   1030  }
   1031 
   1032  if (!URIEquals(newURI, mURI)) {
   1033    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
   1034    LOG(("OBJLC [%p]: Object effective URI changed", this));
   1035    mURI = newURI;
   1036  }
   1037 
   1038  // We don't update content type when loading, as the type is not final and we
   1039  // don't want to superfluously change between mOriginalContentType ->
   1040  // mContentType when doing |obj.data = obj.data| with a channel and differing
   1041  // type.
   1042  if (mType != ObjectType::Loading && mContentType != newMime) {
   1043    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
   1044    retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
   1045    LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
   1046         mContentType.get(), newMime.get()));
   1047    mContentType = newMime;
   1048  }
   1049 
   1050  // If we decided to keep using info from an old channel, but also that state
   1051  // changed, we need to invalidate it.
   1052  if (useChannel && !newChannel && (retval & eParamStateChanged)) {
   1053    mType = ObjectType::Loading;
   1054    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
   1055  }
   1056 
   1057  return retval;
   1058 }
   1059 
   1060 // Only OnStartRequest should be passing the channel parameter
   1061 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad) {
   1062  return LoadObject(aNotify, aForceLoad, nullptr);
   1063 }
   1064 
   1065 nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
   1066                                            nsIRequest* aLoadingChannel) {
   1067  Element* el = AsElement();
   1068  Document* doc = el->OwnerDoc();
   1069  nsresult rv = NS_OK;
   1070 
   1071  // Per bug 1318303, if the parent document is not active, load the alternative
   1072  // and return.
   1073  if (!doc->IsCurrentActiveDocument()) {
   1074    // Since this can be triggered on change of attributes, make sure we've
   1075    // unloaded whatever is loaded first.
   1076    UnloadObject();
   1077    ObjectType oldType = mType;
   1078    mType = ObjectType::Fallback;
   1079    TriggerInnerFallbackLoads();
   1080    NotifyStateChanged(oldType, true);
   1081    return NS_OK;
   1082  }
   1083 
   1084  // XXX(johns): In these cases, we refuse to touch our content and just
   1085  //   remain unloaded, as per legacy behavior. It would make more sense to
   1086  //   load fallback content initially and refuse to ever change state again.
   1087  if (doc->IsBeingUsedAsImage()) {
   1088    return NS_OK;
   1089  }
   1090 
   1091  if (doc->IsLoadedAsData() || doc->IsStaticDocument()) {
   1092    return NS_OK;
   1093  }
   1094 
   1095  LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
   1096       this, aNotify, aForceLoad, aLoadingChannel));
   1097 
   1098  // We can't re-use an already open channel, but aForceLoad may make us try
   1099  // to load a plugin without any changes in channel state.
   1100  if (aForceLoad && mChannelLoaded) {
   1101    CloseChannel();
   1102    mChannelLoaded = false;
   1103  }
   1104 
   1105  // Save these for NotifyStateChanged();
   1106  ObjectType oldType = mType;
   1107 
   1108  ParameterUpdateFlags stateChange = UpdateObjectParameters();
   1109 
   1110  if (!stateChange && !aForceLoad) {
   1111    return NS_OK;
   1112  }
   1113 
   1114  ///
   1115  /// State has changed, unload existing content and attempt to load new type
   1116  ///
   1117  LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)", this,
   1118       stateChange));
   1119 
   1120  // We synchronously start/stop plugin instances below, which may spin the
   1121  // event loop. Re-entering into the load is fine, but at that point the
   1122  // original load call needs to abort when unwinding
   1123  // NOTE this is located *after* the state change check, a subsequent load
   1124  //      with no subsequently changed state will be a no-op.
   1125  if (mIsLoading) {
   1126    LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
   1127  }
   1128  mIsLoading = true;
   1129  AutoSetLoadingToFalse reentryCheck(this);
   1130 
   1131  // Unload existing content, keeping in mind stopping plugins might spin the
   1132  // event loop. Note that we check for still-open channels below
   1133  UnloadObject(false);  // Don't reset state
   1134  if (!mIsLoading) {
   1135    // The event loop must've spun and re-entered into LoadObject, which
   1136    // finished the load
   1137    LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
   1138    return NS_OK;
   1139  }
   1140 
   1141  // Determine what's going on with our channel.
   1142  if (stateChange & eParamChannelChanged) {
   1143    // If the channel params changed, throw away the channel, but unset
   1144    // mChannelLoaded so we'll still try to open a new one for this load if
   1145    // necessary
   1146    CloseChannel();
   1147    mChannelLoaded = false;
   1148  } else if (mType == ObjectType::Fallback && mChannel) {
   1149    // If we opened a channel but then failed to find a loadable state, throw it
   1150    // away. mChannelLoaded will indicate that we tried to load a channel at one
   1151    // point so we wont recurse
   1152    CloseChannel();
   1153  } else if (mType == ObjectType::Loading && mChannel) {
   1154    // We're still waiting on a channel load, already opened one, and
   1155    // channel parameters didn't change
   1156    return NS_OK;
   1157  } else if (mChannelLoaded && mChannel != aLoadingChannel) {
   1158    // The only time we should have a loaded channel with a changed state is
   1159    // when the channel has just opened -- in which case this call should
   1160    // have originated from OnStartRequest
   1161    MOZ_ASSERT_UNREACHABLE(
   1162        "Loading with a channel, but state doesn't make sense");
   1163    return NS_OK;
   1164  }
   1165 
   1166  //
   1167  // Security checks
   1168  //
   1169 
   1170  if (mType != ObjectType::Fallback) {
   1171    bool allowLoad = true;
   1172    int16_t contentPolicy = nsIContentPolicy::ACCEPT;
   1173    // If mChannelLoaded is set we presumably already passed load policy
   1174    // If mType == ObjectType::Loading then we call OpenChannel() which
   1175    // internally creates a new channel and calls asyncOpen() on that channel
   1176    // which then enforces content policy checks.
   1177    if (allowLoad && mURI && !mChannelLoaded && mType != ObjectType::Loading) {
   1178      allowLoad = CheckLoadPolicy(&contentPolicy);
   1179    }
   1180    // If we're loading a type now, check ProcessPolicy. Note that we may check
   1181    // both now in the case of plugins whose type is determined before opening a
   1182    // channel.
   1183    if (allowLoad && mType != ObjectType::Loading) {
   1184      allowLoad = CheckProcessPolicy(&contentPolicy);
   1185    }
   1186 
   1187    // Content policy implementations can mutate the DOM, check for re-entry
   1188    if (!mIsLoading) {
   1189      LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
   1190           this));
   1191      return NS_OK;
   1192    }
   1193 
   1194    // Load denied, switch to null
   1195    if (!allowLoad) {
   1196      LOG(("OBJLC [%p]: Load denied by policy", this));
   1197      mType = ObjectType::Fallback;
   1198    }
   1199  }
   1200 
   1201  // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
   1202  // requires that `embed` and `object` go through `Fetch` with mode=navigate,
   1203  // see 1.3.5. This will in https://fetch.spec.whatwg.org/#fetching plumb us
   1204  // through to https://fetch.spec.whatwg.org/#concept-main-fetch where in step
   1205  // 12 a switch is performed. Since `object` and `embed` have mode=navigate the
   1206  // result of https://fetch.spec.whatwg.org/#concept-scheme-fetch will decide
   1207  // if main fetch proceeds. We short-circuit that scheme-fetch here, inspecting
   1208  // if the scheme of `mURI` is one that would return a network error. The
   1209  // following schemes are allowed through in scheme fetch:
   1210  // "about", "blob", "data", "file", "http", "https".
   1211  //
   1212  // Some accessibility tests use our internal "chrome" scheme.
   1213  if (mType != ObjectType::Fallback && mURI) {
   1214    ObjectType type = ObjectType::Fallback;
   1215    for (const auto& candidate :
   1216         {"about", "blob", "chrome", "data", "file", "http", "https"}) {
   1217      if (mURI->SchemeIs(candidate)) {
   1218        type = mType;
   1219        break;
   1220      }
   1221    }
   1222    mType = type;
   1223  }
   1224 
   1225  // Items resolved as Image/Document are not candidates for content blocking,
   1226  // as well as invalid plugins (they will not have the mContentType set).
   1227  if (mType == ObjectType::Fallback && ShouldBlockContent()) {
   1228    LOG(("OBJLC [%p]: Enable content blocking", this));
   1229    mType = ObjectType::Loading;
   1230  }
   1231 
   1232  // Sanity check: We shouldn't have any loaded resources, pending events, or
   1233  // a final listener at this point
   1234  if (mFrameLoader || mFinalListener) {
   1235    MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
   1236    return NS_OK;
   1237  }
   1238 
   1239  // More sanity-checking:
   1240  // If mChannel is set, mChannelLoaded should be set, and vice-versa
   1241  if (mType != ObjectType::Fallback && !!mChannel != mChannelLoaded) {
   1242    MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
   1243    return NS_OK;
   1244  }
   1245 
   1246  ///
   1247  /// Attempt to load new type
   1248  ///
   1249 
   1250  // We don't set mFinalListener until OnStartRequest has been called, to
   1251  // prevent re-entry ugliness with CloseChannel()
   1252  nsCOMPtr<nsIStreamListener> finalListener;
   1253  switch (mType) {
   1254    case ObjectType::Document: {
   1255      if (!mChannel) {
   1256        // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
   1257        // requires documents have a channel, so this is not a valid state.
   1258        MOZ_ASSERT_UNREACHABLE(
   1259            "Attempting to load a document without a "
   1260            "channel");
   1261        rv = NS_ERROR_FAILURE;
   1262        break;
   1263      }
   1264 
   1265      nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
   1266      if (!docShell) {
   1267        rv = NS_ERROR_FAILURE;
   1268        break;
   1269      }
   1270 
   1271      // We're loading a document, so we have to set LOAD_DOCUMENT_URI
   1272      // (especially important for firing onload)
   1273      nsLoadFlags flags = 0;
   1274      mChannel->GetLoadFlags(&flags);
   1275      flags |= nsIChannel::LOAD_DOCUMENT_URI;
   1276      mChannel->SetLoadFlags(flags);
   1277 
   1278      nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
   1279      NS_ASSERTION(req, "Docshell must be an ifreq");
   1280 
   1281      nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service());
   1282      if (NS_WARN_IF(!uriLoader)) {
   1283        MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
   1284        mFrameLoader->Destroy();
   1285        mFrameLoader = nullptr;
   1286        break;
   1287      }
   1288 
   1289      uint32_t uriLoaderFlags = nsDocShell::ComputeURILoaderFlags(
   1290          docShell->GetBrowsingContext(), LOAD_NORMAL,
   1291          /* aIsDocumentLoad */ false);
   1292 
   1293      rv = uriLoader->OpenChannel(mChannel, uriLoaderFlags, req,
   1294                                  getter_AddRefs(finalListener));
   1295      // finalListener will receive OnStartRequest either below, or if
   1296      // `mChannel` is a `DocumentChannel`, it will be received after
   1297      // RedirectToRealChannel.
   1298    } break;
   1299    case ObjectType::Loading:
   1300      // If our type remains Loading, we need a channel to proceed
   1301      rv = OpenChannel();
   1302      if (NS_FAILED(rv)) {
   1303        LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")", this,
   1304             static_cast<uint32_t>(rv)));
   1305      }
   1306      break;
   1307    case ObjectType::Fallback:
   1308      // Handled below, silence compiler warnings
   1309      break;
   1310  }
   1311 
   1312  //
   1313  // Loaded, handle notifications and fallback
   1314  //
   1315  if (NS_FAILED(rv)) {
   1316    // If we failed in the loading hunk above, switch to null (empty) region
   1317    LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
   1318    mType = ObjectType::Fallback;
   1319  }
   1320 
   1321  if (mType == ObjectType::Fallback) {
   1322    LOG(("OBJLC [%p]: Switching to fallback state", this));
   1323    MOZ_ASSERT(!mFrameLoader, "switched to fallback but also loaded something");
   1324 
   1325    MaybeFireErrorEvent();
   1326 
   1327    if (mChannel) {
   1328      // If we were loading with a channel but then failed over, throw it away
   1329      CloseChannel();
   1330    }
   1331 
   1332    // Don't try to initialize plugins or final listener below
   1333    finalListener = nullptr;
   1334 
   1335    TriggerInnerFallbackLoads();
   1336  }
   1337 
   1338  // Notify of our final state
   1339  NotifyStateChanged(oldType, aNotify);
   1340  NS_ENSURE_TRUE(mIsLoading, NS_OK);
   1341 
   1342  //
   1343  // Spawning plugins and dispatching to the final listener may re-enter, so are
   1344  // delayed until after we fire a notification, to prevent missing
   1345  // notifications or firing them out of order.
   1346  //
   1347  // Note that we ensured that we entered into LoadObject() from
   1348  // ::OnStartRequest above when loading with a channel.
   1349  //
   1350 
   1351  rv = NS_OK;
   1352  if (finalListener) {
   1353    NS_ASSERTION(mType != ObjectType::Fallback && mType != ObjectType::Loading,
   1354                 "We should not have a final listener with a non-loaded type");
   1355    mFinalListener = finalListener;
   1356 
   1357    // If we're a DocumentChannel load, hold off on firing the `OnStartRequest`
   1358    // callback, as we haven't received it yet from our caller.
   1359    RefPtr<DocumentChannel> documentChannel = do_QueryObject(mChannel);
   1360    if (documentChannel) {
   1361      MOZ_ASSERT(
   1362          mType == ObjectType::Document,
   1363          "We have a DocumentChannel here but aren't loading a document?");
   1364    } else {
   1365      rv = finalListener->OnStartRequest(mChannel);
   1366    }
   1367  }
   1368 
   1369  if ((NS_FAILED(rv) && rv != NS_ERROR_PARSED_DATA_CACHED) && mIsLoading) {
   1370    // Since we've already notified of our transition, we can just Unload and
   1371    // call ConfigureFallback (which will notify again)
   1372    oldType = mType;
   1373    mType = ObjectType::Fallback;
   1374    UnloadObject(false);
   1375    NS_ENSURE_TRUE(mIsLoading, NS_OK);
   1376    CloseChannel();
   1377    TriggerInnerFallbackLoads();
   1378    NotifyStateChanged(oldType, true);
   1379  }
   1380 
   1381  return NS_OK;
   1382 }
   1383 
   1384 // This call can re-enter when dealing with plugin listeners
   1385 nsresult nsObjectLoadingContent::CloseChannel() {
   1386  if (mChannel) {
   1387    LOG(("OBJLC [%p]: Closing channel\n", this));
   1388    // Null the values before potentially-reentering, and ensure they survive
   1389    // the call
   1390    nsCOMPtr<nsIChannel> channelGrip(mChannel);
   1391    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
   1392    mChannel = nullptr;
   1393    mFinalListener = nullptr;
   1394    channelGrip->CancelWithReason(NS_BINDING_ABORTED,
   1395                                  "nsObjectLoadingContent::CloseChannel"_ns);
   1396    if (listenerGrip) {
   1397      // mFinalListener is only set by LoadObject after OnStartRequest, or
   1398      // by OnStartRequest in the case of late-opened plugin streams
   1399      listenerGrip->OnStopRequest(channelGrip, NS_BINDING_ABORTED);
   1400    }
   1401  }
   1402  return NS_OK;
   1403 }
   1404 
   1405 bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
   1406    nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
   1407  if (!NS_IsAboutBlankAllowQueryAndFragment(aURI) || !aInheritPrincipal) {
   1408    return false;
   1409  }
   1410 
   1411  if (!mFrameLoader || !mFrameLoader->GetExistingDocShell()) {
   1412    return false;
   1413  }
   1414 
   1415  RefPtr<nsDocShellLoadState> dummyLoadState = new nsDocShellLoadState(mURI);
   1416  return mFrameLoader->GetExistingDocShell()->ShouldDoInitialAboutBlankSyncLoad(
   1417      aURI, dummyLoadState, aPrincipalToInherit);
   1418 }
   1419 
   1420 nsresult nsObjectLoadingContent::OpenChannel() {
   1421  Element* el = AsElement();
   1422  Document* doc = el->OwnerDoc();
   1423  NS_ASSERTION(doc, "No owner document?");
   1424 
   1425  nsresult rv;
   1426  mChannel = nullptr;
   1427 
   1428  // E.g. mms://
   1429  if (!mURI || !CanHandleURI(mURI)) {
   1430    return NS_ERROR_NOT_AVAILABLE;
   1431  }
   1432 
   1433  nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
   1434  nsCOMPtr<nsIChannel> chan;
   1435  RefPtr<ObjectInterfaceRequestorShim> shim =
   1436      new ObjectInterfaceRequestorShim(this);
   1437 
   1438  bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
   1439      el->NodePrincipal(),  // aLoadState->PrincipalToInherit()
   1440      mURI,                 // aLoadState->URI()
   1441      true,                 // aInheritForAboutBlank
   1442      false);               // aForceInherit
   1443 
   1444  bool inheritPrincipal = inheritAttrs && !mURI->SchemeIs("data");
   1445 
   1446  nsSecurityFlags securityFlags =
   1447      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
   1448  if (inheritPrincipal) {
   1449    securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   1450  }
   1451 
   1452  nsContentPolicyType contentPolicyType = GetContentPolicyType();
   1453  // The setting of LOAD_BYPASS_SERVICE_WORKER here is now an optimization.
   1454  // ServiceWorkerInterceptController::ShouldPrepareForIntercept does a more
   1455  // expensive check of BrowsingContext ancestors to look for object/embed.
   1456  nsLoadFlags loadFlags = nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
   1457                          nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
   1458                          nsIRequest::LOAD_HTML_OBJECT_DATA;
   1459  uint32_t sandboxFlags = doc->GetSandboxFlags();
   1460 
   1461  // For object loads we store the policyContainer that potentially needs to
   1462  // be inherited, e.g. in case we are loading an opaque origin
   1463  // like a data: URI. The actual inheritance check happens within
   1464  // Document::InitPolicyContainer(). Please create an actual copy of the
   1465  // policyContainer (do not share the same reference) otherwise modifications
   1466  // done (such as the meta CSP of the new doc) in an opaque origin will
   1467  // incorrectly be propagated to the embedding document.
   1468  RefPtr<PolicyContainer> policyContainerToInherit;
   1469  if (nsCOMPtr<nsIPolicyContainer> policyContainer =
   1470          doc->GetPolicyContainer()) {
   1471    policyContainerToInherit = new PolicyContainer();
   1472    policyContainerToInherit->InitFromOther(
   1473        PolicyContainer::Cast(policyContainer.get()));
   1474  }
   1475 
   1476  // --- Create LoadInfo
   1477  RefPtr<LoadInfo> loadInfo = MOZ_TRY(LoadInfo::Create(
   1478      /*aLoadingPrincipal = aLoadingContext->NodePrincipal() */ nullptr,
   1479      /*aTriggeringPrincipal = aLoadingPrincipal */ nullptr,
   1480      /*aLoadingContext = */ el,
   1481      /*aSecurityFlags = */ securityFlags,
   1482      /*aContentPolicyType = */ contentPolicyType,
   1483      /*aLoadingClientInfo = */ Nothing(),
   1484      /*aController = */ Nothing(),
   1485      /*aSandboxFlags = */ sandboxFlags));
   1486 
   1487  if (inheritAttrs) {
   1488    loadInfo->SetPrincipalToInherit(el->NodePrincipal());
   1489  }
   1490 
   1491  // For object loads we store the policyContainer that potentially needs to
   1492  // be inherited, e.g. in case we are loading an opaque origin
   1493  // like a data: URI. The actual inheritance check happens within
   1494  // Document::InitPolicyContainer(). Please create an actual copy of the
   1495  // policyContainer (do not share the same reference) otherwise modifications
   1496  // done (such as the meta CSP of the new doc) in an opaque origin will
   1497  // incorrectly be propagated to the embedding document.
   1498  if (policyContainerToInherit) {
   1499    loadInfo->SetPolicyContainerToInherit(policyContainerToInherit);
   1500  }
   1501 
   1502  if (DocumentChannel::CanUseDocumentChannel(mURI) &&
   1503      !IsAboutBlankLoadOntoInitialAboutBlank(mURI, inheritPrincipal,
   1504                                             el->NodePrincipal())) {
   1505    // --- Create LoadState
   1506    RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI);
   1507    loadState->SetPrincipalToInherit(el->NodePrincipal());
   1508    loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal());
   1509    if (policyContainerToInherit) {
   1510      loadState->SetPolicyContainer(policyContainerToInherit);
   1511    }
   1512    loadState->SetTriggeringSandboxFlags(sandboxFlags);
   1513 
   1514    // TODO(djg): This was httpChan->SetReferrerInfoWithoutClone(referrerInfo);
   1515    // Is the ...WithoutClone(...) important?
   1516    auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
   1517    loadState->SetReferrerInfo(referrerInfo);
   1518 
   1519    loadState->SetShouldCheckForRecursion(true);
   1520 
   1521    // When loading using DocumentChannel, ensure that the MIME type hint is
   1522    // propagated to DocumentLoadListener. Object elements can override MIME
   1523    // handling in some scenarios.
   1524    if (!mOriginalContentType.IsEmpty()) {
   1525      nsAutoCString parsedMime, dummy;
   1526      NS_ParseResponseContentType(mOriginalContentType, parsedMime, dummy);
   1527      if (!parsedMime.IsEmpty()) {
   1528        loadState->SetTypeHint(parsedMime);
   1529      }
   1530    }
   1531 
   1532    chan =
   1533        DocumentChannel::CreateForObject(loadState, loadInfo, loadFlags, shim);
   1534    MOZ_ASSERT(chan);
   1535    // NS_NewChannel sets the group on the channel.  CreateDocumentChannel does
   1536    // not.
   1537    chan->SetLoadGroup(group);
   1538  } else {
   1539    rv = NS_NewChannelInternal(getter_AddRefs(chan),  // outChannel
   1540                               mURI,                  // aUri
   1541                               loadInfo,              // aLoadInfo
   1542                               nullptr,               // aPerformanceStorage
   1543                               group,                 // aLoadGroup
   1544                               shim,                  // aCallbacks
   1545                               loadFlags,             // aLoadFlags
   1546                               nullptr);              // aIoService
   1547    NS_ENSURE_SUCCESS(rv, rv);
   1548  };
   1549 
   1550  // Referrer
   1551  if (nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan)) {
   1552    auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
   1553 
   1554    rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo);
   1555    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1556 
   1557    // Set the initiator type
   1558    if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(httpChan)) {
   1559      timedChannel->SetInitiatorType(el->LocalName());
   1560    }
   1561 
   1562    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan));
   1563    if (cos && UserActivation::IsHandlingUserInput()) {
   1564      cos->AddClassFlags(nsIClassOfService::UrgentStart);
   1565    }
   1566  }
   1567 
   1568  // AsyncOpen can fail if a file does not exist.
   1569  rv = chan->AsyncOpen(shim);
   1570  NS_ENSURE_SUCCESS(rv, rv);
   1571  LOG(("OBJLC [%p]: Channel opened", this));
   1572  mChannel = chan;
   1573  return NS_OK;
   1574 }
   1575 
   1576 uint32_t nsObjectLoadingContent::GetCapabilities() const {
   1577  return eSupportImages | eSupportDocuments;
   1578 }
   1579 
   1580 void nsObjectLoadingContent::Destroy() {
   1581  if (mFrameLoader) {
   1582    mFrameLoader->Destroy();
   1583    mFrameLoader = nullptr;
   1584  }
   1585 
   1586  // Reset state so that if the element is re-appended to tree again (e.g.
   1587  // adopting to another document), it will reload resource again.
   1588  UnloadObject();
   1589 }
   1590 
   1591 /* static */
   1592 void nsObjectLoadingContent::Traverse(nsObjectLoadingContent* tmp,
   1593                                      nsCycleCollectionTraversalCallback& cb) {
   1594  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
   1595  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy);
   1596 }
   1597 
   1598 /* static */
   1599 void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
   1600  if (tmp->mFrameLoader) {
   1601    tmp->mFrameLoader->Destroy();
   1602  }
   1603  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader);
   1604  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy);
   1605 }
   1606 
   1607 void nsObjectLoadingContent::UnloadObject(bool aResetState) {
   1608  if (mFrameLoader) {
   1609    mFrameLoader->Destroy();
   1610    mFrameLoader = nullptr;
   1611  }
   1612 
   1613  if (aResetState) {
   1614    CloseChannel();
   1615    mChannelLoaded = false;
   1616    mType = ObjectType::Loading;
   1617    mURI = mOriginalURI = mBaseURI = nullptr;
   1618    mContentType.Truncate();
   1619    mOriginalContentType.Truncate();
   1620  }
   1621 
   1622  mScriptRequested = false;
   1623 
   1624  mIsStopping = false;
   1625 
   1626  mSubdocumentIntrinsicSize.reset();
   1627  mSubdocumentIntrinsicRatio.reset();
   1628 }
   1629 
   1630 void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
   1631                                                bool aNotify) {
   1632  LOG(("OBJLC [%p]: NotifyStateChanged: (%u) -> (%u) (notify %i)", this,
   1633       uint32_t(aOldType), uint32_t(mType), aNotify));
   1634 
   1635  dom::Element* thisEl = AsElement();
   1636  // Non-images are always not broken.
   1637  // XXX: I assume we could just remove this completely?
   1638  thisEl->RemoveStates(ElementState::BROKEN, aNotify);
   1639 
   1640  if (mType == aOldType) {
   1641    return;
   1642  }
   1643 
   1644  Document* doc = thisEl->GetComposedDoc();
   1645  if (!doc) {
   1646    return;  // Nothing to do
   1647  }
   1648 
   1649  PresShell* presShell = doc->GetPresShell();
   1650  // If there is no PresShell or it hasn't been initialized there isn't much to
   1651  // do.
   1652  if (!presShell || !presShell->DidInitialize()) {
   1653    return;
   1654  }
   1655  presShell->PostRecreateFramesFor(thisEl);
   1656 }
   1657 
   1658 nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(
   1659    const nsCString& aMIMEType) {
   1660  Element* el = AsElement();
   1661  NS_ASSERTION(el, "must be a content");
   1662 
   1663  Document* doc = el->OwnerDoc();
   1664 
   1665  // Images and documents are always supported.
   1666  MOZ_ASSERT((GetCapabilities() & (eSupportImages | eSupportDocuments)) ==
   1667             (eSupportImages | eSupportDocuments));
   1668 
   1669  LOG(
   1670      ("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
   1671       "el: %p\n",
   1672       this, aMIMEType.get(), el));
   1673  auto ret =
   1674      static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
   1675          aMIMEType, doc->GetSandboxFlags()));
   1676  LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
   1677  return ret;
   1678 }
   1679 
   1680 void nsObjectLoadingContent::CreateStaticClone(
   1681    nsObjectLoadingContent* aDest) const {
   1682  MOZ_ASSERT(aDest->AsElement()->OwnerDoc()->IsStaticDocument());
   1683  aDest->mType = mType;
   1684 
   1685  if (mFrameLoader) {
   1686    aDest->AsElement()->OwnerDoc()->AddPendingFrameStaticClone(aDest,
   1687                                                               mFrameLoader);
   1688  }
   1689 }
   1690 
   1691 NS_IMETHODIMP
   1692 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) {
   1693  NS_IF_ADDREF(*aURI = GetSrcURI());
   1694  return NS_OK;
   1695 }
   1696 
   1697 void nsObjectLoadingContent::TriggerInnerFallbackLoads() {
   1698  MOZ_ASSERT(!mFrameLoader && !mChannel,
   1699             "ConfigureFallback called with loaded content");
   1700  MOZ_ASSERT(mType == ObjectType::Fallback);
   1701 
   1702  Element* el = AsElement();
   1703  if (!el->IsHTMLElement(nsGkAtoms::object)) {
   1704    return;
   1705  }
   1706  // Do a depth-first traverse of node tree with the current element as root,
   1707  // looking for non-<param> elements.  If we find some then we have an HTML
   1708  // fallback for this element.
   1709  for (nsIContent* child = el->GetFirstChild(); child;) {
   1710    // <object> and <embed> elements in the fallback need to StartObjectLoad.
   1711    // Their children should be ignored since they are part of those element's
   1712    // fallback.
   1713    if (auto* embed = HTMLEmbedElement::FromNode(child)) {
   1714      embed->StartObjectLoad(true, true);
   1715      // Skip the children
   1716      child = child->GetNextNonChildNode(el);
   1717    } else if (auto* object = HTMLObjectElement::FromNode(child)) {
   1718      object->StartObjectLoad(true, true);
   1719      // Skip the children
   1720      child = child->GetNextNonChildNode(el);
   1721    } else {
   1722      child = child->GetNextNode(el);
   1723    }
   1724  }
   1725 }
   1726 
   1727 NS_IMETHODIMP
   1728 nsObjectLoadingContent::UpgradeLoadToDocument(
   1729    nsIChannel* aRequest, BrowsingContext** aBrowsingContext) {
   1730  AUTO_PROFILER_LABEL("nsObjectLoadingContent::UpgradeLoadToDocument", NETWORK);
   1731 
   1732  LOG(("OBJLC [%p]: UpgradeLoadToDocument", this));
   1733 
   1734  if (aRequest != mChannel || !aRequest) {
   1735    // happens when a new load starts before the previous one got here.
   1736    return NS_BINDING_ABORTED;
   1737  }
   1738 
   1739  // We should be state loading.
   1740  if (mType != ObjectType::Loading) {
   1741    MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
   1742    return NS_BINDING_ABORTED;
   1743  }
   1744  MOZ_ASSERT(!mChannelLoaded, "mChannelLoaded set already?");
   1745  MOZ_ASSERT(!mFinalListener, "mFinalListener exists already?");
   1746 
   1747  mChannelLoaded = true;
   1748 
   1749  // We don't need to check for errors here, unlike in `OnStartRequest`, as
   1750  // `UpgradeLoadToDocument` is only called when the load is going to become a
   1751  // process-switching load. As we never process switch for failed object loads,
   1752  // we know our channel status is successful.
   1753 
   1754  // Call `LoadObject` to trigger our nsObjectLoadingContext to switch into the
   1755  // specified new state.
   1756  nsresult rv = LoadObject(true, false, aRequest);
   1757  if (NS_WARN_IF(NS_FAILED(rv))) {
   1758    return rv;
   1759  }
   1760 
   1761  RefPtr<BrowsingContext> bc = GetBrowsingContext();
   1762  if (!bc) {
   1763    return NS_ERROR_FAILURE;
   1764  }
   1765 
   1766  // At this point we know that we have a browsing context, so it's time to make
   1767  // sure that that browsing context gets the correct container feature policy.
   1768  // This is needed for `DocumentLoadListener::MaybeTriggerProcessSwitch` to be
   1769  // able to start loading the document with the correct container feature
   1770  // policy in the load info.
   1771  RefreshFeaturePolicy();
   1772 
   1773  bc.forget(aBrowsingContext);
   1774  return NS_OK;
   1775 }
   1776 
   1777 bool nsObjectLoadingContent::ShouldBlockContent() {
   1778  return mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) &&
   1779         StaticPrefs::browser_safebrowsing_blockedURIs_enabled();
   1780 }
   1781 
   1782 Document* nsObjectLoadingContent::GetContentDocument(
   1783    nsIPrincipal& aSubjectPrincipal) {
   1784  Element* el = AsElement();
   1785  if (!el->IsInComposedDoc()) {
   1786    return nullptr;
   1787  }
   1788 
   1789  Document* sub_doc = el->OwnerDoc()->GetSubDocumentFor(el);
   1790  if (!sub_doc) {
   1791    return nullptr;
   1792  }
   1793 
   1794  // Return null for cross-origin contentDocument.
   1795  if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
   1796    return nullptr;
   1797  }
   1798 
   1799  return sub_doc;
   1800 }
   1801 
   1802 void nsObjectLoadingContent::MaybeFireErrorEvent() {
   1803  Element* el = AsElement();
   1804  // Queue a task to fire an error event if we're an <object> element.  The
   1805  // queueing is important, since then we don't have to worry about reentry.
   1806  if (el->IsHTMLElement(nsGkAtoms::object)) {
   1807    RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
   1808        new LoadBlockingAsyncEventDispatcher(el, u"error"_ns, CanBubble::eNo,
   1809                                             ChromeOnlyDispatch::eNo);
   1810    loadBlockingAsyncDispatcher->PostDOMEvent();
   1811  }
   1812 }
   1813 
   1814 bool nsObjectLoadingContent::BlockEmbedOrObjectContentLoading() {
   1815  Element* el = AsElement();
   1816 
   1817  // Traverse up the node tree to see if we have any ancestors that may block us
   1818  // from loading
   1819  for (nsIContent* parent = el->GetParent(); parent;
   1820       parent = parent->GetParent()) {
   1821    if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
   1822      return true;
   1823    }
   1824    // If we have an ancestor that is an object with a source, it'll have an
   1825    // associated displayed type. If that type is not null, don't load content
   1826    // for the embed.
   1827    if (auto* object = HTMLObjectElement::FromNode(parent)) {
   1828      if (object->Type() != ObjectType::Fallback) {
   1829        return true;
   1830      }
   1831    }
   1832  }
   1833  return false;
   1834 }
   1835 
   1836 void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
   1837    const Maybe<IntrinsicSize>& aIntrinsicSize,
   1838    const Maybe<AspectRatio>& aIntrinsicRatio) {
   1839  if (aIntrinsicSize == mSubdocumentIntrinsicSize &&
   1840      aIntrinsicRatio == mSubdocumentIntrinsicRatio) {
   1841    return;
   1842  }
   1843 
   1844  mSubdocumentIntrinsicSize = aIntrinsicSize;
   1845  mSubdocumentIntrinsicRatio = aIntrinsicRatio;
   1846 
   1847  if (nsSubDocumentFrame* sdf = do_QueryFrame(AsElement()->GetPrimaryFrame())) {
   1848    sdf->SubdocumentIntrinsicSizeOrRatioChanged();
   1849  }
   1850 }
   1851 
   1852 void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult) {
   1853  ObjectType oldType = mType;
   1854  if (NS_FAILED(aResult)) {
   1855    UnloadObject();
   1856    mType = ObjectType::Fallback;
   1857    TriggerInnerFallbackLoads();
   1858    NotifyStateChanged(oldType, true);
   1859    return;
   1860  }
   1861 
   1862  // (mChannelLoaded && mChannel) indicates this is a good state, not any sort
   1863  // of failures.
   1864  MOZ_DIAGNOSTIC_ASSERT_IF(mChannelLoaded && mChannel,
   1865                           mType == ObjectType::Document);
   1866  NotifyStateChanged(oldType, true);
   1867 }
   1868 
   1869 void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
   1870  MOZ_DIAGNOSTIC_ASSERT(mFrameLoader);
   1871  if (!mFrameLoader) {
   1872    return;
   1873  }
   1874 
   1875  // If the browsingContext is not ready (because docshell is dead), don't try
   1876  // to create one.
   1877  if (!mFrameLoader->IsRemoteFrame() && !mFrameLoader->GetExistingDocShell()) {
   1878    return;
   1879  }
   1880 
   1881  RefPtr<BrowsingContext> browsingContext = mFrameLoader->GetBrowsingContext();
   1882 
   1883  if (!browsingContext || !browsingContext->IsContentSubframe()) {
   1884    return;
   1885  }
   1886 
   1887  auto* el = nsGenericHTMLElement::FromNode(AsElement());
   1888  if (!el->IsInComposedDoc()) {
   1889    return;
   1890  }
   1891 
   1892  if (ContentChild* cc = ContentChild::GetSingleton()) {
   1893    (void)cc->SendSetContainerFeaturePolicy(
   1894        browsingContext, Some(mFeaturePolicy->ToFeaturePolicyInfo()));
   1895  }
   1896 }
   1897 
   1898 /* static */ already_AddRefed<nsIPrincipal>
   1899 nsObjectLoadingContent::GetFeaturePolicyDefaultOrigin(nsINode* aNode) {
   1900  auto* el = nsGenericHTMLElement::FromNode(aNode);
   1901  nsCOMPtr<nsIURI> nodeURI;
   1902  // Different elements keep this in various locations
   1903  if (el->NodeInfo()->Equals(nsGkAtoms::object)) {
   1904    el->GetURIAttr(nsGkAtoms::data, nullptr, getter_AddRefs(nodeURI));
   1905  } else if (el->NodeInfo()->Equals(nsGkAtoms::embed)) {
   1906    el->GetURIAttr(nsGkAtoms::src, nullptr, getter_AddRefs(nodeURI));
   1907  }
   1908 
   1909  nsCOMPtr<nsIPrincipal> principal;
   1910  if (nodeURI) {
   1911    principal = BasePrincipal::CreateContentPrincipal(
   1912        nodeURI,
   1913        BasePrincipal::Cast(el->NodePrincipal())->OriginAttributesRef());
   1914  } else {
   1915    principal = el->NodePrincipal();
   1916  }
   1917 
   1918  return principal.forget();
   1919 }
   1920 
   1921 void nsObjectLoadingContent::RefreshFeaturePolicy() {
   1922  if (mType != ObjectType::Document) {
   1923    return;
   1924  }
   1925 
   1926  if (!mFeaturePolicy) {
   1927    mFeaturePolicy = MakeAndAddRef<FeaturePolicy>(AsElement());
   1928  }
   1929 
   1930  // The origin can change if 'src' or 'data' attributes change.
   1931  nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin(AsElement());
   1932  MOZ_ASSERT(origin);
   1933  mFeaturePolicy->SetDefaultOrigin(origin);
   1934 
   1935  mFeaturePolicy->InheritPolicy(AsElement()->OwnerDoc()->FeaturePolicy());
   1936  MaybeStoreCrossOriginFeaturePolicy();
   1937 }