mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound.
This commit is contained in:
commit
2d69e0283b
2
CLOBBER
2
CLOBBER
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()` " +
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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, "*");
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -1167,9 +1167,9 @@ let RemoteDebugger = {
|
||||
};
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
DebuggerServer.onConnectionChange = function(what) {
|
||||
DebuggerServer.on("connectionchange", function() {
|
||||
AdbController.updateState();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
14
b2g/config/tooltool-manifests/linux32/releng.manifest
Normal file
14
b2g/config/tooltool-manifests/linux32/releng.manifest
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"size": 50,
|
||||
"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
|
||||
"algorithm": "sha512",
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 160232,
|
||||
"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.xz"
|
||||
}
|
||||
]
|
14
b2g/config/tooltool-manifests/linux64/releng.manifest
Normal file
14
b2g/config/tooltool-manifests/linux64/releng.manifest
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"size": 50,
|
||||
"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
|
||||
"algorithm": "sha512",
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 160232,
|
||||
"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.xz"
|
||||
}
|
||||
]
|
@ -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"
|
||||
}
|
||||
]
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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});
|
||||
|
@ -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}
|
||||
|
@ -1,4 +1,5 @@
|
||||
no_tooltool=1
|
||||
no_sccache=1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/linux32/nightly
|
||||
|
||||
|
@ -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}
|
||||
|
@ -1,4 +1,5 @@
|
||||
no_tooltool=1
|
||||
no_sccache=1
|
||||
|
||||
. $topsrcdir/browser/config/mozconfigs/linux64/nightly
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
});
|
@ -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");
|
||||
});
|
@ -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);
|
||||
}
|
||||
}
|
28
browser/devtools/markupview/test/doc_markup_toggle.html
Normal file
28
browser/devtools/markupview/test/doc_markup_toggle.html
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
@ -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}),
|
||||
|
@ -9,7 +9,7 @@ EXTRA_COMPONENTS += [
|
||||
|
||||
JS_MODULES_PATH = 'modules/experiments'
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
EXTRA_JS_MODULES += [
|
||||
'Experiments.jsm',
|
||||
]
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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) {
|
||||
|
@ -243,7 +243,7 @@ add_task(function* test_cache() {
|
||||
|
||||
// Cleanup.
|
||||
|
||||
yield experiments.disableExperiment();
|
||||
yield experiments._toggleExperimentsEnabled(false);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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:">
|
||||
|
@ -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")
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -11,7 +11,6 @@ XPIDL_SOURCES += [
|
||||
XPIDL_MODULE = 'content_canvas'
|
||||
|
||||
EXPORTS += [
|
||||
'nsICanvasElementExternal.h',
|
||||
'nsICanvasRenderingContextInternal.h',
|
||||
]
|
||||
|
||||
|
@ -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___ */
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -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>
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -3154,6 +3154,7 @@ static nsIAtom** sPropertiesToTraverseAndUnlink[] =
|
||||
&nsGkAtoms::itemref,
|
||||
&nsGkAtoms::itemprop,
|
||||
&nsGkAtoms::sandbox,
|
||||
&nsGkAtoms::sizes,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
@ -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]
|
||||
|
35
content/html/content/test/test_link_sizes.html
Normal file
35
content/html/content/test/test_link_sizes.html
Normal 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>
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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 =
|
||||
|
@ -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] = {
|
||||
|
@ -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
|
||||
|
@ -323,10 +323,8 @@ MediaPluginHost *GetMediaPluginHost()
|
||||
|
||||
void MediaPluginHost::Shutdown()
|
||||
{
|
||||
if (sMediaPluginHost) {
|
||||
delete sMediaPluginHost;
|
||||
sMediaPluginHost = nullptr;
|
||||
}
|
||||
delete sMediaPluginHost;
|
||||
sMediaPluginHost = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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:
|
||||
/**
|
||||
|
@ -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');
|
||||
};
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user