--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
doc_pseudo_elements.html
doc_script_animation.html
doc_simple_animation.html
doc_multiple_animation_types.html
head.js
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
[browser_animation_animated_properties_displayed.js]
[browser_animation_click_selects_animation.js]
[browser_animation_controller_exposes_document_currentTime.js]
skip-if = os == "linux" && !debug # Bug 1234567
[browser_animation_empty_on_invalid_nodes.js]
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -456,8 +456,13 @@ function waitForContextMenu(popup, butto
* @return {Promise} resolves when the preferences have been updated
*/
function pushPref(preferenceName, value) {
return new Promise(resolve => {
let options = {"set": [[preferenceName, value]]};
SpecialPowers.pushPrefEnv(options, resolve);
});
}
+
+var closeToolbox = Task.async(function* () {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ yield gDevTools.closeToolbox(target);
+});
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -6,18 +6,19 @@ support-files =
doc_media_queries.html
doc_pseudoelement.html
doc_sourcemaps.css
doc_sourcemaps.css.map
doc_sourcemaps.html
doc_sourcemaps.scss
head.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_computed_browser-styles.js]
[browser_computed_cycle_color.js]
[browser_computed_getNodeInfo.js]
[browser_computed_keybindings_01.js]
[browser_computed_keybindings_02.js]
--- a/devtools/client/inspector/fonts/test/browser.ini
+++ b/devtools/client/inspector/fonts/test/browser.ini
@@ -3,17 +3,18 @@ tags = devtools
subsuite = devtools
support-files =
browser_fontinspector.html
test_iframe.html
ostrich-black.ttf
ostrich-regular.ttf
head.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_fontinspector.js]
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_edit-previews-show-all.js]
[browser_fontinspector_theme-change.js]
--- a/devtools/client/inspector/layout/test/browser.ini
+++ b/devtools/client/inspector/layout/test/browser.ini
@@ -1,18 +1,19 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_layout_iframe1.html
doc_layout_iframe2.html
head.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_layout.js]
[browser_layout_editablemodel.js]
# [browser_layout_editablemodel_allproperties.js]
# Disabled for too many intermittent failures (bug 1009322)
[browser_layout_editablemodel_bluronclick.js]
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -40,18 +40,19 @@ support-files =
lib_jquery_1.2_min.js
lib_jquery_1.3_min.js
lib_jquery_1.4_min.js
lib_jquery_1.6_min.js
lib_jquery_1.7_min.js
lib_jquery_1.11.1_min.js
lib_jquery_2.1.1_min.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_markup_accessibility_focus_blur.js]
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_markup_accessibility_navigation.js]
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_markup_accessibility_semantics.js]
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -29,18 +29,19 @@ support-files =
doc_sourcemaps.html
doc_sourcemaps.scss
doc_style_editor_link.css
doc_test_image.png
doc_urls_clickable.css
doc_urls_clickable.html
head.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_rules_add-property-and-reselect.js]
[browser_rules_add-property-cancel_01.js]
[browser_rules_add-property-cancel_02.js]
[browser_rules_add-property-cancel_03.js]
[browser_rules_add-property-commented.js]
--- a/devtools/client/inspector/shared/test/browser.ini
+++ b/devtools/client/inspector/shared/test/browser.ini
@@ -8,18 +8,19 @@ support-files =
doc_content_stylesheet_imported.css
doc_content_stylesheet_imported2.css
doc_content_stylesheet_linked.css
doc_content_stylesheet_script.css
doc_content_stylesheet_xul.css
doc_frame_script.js
head.js
!/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
- !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_styleinspector_context-menu-copy-color_01.js]
[browser_styleinspector_context-menu-copy-color_02.js]
subsuite = clipboard
[browser_styleinspector_context-menu-copy-urls.js]
subsuite = clipboard
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -26,18 +26,18 @@ support-files =
doc_inspector_remove-iframe-during-load.html
doc_inspector_search.html
doc_inspector_search-reserved.html
doc_inspector_search-suggestions.html
doc_inspector_search-svg.html
doc_inspector_select-last-selected-01.html
doc_inspector_select-last-selected-02.html
head.js
+ shared-head.js
!/devtools/client/commandline/test/helpers.js
- !/devtools/client/inspector/test/head.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_inspector_addNode_01.js]
[browser_inspector_addNode_02.js]
[browser_inspector_addNode_03.js]
[browser_inspector_breadcrumbs.js]
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -1,17 +1,17 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../framework/test/shared-head.js */
/* import-globals-from ../../commandline/test/helpers.js */
/* import-globals-from ../../shared/test/test-actor-registry.js */
-/* globals registerTestActor, getTestActor */
+/* import-globals-from ../../inspector/test/shared-head.js */
"use strict";
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
// Services.prefs.setBoolPref("devtools.debugger.log", true);
@@ -24,16 +24,21 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
this);
// Import helpers registering the test-actor in remote targets
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
this);
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+ this);
+
DevToolsUtils.testing = true;
registerCleanupFunction(() => {
DevToolsUtils.testing = false;
});
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
});
@@ -117,34 +122,16 @@ var startPicker = Task.async(function* (
* node
*/
function selectAndHighlightNode(selector, inspector) {
info("Highlighting and selecting the node " + selector);
return selectNode(selector, inspector, "test-highlight");
}
/**
- * Set the inspector's current selection to the first match of the given css
- * selector
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @return {Promise} Resolves when the inspector is updated with the new node
- */
-var selectNode = Task.async(function* (selector, inspector, reason = "test") {
- info("Selecting the node for '" + selector + "'");
- let nodeFront = yield getNodeFront(selector, inspector);
- let updated = inspector.once("inspector-updated");
- inspector.selection.setNodeFront(nodeFront, reason);
- yield updated;
-});
-
-/**
* Select node for a given selector, make it focusable and set focus in its
* container element.
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The current inspector-panel instance.
* @return {MarkupContainer}
*/
function* focusNode(selector, inspector) {
getContainerForNodeFront(inspector.walker.rootNode, inspector).elt.focus();
@@ -177,42 +164,16 @@ function clearCurrentNodeSelection(inspe
* with an object: { tab, toolbox, inspector }.
*/
var openInspectorForURL = Task.async(function* (url, hostType) {
let tab = yield addTab(url);
let { inspector, toolbox, testActor } = yield openInspector(hostType);
return { tab, inspector, toolbox, testActor };
});
-/**
- * Open the toolbox, with the inspector tool visible.
- * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
- * @return a promise that resolves when the inspector is ready
- */
-var openInspector = Task.async(function* (hostType) {
- info("Opening the inspector");
-
- let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
- hostType);
- let inspector = toolbox.getPanel("inspector");
-
- if (inspector._updateProgress) {
- info("Need to wait for the inspector to update");
- yield inspector.once("inspector-updated");
- }
-
- info("Waiting for actor features to be detected");
- yield inspector._detectingActorFeatures;
-
- yield registerTestActor(toolbox.target.client);
- let testActor = yield getTestActor(toolbox);
-
- return {toolbox, inspector, testActor};
-});
-
function getActiveInspector() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
return gDevTools.getToolbox(target).getPanel("inspector");
}
/**
* Right click on a node in the test page and click on the inspect menu item.
* @param {TestActor}
@@ -240,113 +201,16 @@ var clickOnInspectMenuItem = Task.async(
let contextClosed = once(contentAreaContextMenu, "popuphidden");
contentAreaContextMenu.hidePopup();
yield contextClosed;
return getActiveInspector();
});
/**
- * Open the toolbox, with the inspector tool visible, and the one of the sidebar
- * tabs selected.
- *
- * @param {String} id
- * The ID of the sidebar tab to be opened
- * @return a promise that resolves when the inspector is ready and the tab is
- * visible and ready
- */
-var openInspectorSidebarTab = Task.async(function* (id) {
- let {toolbox, inspector, testActor} = yield openInspector();
-
- info("Selecting the " + id + " sidebar");
- inspector.sidebar.select(id);
-
- return {
- toolbox,
- inspector,
- testActor
- };
-});
-
-/**
- * Open the toolbox, with the inspector tool visible, and the rule-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the rule view
- * is visible and ready
- */
-function openRuleView() {
- return openInspectorSidebarTab("ruleview").then(data => {
- return {
- toolbox: data.toolbox,
- inspector: data.inspector,
- testActor: data.testActor,
- view: data.inspector.ruleview.view
- };
- });
-}
-
-/**
- * Open the toolbox, with the inspector tool visible, and the computed-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the computed
- * view is visible and ready
- */
-function openComputedView() {
- return openInspectorSidebarTab("computedview").then(data => {
- return {
- toolbox: data.toolbox,
- inspector: data.inspector,
- testActor: data.testActor,
- view: data.inspector.computedview.view
- };
- });
-}
-
-/**
- * Select the rule view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- * The opened inspector panel
- * @return {CssRuleView} the rule view
- */
-function selectRuleView(inspector) {
- inspector.sidebar.select("ruleview");
- return inspector.ruleview.view;
-}
-
-/**
- * Select the computed view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- * The opened inspector panel
- * @return {CssComputedView} the computed view
- */
-function selectComputedView(inspector) {
- inspector.sidebar.select("computedview");
- return inspector.computedview.view;
-}
-
-/**
- * Get the NodeFront for a node that matches a given css selector, via the
- * protocol.
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @return {Promise} Resolves to the NodeFront instance
- */
-function getNodeFront(selector, {walker}) {
- if (selector._form) {
- return selector;
- }
- return walker.querySelector(walker.rootNode, selector);
-}
-
-/**
* Get the NodeFront for a node that matches a given css selector inside a
* given iframe.
* @param {String|NodeFront} selector
* @param {String|NodeFront} frameSelector A selector that matches the iframe
* the node is in
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return {Promise} Resolves when the inspector is updated with the new node
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/shared-head.js
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* globals registerTestActor, getTestActor, Task, openToolboxForTab, gBrowser */
+
+// This file contains functions related to the inspector that are also of interest to
+// other test directores as well.
+
+/**
+ * Open the toolbox, with the inspector tool visible.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return a promise that resolves when the inspector is ready
+ */
+var openInspector = Task.async(function* (hostType) {
+ info("Opening the inspector");
+
+ let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
+ hostType);
+ let inspector = toolbox.getPanel("inspector");
+
+ if (inspector._updateProgress) {
+ info("Need to wait for the inspector to update");
+ yield inspector.once("inspector-updated");
+ }
+
+ info("Waiting for actor features to be detected");
+ yield inspector._detectingActorFeatures;
+
+ yield registerTestActor(toolbox.target.client);
+ let testActor = yield getTestActor(toolbox);
+
+ return {toolbox, inspector, testActor};
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the one of the sidebar
+ * tabs selected.
+ *
+ * @param {String} id
+ * The ID of the sidebar tab to be opened
+ * @return a promise that resolves when the inspector is ready and the tab is
+ * visible and ready
+ */
+var openInspectorSidebarTab = Task.async(function* (id) {
+ let {toolbox, inspector, testActor} = yield openInspector();
+
+ info("Selecting the " + id + " sidebar");
+ inspector.sidebar.select(id);
+
+ return {
+ toolbox,
+ inspector,
+ testActor
+ };
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the rule-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the rule view
+ * is visible and ready
+ */
+function openRuleView() {
+ return openInspectorSidebarTab("ruleview").then(data => {
+ return {
+ toolbox: data.toolbox,
+ inspector: data.inspector,
+ testActor: data.testActor,
+ view: data.inspector.ruleview.view
+ };
+ });
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the computed-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the computed
+ * view is visible and ready
+ */
+function openComputedView() {
+ return openInspectorSidebarTab("computedview").then(data => {
+ return {
+ toolbox: data.toolbox,
+ inspector: data.inspector,
+ testActor: data.testActor,
+ view: data.inspector.computedview.view
+ };
+ });
+}
+
+/**
+ * Select the rule view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ * The opened inspector panel
+ * @return {CssRuleView} the rule view
+ */
+function selectRuleView(inspector) {
+ inspector.sidebar.select("ruleview");
+ return inspector.ruleview.view;
+}
+
+/**
+ * Select the computed view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ * The opened inspector panel
+ * @return {CssComputedView} the computed view
+ */
+function selectComputedView(inspector) {
+ inspector.sidebar.select("computedview");
+ return inspector.computedview.view;
+}
+
+/**
+ * Get the NodeFront for a node that matches a given css selector, via the
+ * protocol.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves to the NodeFront instance
+ */
+function getNodeFront(selector, {walker}) {
+ if (selector._form) {
+ return selector;
+ }
+ return walker.querySelector(walker.rootNode, selector);
+}
+
+/**
+ * Set the inspector's current selection to the first match of the given css
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @param {String} reason Defaults to "test" which instructs the inspector not
+ * to highlight the node upon selection
+ * @return {Promise} Resolves when the inspector is updated with the new node
+ */
+var selectNode = Task.async(function* (selector, inspector, reason = "test") {
+ info("Selecting the node for '" + selector + "'");
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let updated = inspector.once("inspector-updated");
+ inspector.selection.setNodeFront(nodeFront, reason);
+ yield updated;
+});
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -88,22 +88,16 @@ function tunnelToInnerBrowser(outer, inn
// Replace the outer browser's native messageManager with a message manager tunnel
// which we can use to route messages of interest to the inner browser instead.
// Note: The _actual_ messageManager accessible from
// `browser.frameLoader.messageManager` is not overridable and is left unchanged.
// Only the XBL getter `browser.messageManager` is overridden. Browser UI code
// always uses this getter instead of `browser.frameLoader.messageManager` directly,
// so this has the effect of overriding the message manager for browser UI code.
mmTunnel = new MessageManagerTunnel(outer, inner);
- Object.defineProperty(outer, "messageManager", {
- value: mmTunnel,
- writable: false,
- configurable: true,
- enumerable: true,
- });
// We are tunneling to an inner browser with a specific remoteness, so it is simpler
// for the logic of the browser UI to assume this tab has taken on that remoteness,
// even though it's not true. Since the actions the browser UI performs are sent
// down to the inner browser by this tunnel, the tab's remoteness effectively is the
// remoteness of the inner browser.
Object.defineProperty(outer, "isRemoteBrowser", {
get() {
@@ -206,17 +200,16 @@ function tunnelToInnerBrowser(outer, inn
// Reset the XBL binding back to the default.
outer.destroy();
outer.style.MozBinding = "";
// Reset overridden XBL properties and methods. Deleting the override
// means it will fallback to the original XBL binding definitions which
// are on the prototype.
- delete outer.messageManager;
delete outer.isRemoteBrowser;
delete outer.hasContentOpener;
delete outer.docShellIsActive;
delete outer.setDocShellIsActiveAndForeground;
mmTunnel.destroy();
mmTunnel = null;
@@ -263,44 +256,51 @@ function copyPermanentKey(outer, inner)
* without having to touch the original code paths that use them.
*/
function MessageManagerTunnel(outer, inner) {
if (outer.isRemoteBrowser) {
throw new Error("The outer browser must be non-remote.");
}
this.outer = outer;
this.inner = inner;
+ this.tunneledMessageNames = new Set();
this.init();
}
MessageManagerTunnel.prototype = {
/**
* Most message manager methods are left alone and are just passed along to
- * the outer browser's real message manager. `sendAsyncMessage` is only one
- * with special behavior.
+ * the outer browser's real message manager.
*/
PASS_THROUGH_METHODS: [
- "addMessageListener",
- "loadFrameScript",
"killChild",
"assertPermission",
"assertContainApp",
"assertAppHasPermission",
"assertAppHasStatus",
"removeDelayedFrameScript",
"getDelayedFrameScripts",
"loadProcessScript",
"removeDelayedProcessScript",
"getDelayedProcessScripts",
- "removeMessageListener",
"addWeakMessageListener",
"removeWeakMessageListener",
],
+ /**
+ * The following methods are overridden with special behavior while tunneling.
+ */
+ OVERRIDDEN_METHODS: [
+ "loadFrameScript",
+ "addMessageListener",
+ "removeMessageListener",
+ "sendAsyncMessage",
+ ],
+
OUTER_TO_INNER_MESSAGES: [
// Messages sent from remote-browser.xml
"Browser:PurgeSessionHistory",
"InPermitUnload",
"PermitUnload",
// Messages sent from browser.js
"Browser:Reload",
// Messages sent from SelectParentHelper.jsm
@@ -333,69 +333,190 @@ MessageManagerTunnel.prototype = {
"PageVisibility:Hide",
"PageVisibility:Show",
// Messages sent to SessionStore.jsm
"SessionStore:update",
// Messages sent to BrowserTestUtils.jsm
"browser-test-utils:loadEvent",
],
+ OUTER_TO_INNER_MESSAGE_PREFIXES: [
+ // Messages sent from DevTools
+ "debug:",
+ ],
+
+ INNER_TO_OUTER_MESSAGE_PREFIXES: [
+ // Messages sent to DevTools
+ "debug:",
+ ],
+
+ OUTER_TO_INNER_FRAME_SCRIPTS: [
+ // DevTools server for OOP frames
+ "resource://devtools/server/child.js"
+ ],
+
get outerParentMM() {
+ if (!this.outer.frameLoader) {
+ return null;
+ }
return this.outer.frameLoader.messageManager;
},
get outerChildMM() {
// This is only possible because we require the outer browser to be
// non-remote, so we're able to reach into its window and use the child
// side message manager there.
let docShell = this.outer.frameLoader.docShell;
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
},
get innerParentMM() {
+ if (!this.inner.frameLoader) {
+ return null;
+ }
return this.inner.frameLoader.messageManager;
},
+ init() {
+ for (let method of this.PASS_THROUGH_METHODS) {
+ // Workaround bug 449811 to ensure a fresh binding each time through the loop
+ let _method = method;
+ this[_method] = (...args) => {
+ if (!this.outerParentMM) {
+ return null;
+ }
+ return this.outerParentMM[_method](...args);
+ };
+ }
+
+ for (let name of this.INNER_TO_OUTER_MESSAGES) {
+ this.innerParentMM.addMessageListener(name, this);
+ this.tunneledMessageNames.add(name);
+ }
+
+ Services.obs.addObserver(this, "message-manager-close", false);
+
+ // Replace the outer browser's messageManager with this tunnel
+ Object.defineProperty(this.outer, "messageManager", {
+ value: this,
+ writable: false,
+ configurable: true,
+ enumerable: true,
+ });
+ },
+
+ destroy() {
+ if (this.destroyed) {
+ return;
+ }
+ this.destroyed = true;
+ debug("Destroy tunnel");
+
+ Services.obs.removeObserver(this, "message-manager-close");
+
+ // Reset the messageManager. Deleting the override means it will fallback to the
+ // original XBL binding definitions which are on the prototype.
+ delete this.outer.messageManager;
+
+ for (let name of this.tunneledMessageNames) {
+ this.innerParentMM.removeMessageListener(name, this);
+ }
+
+ // Some objects may have cached this tunnel as the messageManager for a frame. To
+ // ensure it keeps working after tunnel close, rewrite the overidden methods as pass
+ // through methods.
+ for (let method of this.OVERRIDDEN_METHODS) {
+ // Workaround bug 449811 to ensure a fresh binding each time through the loop
+ let _method = method;
+ this[_method] = (...args) => {
+ if (!this.outerParentMM) {
+ return null;
+ }
+ return this.outerParentMM[_method](...args);
+ };
+ }
+ },
+
+ observe(subject, topic, data) {
+ if (topic != "message-manager-close") {
+ return;
+ }
+ if (subject == this.innerParentMM) {
+ debug("Inner messageManager has closed");
+ this.destroy();
+ }
+ },
+
+ loadFrameScript(url, ...args) {
+ debug(`Calling loadFrameScript for ${url}`);
+
+ if (!this.OUTER_TO_INNER_FRAME_SCRIPTS.includes(url)) {
+ debug(`Should load ${url} into inner?`);
+ this.outerParentMM.loadFrameScript(url, ...args);
+ return;
+ }
+
+ debug(`Load ${url} into inner`);
+ this.innerParentMM.loadFrameScript(url, ...args);
+ },
+
+ addMessageListener(name, ...args) {
+ debug(`Calling addMessageListener for ${name}`);
+
+ debug(`Add outer listener for ${name}`);
+ // Add an outer listener, just like a simple pass through
+ this.outerParentMM.addMessageListener(name, ...args);
+
+ // If the message name is part of a prefix we're tunneling, we also need to add the
+ // tunnel as an inner listener.
+ if (this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix))) {
+ debug(`Add inner listener for ${name}`);
+ this.innerParentMM.addMessageListener(name, this);
+ this.tunneledMessageNames.add(name);
+ }
+ },
+
+ removeMessageListener(name, ...args) {
+ debug(`Calling removeMessageListener for ${name}`);
+
+ debug(`Remove outer listener for ${name}`);
+ // Remove an outer listener, just like a simple pass through
+ this.outerParentMM.removeMessageListener(name, ...args);
+
+ // Leave the tunnel as an inner listener for the case of prefix messages to avoid
+ // tracking counts of add calls. The inner listener will get removed on destroy.
+ },
+
sendAsyncMessage(name, ...args) {
debug(`Calling sendAsyncMessage for ${name}`);
- if (!this.OUTER_TO_INNER_MESSAGES.includes(name)) {
+ if (!this._shouldTunnelOuterToInner(name)) {
debug(`Should ${name} go to inner?`);
this.outerParentMM.sendAsyncMessage(name, ...args);
return;
}
debug(`${name} outer -> inner`);
this.innerParentMM.sendAsyncMessage(name, ...args);
},
- init() {
- for (let method of this.PASS_THROUGH_METHODS) {
- // Workaround bug 449811 to ensure a fresh binding each time through the loop
- let _method = method;
- this[_method] = (...args) => {
- return this.outerParentMM[_method](...args);
- };
- }
-
- for (let message of this.INNER_TO_OUTER_MESSAGES) {
- this.innerParentMM.addMessageListener(message, this);
- }
- },
-
- destroy() {
- for (let message of this.INNER_TO_OUTER_MESSAGES) {
- this.innerParentMM.removeMessageListener(message, this);
- }
- },
-
receiveMessage({ name, data, objects, principal }) {
- if (!this.INNER_TO_OUTER_MESSAGES.includes(name)) {
+ if (!this._shouldTunnelInnerToOuter(name)) {
debug(`Received unexpected message ${name}`);
return;
}
debug(`${name} inner -> outer`);
this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
},
+ _shouldTunnelOuterToInner(name) {
+ return this.OUTER_TO_INNER_MESSAGES.includes(name) ||
+ this.OUTER_TO_INNER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+ },
+
+ _shouldTunnelInnerToOuter(name) {
+ return this.INNER_TO_OUTER_MESSAGES.includes(name) ||
+ this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+ },
+
};
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -5,26 +5,30 @@ subsuite = devtools
skip-if = !e10s
support-files =
devices.json
doc_page_state.html
head.js
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/framework/test/shared-redux-head.js
+ !/devtools/client/inspector/test/shared-head.js
+ !/devtools/client/shared/test/test-actor-registry.js
[browser_device_modal_error.js]
[browser_device_modal_exit.js]
[browser_device_modal_submit.js]
[browser_device_width.js]
[browser_exit_button.js]
[browser_frame_script_active.js]
[browser_menu_item_01.js]
[browser_menu_item_02.js]
[browser_mouse_resize.js]
[browser_navigation.js]
[browser_page_state.js]
[browser_resize_cmd.js]
-skip-if = true # GCLI target confused after swap, will fix in bug 1240907
+skip-if = true # GCLI target confused after swap, will fix in bug 1240912
[browser_screenshot_button.js]
[browser_shutdown_close_sync.js]
+[browser_toolbox_computed_view.js]
+[browser_toolbox_rule_view.js]
[browser_touch_simulation.js]
[browser_viewport_basics.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the computed-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+ "div {" +
+ " width: 500px;" +
+ " height: 10px;" +
+ " background: purple;" +
+ "} " +
+ "@media screen and (max-width: 200px) {" +
+ " div { " +
+ " width: 100px;" +
+ " }" +
+ "};" +
+ "</style><div></div></html>";
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+ info("Open the responsive design mode and set its size to 500x500 to start");
+ yield setViewportSize(ui, manager, 500, 500);
+
+ info("Open the inspector, computed-view and select the test node");
+ let { inspector, view } = yield openComputedView();
+ yield selectNode("div", inspector);
+
+ info("Try shrinking the viewport and checking the applied styles");
+ yield testShrink(view, inspector, ui, manager);
+
+ info("Try growing the viewport and checking the applied styles");
+ yield testGrow(view, inspector, ui, manager);
+
+ yield closeToolbox();
+});
+
+function* testShrink(computedView, inspector, ui, manager) {
+ is(computedWidth(computedView), "500px", "Should show 500px initially.");
+
+ let onRefresh = inspector.once("computed-view-refreshed");
+ yield setViewportSize(ui, manager, 100, 100);
+ yield onRefresh;
+
+ is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
+}
+
+function* testGrow(computedView, inspector, ui, manager) {
+ let onRefresh = inspector.once("computed-view-refreshed");
+ yield setViewportSize(ui, manager, 500, 500);
+ yield onRefresh;
+
+ is(computedWidth(computedView), "500px", "Should be 500px after growing.");
+}
+
+function computedWidth(computedView) {
+ for (let prop of computedView.propertyViews) {
+ if (prop.name === "width") {
+ return prop.valueNode.textContent;
+ }
+ }
+ return null;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the rule-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+ "div {" +
+ " width: 500px;" +
+ " height: 10px;" +
+ " background: purple;" +
+ "} " +
+ "@media screen and (max-width: 200px) {" +
+ " div { " +
+ " width: 100px;" +
+ " }" +
+ "};" +
+ "</style><div></div></html>";
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+});
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+ info("Open the responsive design mode and set its size to 500x500 to start");
+ yield setViewportSize(ui, manager, 500, 500);
+
+ info("Open the inspector, rule-view and select the test node");
+ let { inspector, view } = yield openRuleView();
+ yield selectNode("div", inspector);
+
+ info("Try shrinking the viewport and checking the applied styles");
+ yield testShrink(view, ui, manager);
+
+ info("Try growing the viewport and checking the applied styles");
+ yield testGrow(view, ui, manager);
+
+ info("Check that ESC still opens the split console");
+ yield testEscapeOpensSplitConsole(inspector);
+
+ yield closeToolbox();
+});
+
+function* testShrink(ruleView, ui, manager) {
+ is(numberOfRules(ruleView), 2, "Should have two rules initially.");
+
+ info("Resize to 100x100 and wait for the rule-view to update");
+ let onRefresh = ruleView.once("ruleview-refreshed");
+ yield setViewportSize(ui, manager, 100, 100);
+ yield onRefresh;
+
+ is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
+}
+
+function* testGrow(ruleView, ui, manager) {
+ info("Resize to 500x500 and wait for the rule-view to update");
+ let onRefresh = ruleView.once("ruleview-refreshed");
+ yield setViewportSize(ui, manager, 500, 500);
+ yield onRefresh;
+
+ is(numberOfRules(ruleView), 2, "Should have two rules after growing.");
+}
+
+function* testEscapeOpensSplitConsole(inspector) {
+ ok(!inspector._toolbox._splitConsole, "Console is not split.");
+
+ info("Press escape");
+ let onSplit = inspector._toolbox.once("split-console");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield onSplit;
+
+ ok(inspector._toolbox._splitConsole, "Console is split after pressing ESC.");
+}
+
+function numberOfRules(ruleView) {
+ return ruleView.element.querySelectorAll(".ruleview-code").length;
+}
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -2,29 +2,40 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../../framework/test/shared-head.js */
/* import-globals-from ../../../framework/test/shared-redux-head.js */
/* import-globals-from ../../../commandline/test/helpers.js */
+/* import-globals-from ../../../inspector/test/shared-head.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
// Import the GCLI test helper
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
this);
+// Import helpers registering the test-actor in remote targets
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
+ this);
+
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+ this);
+
const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
SimpleTest.requestCompleteLog();
SimpleTest.waitForExplicitFinish();
DevToolsUtils.testing = true;
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
Services.prefs.setCharPref("devtools.devices.url",
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -49,16 +49,17 @@ support-files =
doc_uncached.css
doc_uncached.html
doc_xulpage.xul
sync.html
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/shared/test/head.js
!/devtools/client/inspector/test/head.js
+ !/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
[browser_styleeditor_autocomplete.js]
[browser_styleeditor_autocomplete-disabled.js]
[browser_styleeditor_bug_740541_iframes.js]
[browser_styleeditor_bug_851132_middle_click.js]
[browser_styleeditor_bug_870339.js]
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -2244,18 +2244,20 @@ RemoteBrowserTabActor.prototype = {
this._conn, this._browser, onDestroy);
return connect.then(form => {
this._form = form;
return this;
});
},
get _mm() {
- return this._browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
- .messageManager;
+ // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+ // or else fallback to asking the frameLoader itself.
+ return this._browser.messageManager ||
+ this._browser.frameLoader.messageManager;
},
update() {
// If the child happens to be crashed/close/detach, it won't have _form set,
// so only request form update if some code is still listening on the other
// side.
if (this._form) {
let deferred = promise.defer();
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -977,17 +977,19 @@ var DebuggerServer = {
* related TabActor)
* @return object
* A promise object that is resolved once the connection is
* established.
*/
connectToChild(connection, frame, onDestroy) {
let deferred = SyncPromise.defer();
- let mm = frame.frameLoader.messageManager;
+ // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+ // or else fallback to asking the frameLoader itself.
+ let mm = frame.messageManager || frame.frameLoader.messageManager;
mm.loadFrameScript("resource://devtools/server/child.js", false);
this._childMessageManagers.add(mm);
let actor, childTransport;
let prefix = connection.allocID("child");
let netMonitor = null;
// provides hook to actor modules that need to exchange messages
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -17,17 +17,17 @@
factory.call(this, require, this);
} else {
// Cu.import
const Cu = Components.utils;
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
factory.call(this, require, this);
}
}).call(this, function (require, exports) {
- const { Cc, Ci, Cr, CC } = require("chrome");
+ const { Cc, Cr, CC } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn, dumpv } = DevToolsUtils;
const StreamUtils = require("devtools/shared/transport/stream-utils");
const { Packet, JSONPacket, BulkPacket } =
require("devtools/shared/transport/packets");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/event-emitter");
@@ -709,17 +709,17 @@
* multiple servers running in the same child process.
*
* This transport exchanges messages named 'debug:<prefix>:packet', where
* <prefix> is |prefix|, whose data is the protocol packet.
*/
function ChildDebuggerTransport(sender, prefix) {
EventEmitter.decorate(this);
- this._sender = sender.QueryInterface(Ci.nsIMessageSender);
+ this._sender = sender;
this._messageName = "debug:" + prefix + ":packet";
}
/*
* To avoid confusion, we use 'message' to mean something that
* nsIMessageSender conveys, and 'packet' to mean a remote debugging
* protocol packet.
*/
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -76,18 +76,35 @@ function matchRequest(channel, filters)
break;
}
win = win.parent;
}
}
if (filters.topFrame) {
let topFrame = NetworkHelper.getTopFrameForRequest(channel);
- if (topFrame && topFrame === filters.topFrame) {
- return true;
+ while (topFrame) {
+ // In the normal case, a topFrame filter should match the request's topFrame if it
+ // will match at all.
+ if (topFrame === filters.topFrame) {
+ return true;
+ }
+ // As a stop gap approach for RDM, where `filters.topFrame` will be the
+ // <xul:browser> frame for an entire tab and the request's `topFrame` is the
+ // <iframe mozbrower> that triggered the request, we try to climb up parent frames
+ // above the request's `topFrame` to see if they might also match the filter.
+ // In bug 1240912, we want to rework this, since we don't really want to be passing
+ // a frame down to the network monitor.
+ if (!topFrame.ownerGlobal) {
+ break;
+ }
+ topFrame = topFrame.ownerGlobal
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .containerElement;
}
}
if (filters.appId) {
let appId = NetworkHelper.getAppIdForRequest(channel);
if (appId && appId == filters.appId) {
return true;
}
@@ -1578,18 +1595,19 @@ NetworkEventActorProxy.prototype = {
* @constructor
* @param nsIDOMElement frame
* The browser frame to work with (mozbrowser).
* @param string id
* Instance identifier to use for messages.
*/
function NetworkMonitorManager(frame, id) {
this.id = id;
- let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
- .messageManager;
+ // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+ // or else fallback to asking the frameLoader itself.
+ let mm = frame.messageManager || frame.frameLoader.messageManager;
this.messageManager = mm;
this.frame = frame;
this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
this.onNetworkEvent = this.onNetworkEvent.bind(this);
mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
}
exports.NetworkMonitorManager = NetworkMonitorManager;