diff --git a/browser/components/loop/test/desktop-local/panel_test.js b/browser/components/loop/test/desktop-local/panel_test.js
index 82a14639601e..ff967187a805 100644
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -60,7 +60,8 @@ describe("loop.panel", function() {
},
on: sandbox.stub()
},
- confirm: sandbox.stub()
+ confirm: sandbox.stub(),
+ notifyUITour: sandbox.stub()
};
document.mozL10n.initialize(navigator.mozLoop);
diff --git a/browser/components/loop/test/mochitest/browser_toolbarbutton.js b/browser/components/loop/test/mochitest/browser_toolbarbutton.js
index a28d2b1e6955..f5dc1a9cd268 100644
--- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js
+++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js
@@ -11,6 +11,17 @@ Components.utils.import("resource://gre/modules/Promise.jsm", this);
const {LoopRoomsInternal} = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
+const fxASampleToken = {
+ token_type: "bearer",
+ access_token: "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
+ scope: "profile"
+};
+
+const fxASampleProfile = {
+ email: "test@example.com",
+ uid: "abcd1234"
+};
+
registerCleanupFunction(function*() {
MozLoopService.doNotDisturb = false;
MozLoopServiceInternal.fxAOAuthProfile = null;
@@ -18,6 +29,42 @@ registerCleanupFunction(function*() {
Services.prefs.clearUserPref("loop.gettingStarted.seen");
});
+add_task(function* test_LoopUI_getters() {
+ Assert.ok(LoopUI.panel, "LoopUI panel element should be set");
+ Assert.strictEqual(LoopUI.browser, null, "Browser element should not be there yet");
+ Assert.strictEqual(LoopUI.selectedTab, null, "No tab should be selected yet");
+
+ // Load and show the Loop panel for the very first time this session.
+ yield loadLoopPanel();
+ Assert.ok(LoopUI.browser, "Browser element should be there");
+ Assert.strictEqual(LoopUI.selectedTab, "rooms", "Initially the rooms tab should be selected");
+
+ // Hide the panel.
+ yield LoopUI.togglePanel();
+ Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
+
+ // Make sure the contacts tab shows up by simulating a login.
+ MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
+ MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
+ yield MozLoopServiceInternal.notifyStatusChanged("login");
+
+ // Programmatically select the contacts tab.
+ yield LoopUI.togglePanel(null, "contacts");
+ Assert.strictEqual(LoopUI.selectedTab, "contacts", "Contacts tab should be selected now");
+
+ // Switch back to the rooms tab.
+ yield LoopUI.openCallPanel(null, "rooms");
+ Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should be selected now");
+
+ // Hide the panel.
+ yield LoopUI.togglePanel();
+
+ // Logout to prevent interfering with the tests after this one.
+ MozLoopServiceInternal.fxAOAuthTokenData =
+ MozLoopServiceInternal.fxAOAuthProfile = null;
+ yield MozLoopServiceInternal.notifyStatusChanged();
+});
+
add_task(function* test_doNotDisturb() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
yield MozLoopService.doNotDisturb = true;
@@ -30,8 +77,8 @@ add_task(function* test_doNotDisturb_with_login() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
yield MozLoopService.doNotDisturb = true;
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is in disabled state");
- MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
- MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
+ MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
+ MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
yield MozLoopServiceInternal.notifyStatusChanged("login");
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
yield loadLoopPanel();
@@ -56,7 +103,7 @@ add_task(function* test_error_with_login() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
yield MozLoopServiceInternal.setError("testing", {});
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state");
- MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
+ MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
MozLoopServiceInternal.notifyStatusChanged("login");
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state");
yield MozLoopServiceInternal.clearError("testing");
@@ -68,8 +115,8 @@ add_task(function* test_error_with_login() {
add_task(function* test_active() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
- MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
- MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
+ MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
+ MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
yield MozLoopServiceInternal.notifyStatusChanged("login");
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
yield loadLoopPanel();
diff --git a/browser/components/preferences/in-content/tests/browser_bug410900.js b/browser/components/preferences/in-content/tests/browser_bug410900.js
index 5b100966dfdc..b445d1d16558 100644
--- a/browser/components/preferences/in-content/tests/browser_bug410900.js
+++ b/browser/components/preferences/in-content/tests/browser_bug410900.js
@@ -6,6 +6,8 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm");
function test() {
waitForExplicitFinish();
+ Services.prefs.setBoolPref("browser.preferences.inContent", true);
+ registerCleanupFunction(() => Services.prefs.clearUserPref("browser.preferences.inContent"));
// Setup a phony handler to ensure the app pane will be populated.
var handler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
diff --git a/browser/components/preferences/in-content/tests/browser_change_app_handler.js b/browser/components/preferences/in-content/tests/browser_change_app_handler.js
index ffd556316cb5..43781bb87b9e 100644
--- a/browser/components/preferences/in-content/tests/browser_change_app_handler.js
+++ b/browser/components/preferences/in-content/tests/browser_change_app_handler.js
@@ -30,7 +30,9 @@ add_task(function*() {
let chooseItem = list.firstChild.querySelector(".choose-app-item");
let dialogLoadedPromise = promiseLoadSubDialog("chrome://global/content/appPicker.xul");
- chooseItem.click();
+ let cmdEvent = win.document.createEvent("xulcommandevent");
+ cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+ chooseItem.dispatchEvent(cmdEvent);
let dialog = yield dialogLoadedPromise;
info("Dialog loaded");
@@ -57,7 +59,9 @@ add_task(function*() {
dialogLoadedPromise = promiseLoadSubDialog("chrome://browser/content/preferences/applicationManager.xul");
let manageItem = list.firstChild.querySelector(".manage-app-item");
- manageItem.click();
+ cmdEvent = win.document.createEvent("xulcommandevent");
+ cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+ manageItem.dispatchEvent(cmdEvent);
dialog = yield dialogLoadedPromise;
info("Dialog loaded the second time");
diff --git a/browser/components/uitour/UITour.jsm b/browser/components/uitour/UITour.jsm
index 270981defcd4..6a3499a64e4c 100644
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -139,23 +139,23 @@ this.UITour = {
["loop-newRoom", {
infoPanelPosition: "leftcenter topright",
query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop-panel-iframe");
- if (!loopBrowser) {
+ let loopUI = aDocument.defaultView.LoopUI;
+ if (loopUI.selectedTab != "rooms") {
return null;
}
// Use the parentElement full-width container of the button so our arrow
// doesn't overlap the panel contents much.
- return loopBrowser.contentDocument.querySelector(".new-room-button").parentElement;
+ return loopUI.browser.contentDocument.querySelector(".new-room-button").parentElement;
},
}],
["loop-roomList", {
infoPanelPosition: "leftcenter topright",
query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop-panel-iframe");
- if (!loopBrowser) {
+ let loopUI = aDocument.defaultView.LoopUI;
+ if (loopUI.selectedTab != "rooms") {
return null;
}
- return loopBrowser.contentDocument.querySelector(".room-list");
+ return loopUI.browser.contentDocument.querySelector(".room-list");
},
}],
["loop-selectedRoomButtons", {
@@ -178,7 +178,7 @@ this.UITour = {
}],
["loop-signInUpLink", {
query: (aDocument) => {
- let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop-panel-iframe");
+ let loopBrowser = aDocument.defaultView.LoopUI.browser;
if (!loopBrowser) {
return null;
}
diff --git a/browser/components/uitour/test/browser_UITour_loop.js b/browser/components/uitour/test/browser_UITour_loop.js
index 88c842211d0b..64bfc69298bb 100644
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ b/browser/components/uitour/test/browser_UITour_loop.js
@@ -11,6 +11,7 @@ let loopPanel = document.getElementById("loop-notification-panel");
Components.utils.import("resource:///modules/UITour.jsm");
const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
+const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
function test() {
UITourTest();
@@ -181,6 +182,53 @@ let tests = [
});
});
},
+ taskify(function* test_panelTabChangeNotifications() {
+ // First make sure the Loop panel looks like we're logged in to have more than
+ // just one tab to switch to.
+ const fxASampleToken = {
+ token_type: "bearer",
+ access_token: "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
+ scope: "profile"
+ };
+ const fxASampleProfile = {
+ email: "test@example.com",
+ uid: "abcd1234"
+ };
+ MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
+ MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
+ yield MozLoopServiceInternal.notifyStatusChanged("login");
+
+ // Show the Loop menu.
+ yield showMenuPromise("loop");
+
+ // Listen for and test the notifications that will arrive from now on.
+ let tabChangePromise = new Promise(resolve => {
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:PanelTabChanged", "Check Loop:PanelTabChanged notification");
+ is(params, "contacts", "Check the tab name param");
+
+ gContentAPI.observe((event, params) => {
+ is(event, "Loop:PanelTabChanged", "Check Loop:PanelTabChanged notification");
+ is(params, "rooms", "Check the tab name param");
+
+ gContentAPI.observe((event, params) => {
+ ok(false, "No more notifications should have arrived");
+ });
+ resolve();
+ });
+ });
+ });
+
+ // Switch to the contacts tab.
+ yield window.LoopUI.openCallPanel(null, "contacts");
+
+ // Logout. The panel tab will switch back to 'rooms'.
+ MozLoopServiceInternal.fxAOAuthTokenData =
+ MozLoopServiceInternal.fxAOAuthProfile = null;
+ yield MozLoopServiceInternal.notifyStatusChanged();
+
+ yield tabChangePromise;
+ }),
runOffline(function test_notifyLoopChatWindowOpenedClosed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
diff --git a/browser/devtools/netmonitor/test/browser_net_filter-01.js b/browser/devtools/netmonitor/test/browser_net_filter-01.js
index 4186baad1917..2d129c483b12 100644
--- a/browser/devtools/netmonitor/test/browser_net_filter-01.js
+++ b/browser/devtools/netmonitor/test/browser_net_filter-01.js
@@ -4,6 +4,22 @@
/**
* Test if filtering items in the network table works correctly.
*/
+const BASIC_REQUESTS = [
+ { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" },
+ { url: "sjs_content-type-test-server.sjs?fmt=css" },
+ { url: "sjs_content-type-test-server.sjs?fmt=js" },
+];
+
+const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=font" },
+ { url: "sjs_content-type-test-server.sjs?fmt=image" },
+ { url: "sjs_content-type-test-server.sjs?fmt=audio" },
+ { url: "sjs_content-type-test-server.sjs?fmt=video" },
+]);
+
+const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=flash" },
+]);
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
@@ -207,6 +223,7 @@ function test() {
return promise.resolve(null);
}
- aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
+ loadCommonFrameScript();
+ performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH);
});
}
diff --git a/browser/devtools/netmonitor/test/browser_net_filter-02.js b/browser/devtools/netmonitor/test/browser_net_filter-02.js
index 2ab70bee08bd..aafa039d214b 100644
--- a/browser/devtools/netmonitor/test/browser_net_filter-02.js
+++ b/browser/devtools/netmonitor/test/browser_net_filter-02.js
@@ -4,6 +4,22 @@
/**
* Test if filtering items in the network table works correctly with new requests.
*/
+const BASIC_REQUESTS = [
+ { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" },
+ { url: "sjs_content-type-test-server.sjs?fmt=css" },
+ { url: "sjs_content-type-test-server.sjs?fmt=js" },
+];
+
+const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=font" },
+ { url: "sjs_content-type-test-server.sjs?fmt=image" },
+ { url: "sjs_content-type-test-server.sjs?fmt=audio" },
+ { url: "sjs_content-type-test-server.sjs?fmt=video" },
+]);
+
+const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=flash" },
+]);
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
@@ -37,7 +53,7 @@ function test() {
})
.then(() => {
info("Performing more requests.");
- aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
+ performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH);
return waitForNetworkEvents(aMonitor, 8);
})
.then(() => {
@@ -47,7 +63,7 @@ function test() {
})
.then(() => {
info("Performing more requests.");
- aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
+ performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH);
return waitForNetworkEvents(aMonitor, 8);
})
.then(() => {
@@ -169,6 +185,7 @@ function test() {
return promise.resolve(null);
}
- aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
+ loadCommonFrameScript();
+ performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH);
});
}
diff --git a/browser/devtools/netmonitor/test/browser_net_filter-03.js b/browser/devtools/netmonitor/test/browser_net_filter-03.js
index fc177271dc65..8e595b69bcd8 100644
--- a/browser/devtools/netmonitor/test/browser_net_filter-03.js
+++ b/browser/devtools/netmonitor/test/browser_net_filter-03.js
@@ -5,6 +5,22 @@
* Test if filtering items in the network table works correctly with new requests
* and while sorting is enabled.
*/
+const BASIC_REQUESTS = [
+ { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" },
+ { url: "sjs_content-type-test-server.sjs?fmt=css" },
+ { url: "sjs_content-type-test-server.sjs?fmt=js" },
+];
+
+const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=font" },
+ { url: "sjs_content-type-test-server.sjs?fmt=image" },
+ { url: "sjs_content-type-test-server.sjs?fmt=audio" },
+ { url: "sjs_content-type-test-server.sjs?fmt=video" },
+]);
+
+const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=flash" },
+]);
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
@@ -44,7 +60,7 @@ function test() {
})
.then(() => {
info("Performing more requests.");
- aDebuggee.performRequests('{ "getMedia": true }');
+ performRequestsInContent(REQUESTS_WITH_MEDIA);
return waitForNetworkEvents(aMonitor, 7);
})
.then(() => {
@@ -55,7 +71,7 @@ function test() {
})
.then(() => {
info("Performing more requests.");
- aDebuggee.performRequests('{ "getMedia": true }');
+ performRequestsInContent(REQUESTS_WITH_MEDIA);
return waitForNetworkEvents(aMonitor, 7);
})
.then(() => {
@@ -167,7 +183,15 @@ function test() {
return promise.resolve(null);
}
- let str = "'
'" + new Array(10).join(Math.random(10)) + "'
'";
- aDebuggee.performRequests('{ "htmlContent": "' + str + '", "getMedia": true }');
+ // The test assumes that the first HTML request here has a longer response
+ // body than the other HTML requests performed later during the test.
+ let requests = Cu.cloneInto(REQUESTS_WITH_MEDIA, {});
+
+ let newres = "res=
" + new Array(10).join(Math.random(10)) + "
";
+ requests[0].url = requests[0].url.replace("res=undefined", newres);
+
+ loadCommonFrameScript();
+ performRequestsInContent(requests);
+
});
}
diff --git a/browser/devtools/netmonitor/test/browser_net_filter-04.js b/browser/devtools/netmonitor/test/browser_net_filter-04.js
index 1840aec64671..64f6e58f5501 100644
--- a/browser/devtools/netmonitor/test/browser_net_filter-04.js
+++ b/browser/devtools/netmonitor/test/browser_net_filter-04.js
@@ -5,6 +5,23 @@
* Tests if invalid filter types are sanitized when loaded from the preferences.
*/
+const BASIC_REQUESTS = [
+ { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" },
+ { url: "sjs_content-type-test-server.sjs?fmt=css" },
+ { url: "sjs_content-type-test-server.sjs?fmt=js" },
+];
+
+const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=font" },
+ { url: "sjs_content-type-test-server.sjs?fmt=image" },
+ { url: "sjs_content-type-test-server.sjs?fmt=audio" },
+ { url: "sjs_content-type-test-server.sjs?fmt=video" },
+]);
+
+const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([
+ { url: "sjs_content-type-test-server.sjs?fmt=flash" },
+]);
+
function test() {
Services.prefs.setCharPref("devtools.netmonitor.filters", '["js", "bogus"]');
@@ -36,6 +53,7 @@ function test() {
});
});
- aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
+ loadCommonFrameScript();
+ performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH);
});
}
diff --git a/browser/devtools/netmonitor/test/head.js b/browser/devtools/netmonitor/test/head.js
index 561ae17aeb61..b2145be6cd73 100644
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -49,6 +49,8 @@ const CORS_SJS_PATH = "/browser/browser/devtools/netmonitor/test/sjs_cors-test-s
const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
+const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js"
+
gDevTools.testing = true;
SimpleTest.registerCleanupFunction(() => {
gDevTools.testing = false;
@@ -416,3 +418,77 @@ function testFilterButtonsCustom(aMonitor, aIsChecked) {
}
}
}
+
+/**
+ * Loads shared/frame-script-utils.js in the specified tab.
+ *
+ * @param tab
+ * Optional tab to load the frame script in. Defaults to the current tab.
+ */
+function loadCommonFrameScript(tab) {
+ let browser = tab ? tab.linkedBrowser : gBrowser.selectedBrowser;
+
+ browser.messageManager.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
+}
+
+/**
+ * Perform the specified requests in the context of the page content.
+ *
+ * @param Array requests
+ * An array of objects specifying the requests to perform. See
+ * shared/frame-script-utils.js for more information.
+ *
+ * @return A promise that resolves once the requests complete.
+ */
+function performRequestsInContent(requests) {
+ info("Performing requests in the context of the content.");
+ return executeInContent("devtools:test:xhr", requests)
+}
+
+/**
+ * Send an async message to the frame script (chrome -> content) and wait for a
+ * response message with the same name (content -> chrome).
+ *
+ * @param String name
+ * The message name. Should be one of the messages defined
+ * shared/frame-script-utils.js
+ * @param Object data
+ * Optional data to send along
+ * @param Object objects
+ * Optional CPOW objects to send along
+ * @param Boolean expectResponse
+ * If set to false, don't wait for a response with the same name from the
+ * content script. Defaults to true.
+ *
+ * @return Promise
+ * Resolves to the response data if a response is expected, immediately
+ * resolves otherwise
+ */
+function executeInContent(name, data={}, objects={}, expectResponse=true) {
+ let mm = gBrowser.selectedBrowser.messageManager;
+
+ mm.sendAsyncMessage(name, data, objects);
+ if (expectResponse) {
+ return waitForContentMessage(name);
+ } else {
+ return promise.resolve();
+ }
+}
+
+/**
+ * Wait for a content -> chrome message on the message manager (the window
+ * messagemanager is used).
+ * @param {String} name The message name
+ * @return {Promise} A promise that resolves to the response data when the
+ * message has been received
+ */
+function waitForContentMessage(name) {
+ let mm = gBrowser.selectedBrowser.messageManager;
+
+ let def = promise.defer();
+ mm.addMessageListener(name, function onMessage(msg) {
+ mm.removeMessageListener(name, onMessage);
+ def.resolve(msg);
+ });
+ return def.promise;
+}
diff --git a/browser/devtools/shared/frame-script-utils.js b/browser/devtools/shared/frame-script-utils.js
index 5188526ffe1e..a2236a715d8c 100644
--- a/browser/devtools/shared/frame-script-utils.js
+++ b/browser/devtools/shared/frame-script-utils.js
@@ -3,6 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
+const Cu = Components.utils;
+
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+devtools.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
+devtools.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm", "Task");
addMessageListener("devtools:test:history", function ({ data }) {
content.history[data.direction]();
@@ -22,6 +27,79 @@ addMessageListener("devtools:test:console", function ({ data }) {
content.console[method].apply(content.console, data);
});
+/**
+ * Performs a single XMLHttpRequest and returns a promise that resolves once
+ * the request has loaded.
+ *
+ * @param Object data
+ * { method: the request method (default: "GET"),
+ * url: the url to request (default: content.location.href),
+ * body: the request body to send (default: ""),
+ * nocache: append an unique token to the query string (default: true)
+ * }
+ *
+ * @return Promise A promise that's resolved with object
+ * { status: XMLHttpRequest.status,
+ * response: XMLHttpRequest.response }
+ *
+ */
+function promiseXHR(data) {
+ let xhr = new content.XMLHttpRequest();
+
+ let method = data.method || "GET";
+ let url = data.url || content.location.href;
+ let body = data.body || "";
+
+ if (data.nocache) {
+ url += "?devtools-cachebust=" + Math.random();
+ }
+
+ let deferred = promise.defer();
+ xhr.addEventListener("loadend", function loadend(event) {
+ xhr.removeEventListener("loadend", loadend);
+ deferred.resolve({ status: xhr.status, response: xhr.response });
+ });
+
+ xhr.open(method, url);
+ xhr.send(body);
+ return deferred.promise;
+
+}
+
+/**
+ * Performs XMLHttpRequest request(s) in the context of the page. The data
+ * parameter can be either a single object or an array of objects described below.
+ * The requests will be performed one at a time in the order they appear in the data.
+ *
+ * The objects should have following form (any of them can be omitted; defaults
+ * shown below):
+ * {
+ * method: "GET",
+ * url: content.location.href,
+ * body: "",
+ * nocache: true, // Adds a cache busting random token to the URL
+ * }
+ *
+ * The handler will respond with devtools:test:xhr message after all requests
+ * have finished. Following data will be available for each requests
+ * (in the same order as requests):
+ * {
+ * status: XMLHttpRequest.status
+ * response: XMLHttpRequest.response
+ * }
+ */
+addMessageListener("devtools:test:xhr", Task.async(function* ({ data }) {
+ let requests = Array.isArray(data) ? data : [data];
+ let responses = [];
+
+ for (let request of requests) {
+ let response = yield promiseXHR(request);
+ responses.push(response);
+ }
+
+ sendAsyncMessage("devtools:test:xhr", responses);
+}));
+
// To eval in content, look at `evalInDebuggee` in the head.js of canvasdebugger
// for an example.
addMessageListener("devtools:test:eval", function ({ data }) {
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 500eb80f929e..c976bb96d5fa 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -579,6 +579,7 @@
@RESPATH@/components/TCPSocket.manifest
#ifdef MOZ_ACTIVITIES
+@RESPATH@/components/SystemMessageCache.js
@RESPATH@/components/SystemMessageInternal.js
@RESPATH@/components/SystemMessageManager.js
@RESPATH@/components/SystemMessageManager.manifest
diff --git a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
index 00691fe029c8..3c82b20242b0 100644
--- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
@@ -120,7 +120,8 @@ AvStatusToSinkString(BluetoothA2dpConnectionState aState, nsAString& aString)
}
}
-class InitAvrcpResultHandler MOZ_FINAL : public BluetoothAvrcpResultHandler
+class BluetoothA2dpManager::InitAvrcpResultHandler MOZ_FINAL
+ : public BluetoothAvrcpResultHandler
{
public:
InitAvrcpResultHandler(BluetoothProfileResultHandler* aRes)
@@ -155,7 +156,8 @@ private:
nsRefPtr
mRes;
};
-class InitA2dpResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler
+class BluetoothA2dpManager::InitA2dpResultHandler MOZ_FINAL
+ : public BluetoothA2dpResultHandler
{
public:
InitA2dpResultHandler(BluetoothProfileResultHandler* aRes)
@@ -197,7 +199,8 @@ private:
nsRefPtr mRes;
};
-class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothA2dpManager::OnErrorProfileResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
@@ -328,7 +331,8 @@ BluetoothA2dpManager::Get()
return sBluetoothA2dpManager;
}
-class CleanupAvrcpResultHandler MOZ_FINAL : public BluetoothAvrcpResultHandler
+class BluetoothA2dpManager::CleanupAvrcpResultHandler MOZ_FINAL
+ : public BluetoothAvrcpResultHandler
{
public:
CleanupAvrcpResultHandler(BluetoothProfileResultHandler* aRes)
@@ -364,7 +368,8 @@ private:
nsRefPtr mRes;
};
-class CleanupA2dpResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler
+class BluetoothA2dpManager::CleanupA2dpResultHandler MOZ_FINAL
+ : public BluetoothA2dpResultHandler
{
public:
CleanupA2dpResultHandler(BluetoothProfileResultHandler* aRes)
@@ -397,7 +402,8 @@ private:
nsRefPtr mRes;
};
-class CleanupA2dpResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothA2dpManager::CleanupA2dpResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
CleanupA2dpResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
@@ -461,7 +467,8 @@ BluetoothA2dpManager::OnConnectError()
mDeviceAddress.Truncate();
}
-class ConnectResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler
+class BluetoothA2dpManager::ConnectResultHandler MOZ_FINAL
+ : public BluetoothA2dpResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -512,7 +519,8 @@ BluetoothA2dpManager::OnDisconnectError()
mController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
}
-class DisconnectResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler
+class BluetoothA2dpManager::DisconnectResultHandler MOZ_FINAL
+ : public BluetoothA2dpResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
diff --git a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h
index 1c34607a5c68..dbe5c49e3ef7 100644
--- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h
@@ -66,6 +66,15 @@ public:
void GetArtist(nsAString& aArtist);
private:
+ class CleanupA2dpResultHandler;
+ class CleanupA2dpResultHandlerRunnable;
+ class CleanupAvrcpResultHandler;
+ class ConnectResultHandler;
+ class DisconnectResultHandler;
+ class InitA2dpResultHandler;
+ class InitAvrcpResultHandler;
+ class OnErrorProfileResultHandlerRunnable;
+
BluetoothA2dpManager();
void ResetA2dp();
void ResetAvrcp();
diff --git a/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp b/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
index 423b1b8513e6..0690a18295a5 100644
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
@@ -49,7 +49,8 @@ BluetoothGattManager::Get()
return sBluetoothGattManager;
}
-class InitGattResultHandler MOZ_FINAL : public BluetoothGattResultHandler
+class BluetoothGattManager::InitGattResultHandler MOZ_FINAL
+ : public BluetoothGattResultHandler
{
public:
InitGattResultHandler(BluetoothProfileResultHandler* aRes)
@@ -107,7 +108,8 @@ BluetoothGattManager::InitGattInterface(BluetoothProfileResultHandler* aRes)
new InitGattResultHandler(aRes));
}
-class CleanupResultHandler MOZ_FINAL : public BluetoothGattResultHandler
+class BluetoothGattManager::CleanupResultHandler MOZ_FINAL
+ : public BluetoothGattResultHandler
{
public:
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
@@ -136,7 +138,8 @@ private:
nsRefPtr mRes;
};
-class CleanupResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothGattManager::CleanupResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
CleanupResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
diff --git a/dom/bluetooth2/bluedroid/BluetoothGattManager.h b/dom/bluetooth2/bluedroid/BluetoothGattManager.h
index fef3f6bcba0e..1c1d4bfb6379 100644
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.h
@@ -25,6 +25,10 @@ public:
virtual ~BluetoothGattManager();
private:
+ class CleanupResultHandler;
+ class CleanupResultHandlerRunnable;
+ class InitGattResultHandler;
+
BluetoothGattManager();
void HandleShutdown();
diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
index 319660286363..f580018e9f9c 100644
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
@@ -87,7 +87,7 @@ BluetoothOppManager::Observe(nsISupports* aSubject,
return NS_ERROR_UNEXPECTED;
}
-class SendSocketDataTask : public nsRunnable
+class BluetoothOppManager::SendSocketDataTask : public nsRunnable
{
public:
SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
@@ -111,7 +111,7 @@ private:
uint32_t mSize;
};
-class ReadFileTask : public nsRunnable
+class BluetoothOppManager::ReadFileTask : public nsRunnable
{
public:
ReadFileTask(nsIInputStream* aInputStream,
@@ -156,7 +156,7 @@ private:
uint32_t mAvailablePacketSize;
};
-class CloseSocketTask : public Task
+class BluetoothOppManager::CloseSocketTask : public Task
{
public:
CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.h b/dom/bluetooth2/bluedroid/BluetoothOppManager.h
index e26fb759cd94..719c8c37e886 100644
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h
@@ -74,6 +74,10 @@ public:
virtual void OnSocketDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
private:
+ class CloseSocketTask;
+ class ReadFileTask;
+ class SendSocketDataTask;
+
BluetoothOppManager();
bool Init();
void HandleShutdown();
diff --git a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
index 75825ab2bed5..73afc71d8af6 100644
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -260,7 +260,8 @@ BluetoothHfpManager::Init()
return true;
}
-class CleanupInitResultHandler MOZ_FINAL : public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::CleanupInitResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
CleanupInitResultHandler(BluetoothHandsfreeInterface* aInterface,
@@ -309,7 +310,8 @@ private:
nsRefPtr mRes;
};
-class InitResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothHfpManager::InitResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
InitResultHandlerRunnable(CleanupInitResultHandler* aRes)
@@ -328,7 +330,8 @@ private:
nsRefPtr mRes;
};
-class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothHfpManager::OnErrorProfileResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
@@ -413,7 +416,8 @@ BluetoothHfpManager::~BluetoothHfpManager()
hal::UnregisterBatteryObserver(this);
}
-class CleanupResultHandler MOZ_FINAL : public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::CleanupResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
@@ -440,7 +444,8 @@ private:
nsRefPtr mRes;
};
-class DeinitResultHandlerRunnable MOZ_FINAL : public nsRunnable
+class BluetoothHfpManager::DeinitResultHandlerRunnable MOZ_FINAL
+ : public nsRunnable
{
public:
DeinitResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
@@ -590,8 +595,8 @@ BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
}
-class VolumeControlResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::VolumeControlResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -728,7 +733,7 @@ BluetoothHfpManager::HandleShutdown()
sBluetoothHfpManager = nullptr;
}
-class ClccResponseResultHandler MOZ_FINAL
+class BluetoothHfpManager::ClccResponseResultHandler MOZ_FINAL
: public BluetoothHandsfreeResultHandler
{
public:
@@ -765,8 +770,8 @@ BluetoothHfpManager::SendCLCC(Call& aCall, int aIndex)
aCall.mType, new ClccResponseResultHandler());
}
-class FormattedAtResponseResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::FormattedAtResponseResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -785,8 +790,8 @@ BluetoothHfpManager::SendLine(const char* aMessage)
aMessage, new FormattedAtResponseResultHandler());
}
-class AtResponseResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::AtResponseResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -805,8 +810,8 @@ BluetoothHfpManager::SendResponse(BluetoothHandsfreeAtResponse aResponseCode)
aResponseCode, 0, new AtResponseResultHandler());
}
-class PhoneStateChangeResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::PhoneStateChangeResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -837,7 +842,7 @@ BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex)
new PhoneStateChangeResultHandler());
}
-class DeviceStatusNotificationResultHandler MOZ_FINAL
+class BluetoothHfpManager::DeviceStatusNotificationResultHandler MOZ_FINAL
: public BluetoothHandsfreeResultHandler
{
public:
@@ -1093,8 +1098,8 @@ BluetoothHfpManager::ToggleCalls()
nsITelephonyService::CALL_STATE_CONNECTED;
}
-class ConnectAudioResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::ConnectAudioResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -1119,8 +1124,8 @@ BluetoothHfpManager::ConnectSco()
return true;
}
-class DisconnectAudioResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::DisconnectAudioResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -1165,7 +1170,8 @@ BluetoothHfpManager::OnConnectError()
mDeviceAddress.Truncate();
}
-class ConnectResultHandler MOZ_FINAL : public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::ConnectResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
ConnectResultHandler(BluetoothHfpManager* aHfpManager)
@@ -1218,7 +1224,8 @@ BluetoothHfpManager::OnDisconnectError()
mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
}
-class DisconnectResultHandler MOZ_FINAL : public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::DisconnectResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
DisconnectResultHandler(BluetoothHfpManager* aHfpManager)
@@ -1491,8 +1498,8 @@ BluetoothHfpManager::CnumNotification()
SendResponse(HFP_AT_RESPONSE_OK);
}
-class CindResponseResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::CindResponseResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
@@ -1519,8 +1526,8 @@ BluetoothHfpManager::CindNotification()
new CindResponseResultHandler());
}
-class CopsResponseResultHandler MOZ_FINAL
-: public BluetoothHandsfreeResultHandler
+class BluetoothHfpManager::CopsResponseResultHandler MOZ_FINAL
+ : public BluetoothHandsfreeResultHandler
{
public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
diff --git a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
index 1cb15259b1ed..52ea46d12bbb 100644
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
@@ -132,10 +132,27 @@ public:
void KeyPressedNotification() MOZ_OVERRIDE;
private:
+ class AtResponseResultHandler;
class GetVolumeTask;
+ class CindResponseResultHandler;
+ class ClccResponseResultHandler;
+ class CleanupResultHandler;
+ class CleanupInitResultHandler;
class CloseScoTask;
class CloseScoRunnable;
+ class ConnectAudioResultHandler;
+ class ConnectResultHandler;
+ class CopsResponseResultHandler;
+ class DeinitResultHandlerRunnable;
+ class DeviceStatusNotificationResultHandler;
+ class DisconnectAudioResultHandler;
+ class DisconnectResultHandler;
+ class FormattedAtResponseResultHandler;
+ class InitResultHandlerRunnable;
+ class OnErrorProfileResultHandlerRunnable;
+ class PhoneStateChangeResultHandler;
class RespondToBLDNTask;
+ class VolumeControlResultHandler;
friend class BluetoothHfpManagerObserver;
friend class GetVolumeTask;
diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index 0d4e91df2a1c..a5c6871fca02 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2579,6 +2579,16 @@ EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
for (nsIFrame* current = targetFrame; current;
current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
+ // e10s - mark remote content as pannable. This is a work around since
+ // we don't have access to remote frame scroll info here. Apz data may
+ // assist is solving this.
+ if (current && IsRemoteTarget(current->GetContent())) {
+ panDirection = WidgetGestureNotifyEvent::ePanBoth;
+ // We don't know when we reach bounds, so just disable feedback for now.
+ displayPanFeedback = false;
+ break;
+ }
+
nsIAtom* currentFrameType = current->GetType();
// Scrollbars should always be draggable
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
index 456b35268112..df00b5ca32bd 100644
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -91,6 +91,7 @@
#include "nsAnonymousTemporaryFile.h"
#include "nsISpellChecker.h"
#include "nsClipboardProxy.h"
+#include "nsISystemMessageCache.h"
#include "IHistory.h"
#include "nsNetUtil.h"
@@ -530,6 +531,10 @@ InitOnContentProcessCreated()
PostForkPreload();
#endif
+ nsCOMPtr smc =
+ do_GetService("@mozilla.org/system-message-cache;1");
+ NS_WARN_IF(!smc);
+
// This will register cross-process observer.
mozilla::dom::time::InitializeDateCacheCleaner();
}
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index 52e6a69f959d..b799bfc3e224 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -119,6 +119,7 @@
#include "nsISpellChecker.h"
#include "nsIStyleSheet.h"
#include "nsISupportsPrimitives.h"
+#include "nsISystemMessagesInternal.h"
#include "nsITimer.h"
#include "nsIURIFixup.h"
#include "nsIWindowWatcher.h"
@@ -1324,11 +1325,21 @@ ContentParent::ForwardKnownInfo()
#ifdef MOZ_WIDGET_GONK
InfallibleTArray volumeInfo;
nsRefPtr vs = nsVolumeService::GetSingleton();
- if (vs) {
+ if (vs && !mIsForBrowser) {
vs->GetVolumesForIPC(&volumeInfo);
unused << SendVolumes(volumeInfo);
}
#endif /* MOZ_WIDGET_GONK */
+
+ nsCOMPtr systemMessenger =
+ do_GetService("@mozilla.org/system-message-internal;1");
+ if (systemMessenger && !mIsForBrowser) {
+ nsCOMPtr manifestURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(manifestURI), mAppManifestURL);
+ if (NS_SUCCEEDED(rv)) {
+ systemMessenger->RefreshCache(mMessageManager, manifestURI);
+ }
+ }
}
namespace {
diff --git a/dom/messages/SystemMessageCache.js b/dom/messages/SystemMessageCache.js
new file mode 100644
index 000000000000..fcf1fffa6a82
--- /dev/null
+++ b/dom/messages/SystemMessageCache.js
@@ -0,0 +1,84 @@
+/* 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";
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+ "@mozilla.org/childprocessmessagemanager;1",
+ "nsISyncMessageSender");
+
+function debug(aMsg) {
+ // dump("-- SystemMessageCache " + Date.now() + " : " + aMsg + "\n");
+}
+
+const kMessages = ["SystemMessageCache:RefreshCache"];
+
+function SystemMessageCache() {
+ debug("init");
+
+ this._pagesCache = [];
+
+ dump("SystemMessageCache: init");
+ Services.obs.addObserver(this, "xpcom-shutdown", false);
+ kMessages.forEach(function(aMessage) {
+ cpmm.addMessageListener(aMessage, this);
+ }, this);
+}
+
+SystemMessageCache.prototype = {
+
+ observe: function(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "xpcom-shutdown":
+ debug("received xpcom-shutdown");
+ kMessages.forEach(function(aMessage) {
+ cpmm.removeMessageListener(aMessage, this);
+ }, this);
+ Services.obs.removeObserver(this, "xpcom-shutdown");
+ cpmm = null;
+ break;
+ default:
+ break;
+ }
+ },
+
+ receiveMessage: function(aMessage) {
+ switch (aMessage.name) {
+ case "SystemMessageCache:RefreshCache":
+ this._pagesCache = aMessage.data;
+ debug("received RefreshCache");
+ break;
+ default:
+ debug("received unknown message " + aMessage.name);
+ break;
+ }
+ },
+
+ hasPendingMessage: function(aType, aPageURL, aManifestURL) {
+ let hasMessage = this._pagesCache.some(function(aPage) {
+ if (aPage.type === aType &&
+ aPage.pageURL === aPageURL &&
+ aPage.manifestURL === aManifestURL) {
+ return true;
+ }
+ return false;
+ }, this);
+ debug("hasPendingMessage " + aType + " " + aPageURL + " " +
+ aManifestURL + ": " + hasMessage);
+ return hasMessage;
+ },
+
+ classID: Components.ID("{5a19d86a-21e5-4ac8-9634-8c364c73f87f}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessageCache,
+ Ci.nsIMessageListener,
+ Ci.nsIObserver])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SystemMessageCache]);
diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js
index c6305701cb5e..7cb25228c217 100644
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -40,14 +40,13 @@ try {
}
const kMessages =["SystemMessageManager:GetPendingMessages",
- "SystemMessageManager:HasPendingMessages",
"SystemMessageManager:Register",
"SystemMessageManager:Unregister",
"SystemMessageManager:Message:Return:OK",
"SystemMessageManager:AskReadyToRegister",
"SystemMessageManager:HandleMessagesDone",
"SystemMessageManager:HandleMessageDone",
- "child-process-shutdown"]
+ "child-process-shutdown"];
function debug(aMsg) {
// dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
@@ -178,6 +177,19 @@ SystemMessageInternal.prototype = {
return page;
},
+ _findCacheForApp: function(aManifestURL) {
+ let cache = [];
+ this._pages.forEach(function(aPage) {
+ if (aPage.manifestURL === aManifestURL &&
+ aPage.pendingMessages.length != 0) {
+ cache.push({ type: aPage.type,
+ pageURL: aPage.pageURL,
+ manifestURL: aPage.manifestURL });
+ }
+ });
+ return cache;
+ },
+
sendMessage: function(aType, aMessage, aPageURI, aManifestURI, aExtra) {
return new Promise((aResolve, aReject) => {
this.sendMessageInternal(aType, aMessage, aPageURI, aManifestURI, aExtra,
@@ -349,6 +361,18 @@ SystemMessageInternal.prototype = {
pendingMessages: [] });
},
+ refreshCache: function(aChildMM, aManifestURI) {
+ if (!aManifestURI) {
+ throw Cr.NS_ERROR_INVALID_ARG;
+ }
+ this._refreshCacheInternal(aChildMM, aManifestURI.spec);
+ },
+
+ _refreshCacheInternal: function(aChildMM, aManifestURL) {
+ let cache = this._findCacheForApp(aManifestURL);
+ aChildMM.sendAsyncMessage("SystemMessageCache:RefreshCache", cache);
+ },
+
_findTargetIndex: function(aTargets, aTarget) {
if (!aTargets || !aTarget) {
return -1;
@@ -417,7 +441,6 @@ SystemMessageInternal.prototype = {
// TODO: fix bug 988142 to re-enable.
// "SystemMessageManager:Unregister",
"SystemMessageManager:GetPendingMessages",
- "SystemMessageManager:HasPendingMessages",
"SystemMessageManager:Message:Return:OK",
"SystemMessageManager:HandleMessagesDone",
"SystemMessageManager:HandleMessageDone"].indexOf(aMessage.name) != -1) {
@@ -456,6 +479,7 @@ SystemMessageInternal.prototype = {
}
}
+ this._refreshCacheInternal(aMessage.target, msg.manifestURL);
debug("listeners for " + msg.manifestURL +
" innerWinID " + msg.innerWindowID);
break;
@@ -514,21 +538,7 @@ SystemMessageInternal.prototype = {
manifestURL: msg.manifestURL,
pageURL: msg.pageURL,
msgQueue: pendingMessages });
- break;
- }
- case "SystemMessageManager:HasPendingMessages":
- {
- debug("received SystemMessageManager:HasPendingMessages " + msg.type +
- " for " + msg.pageURL + " @ " + msg.manifestURL);
-
- // This is a sync call used to return if a page has pending messages.
- // Find the right page to get its corresponding pending messages.
- let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
- if (!page) {
- return false;
- }
-
- return page.pendingMessages.length != 0;
+ this._refreshCacheInternal(aMessage.target, msg.manifestURL);
break;
}
case "SystemMessageManager:Message:Return:OK":
@@ -697,7 +707,7 @@ SystemMessageInternal.prototype = {
_isPageMatched: function(aPage, aType, aPageURL, aManifestURL) {
return (aPage.type === aType &&
aPage.manifestURL === aManifestURL &&
- aPage.pageURL === aPageURL)
+ aPage.pageURL === aPageURL);
},
_createKeyForPage: function _createKeyForPage(aPage) {
diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js
index 31925e15c12d..d326c2d1b602 100644
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -193,10 +193,16 @@ SystemMessageManager.prototype = {
return false;
}
- return cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages",
- { type: aType,
- pageURL: this._pageURL,
- manifestURL: this._manifestURL })[0];
+
+ /*
+ * NB: If the system message is fired after we received the cache
+ * and before we registered the pageURL we will get false
+ * negative however this is unlikely and will do no harm.
+ */
+ let cache = Cc["@mozilla.org/system-message-cache;1"]
+ .getService(Ci.nsISystemMessageCache);
+
+ return cache.hasPendingMessage(aType, this._pageURL, this._manifestURL);
},
mozIsHandlingMessage: function() {
diff --git a/dom/messages/SystemMessageManager.manifest b/dom/messages/SystemMessageManager.manifest
index 307bd40d8979..33b95f2f7a32 100644
--- a/dom/messages/SystemMessageManager.manifest
+++ b/dom/messages/SystemMessageManager.manifest
@@ -3,3 +3,6 @@ contract @mozilla.org/system-message-manager;1 {bc076ea0-609b-4d8f-83d7-5af7cbdc
component {70589ca5-91ac-4b9e-b839-d6a88167d714} SystemMessageInternal.js
contract @mozilla.org/system-message-internal;1 {70589ca5-91ac-4b9e-b839-d6a88167d714}
+
+component {5a19d86a-21e5-4ac8-9634-8c364c73f87f} SystemMessageCache.js
+contract @mozilla.org/system-message-cache;1 {5a19d86a-21e5-4ac8-9634-8c364c73f87f}
diff --git a/dom/messages/interfaces/moz.build b/dom/messages/interfaces/moz.build
index 3089f53b16d0..269f88cd30b9 100644
--- a/dom/messages/interfaces/moz.build
+++ b/dom/messages/interfaces/moz.build
@@ -6,6 +6,7 @@
XPIDL_SOURCES += [
'nsIDOMNavigatorSystemMessages.idl',
+ 'nsISystemMessageCache.idl',
'nsISystemMessageGlue.idl',
'nsISystemMessagesInternal.idl',
]
diff --git a/dom/messages/interfaces/nsISystemMessageCache.idl b/dom/messages/interfaces/nsISystemMessageCache.idl
new file mode 100644
index 000000000000..4cfc8440b0ac
--- /dev/null
+++ b/dom/messages/interfaces/nsISystemMessageCache.idl
@@ -0,0 +1,13 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(e888447c-6d4d-4045-92bd-1a5985404375)]
+interface nsISystemMessageCache : nsISupports
+{
+ boolean hasPendingMessage(in DOMString type,
+ in DOMString pageURL,
+ in DOMString manifestURL);
+};
diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl
index dbd48dfb9284..ad6f95121d1a 100644
--- a/dom/messages/interfaces/nsISystemMessagesInternal.idl
+++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl
@@ -6,6 +6,7 @@
interface nsIURI;
interface nsIDOMWindow;
+interface nsIMessageSender;
// Implemented by the contract id @mozilla.org/system-message-internal;1
@@ -45,6 +46,12 @@ interface nsISystemMessagesInternal : nsISupports
* @param manifestURI The webapp's manifest URI.
*/
void registerPage(in DOMString type, in nsIURI pageURI, in nsIURI manifestURI);
+
+ /*
+ * Refresh the system message cache in a content process.
+ * @param manifestURI The webapp's manifest URI.
+ */
+ void refreshCache(in nsIMessageSender childMM, in nsIURI manifestURI);
};
[scriptable, uuid(002f0e82-91f0-41de-ad43-569a2b9d12df)]
diff --git a/dom/messages/moz.build b/dom/messages/moz.build
index 19d8125a58a9..c8d978705944 100644
--- a/dom/messages/moz.build
+++ b/dom/messages/moz.build
@@ -7,6 +7,7 @@
DIRS += ['interfaces']
EXTRA_COMPONENTS += [
+ 'SystemMessageCache.js',
'SystemMessageInternal.js',
'SystemMessageManager.js',
'SystemMessageManager.manifest',
diff --git a/mobile/android/base/reading/ReadingListSyncAdapter.java b/mobile/android/base/reading/ReadingListSyncAdapter.java
index 7b3474294e67..4033b7d02577 100644
--- a/mobile/android/base/reading/ReadingListSyncAdapter.java
+++ b/mobile/android/base/reading/ReadingListSyncAdapter.java
@@ -1,5 +1,4 @@
-/* -*- Mode: Java; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
diff --git a/mobile/android/base/resources/values-v21/themes.xml b/mobile/android/base/resources/values-v21/themes.xml
new file mode 100644
index 000000000000..41eef5700ba6
--- /dev/null
+++ b/mobile/android/base/resources/values-v21/themes.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/base/resources/values/colors.xml b/mobile/android/base/resources/values/colors.xml
index ec56d47d7ab9..a0e28fffd707 100644
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -4,6 +4,8 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ #363B40
+
#FFF5F5F5
diff --git a/mobile/android/base/sync/setup/SyncAccounts.java b/mobile/android/base/sync/setup/SyncAccounts.java
index 4f15beabf458..b7a8f2b02806 100644
--- a/mobile/android/base/sync/setup/SyncAccounts.java
+++ b/mobile/android/base/sync/setup/SyncAccounts.java
@@ -285,7 +285,7 @@ public class SyncAccounts {
Logger.debug(LOG_TAG, "Account " + account + " added successfully.");
setSyncAutomatically(account, syncAutomatically);
- setIsSyncable(account, syncAutomatically);
+ setIsSyncable(account, true);
Logger.debug(LOG_TAG, "Set account to sync automatically? " + syncAutomatically + ".");
try {
diff --git a/mobile/android/base/widget/DoorHanger.java b/mobile/android/base/widget/DoorHanger.java
index afbd32a5e716..b53e2f51aaa4 100644
--- a/mobile/android/base/widget/DoorHanger.java
+++ b/mobile/android/base/widget/DoorHanger.java
@@ -17,6 +17,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.Html;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
@@ -160,7 +162,9 @@ public class DoorHanger extends LinearLayout {
}
public void setMessage(String message) {
- mTextView.setText(message);
+ Spanned markupMessage = Html.fromHtml(message);
+ mTextView.setMovementMethod(LinkMovementMethod.getInstance()); // Necessary for clickable links
+ mTextView.setText(markupMessage);
}
public void setIcon(int resId) {
diff --git a/netwerk/base/NetUtil.jsm b/netwerk/base/NetUtil.jsm
index 8cebc55d0004..06feb7a16dda 100644
--- a/netwerk/base/NetUtil.jsm
+++ b/netwerk/base/NetUtil.jsm
@@ -22,6 +22,9 @@ const Cu = Components.utils;
const PR_UINT32_MAX = 0xffffffff;
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
////////////////////////////////////////////////////////////////////////////////
//// NetUtil Object
@@ -81,22 +84,24 @@ this.NetUtil = {
},
/**
- * Asynchronously opens a source and fetches the response. A source can be
- * an nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream. The
- * provided callback will get an input stream containing the response, the
- * result code, and a reference to the request.
+ * Asynchronously opens a source and fetches the response. While the fetch
+ * is asynchronous, I/O may happen on the main thread. When reading from
+ * a local file, prefer using "OS.File" methods instead.
*
* @param aSource
- * The nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream
- * to open.
+ * This argument can be one of the following:
+ * - An options object that will be passed to NetUtil.newChannel.
+ * - An existing nsIChannel.
+ * - An existing nsIInputStream.
+ * Using an nsIURI, nsIFile, or string spec directly is deprecated.
* @param aCallback
* The callback function that will be notified upon completion. It
- * will get two arguments:
+ * will get these arguments:
* 1) An nsIInputStream containing the data from aSource, if any.
* 2) The status code from opening the source.
* 3) Reference to the nsIRequest.
*/
- asyncFetch: function NetUtil_asyncOpen(aSource, aCallback)
+ asyncFetch: function NetUtil_asyncFetch(aSource, aCallback)
{
if (!aSource || !aCallback) {
let exception = new Components.Exception(
@@ -154,31 +159,7 @@ this.NetUtil = {
},
/**
- * Asynchronously opens a source and fetches the response. A source can be
- * an nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream. The
- * provided callback will get an input stream containing the response, the
- * result code, and a reference to the request.
- *
- * Please note, if aSource is an instance of an nsIChannel, then
- * aLoadingNode, aLoadingPrincipal, aTriggeringPrincipal, aSecurityFlags,
- * aContentPolicyType must be "undefined".
- *
- * @param aSource
- * The nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream
- * to open.
- * @param aCallback
- * The callback function that will be notified upon completion. It
- * will get two arguments:
- * 1) An nsIInputStream containing the data from aSource, if any.
- * 2) The status code from opening the source.
- * 3) Reference to the nsIRequest.
- * @param aLoadingNode, aLoadingPrincipal, aTriggeringPrincipal
- * aSecurityFlags, aContentPolicyType
- * See param description in NetUtil_newChannel2.
- *
- * Note: As an interim we have asyncFetch as well as asyncFetch2.
- * Once Bug 1087720 (which converts all js callers to use
- * asyncFetch2) lands, we can remove asyncFetch completely.
+ * @deprecated Use asyncFecth({ ...options... }, callback) instead.
*/
asyncFetch2: function NetUtil_asyncFetch2(aSource,
aCallback,
@@ -303,106 +284,194 @@ this.NetUtil = {
},
/**
- * Constructs a new channel for the given spec, character set, and base URI,
- * or nsIURI, or nsIFile.
+ * Constructs a new channel for the given source.
+ *
+ * Keep in mind that URIs coming from a webpage should *never* use the
+ * systemPrincipal as the loadingPrincipal.
*
* @param aWhatToLoad
- * The string spec for the desired URI, an nsIURI, or an nsIFile.
- * @param aOriginCharset [optional]
+ * This argument used to be a string spec for the desired URI, an
+ * nsIURI, or an nsIFile. Now it should be an options object with
+ * the following properties:
+ * {
+ * uri:
+ * The full URI spec string or nsIURI to create the channel for.
+ * Note that this cannot be an nsIFile and you cannot specify a
+ * non-default charset or base URI. Call NetUtil.newURI first if
+ * you need to construct an URI using those options.
+ * loadingNode:
+ * The loadingDocument of the channel.
+ * The element or document where the result of this request will
+ * be used. This is the document/element that will get access to
+ * the result of this request. For example for an image load,
+ * it's the document in which the image will be loaded. And for
+ * a CSS stylesheet it's the document whose rendering will be
+ * affected by the stylesheet.
+ * If possible, pass in the element which is performing the load.
+ * But if the load is coming from a JS API (such as
+ * XMLHttpRequest) or if the load might be coalesced across
+ * multiple elements (such as for
) then pass in the
+ * Document node instead.
+ * For loads that are not related to any document, such as loads
+ * coming from addons or internal browser features, omit this
+ * property and specify a loadingPrincipal or
+ * loadUsingSystemPrincipal instead.
+ * loadingPrincipal:
+ * The loadingPrincipal of the channel.
+ * The principal of the document where the result of this request
+ * will be used.
+ * This is generally the principal of the loadingNode. However
+ * for loads where loadingNode is omitted this argument still
+ * needs to be passed. For example for loads from a WebWorker,
+ * pass the principal of that worker. For loads from an addon or
+ * from internal browser features, pass the system principal.
+ * This principal should almost always be the system principal if
+ * loadingNode is omitted, in which case you can use the
+ * useSystemPrincipal property. The only exception to this is
+ * for loads from WebWorkers since they don't have any nodes to
+ * be passed as loadingNode.
+ * Please note, loadingPrincipal is *not* the principal of the
+ * resource being loaded, but rather the principal of the context
+ * where the resource will be used.
+ * loadUsingSystemPrincipal:
+ * Set this to true to use the system principal as
+ * loadingPrincipal. This must be omitted if loadingPrincipal or
+ * loadingNode are present.
+ * This should be used with care as it skips security checks.
+ * triggeringPrincipal:
+ * The triggeringPrincipal of the load.
+ * The triggeringPrincipal is the principal of the resource that
+ * caused this particular URL to be loaded.
+ * Most likely the triggeringPrincipal and the loadingPrincipal
+ * are identical, in which case the triggeringPrincipal can be
+ * left out. In some cases the loadingPrincipal and the
+ * triggeringPrincipal are different however, e.g. a stylesheet
+ * may import a subresource. In that case the principal of the
+ * stylesheet which contains the import command is the
+ * triggeringPrincipal, and the principal of the document whose
+ * rendering is affected is the loadingPrincipal.
+ * securityFlags:
+ * The securityFlags of the channel.
+ * Any of the securityflags defined in nsILoadInfo.idl.
+ * contentPolicyType:
+ * The contentPolicyType of the channel.
+ * Any of the content types defined in nsIContentPolicy.idl.
+ * }
+ * @param aOriginCharset [deprecated]
* The character set for the URI. Only used if aWhatToLoad is a
- * string.
- * @param aBaseURI [optional]
- * The base URI for the spec. Only used if aWhatToLoad is a string.
+ * string, which is a deprecated API. Must be undefined otherwise.
+ * Use NetUtil.newURI if you need to use this option.
+ * @param aBaseURI [deprecated]
+ * The base URI for the spec. Only used if aWhatToLoad is a string,
+ * which is a deprecated API. Must be undefined otherwise. Use
+ * NetUtil.newURI if you need to use this option.
*
* @return an nsIChannel object.
*/
newChannel: function NetUtil_newChannel(aWhatToLoad, aOriginCharset,
aBaseURI)
{
- if (!aWhatToLoad) {
- let exception = new Components.Exception(
- "Must have a non-null string spec, nsIURI, or nsIFile object",
+ // Check for the deprecated API first.
+ if (typeof aWhatToLoad == "string" ||
+ (aWhatToLoad instanceof Ci.nsIFile) ||
+ (aWhatToLoad instanceof Ci.nsIURI)) {
+
+ let uri = (aWhatToLoad instanceof Ci.nsIURI)
+ ? aWhatToLoad
+ : this.newURI(aWhatToLoad, aOriginCharset, aBaseURI);
+
+ return this.ioService.newChannelFromURI(uri);
+ }
+
+ // We are using the updated API, that requires only the options object.
+ if (typeof aWhatToLoad != "object" ||
+ aOriginCharset !== undefined ||
+ aBaseURI !== undefined) {
+
+ throw new Components.Exception(
+ "newChannel requires a single object argument",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
- throw exception;
}
- let uri = aWhatToLoad;
- if (!(aWhatToLoad instanceof Ci.nsIURI)) {
- // We either have a string or an nsIFile that we'll need a URI for.
- uri = this.newURI(aWhatToLoad, aOriginCharset, aBaseURI);
+ let { uri,
+ loadingNode,
+ loadingPrincipal,
+ loadUsingSystemPrincipal,
+ triggeringPrincipal,
+ securityFlags,
+ contentPolicyType } = aWhatToLoad;
+
+ if (!uri) {
+ throw new Components.Exception(
+ "newChannel requires the 'uri' property on the options object.",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
}
- return this.ioService.newChannelFromURI(uri);
+ if (typeof uri == "string") {
+ uri = this.newURI(uri);
+ }
+
+ if (!loadingNode && !loadingPrincipal && !loadUsingSystemPrincipal) {
+ throw new Components.Exception(
+ "newChannel requires at least one of the 'loadingNode'," +
+ " 'loadingPrincipal', or 'loadUsingSystemPrincipal'" +
+ " properties on the options object.",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
+ }
+
+ if (loadUsingSystemPrincipal === true) {
+ if (loadingNode || loadingPrincipal) {
+ throw new Components.Exception(
+ "newChannel does not accept 'loadUsingSystemPrincipal'" +
+ " if the 'loadingNode' or 'loadingPrincipal' properties" +
+ " are present on the options object.",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
+ }
+ loadingPrincipal = Services.scriptSecurityManager
+ .getSystemPrincipal();
+ } else if (loadUsingSystemPrincipal !== undefined) {
+ throw new Components.Exception(
+ "newChannel requires the 'loadUsingSystemPrincipal'" +
+ " property on the options object to be 'true' or 'undefined'.",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
+ }
+
+ if (securityFlags === undefined) {
+ securityFlags = Ci.nsILoadInfo.SEC_NORMAL;
+ }
+
+ if (contentPolicyType === undefined) {
+ if (!loadUsingSystemPrincipal) {
+ throw new Components.Exception(
+ "newChannel requires the 'contentPolicyType' property on" +
+ " the options object unless loading from system principal.",
+ Cr.NS_ERROR_INVALID_ARG,
+ Components.stack.caller
+ );
+ }
+ contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
+ }
+
+ return this.ioService.newChannelFromURI2(uri,
+ loadingNode || null,
+ loadingPrincipal || null,
+ triggeringPrincipal || null,
+ securityFlags,
+ contentPolicyType);
},
/**
- * Constructs a new channel for the given spec, character set, and base URI,
- * or nsIURI, or nsIFile.
- *
- * @param aWhatToLoad
- * The string spec for the desired URI, an nsIURI, or an nsIFile.
- * @param aOriginCharset
- * The character set for the URI. Only used if aWhatToLoad is a
- * string.
- * @param aBaseURI
- * The base URI for the spec. Only used if aWhatToLoad is a string.
- * @param aLoadingNode
- * The loadingDocument of the channel.
- * The element or document where the result of this request will be
- * used. This is the document/element that will get access to the
- * result of this request. For example for an image load, it's the
- * document in which the image will be loaded. And for a CSS
- * stylesheet it's the document whose rendering will be affected by
- * the stylesheet.
- * If possible, pass in the element which is performing the load. But
- * if the load is coming from a JS API (such as XMLHttpRequest) or if
- * the load might be coalesced across multiple elements (such as
- * for
) then pass in the Document node instead.
- * For loads that are not related to any document, such as loads coming
- * from addons or internal browser features, use null here.
- * @param aLoadingPrincipal
- * The loadingPrincipal of the channel.
- * The principal of the document where the result of this request will
- * be used.
- * This is generally the principal of the aLoadingNode. However for
- * loads where aLoadingNode is null this argument still needs to be
- * passed. For example for loads from a WebWorker, pass the principal
- * of that worker. For loads from an addon or from internal browser
- * features, pass the system principal.
- * This principal should almost always be the system principal if
- * aLoadingNode is null. The only exception to this is for loads
- * from WebWorkers since they don't have any nodes to be passed as
- * aLoadingNode.
- * Please note, aLoadingPrincipal is *not* the principal of the
- * resource being loaded. But rather the principal of the context
- * where the resource will be used.
- * @param aTriggeringPrincipal
- * The triggeringPrincipal of the load.
- * The triggeringPrincipal is the principal of the resource that caused
- * this particular URL to be loaded.
- * Most likely the triggeringPrincipal and the loadingPrincipal are
- * identical, in which case the triggeringPrincipal can be left out.
- * In some cases the loadingPrincipal and the triggeringPrincipal are
- * different however, e.g. a stylesheet may import a subresource. In
- * that case the principal of the stylesheet which contains the
- * import command is the triggeringPrincipal, and the principal of
- * the document whose rendering is affected is the loadingPrincipal.
- * @param aSecurityFlags
- * The securityFlags of the channel.
- * Any of the securityflags defined in nsILoadInfo.idl
- * @param aContentPolicyType
- * The contentPolicyType of the channel.
- * Any of the content types defined in nsIContentPolicy.idl
- * @return an nsIChannel object.
- *
- * Keep in mind that URIs coming from a webpage should *never* use the
- * systemPrincipal as the loadingPrincipal.
- *
- * Note: As an interim we have newChannel as well as newChannel2.
- * Once Bug 1087720 (which converts all js callers to use
- * newChannel2) lands, we can remove newChannel completely.
+ * @deprecated Use newChannel({ ...options... }) instead.
*/
newChannel2: function NetUtil_newChannel2(aWhatToLoad,
aOriginCharset,
@@ -485,7 +554,7 @@ this.NetUtil = {
let cis = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
try {
- // When replacement is set, the character that is unknown sequence
+ // When replacement is set, the character that is unknown sequence
// replaces with aOptions.replacement character.
if (!("replacement" in aOptions)) {
// aOptions.replacement isn't set.
@@ -533,12 +602,3 @@ this.NetUtil = {
getService(Ci.nsIIOService);
},
};
-
-////////////////////////////////////////////////////////////////////////////////
-//// Initialization
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-// Define our lazy getters.
-XPCOMUtils.defineLazyServiceGetter(this, "ioUtil", "@mozilla.org/io-util;1",
- "nsIIOUtil");
diff --git a/netwerk/test/unit/test_NetUtil.js b/netwerk/test/unit/test_NetUtil.js
index b81cd82ad94c..e6ad1c63a53a 100644
--- a/netwerk/test/unit/test_NetUtil.js
+++ b/netwerk/test/unit/test_NetUtil.js
@@ -261,7 +261,7 @@ function test_ioService()
function test_asyncFetch_no_channel()
{
try {
- NetUtil.asyncFetch2(null, function() { });
+ NetUtil.asyncFetch(null, function() { });
do_throw("should throw!");
}
catch (e) {
@@ -274,7 +274,7 @@ function test_asyncFetch_no_channel()
function test_asyncFetch_no_callback()
{
try {
- NetUtil.asyncFetch2({ });
+ NetUtil.asyncFetch({ });
do_throw("should throw!");
}
catch (e) {
@@ -298,19 +298,13 @@ function test_asyncFetch_with_nsIChannel()
server.start(-1);
// Create our channel.
- let channel = NetUtil.ioService.
- newChannel2("http://localhost:" +
- server.identity.primaryPort + "/test",
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ let channel = NetUtil.newChannel({
+ uri: "http://localhost:" + server.identity.primaryPort + "/test",
+ loadUsingSystemPrincipal: true,
+ });
// Open our channel asynchronously.
- NetUtil.asyncFetch2(channel, function(aInputStream, aResult) {
+ NetUtil.asyncFetch(channel, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -344,7 +338,10 @@ function test_asyncFetch_with_nsIURI()
server.identity.primaryPort + "/test");
// Open our URI asynchronously.
- NetUtil.asyncFetch2(uri, function(aInputStream, aResult) {
+ NetUtil.asyncFetch({
+ uri,
+ loadUsingSystemPrincipal: true,
+ }, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -379,9 +376,10 @@ function test_asyncFetch_with_string()
server.start(-1);
// Open our location asynchronously.
- NetUtil.asyncFetch2("http://localhost:" +
- server.identity.primaryPort + "/test",
- function(aInputStream, aResult) {
+ NetUtil.asyncFetch({
+ uri: "http://localhost:" + server.identity.primaryPort + "/test",
+ loadUsingSystemPrincipal: true,
+ }, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -423,7 +421,11 @@ function test_asyncFetch_with_nsIFile()
do_check_eq(TEST_DATA, getFileContents(file));
// Open our file asynchronously.
- NetUtil.asyncFetch2(file, function(aInputStream, aResult) {
+ // Note that this causes main-tread I/O and should be avoided in production.
+ NetUtil.asyncFetch({
+ uri: NetUtil.newURI(file),
+ loadUsingSystemPrincipal: true,
+ }, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -452,7 +454,7 @@ function test_asyncFetch_with_nsIInputString()
istream.setData(TEST_DATA, TEST_DATA.length);
// Read the input stream asynchronously.
- NetUtil.asyncFetch2(istream, function(aInputStream, aResult) {
+ NetUtil.asyncFetch(istream, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -473,18 +475,13 @@ function test_asyncFetch_with_nsIInputString()
function test_asyncFetch_does_not_block()
{
// Create our channel that has no data.
- let channel = NetUtil.ioService.
- newChannel2("data:text/plain,",
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ let channel = NetUtil.newChannel({
+ uri: "data:text/plain,",
+ loadUsingSystemPrincipal: true,
+ });
// Open our channel asynchronously.
- NetUtil.asyncFetch2(channel, function(aInputStream, aResult) {
+ NetUtil.asyncFetch(channel, function(aInputStream, aResult) {
// Check that we had success.
do_check_true(Components.isSuccessCode(aResult));
@@ -508,7 +505,7 @@ function test_asyncFetch_does_not_block()
function test_newChannel_no_specifier()
{
try {
- NetUtil.newChannel2();
+ NetUtil.newChannel();
do_throw("should throw!");
}
catch (e) {
@@ -533,14 +530,7 @@ function test_newChannel_with_string()
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
- let NetUtilChannel = NetUtil.newChannel2(TEST_SPEC,
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ let NetUtilChannel = NetUtil.newChannel(TEST_SPEC);
do_check_true(iosChannel.URI.equals(NetUtilChannel.URI));
run_next_test();
@@ -559,14 +549,7 @@ function test_newChannel_with_nsIURI()
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
- let NetUtilChannel = NetUtil.newChannel2(uri,
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ let NetUtilChannel = NetUtil.newChannel(uri);
do_check_true(iosChannel.URI.equals(NetUtilChannel.URI));
run_next_test();
@@ -588,19 +571,83 @@ function test_newChannel_with_nsIFile()
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
- let NetUtilChannel = NetUtil.newChannel2(uri,
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ let NetUtilChannel = NetUtil.newChannel(file);
do_check_true(iosChannel.URI.equals(NetUtilChannel.URI));
run_next_test();
}
+function test_newChannel_with_options()
+{
+ let uri = "data:text/plain,";
+
+ let iosChannel = NetUtil.ioService.newChannelFromURI2(NetUtil.newURI(uri),
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_NORMAL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+
+ function checkEqualToIOSChannel(channel) {
+ do_check_true(iosChannel.URI.equals(channel.URI));
+ }
+
+ checkEqualToIOSChannel(NetUtil.newChannel({
+ uri,
+ loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ }));
+
+ checkEqualToIOSChannel(NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ }));
+
+ run_next_test();
+}
+
+function test_newChannel_with_wrong_options()
+{
+ let uri = "data:text/plain,";
+ let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+
+ Assert.throws(() => {
+ NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true }, null, null);
+ }, /requires a single object argument/);
+
+ Assert.throws(() => {
+ NetUtil.newChannel({});
+ }, /requires the 'uri' property/);
+
+ Assert.throws(() => {
+ NetUtil.newChannel({ uri });
+ }, /requires at least one of the 'loadingNode'/);
+
+ Assert.throws(() => {
+ NetUtil.newChannel({
+ uri,
+ loadingPrincipal: systemPrincipal,
+ });
+ }, /requires the 'contentPolicyType'/);
+
+ Assert.throws(() => {
+ NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: systemPrincipal,
+ });
+ }, /to be 'true' or 'undefined'/);
+
+ Assert.throws(() => {
+ NetUtil.newChannel({
+ uri,
+ loadingPrincipal: systemPrincipal,
+ loadUsingSystemPrincipal: true,
+ });
+ }, /does not accept 'loadUsingSystemPrincipal'/);
+
+ run_next_test();
+}
+
function test_readInputStreamToString()
{
const TEST_DATA = "this is a test string\0 with an embedded null";
@@ -756,6 +803,8 @@ function test_readInputStreamToString_invalid_sequence()
test_newChannel_with_string,
test_newChannel_with_nsIURI,
test_newChannel_with_nsIFile,
+ test_newChannel_with_options,
+ test_newChannel_with_wrong_options,
test_readInputStreamToString,
test_readInputStreamToString_no_input_stream,
test_readInputStreamToString_no_bytes_arg,
diff --git a/toolkit/devtools/server/actors/webconsole.js b/toolkit/devtools/server/actors/webconsole.js
index 4fc4236e749f..2b00512f6f6f 100644
--- a/toolkit/devtools/server/actors/webconsole.js
+++ b/toolkit/devtools/server/actors/webconsole.js
@@ -1060,11 +1060,17 @@ WebConsoleActor.prototype =
*/
evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {})
{
+ let trimmedString = aString.trim();
// The help function needs to be easy to guess, so we make the () optional.
- if (aString.trim() == "help" || aString.trim() == "?") {
+ if (trimmedString == "help" || trimmedString == "?") {
aString = "help()";
}
+ // Add easter egg for console.mihai().
+ if (trimmedString == "console.mihai()" || trimmedString == "console.mihai();") {
+ aString = "\"http://incompleteness.me/blog/2015/02/09/console-dot-mihai/\"";
+ }
+
// Find the Debugger.Frame of the given FrameActor.
let frame = null, frameActor = null;
if (aOptions.frameActor) {
diff --git a/tools/profiler/ProfileEntry.h b/tools/profiler/ProfileEntry.h
index fff072cc5720..bbfb9f447f4d 100644
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -75,7 +75,7 @@ typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagSt
class ProfileBuffer {
public:
- NS_INLINE_DECL_REFCOUNTING(ProfileBuffer)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfileBuffer)
explicit ProfileBuffer(int aEntrySize);