diff --git a/b2g/chrome/content/devtools.js b/b2g/chrome/content/devtools.js
index b0e06096b1c5..84590f7dc017 100644
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -58,7 +58,7 @@ let developerHUD = {
return;
if (!DebuggerServer.initialized) {
- RemoteDebugger.start();
+ RemoteDebugger.initServer();
}
// We instantiate a local debugger connection so that watchers can use our
diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js
index d69e7cf8acf6..203a8ee9b996 100644
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -351,7 +351,7 @@ let AdbController = {
// Check if we have a remote debugging session going on. If so, we won't
// disable adb even if the screen is locked.
- let isDebugging = RemoteDebugger.isDebugging;
+ let isDebugging = USBRemoteDebugger.isDebugging;
if (this.DEBUG) {
this.debug("isDebugging=" + isDebugging);
}
@@ -432,45 +432,78 @@ SettingsListener.observe("lockscreen.enabled", false,
AdbController.setLockscreenEnabled.bind(AdbController));
#endif
-// Keep the old setting to not break people that won't have updated
-// gaia and gecko.
-SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
- Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
- // This preference is consulted during startup
- Services.prefs.savePrefFile(null);
- try {
- value ? RemoteDebugger.start() : RemoteDebugger.stop();
- } catch(e) {
- dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
- }
+(function() {
+ // Track these separately here so we can determine the correct value for the
+ // pref "devtools.debugger.remote-enabled", which is true when either mode of
+ // using DevTools is enabled.
+ let devtoolsUSB = false;
+ let devtoolsWiFi = false;
+
+ // Keep the old setting to not break people that won't have updated
+ // gaia and gecko.
+ SettingsListener.observe('devtools.debugger.remote-enabled', false,
+ function(value) {
+ devtoolsUSB = value;
+ Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+ devtoolsUSB || devtoolsWiFi);
+ // This preference is consulted during startup
+ Services.prefs.savePrefFile(null);
+ try {
+ value ? USBRemoteDebugger.start() : USBRemoteDebugger.stop();
+ } catch(e) {
+ dump("Error while initializing USB devtools: "
+ + e + "\n" + e.stack + "\n");
+ }
#ifdef MOZ_WIDGET_GONK
- AdbController.setRemoteDebuggerState(value);
+ AdbController.setRemoteDebuggerState(value);
#endif
-});
+ });
-SettingsListener.observe('debugger.remote-mode', false, function(value) {
- if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
- dump('Illegal value for debugger.remote-mode: ' + value + '\n');
- return;
- }
+ SettingsListener.observe('debugger.remote-mode', false, function(value) {
+ if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
+ dump('Illegal value for debugger.remote-mode: ' + value + '\n');
+ return;
+ }
- Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
- value == 'adb-devtools');
- // This preference is consulted during startup
- Services.prefs.savePrefFile(null);
+ devtoolsUSB = value == 'adb-devtools';
+ Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+ devtoolsUSB || devtoolsWiFi);
+ // This preference is consulted during startup
+ Services.prefs.savePrefFile(null);
- try {
- (value == 'adb-devtools') ? RemoteDebugger.start()
- : RemoteDebugger.stop();
- } catch(e) {
- dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
- }
+ try {
+ (value == 'adb-devtools') ? USBRemoteDebugger.start()
+ : USBRemoteDebugger.stop();
+ } catch(e) {
+ dump("Error while initializing USB devtools: "
+ + e + "\n" + e.stack + "\n");
+ }
#ifdef MOZ_WIDGET_GONK
- AdbController.setRemoteDebuggerState(value != 'disabled');
+ AdbController.setRemoteDebuggerState(value != 'disabled');
#endif
-});
+ });
+
+ SettingsListener.observe('devtools.remote.wifi.enabled', false,
+ function(value) {
+ devtoolsWiFi = value;
+ Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+ devtoolsUSB || devtoolsWiFi);
+ // Allow remote debugging on non-local interfaces when WiFi debug is enabled
+ // TODO: Bug 1034411: Lock down to WiFi interface, instead of all interfaces
+ Services.prefs.setBoolPref('devtools.debugger.force-local', !value);
+ // This preference is consulted during startup
+ Services.prefs.savePrefFile(null);
+
+ try {
+ value ? WiFiRemoteDebugger.start() : WiFiRemoteDebugger.stop();
+ } catch(e) {
+ dump("Error while initializing WiFi devtools: "
+ + e + "\n" + e.stack + "\n");
+ }
+ });
+})();
// =================== Device Storage ====================
SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
@@ -657,6 +690,9 @@ let settingsToObserve = {
defaultValue: false
},
'devtools.eventlooplag.threshold': 100,
+ 'devtools.remote.wifi.visible': {
+ resetToPref: true
+ },
'dom.mozApps.use_reviewer_certs': false,
'layers.draw-borders': false,
'layers.draw-tile-borders': false,
diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js
index 66e208b6dc72..ede5abf9c2ad 100644
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -56,6 +56,16 @@ XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
return DebuggerServer;
});
+XPCOMUtils.defineLazyGetter(this, 'devtools', function() {
+ const { devtools } =
+ Cu.import('resource://gre/modules/devtools/Loader.jsm', {});
+ return devtools;
+});
+
+XPCOMUtils.defineLazyGetter(this, 'discovery', function() {
+ return devtools.require('devtools/toolkit/discovery/discovery');
+});
+
XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
return Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
@@ -809,7 +819,6 @@ let IndexedDBPromptHelper = {
let RemoteDebugger = {
_promptDone: false,
_promptAnswer: false,
- _running: false,
prompt: function debugger_prompt() {
this._promptDone = false;
@@ -830,8 +839,69 @@ let RemoteDebugger = {
this._promptDone = true;
},
+ initServer: function() {
+ if (DebuggerServer.initialized) {
+ return;
+ }
+
+ // Ask for remote connections.
+ DebuggerServer.init(this.prompt.bind(this));
+
+ // /!\ Be careful when adding a new actor, especially global actors.
+ // Any new global actor will be exposed and returned by the root actor.
+
+ // Add Firefox-specific actors, but prevent tab actors to be loaded in
+ // the parent process, unless we enable certified apps debugging.
+ let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+ DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
+
+ /**
+ * Construct a root actor appropriate for use in a server running in B2G.
+ * The returned root actor respects the factories registered with
+ * DebuggerServer.addGlobalActor only if certified apps debugging is on,
+ * otherwise we used an explicit limited list of global actors
+ *
+ * * @param connection DebuggerServerConnection
+ * The conection to the client.
+ */
+ DebuggerServer.createRootActor = function createRootActor(connection)
+ {
+ let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+ let parameters = {
+ // We do not expose browser tab actors yet,
+ // but we still have to define tabList.getList(),
+ // otherwise, client won't be able to fetch global actors
+ // from listTabs request!
+ tabList: {
+ getList: function() {
+ return promise.resolve([]);
+ }
+ },
+ // Use an explicit global actor list to prevent exposing
+ // unexpected actors
+ globalActorFactories: restrictPrivileges ? {
+ webappsActor: DebuggerServer.globalActorFactories.webappsActor,
+ deviceActor: DebuggerServer.globalActorFactories.deviceActor,
+ } : DebuggerServer.globalActorFactories
+ };
+ let { RootActor } = devtools.require("devtools/server/actors/root");
+ let root = new RootActor(connection, parameters);
+ root.applicationType = "operating-system";
+ return root;
+ };
+
+#ifdef MOZ_WIDGET_GONK
+ DebuggerServer.on("connectionchange", function() {
+ AdbController.updateState();
+ });
+#endif
+ }
+};
+
+let USBRemoteDebugger = {
+
get isDebugging() {
- if (!this._running) {
+ if (!this._listener) {
return false;
}
@@ -839,99 +909,78 @@ let RemoteDebugger = {
Object.keys(DebuggerServer._connections).length > 0;
},
- // Start the debugger server.
- start: function debugger_start() {
- if (this._running) {
+ start: function() {
+ if (this._listener) {
return;
}
- if (!DebuggerServer.initialized) {
- // Ask for remote connections.
- DebuggerServer.init(this.prompt.bind(this));
+ RemoteDebugger.initServer();
- // /!\ Be careful when adding a new actor, especially global actors.
- // Any new global actor will be exposed and returned by the root actor.
+ let portOrPath =
+ Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
+ "/data/local/debugger-socket";
- // Add Firefox-specific actors, but prevent tab actors to be loaded in
- // the parent process, unless we enable certified apps debugging.
- let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
- DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
-
- /**
- * Construct a root actor appropriate for use in a server running in B2G.
- * The returned root actor respects the factories registered with
- * DebuggerServer.addGlobalActor only if certified apps debugging is on,
- * otherwise we used an explicit limited list of global actors
- *
- * * @param connection DebuggerServerConnection
- * The conection to the client.
- */
- DebuggerServer.createRootActor = function createRootActor(connection)
- {
- let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
- let parameters = {
- // We do not expose browser tab actors yet,
- // but we still have to define tabList.getList(),
- // otherwise, client won't be able to fetch global actors
- // from listTabs request!
- tabList: {
- getList: function() {
- return promise.resolve([]);
- }
- },
- // Use an explicit global actor list to prevent exposing
- // unexpected actors
- globalActorFactories: restrictPrivileges ? {
- webappsActor: DebuggerServer.globalActorFactories.webappsActor,
- deviceActor: DebuggerServer.globalActorFactories.deviceActor,
- } : DebuggerServer.globalActorFactories
- };
- let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
- let { RootActor } = devtools.require("devtools/server/actors/root");
- let root = new RootActor(connection, parameters);
- root.applicationType = "operating-system";
- return root;
- };
-
-#ifdef MOZ_WIDGET_GONK
- DebuggerServer.on("connectionchange", function() {
- AdbController.updateState();
- });
-#endif
- }
-
- let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
- "/data/local/debugger-socket";
try {
- DebuggerServer.openListener(path);
+ debug("Starting USB debugger on " + portOrPath);
+ this._listener = DebuggerServer.openListener(portOrPath);
// Temporary event, until bug 942756 lands and offers a way to know
// when the server is up and running.
Services.obs.notifyObservers(null, 'debugger-server-started', null);
- this._running = true;
} catch (e) {
- dump('Unable to start debugger server: ' + e + '\n');
+ debug('Unable to start USB debugger server: ' + e);
}
},
- stop: function debugger_stop() {
- if (!this._running) {
- return;
- }
-
- if (!DebuggerServer.initialized) {
- // Can this really happen if we are running?
- this._running = false;
+ stop: function() {
+ if (!this._listener) {
return;
}
try {
- DebuggerServer.closeAllListeners();
+ this._listener.close();
+ this._listener = null;
} catch (e) {
- dump('Unable to stop debugger server: ' + e + '\n');
+ debug('Unable to stop USB debugger server: ' + e);
}
- this._running = false;
}
-}
+
+};
+
+let WiFiRemoteDebugger = {
+
+ start: function() {
+ if (this._listener) {
+ return;
+ }
+
+ RemoteDebugger.initServer();
+
+ try {
+ debug("Starting WiFi debugger");
+ this._listener = DebuggerServer.openListener(-1);
+ let port = this._listener.port;
+ debug("Started WiFi debugger on " + port);
+ discovery.addService("devtools", { port: port });
+ } catch (e) {
+ debug('Unable to start WiFi debugger server: ' + e);
+ }
+ },
+
+ stop: function() {
+ if (!this._listener) {
+ return;
+ }
+
+ try {
+ discovery.removeService("devtools");
+ this._listener.close();
+ this._listener = null;
+ } catch (e) {
+ debug('Unable to stop WiFi debugger server: ' + e);
+ }
+ }
+
+};
let KeyboardHelper = {
handleEvent: function keyboard_handleEvent(detail) {
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 32e8eb903882..958ecfb344bd 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 00e538345741..c5d32039fa51 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 66666cfabbbb..454cc018d963 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 32e8eb903882..958ecfb344bd 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index 4f2e555d5409..4a98091de00e 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index c5fdb1fb624c..d916f232fdff 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "18f160082057a1c10754458f9e4944220099ab67",
+ "revision": "eff18c4265f0d8e49e5a2f2d7c9fdb01c87bd42e",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 1985316964c7..1240a3fe403f 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index aa292dc3624d..b4ad2bfc3c0b 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index bacebdf62a7d..c412dc23cc68 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index d48874a7a50c..9a6d81a71c29 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/browser/devtools/canvasdebugger/canvasdebugger.js b/browser/devtools/canvasdebugger/canvasdebugger.js
index b8131542f06f..cac1d2e7e820 100644
--- a/browser/devtools/canvasdebugger/canvasdebugger.js
+++ b/browser/devtools/canvasdebugger/canvasdebugger.js
@@ -207,8 +207,8 @@ let SnapshotsListView = Heritage.extend(WidgetMethods, {
let thumbnail = document.createElementNS(HTML_NS, "canvas");
thumbnail.className = "snapshot-item-thumbnail";
- thumbnail.width = CanvasFront.THUMBNAIL_HEIGHT;
- thumbnail.height = CanvasFront.THUMBNAIL_HEIGHT;
+ thumbnail.width = CanvasFront.THUMBNAIL_SIZE;
+ thumbnail.height = CanvasFront.THUMBNAIL_SIZE;
let title = document.createElement("label");
title.className = "plain snapshot-item-title";
@@ -712,14 +712,16 @@ let CallsListView = Heritage.extend(WidgetMethods, {
* A single "snapshot-image" instance received from the backend.
*/
showScreenshot: function(screenshot) {
- let { index, width, height, flipped, pixels } = screenshot;
+ let { index, width, height, scaling, flipped, pixels } = screenshot;
let screenshotNode = $("#screenshot-image");
screenshotNode.setAttribute("flipped", flipped);
drawBackground("screenshot-rendering", width, height, pixels);
let dimensionsNode = $("#screenshot-dimensions");
- dimensionsNode.setAttribute("value", ~~width + " x " + ~~height);
+ let actualWidth = (width / scaling) | 0;
+ let actualHeight = (height / scaling) | 0;
+ dimensionsNode.setAttribute("value", actualWidth + " x " + actualHeight);
window.emit(EVENTS.CALL_SCREENSHOT_DISPLAYED);
},
@@ -754,8 +756,8 @@ let CallsListView = Heritage.extend(WidgetMethods, {
let thumbnailNode = document.createElementNS(HTML_NS, "canvas");
thumbnailNode.setAttribute("flipped", flipped);
- thumbnailNode.width = Math.max(CanvasFront.THUMBNAIL_HEIGHT, width);
- thumbnailNode.height = Math.max(CanvasFront.THUMBNAIL_HEIGHT, height);
+ thumbnailNode.width = Math.max(CanvasFront.THUMBNAIL_SIZE, width);
+ thumbnailNode.height = Math.max(CanvasFront.THUMBNAIL_SIZE, height);
drawImage(thumbnailNode, width, height, pixels, { centered: true });
thumbnailNode.className = "filmstrip-thumbnail";
diff --git a/browser/devtools/canvasdebugger/test/browser.ini b/browser/devtools/canvasdebugger/test/browser.ini
index d1fe81961b1a..2430397707b2 100644
--- a/browser/devtools/canvasdebugger/test/browser.ini
+++ b/browser/devtools/canvasdebugger/test/browser.ini
@@ -5,6 +5,7 @@ support-files =
doc_simple-canvas-bitmasks.html
doc_simple-canvas-deep-stack.html
doc_simple-canvas-transparent.html
+ doc_webgl-bindings.html
doc_webgl-enum.html
head.js
@@ -17,6 +18,7 @@ support-files =
[browser_canvas-actor-test-07.js]
[browser_canvas-actor-test-08.js]
[browser_canvas-actor-test-09.js]
+[browser_canvas-actor-test-10.js]
[browser_canvas-frontend-call-highlight.js]
[browser_canvas-frontend-call-list.js]
[browser_canvas-frontend-call-search.js]
diff --git a/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-08.js b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-08.js
index ab31f11255a8..618a3ab2eaa4 100644
--- a/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-08.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-08.js
@@ -18,9 +18,7 @@ function ifTestingSupported() {
ok(true, "Target automatically navigated when the front was set up.");
let snapshotActor = yield front.recordAnimationFrame();
-
let animationOverview = yield snapshotActor.getOverview();
-
let functionCalls = animationOverview.calls;
is(functionCalls[0].name, "clearRect",
diff --git a/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-09.js b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-09.js
index 229f3618bc83..8a8e0883409f 100644
--- a/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-09.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-09.js
@@ -26,6 +26,11 @@ function ifTestingSupported() {
is(functionCalls[0].argsPreview, "DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT",
"The bits passed into `gl.clear` have been cast to their enum values.");
+ is(functionCalls[1].name, "bindTexture",
+ "The function's name is correct.");
+ is(functionCalls[1].argsPreview, "TEXTURE_2D, null",
+ "The bits passed into `gl.bindTexture` have been cast to their enum values.");
+
yield removeTab(target.tab);
finish();
}
diff --git a/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-10.js b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-10.js
new file mode 100644
index 000000000000..bf29eb65960f
--- /dev/null
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-actor-test-10.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the correct framebuffer, renderbuffer and textures are re-bound
+ * after generating screenshots using the actor.
+ */
+
+function ifTestingSupported() {
+ let [target, debuggee, front] = yield initCanavsDebuggerBackend(WEBGL_BINDINGS_URL);
+
+ let navigated = once(target, "navigate");
+
+ yield front.setup({ reload: true });
+ ok(true, "The front was setup up successfully.");
+
+ yield navigated;
+ ok(true, "Target automatically navigated when the front was set up.");
+
+ let snapshotActor = yield front.recordAnimationFrame();
+ let animationOverview = yield snapshotActor.getOverview();
+ let functionCalls = animationOverview.calls;
+
+ let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]);
+ is(firstScreenshot.index, -1,
+ "The first screenshot didn't encounter any draw call.");
+ is(firstScreenshot.scaling, 0.25,
+ "The first screenshot has the correct scaling.");
+ is(firstScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+ "The first screenshot has the correct width.");
+ is(firstScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+ "The first screenshot has the correct height.");
+ is(firstScreenshot.flipped, true,
+ "The first screenshot has the correct 'flipped' flag.");
+ is(firstScreenshot.pixels.length, 0,
+ "The first screenshot should be empty.");
+
+ let gl = debuggee.gl;
+ is(gl.getParameter(gl.FRAMEBUFFER_BINDING), debuggee.customFramebuffer,
+ "The debuggee's gl context framebuffer wasn't changed.");
+ is(gl.getParameter(gl.RENDERBUFFER_BINDING), debuggee.customRenderbuffer,
+ "The debuggee's gl context renderbuffer wasn't changed.");
+ is(gl.getParameter(gl.TEXTURE_BINDING_2D), debuggee.customTexture,
+ "The debuggee's gl context texture binding wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[0], 128,
+ "The debuggee's gl context viewport's left coord. wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[1], 256,
+ "The debuggee's gl context viewport's left coord. wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[2], 384,
+ "The debuggee's gl context viewport's left coord. wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[3], 512,
+ "The debuggee's gl context viewport's left coord. wasn't changed.");
+
+ let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]);
+ is(secondScreenshot.index, 1,
+ "The second screenshot has the correct index.");
+ is(secondScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+ "The second screenshot has the correct width.");
+ is(secondScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+ "The second screenshot has the correct height.");
+ is(secondScreenshot.scaling, 0.25,
+ "The second screenshot has the correct scaling.");
+ is(secondScreenshot.flipped, true,
+ "The second screenshot has the correct 'flipped' flag.");
+ is(secondScreenshot.pixels.length, Math.pow(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, 2),
+ "The second screenshot should not be empty.");
+ is(new Uint8Array(secondScreenshot.pixels.buffer)[0], 0,
+ "The second screenshot has the correct red component.");
+ is(new Uint8Array(secondScreenshot.pixels.buffer)[1], 0,
+ "The second screenshot has the correct green component.");
+ is(new Uint8Array(secondScreenshot.pixels.buffer)[2], 255,
+ "The second screenshot has the correct blue component.");
+ is(new Uint8Array(secondScreenshot.pixels.buffer)[3], 255,
+ "The second screenshot has the correct alpha component.");
+
+ let gl = debuggee.gl;
+ is(gl.getParameter(gl.FRAMEBUFFER_BINDING), debuggee.customFramebuffer,
+ "The debuggee's gl context framebuffer still wasn't changed.");
+ is(gl.getParameter(gl.RENDERBUFFER_BINDING), debuggee.customRenderbuffer,
+ "The debuggee's gl context renderbuffer still wasn't changed.");
+ is(gl.getParameter(gl.TEXTURE_BINDING_2D), debuggee.customTexture,
+ "The debuggee's gl context texture binding still wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[0], 128,
+ "The debuggee's gl context viewport's left coord. still wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[1], 256,
+ "The debuggee's gl context viewport's left coord. still wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[2], 384,
+ "The debuggee's gl context viewport's left coord. still wasn't changed.");
+ is(gl.getParameter(gl.VIEWPORT)[3], 512,
+ "The debuggee's gl context viewport's left coord. still wasn't changed.");
+
+ yield removeTab(target.tab);
+ finish();
+}
diff --git a/browser/devtools/canvasdebugger/test/doc_webgl-bindings.html b/browser/devtools/canvasdebugger/test/doc_webgl-bindings.html
new file mode 100644
index 000000000000..eb1405359f65
--- /dev/null
+++ b/browser/devtools/canvasdebugger/test/doc_webgl-bindings.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+ WebGL editor test page
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/canvasdebugger/test/doc_webgl-enum.html b/browser/devtools/canvasdebugger/test/doc_webgl-enum.html
index 3c639363d508..f7f4d6d1e433 100644
--- a/browser/devtools/canvasdebugger/test/doc_webgl-enum.html
+++ b/browser/devtools/canvasdebugger/test/doc_webgl-enum.html
@@ -25,6 +25,7 @@
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ gl.bindTexture(gl.TEXTURE_2D, null);
window.requestAnimationFrame(drawScene);
}
diff --git a/browser/devtools/canvasdebugger/test/head.js b/browser/devtools/canvasdebugger/test/head.js
index e922c31daac5..88e2aa6abc55 100644
--- a/browser/devtools/canvasdebugger/test/head.js
+++ b/browser/devtools/canvasdebugger/test/head.js
@@ -30,6 +30,7 @@ const SIMPLE_BITMASKS_URL = EXAMPLE_URL + "doc_simple-canvas-bitmasks.html";
const SIMPLE_CANVAS_TRANSPARENT_URL = EXAMPLE_URL + "doc_simple-canvas-transparent.html";
const SIMPLE_CANVAS_DEEP_STACK_URL = EXAMPLE_URL + "doc_simple-canvas-deep-stack.html";
const WEBGL_ENUM_URL = EXAMPLE_URL + "doc_webgl-enum.html";
+const WEBGL_BINDINGS_URL = EXAMPLE_URL + "doc_webgl-bindings.html";
// All tests are asynchronous.
waitForExplicitFinish();
diff --git a/content/base/public/nsIMessageManager.idl b/content/base/public/nsIMessageManager.idl
index 4afa64e33820..fb72bc876cd9 100644
--- a/content/base/public/nsIMessageManager.idl
+++ b/content/base/public/nsIMessageManager.idl
@@ -125,27 +125,29 @@ interface nsIPrincipal;
*
* Parent process Child processes
* ---------------- -----------------
- * global PPMM
+ * global (GPPMM)
* |
- * +<----> child PPMM
+ * +-->parent in-process PIPMM<-->child in-process CIPPMM
* |
- * +-->parent PMM1<------------------>child process CMM1
+ * +-->parent (PPMM1)<------------------>child (CPMM1)
* |
- * +-->parent PMM2<------------------>child process PMM2
+ * +-->parent (PPMM2)<------------------>child (CPMM2)
* ...
*
- * For example: the parent-process PMM1 sends messages directly to
- * only the child-process CMM1.
+ * Note, PIPMM and CIPPMM both run in the parent process.
*
- * For example: CMM1 sends messages directly to PMM1. The global PPMM
+ * For example: the parent-process PPMM1 sends messages to the
+ * child-process CPMM1.
+ *
+ * For example: CPMM1 sends messages directly to PPMM1. The global GPPMM
* will also notify their message listeners when the message arrives.
*
- * For example: messages sent through the global PPMM will be
- * dispatched to the listeners of the same-process, "child PPMM".
- * They will also be broadcast to PPM1, PPM2, etc.
+ * For example: messages sent through the global GPPMM will be
+ * dispatched to the listeners of the same-process, CIPPMM, CPMM1,
+ * CPMM2, etc.
*
* ***** PERFORMANCE AND SECURITY WARNING *****
- * Messages broadcast through the global PPMM can result in messages
+ * Messages broadcast through the GPPMM can result in messages
* being dispatched across many OS processes, and to many processes
* with different permissions. Great care should be taken when
* broadcasting.
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 97fd1ea97089..202edb1d3ba4 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -22,6 +22,7 @@
#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "mozilla/VisualEventTracer.h"
+#include "URIUtils.h"
#ifdef MOZ_LOGGING
// so we can get logging even in release builds (but only for some things)
@@ -4558,16 +4559,24 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
// if this is a Strict-Transport-Security host and the cert
// is bad, don't allow overrides (STS Spec section 7.3).
- nsCOMPtr sss =
- do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- uint32_t flags =
- mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
-
+ uint32_t type = nsISiteSecurityService::HEADER_HSTS;
+ uint32_t flags = mInPrivateBrowsing
+ ? nsISocketProvider::NO_PERMANENT_STORAGE
+ : 0;
bool isStsHost = false;
- rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
- aURI, flags, &isStsHost);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ nsCOMPtr sss =
+ do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sss->IsSecureURI(type, aURI, flags, &isStsHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ mozilla::dom::ContentChild* cc =
+ mozilla::dom::ContentChild::GetSingleton();
+ mozilla::ipc::URIParams uri;
+ SerializeURI(aURI, uri);
+ cc->SendIsSecureURI(type, uri, flags, &isStsHost);
+ }
uint32_t bucketId;
if (isStsHost) {
diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js
index 939a0db982d8..47a3156159f0 100644
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -884,7 +884,7 @@ WebappsApplicationMgmt.prototype = {
}
break;
case "Webapps:Uninstall:Return:OK":
- Services.DOMRequest.fireSuccess(req, msg.origin);
+ Services.DOMRequest.fireSuccess(req, msg.manifestURL);
break;
case "Webapps:Uninstall:Return:KO":
Services.DOMRequest.fireError(req, "NOT_INSTALLED");
diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm
index a242eba6466a..2e6c76feba37 100755
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2080,14 +2080,11 @@ this.DOMApplicationRegistry = {
return false;
}
- // Disallow multiple hosted apps installations from the same origin for now.
- // We will remove this code after multiple apps per origin are supported (bug 778277).
- // This will also disallow reinstalls from the same origin for now.
+ // Disallow reinstalls from the same manifest url for now.
for (let id in this.webapps) {
- if (this.webapps[id].origin == app.origin &&
- !this.webapps[id].packageHash &&
+ if (this.webapps[id].manifestURL == app.manifestURL &&
this._isLaunchable(this.webapps[id])) {
- sendError("MULTIPLE_APPS_PER_ORIGIN_FORBIDDEN");
+ sendError("REINSTALL_FORBIDDEN");
return false;
}
}
diff --git a/dom/apps/tests/mochitest.ini b/dom/apps/tests/mochitest.ini
index 47baedde67f7..8b67d343d118 100644
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -19,6 +19,7 @@ support-files =
[test_app_update.html]
[test_bug_795164.html]
+[test_install_multiple_apps_origin.html]
[test_install_receipts.html]
[test_marketplace_pkg_install.html]
skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
diff --git a/dom/apps/tests/test_install_multiple_apps_origin.html b/dom/apps/tests/test_install_multiple_apps_origin.html
new file mode 100644
index 000000000000..4aef6d1a7edb
--- /dev/null
+++ b/dom/apps/tests/test_install_multiple_apps_origin.html
@@ -0,0 +1,112 @@
+
+
+
+
+ Test for Bug {778277}
+
+
+
+
+
+
+Mozilla Bug {778277}
+
+
+
+
+
+
+
+
+
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index 1a0fd0facdbd..3dfe0bf6d73c 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -98,6 +98,7 @@
#include "nsIPresShell.h"
#include "nsIRemoteBlob.h"
#include "nsIScriptError.h"
+#include "nsISiteSecurityService.h"
#include "nsIStyleSheet.h"
#include "nsISupportsPrimitives.h"
#include "nsIURIFixup.h"
@@ -3239,6 +3240,23 @@ ContentParent::RecvGetSystemMemory(const uint64_t& aGetterId)
return true;
}
+bool
+ContentParent::RecvIsSecureURI(const uint32_t& type,
+ const URIParams& uri,
+ const uint32_t& flags,
+ bool* isSecureURI)
+{
+ nsCOMPtr sss(do_GetService(NS_SSSERVICE_CONTRACTID));
+ if (!sss) {
+ return false;
+ }
+ nsCOMPtr ourURI = DeserializeURI(uri);
+ if (!ourURI) {
+ return false;
+ }
+ nsresult rv = sss->IsSecureURI(type, ourURI, flags, isSecureURI);
+ return NS_SUCCEEDED(rv);
+}
bool
ContentParent::RecvLoadURIExternal(const URIParams& uri)
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
index f2546c1b8445..f29895120cf7 100644
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -422,6 +422,9 @@ private:
virtual bool RecvGetRandomValues(const uint32_t& length,
InfallibleTArray* randomValues) MOZ_OVERRIDE;
+ virtual bool RecvIsSecureURI(const uint32_t& type, const URIParams& uri,
+ const uint32_t& flags, bool* isSecureURI);
+
virtual bool DeallocPHalParent(PHalParent*) MOZ_OVERRIDE;
virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor) MOZ_OVERRIDE;
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
index 3f6a86529a4e..e3581bec1a34 100644
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -478,6 +478,9 @@ parent:
async GetSystemMemory(uint64_t getterId);
+ sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
+ returns (bool isSecureURI);
+
PHal();
PIndexedDB();
diff --git a/dom/system/gonk/NetworkUtils.cpp b/dom/system/gonk/NetworkUtils.cpp
index 59534d31735b..90ba37803d68 100644
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -97,6 +97,8 @@ typedef Tuple3 QueueData;
#define GET_CURRENT_CALLBACK (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].c)
#define GET_CURRENT_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a->mData)
+#define CNT_OF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
+
static NetworkUtils* gNetworkUtils;
static nsTArray gCommandQueue;
static CurrentCommand gCurrentCommand;
@@ -1087,54 +1089,64 @@ NetworkUtils::~NetworkUtils()
void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
{
- bool ret = true;
+ typedef bool (NetworkUtils::*CommandHandler)(NetworkParams&);
- if (aOptions.mCmd.EqualsLiteral("removeNetworkRoute")) {
- removeNetworkRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setDNS")) {
- setDNS(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setDefaultRouteAndDNS")) {
- setDefaultRouteAndDNS(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("removeDefaultRoute")) {
- removeDefaultRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("addHostRoute")) {
- addHostRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("removeHostRoute")) {
- removeHostRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("removeHostRoutes")) {
- removeHostRoutes(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("addSecondaryRoute")) {
- addSecondaryRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("removeSecondaryRoute")) {
- removeSecondaryRoute(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("getNetworkInterfaceStats")) {
- getNetworkInterfaceStats(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setNetworkInterfaceAlarm")) {
- setNetworkInterfaceAlarm(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("enableNetworkInterfaceAlarm")) {
- enableNetworkInterfaceAlarm(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("disableNetworkInterfaceAlarm")) {
- disableNetworkInterfaceAlarm(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setWifiOperationMode")) {
- setWifiOperationMode(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setDhcpServer")) {
- setDhcpServer(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setWifiTethering")) {
- setWifiTethering(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("setUSBTethering")) {
- setUSBTethering(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("enableUsbRndis")) {
- enableUsbRndis(aOptions);
- } else if (aOptions.mCmd.EqualsLiteral("updateUpStream")) {
- updateUpStream(aOptions);
- } else {
- WARN("unknon message");
+ const static struct {
+ const char* mCommandName;
+ CommandHandler mCommandHandler;
+ } COMMAND_HANDLER_TABLE[] = {
+
+ // For command 'testCommand', BUILD_ENTRY(testCommand) will generate
+ // {"testCommand", NetworkUtils::testCommand}
+ #define BUILD_ENTRY(c) {#c, &NetworkUtils::c}
+
+ BUILD_ENTRY(removeNetworkRoute),
+ BUILD_ENTRY(setDNS),
+ BUILD_ENTRY(setDefaultRouteAndDNS),
+ BUILD_ENTRY(removeDefaultRoute),
+ BUILD_ENTRY(addHostRoute),
+ BUILD_ENTRY(removeHostRoute),
+ BUILD_ENTRY(removeHostRoutes),
+ BUILD_ENTRY(addSecondaryRoute),
+ BUILD_ENTRY(removeSecondaryRoute),
+ BUILD_ENTRY(getNetworkInterfaceStats),
+ BUILD_ENTRY(setNetworkInterfaceAlarm),
+ BUILD_ENTRY(enableNetworkInterfaceAlarm),
+ BUILD_ENTRY(disableNetworkInterfaceAlarm),
+ BUILD_ENTRY(setWifiOperationMode),
+ BUILD_ENTRY(setDhcpServer),
+ BUILD_ENTRY(setWifiTethering),
+ BUILD_ENTRY(setUSBTethering),
+ BUILD_ENTRY(enableUsbRndis),
+ BUILD_ENTRY(updateUpStream),
+
+ #undef BUILD_ENTRY
+ };
+
+ // Loop until we find the command name which matches aOptions.mCmd.
+ CommandHandler handler = nullptr;
+ for (size_t i = 0; i < CNT_OF_ARRAY(COMMAND_HANDLER_TABLE); i++) {
+ if (aOptions.mCmd.EqualsASCII(COMMAND_HANDLER_TABLE[i].mCommandName)) {
+ handler = COMMAND_HANDLER_TABLE[i].mCommandHandler;
+ break;
+ }
+ }
+
+ if (!handler) {
+ // Command not found in COMMAND_HANDLER_TABLE.
+ WARN("unknown message: %s", NS_ConvertUTF16toUTF8(aOptions.mCmd).get());
return;
}
+ // Command matches! Dispatch to the handler.
+ (this->*handler)(aOptions);
+
if (!aOptions.mIsAsync) {
+ // The requested command is synchronous, which implies the actual result
+ // from netd is not important to the client. So, just notify the
+ // registered callback.
NetworkResultOptions result;
- result.mRet = ret;
+ result.mRet = true;
postMessage(aOptions, result);
}
}
diff --git a/dom/telephony/gonk/TelephonyService.js b/dom/telephony/gonk/TelephonyService.js
index 99ff96b69832..635195b499d2 100644
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -730,10 +730,11 @@ TelephonyService.prototype = {
serviceId: aClientId,
emergency: aCall.isEmergency,
duration: duration,
- direction: aCall.isOutgoing ? "outgoing" : "incoming"
+ direction: aCall.isOutgoing ? "outgoing" : "incoming",
+ hangUpLocal: aCall.hangUpLocal
};
- if(this._cdmaCallWaitingNumber != null) {
+ if (this._cdmaCallWaitingNumber != null) {
data.secondNumber = this._cdmaCallWaitingNumber;
this._cdmaCallWaitingNumber = null;
}
diff --git a/mobile/android/base/db/SearchHistoryProvider.java b/mobile/android/base/db/SearchHistoryProvider.java
index 06ecd40389a3..05d31fefd6aa 100644
--- a/mobile/android/base/db/SearchHistoryProvider.java
+++ b/mobile/android/base/db/SearchHistoryProvider.java
@@ -4,6 +4,7 @@
package org.mozilla.gecko.db;
+import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import android.content.ContentUris;
@@ -110,10 +111,11 @@ public class SearchHistoryProvider extends SharedBrowserDatabaseProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- String groupBy = null;
- String having = null;
+ final String groupBy = null;
+ final String having = null;
+ final String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
final Cursor cursor = getReadableDatabase(uri).query(SearchHistory.TABLE_NAME, projection,
- selection, selectionArgs, groupBy, having, sortOrder);
+ selection, selectionArgs, groupBy, having, sortOrder, limit);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
diff --git a/mobile/android/base/tests/testSearchHistoryProvider.java b/mobile/android/base/tests/testSearchHistoryProvider.java
index c1d487706ba6..e0ae0d871c4c 100644
--- a/mobile/android/base/tests/testSearchHistoryProvider.java
+++ b/mobile/android/base/tests/testSearchHistoryProvider.java
@@ -13,11 +13,13 @@ import org.mozilla.gecko.db.SearchHistoryProvider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
+import android.net.Uri;
public class testSearchHistoryProvider extends ContentProviderTest {
// Translations of "United Kingdom" in several different languages
- private static final String[] testStrings = {"An Ríocht Aontaithe", // Irish
+ private static final String[] testStrings = {
+ "An Ríocht Aontaithe", // Irish
"Angli", // Albanian
"Britanniarum Regnum", // Latin
"Britio", // Esperanto
@@ -95,6 +97,7 @@ public class testSearchHistoryProvider extends ContentProviderTest {
mTests.add(new TestInsert());
mTests.add(new TestUnicodeQuery());
mTests.add(new TestTimestamp());
+ mTests.add(new TestLimit());
mTests.add(new TestDelete());
mTests.add(new TestIncrement());
}
@@ -111,6 +114,80 @@ public class testSearchHistoryProvider extends ContentProviderTest {
}
}
+ /**
+ * Verify that we can pass a LIMIT clause using a query parameter.
+ */
+ private class TestLimit extends TestCase {
+ @Override
+ public void test() throws Exception {
+ ContentValues cv;
+ for (int i = 0; i < testStrings.length; i++) {
+ cv = new ContentValues();
+ cv.put(SearchHistory.QUERY, testStrings[i]);
+ mProvider.insert(SearchHistory.CONTENT_URI, cv);
+ }
+
+ final int limit = 5;
+
+ // Test 1: Handle proper input.
+
+ Uri uri = SearchHistory.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit))
+ .build();
+
+ Cursor c = mProvider.query(uri, null, null, null, null);
+ try {
+ mAsserter.is(c.getCount(), limit,
+ String.format("Should have %d results", limit));
+ } finally {
+ c.close();
+ }
+
+ // Test 2: Empty input yields all results.
+
+ uri = SearchHistory.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(BrowserContract.PARAM_LIMIT, "")
+ .build();
+
+ c = mProvider.query(uri, null, null, null, null);
+ try {
+ mAsserter.is(c.getCount(), testStrings.length, "Should have all results");
+ } finally {
+ c.close();
+ }
+
+ // Test 3: Illegal params.
+
+ String[] illegalParams = new String[] {"a", "-1"};
+ boolean success = true;
+
+ for (String param : illegalParams) {
+ success = true;
+
+ uri = SearchHistory.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(BrowserContract.PARAM_LIMIT, param)
+ .build();
+
+ try {
+ c = mProvider.query(uri, null, null, null, null);
+ success = false;
+ } catch(IllegalArgumentException e) {
+ // noop.
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ mAsserter.ok(success, "LIMIT", param + " should have been an invalid argument");
+ }
+
+ }
+ }
+
/**
* Verify that we can insert values into the DB, including unicode.
*/
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index cdc665afe1d2..2dfa1b41239f 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -654,6 +654,10 @@ pref("devtools.dump.emit", false);
pref("devtools.discovery.log", false);
// Disable scanning for DevTools devices via WiFi
pref("devtools.remote.wifi.scan", false);
+// Hide UI options for controlling device visibility over WiFi
+// N.B.: This does not set whether the device can be discovered via WiFi, only
+// whether the UI control to make such a choice is shown to the user
+pref("devtools.remote.wifi.visible", false);
// view source
pref("view_source.syntax_highlight", true);
diff --git a/security/manager/boot/src/nsSiteSecurityService.cpp b/security/manager/boot/src/nsSiteSecurityService.cpp
index 3cab048ec8f6..30cc0de889bd 100644
--- a/security/manager/boot/src/nsSiteSecurityService.cpp
+++ b/security/manager/boot/src/nsSiteSecurityService.cpp
@@ -20,6 +20,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/LinkedList.h"
#include "nsSecurityHeaderParser.h"
+#include "nsXULAppAPI.h"
// A note about the preload list:
// When a site specifically disables sts by sending a header with
@@ -87,6 +88,11 @@ NS_IMPL_ISUPPORTS(nsSiteSecurityService,
nsresult
nsSiteSecurityService::Init()
{
+ // Child processes are not allowed direct access to this.
+ if (XRE_GetProcessType() != GeckoProcessType_Default) {
+ MOZ_CRASH("Child process: no direct access to nsSiteSecurityService");
+ }
+
nsresult rv;
mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
diff --git a/toolkit/devtools/server/actors/call-watcher.js b/toolkit/devtools/server/actors/call-watcher.js
index e9c902400f28..ab743bcf9d73 100644
--- a/toolkit/devtools/server/actors/call-watcher.js
+++ b/toolkit/devtools/server/actors/call-watcher.js
@@ -210,9 +210,12 @@ let FunctionCallActor = protocol.ActorClass({
// XXX: All of this sucks. Make this smarter, so that the frontend
// can inspect each argument, be it object or primitive. Bug 978960.
let serializeArgs = () => args.map((arg, i) => {
- if (typeof arg == "undefined") {
+ if (arg === undefined) {
return "undefined";
}
+ if (arg === null) {
+ return "null";
+ }
if (typeof arg == "function") {
return "Function";
}
@@ -631,7 +634,7 @@ CallWatcherFront.ENUM_METHODS[CallWatcherFront.CANVAS_WEBGL_CONTEXT] = {
stencilOpSeparate: [0, 1, 2, 3],
texImage2D: (args) => args.length > 6 ? [0, 2, 6, 7] : [0, 2, 3, 4],
texParameterf: [0, 1],
- texParameteri: [0, 1],
+ texParameteri: [0, 1, 2],
texSubImage2D: (args) => args.length === 9 ? [0, 6, 7] : [0, 4, 5],
vertexAttribPointer: [2]
};
@@ -643,7 +646,7 @@ CallWatcherFront.ENUM_METHODS[CallWatcherFront.CANVAS_WEBGL_CONTEXT] = {
* For example, when gl.clear(gl.COLOR_BUFFER_BIT) is called, the actual passed
* argument's value is 16384, which we want identified as "COLOR_BUFFER_BIT".
*/
-var gEnumRegex = /^[A-Z_]+$/;
+var gEnumRegex = /^[A-Z][A-Z0-9_]+$/;
var gEnumsLookupTable = {};
// These values are returned from errors, or empty values,
diff --git a/toolkit/devtools/server/actors/canvas.js b/toolkit/devtools/server/actors/canvas.js
index 391e4f413a01..df9ac1aabd35 100644
--- a/toolkit/devtools/server/actors/canvas.js
+++ b/toolkit/devtools/server/actors/canvas.js
@@ -80,6 +80,7 @@ protocol.types.addDictType("snapshot-image", {
index: "number",
width: "number",
height: "number",
+ scaling: "number",
flipped: "boolean",
pixels: "uint32-array"
});
@@ -117,7 +118,7 @@ let FrameSnapshotActor = protocol.ActorClass({
protocol.Actor.prototype.initialize.call(this, conn);
this._contentCanvas = canvas;
this._functionCalls = calls;
- this._lastDrawCallScreenshot = screenshot;
+ this._animationFrameEndScreenshot = screenshot;
},
/**
@@ -127,7 +128,7 @@ let FrameSnapshotActor = protocol.ActorClass({
return {
calls: this._functionCalls,
thumbnails: this._functionCalls.map(e => e._thumbnail).filter(e => !!e),
- screenshot: this._lastDrawCallScreenshot
+ screenshot: this._animationFrameEndScreenshot
};
}, {
response: { overview: RetVal("snapshot-overview") }
@@ -148,7 +149,7 @@ let FrameSnapshotActor = protocol.ActorClass({
// To get a screenshot, replay all the steps necessary to render the frame,
// by invoking the context calls up to and including the specified one.
// This will be done in a custom framebuffer in case of a WebGL context.
- let { replayContext, lastDrawCallIndex } = ContextUtils.replayAnimationFrame({
+ let replayData = ContextUtils.replayAnimationFrame({
contextType: global,
canvas: canvas,
calls: calls,
@@ -156,24 +157,25 @@ let FrameSnapshotActor = protocol.ActorClass({
last: index
});
- // To keep things fast, generate an image that's relatively small.
- let dimensions = Math.min(CanvasFront.SCREENSHOT_HEIGHT_MAX, canvas.height);
+ let { replayContext, replayContextScaling, lastDrawCallIndex, doCleanup } = replayData;
+ let [left, top, width, height] = replayData.replayViewport;
let screenshot;
// Depending on the canvas' context, generating a screenshot is done
- // in different ways. In case of the WebGL context, we also need to reset
- // the framebuffer binding to the default value.
+ // in different ways.
if (global == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
- screenshot = ContextUtils.getPixelsForWebGL(replayContext);
- replayContext.bindFramebuffer(replayContext.FRAMEBUFFER, null);
+ screenshot = ContextUtils.getPixelsForWebGL(replayContext, left, top, width, height);
screenshot.flipped = true;
- }
- // In case of 2D contexts, no additional special treatment is necessary.
- else if (global == CallWatcherFront.CANVAS_2D_CONTEXT) {
- screenshot = ContextUtils.getPixelsFor2D(replayContext);
+ } else if (global == CallWatcherFront.CANVAS_2D_CONTEXT) {
+ screenshot = ContextUtils.getPixelsFor2D(replayContext, left, top, width, height);
screenshot.flipped = false;
}
+ // In case of the WebGL context, we also need to reset the framebuffer
+ // binding to the original value, after generating the screenshot.
+ doCleanup();
+
+ screenshot.scaling = replayContextScaling;
screenshot.index = lastDrawCallIndex;
return screenshot;
}, {
@@ -188,17 +190,17 @@ let FrameSnapshotActor = protocol.ActorClass({
let FrameSnapshotFront = protocol.FrontClass(FrameSnapshotActor, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client, form);
- this._lastDrawCallScreenshot = null;
+ this._animationFrameEndScreenshot = null;
this._cachedScreenshots = new WeakMap();
},
/**
- * This implementation caches the last draw call screenshot to optimize
+ * This implementation caches the animation frame end screenshot to optimize
* frontend requests to `generateScreenshotFor`.
*/
getOverview: custom(function() {
return this._getOverview().then(data => {
- this._lastDrawCallScreenshot = data.screenshot;
+ this._animationFrameEndScreenshot = data.screenshot;
return data;
});
}, {
@@ -211,7 +213,7 @@ let FrameSnapshotFront = protocol.FrontClass(FrameSnapshotActor, {
*/
generateScreenshotFor: custom(function(functionCall) {
if (CanvasFront.ANIMATION_GENERATORS.has(functionCall.name)) {
- return promise.resolve(this._lastDrawCallScreenshot);
+ return promise.resolve(this._animationFrameEndScreenshot);
}
let cachedScreenshot = this._cachedScreenshots.get(functionCall);
if (cachedScreenshot) {
@@ -370,12 +372,13 @@ let CanvasActor = exports.CanvasActor = protocol.ActorClass({
let index = this._lastDrawCallIndex;
let width = this._lastContentCanvasWidth;
let height = this._lastContentCanvasHeight;
- let flipped = this._lastThumbnailFlipped;
+ let flipped = !!this._lastThumbnailFlipped; // undefined -> false
let pixels = ContextUtils.getPixelStorage()["32bit"];
- let lastDrawCallScreenshot = {
+ let animationFrameEndScreenshot = {
index: index,
width: width,
height: height,
+ scaling: 1,
flipped: flipped,
pixels: pixels.subarray(0, width * height)
};
@@ -385,7 +388,7 @@ let CanvasActor = exports.CanvasActor = protocol.ActorClass({
let frameSnapshot = new FrameSnapshotActor(this.conn, {
canvas: this._lastDrawCallCanvas,
calls: functionCalls,
- screenshot: lastDrawCallScreenshot
+ screenshot: animationFrameEndScreenshot
});
this._currentAnimationFrameSnapshot.resolve(frameSnapshot);
@@ -408,7 +411,7 @@ let CanvasActor = exports.CanvasActor = protocol.ActorClass({
let h = this._lastContentCanvasHeight = contentCanvas.height;
// To keep things fast, generate images of small and fixed dimensions.
- let dimensions = CanvasFront.THUMBNAIL_HEIGHT;
+ let dimensions = CanvasFront.THUMBNAIL_SIZE;
let thumbnail;
// Create a thumbnail on every draw call on the canvas context, to augment
@@ -529,7 +532,7 @@ let ContextUtils = {
*/
resizePixels: function(srcPixels, srcWidth, srcHeight, dstHeight) {
let screenshotRatio = dstHeight / srcHeight;
- let dstWidth = Math.floor(srcWidth * screenshotRatio);
+ let dstWidth = (srcWidth * screenshotRatio) | 0;
// Use a plain array instead of a Uint32Array to make serializing faster.
let dstPixels = new Array(dstWidth * dstHeight);
@@ -540,8 +543,8 @@ let ContextUtils = {
for (let dstX = 0; dstX < dstWidth; dstX++) {
for (let dstY = 0; dstY < dstHeight; dstY++) {
- let srcX = Math.floor(dstX / screenshotRatio);
- let srcY = Math.floor(dstY / screenshotRatio);
+ let srcX = (dstX / screenshotRatio) | 0;
+ let srcY = (dstY / screenshotRatio) | 0;
let cPos = srcX + srcWidth * srcY;
let dPos = dstX + dstWidth * dstY;
let color = dstPixels[dPos] = srcPixels[cPos];
@@ -566,8 +569,11 @@ let ContextUtils = {
* the respective canvas, and the rendering will be performed into it.
* This is necessary because some state (like shaders, textures etc.) can't
* be shared between two different WebGL contexts.
- * Hopefully, once SharedResources are a thing this won't be necessary:
- * http://www.khronos.org/webgl/wiki/SharedResouces
+ * - Hopefully, once SharedResources are a thing this won't be necessary:
+ * http://www.khronos.org/webgl/wiki/SharedResouces
+ * - Alternatively, we could pursue the idea of using the same context
+ * for multiple canvases, instead of trying to share resources:
+ * https://www.khronos.org/webgl/public-mailing-list/archives/1210/msg00058.html
*
* In case of a 2D context, a new canvas is created, since there's no
* intrinsic state that can't be easily duplicated.
@@ -583,33 +589,59 @@ let ContextUtils = {
* @param number last
* The last (inclusive) function call to end at.
* @return object
- * The context on which the specified calls were invoked and the
- * last registered draw call's index.
+ * The context on which the specified calls were invoked, the
+ * last registered draw call's index and a cleanup function, which
+ * needs to be called whenever any potential followup work is finished.
*/
replayAnimationFrame: function({ contextType, canvas, calls, first, last }) {
let w = canvas.width;
let h = canvas.height;
- let replayCanvas;
let replayContext;
+ let replayContextScaling;
+ let customViewport;
let customFramebuffer;
let lastDrawCallIndex = -1;
+ let doCleanup = () => {};
// In case of WebGL contexts, rendering will be done offscreen, in a
- // custom framebuffer, but on the provided canvas context.
+ // custom framebuffer, but using the same provided context. This is
+ // necessary because it's very memory-unfriendly to rebuild all the
+ // required GL state (like recompiling shaders, setting global flags, etc.)
+ // in an entirely new canvas. However, special care is needed to not
+ // permanently affect the existing GL state in the process.
if (contextType == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
- replayCanvas = canvas;
- replayContext = this.getWebGLContext(replayCanvas);
- customFramebuffer = this.createBoundFramebuffer(replayContext, w, h);
+ // To keep things fast, replay the context calls on a framebuffer
+ // of smaller dimensions than the actual canvas (maximum 256x256 pixels).
+ let scaling = Math.min(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, h) / h;
+ replayContextScaling = scaling;
+ w = (w * scaling) | 0;
+ h = (h * scaling) | 0;
+
+ // Fetch the same WebGL context and bind a new framebuffer.
+ let gl = replayContext = this.getWebGLContext(canvas);
+ let { newFramebuffer, oldFramebuffer } = this.createBoundFramebuffer(gl, w, h);
+ customFramebuffer = newFramebuffer;
+
+ // Set the viewport to match the new framebuffer's dimensions.
+ let { newViewport, oldViewport } = this.setCustomViewport(gl, w, h);
+ customViewport = newViewport;
+
+ // Revert the framebuffer and viewport to the original values.
+ doCleanup = () => {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer);
+ gl.viewport.apply(gl, oldViewport);
+ };
}
// In case of 2D contexts, draw everything on a separate canvas context.
else if (contextType == CallWatcherFront.CANVAS_2D_CONTEXT) {
let contentDocument = canvas.ownerDocument;
- replayCanvas = contentDocument.createElement("canvas");
+ let replayCanvas = contentDocument.createElement("canvas");
replayCanvas.width = w;
replayCanvas.height = h;
replayContext = replayCanvas.getContext("2d");
- replayContext.clearRect(0, 0, w, h);
+ replayContextScaling = 1;
+ customViewport = [0, 0, w, h];
}
// Replay all the context calls up to and including the specified one.
@@ -620,23 +652,33 @@ let ContextUtils = {
// to the default value, since we want to perform the rendering offscreen.
if (name == "bindFramebuffer" && args[1] == null) {
replayContext.bindFramebuffer(replayContext.FRAMEBUFFER, customFramebuffer);
- } else {
- if (type == CallWatcherFront.METHOD_FUNCTION) {
- replayContext[name].apply(replayContext, args);
- } else if (type == CallWatcherFront.SETTER_FUNCTION) {
- replayContext[name] = args;
- } else {
- // Ignore getter calls.
- }
- if (CanvasFront.DRAW_CALLS.has(name)) {
- lastDrawCallIndex = i;
+ continue;
+ }
+ // Also prevent WebGL context calls that try to change the viewport
+ // while our custom framebuffer is bound.
+ if (name == "viewport") {
+ let framebufferBinding = replayContext.getParameter(replayContext.FRAMEBUFFER_BINDING);
+ if (framebufferBinding == customFramebuffer) {
+ replayContext.viewport.apply(replayContext, customViewport);
+ continue;
}
}
+ if (type == CallWatcherFront.METHOD_FUNCTION) {
+ replayContext[name].apply(replayContext, args);
+ } else if (type == CallWatcherFront.SETTER_FUNCTION) {
+ replayContext[name] = args;
+ }
+ if (CanvasFront.DRAW_CALLS.has(name)) {
+ lastDrawCallIndex = i;
+ }
}
return {
replayContext: replayContext,
- lastDrawCallIndex: lastDrawCallIndex
+ replayContextScaling: replayContextScaling,
+ replayViewport: customViewport,
+ lastDrawCallIndex: lastDrawCallIndex,
+ doCleanup: doCleanup
};
},
@@ -691,16 +733,21 @@ let ContextUtils = {
* The generated framebuffer object.
*/
createBoundFramebuffer: function(gl, width, height) {
- let framebuffer = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ let oldFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ let oldRenderbufferBinding = gl.getParameter(gl.RENDERBUFFER_BINDING);
+ let oldTextureBinding = gl.getParameter(gl.TEXTURE_BINDING_2D);
- // Use a texture as the color rendebuffer attachment, since consumenrs of
+ let newFramebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, newFramebuffer);
+
+ // Use a texture as the color renderbuffer attachment, since consumers of
// this function will most likely want to read the rendered pixels back.
let colorBuffer = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorBuffer);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.generateMipmap(gl.TEXTURE_2D);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
let depthBuffer = gl.createRenderbuffer();
@@ -710,10 +757,24 @@ let ContextUtils = {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorBuffer, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ gl.bindTexture(gl.TEXTURE_2D, oldTextureBinding);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbufferBinding);
- return framebuffer;
+ return { oldFramebuffer, newFramebuffer };
+ },
+
+ /**
+ * Sets the viewport of the drawing buffer for a WebGL context.
+ * @param WebGLRenderingContext gl
+ * @param number width
+ * @param number height
+ */
+ setCustomViewport: function(gl, width, height) {
+ let oldViewport = XPCNativeWrapper.unwrap(gl.getParameter(gl.VIEWPORT));
+ let newViewport = [0, 0, width, height];
+ gl.viewport.apply(gl, newViewport);
+
+ return { oldViewport, newViewport };
}
};
@@ -734,8 +795,8 @@ CanvasFront.CANVAS_CONTEXTS = new Set(CANVAS_CONTEXTS);
CanvasFront.ANIMATION_GENERATORS = new Set(ANIMATION_GENERATORS);
CanvasFront.DRAW_CALLS = new Set(DRAW_CALLS);
CanvasFront.INTERESTING_CALLS = new Set(INTERESTING_CALLS);
-CanvasFront.THUMBNAIL_HEIGHT = 50; // px
-CanvasFront.SCREENSHOT_HEIGHT_MAX = 256; // px
+CanvasFront.THUMBNAIL_SIZE = 50; // px
+CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT = 256; // px
CanvasFront.INVALID_SNAPSHOT_IMAGE = {
index: -1,
width: 0,