Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-02-19 16:21:23 +01:00
commit abefd9f650
166 changed files with 3995 additions and 850 deletions

View File

@ -196,6 +196,7 @@ let RemoteDebugger = {
globalActorFactories: restrictPrivileges ? {
webappsActor: DebuggerServer.globalActorFactories.webappsActor,
deviceActor: DebuggerServer.globalActorFactories.deviceActor,
settingsActor: DebuggerServer.globalActorFactories.settingsActor
} : DebuggerServer.globalActorFactories
};
let { RootActor } = devtools.require("devtools/server/actors/root");

View File

@ -329,11 +329,6 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var cx = React.addons.classSet;
// For now all of the menu entries require FxA so hide the whole gear if FxA is disabled.
if (!navigator.mozLoop.fxAEnabled) {
return null;
}
return (
React.createElement("div", {className: "settings-menu dropdown"},
React.createElement("a", {className: "button-settings", onClick: this.showDropdownMenu,
@ -347,7 +342,7 @@ loop.panel = (function(_, mozL10n) {
React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_account"),
onClick: this.handleClickAccountEntry,
icon: "account",
displayed: this._isSignedIn()}),
displayed: this._isSignedIn() && navigator.mozLoop.fxAEnabled}),
React.createElement(SettingsDropdownEntry, {icon: "tour",
label: mozL10n.get("tour_label"),
onClick: this.openGettingStartedTour}),

View File

@ -329,11 +329,6 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var cx = React.addons.classSet;
// For now all of the menu entries require FxA so hide the whole gear if FxA is disabled.
if (!navigator.mozLoop.fxAEnabled) {
return null;
}
return (
<div className="settings-menu dropdown">
<a className="button-settings" onClick={this.showDropdownMenu}
@ -347,7 +342,7 @@ loop.panel = (function(_, mozL10n) {
<SettingsDropdownEntry label={mozL10n.get("settings_menu_item_account")}
onClick={this.handleClickAccountEntry}
icon="account"
displayed={this._isSignedIn()} />
displayed={this._isSignedIn() && navigator.mozLoop.fxAEnabled} />
<SettingsDropdownEntry icon="tour"
label={mozL10n.get("tour_label")}
onClick={this.openGettingStartedTour} />

View File

@ -260,6 +260,17 @@ describe("loop.panel", function() {
});
});
it("should hide the account entry when FxA is not enabled", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
navigator.mozLoop.fxAEnabled = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
expect(view.getDOMNode().querySelectorAll(".icon-account"))
.to.have.length.of(0);
});
describe("SettingsDropdown", function() {
beforeEach(function() {
navigator.mozLoop.logInToFxA = sandbox.stub();
@ -271,14 +282,6 @@ describe("loop.panel", function() {
navigator.mozLoop.fxAEnabled = true;
});
it("should be hidden if FxA is not enabled",
function() {
navigator.mozLoop.fxAEnabled = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
expect(view.getDOMNode()).to.be.null;
});
it("should show a signin entry when user is not authenticated",
function() {
navigator.mozLoop.loggedInToFxA = false;

View File

@ -86,6 +86,7 @@ support-files =
doc_scope-variable-3.html
doc_scope-variable-4.html
doc_script-eval.html
doc_script-bookmarklet.html
doc_script-switching-01.html
doc_script-switching-02.html
doc_split-console-paused-reload.html
@ -413,6 +414,8 @@ skip-if = e10s && debug
skip-if = e10s && debug
[browser_dbg_sources-sorting.js]
skip-if = e10s && debug
[browser_dbg_sources-bookmarklet.js]
skip-if = e10s && debug
[browser_dbg_split-console-paused-reload.js]
skip-if = e10s && debug
[browser_dbg_stack-01.js]

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure javascript bookmarklet scripts appear and load correctly in the source list
*/
const TAB_URL = EXAMPLE_URL + "doc_script-bookmarklet.html";
const BOOKMARKLET_SCRIPT_CODE = "console.log('bookmarklet executed');";
function test() {
let gTab, gPanel, gDebugger;
let gSources, gBreakpoints;
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gSources = gDebugger.DebuggerView.Sources;
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
return Task.spawn(function*() {
let waitForSource = waitForDebuggerEvents(gPanel, gPanel.panelWin.EVENTS.NEW_SOURCE, 1);
// NOTE: devtools debugger panel needs to be already open,
// or the bookmarklet script will not be shown in the sources panel
callInTab(gTab, "injectBookmarklet", BOOKMARKLET_SCRIPT_CODE);
yield waitForSource;
is(gSources.values.length, 2, "Should have 2 source");
let item = gSources.getItemForAttachment(e => {
return e.label.indexOf("javascript:") === 0;
});
ok(item, "Source label is incorrect.");
let res = yield promiseInvoke(gDebugger.DebuggerController.client,
gDebugger.DebuggerController.client.request,
{ to: item.value, type: "source"});
ok(res && res.source == BOOKMARKLET_SCRIPT_CODE, "SourceActor reply received");
is(res.source, BOOKMARKLET_SCRIPT_CODE, "source is correct");
is(res.contentType, "text/javascript", "contentType is correct");
yield closeDebuggerAndFinish(gPanel);
});
});
}

View File

@ -0,0 +1,14 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger test page</title>
</head>
<body>
<script>function injectBookmarklet(bookmarklet) { setTimeout(function() { window.location = "javascript:" + bookmarklet; }, 0); }</script>
</body>
</html>

View File

@ -0,0 +1,603 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci, Cc } = require("chrome");
const { Services } = require("resource://gre/modules/Services.jsm");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/device.properties");
/* `Devices` is a catalog of existing devices and their properties, intended
* for (mobile) device emulation tools and features.
*
* The properties of a device are:
* - name: Device brand and model(s).
* - width: Viewport width.
* - height: Viewport height.
* - pixelRatio: Screen pixel ratio to viewport.
* - userAgent: Device UserAgent string.
* - touch: Whether the screen is touch-enabled.
*
* To add more devices to this catalog, either patch this file, or push new
* device descriptions from your own code (e.g. an addon) like so:
*
* var myPhone = { name: "My Phone", ... };
* require("devtools/shared/devices").Devices.Others.phones.push(myPhone);
*/
let Devices = {
Types: ["phones", "tablets", "notebooks", "televisions", "watches"],
// Get the localized string of a device type.
GetString(deviceType) {
return Strings.GetStringFromName("device." + deviceType);
},
};
exports.Devices = Devices;
// The `Devices.FirefoxOS` list was put together from various sources online.
Devices.FirefoxOS = {
phones: [
{
name: "Firefox OS Flame",
width: 320,
height: 570,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Alcatel One Touch Fire, Fire C",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; ALCATELOneTouch4012X; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Alcatel Fire E",
width: 320,
height: 480,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Mobile; ALCATELOneTouch4012X; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Geeksphone Keon",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Geeksphone Peak, Revolution",
width: 360,
height: 640,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Intex Cloud Fx",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "LG Fireweb",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; LG-D300; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Spice Fire One Mi-FX1",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Symphony GoFox F15",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "Zen Fire 105",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "ZTE Open",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; ZTEOPEN; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "ZTE Open C",
width: 320,
height: 450,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Mobile; OPENC; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
],
tablets: [
{
name: "Foxconn InFocus",
width: 1280,
height: 800,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
{
name: "VIA Vixen",
width: 1024,
height: 600,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Mobile; rv:28.0) Gecko/28.0 Firefox/28.0",
touch: true,
},
],
notebooks: [
],
televisions: [
{
name: "720p HD Television",
width: 1280,
height: 720,
pixelRatio: 1,
userAgent: "",
touch: false,
},
{
name: "1080p Full HD Television",
width: 1920,
height: 1080,
pixelRatio: 1,
userAgent: "",
touch: false,
},
{
name: "4K Ultra HD Television",
width: 3840,
height: 2160,
pixelRatio: 1,
userAgent: "",
touch: false,
},
],
watches: [
{
name: "LG G Watch",
width: 280,
height: 280,
pixelRatio: 1,
userAgent: "",
touch: true,
},
{
name: "LG G Watch R",
width: 320,
height: 320,
pixelRatio: 1,
userAgent: "",
touch: true,
},
{
name: "Moto 360",
width: 320,
height: 290,
pixelRatio: 1,
userAgent: "",
touch: true,
},
{
name: "Samsung Gear Live",
width: 320,
height: 320,
pixelRatio: 1,
userAgent: "",
touch: true,
},
],
};
// `Devices.Others` was derived from the Chromium source code:
// - chromium/src/third_party/WebKit/Source/devtools/front_end/toolbox/OverridesUI.js
Devices.Others = {
phones: [
{
name: "Apple iPhone 3GS",
width: 320,
height: 480,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
touch: true,
},
{
name: "Apple iPhone 4",
width: 320,
height: 480,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
touch: true,
},
{
name: "Apple iPhone 5",
width: 320,
height: 568,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
touch: true,
},
{
name: "Apple iPhone 6",
width: 375,
height: 667,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
touch: true,
},
{
name: "Apple iPhone 6 Plus",
width: 414,
height: 736,
pixelRatio: 3,
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
touch: true,
},
{
name: "BlackBerry Z10",
width: 384,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
touch: true,
},
{
name: "BlackBerry Z30",
width: 360,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
touch: true,
},
{
name: "Google Nexus 4",
width: 384,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
touch: true,
},
{
name: "Google Nexus 5",
width: 360,
height: 640,
pixelRatio: 3,
userAgent: "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
touch: true,
},
{
name: "Google Nexus S",
width: 320,
height: 533,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Nexus S Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "HTC Evo, Touch HD, Desire HD, Desire",
width: 320,
height: 533,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "HTC One X, EVO LTE",
width: 360,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; Android 4.0.3; HTC One X Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
touch: true,
},
{
name: "HTC Sensation, Evo 3D",
width: 360,
height: 640,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
{
name: "LG Optimus 2X, Optimus 3D, Optimus Black",
width: 320,
height: 533,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2; en-us; LG-P990/V08c Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2",
touch: true,
},
{
name: "LG Optimus G",
width: 384,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; Android 4.0; LG-E975 Build/IMM76L) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
touch: true,
},
{
name: "LG Optimus LTE, Optimus 4X HD",
width: 424,
height: 753,
pixelRatio: 1.7,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.3; en-us; LG-P930 Build/GRJ90) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "LG Optimus One",
width: 213,
height: 320,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; LG-MS690 Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Motorola Defy, Droid, Droid X, Milestone",
width: 320,
height: 569,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
touch: true,
},
{
name: "Motorola Droid 3, Droid 4, Droid Razr, Atrix 4G, Atrix 2",
width: 540,
height: 960,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Motorola Droid Razr HD",
width: 720,
height: 1280,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.3; en-us; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Nokia C5, C6, C7, N97, N8, X7",
width: 360,
height: 640,
pixelRatio: 1,
userAgent: "NokiaN97/21.1.107 (SymbianOS/9.4; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebkit/525 (KHTML, like Gecko) BrowserNG/7.1.4",
touch: true,
},
{
name: "Nokia Lumia 7X0, Lumia 8XX, Lumia 900, N800, N810, N900",
width: 320,
height: 533,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 820)",
touch: true,
},
{
name: "Samsung Galaxy Note 3",
width: 360,
height: 640,
pixelRatio: 3,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
{
name: "Samsung Galaxy Note II",
width: 360,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
{
name: "Samsung Galaxy Note",
width: 400,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.3; en-us; SAMSUNG-SGH-I717 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Samsung Galaxy S III, Galaxy Nexus",
width: 360,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
{
name: "Samsung Galaxy S, S II, W",
width: 320,
height: 533,
pixelRatio: 1.5,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.1; en-us; GT-I9000 Build/ECLAIR) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
touch: true,
},
{
name: "Samsung Galaxy S4",
width: 360,
height: 640,
pixelRatio: 3,
userAgent: "Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36",
touch: true,
},
{
name: "Sony Xperia S, Ion",
width: 360,
height: 640,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; LT28at Build/6.1.C.1.111) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
{
name: "Sony Xperia Sola, U",
width: 480,
height: 854,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.3; en-us; SonyEricssonST25i Build/6.0.B.1.564) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Sony Xperia Z, Z1",
width: 360,
height: 640,
pixelRatio: 3,
userAgent: "Mozilla/5.0 (Linux; U; Android 4.2; en-us; SonyC6903 Build/14.1.G.1.518) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
touch: true,
},
],
tablets: [
{
name: "Amazon Kindle Fire HDX 7″",
width: 1920,
height: 1200,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFTHWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
touch: true,
},
{
name: "Amazon Kindle Fire HDX 8.9″",
width: 2560,
height: 1600,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
touch: true,
},
{
name: "Amazon Kindle Fire (First Generation)",
width: 1024,
height: 600,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.0.141.16-Gen4_11004310) AppleWebkit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true",
touch: true,
},
{
name: "Apple iPad 1 / 2 / iPad Mini",
width: 1024,
height: 768,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (iPad; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5",
touch: true,
},
{
name: "Apple iPad 3 / 4",
width: 1024,
height: 768,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
touch: true,
},
{
name: "BlackBerry PlayBook",
width: 1024,
height: 600,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+",
touch: true,
},
{
name: "Google Nexus 10",
width: 1280,
height: 800,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; Android 4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
touch: true,
},
{
name: "Google Nexus 7 2",
width: 960,
height: 600,
pixelRatio: 2,
userAgent: "Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
touch: true,
},
{
name: "Google Nexus 7",
width: 966,
height: 604,
pixelRatio: 1.325,
userAgent: "Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
touch: true,
},
{
name: "Motorola Xoom, Xyboard",
width: 1280,
height: 800,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
touch: true,
},
{
name: "Samsung Galaxy Tab 7.7, 8.9, 10.1",
width: 1280,
height: 800,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
{
name: "Samsung Galaxy Tab",
width: 1024,
height: 600,
pixelRatio: 1,
userAgent: "Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
touch: true,
},
],
notebooks: [
{
name: "Notebook with touch",
width: 1280,
height: 950,
pixelRatio: 1,
userAgent: "",
touch: true,
},
{
name: "Notebook with HiDPI screen",
width: 1440,
height: 900,
pixelRatio: 2,
userAgent: "",
touch: false,
},
{
name: "Generic notebook",
width: 1280,
height: 800,
pixelRatio: 1,
userAgent: "",
touch: false,
},
],
televisions: [
],
watches: [
],
};

View File

@ -48,6 +48,7 @@ EXTRA_JS_MODULES.devtools.shared.timeline += [
EXTRA_JS_MODULES.devtools.shared += [
'autocomplete-popup.js',
'd3.js',
'devices.js',
'doorhanger.js',
'frame-script-utils.js',
'inplace-editor.js',

View File

@ -37,7 +37,6 @@ add_task(function*() {
}
});
function* testLivePreviewData(data, ruleView, selector) {
let testElement = getNode(selector);
let idRuleEditor = getRuleViewRuleEditor(ruleView, 1);
@ -57,10 +56,13 @@ function* testLivePreviewData(data, ruleView, selector) {
EventUtils.synthesizeKey("VK_RETURN", {});
}
// This wait is an orange waiting to happen, but it might take a few event
// loop spins in either the client or parent process before we see the
// updated value.
yield wait(1);
// Wait for the modifyproperties request to complete before
// checking the computed style.
for (let rule of ruleView._elementStyle.rules) {
if (rule._applyingModifications) {
yield rule._applyingModifications;
}
}
// While the editor is still focused in, the display should have changed already
is((yield getComputedStyleProperty(selector, null, "display")),

View File

@ -0,0 +1,19 @@
# 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/.
# LOCALIZATION NOTE These strings are used inside Device Emulation developer
# tools. The correct localization of this file might be to keep it in English,
# or another language commonly spoken among web developers. You want to make
# that choice consistent across the developer tools. A good criteria is the
# language in which you'd find the best documentation on web development on the
# web.
# LOCALIZATION NOTE:
# These strings are category names in a list of devices that a user can choose
# to simulate (e.g. "ZTE Open C", "VIA Vixen", "720p HD Television", etc).
device.phones=Phones
device.tablets=Tablets
device.notebooks=Notebooks
device.televisions=TVs
device.watches=Watches

View File

@ -33,6 +33,7 @@
locale/browser/devtools/appcacheutils.properties (%chrome/browser/devtools/appcacheutils.properties)
locale/browser/devtools/debugger.dtd (%chrome/browser/devtools/debugger.dtd)
locale/browser/devtools/debugger.properties (%chrome/browser/devtools/debugger.properties)
locale/browser/devtools/device.properties (%chrome/browser/devtools/device.properties)
locale/browser/devtools/netmonitor.dtd (%chrome/browser/devtools/netmonitor.dtd)
locale/browser/devtools/netmonitor.properties (%chrome/browser/devtools/netmonitor.properties)
locale/browser/devtools/shadereditor.dtd (%chrome/browser/devtools/shadereditor.dtd)

View File

@ -52,6 +52,7 @@
#include "nsIAuthPrompt2.h"
#include "nsIChannelEventSink.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIServiceWorkerManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScrollableFrame.h"
@ -1041,6 +1042,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
///*****************************************************************************
@ -13921,6 +13923,52 @@ nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
#endif
}
NS_IMETHODIMP
nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate, bool* aShouldIntercept)
{
*aShouldIntercept = false;
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
if (!swm) {
return NS_OK;
}
if (aIsNavigate) {
return swm->IsAvailableForURI(aURI, aShouldIntercept);
}
nsCOMPtr<nsIDocument> doc = GetDocument();
if (!doc) {
return NS_ERROR_NOT_AVAILABLE;
}
return swm->IsControlled(doc, aShouldIntercept);
}
NS_IMETHODIMP
nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
{
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
if (!swm) {
aChannel->Cancel();
return NS_OK;
}
bool isNavigation = false;
nsresult rv = aChannel->GetIsNavigation(&isNavigation);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> doc;
if (!isNavigation) {
doc = GetDocument();
if (!doc) {
return NS_ERROR_NOT_AVAILABLE;
}
}
return swm->DispatchFetchEvent(doc, aChannel);
}
NS_IMETHODIMP
nsDocShell::SetPaymentRequestId(const nsAString& aPaymentRequestId)
{

View File

@ -12,6 +12,7 @@
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIBaseWindow.h"
#include "nsINetworkInterceptController.h"
#include "nsIScrollable.h"
#include "nsITextScroll.h"
#include "nsIContentViewerContainer.h"
@ -152,6 +153,7 @@ class nsDocShell MOZ_FINAL
, public nsILinkHandler
, public nsIClipboardCommands
, public nsIDOMStorageManager
, public nsINetworkInterceptController
, public mozilla::SupportsWeakPtr<nsDocShell>
{
friend class nsDSURIContentListener;
@ -182,6 +184,7 @@ public:
NS_DECL_NSIAUTHPROMPTPROVIDER
NS_DECL_NSICLIPBOARDCOMMANDS
NS_DECL_NSIWEBSHELLSERVICES
NS_DECL_NSINETWORKINTERCEPTCONTROLLER
NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
NS_IMETHOD Stop() MOZ_OVERRIDE {

View File

@ -622,7 +622,7 @@ protected:
DECL_SHIM(nsIApplicationCacheContainer, NSIAPPLICATIONCACHECONTAINER)
#undef DECL_SHIM
};
/**
* Add an ExternalResource for aURI. aViewer and aLoadGroup might be null
* when this is called if the URI didn't result in an XML document. This

View File

@ -106,7 +106,10 @@ using namespace mozilla::dom;
#define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
#define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
#define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
#define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, OPENED in IE and external view
#define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, corresponds to
// "OPENED and the send()
// flag is set" in spec
// terms.
// The above states are mutually exclusive, change with ChangeState() only.
// The states below can be combined.
#define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
@ -1078,8 +1081,7 @@ nsXMLHttpRequest::GetResponseURL(nsAString& aUrl)
{
aUrl.Truncate();
uint16_t readyState;
GetReadyState(&readyState);
uint16_t readyState = ReadyState();
if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
return;
}
@ -3367,9 +3369,12 @@ nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
void
nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
{
// Return error if we're already processing a request
if (XML_HTTP_REQUEST_SENT & mState) {
aRv = NS_ERROR_FAILURE;
// Return error if we're already processing a request. Note that we can't use
// ReadyState() here, because it can't differentiate between "opened" and
// "sent", so we use mState directly.
if (!(mState & XML_HTTP_REQUEST_UNSENT) &&
!(mState & XML_HTTP_REQUEST_OPENED)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}

View File

@ -418,6 +418,14 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::workers::ExtendableEvent',
},
'FetchEvent': {
'headerFile': 'ServiceWorkerEvents.h',
'nativeType': 'mozilla::dom::workers::FetchEvent',
'binaryNames': {
'request': 'request_'
},
},
'FileList': {
'headerFile': 'mozilla/dom/File.h',
},

View File

@ -459,12 +459,45 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenu
void
WebGL2Context::ReadBuffer(GLenum mode)
{
MOZ_CRASH("Not Implemented.");
if (IsContextLost())
return;
MakeContextCurrent();
if (mBoundReadFramebuffer) {
bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
mode <= LastColorAttachment());
if (mode != LOCAL_GL_NONE &&
!isColorAttachment)
{
ErrorInvalidEnumInfo("readBuffer: If READ_FRAMEBUFFER is non-null,"
" `mode` must be COLOR_ATTACHMENTN or NONE."
" Was:", mode);
return;
}
gl->fReadBuffer(mode);
return;
}
// Operating on the default framebuffer.
if (mode != LOCAL_GL_NONE &&
mode != LOCAL_GL_BACK)
{
ErrorInvalidEnumInfo("readBuffer: If READ_FRAMEBUFFER is null, `mode`"
" must be BACK or NONE. Was:", mode);
return;
}
gl->Screen()->SetReadBuffer(mode);
}
void
WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat,
WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
GLenum internalFormat,
GLsizei width, GLsizei height)
{
MOZ_CRASH("Not Implemented.");
RenderbufferStorage_base("renderbufferStorageMultisample", target, samples,
internalFormat, width, height);
}

View File

@ -203,6 +203,7 @@ WebGLContextOptions::WebGLContextOptions()
WebGLContext::WebGLContext()
: WebGLContextUnchecked(nullptr)
, mBypassShaderValidation(false)
, mGLMaxSamples(1)
, mNeedsFakeNoAlpha(false)
{
mGeneration = 0;
@ -1721,6 +1722,7 @@ WebGLContext::GetSurfaceSnapshot(bool* out_premultAlpha)
{
ScopedBindFramebuffer autoFB(gl, 0);
ClearBackbufferIfNeeded();
// TODO: Save, override, then restore glReadBuffer if present.
ReadPixelsIntoDataSurface(gl, surf);
}

View File

@ -254,7 +254,9 @@ public:
void ErrorInvalidOperation(const char* fmt = 0, ...);
void ErrorInvalidValue(const char* fmt = 0, ...);
void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...);
void ErrorInvalidEnumInfo(const char* info, GLenum enumvalue);
void ErrorInvalidEnumInfo(const char* info, GLenum enumValue);
void ErrorInvalidEnumInfo(const char* info, const char* funcName,
GLenum enumValue);
void ErrorOutOfMemory(const char* fmt = 0, ...);
const char* ErrorName(GLenum error);
@ -531,6 +533,11 @@ public:
ErrorResult& rv);
void RenderbufferStorage(GLenum target, GLenum internalFormat,
GLsizei width, GLsizei height);
protected:
void RenderbufferStorage_base(const char* funcName, GLenum target,
GLsizei samples, GLenum internalformat,
GLsizei width, GLsizei height);
public:
void SampleCoverage(GLclampf value, WebGLboolean invert);
void Scissor(GLint x, GLint y, GLsizei width, GLsizei height);
void ShaderSource(WebGLShader* shader, const nsAString& source);
@ -1146,8 +1153,9 @@ protected:
int32_t mGLMaxVertexUniformVectors;
int32_t mGLMaxColorAttachments;
int32_t mGLMaxDrawBuffers;
GLuint mGLMaxTransformFeedbackSeparateAttribs;
uint32_t mGLMaxTransformFeedbackSeparateAttribs;
GLuint mGLMaxUniformBufferBindings;
GLsizei mGLMaxSamples;
public:
GLuint MaxVertexAttribs() const {
@ -1422,6 +1430,10 @@ protected:
uint32_t mMaxFramebufferColorAttachments;
GLenum LastColorAttachment() const {
return LOCAL_GL_COLOR_ATTACHMENT0 + mMaxFramebufferColorAttachments - 1;
}
bool ValidateFramebufferTarget(GLenum target, const char* const info);
WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;

View File

@ -1970,33 +1970,28 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
}
bool isSourceTypeFloat = false;
if (mBoundReadFramebuffer &&
mBoundReadFramebuffer->ColorAttachmentCount() &&
mBoundReadFramebuffer->ColorAttachment(0).IsDefined())
{
isSourceTypeFloat = mBoundReadFramebuffer->ColorAttachment(0).IsReadableFloat();
MakeContextCurrent();
bool isSourceTypeFloat;
if (mBoundReadFramebuffer) {
TexInternalFormat srcFormat;
if (!mBoundReadFramebuffer->ValidateForRead("readPixels", &srcFormat))
return;
MOZ_ASSERT(srcFormat != LOCAL_GL_NONE);
TexType type = TypeFromInternalFormat(srcFormat);
isSourceTypeFloat = (type == LOCAL_GL_FLOAT ||
type == LOCAL_GL_HALF_FLOAT);
} else {
ClearBackbufferIfNeeded();
isSourceTypeFloat = false;
}
if (isReadTypeFloat != isSourceTypeFloat)
return ErrorInvalidOperation("readPixels: Invalid type floatness");
// Check the format and type params to assure they are an acceptable pair (as per spec)
MakeContextCurrent();
if (mBoundReadFramebuffer) {
// prevent readback of arbitrary video memory through uninitialized renderbuffers!
if (!mBoundReadFramebuffer->CheckAndInitializeAttachments())
return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
if (!mBoundReadFramebuffer->HasCompletePlanes(readPlaneBits)) {
return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
}
} else {
ClearBackbufferIfNeeded();
}
bool isFormatAndTypeValid = false;
@ -2128,95 +2123,156 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
}
void
WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
GLsizei samples,
GLenum internalFormat, GLsizei width,
GLsizei height)
{
if (IsContextLost())
return;
if (!mBoundRenderbuffer)
return ErrorInvalidOperation("renderbufferStorage called on renderbuffer 0");
if (!mBoundRenderbuffer) {
ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
return;
}
if (target != LOCAL_GL_RENDERBUFFER)
return ErrorInvalidEnumInfo("renderbufferStorage: target", target);
if (target != LOCAL_GL_RENDERBUFFER) {
ErrorInvalidEnumInfo("`target`", funcName, target);
return;
}
if (width < 0 || height < 0)
return ErrorInvalidValue("renderbufferStorage: width and height must be >= 0");
if (samples < 0 || samples > mGLMaxSamples) {
ErrorInvalidValue("%s: `samples` is out of the valid range.", funcName);
return;
}
if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize)
return ErrorInvalidValue("renderbufferStorage: width or height exceeds maximum renderbuffer size");
if (width < 0 || height < 0) {
ErrorInvalidValue("%s: Width and height must be >= 0.", funcName);
return;
}
if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize) {
ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer"
" size.", funcName);
return;
}
bool isFormatValid = false;
switch (internalFormat) {
case LOCAL_GL_RGBA4:
case LOCAL_GL_RGB5_A1:
case LOCAL_GL_RGB565:
case LOCAL_GL_DEPTH_COMPONENT16:
case LOCAL_GL_STENCIL_INDEX8:
case LOCAL_GL_DEPTH_STENCIL:
isFormatValid = true;
break;
case LOCAL_GL_SRGB8_ALPHA8_EXT:
if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
isFormatValid = true;
break;
case LOCAL_GL_RGB16F:
case LOCAL_GL_RGBA16F:
if (IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float) &&
IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float))
{
isFormatValid = true;
}
break;
case LOCAL_GL_RGB32F:
case LOCAL_GL_RGBA32F:
if (IsExtensionEnabled(WebGLExtensionID::OES_texture_float) &&
IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
{
isFormatValid = true;
}
break;
default:
break;
}
if (!isFormatValid) {
ErrorInvalidEnumInfo("`internalFormat`", funcName, internalFormat);
return;
}
// certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL
GLenum internalformatForGL = internalformat;
GLenum internalFormatForGL = internalFormat;
switch (internalformat) {
switch (internalFormat) {
case LOCAL_GL_RGBA4:
case LOCAL_GL_RGB5_A1:
// 16-bit RGBA formats are not supported on desktop GL
if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGBA8;
if (!gl->IsGLES())
internalFormatForGL = LOCAL_GL_RGBA8;
break;
case LOCAL_GL_RGB565:
// the RGB565 format is not supported on desktop GL
if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGB8;
if (!gl->IsGLES())
internalFormatForGL = LOCAL_GL_RGB8;
break;
case LOCAL_GL_DEPTH_COMPONENT16:
if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24))
internalformatForGL = LOCAL_GL_DEPTH_COMPONENT24;
internalFormatForGL = LOCAL_GL_DEPTH_COMPONENT24;
else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil))
internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8;
break;
case LOCAL_GL_STENCIL_INDEX8:
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
break;
case LOCAL_GL_DEPTH_STENCIL:
// We emulate this in WebGLRenderbuffer if we don't have the requisite extension.
internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8;
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
break;
case LOCAL_GL_SRGB8_ALPHA8_EXT:
break;
case LOCAL_GL_RGB16F:
case LOCAL_GL_RGBA16F: {
bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float) &&
IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float);
if (!hasExtensions)
return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
break;
}
case LOCAL_GL_RGB32F:
case LOCAL_GL_RGBA32F: {
bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_float) &&
IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float);
if (!hasExtensions)
return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
break;
}
default:
return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
break;
}
// Validation complete.
MakeContextCurrent();
bool sizeChanges = width != mBoundRenderbuffer->Width() ||
height != mBoundRenderbuffer->Height() ||
internalformat != mBoundRenderbuffer->InternalFormat();
if (sizeChanges) {
bool willRealloc = samples != mBoundRenderbuffer->Samples() ||
internalFormat != mBoundRenderbuffer->InternalFormat() ||
width != mBoundRenderbuffer->Width() ||
height != mBoundRenderbuffer->Height();
if (willRealloc) {
// Invalidate framebuffer status cache
mBoundRenderbuffer->NotifyFBsStatusChanged();
GetAndFlushUnderlyingGLErrors();
mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL,
width, height);
GLenum error = GetAndFlushUnderlyingGLErrors();
if (error) {
GenerateWarning("renderbufferStorage generated error %s", ErrorName(error));
GenerateWarning("%s generated error %s", funcName,
ErrorName(error));
return;
}
} else {
mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL,
width, height);
}
mBoundRenderbuffer->SetInternalFormat(internalformat);
mBoundRenderbuffer->SetInternalFormatForGL(internalformatForGL);
mBoundRenderbuffer->SetSamples(samples);
mBoundRenderbuffer->SetInternalFormat(internalFormat);
mBoundRenderbuffer->SetInternalFormatForGL(internalFormatForGL);
mBoundRenderbuffer->setDimensions(width, height);
mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData);
}
void
WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
{
RenderbufferStorage_base("renderbufferStorage", target, 0,
internalFormat, width, height);
}
void
WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
{

View File

@ -501,12 +501,23 @@ WebGLContext::ErrorInvalidEnum(const char* fmt, ...)
}
void
WebGLContext::ErrorInvalidEnumInfo(const char* info, GLenum enumvalue)
WebGLContext::ErrorInvalidEnumInfo(const char* info, GLenum enumValue)
{
nsCString name;
EnumName(enumvalue, &name);
EnumName(enumValue, &name);
return ErrorInvalidEnum("%s: invalid enum value %s", info, name.get());
return ErrorInvalidEnum("%s: invalid enum value %s", info, name.BeginReading());
}
void
WebGLContext::ErrorInvalidEnumInfo(const char* info, const char* funcName,
GLenum enumValue)
{
nsCString name;
EnumName(enumValue, &name);
ErrorInvalidEnum("%s: %s: Invalid enum: 0x%04x (%s).", funcName, info,
enumValue, name.BeginReading());
}
void

View File

@ -1286,25 +1286,11 @@ WebGLContext::ValidateCopyTexImage(GLenum format, WebGLTexImageFunc func,
GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
if (mBoundReadFramebuffer) {
if (!mBoundReadFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: Incomplete framebuffer.",
InfoFrom(func, dims));
TexInternalFormat srcFormat;
if (!mBoundReadFramebuffer->ValidateForRead(InfoFrom(func, dims), &srcFormat))
return false;
}
GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
if (!mBoundReadFramebuffer->HasCompletePlanes(readPlaneBits)) {
ErrorInvalidOperation("%s: Read source attachment doesn't have the"
" correct color/depth/stencil type.",
InfoFrom(func, dims));
return false;
}
// Get the correct format for the framebuffer, as it's not the default one.
const WebGLFramebuffer::Attachment& color0 =
mBoundReadFramebuffer->GetAttachment(LOCAL_GL_COLOR_ATTACHMENT0);
fboFormat = mBoundReadFramebuffer->GetFormatForAttachment(color0);
fboFormat = srcFormat.get();
}
// Make sure the format of the framebuffer is a superset of the format
@ -1818,12 +1804,16 @@ WebGLContext::InitAndValidateGL()
mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
mGLMaxSamples = 1;
} else {
gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize);
gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize);
gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize);
gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
if (!gl->GetPotentialInteger(LOCAL_GL_MAX_SAMPLES, (GLint*)&mGLMaxSamples))
mGLMaxSamples = 1;
}
// Calculate log2 of mGLMaxTextureSize and mGLMaxCubeMapTextureSize

View File

@ -28,6 +28,7 @@ WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
, mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
, mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
, mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
, mReadBufferMode(LOCAL_GL_COLOR_ATTACHMENT0)
{
mContext->mFramebuffers.insertBack(this);
@ -977,6 +978,38 @@ WebGLFramebuffer::FinalizeAttachments() const
FinalizeDrawAndReadBuffers(gl, ColorAttachment(0).IsDefined());
}
bool
WebGLFramebuffer::ValidateForRead(const char* info, TexInternalFormat* const out_format)
{
if (mReadBufferMode == LOCAL_GL_NONE) {
mContext->ErrorInvalidOperation("%s: Read buffer mode must not be"
" NONE.", info);
return false;
}
const auto& attachment = GetAttachment(mReadBufferMode);
if (!CheckAndInitializeAttachments()) {
mContext->ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
return false;
}
GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
if (!HasCompletePlanes(readPlaneBits)) {
mContext->ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
" correct color/depth/stencil type.");
return false;
}
if (!attachment.IsDefined()) {
mContext->ErrorInvalidOperation("readPixels: ");
return false;
}
*out_format = attachment.EffectiveInternalFormat();
return true;
}
inline void
ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::Attachment& field)
{

View File

@ -174,6 +174,8 @@ public:
void NotifyAttachableChanged() const;
bool ValidateForRead(const char* info, TexInternalFormat* const out_format);
private:
~WebGLFramebuffer() {
DeleteOnce();
@ -187,6 +189,7 @@ private:
Attachment mDepthAttachment;
Attachment mStencilAttachment;
Attachment mDepthStencilAttachment;
GLenum mReadBufferMode;
};
} // namespace mozilla

View File

@ -54,6 +54,7 @@ WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
, mInternalFormat(0)
, mInternalFormatForGL(0)
, mImageDataStatus(WebGLImageDataStatus::NoImageData)
, mSamples(1)
{
mContext->MakeContextCurrent();
@ -156,11 +157,30 @@ WebGLRenderbuffer::BindRenderbuffer() const
mContext->gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
}
static void
RenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples,
GLenum internalFormat, GLsizei width,
GLsizei height)
{
MOZ_ASSERT_IF(samples >= 1, gl->IsSupported(gl::GLFeature::framebuffer_multisample));
MOZ_ASSERT(samples >= 0);
MOZ_ASSERT(samples <= gl->MaxSamples());
if (samples > 0) {
gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
internalFormat, width, height);
} else {
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width,
height);
}
}
void
WebGLRenderbuffer::RenderbufferStorage(GLenum internalFormat, GLsizei width,
GLsizei height) const
WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, GLenum internalFormat,
GLsizei width, GLsizei height) const
{
gl::GLContext* gl = mContext->gl;
MOZ_ASSERT(samples >= 0 && samples <= 256); // Sanity check.
GLenum primaryFormat = internalFormat;
GLenum secondaryFormat = 0;
@ -170,7 +190,8 @@ WebGLRenderbuffer::RenderbufferStorage(GLenum internalFormat, GLsizei width,
secondaryFormat = LOCAL_GL_STENCIL_INDEX8;
}
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, primaryFormat, width, height);
RenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, width,
height);
if (!mSecondaryRB) {
MOZ_ASSERT(!secondaryFormat);
@ -182,9 +203,10 @@ WebGLRenderbuffer::RenderbufferStorage(GLenum internalFormat, GLsizei width,
// non-depth-stencil attachment point.
gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB);
if (secondaryFormat) {
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, secondaryFormat, width, height);
RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width,
height);
} else {
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_RGBA4, 1, 1);
RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1);
}
}

View File

@ -38,6 +38,9 @@ public:
mImageDataStatus = x;
}
GLsizei Samples() const { return mSamples; }
void SetSamples(GLsizei samples) { mSamples = samples; }
GLenum InternalFormat() const { return mInternalFormat; }
void SetInternalFormat(GLenum internalFormat) {
mInternalFormat = internalFormat;
@ -55,8 +58,8 @@ public:
}
void BindRenderbuffer() const;
void RenderbufferStorage(GLenum internalFormat, GLsizei width,
GLsizei height) const;
void RenderbufferStorage(GLsizei samples, GLenum internalFormat,
GLsizei width, GLsizei height) const;
void FramebufferRenderbuffer(FBAttachment attachment) const;
// Only handles a subset of `pname`s.
GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const;
@ -76,6 +79,7 @@ protected:
GLenum mInternalFormat;
GLenum mInternalFormatForGL;
WebGLImageDataStatus mImageDataStatus;
GLsizei mSamples;
friend class WebGLFramebuffer;
};

View File

@ -866,6 +866,11 @@ FetchBody<Request>::FetchBody();
template
FetchBody<Response>::FetchBody();
template <class Derived>
FetchBody<Derived>::~FetchBody()
{
}
// Returns true if addref succeeded.
// Always succeeds on main thread.
// May fail on worker if RegisterFeature() fails. In that case, it will release

View File

@ -135,6 +135,12 @@ public:
void
CancelPump();
void
SetBodyUsed()
{
mBodyUsed = true;
}
// Always set whenever the FetchBody is created on the worker thread.
workers::WorkerPrivate* mWorkerPrivate;
@ -145,15 +151,7 @@ public:
protected:
FetchBody();
virtual ~FetchBody()
{
}
void
SetBodyUsed()
{
mBodyUsed = true;
}
virtual ~FetchBody();
void
SetMimeType(ErrorResult& aRv);

View File

@ -2,7 +2,6 @@
"WeakMap.prototype.clear.length": true,
"WeakMap.prototype.delete.length": true,
"WeakMap.prototype.get.length": true,
"WeakMap.prototype.get: return undefined": true,
"WeakMap.prototype.has.length": true,
"WeakMap.prototype.set.length": true
}

View File

@ -6,6 +6,7 @@
#include "domstubs.idl"
interface nsIDocument;
interface nsIInterceptedChannel;
interface nsIPrincipal;
interface nsIURI;
@ -18,7 +19,7 @@ interface nsIServiceWorkerUnregisterCallback : nsISupports
[noscript] void UnregisterFailed();
};
[builtinclass, uuid(861b55e9-d6ac-47cf-a528-8590e9b44de6)]
[builtinclass, uuid(464882c8-81c0-4620-b9c4-44c12085b65b)]
interface nsIServiceWorkerManager : nsISupports
{
/**
@ -51,6 +52,15 @@ interface nsIServiceWorkerManager : nsISupports
// Remove ready pending Promise
void removeReadyPromise(in nsIDOMWindow aWindow);
// Returns true if a ServiceWorker is available for the scope of aURI.
bool isAvailableForURI(in nsIURI aURI);
// Returns true if a given document is currently controlled by a ServiceWorker
bool isControlled(in nsIDocument aDocument);
// Cause a fetch event to be dispatched to the worker global associated with the given document.
void dispatchFetchEvent(in nsIDocument aDoc, in nsIInterceptedChannel aChannel);
// aTarget MUST be a ServiceWorkerRegistration.
[noscript] void AddRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
[noscript] void RemoveRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);

View File

@ -13,7 +13,6 @@
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/ipc/InputStreamUtils.h"
using namespace base;
using namespace mozilla::ipc;
using namespace mozilla::jsipc;

View File

@ -9,7 +9,6 @@
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "nsXULAppAPI.h"
using namespace base;
using namespace mozilla::ipc;
using namespace mozilla::jsipc;

View File

@ -172,7 +172,6 @@
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/RemoteSpellCheckEngineChild.h"
using namespace base;
using namespace mozilla;
using namespace mozilla::docshell;
using namespace mozilla::dom::bluetooth;

View File

@ -17,8 +17,6 @@
#include "nsThreadUtils.h"
#endif
using namespace base;
namespace mozilla {
namespace dom {

View File

@ -64,7 +64,7 @@ AppleDecoderModule::Init()
bool haveVideoToolbox = AppleVTLinker::Link();
sIsVTAvailable = haveCoreMedia && haveVideoToolbox;
sIsVTHWAvailable = AppleVTLinker::skPropHWAccel != nullptr;
sIsVTHWAvailable = AppleVTLinker::skPropEnableHWAccel != nullptr;
if (sIsVDAAvailable) {
AppleVDALinker::Unlink();

View File

@ -305,6 +305,20 @@ AppleVTDecoder::InitializeSession()
return NS_ERROR_FAILURE;
}
if (AppleVTLinker::skPropUsingHWAccel) {
CFBooleanRef isUsingHW = nullptr;
rv = VTSessionCopyProperty(mSession,
AppleVTLinker::skPropUsingHWAccel,
kCFAllocatorDefault,
&isUsingHW);
if (rv != noErr) {
LOG("AppleVTDecoder: system doesn't support hardware acceleration");
}
LOG("AppleVTDecoder: %s hardware accelerated decoding",
(rv == noErr && isUsingHW == kCFBooleanTrue) ? "using" : "not using");
} else {
LOG("AppleVTDecoder: couldn't determine hardware acceleration status.");
}
return NS_OK;
}
@ -356,11 +370,11 @@ AppleVTDecoder::CreateDecoderExtensions()
CFDictionaryRef
AppleVTDecoder::CreateDecoderSpecification()
{
if (!AppleVTLinker::skPropHWAccel) {
if (!AppleVTLinker::skPropEnableHWAccel) {
return nullptr;
}
const void* specKeys[] = { AppleVTLinker::skPropHWAccel };
const void* specKeys[] = { AppleVTLinker::skPropEnableHWAccel };
const void* specValues[] = { kCFBooleanTrue };
static_assert(ArrayLength(specKeys) == ArrayLength(specValues),
"Non matching keys/values array size");

View File

@ -10,3 +10,5 @@ LINK_FUNC(VTDecompressionSessionCreate)
LINK_FUNC(VTDecompressionSessionDecodeFrame)
LINK_FUNC(VTDecompressionSessionInvalidate)
LINK_FUNC(VTDecompressionSessionWaitForAsynchronousFrames)
LINK_FUNC(VTSessionCopyProperty)
LINK_FUNC(VTSessionCopySupportedPropertyDictionary)

View File

@ -25,7 +25,8 @@ AppleVTLinker::sLinkStatus = LinkStatus_INIT;
void* AppleVTLinker::sLink = nullptr;
nsrefcnt AppleVTLinker::sRefCount = 0;
CFStringRef AppleVTLinker::skPropHWAccel = nullptr;
CFStringRef AppleVTLinker::skPropEnableHWAccel = nullptr;
CFStringRef AppleVTLinker::skPropUsingHWAccel = nullptr;
#define LINK_FUNC(func) typeof(func) func;
#include "AppleVTFunctions.h"
@ -69,8 +70,10 @@ AppleVTLinker::Link()
#undef LINK_FUNC
// Will only resolve in 10.9 and later.
skPropHWAccel =
skPropEnableHWAccel =
GetIOConst("kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder");
skPropUsingHWAccel =
GetIOConst("kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder");
LOG("Loaded VideoToolbox framework.");
sLinkStatus = LinkStatus_SUCCEEDED;
@ -97,7 +100,8 @@ AppleVTLinker::Unlink()
LOG("Unlinking VideoToolbox framework.");
dlclose(sLink);
sLink = nullptr;
skPropHWAccel = nullptr;
skPropEnableHWAccel = nullptr;
skPropUsingHWAccel = nullptr;
sLinkStatus = LinkStatus_INIT;
}
}

View File

@ -22,7 +22,8 @@ class AppleVTLinker
public:
static bool Link();
static void Unlink();
static CFStringRef skPropHWAccel;
static CFStringRef skPropEnableHWAccel;
static CFStringRef skPropUsingHWAccel;
private:
static void* sLink;

View File

@ -26,6 +26,7 @@ enum {
kVTDecodeInfo_FrameDropped = 1UL << 1,
};
typedef CFTypeRef VTSessionRef;
typedef struct OpaqueVTDecompressionSession* VTDecompressionSessionRef;
typedef void (*VTDecompressionOutputCallback)(
void*,
@ -70,4 +71,18 @@ VTDecompressionSessionInvalidate(
VTDecompressionSessionRef
);
OSStatus
VTSessionCopyProperty(
VTSessionRef,
CFStringRef,
CFAllocatorRef,
void*
);
OSStatus
VTSessionCopySupportedPropertyDictionary(
VTSessionRef,
CFDictionaryRef*
);
#endif // mozilla_VideoToolbox_VideoToolbox_h

View File

@ -0,0 +1,27 @@
/* -*- Mode: IDL; tab-width: 2; 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/.
*
* For more information on this interface, please see
* http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
*/
[Constructor(DOMString type, optional FetchEventInit eventInitDict),
Func="mozilla::dom::workers::ServiceWorkerVisible",
Exposed=(ServiceWorker)]
interface FetchEvent : Event {
readonly attribute Request request;
readonly attribute ServiceWorkerClient client; // The window issuing the request.
readonly attribute boolean isReload;
[Throws] void respondWith(Promise<Response> r);
Promise<Response> forwardTo(USVString url);
Promise<Response> default();
};
dictionary FetchEventInit : EventInit {
Request request;
ServiceWorkerClient client;
boolean isReload;
};

View File

@ -132,6 +132,7 @@ WEBIDL_FILES = [
'EventTarget.webidl',
'ExtendableEvent.webidl',
'Fetch.webidl',
'FetchEvent.webidl',
'File.webidl',
'FileList.webidl',
'FileMode.webidl',

View File

@ -5,9 +5,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ServiceWorkerEvents.h"
#include "ServiceWorkerClient.h"
#include "nsINetworkInterceptController.h"
#include "nsIOutputStream.h"
#include "nsContentUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsNetCID.h"
#include "mozilla/dom/FetchEventBinding.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
@ -15,6 +26,271 @@ using namespace mozilla::dom;
BEGIN_WORKERS_NAMESPACE
FetchEvent::FetchEvent(EventTarget* aOwner)
: Event(aOwner, nullptr, nullptr)
, mWindowId(0)
, mIsReload(false)
, mWaitToRespond(false)
{
}
FetchEvent::~FetchEvent()
{
}
void
FetchEvent::PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
uint64_t aWindowId)
{
mChannel = aChannel;
mServiceWorker = aServiceWorker;
mWindowId = aWindowId;
}
/*static*/ already_AddRefed<FetchEvent>
FetchEvent::Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const FetchEventInit& aOptions,
ErrorResult& aRv)
{
nsRefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports());
MOZ_ASSERT(owner);
nsRefPtr<FetchEvent> e = new FetchEvent(owner);
bool trusted = e->Init(owner);
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
e->SetTrusted(trusted);
e->mRequest = aOptions.mRequest.WasPassed() ?
&aOptions.mRequest.Value() : nullptr;
e->mIsReload = aOptions.mIsReload.WasPassed() ?
aOptions.mIsReload.Value() : false;
e->mClient = aOptions.mClient.WasPassed() ?
&aOptions.mClient.Value() : nullptr;
return e.forget();
}
namespace {
class CancelChannelRunnable MOZ_FINAL : public nsRunnable
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
public:
explicit CancelChannelRunnable(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = mChannel->Cancel();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
};
class FinishResponse MOZ_FINAL : public nsRunnable
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
public:
explicit FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD
Run()
{
AssertIsOnMainThread();
nsresult rv = mChannel->FinishSynthesizedResponse();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response");
return rv;
}
};
class RespondWithHandler MOZ_FINAL : public PromiseNativeHandler
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
public:
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
: mInterceptedChannel(aChannel)
, mServiceWorker(aServiceWorker)
{
}
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
void CancelRequest();
};
struct RespondWithClosure
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
explicit RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
: mInterceptedChannel(aChannel)
{
}
};
void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
{
nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
nsCOMPtr<nsIRunnable> event;
if (NS_SUCCEEDED(aStatus)) {
event = new FinishResponse(data->mInterceptedChannel);
} else {
event = new CancelChannelRunnable(data->mInterceptedChannel);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
}
class MOZ_STACK_CLASS AutoCancel
{
nsRefPtr<RespondWithHandler> mOwner;
public:
explicit AutoCancel(RespondWithHandler* aOwner)
: mOwner(aOwner)
{
}
~AutoCancel()
{
if (mOwner) {
mOwner->CancelRequest();
}
}
void Reset()
{
mOwner = nullptr;
}
};
void
RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
AutoCancel autoCancel(this);
if (!aValue.isObject()) {
return;
}
nsRefPtr<Response> response;
nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIInputStream> body;
response->GetBody(getter_AddRefs(body));
if (NS_WARN_IF(!body) || NS_WARN_IF(response->BodyUsed())) {
return;
}
response->SetBodyUsed();
nsCOMPtr<nsIOutputStream> responseBody;
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel));
nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(!stsThread)) {
return;
}
rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
RespondWithCopyComplete, closure.forget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
autoCancel.Reset();
}
void
RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
CancelRequest();
}
void
RespondWithHandler::CancelRequest()
{
nsCOMPtr<nsIRunnable> runnable = new CancelChannelRunnable(mInterceptedChannel);
NS_DispatchToMainThread(runnable);
}
} // anonymous namespace
void
FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
{
if (mWaitToRespond) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mWaitToRespond = true;
nsRefPtr<RespondWithHandler> handler = new RespondWithHandler(mChannel, mServiceWorker);
aPromise.AppendNativeHandler(handler);
}
already_AddRefed<ServiceWorkerClient>
FetchEvent::Client()
{
if (!mClient) {
mClient = new ServiceWorkerClient(GetParentObject(), mWindowId);
}
nsRefPtr<ServiceWorkerClient> client = mClient;
return client.forget();
}
already_AddRefed<Promise>
FetchEvent::ForwardTo(const nsAString& aUrl)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
MOZ_ASSERT(global);
ErrorResult result;
nsRefPtr<Promise> promise = Promise::Create(global, result);
if (NS_WARN_IF(result.Failed())) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
return promise.forget();
}
already_AddRefed<Promise>
FetchEvent::Default()
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
MOZ_ASSERT(global);
ErrorResult result;
nsRefPtr<Promise> promise = Promise::Create(global, result);
if (result.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
return promise.forget();
}
NS_IMPL_ADDREF_INHERITED(FetchEvent, Event)
NS_IMPL_RELEASE_INHERITED(FetchEvent, Event)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, Event, mRequest, mClient)
ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
: Event(aOwner, nullptr, nullptr)
{

View File

@ -8,12 +8,87 @@
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ExtendableEventBinding.h"
#include "mozilla/dom/FetchEventBinding.h"
#include "mozilla/dom/InstallEventBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsProxyRelease.h"
class nsIInterceptedChannel;
namespace mozilla {
namespace dom {
class Request;
} // namespace dom
} // namespace mozilla
BEGIN_WORKERS_NAMESPACE
class ServiceWorker;
class ServiceWorkerClient;
class FetchEvent MOZ_FINAL : public Event
{
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
nsRefPtr<ServiceWorkerClient> mClient;
nsRefPtr<Request> mRequest;
uint64_t mWindowId;
bool mIsReload;
bool mWaitToRespond;
protected:
explicit FetchEvent(EventTarget* aOwner);
~FetchEvent();
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchEvent, Event)
NS_FORWARD_TO_EVENT
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
{
return FetchEventBinding::Wrap(aCx, this);
}
void PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
uint64_t aWindowId);
static already_AddRefed<FetchEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const FetchEventInit& aOptions,
ErrorResult& aRv);
bool
WaitToRespond() const
{
return mWaitToRespond;
}
Request*
Request_() const
{
return mRequest;
}
already_AddRefed<ServiceWorkerClient>
Client();
bool
IsReload() const
{
return mIsReload;
}
void
RespondWith(Promise& aPromise, ErrorResult& aRv);
already_AddRefed<Promise>
ForwardTo(const nsAString& aUrl);
already_AddRefed<Promise>
Default();
};
class ExtendableEvent : public Event
{

View File

@ -11,7 +11,10 @@
#include "nsIStreamLoader.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsINetworkInterceptController.h"
#include "nsPIDOMWindow.h"
#include "nsDebug.h"
#include "jsapi.h"
@ -19,9 +22,13 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/InstallEventBinding.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
@ -2121,6 +2128,267 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
return NS_OK;
}
class FetchEventRunnable : public WorkerRunnable
, public nsIHttpHeaderVisitor {
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
nsTArray<nsCString> mHeaderNames;
nsTArray<nsCString> mHeaderValues;
uint64_t mWindowId;
nsCString mSpec;
nsCString mMethod;
bool mIsReload;
public:
FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
uint64_t aWindowId)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mInterceptedChannel(aChannel)
, mServiceWorker(aServiceWorker)
, mWindowId(aWindowId)
{
MOZ_ASSERT(aWorkerPrivate);
}
NS_DECL_ISUPPORTS_INHERITED
NS_IMETHOD
VisitHeader(const nsACString& aHeader, const nsACString& aValue)
{
mHeaderNames.AppendElement(aHeader);
mHeaderValues.AppendElement(aValue);
return NS_OK;
}
nsresult
Init()
{
nsCOMPtr<nsIChannel> channel;
nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> uri;
rv = channel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
rv = uri->GetSpec(mSpec);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
NS_ENSURE_TRUE(httpChannel, NS_ERROR_NOT_AVAILABLE);
rv = httpChannel->GetRequestMethod(mMethod);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t loadFlags;
rv = channel->GetLoadFlags(&loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
//TODO(jdm): we should probably include reload-ness in the loadinfo or as a separate load flag
mIsReload = false;
rv = httpChannel->VisitRequestHeaders(this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
return DispatchFetchEvent(aCx, aWorkerPrivate);
}
private:
~FetchEventRunnable() {}
class ResumeRequest MOZ_FINAL : public nsRunnable {
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
public:
explicit ResumeRequest(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD Run()
{
AssertIsOnMainThread();
nsresult rv = mChannel->ResetInterception();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to resume intercepted network request");
return rv;
}
};
bool
DispatchFetchEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
RequestOrUSVString requestInfo;
*requestInfo.SetAsUSVString().ToAStringPtr() = NS_ConvertUTF8toUTF16(mSpec);
RootedDictionary<RequestInit> reqInit(aCx);
reqInit.mMethod.Construct(mMethod);
nsRefPtr<InternalHeaders> internalHeaders = new InternalHeaders(HeadersGuardEnum::Request);
MOZ_ASSERT(mHeaderNames.Length() == mHeaderValues.Length());
for (uint32_t i = 0; i < mHeaderNames.Length(); i++) {
ErrorResult rv;
internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
}
nsRefPtr<Headers> headers = new Headers(globalObj.GetAsSupports(), internalHeaders);
reqInit.mHeaders.Construct();
reqInit.mHeaders.Value().SetAsHeaders() = headers;
//TODO(jdm): set request body
//TODO(jdm): set request same-origin mode and credentials
ErrorResult rv;
nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
RootedDictionary<FetchEventInit> init(aCx);
init.mRequest.Construct();
init.mRequest.Value() = request;
init.mBubbles = false;
init.mCancelable = true;
init.mIsReload.Construct(mIsReload);
nsRefPtr<FetchEvent> event =
FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
event->PostInit(mInterceptedChannel, mServiceWorker, mWindowId);
event->SetTrusted(true);
nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
}
return true;
}
};
NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
NS_IMETHODIMP
ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel)
{
MOZ_ASSERT(aChannel);
nsCOMPtr<nsISupports> serviceWorker;
bool isNavigation = false;
nsresult rv = aChannel->GetIsNavigation(&isNavigation);
NS_ENSURE_SUCCESS(rv, rv);
if (!isNavigation) {
MOZ_ASSERT(aDoc);
rv = GetDocumentController(aDoc->GetWindow(), getter_AddRefs(serviceWorker));
} else {
nsCOMPtr<nsIChannel> internalChannel;
rv = aChannel->GetChannel(getter_AddRefs(internalChannel));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> uri;
rv = internalChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(uri);
// This should only happen if IsAvailableForURI() returned true.
MOZ_ASSERT(registration);
MOZ_ASSERT(registration->mActiveWorker);
nsRefPtr<ServiceWorker> sw;
rv = CreateServiceWorker(registration->mPrincipal,
registration->mActiveWorker->ScriptSpec(),
registration->mScope,
getter_AddRefs(sw));
serviceWorker = sw.forget();
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
uint64_t windowId = aDoc ? aDoc->GetInnerWindow()->WindowID() : 0;
nsRefPtr<ServiceWorker> sw = static_cast<ServiceWorker*>(serviceWorker.get());
nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
new nsMainThreadPtrHolder<ServiceWorker>(sw));
nsRefPtr<FetchEventRunnable> event =
new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, windowId);
rv = event->Init();
NS_ENSURE_SUCCESS(rv, rv);
AutoJSAPI api;
api.Init();
if (NS_WARN_IF(!event->Dispatch(api.cx()))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerManager::IsAvailableForURI(nsIURI* aURI, bool* aIsAvailable)
{
MOZ_ASSERT(aURI);
MOZ_ASSERT(aIsAvailable);
nsRefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(aURI);
*aIsAvailable = registration && registration->mActiveWorker;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerManager::IsControlled(nsIDocument* aDoc, bool* aIsControlled)
{
MOZ_ASSERT(aDoc);
MOZ_ASSERT(aIsControlled);
nsRefPtr<ServiceWorkerRegistrationInfo> registration;
nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
NS_ENSURE_SUCCESS(rv, rv);
*aIsControlled = !!registration;
return NS_OK;
}
nsresult
ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
ServiceWorkerRegistrationInfo** aRegistrationInfo)
{
nsRefPtr<ServiceWorkerRegistrationInfo> registration;
if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
return NS_ERROR_FAILURE;
}
// If the document is controlled, the current worker MUST be non-null.
if (!registration->mActiveWorker) {
return NS_ERROR_NOT_AVAILABLE;
}
registration.forget(aRegistrationInfo);
return NS_OK;
}
/*
* The .controller is for the registration associated with the document when
* the document was loaded.
@ -2137,21 +2405,16 @@ ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports**
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
nsRefPtr<ServiceWorkerRegistrationInfo> registration;
if (!mControlledDocuments.Get(doc, getter_AddRefs(registration))) {
return NS_ERROR_FAILURE;
nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If the document is controlled, the current worker MUST be non-null.
if (!registration->mActiveWorker) {
return NS_ERROR_NOT_AVAILABLE;
}
nsRefPtr<ServiceWorker> serviceWorker;
nsresult rv = CreateServiceWorkerForWindow(window,
registration->mActiveWorker->ScriptSpec(),
registration->mScope,
getter_AddRefs(serviceWorker));
rv = CreateServiceWorkerForWindow(window,
registration->mActiveWorker->ScriptSpec(),
registration->mScope,
getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -399,6 +399,9 @@ private:
nsresult
Update(ServiceWorkerRegistrationInfo* aRegistration);
nsresult
GetDocumentRegistration(nsIDocument* aDoc, ServiceWorkerRegistrationInfo** aRegistrationInfo);
NS_IMETHOD
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
const nsACString& aScriptSpec,

View File

@ -0,0 +1,54 @@
function fetch(name, onload, onerror, headers) {
expectAsyncResult();
onload = onload || function() {
my_ok(false, "XHR load should not complete successfully");
finish();
};
onerror = onerror || function() {
my_ok(false, "XHR load should be intercepted successfully");
finish();
};
var x = new XMLHttpRequest();
x.open('GET', name, true);
x.onload = function() { onload(x) };
x.onerror = function() { onerror(x) };
headers = headers || [];
headers.forEach(function(header) {
x.setRequestHeader(header[0], header[1]);
});
x.send();
}
fetch('synthesized.txt', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response");
finish();
});
fetch('ignored.txt', function(xhr) {
my_ok(xhr.status == 404, "load should be uninterrupted");
finish();
});
fetch('rejected.txt', null, function(xhr) {
my_ok(xhr.status == 0, "load should not complete");
finish();
});
fetch('nonresponse.txt', null, function(xhr) {
my_ok(xhr.status == 0, "load should not complete");
finish();
});
fetch('nonresponse2.txt', null, function(xhr) {
my_ok(xhr.status == 0, "load should not complete");
finish();
});
fetch('headers.txt', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "1", "request header checks should have passed");
finish();
}, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]);

View File

@ -0,0 +1,29 @@
function my_ok(v, msg) {
postMessage({type: "ok", value: v, msg: msg});
}
function finish() {
postMessage('finish');
}
function expectAsyncResult() {
postMessage('expect');
}
expectAsyncResult();
try {
var success = false;
importScripts("nonexistent_imported_script.js");
} catch(x) {
}
my_ok(success, "worker imported script should be intercepted");
finish();
function check_intercepted_script() {
success = true;
}
importScripts('fetch_tests.js')
finish(); //corresponds to the gExpected increment before creating this worker

View File

@ -0,0 +1,147 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 94048 - test install event.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<div id="style-test" style="background-color: white"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
function my_ok(result, msg) {
window.opener.postMessage({status: "ok", result: result, message: msg}, "*");
}
function check_intercepted_script() {
document.getElementById('intercepted-script').test_result =
document.currentScript == document.getElementById('intercepted-script');
}
function fetch(name, onload, onerror, headers) {
gExpected++;
onload = onload || function() {
my_ok(false, "load should not complete successfully");
finish();
};
onerror = onerror || function() {
my_ok(false, "load should be intercepted successfully");
finish();
};
var x = new XMLHttpRequest();
x.open('GET', name, true);
x.onload = function() { onload(x) };
x.onerror = function() { onerror(x) };
headers = headers || [];
headers.forEach(function(header) {
x.setRequestHeader(header[0], header[1]);
});
x.send();
}
var gExpected = 0;
var gEncountered = 0;
function finish() {
gEncountered++;
if (gEncountered == gExpected) {
window.opener.postMessage({status: "done"}, "*");
}
}
function test_onload(creator, complete) {
gExpected++;
var elem = creator();
elem.onload = function() {
complete.call(elem);
finish();
};
elem.onerror = function() {
my_ok(false, elem.tagName + " load should complete successfully");
finish();
};
document.body.appendChild(elem);
}
function expectAsyncResult() {
gExpected++;
}
my_ok(navigator.serviceWorker.controller != null, "should be controlled");
</script>
<script src="fetch_tests.js"></script>
<script>
test_onload(function() {
var elem = document.createElement('img');
elem.src = "nonexistent_image.gifs";
elem.id = 'intercepted-img';
return elem;
}, function() {
my_ok(this.complete, "image should be complete");
my_ok(this.naturalWidth == 1 && this.naturalHeight == 1, "image should be 1x1 gif");
});
test_onload(function() {
var elem = document.createElement('script');
elem.id = 'intercepted-script';
elem.src = "nonexistent_script.js";
return elem;
}, function() {
my_ok(this.test_result, "script load should be intercepted");
});
test_onload(function() {
var elem = document.createElement('link');
elem.href = "nonexistent_stylesheet.css";
elem.rel = "stylesheet";
return elem;
}, function() {
var styled = document.getElementById('style-test');
my_ok(window.getComputedStyle(styled).backgroundColor == 'rgb(0, 0, 0)',
"stylesheet load should be intercepted");
});
test_onload(function() {
var elem = document.createElement('iframe');
elem.id = 'intercepted-iframe';
elem.src = "nonexistent_page.html";
return elem;
}, function() {
my_ok(this.test_result, "iframe load should be intercepted");
});
gExpected++;
var worker = new Worker('nonexistent_worker_script.js');
worker.onmessage = function(e) {
my_ok(e.data == "worker-intercept-success", "worker load intercepted");
finish();
};
worker.onerror = function() {
my_ok(false, "worker load should be intercepted");
};
gExpected++;
var worker = new Worker('fetch_worker_script.js');
worker.onmessage = function(e) {
if (e.data == "finish") {
finish();
} else if (e.data == "expect") {
gExpected++;
} else if (e.data.type == "ok") {
my_ok(e.data.value, e.data.msg);
}
};
worker.onerror = function() {
my_ok(false, "worker should not cause any errors");
};
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,86 @@
onfetch = function(ev) {
if (ev.request.url.contains("synthesized.txt")) {
var p = new Promise(function(resolve) {
var r = new Response("synthesized response body", {});
resolve(r);
});
ev.respondWith(p);
}
else if (ev.request.url.contains("ignored.txt")) {
}
else if (ev.request.url.contains("rejected.txt")) {
var p = new Promise(function(resolve, reject) {
reject();
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonresponse.txt")) {
var p = new Promise(function(resolve, reject) {
resolve(5);
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonresponse2.txt")) {
var p = new Promise(function(resolve, reject) {
resolve({});
});
ev.respondWith(p);
}
else if (ev.request.url.contains("headers.txt")) {
var p = new Promise(function(resolve, reject) {
var ok = true;
ok &= ev.request.headers.get("X-Test1") == "header1";
ok &= ev.request.headers.get("X-Test2") == "header2";
var r = new Response(ok.toString(), {});
resolve(r);
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_image.gif")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response(atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"), {}));
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_script.js")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response("check_intercepted_script();", {}));
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_stylesheet.css")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response("#style-test { background-color: black !important; }", {}));
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_page.html")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response("<script>window.frameElement.test_result = true;</script>", {}));
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_worker_script.js")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response("postMessage('worker-intercept-success')", {}));
});
ev.respondWith(p);
}
else if (ev.request.url.contains("nonexistent_imported_script.js")) {
var p = new Promise(function(resolve, reject) {
resolve(new Response("check_intercepted_script();", {}));
});
ev.respondWith(p);
}
}

View File

@ -4,6 +4,7 @@ support-files =
worker.js
worker2.js
worker3.js
fetch_event_worker.js
parse_error_worker.js
activate_event_error_worker.js
install_event_worker.js
@ -20,10 +21,14 @@ support-files =
worker_unregister.js
worker_update.js
message_posting_worker.js
fetch/index.html
fetch/fetch_worker_script.js
fetch/fetch_tests.js
[test_unregister.html]
skip-if = true # Bug 1133805
[test_installation_simple.html]
[test_fetch_event.html]
[test_get_serviced.html]
[test_install_event.html]
[test_navigator.html]

View File

@ -0,0 +1,62 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 94048 - test install event.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.requestCompleteLog();
function simpleRegister() {
var p = navigator.serviceWorker.register("fetch_event_worker.js", { scope: "./fetch" });
return p;
}
function testController() {
var p = new Promise(function(resolve, reject) {
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "done") {
window.onmessage = null;
w.close();
resolve();
}
}
});
var w = window.open("fetch/index.html");
return p;
}
function runTest() {
simpleRegister()
.then(testController)
.then(function() {
SimpleTest.finish();
}).catch(function(e) {
ok(false, "Some test failed with error " + e);
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.fetch.enabled", true]
]}, runTest);
</script>
</pre>
</body>
</html>

View File

@ -194,7 +194,10 @@ public:
: mColor(aColor)
{}
virtual PatternType GetType() const { return PatternType::COLOR; }
virtual PatternType GetType() const MOZ_OVERRIDE
{
return PatternType::COLOR;
}
Color mColor;
};
@ -219,7 +222,10 @@ public:
{
}
virtual PatternType GetType() const { return PatternType::LINEAR_GRADIENT; }
virtual PatternType GetType() const MOZ_OVERRIDE
{
return PatternType::LINEAR_GRADIENT;
}
Point mBegin; //!< Start of the linear gradient
Point mEnd; /**< End of the linear gradient - NOTE: In the case
@ -256,7 +262,10 @@ public:
{
}
virtual PatternType GetType() const { return PatternType::RADIAL_GRADIENT; }
virtual PatternType GetType() const MOZ_OVERRIDE
{
return PatternType::RADIAL_GRADIENT;
}
Point mCenter1; //!< Center of the inner (focal) circle.
Point mCenter2; //!< Center of the outer circle.
@ -286,7 +295,10 @@ public:
, mSamplingRect(aSamplingRect)
{}
virtual PatternType GetType() const { return PatternType::SURFACE; }
virtual PatternType GetType() const MOZ_OVERRIDE
{
return PatternType::SURFACE;
}
RefPtr<SourceSurface> mSurface; //!< Surface to use for drawing
ExtendMode mExtendMode; /**< This determines how the image is extended
@ -383,7 +395,7 @@ public:
READ_WRITE
};
virtual SurfaceType GetType() const { return SurfaceType::DATA; }
virtual SurfaceType GetType() const MOZ_OVERRIDE { return SurfaceType::DATA; }
/** @deprecated
* Get the raw bitmap data of the surface.
* Can return null if there was OOM allocating surface data.
@ -415,7 +427,7 @@ public:
* Returns a DataSourceSurface with the same data as this one, but
* guaranteed to have surface->GetType() == SurfaceType::DATA.
*/
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
virtual TemporaryRef<DataSourceSurface> GetDataSurface() MOZ_OVERRIDE;
protected:
bool mIsMapped;

View File

@ -57,7 +57,7 @@ class GenericRefCounted : public GenericRefCountedBase
}
public:
virtual void AddRef() {
virtual void AddRef() MOZ_OVERRIDE {
// Note: this method must be thread safe for GenericAtomicRefCounted.
MOZ_ASSERT(int32_t(refCnt) >= 0);
#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
@ -71,7 +71,7 @@ class GenericRefCounted : public GenericRefCountedBase
#endif
}
virtual void Release() {
virtual void Release() MOZ_OVERRIDE {
// Note: this method must be thread safe for GenericAtomicRefCounted.
MOZ_ASSERT(int32_t(refCnt) > 0);
#ifndef MOZ_REFCOUNTED_LEAK_CHECKING

View File

@ -1412,6 +1412,20 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
}
}
if (IsSupported(GLFeature::read_buffer)) {
SymLoadStruct extSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fReadBuffer, { "ReadBuffer", nullptr } },
END_SYMBOLS
};
if (!LoadSymbols(&extSymbols[0], trygl, prefix)) {
NS_ERROR("GL supports read_buffer without supplying its functions.");
MarkUnsupported(GLFeature::read_buffer);
ClearSymbols(extSymbols);
}
}
// Load developer symbols, don't fail if we can't find them.
SymLoadStruct auxSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fGetTexImage, { "GetTexImage", nullptr } },

View File

@ -115,6 +115,7 @@ enum class GLFeature {
occlusion_query2,
packed_depth_stencil,
query_objects,
read_buffer,
renderbuffer_color_float,
renderbuffer_color_half_float,
robustness,

View File

@ -408,6 +408,15 @@ static const FeatureInfo sFeatureInfoArr[] = {
* (added in OpenGL ES 3.0)
*/
},
{
"read_buffer",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
},
{
"renderbuffer_color_float",
GLVersion::GL3,

View File

@ -421,6 +421,12 @@ GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& size)
// Check that we're all set up.
MOZ_ASSERT(SharedSurf() == surf);
// Update the ReadBuffer mode.
if (mGL->IsSupported(gl::GLFeature::read_buffer)) {
BindFB(0);
mRead->SetReadBuffer(mUserReadBufferMode);
}
return true;
}
@ -517,6 +523,16 @@ GLScreenBuffer::CreateRead(SharedSurface* surf)
return ReadBuffer::Create(gl, caps, formats, surf);
}
void
GLScreenBuffer::SetReadBuffer(GLenum mode)
{
MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::read_buffer));
MOZ_ASSERT(GetReadFB() == 0);
mUserReadBufferMode = mode;
mRead->SetReadBuffer(mUserReadBufferMode);
}
bool
GLScreenBuffer::IsDrawFramebufferDefault() const
{
@ -636,7 +652,6 @@ ReadBuffer::Create(GLContext* gl,
if (surf->mAttachType == AttachmentType::Screen) {
// Don't need anything. Our read buffer will be the 'screen'.
return UniquePtr<ReadBuffer>( new ReadBuffer(gl, 0, 0, 0,
surf) );
}
@ -740,5 +755,31 @@ ReadBuffer::Size() const
return mSurf->mSize;
}
void
ReadBuffer::SetReadBuffer(GLenum userMode) const
{
if (!mGL->IsSupported(GLFeature::read_buffer))
return;
GLenum internalMode;
switch (userMode) {
case LOCAL_GL_BACK:
internalMode = (mFB == 0) ? LOCAL_GL_BACK
: LOCAL_GL_COLOR_ATTACHMENT0;
break;
case LOCAL_GL_NONE:
internalMode = LOCAL_GL_NONE;
break;
default:
MOZ_CRASH("Bad value.");
}
mGL->MakeCurrent();
mGL->fReadBuffer(internalMode);
}
} /* namespace gl */
} /* namespace mozilla */

View File

@ -116,6 +116,8 @@ public:
SharedSurface* SharedSurf() const {
return mSurf;
}
void SetReadBuffer(GLenum mode) const;
};
@ -142,6 +144,8 @@ protected:
bool mNeedsBlit;
GLenum mUserReadBufferMode;
// Below are the parts that help us pretend to be framebuffer 0:
GLuint mUserDrawFB;
GLuint mUserReadFB;
@ -160,6 +164,7 @@ protected:
, mCaps(caps)
, mFactory(Move(factory))
, mNeedsBlit(true)
, mUserReadBufferMode(LOCAL_GL_BACK)
, mUserDrawFB(0)
, mUserReadFB(0)
, mInternalDrawFB(0)
@ -223,6 +228,8 @@ public:
void AfterDrawCall();
void BeforeReadCall();
void SetReadBuffer(GLenum userMode);
/**
* Attempts to read pixels from the current bound framebuffer, if
* it is backed by a SharedSurface.

View File

@ -236,6 +236,8 @@ public:
*/
virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
virtual const nsIntRegion& GetValidRegion() const = 0;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
/**
* Store a fence that will signal when the current buffer is no longer being read.

View File

@ -233,7 +233,7 @@ LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaque
!aLayer->GetMaskLayer() &&
aLayer->GetLocalOpacity() == 1.0f) {
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion());
}
localOpaque.MoveBy(transform2d._31, transform2d._32);
const nsIntRect* clip = aLayer->GetEffectiveClipRect();
@ -304,11 +304,8 @@ LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback,
// so we don't need to pass any global transform here.
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
// Disable culling for now. We need to fix /the regressions from
// bug 1085223 before we can re-enable this:
// 1) Don't cull during progressive draw, 2) OS X tp5 regressions.
//nsIntRegion opaque;
//ApplyOcclusionCulling(mRoot, opaque);
nsIntRegion opaque;
ApplyOcclusionCulling(mRoot, opaque);
Render();
mGeometryChanged = false;
@ -1154,6 +1151,20 @@ LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
mCompositor = aManager->GetCompositor();
}
nsIntRegion
LayerComposite::GetFullyRenderedRegion() {
if (TiledLayerComposer* tiled = GetTiledLayerComposer()) {
nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion();
// Discard the region which hasn't been drawn yet when doing
// progressive drawing. Note that if the shadow visible region
// shrunk the tiled valig region may not have discarded this yet.
shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
return shadowVisibleRegion;
} else {
return GetShadowVisibleRegion();
}
}
#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
/*static*/ bool

View File

@ -441,6 +441,13 @@ public:
bool HasLayerBeenComposited() { return mLayerComposited; }
nsIntRect GetClearRect() { return mClearRect; }
/**
* Return the part of the visible region that has been fully rendered.
* While progressive drawing is in progress this region will be
* a subset of the shadow visible region.
*/
nsIntRegion GetFullyRenderedRegion();
protected:
gfx::Matrix4x4 mShadowTransform;
nsIntRegion mShadowVisibleRegion;

View File

@ -231,6 +231,11 @@ public:
return mLowPrecisionTiledBuffer.GetValidRegion();
}
const nsIntRegion& GetValidRegion() const MOZ_OVERRIDE
{
return mTiledBuffer.GetValidRegion();
}
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE
{
CompositableHost::SetCompositor(aCompositor);

View File

@ -74,11 +74,13 @@
namespace mozilla {
namespace layers {
using namespace base;
using namespace mozilla::ipc;
using namespace mozilla::gfx;
using namespace std;
using base::ProcessHandle;
using base::Thread;
CompositorParent::LayerTreeState::LayerTreeState()
: mParent(nullptr)
, mLayerManager(nullptr)

View File

@ -12,9 +12,10 @@
#include "FenceUtilsGonk.h"
using namespace android;
using namespace base;
using namespace mozilla::layers;
using base::FileDescriptor;
namespace IPC {
void

View File

@ -33,10 +33,11 @@
#include "MainThreadUtils.h"
using namespace android;
using namespace base;
using namespace mozilla::layers;
using namespace mozilla::gl;
using base::FileDescriptor;
namespace IPC {
void

View File

@ -5,6 +5,8 @@
#ifndef BASE_FILE_DESCRIPTOR_SHUFFLE_H_
#define BASE_FILE_DESCRIPTOR_SHUFFLE_H_
#include "mozilla/Attributes.h"
// This code exists to perform the shuffling of file descriptors which is
// commonly needed when forking subprocesses. The naive approve is very simple,
// just call dup2 to setup the desired descriptors, but wrong. It's tough to
@ -42,9 +44,9 @@ class InjectionDelegate {
// An implementation of the InjectionDelegate interface using the file
// descriptor table of the current process as the domain.
class FileDescriptorTableInjection : public InjectionDelegate {
bool Duplicate(int* result, int fd);
bool Move(int src, int dest);
void Close(int fd);
virtual bool Duplicate(int* result, int fd) MOZ_OVERRIDE;
virtual bool Move(int src, int dest) MOZ_OVERRIDE;
virtual void Close(int fd) MOZ_OVERRIDE;
};
// A single arc of the directed graph which describes an injective multimapping.

View File

@ -176,7 +176,7 @@ public:
// arbitrary MessageLoop to Quit.
class QuitTask : public Task {
public:
virtual void Run() {
virtual void Run() MOZ_OVERRIDE {
MessageLoop::current()->Quit();
}
};
@ -396,9 +396,9 @@ public:
int delay_ms, bool nestable);
// base::MessagePump::Delegate methods:
virtual bool DoWork();
virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time);
virtual bool DoIdleWork();
virtual bool DoWork() MOZ_OVERRIDE;
virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) MOZ_OVERRIDE;
virtual bool DoIdleWork() MOZ_OVERRIDE;
Type type_;
int32_t id_;

View File

@ -133,10 +133,10 @@ class MessagePumpLibevent : public MessagePump {
// MessagePump methods:
virtual void Run(Delegate* delegate);
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
virtual void Run(Delegate* delegate) MOZ_OVERRIDE;
virtual void Quit() MOZ_OVERRIDE;
virtual void ScheduleWork() MOZ_OVERRIDE;
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) MOZ_OVERRIDE;
protected:
@ -204,9 +204,9 @@ protected:
*/
virtual void OnError() {}
virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage) = 0;
virtual void OnFileCanWriteWithoutBlocking(int /* aFd */) {}
virtual void OnFileCanWriteWithoutBlocking(int /* aFd */) MOZ_OVERRIDE {}
private:
virtual void OnFileCanReadWithoutBlocking(int aFd) MOZ_FINAL;
virtual void OnFileCanReadWithoutBlocking(int aFd) MOZ_FINAL MOZ_OVERRIDE;
nsAutoPtr<char> mReceiveBuffer;
int mReceivedIndex;

View File

@ -98,7 +98,7 @@ class Channel : public Message::Sender {
//
// If you Send() a message on a Close()'d channel, we delete the message
// immediately.
virtual bool Send(Message* message);
virtual bool Send(Message* message) MOZ_OVERRIDE;
// Unsound_IsClosed() and Unsound_NumQueuedMessages() are safe to call from
// any thread, but the value returned may be out of date, because we don't

View File

@ -10,7 +10,8 @@
#include "nsDebug.h"
#include "nsISupportsImpl.h"
using namespace base;
using base::GetCurrentProcessHandle;
using base::ProcessHandle;
namespace mozilla {

View File

@ -451,7 +451,7 @@ class MessageChannel : HasResultCodes
explicit DequeueTask(RefCountedTask* aTask)
: mTask(aTask)
{ }
void Run() { mTask->Run(); }
void Run() MOZ_OVERRIDE { mTask->Run(); }
private:
nsRefPtr<RefCountedTask> mTask;

View File

@ -92,9 +92,17 @@ void SetThisProcessName(const char *aName)
* NOTE: The server process serves at most one load() request.
*/
using namespace base;
using namespace mozilla::dom;
using base::ChildPrivileges;
using base::InjectionArc;
using base::InjectiveMultimap;
using base::ProcessHandle;
using base::ProcessId;
using base::SetCurrentProcessPrivileges;
using base::ShuffleFileDescriptors;
using base::file_handle_mapping_vector;
static bool sProcLoaderClientOnDeinit = false;
static DebugOnly<bool> sProcLoaderClientInitialized = false;
static DebugOnly<bool> sProcLoaderClientGeckoInitialized = false;

View File

@ -11,9 +11,13 @@
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/ipc/Transport.h"
using namespace base;
using namespace IPC;
using base::GetCurrentProcessHandle;
using base::GetProcId;
using base::ProcessHandle;
using base::ProcessId;
namespace mozilla {
namespace ipc {

View File

@ -14,9 +14,10 @@
#include "mozilla/ipc/Transport.h"
#include "mozilla/ipc/FileDescriptor.h"
using namespace base;
using namespace std;
using base::ProcessHandle;
namespace mozilla {
namespace ipc {

View File

@ -10,9 +10,10 @@
#include "mozilla/ipc/Transport.h"
using namespace base;
using namespace std;
using base::ProcessHandle;
namespace mozilla {
namespace ipc {

View File

@ -20,7 +20,6 @@
${INCLUDES}
//-----------------------------------------------------------------------------
using namespace base;
using namespace std;
namespace mozilla {

View File

@ -18,7 +18,6 @@ struct RunnableMethodTraits<mozilla::_ipdltest2::TestOpensOpenedChild>
static void ReleaseCallee(mozilla::_ipdltest2::TestOpensOpenedChild* obj) { }
};
using namespace base;
using namespace mozilla::ipc;
namespace mozilla {

View File

@ -398,15 +398,15 @@ IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn)
{
return pn->isKind(PNK_COLON) &&
pn->getOp() == JSOP_INITPROP &&
BinaryLeft(pn)->isKind(PNK_NAME) &&
BinaryLeft(pn)->name() != cx->names().proto;
BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME);
}
static inline PropertyName *
ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn)
{
MOZ_ASSERT(IsNormalObjectField(cx, pn));
return BinaryLeft(pn)->name();
MOZ_ASSERT(BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME));
return BinaryLeft(pn)->pn_atom->asPropertyName();
}
static inline ParseNode *

View File

@ -1701,8 +1701,6 @@ BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
MOZ_ASSERT(pn->isKind(PNK_NAME));
MOZ_ASSERT_IF(pn->isKind(PNK_FUNCTION), pn->isBound());
/* Don't attempt if 'pn' is already bound or deoptimized or a function. */
if (pn->isBound() || pn->isDeoptimized())
return true;
@ -3692,7 +3690,7 @@ EmitDestructuringOpsObjectHelper(ExclusiveContext *cx, BytecodeEmitter *bce, Par
if (key->isKind(PNK_NUMBER)) {
if (!EmitNumberOp(cx, key->pn_dval, bce)) // ... OBJ OBJ KEY
return false;
} else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
PropertyName *name = key->pn_atom->asPropertyName();
// The parser already checked for atoms representing indexes and
@ -4325,7 +4323,7 @@ ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObje
if (pnid->isKind(PNK_NUMBER)) {
idvalue = NumberValue(pnid->pn_dval);
} else {
MOZ_ASSERT(pnid->isKind(PNK_NAME) || pnid->isKind(PNK_STRING));
MOZ_ASSERT(pnid->isKind(PNK_OBJECT_PROPERTY_NAME) || pnid->isKind(PNK_STRING));
MOZ_ASSERT(pnid->pn_atom != cx->names().proto);
idvalue = StringValue(pnid->pn_atom);
}
@ -6598,7 +6596,7 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitNumberOp(cx, key->pn_dval, bce))
return false;
isIndex = true;
} else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
// The parser already checked for atoms representing indexes and
// used PNK_NUMBER instead, but also watch for ids which TI treats
// as indexes for simpliciation of downstream analysis.
@ -6638,7 +6636,7 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (Emit1(cx, bce, op) < 0)
return false;
} else {
MOZ_ASSERT(key->isKind(PNK_NAME) || key->isKind(PNK_STRING));
MOZ_ASSERT(key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING));
jsatomid index;
if (!bce->makeAtomIndex(key->pn_atom, &index))

View File

@ -322,6 +322,7 @@ ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result)
// Grammar sub-components that should never be reached directly by this
// method, because some parent component should have asserted itself.
case PNK_OBJECT_PROPERTY_NAME:
case PNK_COMPUTED_NAME:
case PNK_SPREAD:
case PNK_MUTATEPROTO:
@ -814,6 +815,7 @@ Fold(ExclusiveContext *cx, ParseNode **pnp,
* NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
* kid or an empty statement for a child.
*/
handler.prepareNodeForMutation(pn);
pn->setKind(PNK_STATEMENTLIST);
pn->setArity(PN_LIST);
pn->makeEmpty();

View File

@ -104,8 +104,8 @@ class FullParseHandler
return dn;
}
ParseNode *newIdentifier(JSAtom *atom, const TokenPos &pos) {
return new_<NullaryNode>(PNK_NAME, JSOP_NOP, pos, atom);
ParseNode *newObjectLiteralPropertyName(JSAtom *atom, const TokenPos &pos) {
return new_<NullaryNode>(PNK_OBJECT_PROPERTY_NAME, JSOP_NOP, pos, atom);
}
ParseNode *newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) {
@ -137,8 +137,7 @@ class FullParseHandler
if (!propExpr)
return null();
if (!addArrayElement(callSite, propExpr))
return null();
addArrayElement(callSite, propExpr);
return callSite;
}
@ -146,10 +145,8 @@ class FullParseHandler
bool addToCallSiteObject(ParseNode *callSiteObj, ParseNode *rawNode, ParseNode *cookedNode) {
MOZ_ASSERT(callSiteObj->isKind(PNK_CALLSITEOBJ));
if (!addArrayElement(callSiteObj, cookedNode))
return false;
if (!addArrayElement(callSiteObj->pn_head, rawNode))
return false;
addArrayElement(callSiteObj, cookedNode);
addArrayElement(callSiteObj->pn_head, rawNode);
/*
* We don't know when the last noSubstTemplate will come in, and we
@ -270,11 +267,10 @@ class FullParseHandler
return true;
}
bool addArrayElement(ParseNode *literal, ParseNode *element) {
void addArrayElement(ParseNode *literal, ParseNode *element) {
if (!element->isConstant())
literal->pn_xflags |= PNX_NONCONST;
literal->append(element);
return true;
}
ParseNode *newObjectLiteral(uint32_t begin) {
@ -297,25 +293,46 @@ class FullParseHandler
return true;
}
bool addPropertyDefinition(ParseNode *literal, ParseNode *name, ParseNode *expr,
bool isShorthand = false) {
bool addPropertyDefinition(ParseNode *literal, ParseNode *key, ParseNode *val) {
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
MOZ_ASSERT(literal->isArity(PN_LIST));
ParseNode *propdef = newBinary(isShorthand ? PNK_SHORTHAND : PNK_COLON, name, expr,
JSOP_INITPROP);
if (isShorthand)
literal->pn_xflags |= PNX_NONCONST;
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
key->isKind(PNK_STRING) ||
key->isKind(PNK_COMPUTED_NAME));
ParseNode *propdef = newBinary(PNK_COLON, key, val, JSOP_INITPROP);
if (!propdef)
return false;
literal->append(propdef);
return true;
}
bool addMethodDefinition(ParseNode *literal, ParseNode *name, ParseNode *fn, JSOp op)
bool addShorthand(ParseNode *literal, ParseNode *name, ParseNode *expr) {
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
MOZ_ASSERT(literal->isArity(PN_LIST));
MOZ_ASSERT(name->isKind(PNK_OBJECT_PROPERTY_NAME));
MOZ_ASSERT(expr->isKind(PNK_NAME));
MOZ_ASSERT(name->pn_atom == expr->pn_atom);
setListFlag(literal, PNX_NONCONST);
ParseNode *propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
if (!propdef)
return false;
literal->append(propdef);
return true;
}
bool addMethodDefinition(ParseNode *literal, ParseNode *key, ParseNode *fn, JSOp op)
{
MOZ_ASSERT(literal->isArity(PN_LIST));
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
key->isKind(PNK_STRING) ||
key->isKind(PNK_COMPUTED_NAME));
literal->pn_xflags |= PNX_NONCONST;
ParseNode *propdef = newBinary(PNK_COLON, name, fn, op);
ParseNode *propdef = newBinary(PNK_COLON, key, fn, op);
if (!propdef)
return false;
literal->append(propdef);

View File

@ -224,12 +224,14 @@ class NameResolver
if (node->isKind(PNK_COLON) || node->isKind(PNK_SHORTHAND)) {
ParseNode *left = node->pn_left;
if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) {
if (left->isKind(PNK_OBJECT_PROPERTY_NAME) || left->isKind(PNK_STRING)) {
if (!appendPropertyReference(left->pn_atom))
return false;
} else if (left->isKind(PNK_NUMBER)) {
if (!appendNumericPropertyReference(left->pn_dval))
return false;
} else {
MOZ_ASSERT(left->isKind(PNK_COMPUTED_NAME));
}
} else {
/*

View File

@ -79,7 +79,6 @@ class NodeStack {
pn->pn_next = top;
top = pn;
}
void pushUnlessNull(ParseNode *pn) { if (pn) push(pn); }
/* Push the children of the PN_LIST node |pn| on the stack. */
void pushList(ParseNode *pn) {
/* This clobbers pn->pn_head if the list is empty; should be okay. */
@ -123,7 +122,8 @@ PushCodeNodeChildren(ParseNode *node, NodeStack *stack)
* them.
*/
node->pn_funbox = nullptr;
stack->pushUnlessNull(node->pn_body);
if (node->pn_body)
stack->push(node->pn_body);
node->pn_body = nullptr;
return PushResult::CleanUpLater;
@ -146,7 +146,8 @@ PushNameNodeChildren(ParseNode *node, NodeStack *stack)
* reference.
*/
if (!node->isUsed()) {
stack->pushUnlessNull(node->pn_expr);
if (node->pn_expr)
stack->push(node->pn_expr);
node->pn_expr = nullptr;
}
@ -167,18 +168,6 @@ PushListNodeChildren(ParseNode *node, NodeStack *stack)
return PushResult::Recyclable;
}
static PushResult
PushTernaryNodeNullableChildren(ParseNode *node, NodeStack *stack)
{
MOZ_ASSERT(node->isArity(PN_TERNARY));
stack->pushUnlessNull(node->pn_kid1);
stack->pushUnlessNull(node->pn_kid2);
stack->pushUnlessNull(node->pn_kid3);
return PushResult::Recyclable;
}
static PushResult
PushUnaryNodeChild(ParseNode *node, NodeStack *stack)
{
@ -189,57 +178,6 @@ PushUnaryNodeChild(ParseNode *node, NodeStack *stack)
return PushResult::Recyclable;
}
static PushResult
PushUnaryNodeNullableChild(ParseNode *node, NodeStack *stack)
{
MOZ_ASSERT(node->isArity(PN_UNARY));
if (node->pn_kid)
stack->push(node->pn_kid);
return PushResult::Recyclable;
}
static PushResult
PushBinaryNodeChildren(ParseNode *node, NodeStack *stack)
{
MOZ_ASSERT(node->isArity(PN_BINARY));
stack->push(node->pn_left);
stack->push(node->pn_right);
return PushResult::Recyclable;
}
static PushResult
PushBinaryNodeNullableChildren(ParseNode *node, NodeStack *stack)
{
MOZ_ASSERT(node->isArity(PN_BINARY) || node->isArity(PN_BINARY_OBJ));
if (node->pn_left != node->pn_right) {
// XXX Is it ever the case that left == right any more? I think this
// used to be PNK_SHORTHAND, but its two children are now different
// nodes that are structurally equivalent, so PNK_SHORTHAND doesn't
// require this.
stack->pushUnlessNull(node->pn_left);
}
stack->pushUnlessNull(node->pn_right);
return PushResult::Recyclable;
}
static PushResult
CanRecycleNullaryNode(ParseNode *node, NodeStack *stack)
{
MOZ_ASSERT(node->isArity(PN_NULLARY));
if (node->isUsed() || node->isDefn())
return PushResult::CleanUpLater;
return PushResult::Recyclable;
}
/*
* Push the children of |pn| on |stack|. Return true if |pn| itself could be
* safely recycled, or false if it must be cleaned later (pn_used and pn_defn
@ -269,6 +207,7 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
case PNK_CONTINUE:
case PNK_DEBUGGER:
case PNK_EXPORT_BATCH_SPEC:
case PNK_OBJECT_PROPERTY_NAME:
MOZ_ASSERT(pn->isArity(PN_NULLARY));
MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately");
@ -295,8 +234,12 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
return PushUnaryNodeChild(pn, stack);
// Nodes with a single nullable child.
case PNK_SEMI:
return PushUnaryNodeNullableChild(pn, stack);
case PNK_SEMI: {
MOZ_ASSERT(pn->isArity(PN_UNARY));
if (pn->pn_kid)
stack->push(pn->pn_kid);
return PushResult::Recyclable;
}
// Binary nodes with two non-null children.
@ -314,6 +257,10 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
case PNK_DIVASSIGN:
case PNK_MODASSIGN:
// ...and a few others.
case PNK_ELEM:
case PNK_LETEXPR:
case PNK_IMPORT_SPEC:
case PNK_EXPORT_SPEC:
case PNK_COLON:
case PNK_CASE:
case PNK_SHORTHAND:
@ -321,8 +268,23 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
case PNK_WHILE:
case PNK_SWITCH:
case PNK_LETBLOCK:
case PNK_FOR:
return PushBinaryNodeChildren(pn, stack);
case PNK_FOR: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
stack->push(pn->pn_left);
stack->push(pn->pn_right);
return PushResult::Recyclable;
}
// PNK_WITH is PN_BINARY_OBJ -- that is, PN_BINARY with (irrelevant for
// this method's purposes) the addition of the StaticWithObject as
// pn_binary_obj. Both left (expression) and right (statement) are
// non-null.
case PNK_WITH: {
MOZ_ASSERT(pn->isArity(PN_BINARY_OBJ));
stack->push(pn->pn_left);
stack->push(pn->pn_right);
return PushResult::Recyclable;
}
// Default nodes, for dumb reasons that we're not changing now (mostly
// structural semi-consistency with PNK_CASE nodes), have a null left
@ -352,6 +314,36 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
return PushResult::Recyclable;
}
// A return node's left half is what you'd expect: the return expression,
// if any. The right half is non-null only for returns inside generator
// functions, with the structure described in the assertions.
case PNK_RETURN: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
if (pn->pn_left)
stack->push(pn->pn_left);
if (pn->pn_right) {
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
MOZ_ASSERT(pn->pn_right->pn_atom->equals(".genrval"));
MOZ_ASSERT(pn->pn_right->isAssigned());
stack->push(pn->pn_right);
}
return PushResult::Recyclable;
}
// Import and export-from nodes have a list of specifiers on the left
// and a module string on the right.
case PNK_IMPORT:
case PNK_EXPORT_FROM: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT_IF(pn->isKind(PNK_IMPORT), pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_left->isKind(PNK_EXPORT_SPEC_LIST));
MOZ_ASSERT(pn->pn_left->isArity(PN_LIST));
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
stack->pushList(pn->pn_left);
stack->push(pn->pn_right);
return PushResult::Recyclable;
}
// Ternary nodes with all children non-null.
case PNK_CONDITIONAL: {
MOZ_ASSERT(pn->isArity(PN_TERNARY));
@ -378,8 +370,16 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
}
// for (;;) nodes have one child per optional component of the loop head.
case PNK_FORHEAD:
return PushTernaryNodeNullableChildren(pn, stack);
case PNK_FORHEAD: {
MOZ_ASSERT(pn->isArity(PN_TERNARY));
if (pn->pn_kid1)
stack->push(pn->pn_kid1);
if (pn->pn_kid2)
stack->push(pn->pn_kid2);
if (pn->pn_kid3)
stack->push(pn->pn_kid3);
return PushResult::Recyclable;
}
// if-statement nodes have condition and consequent children and a
// possibly-null alternative.
@ -405,6 +405,19 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
return PushResult::Recyclable;
}
// A catch node has first kid as catch-variable pattern, the second kid
// as catch condition (which, if non-null, records the |<cond>| in
// SpiderMonkey's |catch (e if <cond>)| extension), and third kid as the
// statements in the catch block.
case PNK_CATCH: {
MOZ_ASSERT(pn->isArity(PN_TERNARY));
stack->push(pn->pn_kid1);
if (pn->pn_kid2)
stack->push(pn->pn_kid2);
stack->push(pn->pn_kid3);
return PushResult::Recyclable;
}
// List nodes with all non-null children.
case PNK_OR:
case PNK_AND:
@ -430,77 +443,54 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
case PNK_DIV:
case PNK_MOD:
case PNK_COMMA:
case PNK_NEW:
case PNK_CALL:
case PNK_GENEXP:
case PNK_ARRAY:
case PNK_OBJECT:
case PNK_TEMPLATE_STRING_LIST:
case PNK_TAGGED_TEMPLATE:
case PNK_CALLSITEOBJ:
case PNK_VAR:
case PNK_CONST:
case PNK_GLOBALCONST:
case PNK_LET:
case PNK_CATCHLIST:
case PNK_STATEMENTLIST:
case PNK_IMPORT_SPEC_LIST:
case PNK_EXPORT_SPEC_LIST:
case PNK_SEQ:
case PNK_ARGSBODY:
return PushListNodeChildren(pn, stack);
// Array comprehension nodes are lists with a single child -- PNK_FOR for
// comprehensions, PNK_LEXICALSCOPE for legacy comprehensions. Probably
// this should be a non-list eventually.
case PNK_ARRAYCOMP: {
#ifdef DEBUG
MOZ_ASSERT(pn->isKind(PNK_ARRAYCOMP));
MOZ_ASSERT(pn->isArity(PN_LIST));
MOZ_ASSERT(pn->pn_count == 1);
MOZ_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE) || pn->pn_head->isKind(PNK_FOR));
#endif
return PushListNodeChildren(pn, stack);
}
case PNK_LABEL:
case PNK_DOT:
case PNK_LEXICALSCOPE:
case PNK_NAME:
return PushNameNodeChildren(pn, stack);
case PNK_DOT:
case PNK_ELEM:
case PNK_STATEMENTLIST:
case PNK_CALL:
case PNK_NAME:
case PNK_TEMPLATE_STRING_LIST:
case PNK_TAGGED_TEMPLATE:
case PNK_CALLSITEOBJ:
case PNK_FUNCTION:
case PNK_WITH:
case PNK_RETURN:
case PNK_NEW:
case PNK_CATCH:
case PNK_GENEXP:
case PNK_ARRAYCOMP:
case PNK_LEXICALSCOPE:
case PNK_LET:
case PNK_LETEXPR:
case PNK_IMPORT:
case PNK_IMPORT_SPEC_LIST:
case PNK_IMPORT_SPEC:
case PNK_EXPORT_FROM:
case PNK_EXPORT_SPEC_LIST:
case PNK_EXPORT_SPEC:
break; // for now
return PushCodeNodeChildren(pn, stack);
case PNK_LIMIT: // invalid sentinel value
MOZ_CRASH("invalid node kind");
}
// Fallthrough for not-yet-handled kinds.
switch (pn->getArity()) {
case PN_CODE:
return PushCodeNodeChildren(pn, stack);
case PN_NAME:
return PushNameNodeChildren(pn, stack);
case PN_LIST:
return PushListNodeChildren(pn, stack);
case PN_TERNARY:
return PushTernaryNodeNullableChildren(pn, stack);
case PN_BINARY:
case PN_BINARY_OBJ:
return PushBinaryNodeNullableChildren(pn, stack);
case PN_UNARY:
return PushUnaryNodeNullableChild(pn, stack);
case PN_NULLARY:
return CanRecycleNullaryNode(pn, stack);
default:
MOZ_CRASH("huh?");
return PushResult::CleanUpLater;
}
MOZ_CRASH("bad ParseNodeKind");
return PushResult::CleanUpLater;
}
/*

View File

@ -89,6 +89,7 @@ class UpvarCookie
F(OBJECT) \
F(CALL) \
F(NAME) \
F(OBJECT_PROPERTY_NAME) \
F(COMPUTED_NAME) \
F(NUMBER) \
F(STRING) \

View File

@ -7008,26 +7008,30 @@ template <>
ParseNode*
Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array)
{
array->setKind(PNK_ARRAYCOMP);
// Remove the single element from array's linked list, leaving us with an
// empty array literal and a comprehension expression.
// Discard our presumed array literal containing only a single element, and
// instead return an array comprehension node. Extract the few bits of
// information needed from the array literal, then free it.
MOZ_ASSERT(array->isKind(PNK_ARRAY));
MOZ_ASSERT(array->pn_count == 1);
ParseNode *bodyExpr = array->last();
uint32_t arrayBegin = handler.getPosition(array).begin;
uint32_t blockid = array->pn_blockid;
ParseNode *bodyExpr = array->pn_head;
array->pn_count = 0;
array->pn_tail = &array->pn_head;
*array->pn_tail = nullptr;
ParseNode *comp = legacyComprehensionTail(bodyExpr, array->pn_blockid, NotGenerator,
nullptr, LegacyComprehensionHeadBlockScopeDepth(pc));
handler.freeTree(array);
ParseNode *comp = legacyComprehensionTail(bodyExpr, blockid, NotGenerator, nullptr,
LegacyComprehensionHeadBlockScopeDepth(pc));
if (!comp)
return null();
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
TokenPos p = handler.getPosition(array);
p.end = pos().end;
return handler.newArrayComprehension(comp, array->pn_blockid, p);
return handler.newArrayComprehension(comp, blockid, TokenPos(arrayBegin, pos().end));
}
template <>
@ -7770,8 +7774,7 @@ Parser<ParseHandler>::arrayInitializer()
return null();
if (foldConstants && !FoldConstants(context, &element, this))
return null();
if (!handler.addArrayElement(literal, element))
return null();
handler.addArrayElement(literal, element);
}
if (tt != TOK_COMMA) {
@ -7936,7 +7939,7 @@ Parser<ParseHandler>::objectLiteral()
op = atom == context->names().get ? JSOP_INITPROP_GETTER
: JSOP_INITPROP_SETTER;
} else {
propname = handler.newIdentifier(atom, pos());
propname = handler.newObjectLiteralPropertyName(atom, pos());
if (!propname)
return null();
break;
@ -7949,7 +7952,7 @@ Parser<ParseHandler>::objectLiteral()
return null();
if (tt == TOK_NAME) {
atom = tokenStream.currentName();
propname = newName(atom->asPropertyName());
propname = handler.newObjectLiteralPropertyName(atom, pos());
if (!propname)
return null();
} else if (tt == TOK_STRING) {
@ -7982,7 +7985,7 @@ Parser<ParseHandler>::objectLiteral()
} else {
// Not an accessor property after all.
tokenStream.ungetToken();
propname = handler.newIdentifier(atom, pos());
propname = handler.newObjectLiteralPropertyName(atom, pos());
if (!propname)
return null();
op = JSOP_INITPROP;
@ -8061,20 +8064,16 @@ Parser<ParseHandler>::objectLiteral()
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
return null();
}
if (!abortIfSyntaxParser())
return null();
tokenStream.ungetToken();
if (!tokenStream.checkForKeyword(atom, nullptr))
return null();
PropertyName *name = handler.isName(propname);
MOZ_ASSERT(atom);
propname = newName(name);
if (!propname)
Node nameExpr = identifierName();
if (!nameExpr)
return null();
Node ident = identifierName();
if (!ident)
return null();
if (!handler.addPropertyDefinition(literal, propname, ident, true))
if (!handler.addShorthand(literal, propname, nameExpr))
return null();
} else if (tt == TOK_LP) {
tokenStream.ungetToken();

View File

@ -120,7 +120,10 @@ class SyntaxParseHandler
return Definition::PLACEHOLDER;
}
Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeName; }
Node newObjectLiteralPropertyName(JSAtom *atom, const TokenPos &pos) {
return NodeName;
}
Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; }
Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
@ -180,11 +183,12 @@ class SyntaxParseHandler
Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeGeneric; }
bool addElision(Node literal, const TokenPos &pos) { return true; }
bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
bool addArrayElement(Node literal, Node element) { return true; }
void addArrayElement(Node literal, Node element) { }
Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; }
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
bool addShorthand(Node literal, Node name, Node expr) { return true; }
bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }

View File

@ -471,7 +471,7 @@ static const size_t MAX_ATTEMPTS = 2;
bool
BacktrackingAllocator::tryAllocateFixed(LiveInterval *interval, bool *success,
bool *pfixed, LiveInterval **pconflicting)
bool *pfixed, LiveIntervalVector &conflicting)
{
// Spill intervals which are required to be in a certain stack slot.
if (!interval->requirement()->allocation().isRegister()) {
@ -482,12 +482,12 @@ BacktrackingAllocator::tryAllocateFixed(LiveInterval *interval, bool *success,
}
AnyRegister reg = interval->requirement()->allocation().toRegister();
return tryAllocateRegister(registers[reg.code()], interval, success, pfixed, pconflicting);
return tryAllocateRegister(registers[reg.code()], interval, success, pfixed, conflicting);
}
bool
BacktrackingAllocator::tryAllocateNonFixed(LiveInterval *interval, bool *success,
bool *pfixed, LiveInterval **pconflicting)
bool *pfixed, LiveIntervalVector &conflicting)
{
// If we want, but do not require an interval to be in a specific
// register, only look at that register for allocating and evict
@ -496,7 +496,7 @@ BacktrackingAllocator::tryAllocateNonFixed(LiveInterval *interval, bool *success
// and will tie up more registers than if we spilled.
if (interval->hint()->kind() == Requirement::FIXED) {
AnyRegister reg = interval->hint()->allocation().toRegister();
if (!tryAllocateRegister(registers[reg.code()], interval, success, pfixed, pconflicting))
if (!tryAllocateRegister(registers[reg.code()], interval, success, pfixed, conflicting))
return false;
if (*success)
return true;
@ -511,11 +511,11 @@ BacktrackingAllocator::tryAllocateNonFixed(LiveInterval *interval, bool *success
return true;
}
if (!*pconflicting || minimalInterval(interval)) {
if (conflicting.empty() || minimalInterval(interval)) {
// Search for any available register which the interval can be
// allocated to.
for (size_t i = 0; i < AnyRegister::Total; i++) {
if (!tryAllocateRegister(registers[i], interval, success, pfixed, pconflicting))
if (!tryAllocateRegister(registers[i], interval, success, pfixed, conflicting))
return false;
if (*success)
return true;
@ -568,19 +568,19 @@ BacktrackingAllocator::processInterval(LiveInterval *interval)
bool canAllocate = setIntervalRequirement(interval);
bool fixed;
LiveInterval *conflict = nullptr;
LiveIntervalVector conflicting;
for (size_t attempt = 0;; attempt++) {
if (canAllocate) {
bool success = false;
fixed = false;
conflict = nullptr;
conflicting.clear();
// Ok, let's try allocating for this interval.
if (interval->requirement()->kind() == Requirement::FIXED) {
if (!tryAllocateFixed(interval, &success, &fixed, &conflict))
if (!tryAllocateFixed(interval, &success, &fixed, conflicting))
return false;
} else {
if (!tryAllocateNonFixed(interval, &success, &fixed, &conflict))
if (!tryAllocateNonFixed(interval, &success, &fixed, conflicting))
return false;
}
@ -588,15 +588,17 @@ BacktrackingAllocator::processInterval(LiveInterval *interval)
if (success)
return true;
// If that didn't work, but we have a non-fixed LiveInterval known
// to be conflicting, maybe we can evict it and try again.
// If that didn't work, but we have one or more non-fixed intervals
// known to be conflicting, maybe we can evict them and try again.
if (attempt < MAX_ATTEMPTS &&
!fixed &&
conflict &&
computeSpillWeight(conflict) < computeSpillWeight(interval))
!conflicting.empty() &&
maximumSpillWeight(conflicting) < computeSpillWeight(interval))
{
if (!evictInterval(conflict))
return false;
for (size_t i = 0; i < conflicting.length(); i++) {
if (!evictInterval(conflicting[i]))
return false;
}
continue;
}
}
@ -607,6 +609,7 @@ BacktrackingAllocator::processInterval(LiveInterval *interval)
// be constructed so that any minimal interval is allocatable.
MOZ_ASSERT(!minimalInterval(interval));
LiveInterval *conflict = conflicting.empty() ? nullptr : conflicting[0];
return chooseIntervalSplit(interval, canAllocate && fixed, conflict);
}
}
@ -782,7 +785,7 @@ BacktrackingAllocator::tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegi
bool
BacktrackingAllocator::tryAllocateRegister(PhysicalRegister &r, LiveInterval *interval,
bool *success, bool *pfixed, LiveInterval **pconflicting)
bool *success, bool *pfixed, LiveIntervalVector &conflicting)
{
*success = false;
@ -796,6 +799,8 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister &r, LiveInterval *in
MOZ_ASSERT_IF(interval->requirement()->kind() == Requirement::FIXED,
interval->requirement()->allocation() == LAllocation(r.reg));
LiveIntervalVector aliasedConflicting;
for (size_t i = 0; i < interval->numRanges(); i++) {
AllocatedRange range(interval, interval->getRange(i)), existing;
for (size_t a = 0; a < r.reg.numAliased(); a++) {
@ -803,26 +808,63 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister &r, LiveInterval *in
if (!rAlias.allocations.contains(range, &existing))
continue;
if (existing.interval->hasVreg()) {
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " %s collides with v%u[%u] %s [weight %lu]",
rAlias.reg.name(), existing.interval->vreg(),
existing.interval->index(),
existing.range->toString(),
computeSpillWeight(existing.interval));
MOZ_ASSERT(existing.interval->getAllocation()->toRegister() == rAlias.reg);
bool duplicate = false;
for (size_t i = 0; i < aliasedConflicting.length(); i++) {
if (aliasedConflicting[i] == existing.interval) {
duplicate = true;
break;
}
}
if (!*pconflicting || computeSpillWeight(existing.interval) < computeSpillWeight(*pconflicting))
*pconflicting = existing.interval;
if (!duplicate && !aliasedConflicting.append(existing.interval))
return false;
} else {
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " %s collides with fixed use %s",
rAlias.reg.name(), existing.range->toString());
}
*pfixed = true;
return true;
}
return true;
}
}
if (!aliasedConflicting.empty()) {
// One or more aliased registers is allocated to another live interval
// overlapping this one. Keep track of the conflicting set, and in the
// case of multiple conflicting sets keep track of the set with the
// lowest maximum spill weight.
if (JitSpewEnabled(JitSpew_RegAlloc)) {
if (aliasedConflicting.length() == 1) {
LiveInterval *existing = aliasedConflicting[0];
JitSpew(JitSpew_RegAlloc, " %s collides with v%u[%u] %s [weight %lu]",
r.reg.name(), existing->vreg(), existing->index(),
existing->rangesToString(), computeSpillWeight(existing));
} else {
JitSpew(JitSpew_RegAlloc, " %s collides with the following", r.reg.name());
for (size_t i = 0; i < aliasedConflicting.length(); i++) {
LiveInterval *existing = aliasedConflicting[i];
JitSpew(JitSpew_RegAlloc, " v%u[%u] %s [weight %lu]",
existing->vreg(), existing->index(),
existing->rangesToString(), computeSpillWeight(existing));
}
}
}
if (conflicting.empty()) {
if (!conflicting.appendAll(aliasedConflicting))
return false;
} else {
if (maximumSpillWeight(aliasedConflicting) < maximumSpillWeight(conflicting)) {
conflicting.clear();
if (!conflicting.appendAll(aliasedConflicting))
return false;
}
}
return true;
}
JitSpew(JitSpew_RegAlloc, " allocated to %s", r.reg.name());
for (size_t i = 0; i < interval->numRanges(); i++) {
@ -1792,6 +1834,15 @@ BacktrackingAllocator::computeSpillWeight(const VirtualRegisterGroup *group)
return maxWeight;
}
size_t
BacktrackingAllocator::maximumSpillWeight(const LiveIntervalVector &intervals)
{
size_t maxWeight = 0;
for (size_t i = 0; i < intervals.length(); i++)
maxWeight = Max(maxWeight, computeSpillWeight(intervals[i]));
return maxWeight;
}
bool
BacktrackingAllocator::trySplitAcrossHotcode(LiveInterval *interval, bool *success)
{

View File

@ -211,13 +211,15 @@ class BacktrackingAllocator
bool tryGroupRegisters(uint32_t vreg0, uint32_t vreg1);
bool tryGroupReusedRegister(uint32_t def, uint32_t use);
bool groupAndQueueRegisters();
bool tryAllocateFixed(LiveInterval *interval, bool *success, bool *pfixed, LiveInterval **pconflicting);
bool tryAllocateNonFixed(LiveInterval *interval, bool *success, bool *pfixed, LiveInterval **pconflicting);
bool tryAllocateFixed(LiveInterval *interval, bool *success, bool *pfixed,
LiveIntervalVector &conflicting);
bool tryAllocateNonFixed(LiveInterval *interval, bool *success, bool *pfixed,
LiveIntervalVector &conflicting);
bool processInterval(LiveInterval *interval);
bool processGroup(VirtualRegisterGroup *group);
bool setIntervalRequirement(LiveInterval *interval);
bool tryAllocateRegister(PhysicalRegister &r, LiveInterval *interval,
bool *success, bool *pfixed, LiveInterval **pconflicting);
bool *success, bool *pfixed, LiveIntervalVector &conflicting);
bool tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegisterGroup *group,
bool *psuccess, bool *pfixed, LiveInterval **pconflicting);
bool evictInterval(LiveInterval *interval);
@ -261,6 +263,8 @@ class BacktrackingAllocator
size_t computePriority(const VirtualRegisterGroup *group);
size_t computeSpillWeight(const VirtualRegisterGroup *group);
size_t maximumSpillWeight(const LiveIntervalVector &intervals);
bool chooseIntervalSplit(LiveInterval *interval, bool fixed, LiveInterval *conflict);
bool splitAt(LiveInterval *interval,

View File

@ -2384,6 +2384,14 @@ MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const BaseIndex &src,
ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), label, cond);
}
void
MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const Address &address, Label *label)
{
MOZ_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, SecondScratchReg);
ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), label, cond);
}
void
MacroAssemblerMIPSCompat::testObjectSet(Condition cond, const ValueOperand &value, Register dest)
{

View File

@ -687,6 +687,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
void branchTestObject(Condition cond, const ValueOperand &value, Label *label);
void branchTestObject(Condition cond, Register tag, Label *label);
void branchTestObject(Condition cond, const BaseIndex &src, Label *label);
void branchTestObject(Condition cond, const Address &src, Label *label);
void testObjectSet(Condition cond, const ValueOperand &value, Register dest);
void branchTestString(Condition cond, const ValueOperand &value, Label *label);

View File

@ -977,6 +977,12 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
branchTestNull(cond, Operand(address), label);
}
// This one, though, clobbers the ScratchReg.
void branchTestObject(Condition cond, const Address &src, Label *label) {
cond = testObject(cond, src);
j(cond, label);
}
// Perform a type-test on a full Value loaded into a register.
// Clobbers the ScratchReg.
void branchTestUndefined(Condition cond, const ValueOperand &src, Label *label) {

View File

@ -360,6 +360,15 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
MOZ_ASSERT(cond == Equal || cond == NotEqual);
return testInt32(cond, Operand(address));
}
Condition testObject(Condition cond, const Operand &operand) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(ToType(operand), ImmTag(JSVAL_TAG_OBJECT));
return cond;
}
Condition testObject(Condition cond, const Address &address) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
return testObject(cond, Operand(address));
}
Condition testDouble(Condition cond, const Operand &operand) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
Condition actual = (cond == Equal) ? Below : AboveOrEqual;

View File

@ -3329,6 +3329,14 @@ EnsureNewArrayElements(ExclusiveContext *cx, ArrayObject *obj, uint32_t length)
return true;
}
static bool
NewArrayIsCachable(ExclusiveContext *cxArg, NewObjectKind newKind)
{
return cxArg->isJSContext() &&
newKind == GenericObject &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback();
}
template <uint32_t maxLength>
static MOZ_ALWAYS_INLINE ArrayObject *
NewArray(ExclusiveContext *cxArg, uint32_t length,
@ -3338,15 +3346,13 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
allocKind = GetBackgroundAllocKind(allocKind);
NewObjectCache::EntryIndex entry = -1;
uint64_t gcNumber = 0;
if (JSContext *cx = cxArg->maybeJSContext()) {
bool isCachable = NewArrayIsCachable(cxArg, newKind);
if (isCachable) {
JSContext *cx = cxArg->asJSContext();
JSRuntime *rt = cx->runtime();
NewObjectCache &cache = rt->newObjectCache;
if (newKind == GenericObject &&
!cx->compartment()->hasObjectMetadataCallback() &&
cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
{
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) {
gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
JSObject *obj = cache.newObjectFromHit(cx, entry, heap);
if (obj) {
@ -3361,8 +3367,6 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
}
return arr;
}
} else {
gcNumber = rt->gc.gcNumber();
}
}
@ -3405,11 +3409,11 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
if (newKind == SingletonObject && !JSObject::setSingleton(cxArg, arr))
return nullptr;
if (entry != -1 &&
cxArg->asJSContext()->runtime()->gc.gcNumber() == gcNumber)
{
cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_,
cxArg->global(), allocKind, arr);
if (isCachable) {
NewObjectCache &cache = cxArg->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
cache.lookupGlobal(&ArrayObject::class_, cxArg->global(), allocKind, &entry);
cache.fillGlobal(entry, &ArrayObject::class_, cxArg->global(), allocKind, arr);
}
if (maxLength > 0 && !EnsureNewArrayElements(cxArg, arr, std::min(maxLength, length)))

View File

@ -1110,17 +1110,17 @@ struct CompartmentFilter {
};
struct AllCompartments : public CompartmentFilter {
virtual bool match(JSCompartment *c) const { return true; }
virtual bool match(JSCompartment *c) const MOZ_OVERRIDE { return true; }
};
struct ContentCompartmentsOnly : public CompartmentFilter {
virtual bool match(JSCompartment *c) const {
virtual bool match(JSCompartment *c) const MOZ_OVERRIDE {
return !IsSystemCompartment(c);
}
};
struct ChromeCompartmentsOnly : public CompartmentFilter {
virtual bool match(JSCompartment *c) const {
virtual bool match(JSCompartment *c) const MOZ_OVERRIDE {
return IsSystemCompartment(c);
}
};
@ -1128,13 +1128,13 @@ struct ChromeCompartmentsOnly : public CompartmentFilter {
struct SingleCompartment : public CompartmentFilter {
JSCompartment *ours;
explicit SingleCompartment(JSCompartment *c) : ours(c) {}
virtual bool match(JSCompartment *c) const { return c == ours; }
virtual bool match(JSCompartment *c) const MOZ_OVERRIDE { return c == ours; }
};
struct CompartmentsWithPrincipals : public CompartmentFilter {
JSPrincipals *principals;
explicit CompartmentsWithPrincipals(JSPrincipals *p) : principals(p) {}
virtual bool match(JSCompartment *c) const {
virtual bool match(JSCompartment *c) const MOZ_OVERRIDE {
return JS_GetCompartmentPrincipals(c) == principals;
}
};

View File

@ -1218,6 +1218,20 @@ NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto
return fill(entry, clasp, proto.raw(), kind, obj);
}
static bool
NewObjectWithTaggedProtoIsCachable(ExclusiveContext *cxArg, Handle<TaggedProto> proto,
NewObjectKind newKind, const Class *clasp,
HandleObject parentArg)
{
return cxArg->isJSContext() &&
proto.isObject() &&
newKind == GenericObject &&
clasp->isNative() &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback() &&
(!parentArg || parentArg == proto.toObject()->getParent()) &&
!proto.toObject()->is<GlobalObject>();
}
JSObject *
js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp,
Handle<TaggedProto> proto, HandleObject parentArg,
@ -1226,25 +1240,16 @@ js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp,
if (CanBeFinalizedInBackground(allocKind, clasp))
allocKind = GetBackgroundAllocKind(allocKind);
NewObjectCache::EntryIndex entry = -1;
uint64_t gcNumber = 0;
if (JSContext *cx = cxArg->maybeJSContext()) {
bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, proto, newKind, clasp, parentArg);
if (isCachable) {
JSContext *cx = cxArg->asJSContext();
JSRuntime *rt = cx->runtime();
NewObjectCache &cache = rt->newObjectCache;
if (proto.isObject() &&
newKind == GenericObject &&
clasp->isNative() &&
!cx->compartment()->hasObjectMetadataCallback() &&
(!parentArg || parentArg == proto.toObject()->getParent()) &&
!proto.toObject()->is<GlobalObject>())
{
if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
return obj;
} else {
gcNumber = rt->gc.gcNumber();
}
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
return obj;
}
}
@ -1264,12 +1269,11 @@ js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp,
if (!obj)
return nullptr;
if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() &&
cxArg->asJSContext()->runtime()->gc.gcNumber() == gcNumber)
{
cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
proto, allocKind,
&obj->as<NativeObject>());
if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
NewObjectCache &cache = cxArg->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
}
return obj;
@ -1369,6 +1373,17 @@ FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject prot
return true;
}
static bool
NewObjectWithClassProtoIsCachable(ExclusiveContext *cxArg, HandleObject parent,
JSProtoKey protoKey, NewObjectKind newKind, const Class *clasp)
{
return cxArg->isJSContext() &&
parent->is<GlobalObject>() &&
protoKey != JSProto_Null &&
newKind == GenericObject &&
clasp->isNative() &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback();
}
JSObject *
js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
@ -1396,24 +1411,16 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
*/
JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp);
NewObjectCache::EntryIndex entry = -1;
uint64_t gcNumber = 0;
if (JSContext *cx = cxArg->maybeJSContext()) {
bool isCachable = NewObjectWithClassProtoIsCachable(cxArg, parent, protoKey, newKind, clasp);
if (isCachable) {
JSContext *cx = cxArg->asJSContext();
JSRuntime *rt = cx->runtime();
NewObjectCache &cache = rt->newObjectCache;
if (parent->is<GlobalObject>() &&
protoKey != JSProto_Null &&
newKind == GenericObject &&
clasp->isNative() &&
!cx->compartment()->hasObjectMetadataCallback())
{
if (cache.lookupGlobal(clasp, &parent->as<GlobalObject>(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
return obj;
} else {
gcNumber = rt->gc.gcNumber();
}
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGlobal(clasp, &parent->as<GlobalObject>(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
return obj;
}
}
@ -1430,17 +1437,29 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
if (!obj)
return nullptr;
if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() &&
cxArg->asJSContext()->runtime()->gc.gcNumber() == gcNumber)
{
cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp,
&parent->as<GlobalObject>(),
allocKind, &obj->as<NativeObject>());
if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
NewObjectCache &cache = cxArg->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
cache.lookupGlobal(clasp, &parent->as<GlobalObject>(), allocKind, &entry);
cache.fillGlobal(entry, clasp, &parent->as<GlobalObject>(), allocKind,
&obj->as<NativeObject>());
}
return obj;
}
static bool
NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObject parent,
NewObjectKind newKind)
{
return group->proto().isObject() &&
parent == group->proto().toObject()->getParent() &&
newKind == GenericObject &&
group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) &&
!cx->compartment()->hasObjectMetadataCallback();
}
/*
* Create a plain object with the specified group. This bypasses getNewGroup to
* avoid losing creation site information for objects made by scripted 'new'.
@ -1455,24 +1474,15 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
if (CanBeFinalizedInBackground(allocKind, group->clasp()))
allocKind = GetBackgroundAllocKind(allocKind);
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
uint64_t gcNumber = 0;
if (group->proto().isObject() &&
parent == group->proto().toObject()->getParent() &&
newKind == GenericObject &&
group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) &&
!cx->compartment()->hasObjectMetadataCallback())
{
bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind);
if (isCachable) {
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGroup(group, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry,
GetInitialHeap(newKind, group->clasp()));
if (obj)
return obj;
} else {
gcNumber = cx->runtime()->gc.gcNumber();
}
}
@ -1480,9 +1490,10 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
if (!obj)
return nullptr;
if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() &&
cx->runtime()->gc.gcNumber() == gcNumber)
{
if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
cache.lookupGroup(group, allocKind, &entry);
cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
}

View File

@ -1796,6 +1796,7 @@ class ASTSerializer
bool identifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst);
bool identifier(ParseNode *pn, MutableHandleValue dst);
bool objectPropertyName(ParseNode *pn, MutableHandleValue dst);
bool literal(ParseNode *pn, MutableHandleValue dst);
bool pattern(ParseNode *pn, MutableHandleValue dst);
@ -3058,7 +3059,7 @@ ASTSerializer::propertyName(ParseNode *pn, MutableHandleValue dst)
{
if (pn->isKind(PNK_COMPUTED_NAME))
return expression(pn, dst);
if (pn->isKind(PNK_NAME))
if (pn->isKind(PNK_OBJECT_PROPERTY_NAME))
return identifier(pn, dst);
LOCAL_ASSERT(pn->isKind(PNK_STRING) || pn->isKind(PNK_NUMBER));
@ -3250,6 +3251,17 @@ ASTSerializer::identifier(ParseNode *pn, MutableHandleValue dst)
return identifier(pnAtom, &pn->pn_pos, dst);
}
bool
ASTSerializer::objectPropertyName(ParseNode *pn, MutableHandleValue dst)
{
LOCAL_ASSERT(pn->isKind(PNK_OBJECT_PROPERTY_NAME));
LOCAL_ASSERT(pn->isArity(PN_NULLARY));
LOCAL_ASSERT(pn->pn_atom);
RootedAtom pnAtom(cx, pn->pn_atom);
return identifier(pnAtom, &pn->pn_pos, dst);
}
bool
ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
{

View File

@ -200,16 +200,6 @@ ObjectValueMap::findZoneEdges()
return true;
}
static JSObject *
GetKeyArg(JSContext *cx, CallArgs &args)
{
if (args[0].isPrimitive()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
return nullptr;
}
return &args[0].toObject();
}
MOZ_ALWAYS_INLINE bool
IsWeakMap(HandleValue v)
{
@ -221,16 +211,13 @@ WeakMap_has_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"WeakMap.has", "0", "s");
return false;
if (!args.get(0).isObject()) {
args.rval().setBoolean(false);
return true;
}
JSObject *key = GetKeyArg(cx, args);
if (!key)
return false;
if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
JSObject *key = &args[0].toObject();
if (map->has(key)) {
args.rval().setBoolean(true);
return true;
@ -274,23 +261,20 @@ WeakMap_get_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"WeakMap.get", "0", "s");
return false;
if (!args.get(0).isObject()) {
args.rval().setUndefined();
return true;
}
JSObject *key = GetKeyArg(cx, args);
if (!key)
return false;
if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
JSObject *key = &args[0].toObject();
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
args.rval().set(ptr->value());
return true;
}
}
args.rval().set((args.length() > 1) ? args[1] : UndefinedValue());
args.rval().setUndefined();
return true;
}
@ -306,16 +290,13 @@ WeakMap_delete_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"WeakMap.delete", "0", "s");
return false;
if (!args.get(0).isObject()) {
args.rval().setBoolean(false);
return true;
}
JSObject *key = GetKeyArg(cx, args);
if (!key)
return false;
if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
JSObject *key = &args[0].toObject();
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
map->remove(ptr);
args.rval().setBoolean(true);
@ -402,20 +383,16 @@ WeakMap_set_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"WeakMap.set", "0", "s");
if (!args.get(0).isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
return false;
}
RootedObject key(cx, GetKeyArg(cx, args));
if (!key)
return false;
RootedValue value(cx, (args.length() > 1) ? args[1] : UndefinedValue());
RootedObject key(cx, &args[0].toObject());
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
if (!SetWeakMapEntryInternal(cx, map, key, value))
if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
return false;
args.rval().set(args.thisv());
return true;

View File

@ -83,22 +83,39 @@ function test()
var map = new WeakMap();
check(function() !map.has(key));
map.set(key, 42);
check(function() map.delete(key) == false);
check(function() map.set(key, 42) === map);
check(function() map.get(key) == 42);
check(function() typeof map.get({}) == "undefined");
check(function() map.get({}, "foo") == "foo");
check(function() map.get({}, "foo") == undefined);
gc(); gc(); gc();
check(function() map.get(key) == 42);
map.delete(key);
check(function() map.delete(key) == true);
check(function() map.delete(key) == false);
check(function() map.delete({}) == false);
check(function() typeof map.get(key) == "undefined");
check(function() !map.has(key));
check(function() map.delete(key) == false);
var value = { };
map.set(new Object(), value);
check(function() map.set(new Object(), value) === map);
gc(); gc(); gc();
check(function() map.has("non-object key") == false);
check(function() map.has() == false);
check(function() map.get("non-object key") == undefined);
check(function() map.get() == undefined);
check(function() map.delete("non-object key") == false);
check(function() map.delete() == false);
check(function() map.set(key) === map);
check(function() map.get(key) == undefined);
checkThrows(function() map.set("non-object key", value));
print ("done");
reportCompare(0, TestFailCount, "weak map tests");

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