Merge m-c to b2g-inbound.

This commit is contained in:
Ryan VanderMeulen 2014-04-17 22:37:10 -04:00
commit 2d69e0283b
259 changed files with 5540 additions and 2278 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 916012 moves definition from one WEBIDL_FILE to another (Bug 979886)
Bug 995411 moves some files around in gfx/layers and widget/xpwidget

View File

@ -65,8 +65,12 @@ exports.install = function install(xpiPath) {
// Order AddonManager to install the addon
AddonManager.getInstallForFile(file, function(install) {
install.addListener(listener);
install.install();
if (install.error != null) {
install.addListener(listener);
install.install();
} else {
reject(install.error);
}
});
return promise;

View File

@ -286,17 +286,6 @@ const ContentWorker = Object.freeze({
value: self
});
// Deprecated use of on/postMessage from globals
exports.postMessage = function deprecatedPostMessage() {
console.error("DEPRECATED: The global `postMessage()` function in " +
"content scripts is deprecated in favor of the " +
"`self.postMessage()` function, which works the same. " +
"Replace calls to `postMessage()` with calls to " +
"`self.postMessage()`." +
"For more info on `self.on`, see " +
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
return self.postMessage.apply(null, arguments);
};
exports.on = function deprecatedOn() {
console.error("DEPRECATED: The global `on()` function in content " +
"scripts is deprecated in favor of the `self.on()` " +

View File

@ -46,9 +46,11 @@ function onDocumentReady2Translate(event) {
try {
// Finally display document when we finished replacing all text content
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
if (document.defaultView) {
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
}
}
catch(e) {
console.exception(e);

View File

@ -223,7 +223,6 @@ exports.show = show
function setupPanelFrame(frame) {
frame.setAttribute("flex", 1);
frame.setAttribute("transparent", "transparent");
frame.setAttribute("showcaret", true);
frame.setAttribute("autocompleteenabled", true);
if (platform === "darwin") {
frame.style.borderRadius = "6px";

View File

@ -191,9 +191,6 @@ exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
helper.createWorker(
'new ' + function ContentScriptScope() {
assert(postMessage === postMessage,
"verify that we doesn't generate multiple functions for the same method");
var json = JSON.stringify({foo : "bar\n \"escaped\"."});
document.getElementById("iframe").contentWindow.postMessage(json, "*");

View File

@ -26,7 +26,13 @@ const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
function makeWindow(contentURL) {
const WINDOW_SCRIPT_URL = "data:text/html;charset=utf-8," +
"<script>window.addEventListener('message', function (e) {" +
" if (e.data === 'from -> content-script')" +
" window.postMessage('from -> window', '*');" +
"});</script>";
function makeWindow() {
let content =
"<?xml version=\"1.0\"?>" +
"<window " +
@ -782,50 +788,6 @@ exports["test:check worker API with page history"] = WorkerTest(
}
);
exports["test:global postMessage"] = WorkerTest(
DEFAULT_CONTENT_URL,
function(assert, browser, done) {
let { loader } = LoaderWithHookedConsole(module, onMessage);
setPref(DEPRECATE_PREF, true);
// Intercept all console method calls
let seenMessages = 0;
function onMessage(type, message) {
seenMessages++;
assert.equal(type, "error", "Should be an error");
assert.equal(message, "DEPRECATED: The global `postMessage()` function in " +
"content scripts is deprecated in favor of the " +
"`self.postMessage()` function, which works the same. " +
"Replace calls to `postMessage()` with calls to " +
"`self.postMessage()`." +
"For more info on `self.on`, see " +
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.",
"Should have seen the deprecation message")
}
assert.notEqual(browser.contentWindow.location.href, "about:blank",
"window is now on the right document");
let window = browser.contentWindow
let worker = loader.require("sdk/content/worker").Worker({
window: window,
contentScript: "new " + function WorkerScope() {
postMessage("success");
},
contentScriptWhen: "ready",
onMessage: function(msg) {
assert.equal("success", msg, "Should have seen the right postMessage call");
assert.equal(1, seenMessages, "Should have seen the deprecation message");
done();
}
});
assert.equal(worker.url, window.location.href,
"worker.url works");
worker.postMessage("hi!");
}
);
exports['test:conentScriptFile as URL instance'] = WorkerTest(
DEFAULT_CONTENT_URL,
function(assert, browser, done) {
@ -889,8 +851,8 @@ exports["test:onDetach in contentScript on destroy"] = WorkerTest(
})
},
});
browser.contentWindow.addEventListener('hashchange', _ => {
assert.equal(browser.contentWindow.location.hash, '#detach!',
browser.contentWindow.addEventListener('hashchange', _ => {
assert.equal(browser.contentWindow.location.hash, '#detach!',
"location.href is as expected");
done();
})
@ -910,8 +872,8 @@ exports["test:onDetach in contentScript on unload"] = WorkerTest(
})
},
});
browser.contentWindow.addEventListener('hashchange', _ => {
assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
browser.contentWindow.addEventListener('hashchange', _ => {
assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
"location.href is as expected");
done();
})
@ -954,4 +916,25 @@ exports["test:console method log functions properly"] = WorkerTest(
}
);
exports["test:global postMessage"] = WorkerTest(
WINDOW_SCRIPT_URL,
function(assert, browser, done) {
let contentScript = "window.addEventListener('message', function (e) {" +
" if (e.data === 'from -> window')" +
" self.port.emit('response', e.data, e.origin);" +
"});" +
"postMessage('from -> content-script', '*');";
let { loader } = LoaderWithHookedConsole(module);
let worker = loader.require("sdk/content/worker").Worker({
window: browser.contentWindow,
contentScriptWhen: "ready",
contentScript: contentScript
});
worker.port.on("response", (data, origin) => {
assert.equal(data, "from -> window", "Communication from content-script to window completed");
done();
});
});
require("test").run(exports);

View File

@ -1,12 +1,10 @@
/* 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 { Loader } = require('sdk/test/loader');
const Pages = require("sdk/page-worker");
const Page = Pages.Page;
const { Page } = require("sdk/page-worker");
const { URL } = require("sdk/url");
const fixtures = require("./fixtures");
const testURI = fixtures.url("test.html");
@ -93,17 +91,17 @@ exports.testPageProperties = function(assert) {
exports.testConstructorAndDestructor = function(assert, done) {
let loader = Loader(module);
let Pages = loader.require("sdk/page-worker");
let { Page } = loader.require("sdk/page-worker");
let global = loader.sandbox("sdk/page-worker");
let pagesReady = 0;
let page1 = Pages.Page({
let page1 = Page({
contentScript: "self.postMessage('')",
contentScriptWhen: "end",
onMessage: pageReady
});
let page2 = Pages.Page({
let page2 = Page({
contentScript: "self.postMessage('')",
contentScriptWhen: "end",
onMessage: pageReady
@ -128,9 +126,9 @@ exports.testConstructorAndDestructor = function(assert, done) {
exports.testAutoDestructor = function(assert, done) {
let loader = Loader(module);
let Pages = loader.require("sdk/page-worker");
let { Page } = loader.require("sdk/page-worker");
let page = Pages.Page({
let page = Page({
contentScript: "self.postMessage('')",
contentScriptWhen: "end",
onMessage: function() {
@ -316,12 +314,12 @@ exports.testPingPong = function(assert, done) {
exports.testRedirect = function (assert, done) {
let page = Page({
contentURL: 'data:text/html;charset=utf-8,first-page',
contentScript: '(function () {' +
contentScriptWhen: "end",
contentScript: '' +
'if (/first-page/.test(document.location.href)) ' +
' document.location.href = "data:text/html;charset=utf-8,redirect";' +
'else ' +
' self.port.emit("redirect", document.location.href);' +
'})();'
' self.port.emit("redirect", document.location.href);'
});
page.port.on('redirect', function (url) {

View File

@ -1167,9 +1167,9 @@ let RemoteDebugger = {
};
#ifdef MOZ_WIDGET_GONK
DebuggerServer.onConnectionChange = function(what) {
DebuggerServer.on("connectionchange", function() {
AdbController.updateState();
}
});
#endif
}

View File

@ -3,6 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
no_tooltool=1
no_sccache=1
# This file is included at the top of all b2g mozconfigs

View File

@ -21,7 +21,8 @@ export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
# Use ccache
# Use sccache
no_sccache=
. "$topsrcdir/build/mozconfig.cache"
#B2G options

View File

@ -22,7 +22,8 @@ export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
# Use ccache
# Use sccache
no_sccache=
. "$topsrcdir/build/mozconfig.cache"
#B2G options

View File

@ -21,7 +21,8 @@ export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
# Use ccache
# Use sccache
no_sccache=
. "$topsrcdir/build/mozconfig.cache"
#B2G options

View File

@ -1,14 +0,0 @@
[
{
"size": 195,
"digest": "236362c71c433971c36b46d34e8560342435718364bc390df8de6a33249fb1fbf4fc3d0143f1e22bca262a7af7dc1b277a920bfde3ee8197eb07db2e7cef3e1f",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 63159127,
"digest": "fcf629c815b5cbed7858d7697815f355275dcc6b060ae5455b4b31fde0d78ebc176927564a5353ceacdb9f9c9bfc1357f1341bf6ba844c25153a89664e661510",
"algorithm": "sha512",
"filename": "gonk-toolchain-7.tar.bz2"
}
]

View File

@ -0,0 +1,14 @@
[
{
"size": 50,
"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 160232,
"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
"algorithm": "sha512",
"filename": "sccache.tar.xz"
}
]

View File

@ -0,0 +1,14 @@
[
{
"size": 50,
"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 160232,
"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
"algorithm": "sha512",
"filename": "sccache.tar.xz"
}
]

View File

@ -1,14 +0,0 @@
[
{
"size": 195,
"digest": "da2edcb1ec9b169f6c685d02ebd0bd4ad53ace2df58598f15e1bde43dd74bcb816620601587c97e636bda3327045b43c8f5e973672ebd904e06036f70466908f",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 121166734,
"digest": "10da1d28d49ff1aa9ad3d84e52235dc8ed7df6721530b896a53424480ec23a2e9f28cadd631f562342325611ecb72152be51f9b62616356f33005f6cc0fb82fe",
"algorithm": "sha512",
"filename": "gonk-toolchain-3.tar.bz2"
}
]

View File

@ -7,6 +7,7 @@ XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
});
const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger";
const DOORHANGER_ACTIVATE_DELAY_MS = 5000;
let gFxAccounts = {
@ -33,16 +34,6 @@ let gFxAccounts = {
];
},
// The set of topics that only the active window should handle.
get activeWindowTopics() {
// Do all this dance to lazy-load FxAccountsCommon.
delete this.activeWindowTopics;
return this.activeWindowTopics = new Set([
"weave:service:sync:start",
FxAccountsCommon.ONVERIFIED_NOTIFICATION
]);
},
get button() {
delete this.button;
return this.button = document.getElementById("PanelUI-fxa-status");
@ -64,9 +55,8 @@ let gFxAccounts = {
},
get isActiveWindow() {
let mostRecentNonPopupWindow =
RecentWindow.getMostRecentBrowserWindow({allowPopups: false});
return window == mostRecentNonPopupWindow;
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
return fm.activeWindow == window;
},
init: function () {
@ -79,6 +69,7 @@ let gFxAccounts = {
Services.obs.addObserver(this, topic, false);
}
addEventListener("activate", this);
gNavToolbox.addEventListener("customizationstarting", this);
gNavToolbox.addEventListener("customizationending", this);
@ -100,11 +91,6 @@ let gFxAccounts = {
},
observe: function (subject, topic) {
// Ignore certain topics if we're not the active window.
if (this.activeWindowTopics.has(topic) && !this.isActiveWindow) {
return;
}
switch (topic) {
case FxAccountsCommon.ONVERIFIED_NOTIFICATION:
Services.prefs.setBoolPref(PREF_SYNC_START_DOORHANGER, true);
@ -119,6 +105,10 @@ let gFxAccounts = {
},
onSyncStart: function () {
if (!this.isActiveWindow) {
return;
}
let showDoorhanger = false;
try {
@ -132,8 +122,17 @@ let gFxAccounts = {
},
handleEvent: function (event) {
this._inCustomizationMode = event.type == "customizationstarting";
this.updateUI();
if (event.type == "activate") {
// Our window might have been in the background while we received the
// sync:start notification. If still needed, show the doorhanger after
// a short delay. Without this delay the doorhanger would not show up
// or with a too small delay show up while we're still animating the
// window.
setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS);
} else {
this._inCustomizationMode = event.type == "customizationstarting";
this.updateUI();
}
},
showDoorhanger: function (id) {

View File

@ -6999,10 +6999,6 @@ var TabContextMenu = {
aPopupMenu.triggerNode : gBrowser.selectedTab;
let disabled = gBrowser.tabs.length == 1;
// Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
document.getElementById("context_closeTab").disabled =
disabled && gBrowser.tabContainer._closeWindowWithLastTab;
var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
for (let menuItem of menuItems)
menuItem.disabled = disabled;

View File

@ -51,6 +51,11 @@ function checkPage() {
// Now press the "Try Again" button
ok(gBrowser.contentDocument.getElementById("errorTryAgain"),
"The error page has got a #errorTryAgain element");
// Re-enable the proxy so example.com is resolved to localhost, rather than
// the actual example.com.
Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
gBrowser.contentDocument.getElementById("errorTryAgain").click();
ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
@ -60,7 +65,6 @@ function checkPage() {
}
registerCleanupFunction(function() {
Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
Services.prefs.setBoolPref("browser.cache.disk.enable", true);
Services.prefs.setBoolPref("browser.cache.memory.enable", true);
Services.io.offline = false;

View File

@ -1,3 +1,7 @@
/* 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/. */
const kObservedTopics = [
"getUserMedia:response:allow",
"getUserMedia:revoke",
@ -736,6 +740,50 @@ let gTests = [
info("request video, stop sharing resets video only");
yield stopAndCheckPerm(false, true);
}
},
{
desc: "'Always Allow' ignored and not shown on http pages",
run: function checkNoAlwaysOnHttp() {
// Load an http page instead of the https version.
let deferred = Promise.defer();
let browser = gBrowser.selectedTab.linkedBrowser;
browser.addEventListener("load", function onload() {
browser.removeEventListener("load", onload, true);
deferred.resolve();
}, true);
content.location = content.location.href.replace("https://", "http://");
yield deferred.promise;
// Initially set both permissions to 'allow'.
let Perms = Services.perms;
let uri = content.document.documentURIObject;
Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
Perms.add(uri, "camera", Perms.ALLOW_ACTION);
// Request devices and expect a prompt despite the saved 'Allow' permission,
// because the connection isn't secure.
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
content.wrappedJSObject.requestDevice(true, true);
});
expectObserverCalled("getUserMedia:request");
// Ensure that the 'Always Allow' action isn't shown.
let alwaysLabel = gNavigatorBundle.getString("getUserMedia.always.label");
ok(!!alwaysLabel, "found the 'Always Allow' localized label");
let labels = [];
let notification = PopupNotifications.panel.firstChild;
for (let node of notification.childNodes) {
if (node.localName == "menuitem")
labels.push(node.getAttribute("label"));
}
is(labels.indexOf(alwaysLabel), -1, "The 'Always Allow' item isn't shown");
// Cleanup.
yield closeStream(true);
Perms.remove(uri.host, "camera");
Perms.remove(uri.host, "microphone");
}
}
];

View File

@ -163,8 +163,8 @@ let gSyncPane = {
checkbox.checked = false;
}
checkbox.disabled = !allowPasswordsEngine;
help.hidden = allowPasswordsEngine;
checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
help.hidden = allowPasswordsEngine || enginesListDisabled;
});
// If fxAccountEnabled is false and we are in a "not configured" state,
// then fxAccounts is probably fully disabled rather than just unconfigured,

View File

@ -163,8 +163,8 @@ let gSyncPane = {
checkbox.checked = false;
}
checkbox.disabled = !allowPasswordsEngine;
help.hidden = allowPasswordsEngine;
checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
help.hidden = allowPasswordsEngine || enginesListDisabled;
});
// If fxAccountEnabled is false and we are in a "not configured" state,
// then fxAccounts is probably fully disabled rather than just unconfigured,

View File

@ -7,7 +7,7 @@ function test() {
let originalTab = gBrowser.tabs[0];
popup(originalTab);
ok(document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is disabled");
ok(!document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is enabled");
ok(document.getElementById("context_openTabInWindow").disabled, "The 'Move to New Window' menu item is disabled");
let newTabOne = gBrowser.addTab("about:blank", {skipAnimation: true});

View File

@ -1,4 +1,5 @@
no_tooltool=1
no_sccache=1
ac_add_options --with-l10n-base=../../l10n
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}

View File

@ -1,4 +1,5 @@
no_tooltool=1
no_sccache=1
. $topsrcdir/browser/config/mozconfigs/linux32/nightly

View File

@ -1,4 +1,5 @@
no_tooltool=1
no_sccache=1
ac_add_options --with-l10n-base=../../l10n
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}

View File

@ -1,4 +1,5 @@
no_tooltool=1
no_sccache=1
. $topsrcdir/browser/config/mozconfigs/linux64/nightly

View File

@ -16,9 +16,13 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
let require = devtools.require;
let Telemetry = require("devtools/shared/telemetry");
let EventEmitter = require("devtools/toolkit/event-emitter");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
let processes = Set();
/**
* Constructor for creating a process that will hold a chrome toolbox.
*
@ -30,15 +34,33 @@ this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
* An object with properties for configuring BrowserToolboxProcess.
*/
this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
let emitter = new EventEmitter();
this.on = emitter.on.bind(emitter);
this.off = emitter.off.bind(emitter);
this.once = emitter.once.bind(emitter);
// Forward any events to the shared emitter.
this.emit = function(...args) {
emitter.emit(...args);
BrowserToolboxProcess.emit(...args);
}
// If first argument is an object, use those properties instead of
// all three arguments
if (typeof aOnClose === "object") {
this._closeCallback = aOnClose.onClose;
this._runCallback = aOnClose.onRun;
if (aOnClose.onClose) {
this.on("close", aOnClose.onClose);
}
if (aOnClose.onRun) {
this.on("run", aOnClose.onRun);
}
this._options = aOnClose;
} else {
this._closeCallback = aOnClose;
this._runCallback = aOnRun;
if (aOnClose) {
this.on("close", aOnClose);
}
if (aOnRun) {
this.on("run", aOnRun);
}
this._options = aOptions || {};
}
@ -49,8 +71,12 @@ this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aO
this._initServer();
this._initProfile();
this._create();
processes.add(this);
};
EventEmitter.decorate(BrowserToolboxProcess);
/**
* Initializes and starts a chrome toolbox process.
* @return object
@ -59,6 +85,25 @@ BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) {
return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
};
/**
* Passes a set of options to the BrowserAddonActors for the given ID.
*
* @param aId string
* The ID of the add-on to pass the options to
* @param aOptions object
* The options.
* @return a promise that will be resolved when complete.
*/
BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
let promises = [];
for (let process of processes.values()) {
promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
}
return promise.all(promises);
};
BrowserToolboxProcess.prototype = {
/**
* Initializes the debugger server.
@ -77,6 +122,9 @@ BrowserToolboxProcess.prototype = {
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
}
if (!this.debuggerServer.initialized) {
@ -169,9 +217,7 @@ BrowserToolboxProcess.prototype = {
this._telemetry.toolOpened("jsbrowserdebugger");
dumpn("Chrome toolbox is now running...");
if (typeof this._runCallback == "function") {
this._runCallback.call({}, this);
}
this.emit("run", this);
},
/**
@ -196,9 +242,8 @@ BrowserToolboxProcess.prototype = {
dumpn("Chrome toolbox is now closed...");
this.closed = true;
if (typeof this._closeCallback == "function") {
this._closeCallback.call({}, this);
}
this.emit("close", this);
processes.delete(this);
}
};

View File

@ -9,9 +9,11 @@
const Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
let gClient;
let gConnectionTimeout;
@ -75,59 +77,97 @@ function submit() {
/**
* Connection is ready. List actors and build buttons.
*/
function onConnectionReady(aType, aTraits) {
let onConnectionReady = Task.async(function*(aType, aTraits) {
clearTimeout(gConnectionTimeout);
gClient.listTabs(function(aResponse) {
document.body.classList.remove("connecting");
document.body.classList.add("actors-mode");
let parent = document.getElementById("tabActors");
// Add Global Process debugging...
let globals = JSON.parse(JSON.stringify(aResponse));
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will always
// be there).
// Add one entry for each open tab.
for (let i = 0; i < aResponse.tabs.length; i++) {
buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
}
let gParent = document.getElementById("globalActors");
// Build the Remote Process button
if (Object.keys(globals).length > 1) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(globals, true);
let deferred = promise.defer();
gClient.listAddons(deferred.resolve);
let response = yield deferred.promise;
let parent = document.getElementById("addonActors")
if (!response.error && response.addons.length > 0) {
// Add one entry for each add-on.
for (let addon of response.addons) {
if (!addon.debuggable) {
continue;
}
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
a.className = "remote-process";
a.href = "#";
gParent.appendChild(a);
}
// Move the selected tab on top
let selectedLink = parent.querySelector("a.selected");
if (selectedLink) {
parent.insertBefore(selectedLink, parent.firstChild);
buildAddonLink(addon, parent);
}
}
else {
// Hide the section when there are no add-ons
parent.previousElementSibling.remove();
parent.remove();
}
// Ensure the first link is focused
let firstLink = parent.querySelector("a:first-of-type");
if (firstLink) {
firstLink.focus();
}
deferred = promise.defer();
gClient.listTabs(deferred.resolve);
response = yield deferred.promise;
});
parent = document.getElementById("tabActors");
// Add Global Process debugging...
let globals = JSON.parse(JSON.stringify(response));
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will always
// be there).
// Add one entry for each open tab.
for (let i = 0; i < response.tabs.length; i++) {
buildTabLink(response.tabs[i], parent, i == response.selected);
}
let gParent = document.getElementById("globalActors");
// Build the Remote Process button
if (Object.keys(globals).length > 1) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(globals, true);
}
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
a.className = "remote-process";
a.href = "#";
gParent.appendChild(a);
}
// Move the selected tab on top
let selectedLink = parent.querySelector("a.selected");
if (selectedLink) {
parent.insertBefore(selectedLink, parent.firstChild);
}
document.body.classList.remove("connecting");
document.body.classList.add("actors-mode");
// Ensure the first link is focused
let firstLink = parent.querySelector("a:first-of-type");
if (firstLink) {
firstLink.focus();
}
});
/**
* Build one button for an add-on actor.
*/
function buildAddonLink(addon, parent) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger");
}
a.textContent = addon.name;
a.title = addon.id;
a.href = "#";
parent.appendChild(a);
}
/**
* Build one button for an actor.
* Build one button for a tab actor.
*/
function buildLink(tab, parent, selected) {
function buildTabLink(tab, parent, selected) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(tab);
@ -173,7 +213,7 @@ function handleConnectionTimeout() {
* The user clicked on one of the buttons.
* Opens the toolbox.
*/
function openToolbox(form, chrome=false) {
function openToolbox(form, chrome=false, tool="webconsole") {
let options = {
form: form,
client: gClient,
@ -181,7 +221,7 @@ function openToolbox(form, chrome=false) {
};
devtools.TargetFactory.forRemoteTab(options).then((target) => {
let hostType = devtools.Toolbox.HostType.WINDOW;
gDevTools.showToolbox(target, "webconsole", hostType).then((toolbox) => {
gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
toolbox.once("destroyed", function() {
gClient.close();
});

View File

@ -39,6 +39,8 @@
<section id="actors-list">
<p>&availableTabs;</p>
<ul class="actors" id="tabActors"></ul>
<p>&availableAddons;</p>
<ul class="actors" id="addonActors"></ul>
<p>&availableProcesses;</p>
<ul class="actors" id="globalActors"></ul>
</section>

View File

@ -822,11 +822,17 @@ MarkupView.prototype = {
/**
* Mark the given node expanded.
* @param aNode The NodeFront to mark as expanded.
* @param {NodeFront} aNode The NodeFront to mark as expanded.
* @param {Boolean} aExpanded Whether the expand or collapse.
* @param {Boolean} aExpandDescendants Whether to expand all descendants too
*/
setNodeExpanded: function(aNode, aExpanded) {
setNodeExpanded: function(aNode, aExpanded, aExpandDescendants) {
if (aExpanded) {
this.expandNode(aNode);
if (aExpandDescendants) {
this.expandAll(aNode);
} else {
this.expandNode(aNode);
}
} else {
this.collapseNode(aNode);
}
@ -1413,7 +1419,7 @@ MarkupContainer.prototype = {
_onToggle: function(event) {
this.markup.navigate(this);
if(this.hasChildren) {
this.markup.setNodeExpanded(this.node, !this.expanded);
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
}
event.stopPropagation();
},

View File

@ -9,6 +9,7 @@ support-files =
doc_markup_pagesize_01.html
doc_markup_pagesize_02.html
doc_markup_search.html
doc_markup_toggle.html
doc_markup_tooltip.png
head.js
helper_attributes_test_runner.js
@ -37,3 +38,6 @@ support-files =
[browser_markupview_tag_edit_07.js]
[browser_markupview_tag_edit_08.js]
[browser_markupview_textcontent_edit_01.js]
[browser_markupview_toggle_01.js]
[browser_markupview_toggle_02.js]
[browser_markupview_toggle_03.js]

View File

@ -113,14 +113,6 @@ function pressKey(key) {
}
}
function waitForChildrenUpdated(inspector) {
let def = promise.defer();
inspector.markup._waitForChildren().then(() => {
executeSoon(def.resolve);
});
return def.promise;
}
function checkSelectedNode(key, className, inspector) {
let node = inspector.selection.node;

View File

@ -0,0 +1,45 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test toggling (expand/collapse) elements by clicking on twisties
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
info("Getting the container for the UL parent element");
let container = getContainerForRawNode("ul", inspector);
info("Clicking on the UL parent expander, and waiting for children");
let onChildren = waitForChildrenUpdated(inspector);
let onUpdated = inspector.once("inspector-updated");
EventUtils.synthesizeMouseAtCenter(container.expander, {},
inspector.markup.doc.defaultView);
yield onChildren;
yield onUpdated;
info("Checking that child LI elements have been created");
for (let li of content.document.querySelectorAll("li")) {
ok(getContainerForRawNode(li, inspector),
"A container for the child LI element was created");
}
ok(container.expanded, "Parent UL container is expanded");
info("Clicking again on the UL expander");
// No need to wait, this is a local, synchronous operation where nodes are
// only hidden from the view, not destroyed
EventUtils.synthesizeMouseAtCenter(container.expander, {},
inspector.markup.doc.defaultView);
info("Checking that child LI elements have been hidden");
for (let li of content.document.querySelectorAll("li")) {
let liContainer = getContainerForRawNode(li, inspector);
is(liContainer.elt.getClientRects().length, 0,
"The container for the child LI element was hidden");
}
ok(!container.expanded, "Parent UL container is collapsed");
});

View File

@ -0,0 +1,45 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test toggling (expand/collapse) elements by dbl-clicking on tag lines
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
info("Getting the container for the UL parent element");
let container = getContainerForRawNode("ul", inspector);
info("Dbl-clicking on the UL parent expander, and waiting for children");
let onChildren = waitForChildrenUpdated(inspector);
let onUpdated = inspector.once("inspector-updated");
EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
inspector.markup.doc.defaultView);
yield onChildren;
yield onUpdated;
info("Checking that child LI elements have been created");
for (let li of content.document.querySelectorAll("li")) {
ok(getContainerForRawNode(li, inspector),
"A container for the child LI element was created");
}
ok(container.expanded, "Parent UL container is expanded");
info("Dbl-clicking again on the UL expander");
// No need to wait, this is a local, synchronous operation where nodes are
// only hidden from the view, not destroyed
EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
inspector.markup.doc.defaultView);
info("Checking that child LI elements have been hidden");
for (let li of content.document.querySelectorAll("li")) {
let liContainer = getContainerForRawNode(li, inspector);
is(liContainer.elt.getClientRects().length, 0,
"The container for the child LI element was hidden");
}
ok(!container.expanded, "Parent UL container is collapsed");
});

View File

@ -0,0 +1,44 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test toggling (expand/collapse) elements by alt-clicking on twisties, which
// should expand all the descendants
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
info("Getting the container for the UL parent element");
let container = getContainerForRawNode("ul", inspector);
info("Alt-clicking on the UL parent expander, and waiting for children");
let onUpdated = inspector.once("inspector-updated");
EventUtils.synthesizeMouseAtCenter(container.expander, {altKey: true},
inspector.markup.doc.defaultView);
yield onUpdated;
yield waitForMultipleChildrenUpdates(inspector);
info("Checking that all nodes exist and are expanded");
for (let node of content.document.querySelectorAll("ul, li, span, em")) {
let nodeContainer = getContainerForRawNode(node, inspector);
ok(nodeContainer, "Container for node " + node.tagName + " exists");
ok(nodeContainer.expanded,
"Container for node " + node.tagName + " is expanded");
}
});
// The expand all operation of the markup-view calls itself recursively and
// there's not one event we can wait for to know when it's done
function* waitForMultipleChildrenUpdates(inspector) {
// As long as child updates are queued up while we wait for an update already
// wait again
if (inspector.markup._queuedChildUpdates &&
inspector.markup._queuedChildUpdates.size) {
yield waitForChildrenUpdated(inspector);
return yield waitForMultipleChildrenUpdates(inspector);
}
}

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Expanding and collapsing markup-view containers</title>
</head>
<body>
<ul>
<li>
<span>list <em>item</em></span>
</li>
<li>
<span>list <em>item</em></span>
</li>
<li>
<span>list <em>item</em></span>
</li>
<li>
<span>list <em>item</em></span>
</li>
<li>
<span>list <em>item</em></span>
</li>
<li>
<span>list <em>item</em></span>
</li>
</ul>
</body>
</html>

View File

@ -166,6 +166,23 @@ function getContainerForRawNode(nodeOrSelector, {markup}) {
return container;
}
/**
* Using the markupview's _waitForChildren function, wait for all queued
* children updates to be handled.
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return a promise that resolves when all queued children updates have been
* handled
*/
function waitForChildrenUpdated({markup}) {
info("Waiting for queued children updates to be handled");
let def = promise.defer();
markup._waitForChildren().then(() => {
executeSoon(def.resolve);
});
return def.promise;
}
/**
* Simulate a mouse-over on the markup-container (a line in the markup-view)
* that corresponds to the node or selector passed.
@ -352,3 +369,15 @@ function searchUsingSelectorSearch(selector, inspector) {
field.value = selector;
EventUtils.sendKey("return", inspector.panelWin);
}
/**
* This shouldn't be used in the tests, but is useful when writing new tests or
* debugging existing tests in order to introduce delays in the test steps
* @param {Number} ms The time to wait
* @return A promise that resolves when the time is passed
*/
function wait(ms) {
let def = promise.defer();
content.setTimeout(def.resolve, ms);
return def.promise;
}

View File

@ -42,11 +42,9 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils",
return mod;
});
#ifdef MOZ_CRASHREPORTER
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
"@mozilla.org/xre/app-info;1",
"nsICrashReporter");
#endif
const FILE_CACHE = "experiments.json";
const OBSERVER_TOPIC = "experiments-changed";
@ -76,20 +74,29 @@ const TELEMETRY_LOG = {
// log(key, [kind, experimentId, details])
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
ACTIVATION: {
ACTIVATED: "ACTIVATED", // successfully activated
INSTALL_FAILURE: "INSTALL_FAILURE", // failed to install the extension
REJECTED: "REJECTED", // experiment was rejected because of it's conditions,
// provides details on which
// Successfully activated.
ACTIVATED: "ACTIVATED",
// Failed to install the add-on.
INSTALL_FAILURE: "INSTALL_FAILURE",
// Experiment does not meet activation requirements. Details will
// be provided.
REJECTED: "REJECTED",
},
// log(key, [kind, experimentId, optionalDetails...])
TERMINATION_KEY: "EXPERIMENT_TERMINATION",
TERMINATION: {
USERDISABLED: "USERDISABLED", // the user disabled this experiment
FROM_API: "FROM_API", // the experiment disabled itself
EXPIRED: "EXPIRED", // experiment expired e.g. by exceeding the end-date
RECHECK: "RECHECK", // disabled after re-evaluating conditions,
// provides details on which
// The Experiments service was disabled.
SERVICE_DISABLED: "SERVICE_DISABLED",
// Add-on uninstalled.
ADDON_UNINSTALLED: "ADDON_UNINSTALLED",
// The experiment disabled itself.
FROM_API: "FROM_API",
// The experiment expired (e.g. by exceeding the end date).
EXPIRED: "EXPIRED",
// Disabled after re-evaluating conditions. If this is specified,
// details will be provided.
RECHECK: "RECHECK",
},
};
@ -106,6 +113,10 @@ let gExperimentEntryCounter = 0;
// installs.
let gActiveInstallURLs = new Set();
// Tracks add-on IDs that are being uninstalled by us. This allows us
// to differentiate between expected uninstalled and user-driven uninstalls.
let gActiveUninstallAddonIDs = new Set();
let gLogger;
let gLogDumping = false;
@ -316,6 +327,14 @@ Experiments.Policy.prototype = {
},
};
function AlreadyShutdownError(message="already shut down") {
this.name = "AlreadyShutdownError";
this.message = message;
}
AlreadyShutdownError.prototype = new Error();
AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
/**
* Manages the experiments and provides an interface to control them.
*/
@ -340,9 +359,6 @@ Experiments.Experiments = function (policy=new Experiments.Policy()) {
// Loading the cache happens once asynchronously on startup
this._loadTask = null;
// Ignore addon-manager notifications for addons that we are uninstalling ourself
this._pendingUninstall = null;
// The _main task handles all other actions:
// * refreshing the manifest off the network (if _refresh)
// * disabling/enabling experiments
@ -426,7 +442,11 @@ Experiments.Experiments.prototype = {
this._shutdown = true;
if (this._mainTask) {
yield this._mainTask;
try {
yield this._mainTask;
} catch (e if e instanceof AlreadyShutdownError) {
// We error out of tasks after shutdown via that exception.
}
}
this._log.info("Completed uninitialization.");
@ -449,7 +469,7 @@ Experiments.Experiments.prototype = {
*/
_checkForShutdown: function() {
if (this._shutdown) {
throw Error("uninit() already called");
throw new AlreadyShutdownError("uninit() already called");
}
},
@ -468,7 +488,7 @@ Experiments.Experiments.prototype = {
gPrefs.set(PREF_ENABLED, enabled);
},
_toggleExperimentsEnabled: function (enabled) {
_toggleExperimentsEnabled: Task.async(function* (enabled) {
this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
let wasEnabled = gExperimentsEnabled;
gExperimentsEnabled = enabled && telemetryEnabled();
@ -478,14 +498,14 @@ Experiments.Experiments.prototype = {
}
if (gExperimentsEnabled) {
this.updateManifest();
yield this.updateManifest();
} else {
this.disableExperiment();
yield this.disableExperiment(TELEMETRY_LOG.TERMINATION.SERVICE_DISABLED);
if (this._timer) {
this._timer.clear();
}
}
},
}),
_telemetryStatusChanged: function () {
this._toggleExperimentsEnabled(gExperimentsEnabled);
@ -667,21 +687,9 @@ Experiments.Experiments.prototype = {
// START OF ADD-ON LISTENERS
onDisabled: function (addon) {
this._log.trace("onDisabled() - addon id: " + addon.id);
if (addon.id == this._pendingUninstall) {
return;
}
let activeExperiment = this._getActiveExperiment();
if (!activeExperiment || activeExperiment._addonId != addon.id) {
return;
}
this.disableExperiment();
},
onUninstalled: function (addon) {
this._log.trace("onUninstalled() - addon id: " + addon.id);
if (addon.id == this._pendingUninstall) {
if (gActiveUninstallAddonIDs.has(addon.id)) {
this._log.trace("matches pending uninstall");
return;
}
@ -689,7 +697,8 @@ Experiments.Experiments.prototype = {
if (!activeExperiment || activeExperiment._addonId != addon.id) {
return;
}
this.disableExperiment();
this.disableExperiment(TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED);
},
onInstallStarted: function (install) {
@ -927,15 +936,17 @@ Experiments.Experiments.prototype = {
},
/**
* Disable an experiment by id.
* @param experimentId The id of the experiment.
* @param userDisabled (optional) Whether this is disabled as a result of a user action.
* Disables all active experiments.
*
* @return Promise<> Promise that will get resolved once the task is done or failed.
*/
disableExperiment: function (userDisabled=true) {
this._log.trace("disableExperiment()");
disableExperiment: function (reason) {
if (!reason) {
throw new Error("Must specify a termination reason.");
}
this._terminateReason = userDisabled ? TELEMETRY_LOG.TERMINATION.USERDISABLED : TELEMETRY_LOG.TERMINATION.FROM_API;
this._log.trace("disableExperiment()");
this._terminateReason = reason;
return this._run();
},
@ -992,44 +1003,43 @@ Experiments.Experiments.prototype = {
gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
}
// Ensure the active experiment is in the proper state. This may install,
// uninstall, upgrade, or enable the experiment add-on. What exactly is
// abstracted away from us by design.
if (activeExperiment) {
this._pendingUninstall = activeExperiment._addonId;
try {
let wasStopped;
if (this._terminateReason) {
yield activeExperiment.stop(this._terminateReason);
wasStopped = true;
let changes;
let shouldStopResult = yield activeExperiment.shouldStop();
if (shouldStopResult.shouldStop) {
let expireReasons = ["endTime", "maxActiveSeconds"];
let kind, reason;
if (expireReasons.indexOf(shouldStopResult.reason[0]) != -1) {
kind = TELEMETRY_LOG.TERMINATION.EXPIRED;
reason = null;
} else {
wasStopped = yield activeExperiment.maybeStop();
kind = TELEMETRY_LOG.TERMINATION.RECHECK;
reason = shouldStopResult.reason;
}
if (wasStopped) {
this._dirty = true;
this._log.debug("evaluateExperiments() - stopped experiment "
+ activeExperiment.id);
activeExperiment = null;
activeChanged = true;
} else if (!gExperimentsEnabled) {
// No further actions if the feature is disabled.
} else if (activeExperiment.needsUpdate) {
this._log.debug("evaluateExperiments() - updating experiment "
+ activeExperiment.id);
try {
yield activeExperiment.stop();
yield activeExperiment.start();
} catch (e) {
this._log.error(e);
// On failure try the next experiment.
activeExperiment = null;
}
this._dirty = true;
activeChanged = true;
} else {
yield activeExperiment.ensureActive();
}
} finally {
this._pendingUninstall = null;
changes = yield activeExperiment.stop(kind, reason);
}
else if (this._terminateReason) {
changes = yield activeExperiment.stop(this._terminateReason);
}
else {
changes = yield activeExperiment.reconcileAddonState();
}
if (changes) {
this._dirty = true;
activeChanged = true;
}
if (!activeExperiment._enabled) {
activeExperiment = null;
activeChanged = true;
}
}
this._terminateReason = null;
if (!activeExperiment && gExperimentsEnabled) {
@ -1052,30 +1062,35 @@ Experiments.Experiments.prototype = {
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY, data);
}
if (applicable) {
this._log.debug("evaluateExperiments() - activating experiment " + id);
try {
yield experiment.start();
activeChanged = true;
activeExperiment = experiment;
this._dirty = true;
break;
} catch (e) {
// On failure try the next experiment.
}
if (!applicable) {
continue;
}
this._log.debug("evaluateExperiments() - activating experiment " + id);
try {
yield experiment.start();
activeChanged = true;
activeExperiment = experiment;
this._dirty = true;
break;
} catch (e) {
// On failure, clean up the best we can and try the next experiment.
this._log.error("evaluateExperiments() - Unable to start experiment: " + e.message);
experiment._enabled = false;
yield experiment.reconcileAddonState();
}
}
}
gPrefs.set(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);
if (activeChanged) {
Services.obs.notifyObservers(null, OBSERVER_TOPIC, null);
}
#ifdef MOZ_CRASHREPORTER
if (activeExperiment) {
if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
}
#endif
},
/*
@ -1127,7 +1142,7 @@ Experiments.ExperimentEntry = function (policy) {
"Browser.Experiments.Experiments",
"ExperimentEntry #" + gExperimentEntryCounter++ + "::");
// Is this experiment running?
// Is the experiment supposed to be running.
this._enabled = false;
// When this experiment was started, if ever.
this._startDate = null;
@ -1198,6 +1213,11 @@ Experiments.ExperimentEntry.prototype = {
"_endDate",
]),
ADDON_CHANGE_NONE: 0,
ADDON_CHANGE_INSTALL: 1,
ADDON_CHANGE_UNINSTALL: 2,
ADDON_CHANGE_ENABLE: 4,
/*
* Initialize entry from the manifest.
* @param data The experiment data from the manifest.
@ -1487,26 +1507,18 @@ Experiments.ExperimentEntry.prototype = {
/*
* Start running the experiment.
*
* @return Promise<> Resolved when the operation is complete.
*/
start: function () {
start: Task.async(function* () {
this._log.trace("start() for " + this.id);
return Task.spawn(function* ExperimentEntry_start_task() {
let addons = yield installedExperimentAddons();
if (addons.length > 0) {
this._log.error("start() - there are already "
+ addons.length + " experiment addons installed");
yield uninstallAddons(addons);
}
yield this._installAddon();
gPrefs.set(PREF_ACTIVE_EXPERIMENT, true);
}.bind(this));
},
this._enabled = true;
return yield this.reconcileAddonState();
}),
// Async install of the addon for this experiment, part of the start task above.
_installAddon: function* () {
_installAddon: Task.async(function* () {
let deferred = Promise.defer();
let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash;
@ -1605,75 +1617,105 @@ Experiments.ExperimentEntry.prototype = {
install.addListener(listener);
install.install();
return deferred.promise;
},
return yield deferred.promise;
}),
/*
/**
* Stop running the experiment if it is active.
* @param terminationKind (optional) The termination kind, e.g. USERDISABLED or EXPIRED.
* @param terminationReason (optional) The termination reason details for
* termination kind RECHECK.
*
* @param terminationKind (optional)
* The termination kind, e.g. ADDON_UNINSTALLED or EXPIRED.
* @param terminationReason (optional)
* The termination reason details for termination kind RECHECK.
* @return Promise<> Resolved when the operation is complete.
*/
stop: function (terminationKind, terminationReason) {
stop: Task.async(function* (terminationKind, terminationReason) {
this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
if (!this._enabled) {
this._log.warning("stop() - experiment not enabled: " + id);
return Promise.reject();
throw new Error("Must not call stop() on an inactive experiment.");
}
this._enabled = false;
gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
let deferred = Promise.defer();
let updateDates = () => {
let now = this._policy.now();
this._lastChangedDate = now;
this._endDate = now;
};
let changes = yield this.reconcileAddonState();
let now = this._policy.now();
this._lastChangedDate = now;
this._endDate = now;
this._logTermination(terminationKind, terminationReason);
this._getAddon().then((addon) => {
if (!addon) {
let message = "could not get Addon for " + this.id;
this._log.warn("stop() - " + message);
updateDates();
deferred.resolve();
return;
}
updateDates();
this._logTermination(terminationKind, terminationReason);
deferred.resolve(uninstallAddons([addon]));
});
return deferred.promise;
},
return changes;
}),
/**
* Try to ensure this experiment is active.
* Reconcile the state of the add-on against what it's supposed to be.
*
* The returned promise will be resolved if the experiment is active
* in the Addon Manager or rejected if it isn't.
* If we are active, ensure the add-on is enabled and up to date.
*
* @return Promise<>
* If we are inactive, ensure the add-on is not installed.
*/
ensureActive: Task.async(function* () {
this._log.trace("ensureActive() for " + this.id);
reconcileAddonState: Task.async(function* () {
this._log.trace("reconcileAddonState()");
if (!this._enabled) {
if (!this._addonId) {
this._log.trace("reconcileAddonState() - Experiment is not enabled and " +
"has no add-on. Doing nothing.");
return this.ADDON_CHANGE_NONE;
}
let addon = yield this._getAddon();
if (!addon) {
this._log.trace("reconcileAddonState() - Inactive experiment has no " +
"add-on. Doing nothing.");
return this.ADDON_CHANGE_NONE;
}
this._log.info("reconcileAddonState() - Uninstalling add-on for inactive " +
"experiment: " + addon.id);
gActiveUninstallAddonIDs.add(addon.id);
yield uninstallAddons([addon]);
gActiveUninstallAddonIDs.delete(addon.id);
return this.ADDON_CHANGE_UNINSTALL;
}
// If we get here, we're supposed to be active.
let changes = 0;
// That requires an add-on.
let currentAddon = yield this._getAddon();
// If we have an add-on but it isn't up to date, uninstall it
// (to prepare for reinstall).
if (currentAddon && this._needsUpdate) {
this._log.info("reconcileAddonState() - Uninstalling add-on because update " +
"needed: " + currentAddon.id);
gActiveUninstallAddonIDs.add(currentAddon.id);
yield uninstallAddons([currentAddon]);
gActiveUninstallAddonIDs.delete(currentAddon.id);
changes |= this.ADDON_CHANGE_UNINSTALL;
}
if (!currentAddon || this._needsUpdate) {
this._log.info("reconcileAddonState() - Installing add-on.");
yield this._installAddon();
changes |= this.ADDON_CHANGE_INSTALL;
}
let addon = yield this._getAddon();
if (!addon) {
this._log.warn("Experiment is not installed: " + this._addonId);
throw new Error("Experiment is not installed: " + this._addonId);
throw new Error("Could not obtain add-on for experiment that should be " +
"enabled.");
}
// User disabled likely means the experiment is disabled at startup,
// since the permissions don't allow it to be disabled by the user.
// If we have the add-on and it is enabled, we are done.
if (!addon.userDisabled) {
return;
return changes;
}
let deferred = Promise.defer();
// Else we need to enable it.
let listener = {
onEnabled: enabledAddon => {
if (enabledAddon.id != addon.id) {
@ -1689,7 +1731,11 @@ Experiments.ExperimentEntry.prototype = {
AddonManager.addAddonListener(listener);
addon.userDisabled = false;
yield deferred.promise;
}),
changes |= this.ADDON_CHANGE_ENABLE;
this._log.info("Add-on has been enabled: " + addon.id);
return changes;
}),
/**
* Obtain the underlying Addon from the Addon Manager.
@ -1697,6 +1743,10 @@ Experiments.ExperimentEntry.prototype = {
* @return Promise<Addon|null>
*/
_getAddon: function () {
if (!this._addonId) {
return Promise.resolve(null);
}
let deferred = Promise.defer();
AddonManager.getAddonByID(this._addonId, deferred.resolve);
@ -1722,43 +1772,18 @@ Experiments.ExperimentEntry.prototype = {
TelemetryLog.log(TELEMETRY_LOG.TERMINATION_KEY, data);
},
/*
* Stop if experiment stop criteria are met.
* @return Promise<boolean> Resolved when done stopping or checking,
* the value indicates whether it was stopped.
/**
* Determine whether an active experiment should be stopped.
*/
maybeStop: function () {
this._log.trace("maybeStop()");
return Task.spawn(function* ExperimentEntry_maybeStop_task() {
if (!gExperimentsEnabled) {
this._log.warn("maybeStop() - should not get here");
yield this.stop(TELEMETRY_LOG.TERMINATION.FROM_API);
return true;
}
shouldStop: function () {
if (!this._enabled) {
throw new Error("shouldStop must not be called on disabled experiments.");
}
let result = yield this._shouldStop();
if (result.shouldStop) {
let expireReasons = ["endTime", "maxActiveSeconds"];
if (expireReasons.indexOf(result.reason[0]) != -1) {
yield this.stop(TELEMETRY_LOG.TERMINATION.EXPIRED);
} else {
yield this.stop(TELEMETRY_LOG.TERMINATION.RECHECK, result.reason);
}
}
return result.shouldStop;
}.bind(this));
},
_shouldStop: function () {
let data = this._manifestData;
let now = this._policy.now() / 1000; // The manifest times are in seconds.
let maxActiveSec = data.maxActiveSeconds || 0;
if (!this._enabled) {
return Promise.resolve({shouldStop: false});
}
let deferred = Promise.defer();
this.isApplicable().then(
() => deferred.resolve({shouldStop: false}),

View File

@ -9,7 +9,7 @@ EXTRA_COMPONENTS += [
JS_MODULES_PATH = 'modules/experiments'
EXTRA_PP_JS_MODULES += [
EXTRA_JS_MODULES += [
'Experiments.jsm',
]

View File

@ -102,7 +102,8 @@ add_task(function* test_startStop() {
Assert.equal(result.applicable, true, "Experiment should now be applicable.");
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
yield experiment.start();
let changes = yield experiment.start();
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
@ -110,12 +111,14 @@ add_task(function* test_startStop() {
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
Assert.ok(addons[0].isActive, "The add-on is active.");
yield experiment.stop();
changes = yield experiment.stop();
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on was uninstalled.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
Assert.equal(addons.length, 0, "Experiment should be uninstalled from the Addon Manager.");
yield experiment.start();
changes = yield experiment.start();
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
@ -123,20 +126,18 @@ add_task(function* test_startStop() {
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
Assert.ok(addons[0].isActive, "The add-on is active.");
let result = yield experiment._shouldStop();
let result = yield experiment.shouldStop();
Assert.equal(result.shouldStop, false, "shouldStop should be false.");
let maybeStop = yield experiment.maybeStop();
Assert.equal(maybeStop, false, "Experiment should not have been stopped.");
Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 1, "Experiment still in add-ons manager.");
Assert.ok(addons[0].isActive, "The add-on is still active.");
defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
result = yield experiment._shouldStop();
result = yield experiment.shouldStop();
Assert.equal(result.shouldStop, true, "shouldStop should now be true.");
maybeStop = yield experiment.maybeStop();
Assert.equal(maybeStop, true, "Experiment should have been stopped.");
changes = yield experiment.stop();
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on should be uninstalled.");
Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "Experiment add-on is uninstalled.");

View File

@ -424,7 +424,7 @@ add_task(function* test_disableExperiment() {
now = futureDate(now, 1 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment();
yield experiments.disableExperiment("foo");
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
@ -749,7 +749,7 @@ add_task(function* test_userDisabledAndUpdated() {
now = futureDate(now, 20 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment();
yield experiments.disableExperiment("foo");
Assert.equal(observerFireCount, ++expectedObserverFireCount,
"Experiments observer should have been called.");
@ -1405,7 +1405,7 @@ add_task(function* testForeignExperimentInstall() {
let addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
let failed;
let failed = false;
try {
yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
} catch (ex) {

View File

@ -243,7 +243,7 @@ add_task(function* test_cache() {
// Cleanup.
yield experiments.disableExperiment();
yield experiments._toggleExperimentsEnabled(false);
yield experiments.uninit();
yield removeCacheFile();
});

View File

@ -6,7 +6,7 @@
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/TelemetryLog.jsm");
Cu.import("resource://gre/modules/TelemetryPing.jsm");
Cu.import("resource:///modules/experiments/Experiments.jsm");
let bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
const FILE_MANIFEST = "experiments.manifest";
@ -25,25 +25,7 @@ let gPolicy = null;
let gManifestObject = null;
let gManifestHandlerURI = null;
const TLOG = {
// log(key, [kind, experimentId, details])
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
ACTIVATION: {
ACTIVATED: "ACTIVATED",
INSTALL_FAILURE: "INSTALL_FAILURE",
REJECTED: "REJECTED",
},
// log(key, [kind, experimentId, optionalDetails...])
TERMINATION_KEY: "EXPERIMENT_TERMINATION",
TERMINATION: {
USERDISABLED: "USERDISABLED",
FROM_API: "FROM_API",
EXPIRED: "EXPIRED",
RECHECK: "RECHECK",
},
};
const TLOG = bsp.TELEMETRY_LOG;
let gGlobalScope = this;
function loadAddonManager() {
@ -263,12 +245,12 @@ add_task(function* test_telemetryBasics() {
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]);
// Fake user-disable of an experiment.
// Fake user uninstall of experiment via add-on manager.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment();
yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED);
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
@ -276,7 +258,7 @@ add_task(function* test_telemetryBasics() {
log = TelemetryPing.getPayload().log;
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
[TLOG.TERMINATION.USERDISABLED, EXPERIMENT2_ID]);
[TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]);
// Trigger update with experiment 1a ready to start.
@ -295,12 +277,12 @@ add_task(function* test_telemetryBasics() {
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]);
// Trigger non-user-disable of an experiment via the API
// Trigger disable of an experiment via the API.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment(false);
yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API);
list = yield experiments.getExperiments();
Assert.equal(list.length, 3, "Experiment list should have 3 entries.");

View File

@ -230,6 +230,9 @@ Var ControlRightPX
!include "defines.nsi"
; Must be included after defines.nsi
!include "locale-fonts.nsh"
; The OFFICIAL define is a workaround to support different urls for Release and
; Beta since they share the same branding when building with other branches that
; set the update channel to beta.
@ -416,9 +419,27 @@ Function .onInit
!endif
StrCpy $WasOptionsButtonClicked "0"
CreateFont $FontBlurb "$(^Font)" "12" "500"
CreateFont $FontNormal "$(^Font)" "11" "500"
CreateFont $FontItalic "$(^Font)" "11" "500" /ITALIC
StrCpy $0 ""
!ifdef FONT_FILE1
${If} ${FileExists} "$FONTS\${FONT_FILE1}"
StrCpy $0 "${FONT_NAME1}"
${EndIf}
!endif
!ifdef FONT_FILE2
${If} $0 == ""
${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
StrCpy $0 "${FONT_NAME2}"
${EndIf}
!endif
${If} $0 == ""
StrCpy $0 "$(^Font)"
${EndIf}
CreateFont $FontBlurb "$0" "12" "500"
CreateFont $FontNormal "$0" "11" "500"
CreateFont $FontItalic "$0" "11" "500" /ITALIC
InitPluginsDir
File /oname=$PLUGINSDIR\bgintro.bmp "bgintro.bmp"

View File

@ -13,6 +13,7 @@
<!ENTITY port "Port:">
<!ENTITY connect "Connect">
<!ENTITY connecting "Connecting…">
<!ENTITY availableAddons "Available remote add-ons:">
<!ENTITY availableTabs "Available remote tabs:">
<!ENTITY availableProcesses "Available remote processes:">
<!ENTITY connectionError "Error:">

View File

@ -140,14 +140,6 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
};
let secondaryActions = [
{
label: stringBundle.getString("getUserMedia.always.label"),
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
callback: function () {
// don't save unless secure load!
mainAction.callback(aSecure);
}
},
{
label: stringBundle.getString("getUserMedia.denyRequest.label"),
accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
@ -160,8 +152,8 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
callback: function () {
denyRequest(aCallID);
// Let someone save "Never" for http sites so that they can be stopped from
// bothering you with doorhangers
// Let someone save "Never" for http sites so that they can be stopped from
// bothering you with doorhangers.
let perms = Services.perms;
if (audioDevices.length)
perms.add(uri, "microphone", perms.DENY_ACTION);
@ -171,6 +163,17 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
}
];
if (aSecure) {
// Don't show the 'Always' action if the connection isn't secure.
secondaryActions.unshift({
label: stringBundle.getString("getUserMedia.always.label"),
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
callback: function () {
mainAction.callback(true);
}
});
}
let options = {
eventCallback: function(aTopic, aNewBrowser) {
if (aTopic == "swapping")

View File

@ -35,6 +35,11 @@
moveY 3.4s linear 0s infinite alternate;
}
#PanelUI-popup #PanelUI-contents:-moz-locale-dir(rtl):empty::before {
animation: moveXRTL 3.05s linear 0s infinite alternate,
moveY 3.4s linear 0s infinite alternate;
}
#PanelUI-popup #PanelUI-contents:empty:hover::before {
background-image: url(chrome://browser/skin/customizableui/whimsy.png);
}
@ -53,6 +58,12 @@
/* These values are adjusted for the padding on the panel. */
from { margin-left: -15px; } to { margin-left: calc(100% - 49px); }
}
@keyframes moveXRTL {
/* These values are adjusted for the padding on the panel. */
from { margin-right: -15px; } to { margin-right: calc(100% - 49px); }
}
@keyframes moveY {
/* These values are adjusted for the padding and height of the panel. */
from { margin-top: -.5em; } to { margin-top: calc(64px - .5em); }

View File

@ -293,12 +293,21 @@ case "$target" in
if test ! -d "$android_platform_tools" ; then
android_platform_tools="$android_sdk"/tools # SDK Tools < r8
fi
# The build tools got moved around to different directories in
# SDK Tools r22. Try to locate them.
dnl The build tools got moved around to different directories in SDK
dnl Tools r22. Try to locate them. This is awful, but, from
dnl http://stackoverflow.com/a/4495368, the following sorts versions
dnl of the form x.y.z.a.b from newest to oldest:
dnl sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr
dnl We want to favour the newer versions that start with 'android-';
dnl that's what the sed is about.
dnl We might iterate over directories that aren't build-tools at all;
dnl we use the presence of aapt as a marker.
AC_MSG_CHECKING([for android build-tools directory])
android_build_tools=""
for suffix in android-4.4 android-4.3 android-4.2.2 19.0.3 19.0.2 19.0.0 18.1.0 18.0.1 18.0.0 17.0.0; do
tools_directory="$android_sdk_root/build-tools/$suffix"
if test -d "$tools_directory" ; then
for suffix in `ls "$android_sdk_root/build-tools" | sed -e "s,android-,999.," | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr`; do
tools_directory=`echo "$android_sdk_root/build-tools/$suffix" | sed -e "s,999.,android-,"`
if test -d "$tools_directory" -a -f "$tools_directory/aapt"; then
android_build_tools="$tools_directory"
break
fi
@ -306,6 +315,13 @@ case "$target" in
if test -z "$android_build_tools" ; then
android_build_tools="$android_platform_tools" # SDK Tools < r22
fi
if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
AC_MSG_RESULT([$android_build_tools])
else
AC_MSG_ERROR([not found. Please check your SDK for the subdirectory of build-tools. With the current configuration, it should be in $android_sdk_root/build_tools])
fi
ANDROID_SDK="${android_sdk}"
ANDROID_SDK_ROOT="${android_sdk_root}"
if test -e "${ANDROID_SDK_ROOT}/extras/android/compatibility/v4/android-support-v4.jar" ; then

View File

@ -9,7 +9,7 @@ $(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.j
EOF
bucket=
if test -z "$SCCACHE_DISABLE" -a -z "$no_tooltool"; then
if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache"; then
case "${branch}_${master}" in
try_*scl1.mozilla.com*|try_*.scl3.mozilla.com*)
bucket=mozilla-releng-ceph-cache-scl3-try

View File

@ -998,6 +998,7 @@ GK_ATOM(showresizer, "showresizer")
GK_ATOM(simple, "simple")
GK_ATOM(single, "single")
GK_ATOM(size, "size")
GK_ATOM(sizes, "sizes")
GK_ATOM(sizemode, "sizemode")
GK_ATOM(sizetopopup, "sizetopopup")
GK_ATOM(slider, "slider")

View File

@ -11,7 +11,6 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'content_canvas'
EXPORTS += [
'nsICanvasElementExternal.h',
'nsICanvasRenderingContextInternal.h',
]

View File

@ -1,58 +0,0 @@
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsICanvasElementExternal_h___
#define nsICanvasElementExternal_h___
#include "nsISupports.h"
#include "GraphicsFilter.h"
class gfxContext;
class nsIFrame;
struct gfxRect;
#define NS_ICANVASELEMENTEXTERNAL_IID \
{ 0x51870f54, 0x6c4c, 0x469a, {0xad, 0x46, 0xf0, 0xa9, 0x8e, 0x32, 0xa7, 0xe2 } }
class nsRenderingContext;
class nsICanvasRenderingContextInternal;
struct _cairo_surface;
/*
* This interface contains methods that are needed outside of the content/layout
* modules, specifically widget. It should eventually go away when we support
* libxul builds, and HTMLCanvasElement be used directly.
*
* Code internal to content/layout should /never/ use this interface; if the
* same functionality is needed in both places, two separate methods should be
* used.
*/
class nsICanvasElementExternal : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASELEMENTEXTERNAL_IID)
enum {
RenderFlagPremultAlpha = 0x1
};
/**
* Get the size in pixels of this canvas element
*/
NS_IMETHOD_(nsIntSize) GetSizeExternal() = 0;
/*
* Ask the canvas element to tell the contexts to render themselves
* to the given gfxContext at the origin of its coordinate space.
*/
NS_IMETHOD RenderContextsExternal(gfxContext *ctx,
GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasElementExternal, NS_ICANVASELEMENTEXTERNAL_IID)
#endif /* nsICanvasElementExternal_h___ */

View File

@ -6,6 +6,7 @@
#ifndef nsICanvasRenderingContextInternal_h___
#define nsICanvasRenderingContextInternal_h___
#include "mozilla/gfx/2D.h"
#include "nsISupports.h"
#include "nsIInputStream.h"
#include "nsIDocShell.h"
@ -14,8 +15,8 @@
#include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x9a6a5bdf, 0x1261, 0x4057, \
{ 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
{ 0x3cc9e801, 0x1806, 0x4ff6, \
{ 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
class gfxContext;
class gfxASurface;
@ -41,10 +42,6 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
enum {
RenderFlagPremultAlpha = 0x1
};
void SetCanvasElement(mozilla::dom::HTMLCanvasElement* aParentCanvas)
{
mCanvasElement = aParentCanvas;
@ -66,11 +63,6 @@ public:
NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
// Render the canvas at the origin of the given gfxContext
NS_IMETHOD Render(gfxContext *ctx,
GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
// Creates an image buffer. Returns null on failure.
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
@ -83,14 +75,13 @@ public:
NS_IMETHOD GetInputStream(const char *aMimeType,
const char16_t *aEncoderOptions,
nsIInputStream **aStream) = 0;
// If this canvas context can be represented with a simple Thebes surface,
// return the surface. Otherwise returns an error.
NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
// This gets an Azure SourceSurface for the canvas, this will be a snapshot
// of the canvas at the time it was called.
virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() = 0;
// If aPremultAlpha is provided, then it assumed the callee can handle
// un-premultiplied surfaces, and *aPremultAlpha will be set to false
// if one is returned.
virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) = 0;
// If this context is opaque, the backing store of the canvas should
// be created as opaque; all compositing operators should assume the

View File

@ -1053,51 +1053,6 @@ CanvasRenderingContext2D::SetIsIPC(bool isIPC)
return NS_OK;
}
NS_IMETHODIMP
CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32_t aFlags)
{
nsresult rv = NS_OK;
EnsureTarget();
if (!IsTargetValid()) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> surface;
if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
pat->SetFilter(aFilter);
pat->SetExtend(gfxPattern::EXTEND_PAD);
gfxContext::GraphicsOperator op = ctx->CurrentOperator();
if (mOpaque)
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
// XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
// pixel alignment for this stuff!
ctx->NewPath();
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
ctx->Fill();
if (mOpaque)
ctx->SetOperator(op);
if (!(aFlags & RenderFlagPremultAlpha)) {
nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
MOZ_ASSERT(gis, "If non-premult alpha, must be able to get image surface!");
gfxUtils::UnpremultiplyImageSurface(gis);
}
return rv;
}
NS_IMETHODIMP
CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
{
@ -3266,28 +3221,6 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// Special case for Canvas, which could be an Azure canvas!
nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
if (srcCanvas == this) {
// Self-copy.
srcSurf = mTarget->Snapshot();
imgSize = gfxIntSize(mWidth, mHeight);
} else if (srcCanvas) {
// This might not be an Azure canvas!
srcSurf = srcCanvas->GetSurfaceSnapshot();
if (srcSurf) {
if (mCanvasElement) {
// Do security check here.
CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
element->NodePrincipal(),
canvas->IsWriteOnly(),
false);
}
imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
}
}
} else {
if (image.IsHTMLImageElement()) {
HTMLImageElement* img = &image.GetAsHTMLImageElement();
@ -4147,27 +4080,6 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
return NS_OK;
}
NS_IMETHODIMP
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
{
EnsureTarget();
if (!IsTargetValid()) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> thebesSurface =
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
if (!thebesSurface) {
return NS_ERROR_FAILURE;
}
*surface = thebesSurface;
NS_ADDREF(*surface);
return NS_OK;
}
static already_AddRefed<ImageData>
CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
uint32_t w, uint32_t h, ErrorResult& error)

View File

@ -457,16 +457,18 @@ public:
NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE;
NS_IMETHOD Render(gfxContext *ctx,
GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream **aStream) MOZ_OVERRIDE;
NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
{ EnsureTarget(); return mTarget->Snapshot(); }
mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) MOZ_OVERRIDE
{
EnsureTarget();
if (aPremultAlpha) {
*aPremultAlpha = true;
}
return mTarget->Snapshot();
}
NS_IMETHOD SetIsOpaque(bool isOpaque) MOZ_OVERRIDE;
bool GetIsOpaque() MOZ_OVERRIDE { return mOpaque; }

View File

@ -136,25 +136,6 @@ WebGLContext::WebGLContext()
mFakeVertexAttrib0BufferObject = 0;
mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
// these are de default values, see 6.2 State tables in the OpenGL ES 2.0.25 spec
mColorWriteMask[0] = 1;
mColorWriteMask[1] = 1;
mColorWriteMask[2] = 1;
mColorWriteMask[3] = 1;
mDepthWriteMask = 1;
mColorClearValue[0] = 0.f;
mColorClearValue[1] = 0.f;
mColorClearValue[2] = 0.f;
mColorClearValue[3] = 0.f;
mDepthClearValue = 1.f;
mStencilClearValue = 0;
mStencilRefFront = 0;
mStencilRefBack = 0;
mStencilValueMaskFront = 0xffffffff;
mStencilValueMaskBack = 0xffffffff;
mStencilWriteMaskFront = 0xffffffff;
mStencilWriteMaskBack = 0xffffffff;
mViewportX = 0;
mViewportY = 0;
mViewportWidth = 0;
@ -209,7 +190,7 @@ WebGLContext::WebGLContext()
InvalidateBufferFetching();
mIsScreenCleared = false;
mBackbufferNeedsClear = true;
mDisableFragHighP = false;
@ -423,7 +404,7 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
mHeight = gl->OffscreenSize().height;
mResetLayer = true;
ClearScreen();
mBackbufferNeedsClear = true;
return NS_OK;
}
@ -610,7 +591,11 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
gl->fClearDepth(1.0f);
gl->fClearStencil(0);
gl->ClearSafely();
mBackbufferNeedsClear = true;
// Clear immediately, because we need to present the cleared initial
// buffer.
ClearBackbufferIfNeeded();
mShouldPresent = true;
@ -625,46 +610,23 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
return NS_OK;
}
NS_IMETHODIMP
WebGLContext::Render(gfxContext *ctx, GraphicsFilter f, uint32_t aFlags)
void
WebGLContext::ClearBackbufferIfNeeded()
{
if (!gl)
return NS_OK;
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageFormat::ARGB32);
if (surf->CairoStatus() != 0)
return NS_ERROR_FAILURE;
if (!mBackbufferNeedsClear)
return;
#ifdef DEBUG
gl->MakeCurrent();
ReadScreenIntoImageSurface(gl, surf);
bool srcPremultAlpha = mOptions.premultipliedAlpha;
bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
GLuint fb = 0;
gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb);
MOZ_ASSERT(fb == 0);
#endif
if (!srcPremultAlpha && dstPremultAlpha) {
gfxUtils::PremultiplyImageSurface(surf);
} else if (srcPremultAlpha && !dstPremultAlpha) {
gfxUtils::UnpremultiplyImageSurface(surf);
}
surf->MarkDirty();
ClearScreen();
nsRefPtr<gfxPattern> pat = new gfxPattern(surf);
pat->SetFilter(f);
// Pixels from ReadPixels will be "upside down" compared to
// what cairo wants, so draw with a y-flip and a translte to
// flip them.
gfxMatrix m;
m.Translate(gfxPoint(0.0, mHeight));
m.Scale(1.0, -1.0);
pat->SetMatrix(m);
ctx->NewPath();
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
ctx->Fill();
return NS_OK;
mBackbufferNeedsClear = false;
}
void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
@ -758,25 +720,31 @@ WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
*aImageBuffer = nullptr;
*aFormat = 0;
nsRefPtr<gfxImageSurface> imgsurf =
new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageFormat::ARGB32);
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
bool premult;
RefPtr<SourceSurface> snapshot =
GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
if (!snapshot) {
return;
}
MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
if (!imgsurf || imgsurf->CairoStatus()) {
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
DataSourceSurface::MappedSurface map;
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
return;
}
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
if (!ctx || ctx->HasError()) {
static const fallible_t fallible = fallible_t();
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
if (!imageBuffer) {
dataSurface->Unmap();
return;
}
memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
// Use Render() to make sure that appropriate y-flip gets applied
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
if (NS_FAILED(rv)) {
return;
}
dataSurface->Unmap();
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
@ -785,17 +753,10 @@ WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(imgsurf);
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
static const fallible_t fallible = fallible_t();
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
if (!imageBuffer) {
return;
}
memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
*aImageBuffer = imageBuffer;
*aFormat = format;
}
@ -827,12 +788,6 @@ WebGLContext::GetInputStream(const char* aMimeType,
encoder, aEncoderOptions, aStream);
}
NS_IMETHODIMP
WebGLContext::GetThebesSurface(gfxASurface **surface)
{
return NS_ERROR_NOT_AVAILABLE;
}
void WebGLContext::UpdateLastUseIndex()
{
static CheckedInt<uint64_t> sIndex = 0;
@ -1014,7 +969,6 @@ WebGLContext::ClearScreen()
colorAttachmentsMask[0] = true;
ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask);
mIsScreenCleared = true;
}
#ifdef DEBUG
@ -1202,13 +1156,14 @@ WebGLContext::PresentScreenBuffer()
}
gl->MakeCurrent();
MOZ_ASSERT(!mBackbufferNeedsClear);
if (!gl->PublishFrame()) {
this->ForceLoseContext();
return false;
}
if (!mOptions.preserveDrawingBuffer) {
ClearScreen();
mBackbufferNeedsClear = true;
}
mShouldPresent = false;
@ -1376,9 +1331,61 @@ void
WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
WebGLContext::GetSurfaceSnapshot()
WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
{
return nullptr;
if (!gl)
return nullptr;
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageFormat::ARGB32,
mWidth * 4, 0, false);
if (surf->CairoStatus() != 0) {
return nullptr;
}
gl->MakeCurrent();
{
ScopedBindFramebuffer autoFB(gl, 0);
ClearBackbufferIfNeeded();
ReadPixelsIntoImageSurface(gl, surf);
}
if (aPremultAlpha) {
*aPremultAlpha = true;
}
bool srcPremultAlpha = mOptions.premultipliedAlpha;
if (!srcPremultAlpha) {
if (aPremultAlpha) {
*aPremultAlpha = false;
} else {
gfxUtils::PremultiplyImageSurface(surf);
surf->MarkDirty();
}
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTarget(BackendType::CAIRO,
IntSize(mWidth, mHeight),
SurfaceFormat::B8G8R8A8);
if (!dt) {
return nullptr;
}
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf);
Matrix m;
m.Translate(0.0, mHeight);
m.Scale(1.0, -1.0);
dt->SetTransform(m);
dt->DrawSurface(source,
Rect(0, 0, mWidth, mHeight),
Rect(0, 0, mWidth, mHeight),
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
return dt->Snapshot();
}
//

View File

@ -165,15 +165,11 @@ public:
{ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD Reset() MOZ_OVERRIDE
{ /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD Render(gfxContext *ctx,
GraphicsFilter f,
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream **aStream) MOZ_OVERRIDE;
NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE;
mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha) MOZ_OVERRIDE;
NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
bool GetIsOpaque() MOZ_OVERRIDE { return false; }
@ -243,6 +239,7 @@ public:
// Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
void ClearScreen();
void ClearBackbufferIfNeeded();
bool MinCapabilityMode() const { return mMinCapability; }
@ -841,7 +838,7 @@ protected:
bool mLoseContextOnHeapMinimize;
bool mCanLoseContextInForeground;
bool mShouldPresent;
bool mIsScreenCleared;
bool mBackbufferNeedsClear;
bool mDisableFragHighP;
template<typename WebGLObjectType>
@ -1034,7 +1031,7 @@ protected:
if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE)
flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
if (!mPixelStorePremultiplyAlpha)
flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA;
flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
return nsLayoutUtils::SurfaceFromElement(aElement, flags);
}
template<class ElementType>

View File

@ -103,6 +103,8 @@ bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcoun
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
return false;
}
} else {
ClearBackbufferIfNeeded();
}
if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
@ -264,6 +266,8 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
return false;
}
} else {
ClearBackbufferIfNeeded();
}
if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
@ -333,7 +337,7 @@ void WebGLContext::Draw_cleanup()
if (!mBoundFramebuffer) {
Invalidate();
mShouldPresent = true;
mIsScreenCleared = false;
MOZ_ASSERT(!mBackbufferNeedsClear);
}
if (gl->WorkAroundDriverBugs()) {

View File

@ -35,43 +35,13 @@ WebGLContext::Clear(GLbitfield mask)
gl->fClear(mask);
return;
} else {
ClearBackbufferIfNeeded();
}
// Ok, we're clearing the default framebuffer/screen.
bool needsClear = true;
if (mIsScreenCleared) {
bool isClearRedundant = true;
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
if (mColorClearValue[0] != 0.0f ||
mColorClearValue[1] != 0.0f ||
mColorClearValue[2] != 0.0f ||
mColorClearValue[3] != 0.0f)
{
isClearRedundant = false;
}
}
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
if (mDepthClearValue != 1.0f) {
isClearRedundant = false;
}
}
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
if (mStencilClearValue != 0) {
isClearRedundant = false;
}
}
if (isClearRedundant)
needsClear = false;
}
if (needsClear) {
gl->fClear(mask);
mIsScreenCleared = false;
}
gl->fClear(mask);
Invalidate();
mShouldPresent = true;

View File

@ -29,6 +29,7 @@
#include "nsLayoutUtils.h"
#include "CanvasUtils.h"
#include "gfxUtils.h"
#include "jsfriendapi.h"
@ -472,6 +473,8 @@ WebGLContext::CopyTexImage2D(GLenum target,
return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
}
} else {
ClearBackbufferIfNeeded();
}
bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA ||
@ -584,6 +587,8 @@ WebGLContext::CopyTexSubImage2D(GLenum target,
return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
}
} else {
ClearBackbufferIfNeeded();
}
bool texFormatRequiresAlpha = (internalFormat == LOCAL_GL_RGBA ||
@ -2194,6 +2199,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
}
} else {
ClearBackbufferIfNeeded();
}
// Now that the errors are out of the way, on to actually reading
@ -2521,6 +2528,10 @@ WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromE
return NS_OK;
}
if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) {
data = gfxUtils::UnpremultiplyDataSurface(data);
}
// We disallow loading cross-domain images and videos that have not been validated
// with CORS as WebGL textures. The reason for doing that is that timing
// attacks on WebGL shaders are able to retrieve approximations of the

View File

@ -80,9 +80,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
if (MinCapabilityMode()) {
switch(pname) {
//
////////////////////////////
// Single-value params
//
// int
case LOCAL_GL_MAX_VERTEX_ATTRIBS:
@ -118,18 +117,15 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
}
if (IsExtensionEnabled(WEBGL_draw_buffers))
{
if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS)
{
if (IsExtensionEnabled(WEBGL_draw_buffers)) {
if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
return JS::Int32Value(mGLMaxColorAttachments);
}
else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS)
{
} else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
return JS::Int32Value(mGLMaxDrawBuffers);
}
else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
} else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
{
if (mBoundFramebuffer) {
GLint iv = 0;
@ -149,17 +145,12 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
if (IsExtensionEnabled(OES_vertex_array_object)) {
switch (pname) {
case LOCAL_GL_VERTEX_ARRAY_BINDING:
{
if (mBoundVertexArray == mDefaultVertexArray){
return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
}
return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) {
if (mBoundVertexArray == mDefaultVertexArray){
return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
}
return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
}
}
@ -171,8 +162,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return StringValue(cx, "Mozilla", rv);
case LOCAL_GL_RENDERER:
return StringValue(cx, "Mozilla", rv);
case LOCAL_GL_VERSION:
{
case LOCAL_GL_VERSION: {
const char* version = 0;
if (IsWebGL2()) {
@ -189,13 +179,11 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
// Privileged string params exposed by WEBGL_debug_renderer_info:
case UNMASKED_VENDOR_WEBGL:
case UNMASKED_RENDERER_WEBGL:
{
case UNMASKED_RENDERER_WEBGL: {
// The privilege check is done in WebGLContext::IsExtensionSupported.
// So here we just have to check that the extension is enabled.
if (!IsExtensionEnabled(WEBGL_debug_renderer_info)) {
ErrorInvalidEnumInfo("getParameter: parameter", pname);
return JS::NullValue();
break;
}
GLenum glstringname = LOCAL_GL_NONE;
if (pname == UNMASKED_VENDOR_WEBGL) {
@ -207,9 +195,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return StringValue(cx, string, rv);
}
//
////////////////////////////////
// Single-value params
//
// unsigned int
case LOCAL_GL_CULL_FACE_MODE:
@ -230,8 +217,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_BLEND_DST_ALPHA:
case LOCAL_GL_BLEND_EQUATION_RGB:
case LOCAL_GL_BLEND_EQUATION_ALPHA:
case LOCAL_GL_GENERATE_MIPMAP_HINT:
{
case LOCAL_GL_GENERATE_MIPMAP_HINT: {
GLint i = 0;
gl->fGetIntegerv(pname, &i);
return JS::NumberValue(uint32_t(i));
@ -254,23 +240,20 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_BLUE_BITS:
case LOCAL_GL_ALPHA_BITS:
case LOCAL_GL_DEPTH_BITS:
case LOCAL_GL_STENCIL_BITS:
{
case LOCAL_GL_STENCIL_BITS: {
GLint i = 0;
gl->fGetIntegerv(pname, &i);
return JS::Int32Value(i);
}
case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: {
if (IsExtensionEnabled(OES_standard_derivatives)) {
GLint i = 0;
gl->fGetIntegerv(pname, &i);
return JS::Int32Value(i);
} else {
break;
}
else {
ErrorInvalidEnum("getParameter: parameter", pname);
return JS::NullValue();
}
}
case LOCAL_GL_MAX_TEXTURE_SIZE:
return JS::Int32Value(mGLMaxTextureSize);
@ -291,8 +274,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
return JS::Int32Value(0);
case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS:
{
case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: {
uint32_t length = mCompressedTextureFormats.Length();
JSObject* obj = Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements());
if (!obj) {
@ -300,8 +282,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
{
case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: {
if (!IsWebGL2()) {
break;
}
@ -313,8 +294,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
case LOCAL_GL_STENCIL_BACK_WRITEMASK:
case LOCAL_GL_STENCIL_VALUE_MASK:
case LOCAL_GL_STENCIL_WRITEMASK:
{
case LOCAL_GL_STENCIL_WRITEMASK: {
GLint i = 0; // the GL api (glGetIntegerv) only does signed ints
gl->fGetIntegerv(pname, &i);
GLuint i_unsigned(i); // this is where -1 becomes 2^32-1
@ -323,21 +303,20 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
// float
case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: {
if (IsExtensionEnabled(EXT_texture_filter_anisotropic)) {
GLfloat f = 0.f;
gl->fGetFloatv(pname, &f);
return JS::DoubleValue(f);
} else {
ErrorInvalidEnumInfo("getParameter: parameter", pname);
return JS::NullValue();
break;
}
}
case LOCAL_GL_DEPTH_CLEAR_VALUE:
case LOCAL_GL_LINE_WIDTH:
case LOCAL_GL_POLYGON_OFFSET_FACTOR:
case LOCAL_GL_POLYGON_OFFSET_UNITS:
case LOCAL_GL_SAMPLE_COVERAGE_VALUE:
{
case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
GLfloat f = 0.f;
gl->fGetFloatv(pname, &f);
return JS::DoubleValue(f);
@ -352,8 +331,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case LOCAL_GL_POLYGON_OFFSET_FILL:
case LOCAL_GL_SCISSOR_TEST:
case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
case LOCAL_GL_DEPTH_WRITEMASK:
{
case LOCAL_GL_DEPTH_WRITEMASK: {
realGLboolean b = 0;
gl->fGetBooleanv(pname, &b);
return JS::BooleanValue(bool(b));
@ -369,13 +347,13 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
case UNPACK_COLORSPACE_CONVERSION_WEBGL:
return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion));
//
////////////////////////////////
// Complex values
//
case LOCAL_GL_DEPTH_RANGE: // 2 floats
case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: // 2 floats
case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: // 2 floats
{
// 2 floats
case LOCAL_GL_DEPTH_RANGE:
case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
GLfloat fv[2] = { 0 };
gl->fGetFloatv(pname, fv);
JSObject* obj = Float32Array::Create(cx, this, 2, fv);
@ -385,9 +363,9 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_COLOR_CLEAR_VALUE: // 4 floats
case LOCAL_GL_BLEND_COLOR: // 4 floats
{
// 4 floats
case LOCAL_GL_COLOR_CLEAR_VALUE:
case LOCAL_GL_BLEND_COLOR: {
GLfloat fv[4] = { 0 };
gl->fGetFloatv(pname, fv);
JSObject* obj = Float32Array::Create(cx, this, 4, fv);
@ -397,8 +375,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_MAX_VIEWPORT_DIMS: // 2 ints
{
// 2 ints
case LOCAL_GL_MAX_VIEWPORT_DIMS: {
GLint iv[2] = { 0 };
gl->fGetIntegerv(pname, iv);
JSObject* obj = Int32Array::Create(cx, this, 2, iv);
@ -408,9 +386,9 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_SCISSOR_BOX: // 4 ints
case LOCAL_GL_VIEWPORT: // 4 ints
{
// 4 ints
case LOCAL_GL_SCISSOR_BOX:
case LOCAL_GL_VIEWPORT: {
GLint iv[4] = { 0 };
gl->fGetIntegerv(pname, iv);
JSObject* obj = Int32Array::Create(cx, this, 4, iv);
@ -420,8 +398,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_COLOR_WRITEMASK: // 4 bools
{
// 4 bools
case LOCAL_GL_COLOR_WRITEMASK: {
realGLboolean gl_bv[4] = { 0 };
gl->fGetBooleanv(pname, gl_bv);
bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]),
@ -433,53 +411,46 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
return arr;
}
case LOCAL_GL_ARRAY_BUFFER_BINDING:
{
case LOCAL_GL_ARRAY_BUFFER_BINDING: {
return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv);
}
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
{
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: {
if (!IsWebGL2()) {
break;
}
return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv);
}
case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING:
{
case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: {
return WebGLObjectAsJSValue(cx, mBoundVertexArray->mBoundElementArrayBuffer.get(), rv);
}
case LOCAL_GL_RENDERBUFFER_BINDING:
{
case LOCAL_GL_RENDERBUFFER_BINDING: {
return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv);
}
case LOCAL_GL_FRAMEBUFFER_BINDING:
{
case LOCAL_GL_FRAMEBUFFER_BINDING: {
return WebGLObjectAsJSValue(cx, mBoundFramebuffer.get(), rv);
}
case LOCAL_GL_CURRENT_PROGRAM:
{
case LOCAL_GL_CURRENT_PROGRAM: {
return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv);
}
case LOCAL_GL_TEXTURE_BINDING_2D:
{
case LOCAL_GL_TEXTURE_BINDING_2D: {
return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv);
}
case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP:
{
case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: {
return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv);
}
default:
ErrorInvalidEnumInfo("getParameter: parameter", pname);
break;
}
ErrorInvalidEnumInfo("getParameter: parameter", pname);
return JS::NullValue();
}

View File

@ -1626,6 +1626,27 @@ WebGLContext::InitAndValidateGL()
mDisableFragHighP = true;
}
// These are the default values, see 6.2 State tables in the
// OpenGL ES 2.0.25 spec.
mColorWriteMask[0] = 1;
mColorWriteMask[1] = 1;
mColorWriteMask[2] = 1;
mColorWriteMask[3] = 1;
mDepthWriteMask = 1;
mColorClearValue[0] = 0.f;
mColorClearValue[1] = 0.f;
mColorClearValue[2] = 0.f;
mColorClearValue[3] = 0.f;
mDepthClearValue = 1.f;
mStencilClearValue = 0;
mStencilRefFront = 0;
mStencilRefBack = 0;
mStencilValueMaskFront = 0xffffffff;
mStencilValueMaskBack = 0xffffffff;
mStencilWriteMaskFront = 0xffffffff;
mStencilWriteMaskBack = 0xffffffff;
// Bindings, etc.
mActiveTexture = 0;
mEmitContextLostErrorOnce = true;
mWebGLError = LOCAL_GL_NO_ERROR;

View File

@ -13,7 +13,6 @@
#include "nsSize.h"
#include "nsError.h"
#include "nsICanvasElementExternal.h"
#include "mozilla/gfx/Rect.h"
class nsICanvasRenderingContextInternal;
@ -26,6 +25,9 @@ namespace layers {
class CanvasLayer;
class LayerManager;
}
namespace gfx {
class SourceSurface;
}
namespace dom {
@ -34,7 +36,6 @@ class HTMLCanvasPrintState;
class PrintCallback;
class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement,
public nsICanvasElementExternal,
public nsIDOMHTMLCanvasElement
{
enum {
@ -159,13 +160,7 @@ public:
*/
bool GetIsOpaque();
/*
* nsICanvasElementExternal -- for use outside of content/layout
*/
NS_IMETHOD_(nsIntSize) GetSizeExternal() MOZ_OVERRIDE;
NS_IMETHOD RenderContextsExternal(gfxContext *aContext,
GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
virtual TemporaryRef<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
virtual bool ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,

View File

@ -38,6 +38,7 @@
#endif
using namespace mozilla::layers;
using namespace mozilla::gfx;
NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
@ -130,9 +131,7 @@ NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement)
NS_INTERFACE_TABLE_INHERITED2(HTMLCanvasElement,
nsIDOMHTMLCanvasElement,
nsICanvasElementExternal)
NS_INTERFACE_TABLE_INHERITED1(HTMLCanvasElement, nsIDOMHTMLCanvasElement)
NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
@ -934,19 +933,13 @@ HTMLCanvasElement::MarkContextClean()
mCurrentContext->MarkContextClean();
}
NS_IMETHODIMP_(nsIntSize)
HTMLCanvasElement::GetSizeExternal()
{
return GetWidthHeight();
}
NS_IMETHODIMP
HTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, GraphicsFilter aFilter, uint32_t aFlags)
TemporaryRef<SourceSurface>
HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mCurrentContext)
return NS_OK;
return nullptr;
return mCurrentContext->Render(aContext, aFilter, aFlags);
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
}
} // namespace dom

View File

@ -196,10 +196,16 @@ HTMLLinkElement::ParseAttribute(int32_t aNamespaceID,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
}
if (aAttribute == nsGkAtoms::sizes) {
aResult.ParseAtomArray(aValue);
return true;
}
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,

View File

@ -109,6 +109,10 @@ public:
{
SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv);
}
nsDOMSettableTokenList* Sizes()
{
return GetTokenList(nsGkAtoms::sizes);
}
// XPCOM GetType is fine.
void SetType(const nsAString& aType, ErrorResult& aRv)
{

View File

@ -3154,6 +3154,7 @@ static nsIAtom** sPropertiesToTraverseAndUnlink[] =
&nsGkAtoms::itemref,
&nsGkAtoms::itemprop,
&nsGkAtoms::sandbox,
&nsGkAtoms::sizes,
nullptr
};

View File

@ -448,6 +448,7 @@ skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fai
[test_imageSrcSet.html]
[test_li_attributes_reflection.html]
[test_link_attributes_reflection.html]
[test_link_sizes.html]
[test_map_attributes_reflection.html]
[test_meta_attributes_reflection.html]
[test_mod_attributes_reflection.html]

View File

@ -0,0 +1,35 @@
<!doctype html>
<html>
<head>
<title>Test link.sizes attribute</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" sizes="16x16 24x24 32x32 48x48">
</head>
<body>
<pre id="test">
<script>
var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; ++i) {
var link = links[i];
ok("sizes" in link, "link.sizes exists");
if (link.rel == 'shortcut icon') {
is(link.sizes.value, "16x16 24x24 32x32 48x48", 'link.sizes.value correct value');
is(link.sizes.length, 4, 'link.sizes.length correct value');
ok(link.sizes.contains('32x32'), 'link.sizes.contains() works');
link.sizes.add('64x64');
is(link.sizes.length, 5, 'link.sizes.length correct value');
link.sizes.remove('64x64');
is(link.sizes.length, 4, 'link.sizes.length correct value');
is(link.sizes + "", "16x16 24x24 32x32 48x48", 'link.sizes stringify correct value');
} else {
is(link.sizes.value, "", 'link.sizes correct value');
}
}
</script>
</pre>
</body>
</html>

View File

@ -502,6 +502,7 @@ AudioStream::CheckForStart()
NS_IMETHODIMP
AudioInitTask::Run()
{
MOZ_ASSERT(mThread);
if (NS_IsMainThread()) {
mThread->Shutdown(); // can't Shutdown from the thread itself, darn
mThread = nullptr;

View File

@ -433,7 +433,12 @@ public:
nsresult Dispatch()
{
return NS_NewNamedThread("CubebInit", getter_AddRefs(mThread), this);
// Can't add 'this' as the event to run, since mThread may not be set yet
nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread));
if (NS_SUCCEEDED(rv)) {
rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
return rv;
}
protected:

View File

@ -1259,8 +1259,8 @@ MediaStreamGraphImpl::RunThread()
UpdateStreamOrder();
}
TrackRate sampleRate;
// Find the sampling rate that we need to use for non-realtime graphs.
TrackRate sampleRate = IdealAudioRate();
if (!mRealtime) {
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
@ -1270,6 +1270,8 @@ MediaStreamGraphImpl::RunThread()
break;
}
}
} else {
sampleRate = IdealAudioRate();
}
GraphTime endBlockingDecisions =

View File

@ -30,10 +30,8 @@ GStreamerFormatHelper* GStreamerFormatHelper::Instance() {
}
void GStreamerFormatHelper::Shutdown() {
if (gInstance) {
delete gInstance;
gInstance = nullptr;
}
delete gInstance;
gInstance = nullptr;
}
static char const *const sContainers[6][2] = {

View File

@ -180,10 +180,8 @@ void AudioOutput::Close()
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
mTrack.clear();
if (mCallbackData) {
delete mCallbackData;
mCallbackData = NULL;
}
delete mCallbackData;
mCallbackData = nullptr;
}
// static

View File

@ -323,10 +323,8 @@ MediaPluginHost *GetMediaPluginHost()
void MediaPluginHost::Shutdown()
{
if (sMediaPluginHost) {
delete sMediaPluginHost;
sMediaPluginHost = nullptr;
}
delete sMediaPluginHost;
sMediaPluginHost = nullptr;
}
} // namespace mozilla

View File

@ -386,9 +386,7 @@ public:
tableInterpolationFactor);
// mPhase runs 0..periodicWaveSize here instead of 0..2*M_PI.
mPhase += periodicWaveSize * mFinalFrequency * rate;
if (mPhase >= periodicWaveSize) {
mPhase -= periodicWaveSize;
}
mPhase = fmod(mPhase, periodicWaveSize);
// Bilinear interpolation between adjacent samples in each table.
uint32_t j1 = floor(mPhase);
uint32_t j2 = j1 + 1;

View File

@ -468,10 +468,7 @@ MediaEngineWebRTCAudioSource::Shutdown()
mVoENetwork->DeRegisterExternalTransport(mChannel);
}
if (mNullTransport) {
delete mNullTransport;
}
delete mNullTransport;
return;
}
@ -491,9 +488,7 @@ MediaEngineWebRTCAudioSource::Shutdown()
mVoENetwork->DeRegisterExternalTransport(mChannel);
}
if (mNullTransport) {
delete mNullTransport;
}
delete mNullTransport;
mVoEProcessing = nullptr;
mVoENetwork = nullptr;

View File

@ -7,6 +7,8 @@
#include "mozilla/dom/ConsoleBinding.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/Maybe.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDocument.h"
#include "nsDOMNavigationTiming.h"
@ -16,6 +18,7 @@
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "xpcprivate.h"
#include "nsContentUtils.h"
#include "nsIConsoleAPIStorage.h"
#include "nsIDOMWindowUtils.h"
@ -171,7 +174,16 @@ public:
nsString mMethodString;
nsTArray<JS::Heap<JS::Value>> mArguments;
Sequence<ConsoleStackEntry> mStack;
// Stack management is complicated, because we want to do it as
// lazily as possible. Therefore, we have the following behavior:
// 1) mTopStackFrame is initialized whenever we have any JS on the stack
// 2) mReifiedStack is initialized if we're created in a worker.
// 3) mStack is set (possibly to null if there is no JS on the stack) if
// we're created on main thread.
Maybe<ConsoleStackEntry> mTopStackFrame;
Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
nsCOMPtr<nsIStackFrame> mStack;
};
// This class is used to clear any exception at the end of this method.
@ -733,6 +745,58 @@ Console::__noSuchMethod__()
// Nothing to do.
}
static
nsresult
StackFrameToStackEntry(nsIStackFrame* aStackFrame,
ConsoleStackEntry& aStackEntry,
uint32_t aLanguage)
{
MOZ_ASSERT(aStackFrame);
nsresult rv = aStackFrame->GetFilename(aStackEntry.mFilename);
NS_ENSURE_SUCCESS(rv, rv);
int32_t lineNumber;
rv = aStackFrame->GetLineNumber(&lineNumber);
NS_ENSURE_SUCCESS(rv, rv);
aStackEntry.mLineNumber = lineNumber;
rv = aStackFrame->GetName(aStackEntry.mFunctionName);
NS_ENSURE_SUCCESS(rv, rv);
aStackEntry.mLanguage = aLanguage;
return NS_OK;
}
static
nsresult
ReifyStack(nsIStackFrame* aStack, nsTArray<ConsoleStackEntry>& aRefiedStack)
{
nsCOMPtr<nsIStackFrame> stack(aStack);
while (stack) {
uint32_t language;
nsresult rv = stack->GetLanguage(&language);
NS_ENSURE_SUCCESS(rv, rv);
if (language == nsIProgrammingLanguage::JAVASCRIPT ||
language == nsIProgrammingLanguage::JAVASCRIPT2) {
ConsoleStackEntry& data = *aRefiedStack.AppendElement();
rv = StackFrameToStackEntry(stack, data, language);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIStackFrame> caller;
rv = stack->GetCaller(getter_AddRefs(caller));
NS_ENSURE_SUCCESS(rv, rv);
stack.swap(caller);
}
return NS_OK;
}
// Queue a call to a console method. See the CALL_DELAY constant.
void
Console::Method(JSContext* aCx, MethodName aMethodName,
@ -797,8 +861,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
return;
}
// nsIStackFrame is not thread-safe so we take what we need and we store in
// an array of ConsoleStackEntry objects.
// Walk up to the first JS stack frame and save it if we find it.
do {
uint32_t language;
nsresult rv = stack->GetLanguage(&language);
@ -809,30 +872,16 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
if (language == nsIProgrammingLanguage::JAVASCRIPT ||
language == nsIProgrammingLanguage::JAVASCRIPT2) {
ConsoleStackEntry& data = *callData->mStack.AppendElement();
rv = stack->GetFilename(data.mFilename);
callData->mTopStackFrame.construct();
nsresult rv = StackFrameToStackEntry(stack,
callData->mTopStackFrame.ref(),
language);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
int32_t lineNumber;
rv = stack->GetLineNumber(&lineNumber);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
data.mLineNumber = lineNumber;
rv = stack->GetName(data.mFunctionName);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return;
}
data.mLanguage = language;
break;
}
nsCOMPtr<nsIStackFrame> caller;
@ -845,6 +894,19 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
stack.swap(caller);
} while (stack);
if (NS_IsMainThread()) {
callData->mStack = stack;
} else {
// nsIStackFrame is not threadsafe, so we need to snapshot it now,
// before we post our runnable to the main thread.
callData->mReifiedStack.construct();
nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
if (NS_WARN_IF(NS_FAILED(rv))) {
Throw(aCx, rv);
return;
}
}
// Monotonic timer for 'time' and 'timeEnd'
if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd) && mWindow) {
nsGlobalWindow *win = static_cast<nsGlobalWindow*>(mWindow.get());
@ -918,14 +980,60 @@ Console::Notify(nsITimer *timer)
return NS_OK;
}
// We store information to lazily compute the stack in the reserved slots of
// LazyStackGetter. The first slot always stores a JS object: it's either the
// JS wrapper of the nsIStackFrame or the actual reified stack representation.
// The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
// reified the stack yet, or an UndefinedValue() otherwise.
enum {
SLOT_STACKOBJ,
SLOT_RAW_STACK
};
bool
LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
JS::Rooted<JSObject*> callee(aCx, &args.callee());
JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
if (v.isUndefined()) {
// Already reified.
args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
return true;
}
nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
nsTArray<ConsoleStackEntry> reifiedStack;
nsresult rv = ReifyStack(stack, reifiedStack);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return false;
}
JS::Rooted<JS::Value> stackVal(aCx);
if (!ToJSValue(aCx, reifiedStack, &stackVal)) {
return false;
}
MOZ_ASSERT(stackVal.isObject());
js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
args.rval().set(stackVal);
return true;
}
void
Console::ProcessCallData(ConsoleCallData* aData)
{
MOZ_ASSERT(aData);
MOZ_ASSERT(NS_IsMainThread());
ConsoleStackEntry frame;
if (!aData->mStack.IsEmpty()) {
frame = aData->mStack[0];
if (!aData->mTopStackFrame.empty()) {
frame = aData->mTopStackFrame.ref();
}
AutoSafeJSContext cx;
@ -971,14 +1079,9 @@ Console::ProcessCallData(ConsoleCallData* aData)
ArgumentsToValueList(aData->mArguments, event.mArguments.Value());
}
if (ShouldIncludeStackrace(aData->mMethodName)) {
event.mStacktrace.Construct();
event.mStacktrace.Value().SwapElements(aData->mStack);
}
else if (aData->mMethodName == MethodGroup ||
aData->mMethodName == MethodGroupCollapsed ||
aData->mMethodName == MethodGroupEnd) {
if (aData->mMethodName == MethodGroup ||
aData->mMethodName == MethodGroupCollapsed ||
aData->mMethodName == MethodGroupEnd) {
ComposeGroupName(cx, aData->mArguments, event.mGroupName);
}
@ -994,6 +1097,18 @@ Console::ProcessCallData(ConsoleCallData* aData)
event.mCounter = IncreaseCounter(cx, frame, aData->mArguments);
}
// We want to create a console event object and pass it to our
// nsIConsoleAPIStorage implementation. We want to define some accessor
// properties on this object, and those will need to keep an nsIStackFrame
// alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
// further, passing untrusted objects to system code is likely to run afoul of
// Object Xrays. So we want to wrap in a system-principal scope here. But
// which one? We could cheat and try to get the underlying JSObject* of
// mStorage, but that's a bit fragile. Instead, we just use the junk scope,
// with explicit permission from the XPConnect module owner. If you're
// tempted to do that anywhere else, talk to said module owner first.
JSAutoCompartment ac2(cx, xpc::GetJunkScope());
JS::Rooted<JS::Value> eventValue(cx);
if (!event.ToObject(cx, &eventValue)) {
Throw(cx, NS_ERROR_FAILURE);
@ -1007,6 +1122,51 @@ Console::ProcessCallData(ConsoleCallData* aData)
return;
}
if (ShouldIncludeStackrace(aData->mMethodName)) {
// Now define the "stacktrace" property on eventObj. There are two cases
// here. Either we came from a worker and have a reified stack, or we want
// to define a getter that will lazily reify the stack.
if (!aData->mReifiedStack.empty()) {
JS::Rooted<JS::Value> stacktrace(cx);
if (!ToJSValue(cx, aData->mReifiedStack.ref(), &stacktrace) ||
!JS_DefineProperty(cx, eventObj, "stacktrace", stacktrace,
JSPROP_ENUMERATE)) {
return;
}
} else {
JSFunction* fun = js::NewFunctionWithReserved(cx, LazyStackGetter, 0, 0,
eventObj, "stacktrace");
if (!fun) {
return;
}
JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
// We want to store our stack in the function and have it stay alive. But
// we also need sane access to the C++ nsIStackFrame. So store both a JS
// wrapper and the raw pointer: the former will keep the latter alive.
JS::Rooted<JS::Value> stackVal(cx);
nsresult rv = nsContentUtils::WrapNative(cx, aData->mStack,
&stackVal);
if (NS_FAILED(rv)) {
return;
}
js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
JS::PrivateValue(aData->mStack.get()));
if (!JS_DefineProperty(cx, eventObj, "stacktrace",
JS::UndefinedHandleValue,
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
JSPROP_SETTER,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
nullptr)) {
return;
}
}
}
if (!mStorage) {
mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
}

View File

@ -12459,82 +12459,38 @@ nsGlobalWindow::GetScrollFrame()
}
nsresult
nsGlobalWindow::BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI,
JSContext **aCXused)
nsGlobalWindow::SecurityCheckURL(const char *aURL)
{
nsIScriptContext *scx = GetContextInternal();
JSContext *cx = nullptr;
*aBuiltURI = nullptr;
if (aCXused)
*aCXused = nullptr;
// get JSContext
NS_ASSERTION(scx, "opening window missing its context");
NS_ASSERTION(mDoc, "opening window missing its document");
if (!scx || !mDoc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMChromeWindow> chrome_win = do_QueryObject(this);
if (nsContentUtils::IsCallerChrome() && !chrome_win) {
// If open() is called from chrome on a non-chrome window, we'll
// use the context from the window on which open() is being called
// to prevent giving chrome priveleges to new windows opened in
// such a way. This also makes us get the appropriate base URI for
// the below URI resolution code.
cx = scx->GetNativeContext();
} else {
// get the JSContext from the call stack
cx = nsContentUtils::GetCurrentJSContext();
}
/* resolve the URI, which could be relative to the calling window
(note the algorithm to get the base URI should match the one
used to actually kick off the load in nsWindowWatcher.cpp). */
nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
nsIURI* baseURI = nullptr;
nsCOMPtr<nsIURI> uriToLoad;
nsCOMPtr<nsPIDOMWindow> sourceWindow;
if (cx) {
nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx);
if (scriptcx)
sourceWindow = do_QueryInterface(scriptcx->GetGlobalObject());
JSContext* topCx = nsContentUtils::GetCurrentJSContext();
if (topCx) {
sourceWindow = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(topCx));
}
if (!sourceWindow) {
sourceWindow = this;
}
AutoJSContext cx;
nsGlobalWindow* sourceWin = static_cast<nsGlobalWindow*>(sourceWindow.get());
JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject());
// Resolve the baseURI, which could be relative to the calling window.
//
// Note the algorithm to get the base URI should match the one
// used to actually kick off the load in nsWindowWatcher.cpp.
nsCOMPtr<nsIDocument> doc = sourceWindow->GetDoc();
nsIURI* baseURI = nullptr;
nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
if (doc) {
baseURI = doc->GetDocBaseURI();
charset = doc->GetDocumentCharacterSet();
}
if (aCXused)
*aCXused = cx;
return NS_NewURI(aBuiltURI, nsDependentCString(aURL), charset.get(), baseURI);
}
nsresult
nsGlobalWindow::SecurityCheckURL(const char *aURL)
{
JSContext *cxUsed;
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(BuildURIfromBase(aURL, getter_AddRefs(uri), &cxUsed))) {
return NS_ERROR_FAILURE;
nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
charset.get(), baseURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!cxUsed) {
return NS_OK;
}
AutoPushJSContext cx(cxUsed);
if (NS_FAILED(nsContentUtils::GetSecurityManager()->
CheckLoadURIFromScript(cx, uri))) {
return NS_ERROR_FAILURE;

View File

@ -1198,8 +1198,7 @@ protected:
already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
nsresult SecurityCheckURL(const char *aURL);
nsresult BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI,
JSContext **aCXused);
bool PopupWhitelisted();
PopupControlState RevisePopupAbuseLevel(PopupControlState);
void FireAbuseEvents(bool aBlocked, bool aWindow,

View File

@ -131,28 +131,7 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
}
}
nsRefPtr<Exception> finalException;
// Do we use DOM exceptions for this error code?
switch (NS_ERROR_GET_MODULE(aRv)) {
case NS_ERROR_MODULE_DOM:
case NS_ERROR_MODULE_SVG:
case NS_ERROR_MODULE_DOM_XPATH:
case NS_ERROR_MODULE_DOM_INDEXEDDB:
case NS_ERROR_MODULE_DOM_FILEHANDLE:
finalException = DOMException::Create(aRv);
break;
default:
break;
}
// If not, use the default.
if (!finalException) {
// aMessage can be null.
finalException = new Exception(nsCString(aMessage), aRv,
EmptyCString(), nullptr, nullptr);
}
nsRefPtr<Exception> finalException = CreateException(aCx, aRv, aMessage);
MOZ_ASSERT(finalException);
if (!ThrowExceptionObject(aCx, finalException)) {
@ -164,6 +143,29 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
return false;
}
already_AddRefed<Exception>
CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
{
// Do we use DOM exceptions for this error code?
switch (NS_ERROR_GET_MODULE(aRv)) {
case NS_ERROR_MODULE_DOM:
case NS_ERROR_MODULE_SVG:
case NS_ERROR_MODULE_DOM_XPATH:
case NS_ERROR_MODULE_DOM_INDEXEDDB:
case NS_ERROR_MODULE_DOM_FILEHANDLE:
return DOMException::Create(aRv);
default:
break;
}
// If not, use the default.
// aMessage can be null, so we can't use nsDependentCString on it.
nsRefPtr<Exception> exception =
new Exception(nsCString(aMessage), aRv,
EmptyCString(), nullptr, nullptr);
return exception.forget();
}
already_AddRefed<nsIStackFrame>
GetCurrentJSStack()
{

View File

@ -30,6 +30,11 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException);
bool
ThrowExceptionObject(JSContext* aCx, nsIException* aException);
// Create an exception object for the given nsresult and message but
// don't set it pending on aCx. This never returns null.
already_AddRefed<Exception>
CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr);
already_AddRefed<nsIStackFrame>
GetCurrentJSStack();

View File

@ -5,6 +5,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/Exceptions.h"
#include "nsAString.h"
#include "nsContentUtils.h"
#include "nsStringBuffer.h"
@ -49,5 +51,14 @@ ISupportsToJSValue(JSContext* aCx,
} // namespace tojsvalue_detail
bool
ToJSValue(JSContext* aCx,
nsresult aArgument,
JS::MutableHandle<JS::Value> aValue)
{
nsRefPtr<Exception> exception = CreateException(aCx, aArgument);
return ToJSValue(aCx, exception, aValue);
}
} // namespace dom
} // namespace mozilla

View File

@ -54,6 +54,14 @@ ToJSValue(JSContext* aCx,
return true;
}
// The uint32_t version is disabled for now because on the super-old b2g
// compiler nsresult and uint32_t are the same type. If someone needs this at
// some point we'll need to figure out how to make it work (e.g. by switching to
// traits structs and using the trick IPC's ParamTraits uses, where a traits
// struct templated on the type inherits from a base traits struct of some sort,
// templated on the same type, or something). Maybe b2g will update to a modern
// compiler before that happens....
#if 0
inline bool
ToJSValue(JSContext* aCx,
uint32_t aArgument,
@ -65,6 +73,7 @@ ToJSValue(JSContext* aCx,
aValue.setNumber(aArgument);
return true;
}
#endif
inline bool
ToJSValue(JSContext* aCx,
@ -203,6 +212,22 @@ ToJSValue(JSContext* aCx,
return aArgument.ToObject(aCx, aValue);
}
// Accept existing JS values (which may not be same-compartment with us
inline bool
ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
JS::MutableHandle<JS::Value> aValue)
{
aValue.set(aArgument);
return MaybeWrapValue(aCx, aValue);
}
// Accept nsresult, for use in rejections, and create an XPCOM
// exception object representing that nsresult.
bool
ToJSValue(JSContext* aCx,
nsresult aArgument,
JS::MutableHandle<JS::Value> aValue);
// Accept arrays of other things we accept
template <typename T>
bool

View File

@ -18,6 +18,8 @@
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/layers/ActiveElementManager.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
@ -68,13 +70,11 @@
#include "StructuredCloneUtils.h"
#include "nsViewportInfo.h"
#include "JavaScriptChild.h"
#include "APZCCallbackHelper.h"
#include "nsILoadContext.h"
#include "ipc/nsGUIEventIPC.h"
#include "mozilla/gfx/Matrix.h"
#include "UnitTransforms.h"
#include "ClientLayerManager.h"
#include "ActiveElementManager.h"
#include "nsColorPickerProxy.h"

View File

@ -42,7 +42,7 @@ namespace layout {
class RenderFrameChild;
}
namespace widget {
namespace layers {
class ActiveElementManager;
}
@ -232,7 +232,7 @@ class TabChild : public PBrowserChild,
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
typedef mozilla::widget::ActiveElementManager ActiveElementManager;
typedef mozilla::layers::ActiveElementManager ActiveElementManager;
public:
/**

View File

@ -493,12 +493,21 @@ function PeerConnectionTest(options) {
PeerConnectionTest.prototype.close = function PCT_close(onSuccess) {
info("Closing peer connections. Connection state=" + this.connected);
function signalingstatechangeClose(state) {
info("'onsignalingstatechange' event '" + state + "' received");
is(state, "closed", "onsignalingstatechange event is closed");
}
// There is no onclose event for the remote peer existent yet. So close it
// side-by-side with the local peer.
if (this.pcLocal)
if (this.pcLocal) {
this.pcLocal.onsignalingstatechange = signalingstatechangeClose;
this.pcLocal.close();
if (this.pcRemote)
}
if (this.pcRemote) {
this.pcRemote.onsignalingstatechange = signalingstatechangeClose;
this.pcRemote.close();
}
this.connected = false;
onSuccess();
@ -585,8 +594,9 @@ function PCT_setLocalDescription(peer, desc, onSuccess) {
}
}
peer.onsignalingstatechange = function () {
info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
peer.onsignalingstatechange = function (state) {
//info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
info(peer + ": 'onsignalingstatechange' event '" + state + "' received");
eventFired = true;
check_next_test();
@ -646,8 +656,8 @@ function PCT_setRemoteDescription(peer, desc, onSuccess) {
}
}
peer.onsignalingstatechange = function () {
info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
peer.onsignalingstatechange = function (state) {
info(peer + ": 'onsignalingstatechange' event '" + state + "' received");
eventFired = true;
check_next_test();
@ -1168,7 +1178,9 @@ function PeerConnectionWrapper(label, configuration) {
this._pc.onsignalingstatechange = function (aEvent) {
info(self + ": 'onsignalingstatechange' event fired");
self.onsignalingstatechange();
// this calls the eventhandler only once and then overwrites it with the
// default unexpectedEvent handler
self.onsignalingstatechange(aEvent);
self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange');
};
}

View File

@ -56,6 +56,11 @@
var description;
var exception = null;
// handle the event which the close() triggers
test.pcLocal.onsignalingstatechange = function (state) {
is(state, "closed", "Received expected onsignalingstatechange event 'closed'");
}
test.pcLocal.close();
try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
@ -66,6 +71,11 @@
ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
exception = null;
// handle the event which the close() triggers
test.pcRemote.onsignalingstatechange = function (state) {
is(state, "closed", "Received expected onsignalingstatechange event 'closed'");
}
test.pcRemote.close();
try { description = test.pcRemote.localDescription; } catch (e) { exception = e; }

View File

@ -49,6 +49,8 @@
exception = null;
try { pconnects.createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: "42" }] }); } catch (e) { exception = e; }
ok(!exception, "createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: \"42\" }] }) succeeds");
pconnect.close();
pconnects.close();
pconnect = null;
pconnects = null;
SimpleTest.finish();

View File

@ -78,15 +78,17 @@ public:
JS::Handle<JS::Value> aValue);
// Helpers for using Promise from C++.
// Most DOM objects are handled already. To add a new type T, such as ints,
// or dictionaries, add a ToJSValue overload in ToJSValue.h.
// Most DOM objects are handled already. To add a new type T, add a
// ToJSValue overload in ToJSValue.h.
// aArg is a const reference so we can pass rvalues like integer constants
template <typename T>
void MaybeResolve(T& aArg) {
void MaybeResolve(const T& aArg) {
MaybeSomething(aArg, &Promise::MaybeResolve);
}
// aArg is a const reference so we can pass rvalues like NS_ERROR_*
template <typename T>
void MaybeReject(T& aArg) {
void MaybeReject(const T& aArg) {
MaybeSomething(aArg, &Promise::MaybeReject);
}

View File

@ -47,7 +47,12 @@ dictionary ConsoleEvent {
sequence<any> styles;
boolean private = false;
sequence<ConsoleStackEntry> stacktrace;
// stacktrace is handled via a getter in some cases so we can construct it
// lazily. Note that we're not making this whole thing an interface because
// consumers expect to see own properties on it, which would mean making the
// props unforgeable, which means lots of JSFunction allocations. Maybe we
// should fix those consumers, of course....
// sequence<ConsoleStackEntry> stacktrace;
DOMString groupName = "";
any timer = null;
any counter = null;

View File

@ -28,8 +28,7 @@ interface HTMLLinkElement : HTMLElement {
attribute DOMString hreflang;
[SetterThrows, Pure]
attribute DOMString type;
// Not supported yet:
// [PutForwards=value] readonly attribute DOMSettableTokenList sizes;
[PutForwards=value] readonly attribute DOMSettableTokenList sizes;
};
HTMLLinkElement implements LinkStyle;

View File

@ -4,7 +4,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManager.h"
#include "AsyncCompositionManager.h" // for ViewTransform
#include "Compositor.h" // for Compositor
#include "CompositorParent.h" // for CompositorParent, etc
#include "InputData.h" // for InputData, etc
@ -12,6 +11,7 @@
#include "gfx3DMatrix.h" // for gfx3DMatrix
#include "mozilla/dom/Touch.h" // for Touch
#include "mozilla/gfx/Point.h" // for Point
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/mozalloc.h" // for operator new

View File

@ -14,7 +14,6 @@
#include "FrameMetrics.h" // for FrameMetrics, etc
#include "GestureEventListener.h" // for GestureEventListener
#include "InputData.h" // for MultiTouchInput, etc
#include "LayerTransactionParent.h" // for LayerTransactionParent
#include "Units.h" // for CSSRect, CSSPoint, etc
#include "UnitTransforms.h" // for TransformTo
#include "base/message_loop.h" // for MessageLoop
@ -40,7 +39,7 @@
#include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/Axis.h" // for AxisX, AxisY, Axis, etc
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
#include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
#include "mozilla/layers/TaskThrottler.h" // for TaskThrottler
#include "mozilla/mozalloc.h" // for operator new, etc
@ -619,9 +618,10 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
mX.StartTouch(point.x);
mY.StartTouch(point.y);
APZCTreeManager* treeManagerLocal = mTreeManager;
if (treeManagerLocal) {
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (treeManagerLocal && controller) {
bool touchCanBePan = treeManagerLocal->CanBePanned(this);
mGeckoContentController->NotifyAPZStateChange(
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::StartTouch, touchCanBePan);
}
SetState(TOUCHING);
@ -962,8 +962,10 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aP
}
void AsyncPanZoomController::OnTouchEndOrCancel() {
mGeckoContentController->NotifyAPZStateChange(
GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
}
}
nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
@ -1108,7 +1110,9 @@ nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent
}
if (IsPanningState(mState)) {
mGeckoContentController->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
}
return nsEventStatus_eConsumeNoDefault;
}
// Don't consume an event that didn't trigger a panning.
@ -2007,12 +2011,12 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
mState = aNewState;
}
if (mGeckoContentController) {
if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
mGeckoContentController->NotifyAPZStateChange(
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::TransformBegin);
} else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
mGeckoContentController->NotifyAPZStateChange(
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::TransformEnd);
}
}

View File

@ -8,7 +8,7 @@
#define mozilla_layers_AsyncPanZoomController_h
#include "CrossProcessMutex.h"
#include "GeckoContentController.h"
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Monitor.h"

Some files were not shown because too many files have changed in this diff Show More