Bug 1037687, load iframes in shadow DOM, r=wchen
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 24 Feb 2015 16:41:43 +0200
changeset 259345 53365d1c2b7f61efd40dd3839e6205f63d10c0e5
parent 259344 26cc212192fa8034fa3a9b9246ec7c141691738b
child 259346 c26d8b2d5edf85ace207f634a097daf441e0a853
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen
bugs1037687
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1037687, load iframes in shadow DOM, r=wchen
dom/base/FragmentOrElement.cpp
dom/base/ShadowRoot.cpp
dom/base/ShadowRoot.h
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsObjectLoadingContent.cpp
dom/base/test/mochitest.ini
dom/base/test/test_bug1037687.html
dom/html/HTMLObjectElement.cpp
dom/html/HTMLSharedObjectElement.cpp
dom/html/nsGenericHTMLFrameElement.cpp
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1179,16 +1179,20 @@ FragmentOrElement::DestroyContent()
   //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
   ReleaseWrapper(this);
 
   uint32_t i, count = mAttrsAndChildren.ChildCount();
   for (i = 0; i < count; ++i) {
     // The child can remove itself from the parent in BindToTree.
     mAttrsAndChildren.ChildAt(i)->DestroyContent();
   }
+  ShadowRoot* shadowRoot = GetShadowRoot();
+  if (shadowRoot) {
+    shadowRoot->DestroyContent();
+  }
 }
 
 void
 FragmentOrElement::SaveSubtreeState()
 {
   uint32_t i, count = mAttrsAndChildren.ChildCount();
   for (i = 0; i < count; ++i) {
     mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -706,16 +706,25 @@ ShadowRoot::ContentRemoved(nsIDocument* 
 
   // Watch for node that is removed from the pool because
   // it may need to be removed from an insertion point.
   if (IsPooledNode(aChild, aContainer, mPoolHost)) {
     RemoveDistributedNode(aChild);
   }
 }
 
+void
+ShadowRoot::DestroyContent()
+{
+  if (mOlderShadow) {
+    mOlderShadow->DestroyContent();
+  }
+  DocumentFragment::DestroyContent();
+}
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList, StyleSheetList,
                                    mShadowRoot)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList)
 NS_INTERFACE_MAP_END_INHERITING(StyleSheetList)
 
 NS_IMPL_ADDREF_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
 NS_IMPL_RELEASE_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -128,16 +128,18 @@ public:
   ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; }
   void StyleSheetChanged();
 
   bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
   void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
   {
     mIsComposedDocParticipant = aIsComposedDocParticipant;
   }
+
+  virtual void DestroyContent() MOZ_OVERRIDE;
 protected:
   virtual ~ShadowRoot();
 
   // The pool host is the parent of the nodes that will be distributed
   // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
   nsCOMPtr<nsIContent> mPoolHost;
 
   // An array of content insertion points that are a descendant of the ShadowRoot
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -14,16 +14,23 @@
 #include "xpcprivate.h"
 
 namespace mozilla {
 namespace dom {
 
 static bool
 ShouldExposeChildWindow(nsString& aNameBeingResolved, nsIDOMWindow *aChild)
 {
+  nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aChild);
+  NS_ENSURE_TRUE(piWin, false);
+  Element* e = piWin->GetFrameElementInternal();
+  if (e && e->IsInShadowTree()) {
+    return false;
+  }
+
   // If we're same-origin with the child, go ahead and expose it.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aChild);
   NS_ENSURE_TRUE(sop, false);
   if (nsContentUtils::SubjectPrincipal()->Equals(sop->GetPrincipal())) {
     return true;
   }
 
   // If we're not same-origin, expose it _only_ if the name of the browsing
@@ -58,19 +65,16 @@ ShouldExposeChildWindow(nsString& aNameB
   // in Gecko, and probably in other UAs as well. So we go with a simpler
   // approximation of the above. This approximation will only break sites that
   // rely on their cross-origin subframes setting window.name to a known value,
   // which is unlikely to be very common. And while it does introduce a
   // dependency on cross-origin state when doing global lookups, it doesn't
   // allow the child to arbitrarily pollute the parent namespace, and requires
   // cross-origin communication only in a limited set of cases that can be
   // computed independently by the parent.
-  nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aChild);
-  NS_ENSURE_TRUE(piWin, false);
-  Element* e = piWin->GetFrameElementInternal();
   return e && e->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
                              aNameBeingResolved, eCaseMatters);
 }
 
 bool
 WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx,
                                                    JS::Handle<JSObject*> aProxy,
                                                    JS::Handle<jsid> aId,
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -189,17 +189,17 @@ nsFrameLoader::~nsFrameLoader()
 }
 
 nsFrameLoader*
 nsFrameLoader::Create(Element* aOwner, bool aNetworkCreated)
 {
   NS_ENSURE_TRUE(aOwner, nullptr);
   nsIDocument* doc = aOwner->OwnerDoc();
   NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
-                 ((!doc->IsLoadedAsData() && aOwner->GetUncomposedDoc()) ||
+                 ((!doc->IsLoadedAsData() && aOwner->GetComposedDoc()) ||
                    doc->IsStaticDocument()),
                  nullptr);
 
   return new nsFrameLoader(aOwner, aNetworkCreated);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadFrame()
@@ -344,17 +344,17 @@ public:
 
 private:
   nsRefPtr<nsFrameLoader> mFrameLoader;
 };
 
 nsresult
 nsFrameLoader::ReallyStartLoadingInternal()
 {
-  NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInDoc());
+  NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc());
 
   PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading",
     js::ProfileEntry::Category::OTHER);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -871,22 +871,22 @@ nsFrameLoader::ShowRemoteFrame(const nsI
     }
   }
 
   // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   // cross-process layers; need to figure out what behavior we really
   // want here.  For now, hack.
   if (!mRemoteBrowserShown) {
     if (!mOwnerContent ||
-        !mOwnerContent->GetUncomposedDoc()) {
+        !mOwnerContent->GetComposedDoc()) {
       return false;
     }
 
     nsRefPtr<layers::LayerManager> layerManager =
-      nsContentUtils::LayerManagerForDocument(mOwnerContent->GetUncomposedDoc());
+      nsContentUtils::LayerManagerForDocument(mOwnerContent->GetComposedDoc());
     if (!layerManager) {
       // This is just not going to work.
       return false;
     }
 
     nsPIDOMWindow* win = mOwnerContent->OwnerDoc()->GetWindow();
     bool parentIsActive = false;
     if (win) {
@@ -967,18 +967,18 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   bool equal;
   nsresult rv =
     ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
   if (NS_FAILED(rv) || !equal) {
     // Security problems loom.  Just bail on it all
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsIDocument* ourDoc = ourContent->GetCurrentDoc();
-  nsIDocument* otherDoc = otherContent->GetCurrentDoc();
+  nsIDocument* ourDoc = ourContent->GetComposedDoc();
+  nsIDocument* otherDoc = otherContent->GetComposedDoc();
   if (!ourDoc || !otherDoc) {
     // Again, how odd, given that we had docshells
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   nsIPresShell* ourShell = ourDoc->GetShell();
   nsIPresShell* otherShell = otherDoc->GetShell();
   if (!ourShell || !otherShell) {
@@ -1186,18 +1186,18 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   }
 
   nsCOMPtr<nsIDocument> ourParentDocument =
     ourChildDocument->GetParentDocument();
   nsCOMPtr<nsIDocument> otherParentDocument =
     otherChildDocument->GetParentDocument();
 
   // Make sure to swap docshells between the two frames.
-  nsIDocument* ourDoc = ourContent->GetUncomposedDoc();
-  nsIDocument* otherDoc = otherContent->GetUncomposedDoc();
+  nsIDocument* ourDoc = ourContent->GetComposedDoc();
+  nsIDocument* otherDoc = otherContent->GetComposedDoc();
   if (!ourDoc || !otherDoc) {
     // Again, how odd, given that we had docshells
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
   NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
 
@@ -1607,17 +1607,17 @@ nsFrameLoader::MaybeCreateDocShell()
     mRemoteFrame = true;
     return NS_OK;
   }
 
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->OwnerDoc();
-  if (!(doc->IsStaticDocument() || mOwnerContent->IsInDoc())) {
+  if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (doc->IsResourceDoc() || !doc->IsActive()) {
     // Don't allow subframe loads in resource documents, nor
     // in non-active documents.
     return NS_ERROR_NOT_AVAILABLE;
   }
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -117,17 +117,17 @@ IsJavaMIME(const nsACString & aMIMEType)
 {
   return
     nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Java;
 }
 
 static bool
 InActiveDocument(nsIContent *aContent)
 {
-  if (!aContent->IsInDoc()) {
+  if (!aContent->IsInComposedDoc()) {
     return false;
   }
   nsIDocument *doc = aContent->OwnerDoc();
   return (doc && doc->IsActive());
 }
 
 ///
 /// Runnables and helper classes
@@ -3310,21 +3310,20 @@ nsObjectLoadingContent::ShouldPlay(Fallb
 }
 
 nsIDocument*
 nsObjectLoadingContent::GetContentDocument()
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
-  if (!thisContent->IsInDoc()) {
+  if (!thisContent->IsInComposedDoc()) {
     return nullptr;
   }
 
-  // XXXbz should this use GetComposedDoc()?  sXBL/XBL2 issue!
   nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
   if (!sub_doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
   if (!nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
     return nullptr;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -762,16 +762,17 @@ disabled = Disabled for now. Mochitest i
 support-files = bug444546.sjs
 [test_bug503473.html]
 disabled = Disabled due to making the harness time out
 support-files = file_bug503473-frame.sjs
 [test_bug1011748.html]
 skip-if = buildapp == 'b2g' || e10s
 support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
 [test_bug1025933.html]
+[test_bug1037687.html]
 [test_element.matches.html]
 [test_user_select.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1081686.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_window_define_nonconfigurable.html]
 skip-if = true # bug 1107443 - code for newly-added test was disabled
 [test_root_iframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1037687.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037687
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1037687</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1037687 **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  var host;
+  var sr;
+  var embed;
+  var object;
+  var iframe;
+  var resourceLoadCount = 0;
+
+  function resourceLoaded(event) {
+    ++resourceLoadCount;
+    ok(true, event.target + " got " + event.load);
+    if (resourceLoadCount == 3) {
+      SimpleTest.finish();
+    }
+  }
+
+  function createResource(sr, type) {
+    var el = document.createElement(type);
+    var attrName = type == "object" ? "data" : "src";
+    el.setAttribute(attrName, "file_mozfiledataurl_img.jpg");
+    el.onload = resourceLoaded;
+    var info = document.createElement("div");
+    info.textContent = type;
+    sr.appendChild(info);
+    sr.appendChild(el);
+  }
+
+  function test() {
+    host = document.getElementById("host");
+    sr = host.createShadowRoot();
+    embed = createResource(sr, "embed");
+    object = createResource(sr, "object");
+    iframe = createResource(sr, "iframe");
+  }
+
+  </script>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1037687">Mozilla Bug 1037687</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<div id="host"></div>
+</body>
+</html>
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -63,17 +63,17 @@ HTMLObjectElement::IsDoneAddingChildren(
 
 void
 HTMLObjectElement::DoneAddingChildren(bool aHaveNotified)
 {
   mIsDoneAddingChildren = true;
 
   // If we're already in a document, we need to trigger the load
   // Otherwise, BindToTree takes care of that.
-  if (IsInDoc()) {
+  if (IsInComposedDoc()) {
     StartObjectLoad(aHaveNotified);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLObjectElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLObjectElement,
                                                   nsGenericHTMLFormElement)
@@ -226,34 +226,34 @@ HTMLObjectElement::SetAttr(int32_t aName
 
   // if aNotify is false, we are coming from the parser or some such place;
   // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
-  if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
+  if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLObjectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify)
 {
   nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID,
                                                     aAttribute, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // See comment in SetAttr
-  if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
+  if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 bool
@@ -455,17 +455,17 @@ HTMLObjectElement::GetAttributeMappingFu
   return &MapAttributesIntoRule;
 }
 
 void
 HTMLObjectElement::StartObjectLoad(bool aNotify)
 {
   // BindToTree can call us asynchronously, and we may be removed from the tree
   // in the interim
-  if (!IsInDoc() || !OwnerDoc()->IsActive()) {
+  if (!IsInComposedDoc() || !OwnerDoc()->IsActive()) {
     return;
   }
 
   LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 EventStates
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -75,17 +75,17 @@ HTMLSharedObjectElement::IsDoneAddingChi
 void
 HTMLSharedObjectElement::DoneAddingChildren(bool aHaveNotified)
 {
   if (!mIsDoneAddingChildren) {
     mIsDoneAddingChildren = true;
 
     // If we're already in a document, we need to trigger the load
     // Otherwise, BindToTree takes care of that.
-    if (IsInDoc()) {
+    if (IsInComposedDoc()) {
       StartObjectLoad(aHaveNotified);
     }
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLSharedObjectElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSharedObjectElement,
@@ -175,17 +175,17 @@ HTMLSharedObjectElement::SetAttr(int32_t
 
   // if aNotify is false, we are coming from the parser or some such place;
   // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
-  if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
+  if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aName == URIAttrName()) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 bool
@@ -308,17 +308,17 @@ HTMLSharedObjectElement::GetAttributeMap
   return &MapAttributesIntoRule;
 }
 
 void
 HTMLSharedObjectElement::StartObjectLoad(bool aNotify)
 {
   // BindToTree can call us asynchronously, and we may be removed from the tree
   // in the interim
-  if (!IsInDoc() || !OwnerDoc()->IsActive()) {
+  if (!IsInComposedDoc() || !OwnerDoc()->IsActive()) {
     return;
   }
 
   LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 EventStates
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -126,17 +126,17 @@ nsGenericHTMLFrameElement::GetContentWin
                "Uh, this window should always be an outer window!");
 
   return win.forget();
 }
 
 void
 nsGenericHTMLFrameElement::EnsureFrameLoader()
 {
-  if (!IsInDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
+  if (!IsInComposedDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
     // If frame loader is there, we just keep it around, cached
     return;
   }
 
   // Strangely enough, this method doesn't actually ensure that the
   // frameloader exists.  It's more of a best-effort kind of thing.
   mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
   if (mIsPrerendered) {
@@ -216,17 +216,17 @@ nsGenericHTMLFrameElement::BindToTree(ns
                                       nsIContent* aBindingParent,
                                       bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (aDocument) {
+  if (IsInComposedDoc()) {
     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                  "Missing a script blocker!");
 
     PROFILER_LABEL("nsGenericHTMLFrameElement", "BindToTree",
       js::ProfileEntry::Category::OTHER);
 
     // We're in a document now.  Kick off the frame load.
     LoadSrc();