Merge inbound to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Sun, 04 Mar 2018 23:50:03 +0200
changeset 461516 190b536928f8a8ca96e52101d2013c88a1a66384
parent 461515 c74a5f3559834991fc6854be0ce82d3f802bc30e (current diff)
parent 461514 7cc75e6db0ee34102119caef09af078af234f620 (diff)
child 461517 b2bf42743febfe4d3d29753b9ff8f72aaca5b237
child 461529 c842abb7cfbf39e0c1cfb9a1f3a1d6415cd10744
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
190b536928f8 / 60.0a1 / 20180304220118 / files
nightly linux64
190b536928f8 / 60.0a1 / 20180304220118 / files
nightly mac
190b536928f8 / 60.0a1 / 20180304220118 / files
nightly win32
190b536928f8 / 60.0a1 / 20180304220118 / files
nightly win64
190b536928f8 / 60.0a1 / 20180304220118 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -34,16 +34,17 @@ support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
   ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
   ../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
   ../../../../../toolkit/components/reader/test/readerModeNonArticle.html
   ../../../../../toolkit/components/reader/test/readerModeArticle.html
 
 [browser_ext_browserAction_area.js]
+[browser_ext_browserAction_experiment.js]
 [browser_ext_browserAction_context.js]
 skip-if = os == 'win' || os == 'mac' # Bug 1405453
 [browser_ext_browserAction_contextMenu.js]
 # bug 1369197
 skip-if = os == 'linux'
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_experiment.js
@@ -0,0 +1,137 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let fooExperimentAPIs = {
+  foo: {
+    schema: "schema.json",
+    parent: {
+      scopes: ["addon_parent"],
+      script: "parent.js",
+      paths: [["experiments", "foo", "parent"]],
+    },
+    child: {
+      scopes: ["addon_child"],
+      script: "child.js",
+      paths: [["experiments", "foo", "child"]],
+    },
+  },
+};
+
+let fooExperimentFiles = {
+  "schema.json": JSON.stringify([
+    {
+      "namespace": "experiments.foo",
+      "types": [
+        {
+          "id": "Meh",
+          "type": "object",
+          "properties": {},
+        },
+      ],
+      "functions": [
+        {
+          "name": "parent",
+          "type": "function",
+          "async": true,
+          "parameters": [],
+        },
+        {
+          "name": "child",
+          "type": "function",
+          "parameters": [],
+          "returns": {"type": "string"},
+        },
+      ],
+    },
+  ]),
+
+  /* globals ExtensionAPI */
+  "parent.js": () => {
+    this.foo = class extends ExtensionAPI {
+      getAPI(context) {
+        return {
+          experiments: {
+            foo: {
+              parent() {
+                return Promise.resolve("parent");
+              },
+            },
+          },
+        };
+      }
+    };
+  },
+
+  "child.js": () => {
+    this.foo = class extends ExtensionAPI {
+      getAPI(context) {
+        return {
+          experiments: {
+            foo: {
+              child() {
+                return "child";
+              },
+            },
+          },
+        };
+      }
+    };
+  },
+};
+
+async function testFooExperiment() {
+  browser.test.assertEq("object", typeof browser.experiments,
+                        "typeof browser.experiments");
+
+  browser.test.assertEq("object", typeof browser.experiments.foo,
+                        "typeof browser.experiments.foo");
+
+  browser.test.assertEq("function", typeof browser.experiments.foo.child,
+                        "typeof browser.experiments.foo.child");
+
+  browser.test.assertEq("function", typeof browser.experiments.foo.parent,
+                        "typeof browser.experiments.foo.parent");
+
+  browser.test.assertEq("child", browser.experiments.foo.child(),
+                        "foo.child()");
+
+  browser.test.assertEq("parent", await browser.experiments.foo.parent(),
+                        "await foo.parent()");
+}
+
+add_task(async function test_browseraction_with_experiment() {
+  async function background() {
+    await new Promise(resolve => browser.browserAction.onClicked.addListener(resolve));
+    browser.test.log("Got browserAction.onClicked");
+
+    await testFooExperiment();
+
+    browser.test.notifyPass("background-browserAction-experiments.foo");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    isPrivileged: true,
+
+    manifest: {
+      browser_action: {},
+
+      experiment_apis: fooExperimentAPIs,
+    },
+
+    background: `
+      ${testFooExperiment}
+      (${background})();
+    `,
+
+    files: fooExperimentFiles,
+  });
+
+  await extension.startup();
+
+  await clickBrowserAction(extension, window);
+
+  await extension.awaitFinish("background-browserAction-experiments.foo");
+
+  await extension.unload();
+});
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -90,16 +90,34 @@ ShadowRoot::~ShadowRoot()
 
 JSObject*
 ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
+ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
+{
+  size_t sheetCount = aOther->SheetCount();
+  for (size_t i = 0; i < sheetCount; ++i) {
+    StyleSheet* sheet = aOther->SheetAt(i);
+    if (sheet->IsApplicable()) {
+      RefPtr<StyleSheet> clonedSheet =
+        sheet->Clone(nullptr, nullptr, nullptr, nullptr);
+      if (clonedSheet) {
+        AppendStyleSheet(*clonedSheet.get());
+        Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(),
+                                            clonedSheet->AsServo());
+      }
+    }
+  }
+}
+
+void
 ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
 {
   MOZ_ASSERT(aSlot);
 
   // Note that if name attribute missing, the slot is a default slot.
   nsAutoString name;
   aSlot->GetName(name);
 
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -84,16 +84,20 @@ public:
   // [deprecated] Shadow DOM v0
   void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
   void RemoveSheet(StyleSheet* aSheet);
   StyleSheetList* StyleSheets()
   {
     return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
   }
 
+  /**
+   * Clones internal state, for example stylesheets, of aOther to 'this'.
+   */
+  void CloneInternalDataFrom(ShadowRoot* aOther);
 private:
 
   /**
    * Try to reassign an element to a slot and returns whether the assignment
    * changed.
    */
   bool MaybeReassignElement(Element* aElement, const nsAttrValue* aOldValue);
 
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -652,24 +652,52 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
                       aReparentScope, aNodesWithProperties, clone,
                       aError);
       if (NS_WARN_IF(aError.Failed())) {
         return nullptr;
       }
     }
   }
 
-  if (aDeep && !aClone && aNode->IsElement()) {
-    if (ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot()) {
-      nsCOMPtr<nsINode> child =
-        CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
-                      aReparentScope, aNodesWithProperties, clone,
-                      aError);
-      if (NS_WARN_IF(aError.Failed())) {
-        return nullptr;
+  if (aDeep && aNode->IsElement()) {
+    if (aClone) {
+      if (clone->OwnerDoc()->IsStaticDocument()) {
+        ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot();
+        if (originalShadowRoot) {
+          ShadowRootInit init;
+          init.mMode = originalShadowRoot->Mode();
+          RefPtr<ShadowRoot> newShadowRoot =
+            clone->AsElement()->AttachShadow(init, aError);
+          if (NS_WARN_IF(aError.Failed())) {
+            return nullptr;
+          }
+
+          newShadowRoot->CloneInternalDataFrom(originalShadowRoot);
+          for (nsIContent* origChild = originalShadowRoot->GetFirstChild();
+               origChild;
+               origChild = origChild->GetNextSibling()) {
+            nsCOMPtr<nsINode> child =
+              CloneAndAdopt(origChild, aClone, aDeep, nodeInfoManager,
+                            aReparentScope, aNodesWithProperties, newShadowRoot,
+                            aError);
+            if (NS_WARN_IF(aError.Failed())) {
+              return nullptr;
+            }
+          }
+        }
+      }
+    } else {
+      if (ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot()) {
+        nsCOMPtr<nsINode> child =
+          CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
+                        aReparentScope, aNodesWithProperties, clone,
+                        aError);
+        if (NS_WARN_IF(aError.Failed())) {
+          return nullptr;
+        }
       }
     }
   }
 
   // Cloning template element.
   if (aDeep && aClone && IsTemplateElement(aNode)) {
     DocumentFragment* origContent =
       static_cast<HTMLTemplateElement*>(aNode)->Content();