Merge mozilla-central to autoland

--HG--
extra : rebase_source : 46bdde97ce6567ce8a06b1609de547ef11b24c2a
This commit is contained in:
Carsten "Tomcat" Book 2017-04-20 13:06:52 +02:00
commit 87edaf78ee
77 changed files with 59477 additions and 68323 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,6 @@ DevToolsModules(
'debugger.css',
'debugger.js',
'panel.js',
'parser-worker.js',
'pretty-print-worker.js',
'source-map-worker.js'
)

View File

@ -4,7 +4,7 @@
"use strict";
const { Task } = require("devtools/shared/task");
var { LocalizationHelper } = require("devtools/shared/l10n");
var {LocalizationHelper} = require("devtools/shared/l10n");
const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
var L10N = new LocalizationHelper(DBG_STRINGS_URI);
@ -16,46 +16,38 @@ function DebuggerPanel(iframeWindow, toolbox) {
}
DebuggerPanel.prototype = {
open: async function() {
open: Task.async(function* () {
if (!this.toolbox.target.isRemote) {
await this.toolbox.target.makeRemote();
yield this.toolbox.target.makeRemote();
}
const {
actions,
store,
selectors,
client
} = await this.panelWin.Debugger.bootstrap({
yield this.panelWin.Debugger.bootstrap({
threadClient: this.toolbox.threadClient,
tabTarget: this.toolbox.target,
debuggerClient: this.toolbox.target._client,
sourceMaps: this.toolbox.sourceMapService
tabTarget: this.toolbox.target
});
this._actions = actions;
this._store = store;
this._selectors = selectors;
this._client = client;
this.isReady = true;
return this;
}),
_store: function () {
return this.panelWin.Debugger.store;
},
getVarsForTests() {
return {
store: this._store,
selectors: this._selectors,
actions: this._actions,
client: this._client
};
_getState: function () {
return this._store().getState();
},
_getState: function() {
return this._store.getState();
_actions: function () {
return this.panelWin.Debugger.actions;
},
getFrames: function() {
let frames = this._selectors.getFrames(this._getState());
_selectors: function () {
return this.panelWin.Debugger.selectors;
},
getFrames: function () {
let frames = this._selectors().getFrames(this._getState());
// Frames is null when the debugger is not paused.
if (!frames) {
@ -66,7 +58,7 @@ DebuggerPanel.prototype = {
}
frames = frames.toJS();
const selectedFrame = this._selectors.getSelectedFrame(this._getState());
const selectedFrame = this._selectors().getSelectedFrame(this._getState());
const selected = frames.findIndex(frame => frame.id == selectedFrame.id);
frames.forEach(frame => {
@ -76,15 +68,7 @@ DebuggerPanel.prototype = {
return { frames, selected };
},
selectSource(sourceURL, sourceLine) {
this._actions.selectSourceURL(sourceURL, { line: sourceLine });
},
getSource(sourceURL) {
return this._selectors.getSourceByURL(this._getState(), sourceURL);
},
destroy: function() {
destroy: function () {
this.panelWin.Debugger.destroy();
this.emit("destroyed");
}

File diff suppressed because one or more lines are too long

View File

@ -55,7 +55,7 @@ return /******/ (function(modules) { // webpackBootstrap
/***/ 0:
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(964);
module.exports = __webpack_require__(801);
/***/ },
@ -75,6 +75,76 @@ return /******/ (function(modules) { // webpackBootstrap
/***/ },
/***/ 801:
/***/ function(module, exports, __webpack_require__) {
"use strict";
var prettyFast = __webpack_require__(802);
var assert = __webpack_require__(223);
function prettyPrint(_ref) {
var url = _ref.url,
indent = _ref.indent,
source = _ref.source;
try {
var prettified = prettyFast(source, {
url: url,
indent: " ".repeat(indent)
});
return {
code: prettified.code,
mappings: prettified.map._mappings
};
} catch (e) {
throw new Error(`${e.message}\n${e.stack}`);
}
}
function invertMappings(mappings) {
return mappings._array.map(m => {
var mapping = {
generated: {
line: m.originalLine,
column: m.originalColumn
}
};
if (m.source) {
mapping.source = m.source;
mapping.original = {
line: m.generatedLine,
column: m.generatedColumn
};
mapping.name = m.name;
}
return mapping;
});
}
self.onmessage = function (msg) {
var _msg$data = msg.data,
id = _msg$data.id,
args = _msg$data.args;
assert(msg.data.method === "prettyPrint", "Method must be `prettyPrint`");
try {
var _prettyPrint = prettyPrint(args[0]),
code = _prettyPrint.code,
mappings = _prettyPrint.mappings;
self.postMessage({ id, response: {
code, mappings: invertMappings(mappings)
} });
} catch (e) {
self.postMessage({ id, error: e });
}
};
/***/ },
/***/ 802:
/***/ function(module, exports, __webpack_require__) {
@ -5848,80 +5918,6 @@ return /******/ (function(modules) { // webpackBootstrap
}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ },
/***/ 964:
/***/ function(module, exports, __webpack_require__) {
"use strict";
var prettyFast = __webpack_require__(802);
var assert = __webpack_require__(223);
function prettyPrint(_ref) {
var url = _ref.url,
indent = _ref.indent,
source = _ref.source;
try {
var prettified = prettyFast(source, {
url: url,
indent: " ".repeat(indent)
});
return {
code: prettified.code,
mappings: prettified.map._mappings
};
} catch (e) {
throw new Error(`${e.message}\n${e.stack}`);
}
}
function invertMappings(mappings) {
return mappings._array.map(m => {
var mapping = {
generated: {
line: m.originalLine,
column: m.originalColumn
}
};
if (m.source) {
mapping.source = m.source;
mapping.original = {
line: m.generatedLine,
column: m.generatedColumn
};
mapping.name = m.name;
}
return mapping;
});
}
self.onmessage = function (msg) {
var _msg$data = msg.data,
id = _msg$data.id,
args = _msg$data.args;
assert(msg.data.method === "prettyPrint", "Method must be `prettyPrint`");
try {
var _prettyPrint = prettyPrint(args[0]),
code = _prettyPrint.code,
mappings = _prettyPrint.mappings;
self.postMessage({
id,
response: {
code,
mappings: invertMappings(mappings)
}
});
} catch (e) {
self.postMessage({ id, error: e });
}
};
/***/ }
/******/ })

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,6 @@ support-files =
[browser_dbg-breakpoints.js]
[browser_dbg-breakpoints-cond.js]
[browser_dbg-call-stack.js]
[browser_dbg-expressions.js]
[browser_dbg-scopes.js]
[browser_dbg-chrome-create.js]
[browser_dbg-chrome-debugging.js]

View File

@ -54,6 +54,6 @@ add_task(function* () {
button = toggleButton(dbg);
frames = findAllElements(dbg, "frames");
is(button.innerText, "Collapse Rows", "toggle button should be collapsed");
is(button.innerText, "Collapse Rows", "toggle button should be collapse");
is(frames.length, 22, "All of the frames should be shown");
});

View File

@ -18,9 +18,7 @@ add_task(function* () {
// Wait for the source text to load and make sure we're in the right
// place.
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
// TODO: revisit highlighting lines when the debugger opens
//assertHighlightLocation(dbg, "long.js", 66);
assertHighlightLocation(dbg, "long.js", 66);
// Jump to line 16 and make sure the editor scrolled.
yield selectSource(dbg, "long.js", 16);

View File

@ -50,5 +50,5 @@ add_task(function* () {
invokeInTab("testModel");
yield waitForPaused(dbg);
assertPausedLocation(dbg, longSrc, 66);
ok(isElementVisible(dbg, "breakpoint"), "Breakpoint is visible");
// ok(isElementVisible(dbg, "breakpoint"), "Breakpoint is visible");
});

View File

@ -1,56 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* tests the watch expressions component
* 1. add watch expressions
* 2. edit watch expressions
* 3. delete watch expressions
*/
const expressionSelectors = {
input: "input.input-expression"
};
function getLabel(dbg, index) {
return findElement(dbg, "expressionNode", index).innerText;
}
function getValue(dbg, index) {
return findElement(dbg, "expressionValue", index).innerText;
}
async function addExpression(dbg, input) {
info("Adding an expression");
findElementWithSelector(dbg, expressionSelectors.input).focus();
type(dbg, input);
pressKey(dbg, "Enter");
await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
}
async function editExpression(dbg, input) {
info("updating the expression");
dblClickElement(dbg, "expressionNode", 1);
type(dbg, input);
pressKey(dbg, "Enter");
await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
}
const {
setupTestRunner,
expressions
} = require("devtools/client/debugger/new/integration-tests");
add_task(function*() {
const dbg = yield initDebugger("doc-script-switching.html");
invokeInTab("firstCall");
yield waitForPaused(dbg);
yield addExpression(dbg, "f");
is(getLabel(dbg, 1), "f");
is(getValue(dbg, 1), "ReferenceError");
yield editExpression(dbg, "oo");
is(getLabel(dbg, 1), "foo()");
is(getValue(dbg, 1), "");
yield deleteExpression(dbg, "foo");
is(findAllElements(dbg, "expressionNodes").length, 0);
setupTestRunner(this);
yield expressions(this);
});

View File

@ -20,6 +20,7 @@ add_task(function* () {
const dbg = yield initDebugger("doc-exceptions.html");
// test skipping an uncaught exception
yield togglePauseOnExceptions(dbg, false, false);
yield uncaughtException();
ok(!isPaused(dbg));

View File

@ -3,7 +3,7 @@
// Tests basic pretty-printing functionality.
add_task(function*() {
add_task(function* () {
const dbg = yield initDebugger("doc-minified.html");
yield selectSource(dbg, "math.min.js");

View File

@ -19,13 +19,4 @@ add_task(function* () {
is(getLabel(dbg, 1), "secondCall");
is(getLabel(dbg, 2), "<this>");
is(getLabel(dbg, 4), "foo()");
toggleNode(dbg, 4);
yield waitForDispatch(dbg, "LOAD_OBJECT_PROPERTIES");
is(getLabel(dbg, 5), "prototype");
yield stepOver(dbg);
is(getLabel(dbg, 4), "foo()");
is(getLabel(dbg, 5), "prototype");
});

View File

@ -4,10 +4,7 @@
// Test that an error while loading a sourcemap does not break
// debugging.
add_task(function*() {
// NOTE: the CORS call makes the test run times inconsistent
requestLongerTimeout(2);
add_task(function* () {
const dbg = yield initDebugger("doc-sourcemap-bogus.html");
const { selectors: { getSources }, getState } = dbg;

View File

@ -4,10 +4,7 @@
// Tests loading sourcemapped sources, setting breakpoints, and
// stepping in them.
add_task(function*() {
// NOTE: the CORS call makes the test run times inconsistent
requestLongerTimeout(2);
add_task(function* () {
const dbg = yield initDebugger("doc-sourcemaps.html");
const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
@ -16,27 +13,21 @@ add_task(function*() {
const entrySrc = findSource(dbg, "entry.js");
yield selectSource(dbg, entrySrc);
ok(
dbg.win.cm.getValue().includes("window.keepMeAlive"),
"Original source text loaded correctly"
);
ok(dbg.win.cm.getValue().includes("window.keepMeAlive"),
"Original source text loaded correctly");
// Test that breakpoint sliding is not attempted. The breakpoint
// should not move anywhere.
yield addBreakpoint(dbg, entrySrc, 13);
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
ok(
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
"Breakpoint has correct line"
);
ok(getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
"Breakpoint has correct line");
// Test breaking on a breakpoint
yield addBreakpoint(dbg, "entry.js", 15);
is(getBreakpoints(getState()).size, 2, "Two breakpoints exist");
ok(
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 15 }),
"Breakpoint has correct line"
);
ok(getBreakpoint(getState(), { sourceId: entrySrc.id, line: 15 }),
"Breakpoint has correct line");
invokeInTab("keepMeAlive");
yield waitForPaused(dbg);

View File

@ -6,10 +6,7 @@
// This source map does not have source contents, so it's fetched separately
add_task(function*() {
// NOTE: the CORS call makes the test run times inconsistent
requestLongerTimeout(2);
add_task(function* () {
const dbg = yield initDebugger("doc-sourcemaps2.html");
const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
@ -23,10 +20,8 @@ add_task(function*() {
// Test that breakpoint is not off by a line.
yield addBreakpoint(dbg, mainSrc, 4);
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
ok(
getBreakpoint(getState(), { sourceId: mainSrc.id, line: 4 }),
"Breakpoint has correct line"
);
ok(getBreakpoint(getState(), { sourceId: mainSrc.id, line: 4 }),
"Breakpoint has correct line");
invokeInTab("logMessage");

View File

@ -6,7 +6,6 @@
<body>
<script>
debugger;
// This inline script allows this HTML page to show up as a
// source. It also needs to introduce a new global variable so
// it's not immediately garbage collected.

View File

@ -33,14 +33,13 @@
*/
// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this
);
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
var { Toolbox } = require("devtools/client/framework/toolbox");
const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
Services.prefs.clearUserPref("devtools.debugger.tabs")
Services.prefs.clearUserPref("devtools.debugger.pending-selected-location")
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
@ -77,9 +76,9 @@ function _afterDispatchDone(store, type) {
type: "@@service/waitUntil",
predicate: action => {
if (action.type === type) {
return action.status
? action.status === "done" || action.status === "error"
: true;
return action.status ?
(action.status === "done" || action.status === "error") :
true;
}
},
run: (dispatch, getState, action) => {
@ -103,7 +102,7 @@ function _afterDispatchDone(store, type) {
function waitForDispatch(dbg, type, eventRepeat = 1) {
let count = 0;
return Task.spawn(function*() {
return Task.spawn(function* () {
info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
while (count < eventRepeat) {
yield _afterDispatchDone(dbg.store, type);
@ -171,23 +170,21 @@ function waitForSources(dbg, ...sources) {
info("Waiting on sources: " + sources.join(", "));
const { selectors: { getSources }, store } = dbg;
return Promise.all(
sources.map(url => {
function sourceExists(state) {
return getSources(state).some(s => {
return s.get("url").includes(url);
});
}
return Promise.all(sources.map(url => {
function sourceExists(state) {
return getSources(state).some(s => {
return s.get("url").includes(url);
});
}
if (!sourceExists(store.getState())) {
return waitForState(dbg, sourceExists);
}
})
);
if (!sourceExists(store.getState())) {
return waitForState(dbg, sourceExists);
}
}));
}
function waitForElement(dbg, selector) {
return waitUntil(() => findElementWithSelector(dbg, selector));
return waitUntil(() => findElementWithSelector(dbg, selector))
}
/**
@ -212,10 +209,8 @@ function assertPausedLocation(dbg, source, line) {
is(location.get("line"), line);
// Check the debug line
ok(
dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
"Line is highlighted as paused"
);
ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
"Line is highlighted as paused");
}
/**
@ -237,14 +232,10 @@ function assertHighlightLocation(dbg, source, line) {
// Check the highlight line
const lineEl = findElement(dbg, "highlightLine");
ok(lineEl, "Line is highlighted");
ok(
isVisibleWithin(findElement(dbg, "codeMirror"), lineEl),
"Highlighted line is visible"
);
ok(
dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"),
"Line is highlighted"
);
// ok(isVisibleWithin(findElement(dbg, "codeMirror"), lineEl),
// "Highlighted line is visible");
ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"),
"Line is highlighted");
}
/**
@ -267,11 +258,12 @@ function isPaused(dbg) {
* @static
*/
function waitForPaused(dbg) {
return Task.spawn(function*() {
return Task.spawn(function* () {
// We want to make sure that we get both a real paused event and
// that the state is fully populated. The client may do some more
// work (call other client methods) before populating the state.
yield waitForThreadEvents(dbg, "paused"), yield waitForState(dbg, state => {
yield waitForThreadEvents(dbg, "paused"),
yield waitForState(dbg, state => {
const pause = dbg.selectors.getPause(state);
// Make sure we have the paused state.
if (!pause) {
@ -287,16 +279,15 @@ function waitForPaused(dbg) {
}
function createDebuggerContext(toolbox) {
const panel = toolbox.getPanel("jsdebugger");
const win = panel.panelWin;
const { store, client, selectors, actions } = panel.getVarsForTests();
const win = toolbox.getPanel("jsdebugger").panelWin;
const store = win.Debugger.store;
return {
actions: actions,
selectors: selectors,
actions: win.Debugger.actions,
selectors: win.Debugger.selectors,
getState: store.getState,
store: store,
client: client,
client: win.Debugger.client,
toolbox: toolbox,
win: win
};
@ -312,13 +303,9 @@ function createDebuggerContext(toolbox) {
* @static
*/
function initDebugger(url, ...sources) {
return Task.spawn(function*() {
Services.prefs.clearUserPref("devtools.debugger.pause-on-exceptions");
Services.prefs.clearUserPref("devtools.debugger.ignore-caught-exceptions");
Services.prefs.clearUserPref("devtools.debugger.tabs");
Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
Services.prefs.clearUserPref("devtools.debugger.pending-breakpoints");
Services.prefs.clearUserPref("devtools.debugger.expressions");
return Task.spawn(function* () {
Services.prefs.clearUserPref("devtools.debugger.tabs")
Services.prefs.clearUserPref("devtools.debugger.pending-selected-location")
const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
return createDebuggerContext(toolbox);
});
@ -442,11 +429,6 @@ function resume(dbg) {
return waitForThreadEvents(dbg, "resumed");
}
function deleteExpression(dbg, input) {
info("Resuming");
return dbg.actions.deleteExpression({ input });
}
/**
* Reloads the debuggee.
*
@ -518,11 +500,8 @@ function removeBreakpoint(dbg, sourceId, line, col) {
* @return {Promise}
* @static
*/
function togglePauseOnExceptions(
dbg,
pauseOnExceptions,
ignoreCaughtExceptions
) {
function togglePauseOnExceptions(dbg,
pauseOnExceptions, ignoreCaughtExceptions) {
const command = dbg.actions.pauseOnExceptions(
pauseOnExceptions,
ignoreCaughtExceptions
@ -547,7 +526,7 @@ function togglePauseOnExceptions(
*/
function invokeInTab(fnc) {
info(`Invoking function ${fnc} in tab`);
return ContentTask.spawn(gBrowser.selectedBrowser, fnc, function*(fnc) {
return ContentTask.spawn(gBrowser.selectedBrowser, fnc, function* (fnc) {
content.wrappedJSObject[fnc](); // eslint-disable-line mozilla/no-cpows-in-tests, max-len
});
}
@ -555,21 +534,18 @@ function invokeInTab(fnc) {
const isLinux = Services.appinfo.OS === "Linux";
const cmdOrCtrl = isLinux ? { ctrlKey: true } : { metaKey: true };
const keyMappings = {
sourceSearch: { code: "p", modifiers: cmdOrCtrl },
fileSearch: { code: "f", modifiers: cmdOrCtrl },
Enter: { code: "VK_RETURN" },
Up: { code: "VK_UP" },
Down: { code: "VK_DOWN" },
Tab: { code: "VK_TAB" },
Escape: { code: "VK_ESCAPE" },
sourceSearch: { code: "p", modifiers: cmdOrCtrl},
fileSearch: { code: "f", modifiers: cmdOrCtrl},
"Enter": { code: "VK_RETURN" },
"Up": { code: "VK_UP" },
"Down": { code: "VK_DOWN" },
"Tab": { code: "VK_TAB" },
"Escape": { code: "VK_ESCAPE" },
pauseKey: { code: "VK_F8" },
resumeKey: { code: "VK_F8" },
stepOverKey: { code: "VK_F10" },
stepInKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux } },
stepOutKey: {
code: "VK_F11",
modifiers: { ctrlKey: isLinux, shiftKey: true }
}
stepInKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux }},
stepOutKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux, shiftKey: true }}
};
/**
@ -585,7 +561,11 @@ function pressKey(dbg, keyName) {
let keyEvent = keyMappings[keyName];
const { code, modifiers } = keyEvent;
return EventUtils.synthesizeKey(code, modifiers || {}, dbg.win);
return EventUtils.synthesizeKey(
code,
modifiers || {},
dbg.win
);
}
function type(dbg, string) {
@ -597,19 +577,14 @@ function type(dbg, string) {
function isVisibleWithin(outerEl, innerEl) {
const innerRect = innerEl.getBoundingClientRect();
const outerRect = outerEl.getBoundingClientRect();
return innerRect.top > outerRect.top && innerRect.bottom < outerRect.bottom;
return innerRect.top > outerRect.top &&
innerRect.bottom < outerRect.bottom;
}
const selectors = {
callStackHeader: ".call-stack-pane ._header",
callStackBody: ".call-stack-pane .pane",
expressionNode: i =>
`.expressions-list .tree-node:nth-child(${i}) .object-label`,
expressionValue: i =>
`.expressions-list .tree-node:nth-child(${i}) .object-value`,
expressionClose: i =>
`.expressions-list .expression-container:nth-child(${i}) .close`,
expressionNodes: ".expressions-list .tree-node",
scopesHeader: ".scopes-pane ._header",
breakpointItem: i => `.breakpoints-list .breakpoint:nth-child(${i})`,
scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
@ -630,7 +605,7 @@ const selectors = {
sourceFooter: ".source-footer",
sourceNode: i => `.sources-list .tree-node:nth-child(${i})`,
sourceNodes: ".sources-list .tree-node",
sourceArrow: i => `.sources-list .tree-node:nth-child(${i}) .arrow`
sourceArrow: i => `.sources-list .tree-node:nth-child(${i}) .arrow`,
};
function getSelector(elementName, ...args) {
@ -672,9 +647,6 @@ function findAllElements(dbg, elementName, ...args) {
*/
function clickElement(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args);
const el = findElement(dbg, elementName, ...args);
el.scrollIntoView();
return EventUtils.synthesizeMouseAtCenter(
findElementWithSelector(dbg, selector),
{},
@ -682,22 +654,12 @@ function clickElement(dbg, elementName, ...args) {
);
}
function dblClickElement(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args);
return EventUtils.synthesizeMouseAtCenter(
findElementWithSelector(dbg, selector),
{ clickCount: 2 },
dbg.win
);
}
function rightClickElement(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args);
const doc = dbg.win.document;
return EventUtils.synthesizeMouseAtCenter(
doc.querySelector(selector),
{ type: "contextmenu" },
{type: "contextmenu"},
dbg.win
);
}
@ -707,10 +669,10 @@ function selectMenuItem(dbg, index) {
const doc = dbg.toolbox.win.document;
// there are several context menus, we want the one with the menu-api
const popup = doc.querySelector('menupopup[menu-api="true"]');
const popup = doc.querySelector("menupopup[menu-api=\"true\"]");
const item = popup.querySelector(`menuitem:nth-child(${index})`);
return EventUtils.synthesizeMouseAtCenter(item, {}, dbg.toolbox.win);
return EventUtils.synthesizeMouseAtCenter(item, {}, dbg.toolbox.win );
}
/**

View File

@ -124,10 +124,6 @@ blackBoxCheckboxTooltip=Toggle black boxing
# searching all the source files the debugger has seen.
sources.search.key=P
# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger
# does not have any sources.
sources.noSourcesAvailable=This page has no sources
# LOCALIZATION NOTE (sources.searchAlt.key): Alternate key shortcut to open
# the search for searching all the source files the debugger has seen.
sources.searchAlt.key=O
@ -239,17 +235,10 @@ callStack.expand=Expand Rows
# for the summarizing the selected search result. e.g. 5 of 10 results.
editor.searchResults=%d of %d results
# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.
editor.singleResult=1 result
# LOCALIZATION NOTE (editor.noResults): Editor Search bar message
# for when no results found.
editor.noResults=no results
# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for
# toggling search type buttons(function search, variable search)
editor.searchTypeToggleTitle=Search for:
# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item
# for adding a breakpoint on a line.
editor.addBreakpoint=Add Breakpoint
@ -282,9 +271,9 @@ editor.conditionalPanel.placeholder=This breakpoint will pause when the expressi
# close button inside ConditionalPanel component
editor.conditionalPanel.close=Cancel edit breakpoint and close
# LOCALIZATION NOTE (editor.jumpToMappedLocation): Context menu item
# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item
# for navigating to a source mapped location
editor.jumpToMappedLocation=Jump to %s location
editor.jumpToMappedLocation1=Jump to %S location
# LOCALIZATION NOTE (generated): Source Map term for a server source location
generated=generated
@ -352,22 +341,6 @@ sourceTabs.prettyPrint=Pretty Print Source
# the editor context menu.
sourceTabs.prettyPrint.accesskey=p
# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated
# with the black box button
sourceFooter.blackbox=Blackbox Source
# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated
# with the black box button
sourceFooter.unblackbox=Unblackbox Source
# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to black box
# an associated source
sourceFooter.blackbox.accesskey=b
# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated
# with a blackboxed source
sourceFooter.blackboxed=Blackboxed Source
# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed
# for close tab button in source tabs.
sourceTabs.closeTabButtonTooltip=Close tab
@ -420,6 +393,10 @@ sourceSearch.search=Search Sources…
# message when the query did not match any of the sources.
sourceSearch.noResults=No files matching %S found
# LOCALIZATION NOTE (sourceFooter.debugBtnTooltip): Tooltip text associated
# with the pretty-print button
sourceFooter.debugBtnTooltip=Prettify Source
# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip
# when the debugger will not pause on exceptions.
ignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions
@ -531,30 +508,8 @@ watchExpressionsSeparatorLabel2=\u0020→
# in the functions search panel as a separator between function's inferred name
# and its real name (if available).
functionSearchSeparatorLabel=
# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder
# text displayed when the user searches for functions in a file
symbolSearch.search.functionsPlaceholder=Search functions…
# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder
# text displayed when the user searches for variables in a file
symbolSearch.search.variablesPlaceholder=Search variables…
# LOCALIZATION NOTE(symbolSearch.search.key): The shortcut (cmd+shift+o) for
# searching for a function or variable
symbolSearch.search.key=O
# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option
# when searching text in a file
symbolSearch.searchModifier.regex=Regex
# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option
# when searching text in a file
symbolSearch.searchModifier.caseSensitive=Case sensitive
# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option
# when searching text in a file
symbolSearch.searchModifier.wholeWord=Whole word
functionSearch.search.placeholder=Search Functions…
functionSearch.search.key=O
# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
# as a description in the notification panel popup, when multiple debuggers are
@ -622,7 +577,3 @@ whyPaused.debugCommand=Paused on debugged function
# in a info block explaining how the debugger is currently paused on an event
# listener breakpoint set
whyPaused.other=Debugger paused
# LOCALIZATION NOTE (ctrl): The text that is used for documenting
# keyboard shortcuts that use the control key
ctrl=Ctrl

View File

@ -1,5 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
# -*- indent-tabs-mode: nil; js-indent-level: 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/.
#ifdef RELEASE_OR_BETA
pref("devtools.debugger.new-debugger-frontend", false);
@ -15,7 +17,7 @@ pref("devtools.debugger.chrome-debugging-websocket", false);
pref("devtools.debugger.remote-host", "localhost");
pref("devtools.debugger.remote-timeout", 20000);
pref("devtools.debugger.pause-on-exceptions", false);
pref("devtools.debugger.ignore-caught-exceptions", false);
pref("devtools.debugger.ignore-caught-exceptions", true);
pref("devtools.debugger.source-maps-enabled", true);
pref("devtools.debugger.client-source-maps-enabled", true);
pref("devtools.debugger.pretty-print-enabled", true);
@ -36,5 +38,4 @@ pref("devtools.debugger.start-panel-collapsed", false);
pref("devtools.debugger.end-panel-collapsed", false);
pref("devtools.debugger.tabs", "[]");
pref("devtools.debugger.pending-selected-location", "{}");
pref("devtools.debugger.pending-breakpoints", "[]");
pref("devtools.debugger.expressions", "[]");

View File

@ -59,10 +59,10 @@ exports.viewSourceInDebugger = Task.async(function* (toolbox, sourceURL, sourceL
// New debugger frontend
if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
const source = dbg.getSource(sourceURL);
const source = dbg._selectors().getSourceByURL(dbg._getState(), sourceURL);
if (source) {
yield toolbox.selectTool("jsdebugger");
dbg.selectSource(sourceURL, sourceLine);
dbg._actions().selectSourceURL(sourceURL, { line: sourceLine });
return true;
}

View File

@ -67,7 +67,7 @@ function* checkClickOnNode(hud, toolbox, frameLinkNode) {
let dbg = toolbox.getPanel("jsdebugger");
is(
dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
dbg._selectors().getSelectedSource(dbg._getState()).get("url"),
url,
"expected source url"
);

View File

@ -56,7 +56,7 @@ function* checkClickOnNode(hud, toolbox, frameLinkNode) {
let url = frameLinkNode.getAttribute("data-url");
let dbg = toolbox.getPanel("jsdebugger");
is(
dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
dbg._selectors().getSelectedSource(dbg._getState()).get("url"),
url,
`Debugger is opened at expected source url (${url})`
);

View File

@ -81,7 +81,7 @@ function test() {
let toolbox = yield gDevTools.getToolbox(hud.target);
let dbg = toolbox.getPanel("jsdebugger");
is(dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
is(dbg._selectors().getSelectedSource(dbg._getState()).get("url"),
url,
"expected source url");
}

View File

@ -8790,6 +8790,59 @@ nsContentUtils::StorageAllowedForPrincipal(nsIPrincipal* aPrincipal)
return InternalStorageAllowedForPrincipal(aPrincipal, nullptr);
}
// static, private
void
nsContentUtils::GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
uint32_t* aLifetimePolicy,
uint32_t* aBehavior)
{
*aLifetimePolicy = sCookiesLifetimePolicy;
*aBehavior = sCookiesBehavior;
// Any permissions set for the given principal will override our default
// settings from preferences.
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return;
}
uint32_t perm;
permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
switch (perm) {
case nsICookiePermission::ACCESS_ALLOW:
*aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
*aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
break;
case nsICookiePermission::ACCESS_DENY:
*aBehavior = nsICookieService::BEHAVIOR_REJECT;
*aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
break;
case nsICookiePermission::ACCESS_SESSION:
*aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
*aLifetimePolicy = nsICookieService::ACCEPT_SESSION;
break;
case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
*aBehavior = nsICookieService::BEHAVIOR_REJECT_FOREIGN;
// NOTE: The decision was made here to override the lifetime policy to be
// ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
// prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
// specific domain. As BEHAVIOR_REJECT_FOREIGN isn't visible in our UI,
// this is probably not an issue.
*aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
break;
case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
*aBehavior = nsICookieService::BEHAVIOR_LIMIT_FOREIGN;
// NOTE: The decision was made here to override the lifetime policy to be
// ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
// prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
// specific domain. As BEHAVIOR_LIMIT_FOREIGN isn't visible in our UI,
// this is probably not an issue.
*aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
break;
}
}
// static, private
nsContentUtils::StorageAccess
nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
@ -8819,28 +8872,12 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return StorageAccess::eDeny;
}
// check the permission manager for any allow or deny permissions
// for cookies for the window.
uint32_t perm;
permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
if (perm == nsIPermissionManager::DENY_ACTION) {
return StorageAccess::eDeny;
}
if (perm == nsICookiePermission::ACCESS_SESSION) {
return std::min(access, StorageAccess::eSessionScoped);
}
if (perm == nsIPermissionManager::ALLOW_ACTION) {
return access;
}
uint32_t lifetimePolicy;
uint32_t behavior;
GetCookieBehaviorForPrincipal(aPrincipal, &lifetimePolicy, &behavior);
// Check if we should only allow storage for the session, and record that fact
if (sCookiesLifetimePolicy == nsICookieService::ACCEPT_SESSION) {
if (lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
// Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
// so perform a std::min comparison to make sure we preserve ePrivateBrowsing
// if it has been set.
@ -8881,13 +8918,13 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
}
// We don't want to prompt for every attempt to access permissions.
if (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT) {
if (behavior == nsICookieService::BEHAVIOR_REJECT) {
return StorageAccess::eDeny;
}
// In the absense of a window, we assume that we are first-party.
if (aWindow && (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
sCookiesBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN)) {
if (aWindow && (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN)) {
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID);
MOZ_ASSERT(thirdPartyUtil);

View File

@ -2911,6 +2911,16 @@ private:
CallOnRemoteChildFunction aCallback,
void* aArg);
/**
* Gets the current cookie lifetime policy and cookie behavior for a given
* principal by checking with preferences and the permission manager.
*
* Used in the implementation of InternalStorageAllowedForPrincipal.
*/
static void GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
uint32_t* aLifetimePolicy,
uint32_t* aBehavior);
/*
* Checks if storage for a given principal is permitted by the user's
* preferences. If aWindow is non-null, its principal must be passed as

View File

@ -1376,8 +1376,6 @@ nsIDocument::nsIDocument()
mUserHasInteracted(false)
{
SetIsInDocument();
PR_INIT_CLIST(&mDOMMediaQueryLists);
}
nsDocument::nsDocument(const char* aContentType)
@ -1457,7 +1455,7 @@ nsDocument::ClearAllBoxObjects()
nsIDocument::~nsIDocument()
{
MOZ_ASSERT(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
"must not have media query lists left");
if (mNodeInfoManager) {
@ -1858,9 +1856,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
// We own only the items in mDOMMediaQueryLists that have listeners;
// this reference is managed by their AddListener and RemoveListener
// methods.
for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
MediaQueryList *mql = static_cast<MediaQueryList*>(l);
for (auto mql : tmp->mDOMMediaQueryLists) {
if (mql->HasListeners()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
cb.NoteXPCOMChild(mql);
@ -1974,12 +1970,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
// We own only the items in mDOMMediaQueryLists that have listeners;
// this reference is managed by their AddListener and RemoveListener
// methods.
for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
l != &tmp->mDOMMediaQueryLists; ) {
PRCList *next = PR_NEXT_LINK(l);
MediaQueryList *mql = static_cast<MediaQueryList*>(l);
for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
MediaQueryList* next = mql->getNext();
mql->Disconnect();
l = next;
mql = next;
}
tmp->mInUnlinkOrDeletion = false;
@ -7182,8 +7176,7 @@ nsIDocument::MatchMedia(const nsAString& aMediaQueryList)
{
RefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
// Insert the new item at the end of the linked list.
PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
mDOMMediaQueryLists.insertBack(result);
return result.forget();
}

View File

@ -31,7 +31,6 @@
#include "nsContentListDeclarations.h"
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include "prclist.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/DispatcherTrait.h"
#include "mozilla/LinkedList.h"
@ -1875,8 +1874,8 @@ public:
already_AddRefed<mozilla::dom::MediaQueryList>
MatchMedia(const nsAString& aMediaQueryList);
const PRCList* MediaQueryLists() const {
return &mDOMMediaQueryLists;
mozilla::LinkedList<mozilla::dom::MediaQueryList>& MediaQueryLists() {
return mDOMMediaQueryLists;
}
/**
@ -3375,7 +3374,7 @@ protected:
uint32_t mBlockDOMContentLoaded;
// Our live MediaQueryLists
PRCList mDOMMediaQueryLists;
mozilla::LinkedList<mozilla::dom::MediaQueryList> mDOMMediaQueryLists;
// Flags for use counters used directly by this document.
std::bitset<mozilla::eUseCounter_Count> mUseCounters;

View File

@ -919,10 +919,6 @@ nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
rv = hasher->Finish(true, hash);
NS_ENSURE_SUCCESS(rv, false);
// The NSS Base64 encoder automatically adds linebreaks "\r\n" every 64
// characters. We need to remove these so we can properly validate longer
// (SHA-512) base64-encoded hashes
hash.StripChars("\r\n");
return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
}

View File

@ -0,0 +1,10 @@
<script>
try {
sessionStorage.setItem("am_i_blocked", "nope");
window.parent.postMessage('sessionStorage=true', '*');
document.body.innerHTML += 'yes';
} catch (ex) {
window.parent.postMessage('sessionStorage=false', '*');
document.body.innerHTML += 'no';
}
</script>

View File

@ -16,6 +16,7 @@ support-files =
interOriginTest2.js
localStorageCommon.js
frameLocalStorageSessionOnly.html
file_tryAccessSessionStorage.html
[test_brokenUTF-16.html]
[test_bug600307-DBOps.html]
@ -50,3 +51,4 @@ skip-if = toolkit == 'android'
[test_lowDeviceStorage.html]
[test_storageConstructor.html]
[test_localStorageSessionPrefOverride.html]
[test_firstPartyOnlyPermission.html]

View File

@ -0,0 +1,62 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>first party storage permission test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
TRY_ACCESS_SESSION_STORAGE =
'http://example.com/tests/dom/tests/mochitest/localstorage/file_tryAccessSessionStorage.html';
add_task(function*() {
yield SpecialPowers.pushPrefEnv({
set: [['network.cookie.cookieBehavior', SpecialPowers.Ci.nsICookieService.BEHAVIOR_REJECT]],
});
try {
sessionStorage.setItem("blocked", "blocked");
ok(false, "Shouldn't be avaliable yet");
} catch (ex) {
ok(true, "Shouldn't be avaliable yet");
}
yield new Promise(resolve => SpecialPowers.pushPermissions([{
type: 'cookie',
allow: SpecialPowers.Ci.nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY,
context: document,
}], resolve));
// With the permission set to ACCESS_ALLOW_FIRST_PARTY_ONLY, we should be
// able to run it from this iframe (as we are first party with the test
// runner parent document).
try {
sessionStorage.setItem("blocked", "blocked");
ok(true, "Should be avaliable");
} catch (ex) {
ok(false, "Should be avaliable");
}
// A third party iframe should not have access however.
yield new Promise(resolve => {
window.onmessage = evt => {
window.onmessage = null;
is(evt.data, "sessionStorage=false");
resolve();
};
let iframe = document.createElement('iframe');
iframe.setAttribute('src', TRY_ACCESS_SESSION_STORAGE);
document.body.appendChild(iframe);
});
});
</script>
</head>
<body>
</body>
</html>

View File

@ -33,7 +33,6 @@
#include "ScopedGLHelpers.h"
#include "SharedSurfaceGL.h"
#include "GfxTexturesReporter.h"
#include "TextureGarbageBin.h"
#include "gfx2DGlue.h"
#include "gfxPrefs.h"
#include "mozilla/IntegerPrintfMacros.h"
@ -947,8 +946,6 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
mCaps.alpha = false;
}
mTexGarbageBin = new TextureGarbageBin(this);
MOZ_ASSERT(IsCurrent());
if (ShouldSpew() && IsExtensionSupported(KHR_debug)) {
@ -2106,9 +2103,7 @@ GLContext::MarkDestroyed()
mBlitHelper = nullptr;
mReadTexImageHelper = nullptr;
if (MakeCurrent()) {
mTexGarbageBin->GLContextTeardown();
} else {
if (!MakeCurrent()) {
NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
}
@ -2388,12 +2383,6 @@ GLContext::CleanDirtyScreen()
AfterGLReadCall();
}
void
GLContext::EmptyTexGarbageBin()
{
TexGarbageBin()->EmptyGarbage();
}
bool
GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const
{

View File

@ -63,7 +63,6 @@ namespace mozilla {
class GLReadTexImageHelper;
class GLScreenBuffer;
class SharedSurface;
class TextureGarbageBin;
struct SurfaceCaps;
} // namespace gl
@ -3457,17 +3456,6 @@ public:
bool IsDrawingToDefaultFramebuffer();
protected:
RefPtr<TextureGarbageBin> mTexGarbageBin;
public:
TextureGarbageBin* TexGarbageBin() {
MOZ_ASSERT(mTexGarbageBin);
return mTexGarbageBin;
}
void EmptyTexGarbageBin();
bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const;
protected:

View File

@ -11,7 +11,6 @@
#include "GLReadTexImageHelper.h"
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
#include "SharedSurface.h"
#include "TextureGarbageBin.h"
namespace mozilla {
namespace gl {

View File

@ -15,7 +15,6 @@ namespace gl {
class GLContext;
class GLLibraryEGL;
class TextureGarbageBin;
class SharedSurface_EGLImage
: public SharedSurface

View File

@ -1,45 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TextureGarbageBin.h"
#include "GLContext.h"
using namespace mozilla;
using namespace mozilla::gl;
void
TextureGarbageBin::GLContextTeardown()
{
EmptyGarbage();
MutexAutoLock lock(mMutex);
mGL = nullptr;
}
void
TextureGarbageBin::Trash(GLuint tex)
{
MutexAutoLock lock(mMutex);
if (!mGL)
return;
mGarbageTextures.push(tex);
}
void
TextureGarbageBin::EmptyGarbage()
{
MutexAutoLock lock(mMutex);
if (!mGL)
return;
MOZ_RELEASE_ASSERT(mGL->IsCurrent(), "GFX: GL context not current.");
while (!mGarbageTextures.empty()) {
GLuint tex = mGarbageTextures.top();
mGarbageTextures.pop();
mGL->fDeleteTextures(1, &tex);
}
}

View File

@ -1,47 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef TEXTUREGARBAGEBIN_H_
#define TEXTUREGARBAGEBIN_H_
#include <stack>
#include "mozilla/Mutex.h"
#include "nsISupportsImpl.h"
#include "GLContextTypes.h"
namespace mozilla {
namespace gl {
class TextureGarbageBin final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureGarbageBin)
private:
// Private destructor, to discourage deletion outside of Release():
~TextureGarbageBin()
{
}
GLContext* mGL;
Mutex mMutex;
std::stack<GLuint> mGarbageTextures;
public:
explicit TextureGarbageBin(GLContext* gl)
: mGL(gl)
, mMutex("TextureGarbageBin mutex")
{}
void GLContextTeardown();
void Trash(GLuint tex);
void EmptyGarbage();
};
} // namespace gl
} // namespace mozilla
#endif // TEXTUREGARBAGEBIN_H_

View File

@ -51,7 +51,6 @@ EXPORTS += [
'SharedSurfaceEGL.h',
'SharedSurfaceGL.h',
'SurfaceTypes.h',
'TextureGarbageBin.h',
]
if CONFIG['MOZ_X11']:
@ -140,7 +139,6 @@ UNIFIED_SOURCES += [
'SharedSurfaceEGL.cpp',
'SharedSurfaceGL.cpp',
'SurfaceTypes.cpp',
'TextureGarbageBin.cpp',
'TextureImageEGL.cpp',
]

View File

@ -855,7 +855,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS
// application.
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46;
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37;
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))

View File

@ -237,12 +237,6 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
// A's DataView.prototype. So even though we're creating the DataView in B,
// its [[Prototype]] must be (a cross-compartment wrapper for) the
// DataView.prototype in A.
//
// As if this were not confusing enough, the way we actually do this is also
// tricky. We call compartment A's createDataViewForThis method, passing it
// bufobj as `this`. That calls ArrayBufferObject::createDataViewForThis(),
// which uses CallNonGenericMethod to switch to compartment B so that
// the new DataView is created there.
bool
DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args)
{

View File

@ -57,7 +57,6 @@ class MOZ_STACK_CLASS BytecodeCompiler
JSScript* compileScript(HandleObject environment, SharedContext* sc);
bool checkLength();
bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
bool enqueueOffThreadSourceCompression();
bool canLazilyParse();
bool createParser();
bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
@ -83,7 +82,6 @@ class MOZ_STACK_CLASS BytecodeCompiler
RootedScriptSource sourceObject;
ScriptSource* scriptSource;
SourceCompressionTask* sourceCompressionTask_;
Maybe<UsedNameTracker> usedNames;
Maybe<Parser<SyntaxParseHandler>> syntaxParser;
@ -173,7 +171,6 @@ BytecodeCompiler::BytecodeCompiler(JSContext* cx,
enclosingScope(cx, enclosingScope),
sourceObject(cx),
scriptSource(nullptr),
sourceCompressionTask_(nullptr),
directives(options.strictOption),
startPosition(keepAtoms),
script(cx)
@ -219,40 +216,6 @@ BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
return true;
}
bool
BytecodeCompiler::enqueueOffThreadSourceCompression()
{
// There are several cases where source compression is not a good idea:
// - If the script is tiny, then compression will save little or no space.
// - If there is only one core, then compression will contend with JS
// execution (which hurts benchmarketing).
//
// Otherwise, enqueue a compression task to be processed when a major
// GC is requested.
if (!scriptSource->hasUncompressedSource())
return true;
bool canCompressOffThread =
HelperThreadState().cpuCount > 1 &&
HelperThreadState().threadCount >= 2 &&
CanUseExtraThreads();
const size_t TINY_SCRIPT = 256;
if (TINY_SCRIPT <= sourceBuffer.length() && canCompressOffThread) {
// Heap allocate the task. It will be freed upon compression
// completing in AttachFinishedCompressedSources.
SourceCompressionTask* task = cx->new_<SourceCompressionTask>(cx->runtime(),
scriptSource);
if (!task)
return false;
if (!EnqueueOffThreadCompression(cx, task))
return false;
sourceCompressionTask_ = task;
}
return true;
}
bool
BytecodeCompiler::canLazilyParse()
{
@ -416,7 +379,7 @@ BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
script->scriptSource()->recordParseEnded();
// Enqueue an off-thread source compression task after finishing parsing.
if (!enqueueOffThreadSourceCompression())
if (!scriptSource->tryCompressOffThread(cx))
return nullptr;
MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
@ -481,7 +444,7 @@ BytecodeCompiler::compileModule()
module->setInitialEnvironment(env);
// Enqueue an off-thread source compression task after finishing parsing.
if (!enqueueOffThreadSourceCompression())
if (!scriptSource->tryCompressOffThread(cx))
return nullptr;
MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
@ -537,7 +500,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
return false;
// Enqueue an off-thread source compression task after finishing parsing.
if (!enqueueOffThreadSourceCompression())
if (!scriptSource->tryCompressOffThread(cx))
return false;
return true;
@ -549,12 +512,6 @@ BytecodeCompiler::sourceObjectPtr() const
return sourceObject.get();
}
SourceCompressionTask*
BytecodeCompiler::sourceCompressionTask() const
{
return sourceCompressionTask_;
}
ScriptSourceObject*
frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
@ -606,22 +563,17 @@ class MOZ_STACK_CLASS AutoInitializeSourceObject
{
BytecodeCompiler& compiler_;
ScriptSourceObject** sourceObjectOut_;
SourceCompressionTask** sourceCompressionTaskOut_;
public:
AutoInitializeSourceObject(BytecodeCompiler& compiler,
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
ScriptSourceObject** sourceObjectOut)
: compiler_(compiler),
sourceObjectOut_(sourceObjectOut),
sourceCompressionTaskOut_(sourceCompressionTaskOut)
sourceObjectOut_(sourceObjectOut)
{ }
~AutoInitializeSourceObject() {
if (sourceObjectOut_)
*sourceObjectOut_ = compiler_.sourceObjectPtr();
if (sourceCompressionTaskOut_)
*sourceCompressionTaskOut_ = compiler_.sourceCompressionTask();
}
};
@ -629,12 +581,11 @@ JSScript*
frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
ScriptSourceObject** sourceObjectOut)
{
MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
return compiler.compileGlobalScript(scopeKind);
}
@ -643,19 +594,17 @@ frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
HandleObject environment, HandleScope enclosingScope,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
ScriptSourceObject** sourceObjectOut)
{
BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
return compiler.compileEvalScript(environment, enclosingScope);
}
ModuleObject*
frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
SourceBufferHolder& srcBuf, LifoAlloc& alloc,
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
ScriptSourceObject** sourceObjectOut)
{
MOZ_ASSERT(srcBuf.get());
MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
@ -667,7 +616,7 @@ frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInpu
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
return compiler.compileModule();
}

View File

@ -23,7 +23,6 @@ class LazyScript;
class LifoAlloc;
class ModuleObject;
class ScriptSourceObject;
class SourceCompressionTask;
namespace frontend {
@ -35,16 +34,14 @@ JSScript*
CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr);
JSScript*
CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
HandleObject scopeChain, HandleScope enclosingScope,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr);
ModuleObject*
CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
@ -53,8 +50,7 @@ CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
ModuleObject*
CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, LifoAlloc& alloc,
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr);
MOZ_MUST_USE bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);

View File

@ -5053,22 +5053,18 @@ SweepCompressionTasksTask::run()
// Attach finished compression tasks.
auto& finished = HelperThreadState().compressionFinishedList(lock);
for (size_t i = 0; i < finished.length(); i++) {
SourceCompressionTask* task = finished[i];
if (task->runtimeMatches(runtime())) {
if (finished[i]->runtimeMatches(runtime())) {
UniquePtr<SourceCompressionTask> task(Move(finished[i]));
HelperThreadState().remove(finished, &i);
task->complete();
js_delete(task);
}
}
// Sweep pending tasks that are holding onto should-be-dead ScriptSources.
auto& pending = HelperThreadState().compressionPendingList(lock);
for (size_t i = 0; i < pending.length(); i++) {
SourceCompressionTask* task = pending[i];
if (task->shouldCancel()) {
if (pending[i]->shouldCancel())
HelperThreadState().remove(pending, &i);
js_delete(task);
}
}
}

View File

@ -1821,6 +1821,50 @@ ScriptSource::setSource(SharedImmutableTwoByteString&& string)
data = SourceType(Uncompressed(mozilla::Move(string)));
}
bool
ScriptSource::tryCompressOffThread(JSContext* cx)
{
if (!data.is<Uncompressed>())
return true;
// There are several cases where source compression is not a good idea:
// - If the script is tiny, then compression will save little or no space.
// - If there is only one core, then compression will contend with JS
// execution (which hurts benchmarketing).
//
// Otherwise, enqueue a compression task to be processed when a major
// GC is requested.
bool canCompressOffThread =
HelperThreadState().cpuCount > 1 &&
HelperThreadState().threadCount >= 2 &&
CanUseExtraThreads();
const size_t TINY_SCRIPT = 256;
if (TINY_SCRIPT > length() || !canCompressOffThread)
return true;
// The SourceCompressionTask needs to record the major GC number for
// scheduling. If we're parsing off thread, this number is not safe to
// access.
//
// When parsing on the main thread, the attempts made to compress off
// thread in BytecodeCompiler will succeed.
//
// When parsing off-thread, the above attempts will fail and the attempt
// made in ParseTask::finish will succeed.
if (!CurrentThreadCanAccessRuntime(cx->runtime()))
return true;
// Heap allocate the task. It will be freed upon compression
// completing in AttachFinishedCompressedSources.
auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), this);
if (!task) {
ReportOutOfMemory(cx);
return false;
}
return EnqueueOffThreadCompression(cx, Move(task));
}
MOZ_MUST_USE bool
ScriptSource::setCompressedSource(JSContext* cx,
mozilla::UniquePtr<char[], JS::FreePolicy>&& raw,

View File

@ -573,6 +573,8 @@ class ScriptSource
size_t length);
void setSource(SharedImmutableTwoByteString&& string);
MOZ_MUST_USE bool tryCompressOffThread(JSContext* cx);
MOZ_MUST_USE bool setCompressedSource(JSContext* cx,
UniqueChars&& raw,
size_t rawLength,

View File

@ -0,0 +1,230 @@
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
// Ensure the various error conditions are tested in the correct order.
const otherGlobal = newGlobal();
function* createBuffers(lengths = [0, 8]) {
for (let length of lengths) {
let buffer = new ArrayBuffer(length);
yield {buffer, detach: () => detachArrayBuffer(buffer)};
}
for (let length of lengths) {
let buffer = new otherGlobal.ArrayBuffer(length);
yield {buffer, detach: () => otherGlobal.detachArrayBuffer(buffer)};
}
}
const poisonedValue = new Proxy({}, new Proxy({}, {
get() {
// Throws an exception when any proxy trap is invoked.
throw new Error("Poisoned Value");
}
}));
class ExpectedError extends Error { }
function ConstructorWithThrowingPrototype(detach) {
return Object.defineProperty(function(){}.bind(null), "prototype", {
get() {
if (detach)
detach();
throw new ExpectedError();
}
});
}
function ValueThrowing(detach) {
return {
valueOf() {
if (detach)
detach();
throw new ExpectedError();
}
};
}
function ValueReturning(value, detach) {
return {
valueOf() {
if (detach)
detach();
return value;
}
};
}
// Ensure step 4 |AllocateTypedArray| is executed before step 6 |ToIndex(byteOffset)|.
for (let {buffer} of createBuffers()) {
let constructor = ConstructorWithThrowingPrototype();
assertThrowsInstanceOf(() =>
Reflect.construct(Int32Array, [buffer, poisonedValue, 0], constructor), ExpectedError);
}
// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|.
for (let {buffer, detach} of createBuffers()) {
let constructor = ConstructorWithThrowingPrototype();
detach();
assertThrowsInstanceOf(() =>
Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError);
}
// Ensure step 4 |AllocateTypedArray| is executed before step 9 |IsDetachedBuffer(buffer)|.
// - Variant: Detach buffer dynamically.
for (let {buffer, detach} of createBuffers()) {
let constructor = ConstructorWithThrowingPrototype(detach);
assertThrowsInstanceOf(() =>
Reflect.construct(Int32Array, [buffer, 0, 0], constructor), ExpectedError);
}
// Ensure step 4 |AllocateTypedArray| is executed before step 8.a |ToIndex(length)|.
for (let {buffer} of createBuffers()) {
let constructor = ConstructorWithThrowingPrototype();
assertThrowsInstanceOf(() =>
Reflect.construct(Int32Array, [buffer, 0, poisonedValue], constructor), ExpectedError);
}
// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueThrowing();
detach();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError);
}
// Ensure step 6 |ToIndex(byteOffset)| is executed before step 9 |IsDetachedBuffer(buffer)|.
// - Variant: Detach buffer dynamically.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueThrowing(detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), ExpectedError);
}
// Ensure step 6 |ToIndex(byteOffset)| is executed before step 8.a |ToIndex(length)|.
for (let {buffer} of createBuffers()) {
let byteOffset = ValueThrowing();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, poisonedValue), ExpectedError);
}
// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 9 |IsDetachedBuffer(buffer)|.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 1;
detach();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError);
}
// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 9 |IsDetachedBuffer(buffer)|.
// - Variant: Detach buffer dynamically.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueReturning(1, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, 0), RangeError);
}
// Ensure step 7 |offset modulo elementSize ≠ 0| is executed before step 8.a |ToIndex(length)|.
for (let {buffer} of createBuffers()) {
assertThrowsInstanceOf(() => new Int32Array(buffer, 1, poisonedValue), RangeError);
}
// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 0;
let length = ValueThrowing();
detach();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
}
// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
// - Variant: Detach buffer dynamically (1).
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueReturning(0, detach);
let length = ValueThrowing();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
}
// Ensure step 8.a |ToIndex(length)| is executed before step 9 |IsDetachedBuffer(buffer)|.
// - Variant: Detach buffer dynamically (2).
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 0;
let length = ValueThrowing(detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), ExpectedError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠ 0|.
for (let {buffer, detach} of createBuffers([1, 9])) {
let byteOffset = 0;
detach();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.a |bufferByteLength modulo elementSize ≠ 0|.
// - Variant: Detach buffer dynamically.
for (let {buffer, detach} of createBuffers([1, 9])) {
let byteOffset = ValueReturning(0, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 64;
detach();
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 11.c |newByteLength < 0|.
// - Variant: Detach buffer dynamically.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueReturning(64, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset), TypeError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|.
// - Case A: The given byteOffset is too large.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 64;
let length = ValueReturning(0, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
}
// Ensure step 9 |IsDetachedBuffer(buffer)| is executed before step 12.b |offset+newByteLength > bufferByteLength|.
// - Case B: The given length is too large.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 0;
let length = ValueReturning(64, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
}
// Ensure we handle the case when ToIndex(byteOffset) detaches the array buffer.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = ValueReturning(0, detach);
let length = 0;
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
}
// Ensure we handle the case when ToIndex(length) detaches the array buffer.
for (let {buffer, detach} of createBuffers()) {
let byteOffset = 0;
let length = ValueReturning(0, detach);
assertThrowsInstanceOf(() => new Int32Array(buffer, byteOffset, length), TypeError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -254,11 +254,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
// initialize()d to become a real, content-visible ArrayBufferObject.
static ArrayBufferObject* createEmpty(JSContext* cx);
template<typename T>
static bool createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args);
template<typename T>
static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);
static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
uint32_t count);

View File

@ -72,20 +72,6 @@ class GlobalObject : public NativeObject
EVAL = APPLICATION_SLOTS + STANDARD_CLASS_SLOTS,
THROWTYPEERROR,
/*
* Instances of the internal createArrayFromBuffer function used by the
* typed array code, one per typed array element type.
*/
FROM_BUFFER_UINT8,
FROM_BUFFER_INT8,
FROM_BUFFER_UINT16,
FROM_BUFFER_INT16,
FROM_BUFFER_UINT32,
FROM_BUFFER_INT32,
FROM_BUFFER_FLOAT32,
FROM_BUFFER_FLOAT64,
FROM_BUFFER_UINT8CLAMPED,
/* One-off properties stored after slots for built-ins. */
LEXICAL_ENVIRONMENT,
EMPTY_GLOBAL_SCOPE,
@ -260,21 +246,6 @@ class GlobalObject : public NativeObject
return classIsInitialized(JSProto_DataView);
}
Value createArrayFromBufferHelper(uint32_t slot) const {
MOZ_ASSERT(FROM_BUFFER_UINT8 <= slot && slot <= FROM_BUFFER_UINT8CLAMPED);
return getSlot(slot);
}
void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun) {
MOZ_ASSERT(getSlotRef(slot).isUndefined());
setSlot(slot, ObjectValue(*fun));
}
public:
template<typename T>
inline void setCreateArrayFromBuffer(Handle<JSFunction*> fun);
private:
// Disallow use of unqualified JSObject::create in GlobalObject.
static GlobalObject* create(...) = delete;
@ -779,9 +750,6 @@ class GlobalObject : public NativeObject
return &v.toObject();
}
template<typename T>
inline Value createArrayFromBuffer() const;
static bool isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global);
// Warn about use of the deprecated watch/unwatch functions in the global
@ -889,132 +857,6 @@ class GlobalObject : public NativeObject
JSObject* getStarGeneratorFunctionPrototype();
};
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint8_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT8);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int8_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT8);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint16_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT16);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int16_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT16);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint32_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int32_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<float>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_FLOAT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<double>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_FLOAT64);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint8_clamped>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED);
}
/*
* Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
* non-configurable, and non-writable; and define proto.constructor = ctor as

View File

@ -301,7 +301,7 @@ ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
}
@ -313,7 +313,7 @@ ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
}
@ -340,8 +340,8 @@ ParseTask::finish(JSContext* cx)
RootedScriptSource sso(cx, sourceObject);
if (!ScriptSourceObject::initFromOptions(cx, sso, options))
return false;
if (sourceCompressionTask)
sourceCompressionTask->fixupMajorGCNumber(cx->runtime());
if (!sso->source()->tryCompressOffThread(cx))
return false;
}
return true;
@ -385,8 +385,7 @@ ScriptParseTask::parse(JSContext* cx)
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject,
/* sourceCompressionTaskOut = */ &sourceCompressionTask);
/* sourceObjectOut = */ &sourceObject);
}
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
@ -401,8 +400,7 @@ void
ModuleParseTask::parse(JSContext* cx)
{
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject,
&sourceCompressionTask);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
if (module)
script = module->script();
}
@ -1123,13 +1121,13 @@ GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadStat
{
auto& pending = compressionPendingList(lock);
auto& worklist = compressionWorklist(lock);
MOZ_ASSERT(worklist.capacity() >= pending.length());
for (size_t i = 0; i < pending.length(); i++) {
SourceCompressionTask* task = pending[i];
if (task->shouldStart()) {
if (pending[i]->shouldStart()) {
// OOMing during appending results in the task not being scheduled
// and deleted.
Unused << worklist.append(Move(pending[i]));
remove(pending, &i);
worklist.infallibleAppend(task);
}
}
}
@ -1730,8 +1728,13 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
MOZ_ASSERT(idle());
currentTask.emplace(HelperThreadState().compressionWorklist(locked).popCopy());
SourceCompressionTask* task = compressionTask();
UniquePtr<SourceCompressionTask> task;
{
auto& worklist = HelperThreadState().compressionWorklist(locked);
task = Move(worklist.back());
worklist.popBack();
currentTask.emplace(task.get());
}
{
AutoUnlockHelperThreadState unlock(locked);
@ -1744,7 +1747,7 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!HelperThreadState().compressionFinishedList(locked).append(task))
if (!HelperThreadState().compressionFinishedList(locked).append(Move(task)))
oomUnsafe.crash("handleCompressionWorkload");
}
@ -1755,22 +1758,14 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
}
bool
js::EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task)
js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
{
AutoLockHelperThreadState lock;
auto& pending = HelperThreadState().compressionPendingList(lock);
auto& worklist = HelperThreadState().compressionWorklist(lock);
if (!pending.append(task)) {
if (!pending.append(Move(task))) {
if (!cx->helperThread())
ReportOutOfMemory(cx);
js_delete(task);
return false;
}
if (!worklist.reserve(pending.length())) {
if (!cx->helperThread())
ReportOutOfMemory(cx);
pending.popBack();
return false;
}
@ -1782,11 +1777,8 @@ static void
ClearCompressionTaskList(T& list, JSRuntime* runtime)
{
for (size_t i = 0; i < list.length(); i++) {
SourceCompressionTask* task = list[i];
if (task->runtimeMatches(runtime)) {
if (list[i]->runtimeMatches(runtime))
HelperThreadState().remove(list, &i);
js_delete(task);
}
}
}

View File

@ -72,7 +72,7 @@ class GlobalHelperThreadState
typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector;
typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskVector;
@ -165,7 +165,10 @@ class GlobalHelperThreadState
template <typename T>
void remove(T& vector, size_t* index)
{
vector[(*index)--] = vector.back();
// Self-moving is undefined behavior.
if (*index != vector.length() - 1)
vector[*index] = mozilla::Move(vector.back());
(*index)--;
vector.popBack();
}
@ -550,7 +553,7 @@ struct AutoEnqueuePendingParseTasksAfterGC {
// Enqueue a compression job to be processed if there's a major GC.
bool
EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task);
EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task);
// Cancel all scheduled, in progress, or finished compression tasks for
// runtime.
@ -623,10 +626,6 @@ struct ParseTask
// Holds the ScriptSourceObject generated for the script compilation.
ScriptSourceObject* sourceObject;
// Holds the SourceCompressionTask, if any were enqueued for the
// ScriptSource of sourceObject.
SourceCompressionTask* sourceCompressionTask;
// Any errors or warnings produced during compilation. These are reported
// when finishing the script.
Vector<CompileError*, 0, SystemAllocPolicy> errors;
@ -704,7 +703,6 @@ class SourceCompressionTask
JSRuntime* runtime_;
// The major GC number of the runtime when the task was enqueued.
static const uint64_t MajorGCNumberWaitingForFixup = UINT64_MAX;
uint64_t majorGCNumber_;
// The source to be compressed.
@ -717,31 +715,19 @@ class SourceCompressionTask
mozilla::Maybe<SharedImmutableString> resultString_;
public:
// The majorGCNumber is used for scheduling tasks. If the task is being
// enqueued from an off-thread parsing task, leave the GC number
// UINT64_MAX to be fixed up when the parse task finishes.
// The majorGCNumber is used for scheduling tasks.
SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
: runtime_(rt),
majorGCNumber_(CurrentThreadCanAccessRuntime(rt)
? rt->gc.majorGCCount()
: MajorGCNumberWaitingForFixup),
majorGCNumber_(rt->gc.majorGCCount()),
sourceHolder_(source)
{ }
bool runtimeMatches(JSRuntime* runtime) const {
return runtime == runtime_;
}
void fixupMajorGCNumber(JSRuntime* runtime) {
MOZ_ASSERT(majorGCNumber_ == MajorGCNumberWaitingForFixup);
majorGCNumber_ = runtime->gc.majorGCCount();
}
bool shouldStart() const {
// We wait 2 major GCs to start compressing, in order to avoid
// immediate compression.
if (majorGCNumber_ == MajorGCNumberWaitingForFixup)
return false;
return runtime_->gc.majorGCCount() > majorGCNumber_ + 1;
}

View File

@ -384,28 +384,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return fun;
}
static bool
getOrCreateCreateArrayFromBufferFunction(JSContext* cx, MutableHandleValue fval)
{
RootedValue cache(cx, cx->global()->createArrayFromBuffer<NativeType>());
if (cache.isObject()) {
MOZ_ASSERT(cache.toObject().is<JSFunction>());
fval.set(cache);
return true;
}
RootedFunction fun(cx);
fun = NewNativeFunction(cx, ArrayBufferObject::createTypedArrayFromBuffer<NativeType>,
0, nullptr);
if (!fun)
return false;
cx->global()->setCreateArrayFromBuffer<NativeType>(fun);
fval.setObject(*fun);
return true;
}
static inline const Class* instanceClass()
{
return TypedArrayObject::classForType(ArrayTypeID());
@ -479,10 +457,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
}
static TypedArrayObject*
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
HandleObject proto)
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset,
uint32_t len, HandleObject proto)
{
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
MOZ_ASSERT_IF(buffer, !buffer->isDetached());
MOZ_ASSERT(len < INT32_MAX / sizeof(NativeType));
gc::AllocKind allocKind = buffer
? GetGCObjectKind(instanceClass())
@ -574,14 +554,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return obj;
}
static TypedArrayObject*
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
uint32_t byteOffset, uint32_t len)
{
RootedObject proto(cx, nullptr);
return makeInstance(cx, buffer, byteOffset, len, proto);
}
static TypedArrayObject*
makeTemplateObject(JSContext* cx, int32_t len)
{
@ -735,6 +707,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return true;
}
private:
static JSObject*
create(JSContext* cx, const CallArgs& args)
{
@ -767,13 +740,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
return fromArray(cx, dataObj, newTarget);
/* (ArrayBuffer, [byteOffset, [length]]) */
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
// Step 4.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return nullptr;
int32_t byteOffset = 0;
if (args.hasDefined(1)) {
// Step 6.
if (!ToInt32(cx, args[1], &byteOffset))
return nullptr;
if (byteOffset < 0) {
@ -782,10 +759,18 @@ class TypedArrayObjectTemplate : public TypedArrayObject
"1");
return nullptr;
}
// Step 7.
if (byteOffset % sizeof(NativeType) != 0) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return nullptr;
}
}
int32_t length = -1;
if (args.hasDefined(2)) {
// Step 8.a.
if (!ToInt32(cx, args[2], &length))
return nullptr;
if (length < 0) {
@ -796,131 +781,180 @@ class TypedArrayObjectTemplate : public TypedArrayObject
}
}
return fromBufferWithProto(cx, dataObj, byteOffset, length, proto);
// Steps 9-17.
if (dataObj->is<ArrayBufferObjectMaybeShared>()) {
HandleArrayBufferObjectMaybeShared buffer = dataObj.as<ArrayBufferObjectMaybeShared>();
return fromBufferSameCompartment(cx, buffer, byteOffset, length, proto);
}
return fromBufferWrapped(cx, dataObj, byteOffset, length, proto);
}
// ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
// Steps 9-12.
static bool
computeAndCheckLength(JSContext* cx, HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,
uint32_t byteOffset, int32_t lengthInt, uint32_t* length)
{
MOZ_ASSERT(byteOffset % sizeof(NativeType) == 0);
// Step 9.
if (bufferMaybeUnwrapped->isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
// Step 10.
uint32_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
// 11.c, 12.b.
if (byteOffset > bufferByteLength) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return false; // invalid byteOffset
}
uint32_t len;
if (lengthInt < 0) {
// Step 11.b.
uint32_t newByteLength = bufferByteLength - byteOffset;
len = newByteLength / sizeof(NativeType);
// Step 11.a.
if (len * sizeof(NativeType) != newByteLength) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return false; // given byte array doesn't map exactly to sizeof(NativeType) * N
}
// ArrayBuffer is too large for TypedArrays:
// Standalone ArrayBuffers can hold up to INT32_MAX bytes, whereas
// buffers in TypedArrays must have less than or equal to
// |INT32_MAX - sizeof(NativeType) - INT32_MAX % sizeof(NativeType)|
// bytes.
if (len >= INT32_MAX / sizeof(NativeType)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return false;
}
} else {
// Step 12.a (implicit).
len = uint32_t(lengthInt);
// Step 12.b.
if (len >= INT32_MAX / sizeof(NativeType)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return false;
}
uint32_t newByteLength = len * sizeof(NativeType);
// Step 12.b (|byteOffset| moved to the RHS to avoid overflow).
if (newByteLength > bufferByteLength - byteOffset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return false; // |byteOffset + newByteLength| is too big for the arraybuffer
}
}
*length = len;
return true;
}
// ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
// Steps 9-17.
static JSObject*
fromBufferSameCompartment(JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
uint32_t byteOffset, int32_t lengthInt, HandleObject proto)
{
// Steps 9-12.
uint32_t length;
if (!computeAndCheckLength(cx, buffer, byteOffset, lengthInt, &length))
return nullptr;
// Steps 13-17.
return makeInstance(cx, buffer, byteOffset, length, proto);
}
// Create a TypedArray object in another compartment.
//
// ES6 supports creating a TypedArray in global A (using global A's
// TypedArray constructor) backed by an ArrayBuffer created in global B.
//
// Our TypedArrayObject implementation doesn't support a TypedArray in
// compartment A backed by an ArrayBuffer in compartment B. So in this
// case, we create the TypedArray in B (!) and return a cross-compartment
// wrapper.
//
// Extra twist: the spec says the new TypedArray's [[Prototype]] must be
// A's TypedArray.prototype. So even though we're creating the TypedArray
// in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
// TypedArray.prototype in A.
static JSObject*
fromBufferWrapped(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
HandleObject proto)
{
JSObject* unwrapped = CheckedUnwrap(bufobj);
if (!unwrapped) {
ReportAccessDenied(cx);
return nullptr;
}
if (!unwrapped->is<ArrayBufferObjectMaybeShared>()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return nullptr;
}
RootedArrayBufferObjectMaybeShared unwrappedBuffer(cx);
unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
uint32_t length;
if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthInt, &length))
return nullptr;
// Make sure to get the [[Prototype]] for the created typed array from
// this compartment.
RootedObject protoRoot(cx, proto);
if (!protoRoot) {
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
return nullptr;
}
RootedObject typedArray(cx);
{
JSAutoCompartment ac(cx, unwrappedBuffer);
RootedObject wrappedProto(cx, protoRoot);
if (!cx->compartment()->wrap(cx, &wrappedProto))
return nullptr;
typedArray = makeInstance(cx, unwrappedBuffer, byteOffset, length, wrappedProto);
if (!typedArray)
return nullptr;
}
if (!cx->compartment()->wrap(cx, &typedArray))
return nullptr;
return typedArray;
}
public:
static JSObject*
fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
}
static JSObject*
fromBufferWithProto(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
HandleObject proto)
fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt)
{
if (bufobj->is<ProxyObject>()) {
/*
* Normally, NonGenericMethodGuard handles the case of transparent
* wrappers. However, we have a peculiar situation: we want to
* construct the new typed array in the compartment of the buffer,
* so that the typed array can point directly at their buffer's
* data without crossing compartment boundaries. So we use the
* machinery underlying NonGenericMethodGuard directly to proxy the
* native call. We will end up with a wrapper in the origin
* compartment for a view in the target compartment referencing the
* ArrayBufferObject in that same compartment.
*/
JSObject* wrapped = CheckedUnwrap(bufobj);
if (!wrapped) {
ReportAccessDenied(cx);
return nullptr;
}
if (!IsAnyArrayBuffer(wrapped)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return nullptr; // must be arrayBuffer
}
/*
* And for even more fun, the new view's prototype should be
* set to the origin compartment's prototype object, not the
* target's (specifically, the actual view in the target
* compartment will use as its prototype a wrapper around the
* origin compartment's view.prototype object).
*
* Rather than hack some crazy solution together, implement
* this all using a private helper function, created when
* ArrayBufferObject was initialized and cached in the global.
* This reuses all the existing cross-compartment crazy so we
* don't have to do anything *uniquely* crazy here.
*/
RootedObject protoRoot(cx, proto);
if (!protoRoot) {
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
return nullptr;
}
FixedInvokeArgs<3> args(cx);
args[0].setNumber(byteOffset);
args[1].setInt32(lengthInt);
args[2].setObject(*protoRoot);
RootedValue fval(cx);
if (!getOrCreateCreateArrayFromBufferFunction(cx, &fval))
return nullptr;
RootedValue thisv(cx, ObjectValue(*bufobj));
RootedValue rval(cx);
if (!js::Call(cx, fval, thisv, args, &rval))
return nullptr;
return &rval.toObject();
}
if (!IsAnyArrayBuffer(bufobj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return nullptr; // must be arrayBuffer
}
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
if (IsArrayBuffer(bufobj)) {
ArrayBufferObject& buf = AsArrayBuffer(bufobj);
if (buf.isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return nullptr;
}
buffer = static_cast<ArrayBufferObjectMaybeShared*>(&buf);
} else {
buffer = static_cast<ArrayBufferObjectMaybeShared*>(&AsSharedArrayBuffer(bufobj));
}
if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
if (byteOffset % sizeof(NativeType) != 0) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return nullptr; // invalid byteOffset
}
uint32_t len;
if (lengthInt == -1) {
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
}
} else {
len = uint32_t(lengthInt);
if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
HandleArrayBufferObjectMaybeShared buffer = bufobj.as<ArrayBufferObjectMaybeShared>();
return fromBufferSameCompartment(cx, buffer, byteOffset, lengthInt, nullptr);
}
// Go slowly and check for overflow.
uint32_t arrayByteLength = len * sizeof(NativeType);
if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType)
}
if (arrayByteLength + byteOffset > buffer->byteLength()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
return nullptr; // byteOffset + len is too big for the arraybuffer
}
return makeInstance(cx, buffer, byteOffset, len, proto);
return fromBufferWrapped(cx, bufobj, byteOffset, lengthInt, nullptr);
}
static bool
@ -1779,38 +1813,6 @@ TypedArrayObject::sharedTypedArrayPrototypeClass = {
&TypedArrayObjectSharedTypedArrayPrototypeClassSpec
};
template<typename T>
bool
ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args)
{
typedef TypedArrayObjectTemplate<T> ArrayType;
MOZ_ASSERT(IsAnyArrayBuffer(args.thisv()));
MOZ_ASSERT(args.length() == 3);
Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
Rooted<JSObject*> proto(cx, &args[2].toObject());
Rooted<JSObject*> obj(cx);
double byteOffset = args[0].toNumber();
MOZ_ASSERT(0 <= byteOffset);
MOZ_ASSERT(byteOffset <= UINT32_MAX);
MOZ_ASSERT(byteOffset == uint32_t(byteOffset));
obj = ArrayType::fromBufferWithProto(cx, buffer, uint32_t(byteOffset), args[1].toInt32(),
proto);
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
template<typename T>
bool
ArrayBufferObject::createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsAnyArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
}
// this default implementation is only valid for integer types
// less than 32-bits in size.
template<typename NativeType>

View File

@ -427,23 +427,6 @@ TypedArrayElemSize(Scalar::Type viewType)
extern void
SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset,
TypedArrayObject* unsafeSrcCrossCompartment);
static inline bool
IsAnyArrayBuffer(HandleObject obj)
{
return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
}
static inline bool
IsAnyArrayBuffer(JSObject* obj)
{
return IsArrayBuffer(obj) || IsSharedArrayBuffer(obj);
}
static inline bool
IsAnyArrayBuffer(HandleValue v)
{
return v.isObject() && IsAnyArrayBuffer(&v.toObject());
}
} // namespace js

View File

@ -2073,7 +2073,7 @@ nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
mPendingViewportChange = false;
if (mDocument->IsBeingUsedAsImage()) {
MOZ_ASSERT(PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists()));
MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
return;
}
@ -2088,13 +2088,11 @@ nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
// Note that we do this after the new style from media queries in
// style sheets has been computed.
if (!PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists())) {
if (!mDocument->MediaQueryLists().isEmpty()) {
// We build a list of all the notifications we're going to send
// before we send any of them.
for (PRCList *l = PR_LIST_HEAD(mDocument->MediaQueryLists());
l != mDocument->MediaQueryLists(); l = PR_NEXT_LINK(l)) {
for (auto mql : mDocument->MediaQueryLists()) {
nsAutoMicroTask mt;
MediaQueryList *mql = static_cast<MediaQueryList*>(l);
mql->MaybeNotify();
}
}

View File

@ -3,7 +3,7 @@
<title>CSS transforms: Creating containing block for fixed positioned elements</title>
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#perspective-property">
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
<link rel="match" href="containing-block-dynamic-1-ref.html">
<meta name="assert" content="It also establishes a containing block (somewhat similar to position: relative), just like the transform property does.">
<meta name="flags" content="dom">

View File

@ -3,7 +3,7 @@
<title>CSS transforms: Creating containing block for fixed positioned elements</title>
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#perspective-property">
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
<link rel="match" href="containing-block-dynamic-1-ref.html">
<meta name="assert" content="It also establishes a containing block (somewhat similar to position: relative), just like the transform property does.">
<meta name="flags" content="dom">

View File

@ -28,17 +28,11 @@ MediaQueryList::MediaQueryList(nsIDocument* aDocument,
mMediaList =
MediaList::Create(aDocument->GetStyleBackendType(), aMediaQueryList);
PR_INIT_CLIST(this);
KeepAliveIfHasListenersFor(ONCHANGE_STRING);
}
MediaQueryList::~MediaQueryList()
{
if (mDocument) {
PR_REMOVE_LINK(this);
}
}
{}
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
@ -50,7 +44,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
DOMEventTargetHelper)
if (tmp->mDocument) {
PR_REMOVE_LINK(tmp);
tmp->remove();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
}
tmp->Disconnect();

View File

@ -13,7 +13,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "prclist.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Attributes.h"
#include "nsWrapperCache.h"
#include "mozilla/DOMEventTargetHelper.h"
@ -27,7 +27,7 @@ namespace dom {
class MediaList;
class MediaQueryList final : public DOMEventTargetHelper,
public PRCList
public mozilla::LinkedListElement<MediaQueryList>
{
public:
// The caller who constructs is responsible for calling Evaluate

357
mfbt/DoublyLinkedList.h Normal file
View File

@ -0,0 +1,357 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
/** A doubly-linked list with flexible next/prev naming. */
#ifndef mozilla_DoublyLinkedList_h
#define mozilla_DoublyLinkedList_h
#include <algorithm>
#include <iterator>
/**
* Where mozilla::LinkedList strives for ease of use above all other
* considerations, mozilla::DoublyLinkedList strives for flexibility. The
* following are things that can be done with mozilla::DoublyLinkedList that
* cannot be done with mozilla::LinkedList:
*
* * Arbitrary next/prev placement and naming. With the tools provided here,
* the next and previous pointers can be at the end of the structure, in a
* sub-structure, stored with a tag, in a union, wherever, as long as you
* can look them up and set them on demand.
* * Can be used without deriving from a new base and, thus, does not require
* use of constructors.
*
* Example:
*
* class Observer : public DoublyLinkedListElement<Observer>
* {
* public:
* void observe(char* aTopic) { ... }
* };
*
* class ObserverContainer
* {
* private:
* DoublyLinkedList<Observer> mList;
*
* public:
* void addObserver(Observer* aObserver)
* {
* // Will assert if |aObserver| is part of another list.
* mList.pushBack(aObserver);
* }
*
* void removeObserver(Observer* aObserver)
* {
* // Will assert if |aObserver| is not part of |list|.
* mList.remove(aObserver);
* }
*
* void notifyObservers(char* aTopic)
* {
* for (Observer* o : mList) {
* o->observe(aTopic);
* }
* }
* };
*/
namespace mozilla {
/**
* Provides access to a next and previous element pointer named |mNext| and
* |mPrev| respectively. This class is the default and will work if the list
* element derives from DoublyLinkedListElement.
*
* Although designed to work with DoublyLinkedListElement this will als work
* with any class that defines |mNext| and |mPrev| members with the correct
* type.
*/
template <typename T>
struct DoublyLinkedSiblingAccess {
static void SetNext(T* aElm, T* aNext) { aElm->mNext = aNext; }
static T* GetNext(T* aElm) { return aElm->mNext; }
static void SetPrev(T* aElm, T* aPrev) { aElm->mPrev = aPrev; }
static T* GetPrev(T* aElm) { return aElm->mPrev; }
};
/**
* Deriving from this will allow T to be inserted into and removed from a
* DoublyLinkedList.
*/
template <typename T>
struct DoublyLinkedListElement
{
friend struct DoublyLinkedSiblingAccess<T>;
T* mNext;
T* mPrev;
public:
DoublyLinkedListElement() : mNext(nullptr), mPrev(nullptr) {}
};
/**
* A doubly linked list. |T| is the type of element stored in this list. |T|
* must contain or have access to unique next and previous element pointers.
* The template argument |SiblingAccess| provides code to tell this list how to
* get and set the next and previous pointers. The actual storage of next/prev
* links may reside anywhere and be encoded in any way.
*/
template <typename T, typename SiblingAccess = DoublyLinkedSiblingAccess<T>>
class DoublyLinkedList final
{
T* mHead;
T* mTail;
/**
* Checks that either the list is empty and both mHead and mTail are nullptr
* or the list has entries and both mHead and mTail are non-null.
*/
bool isStateValid() const {
return (mHead != nullptr) == (mTail != nullptr);
}
static bool ElementNotInList(T* aElm) {
return !SiblingAccess::GetNext(aElm) && !SiblingAccess::GetPrev(aElm);
}
public:
DoublyLinkedList() : mHead(nullptr), mTail(nullptr) {}
class Iterator final {
T* mCurrent;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
Iterator() : mCurrent(nullptr) {}
explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
T& operator *() const { return *mCurrent; }
T* operator ->() const { return mCurrent; }
Iterator& operator++() {
mCurrent = SiblingAccess::GetNext(mCurrent);
return *this;
}
Iterator operator++(int) {
Iterator result = *this;
++(*this);
return result;
}
Iterator& operator--() {
mCurrent = SiblingAccess::GetPrev(mCurrent);
return *this;
}
Iterator operator--(int) {
Iterator result = *this;
--(*this);
return result;
}
bool operator!=(const Iterator& aOther) const {
return mCurrent != aOther.mCurrent;
}
bool operator==(const Iterator& aOther) const {
return mCurrent == aOther.mCurrent;
}
explicit operator bool() const {
return mCurrent;
}
};
Iterator begin() { return Iterator(mHead); }
const Iterator begin() const { return Iterator(mHead); }
const Iterator cbegin() const { return Iterator(mHead); }
Iterator end() { return Iterator(); }
const Iterator end() const { return Iterator(); }
const Iterator cend() const { return Iterator(); }
/**
* Returns true if the list contains no elements.
*/
bool isEmpty() const {
MOZ_ASSERT(isStateValid());
return mHead == nullptr;
}
/**
* Inserts aElm into the list at the head position. |aElm| must not already
* be in a list.
*/
void pushFront(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
SiblingAccess::SetNext(aElm, mHead);
if (mHead) {
MOZ_ASSERT(!SiblingAccess::GetPrev(mHead));
SiblingAccess::SetPrev(mHead, aElm);
}
mHead = aElm;
if (!mTail) {
mTail = aElm;
}
}
/**
* Remove the head of the list and return it. Calling this on an empty list
* will assert.
*/
T* popFront() {
MOZ_ASSERT(!isEmpty());
MOZ_ASSERT(isStateValid());
T* result = mHead;
mHead = result ? SiblingAccess::GetNext(result) : nullptr;
if (mHead) {
SiblingAccess::SetPrev(mHead, nullptr);
}
if (mTail == result) {
mTail = nullptr;
}
if (result) {
SiblingAccess::SetNext(result, nullptr);
SiblingAccess::SetPrev(result, nullptr);
}
return result;
}
/**
* Inserts aElm into the list at the tail position. |aElm| must not already
* be in a list.
*/
void pushBack(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
SiblingAccess::SetNext(aElm, nullptr);
SiblingAccess::SetPrev(aElm, mTail);
if (mTail) {
MOZ_ASSERT(!SiblingAccess::GetNext(mTail));
SiblingAccess::SetNext(mTail, aElm);
}
mTail = aElm;
if (!mHead) {
mHead = aElm;
}
}
/**
* Remove the tail of the list and return it. Calling this on an empty list
* will assert.
*/
T* popBack() {
MOZ_ASSERT(!isEmpty());
MOZ_ASSERT(isStateValid());
T* result = mTail;
mTail = result ? SiblingAccess::GetPrev(result) : nullptr;
if (mTail) {
SiblingAccess::SetNext(mTail, nullptr);
}
if (mHead == result) {
mHead = nullptr;
}
if (result) {
SiblingAccess::SetNext(result, nullptr);
SiblingAccess::SetPrev(result, nullptr);
}
return result;
}
/**
* Insert the given |aElm| *before* |aIter|.
*/
void insertBefore(const Iterator& aIter, T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(ElementNotInList(aElm));
MOZ_ASSERT(isStateValid());
if (!aIter) {
return pushBack(aElm);
} else if (aIter == begin()) {
return pushFront(aElm);
}
T* after = &(*aIter);
T* before = SiblingAccess::GetPrev(after);
MOZ_ASSERT(before);
SiblingAccess::SetNext(before, aElm);
SiblingAccess::SetPrev(aElm, before);
SiblingAccess::SetNext(aElm, after);
SiblingAccess::SetPrev(after, aElm);
}
/**
* Removes the given element from the list. The element must be in this list.
*/
void remove(T* aElm) {
MOZ_ASSERT(aElm);
MOZ_ASSERT(SiblingAccess::GetNext(aElm) || SiblingAccess::GetPrev(aElm) ||
(aElm == mHead && aElm == mTail),
"Attempted to remove element not in this list");
if (T* prev = SiblingAccess::GetPrev(aElm)) {
SiblingAccess::SetNext(prev, SiblingAccess::GetNext(aElm));
} else {
MOZ_ASSERT(mHead == aElm);
mHead = SiblingAccess::GetNext(aElm);
}
if (T* next = SiblingAccess::GetNext(aElm)) {
SiblingAccess::SetPrev(next, SiblingAccess::GetPrev(aElm));
} else {
MOZ_ASSERT(mTail == aElm);
mTail = SiblingAccess::GetPrev(aElm);
}
SiblingAccess::SetNext(aElm, nullptr);
SiblingAccess::SetPrev(aElm, nullptr);
}
/**
* Returns an iterator referencing the first found element whose value matches
* the given element according to operator==.
*/
Iterator find(const T& aElm) {
return std::find(begin(), end(), aElm);
}
/**
* Returns whether the given element is in the list. Note that this uses
* T::operator==, not pointer comparison.
*/
bool contains(const T& aElm) {
return find(aElm) != Iterator();
}
};
} // namespace mozilla
#endif // mozilla_DoublyLinkedList_h

View File

@ -35,6 +35,7 @@ EXPORTS.mozilla = [
'decimal/Decimal.h',
'double-conversion/source/double-conversion.h',
'double-conversion/source/utils.h',
'DoublyLinkedList.h',
'EndianUtils.h',
'EnumeratedArray.h',
'EnumeratedRange.h',

View File

@ -0,0 +1,173 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Assertions.h"
#include "mozilla/DoublyLinkedList.h"
using mozilla::DoublyLinkedList;
using mozilla::DoublyLinkedListElement;
struct SomeClass : public DoublyLinkedListElement<SomeClass> {
unsigned int mValue;
explicit SomeClass(int aValue) : mValue(aValue) {}
void incr() { ++mValue; }
bool operator==(const SomeClass& other) { return mValue == other.mValue; }
};
template <typename ListType, size_t N>
static void
CheckListValues(ListType& list, unsigned int (&values)[N])
{
size_t count = 0;
for (auto& x : list) {
MOZ_RELEASE_ASSERT(x.mValue == values[count]);
++count;
}
MOZ_RELEASE_ASSERT(count == N);
}
static void
TestDoublyLinkedList()
{
DoublyLinkedList<SomeClass> list;
SomeClass one(1), two(2), three(3);
MOZ_RELEASE_ASSERT(list.isEmpty());
MOZ_RELEASE_ASSERT(!list.begin());
MOZ_RELEASE_ASSERT(!list.end());
for (SomeClass& x : list) {
MOZ_RELEASE_ASSERT(x.mValue);
MOZ_RELEASE_ASSERT(false);
}
list.pushFront(&one);
{ unsigned int check[] { 1 }; CheckListValues(list, check); }
MOZ_RELEASE_ASSERT(list.contains(one));
MOZ_RELEASE_ASSERT(!list.contains(two));
MOZ_RELEASE_ASSERT(!list.contains(three));
MOZ_RELEASE_ASSERT(!list.isEmpty());
MOZ_RELEASE_ASSERT(list.begin()->mValue == 1);
MOZ_RELEASE_ASSERT(!list.end());
list.pushFront(&two);
{ unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
MOZ_RELEASE_ASSERT(!list.end());
MOZ_RELEASE_ASSERT(!list.contains(three));
list.pushBack(&three);
{ unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
MOZ_RELEASE_ASSERT(!list.end());
list.remove(&one);
{ unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
list.insertBefore(list.find(three), &one);
{ unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
list.remove(&three);
{ unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
list.insertBefore(list.find(two), &three);
{ unsigned int check[] { 3, 2, 1 }; CheckListValues(list, check); }
list.remove(&three);
{ unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
list.insertBefore(++list.find(two), &three);
{ unsigned int check[] { 2, 3, 1 }; CheckListValues(list, check); }
list.remove(&one);
{ unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
list.remove(&two);
{ unsigned int check[] { 3 }; CheckListValues(list, check); }
list.insertBefore(list.find(three), &two);
{ unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
list.remove(&three);
{ unsigned int check[] { 2 }; CheckListValues(list, check); }
list.remove(&two);
MOZ_RELEASE_ASSERT(list.isEmpty());
list.pushBack(&three);
{ unsigned int check[] { 3 }; CheckListValues(list, check); }
list.pushFront(&two);
{ unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
// This should modify the values of |two| and |three| as pointers to them are
// stored in the list, not copies.
for (SomeClass& x : list) {
x.incr();
}
MOZ_RELEASE_ASSERT(*list.begin() == two);
MOZ_RELEASE_ASSERT(*++list.begin() == three);
SomeClass four(4);
MOZ_RELEASE_ASSERT(++list.begin() == list.find(four));
}
static void
TestCustomAccessor()
{
struct InTwoLists {
explicit InTwoLists(unsigned int aValue) : mValue(aValue) {}
DoublyLinkedListElement<InTwoLists> mListOne;
DoublyLinkedListElement<InTwoLists> mListTwo;
unsigned int mValue;
};
struct ListOneSiblingAccess {
static void SetNext(InTwoLists* aElm, InTwoLists* aNext) { aElm->mListOne.mNext = aNext; }
static InTwoLists* GetNext(InTwoLists* aElm) { return aElm->mListOne.mNext; }
static void SetPrev(InTwoLists* aElm, InTwoLists* aPrev) { aElm->mListOne.mPrev = aPrev; }
static InTwoLists* GetPrev(InTwoLists* aElm) { return aElm->mListOne.mPrev; }
};
struct ListTwoSiblingAccess {
static void SetNext(InTwoLists* aElm, InTwoLists* aNext) { aElm->mListTwo.mNext = aNext; }
static InTwoLists* GetNext(InTwoLists* aElm) { return aElm->mListTwo.mNext; }
static void SetPrev(InTwoLists* aElm, InTwoLists* aPrev) { aElm->mListTwo.mPrev = aPrev; }
static InTwoLists* GetPrev(InTwoLists* aElm) { return aElm->mListTwo.mPrev; }
};
DoublyLinkedList<InTwoLists, ListOneSiblingAccess> listOne;
DoublyLinkedList<InTwoLists, ListTwoSiblingAccess> listTwo;
InTwoLists one(1);
InTwoLists two(2);
listOne.pushBack(&one);
listOne.pushBack(&two);
{ unsigned int check[] { 1, 2 }; CheckListValues(listOne, check); }
listTwo.pushBack(&one);
listTwo.pushBack(&two);
{ unsigned int check[] { 1, 2 }; CheckListValues(listTwo, check); }
(void)listTwo.popBack();
{ unsigned int check[] { 1, 2 }; CheckListValues(listOne, check); }
{ unsigned int check[] { 1 }; CheckListValues(listTwo, check); }
}
int
main()
{
TestDoublyLinkedList();
TestCustomAccessor();
return 0;
}

View File

@ -21,6 +21,7 @@ CppUnitTests([
'TestCheckedInt',
'TestCountPopulation',
'TestCountZeroes',
'TestDoublyLinkedList',
'TestEndian',
'TestEnumeratedArray',
'TestEnumSet',

View File

@ -10,7 +10,7 @@
windowtype="navigator:browser"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<browser id="content" type="content" primary="true" src="about:blank" flex="1" remote="true"/>
<browser id="content" type="content" primary="true" src="about:blank" flex="1"/>
<script type="application/javascript" src="chrome://geckoview/content/geckoview.js"/>
</window>

View File

@ -22,10 +22,23 @@ public final class GeckoViewSettings {
}
}
/*
* Key to enabled and disable tracking protection.
*/
public static final Key<Boolean> USE_TRACKING_PROTECTION =
new Key<Boolean>("useTrackingProtection");
/*
* Key to enabled and disable private mode browsing.
*/
public static final Key<Boolean> USE_PRIVATE_MODE =
new Key<Boolean>("usePrivateMode");
/*
* Key to enabled and disable multiprocess browsing (e10s).
* Note: can only be set during GeckoView initialization, changes during an
* active GeckoView session will be ignored.
*/
public static final Key<Boolean> USE_MULTIPROCESS =
new Key<Boolean>("useMultiprocess");
private final EventDispatcher mEventDispatcher;
private final GeckoBundle mBundle;
@ -40,6 +53,7 @@ public final class GeckoViewSettings {
setBoolean(USE_TRACKING_PROTECTION, false);
setBoolean(USE_PRIVATE_MODE, false);
setBoolean(USE_MULTIPROCESS, true);
}
/* package */ GeckoViewSettings(GeckoViewSettings settings, EventDispatcher eventDispatcher) {

View File

@ -40,7 +40,15 @@ public final class HardwareCodecCapabilityUtils {
@WrapForJNI
public static boolean findDecoderCodecInfoForMimeType(String aMimeType) {
for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
int numCodecs = 0;
try {
numCodecs = MediaCodecList.getCodecCount();
} catch (final RuntimeException e) {
Log.e(LOGTAG, "Failed to retrieve media codec count", e);
return false;
}
for (int i = 0; i < numCodecs; ++i) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (info.isEncoder()) {
continue;

View File

@ -51,18 +51,18 @@ public class GeckoViewActivity extends Activity {
}
@Override
protected void onNewIntent(Intent externalIntent) {
Log.d(LOGTAG, "SNORP: onNewIntent: " + externalIntent.getData().toString());
loadFromIntent(externalIntent);
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if (intent.getData() != null) {
loadFromIntent(intent);
}
}
private void loadFromIntent(Intent intent) {
Uri u = intent.getData();
if (u != null) {
mGeckoView.loadUri(u.toString());
} else {
mGeckoView.loadUri(DEFAULT_URL);
}
private void loadFromIntent(final Intent intent) {
final Uri uri = intent.getData();
mGeckoView.loadUri(uri != null ? uri.toString() : DEFAULT_URL);
}
@Override

View File

@ -23,10 +23,15 @@ function debug(aMsg) {
// Handles GeckoView settings including:
// * tracking protection
// * multiprocess
class GeckoViewSettings extends GeckoViewModule {
init() {
this._isSafeBrowsingInit = false;
this._useTrackingProtection = false;
// We only allow to set this setting during initialization, further updates
// will be ignored.
this.useMultiprocess = !!this.settings.useMultiprocess;
}
onSettingsUpdate() {
@ -52,4 +57,23 @@ class GeckoViewSettings extends GeckoViewModule {
this._useTrackingProtection = aUse;
}
}
get useMultiprocess() {
return this.browser.getAttribute("remote") == "true";
}
set useMultiprocess(aUse) {
if (aUse == this.useMultiprocess) {
return;
}
let parentNode = this.browser.parentNode;
parentNode.removeChild(this.browser);
if (aUse) {
this.browser.setAttribute("remote", "true");
} else {
this.browser.removeAttribute("remote");
}
parentNode.appendChild(this.browser);
}
}

View File

@ -1528,7 +1528,7 @@ pref("network.http.sendRefererHeader", 2);
pref("network.http.referer.userControlPolicy", 3);
// false=real referer, true=spoof referer (use target URI as referer)
pref("network.http.referer.spoofSource", false);
// false=allow onion referer, true=hide onion referer (use target URI as referer)
// false=allow onion referer, true=hide onion referer (use empty referer)
pref("network.http.referer.hideOnionSource", false);
// 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
pref("network.http.referer.trimmingPolicy", 0);

View File

@ -930,31 +930,24 @@ nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::Pendi
}
uint32_t
nsHttpConnectionMgr::AvailableNewConnectionCount(nsConnectionEntry * ent)
nsHttpConnectionMgr::TotalActiveConnections(nsConnectionEntry *ent) const
{
// Add in the in-progress tcp connections, we will assume they are
// keepalive enabled.
// Exclude half-open's that has already created a usable connection.
// This prevents the limit being stuck on ipv6 connections that
// eventually time out after typical 21 seconds of no ACK+SYN reply.
uint32_t totalCount =
ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
uint16_t maxPersistConns;
return ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
}
uint32_t
nsHttpConnectionMgr::MaxPersistConnections(nsConnectionEntry *ent) const
{
if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
maxPersistConns = mMaxPersistConnsPerProxy;
} else {
maxPersistConns = mMaxPersistConnsPerHost;
return static_cast<uint32_t>(mMaxPersistConnsPerProxy);
}
LOG(("nsHttpConnectionMgr::AvailableNewConnectionCount "
"total connection count = %d, limit %d\n",
totalCount, maxPersistConns));
return maxPersistConns > totalCount
? maxPersistConns - totalCount
: 0;
return static_cast<uint32_t>(mMaxPersistConnsPerHost);
}
bool
@ -986,7 +979,12 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
return dispatchedSuccessfully;
}
uint32_t availableConnections = AvailableNewConnectionCount(ent);
uint32_t totalCount = TotalActiveConnections(ent);
uint32_t maxPersistConns = MaxPersistConnections(ent);
uint32_t availableConnections = maxPersistConns > totalCount
? maxPersistConns - totalCount
: 0;
// No need to try dispatching if we reach the active connection limit.
if (!availableConnections) {
return dispatchedSuccessfully;
@ -1099,14 +1097,15 @@ bool
nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t caps)
{
nsHttpConnectionInfo *ci = ent->mConnInfo;
uint32_t totalCount = TotalActiveConnections(ent);
uint32_t maxPersistConns = MaxPersistConnections(ent);
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
ci->HashKey().get(), caps));
uint32_t availableConnections = AvailableNewConnectionCount(ent);
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x,"
"totalCount=%u, maxPersistConns=%u]\n",
ci->HashKey().get(), caps, totalCount, maxPersistConns));
if (caps & NS_HTTP_URGENT_START) {
if (availableConnections > static_cast<uint32_t>(mMaxUrgentExcessiveConns)) {
if (totalCount >= (mMaxUrgentExcessiveConns + maxPersistConns)) {
LOG(("The number of total connections are greater than or equal to sum of "
"max urgent-start queue length and the number of max persistent connections.\n"));
return true;
@ -1131,7 +1130,7 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t ca
return true;
}
bool result = !availableConnections;
bool result = (totalCount >= maxPersistConns);
LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
return result;
}

View File

@ -476,10 +476,14 @@ private:
nsConnectionEntry *ent,
bool considerAll);
// Given the connection entry, return the available count for creating
// new connections. Return 0 if the active connection count is
// exceeded |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|.
uint32_t AvailableNewConnectionCount(nsConnectionEntry * ent);
// Return total active connection count, which is the sum of
// active connections and unconnected half open connections.
uint32_t TotalActiveConnections(nsConnectionEntry *ent) const;
// Return |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|,
// depending whether the proxy is used.
uint32_t MaxPersistConnections(nsConnectionEntry *ent) const;
bool AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps);
MOZ_MUST_USE nsresult TryDispatchTransaction(nsConnectionEntry *ent,
bool onlyReusedConnection,

View File

@ -758,10 +758,6 @@
// See bug 341047 and comments in overflow handler as to why
// try..catch is needed here
this._updateScrollButtonsDisabledState();
let childNodes = this._getScrollableElements();
if (childNodes && childNodes.length)
this.ensureElementIsVisible(childNodes[0], false);
} catch (e) {
this.removeAttribute("notoverflowing");
}

View File

@ -174,7 +174,6 @@ this.AUSTLMY = {
DWNLD_RETRY_NET_RESET: 4,
DWNLD_ERR_NO_UPDATE: 5,
DWNLD_ERR_NO_UPDATE_PATCH: 6,
DWNLD_ERR_NO_PATCH_FILE: 7,
DWNLD_ERR_PATCH_SIZE_LARGER: 8,
DWNLD_ERR_PATCH_SIZE_NOT_EQUAL: 9,
DWNLD_ERR_BINDING_ABORTED: 10,

View File

@ -61,7 +61,7 @@
#define UNEXPECTED_MAR_ERROR 40
#define UNEXPECTED_BSPATCH_ERROR 41
#define UNEXPECTED_FILE_OPERATION_ERROR 42
#define FILESYSTEM_MOUNT_READWRITE_ERROR 43
// #define FILESYSTEM_MOUNT_READWRITE_ERROR 43 // Removed support for gonk
#define DELETE_ERROR_EXPECTED_DIR 46
#define DELETE_ERROR_EXPECTED_FILE 47
#define RENAME_ERROR_EXPECTED_FILE 48

View File

@ -183,12 +183,6 @@ interface nsIUpdate : nsISupports
*/
attribute boolean isSecurityUpdate;
/**
* Whether or not the update being downloaded is an OS update. This is
* generally only possible in Gonk right now.
*/
attribute boolean isOSUpdate;
/**
* When the update was installed.
*/
@ -386,14 +380,6 @@ interface nsIApplicationUpdateService : nsISupports
*/
AString downloadUpdate(in nsIUpdate update, in boolean background);
/**
* Apply the OS update which has been downloaded and staged as applied.
* @param update
* The update has been downloaded and staged as applied.
* @throws if the update object is not an OS update.
*/
void applyOsUpdate(in nsIUpdate update);
/**
* Get the Active Updates directory
* @returns An nsIFile for the active updates directory.

View File

@ -60,8 +60,6 @@ const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.proper
const KEY_UPDROOT = "UpdRootD";
const KEY_EXECUTABLE = "XREExeF";
// Gonk only
const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD";
const DIR_UPDATES = "updates";
@ -83,7 +81,6 @@ const STATE_PENDING_SERVICE = "pending-service";
const STATE_PENDING_ELEVATE = "pending-elevate";
const STATE_APPLYING = "applying";
const STATE_APPLIED = "applied";
const STATE_APPLIED_OS = "applied-os";
const STATE_APPLIED_SERVICE = "applied-service";
const STATE_SUCCEEDED = "succeeded";
const STATE_DOWNLOAD_FAILED = "download-failed";
@ -203,17 +200,6 @@ const APPID_TO_TOPIC = {
var gUpdateMutexHandle = null;
// Gonk only
var gSDCardMountLock = null;
// Gonk only
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
return Services.env.get("EXTERNAL_STORAGE");
});
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
@ -568,13 +554,6 @@ function getCanStageUpdates() {
return true;
}
// For Gonk, the updater will remount the /system partition to move staged
// files into place.
if (AppConstants.platform == "gonk") {
LOG("getCanStageUpdates - able to stage updates because this is gonk");
return true;
}
if (!hasUpdateMutex()) {
LOG("getCanStageUpdates - unable to apply updates because another " +
"instance of the application is already handling updates for this " +
@ -799,105 +778,6 @@ function writeVersionFile(dir, version) {
writeStringToFile(versionFile, version);
}
/**
* Gonk only function that reads the link file specified in the update.link file
* in the specified directory and returns the nsIFile for the corresponding
* file.
* @param dir
* The dir to look for an update.link file in
* @return A nsIFile for the file path specified in the
* update.link file or null if the update.link file
* doesn't exist.
*/
function getFileFromUpdateLink(dir) {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
let linkFile = dir.clone();
linkFile.append(FILE_UPDATE_LINK);
let link = readStringFromFile(linkFile);
LOG("getFileFromUpdateLink linkFile.path: " + linkFile.path + ", link: " + link);
if (!link) {
return null;
}
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(link);
return file;
}
/**
* Gonk only function to create a link file. This allows the actual patch to
* live in a directory different from the update directory.
* @param dir
* The patch directory where the update.link file
* should be written.
* @param patchFile
* The fully qualified filename of the patchfile.
*/
function writeLinkFile(dir, patchFile) {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
let linkFile = dir.clone();
linkFile.append(FILE_UPDATE_LINK);
writeStringToFile(linkFile, patchFile.path);
if (patchFile.path.indexOf(gExtStorage) == 0) {
// The patchfile is being stored on external storage. Try to lock it
// so that it doesn't get shared with the PC while we're downloading
// to it.
acquireSDCardMountLock();
}
}
/**
* Gonk only function to acquire a VolumeMountLock for the sdcard volume.
*
* This prevents the SDCard from being shared with the PC while
* we're downloading the update.
*/
function acquireSDCardMountLock() {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
let volsvc = Cc["@mozilla.org/telephony/volume-service;1"].
getService(Ci.nsIVolumeService);
if (volsvc) {
gSDCardMountLock = volsvc.createMountLock("sdcard");
}
}
/**
* Gonk only function that determines if the state corresponds to an
* interrupted update. This could either be because the download was
* interrupted, or because staging the update was interrupted.
*
* @return true if the state corresponds to an interrupted
* update.
*/
function isInterruptedUpdate(status) {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
return (status == STATE_DOWNLOADING) ||
(status == STATE_PENDING) ||
(status == STATE_APPLYING);
}
/**
* Releases any SDCard mount lock that we might have.
*
* This once again allows the SDCard to be shared with the PC.
*/
function releaseSDCardMountLock() {
if (AppConstants.platform != "gonk") {
throw Cr.NS_ERROR_UNEXPECTED;
}
if (gSDCardMountLock) {
gSDCardMountLock.unlock();
gSDCardMountLock = null;
}
}
/**
* Determines if the service should be used to attempt an update
* or not.
@ -1060,15 +940,6 @@ function cleanUpUpdatesDir(aRemovePatchFiles = true) {
let dirEntries = updateDir.directoryEntries;
while (dirEntries.hasMoreElements()) {
let file = dirEntries.getNext().QueryInterface(Ci.nsIFile);
if (AppConstants.platform == "gonk") {
if (file.leafName == FILE_UPDATE_LINK) {
let linkedFile = getFileFromUpdateLink(updateDir);
if (linkedFile && linkedFile.exists()) {
linkedFile.remove(false);
}
}
}
// Now, recursively remove this file. The recursive removal is needed for
// Mac OSX because this directory will contain a copy of updater.app,
// which is itself a directory and the MozUpdater directory on platforms
@ -1080,9 +951,6 @@ function cleanUpUpdatesDir(aRemovePatchFiles = true) {
}
}
}
if (AppConstants.platform == "gonk") {
releaseSDCardMountLock();
}
}
/**
@ -1350,9 +1218,6 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
case STATE_APPLIED:
stateCode = 7;
break;
case STATE_APPLIED_OS:
stateCode = 8;
break;
case STATE_APPLIED_SERVICE:
stateCode = 9;
break;
@ -1830,12 +1695,6 @@ function UpdateService() {
LOG("Creating UpdateService");
Services.obs.addObserver(this, "xpcom-shutdown");
Services.prefs.addObserver(PREF_APP_UPDATE_LOG, this);
if (AppConstants.platform == "gonk") {
// PowerManagerService::SyncProfile (which is called for Reboot, PowerOff
// and Restart) sends the profile-change-net-teardown event. We can then
// pause the download in a similar manner to xpcom-shutdown.
Services.obs.addObserver(this, "profile-change-net-teardown");
}
}
UpdateService.prototype = {
@ -1917,7 +1776,6 @@ UpdateService.prototype = {
gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
}
break;
case "profile-change-net-teardown": // fall thru
case "xpcom-shutdown":
Services.obs.removeObserver(this, topic);
Services.prefs.removeObserver(PREF_APP_UPDATE_LOG, this);
@ -1983,23 +1841,6 @@ UpdateService.prototype = {
return;
}
if (AppConstants.platform == "gonk") {
// This code is called very early in the boot process, before we've even
// had a chance to setup the UI so we can give feedback to the user.
//
// Since the download may be occuring over a link which has associated
// cost, we want to require user-consent before resuming the download.
// Also, applying an already downloaded update now is undesireable,
// since the phone will look dead while the update is being applied.
// Applying the update can take several minutes. Instead we wait until
// the UI is initialized so it is possible to give feedback to and get
// consent to update from the user.
if (isInterruptedUpdate(status)) {
LOG("UpdateService:_postUpdateProcessing - interrupted update detected - wait for user consent");
return;
}
}
if (status == STATE_DOWNLOADING) {
LOG("UpdateService:_postUpdateProcessing - patch found in downloading " +
"state");
@ -2041,34 +1882,6 @@ UpdateService.prototype = {
return;
}
if (AppConstants.platform == "gonk") {
// The update is only applied but not selected to be installed
if (status == STATE_APPLIED && update && update.isOSUpdate) {
LOG("UpdateService:_postUpdateProcessing - update staged as applied found");
return;
}
if (status == STATE_APPLIED_OS && update && update.isOSUpdate) {
// In gonk, we need to check for OS update status after startup, since
// the recovery partition won't write to update.status for us
let recoveryService = Cc["@mozilla.org/recovery-service;1"].
getService(Ci.nsIRecoveryService);
let fotaStatus = recoveryService.getFotaUpdateStatus();
switch (fotaStatus) {
case Ci.nsIRecoveryService.FOTA_UPDATE_SUCCESS:
status = STATE_SUCCEEDED;
break;
case Ci.nsIRecoveryService.FOTA_UPDATE_FAIL:
status = STATE_FAILED + ": " + FOTA_GENERAL_ERROR;
break;
case Ci.nsIRecoveryService.FOTA_UPDATE_UNKNOWN:
default:
status = STATE_FAILED + ": " + FOTA_UNKNOWN_ERROR;
break;
}
}
}
if (!update) {
if (status != STATE_SUCCEEDED) {
LOG("UpdateService:_postUpdateProcessing - previous patch failed " +
@ -2527,11 +2340,6 @@ UpdateService.prototype = {
var um = Cc["@mozilla.org/updates/update-manager;1"].
getService(Ci.nsIUpdateManager);
if (um.activeUpdate) {
if (AppConstants.platform == "gonk") {
// For gonk, the user isn't necessarily aware of the update, so we need
// to show the prompt to make sure.
this._showPrompt(um.activeUpdate);
}
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_HAS_ACTIVEUPDATE);
return;
}
@ -2734,21 +2542,6 @@ UpdateService.prototype = {
}
this._downloader.cancel();
}
if (AppConstants.platform == "gonk") {
let um = Cc["@mozilla.org/updates/update-manager;1"].
getService(Ci.nsIUpdateManager);
let activeUpdate = um.activeUpdate;
if (activeUpdate &&
(activeUpdate.appVersion != update.appVersion ||
activeUpdate.buildID != update.buildID)) {
// We have an activeUpdate (which presumably was interrupted), and are
// about start downloading a new one. Make sure we remove all traces
// of the active one (otherwise we'll start appending the new update.mar
// the the one that's been partially downloaded).
LOG("UpdateService:downloadUpdate - removing stale active update.");
cleanupActiveUpdate();
}
}
// Set the previous application version prior to downloading the update.
update.previousAppVersion = Services.appinfo.version;
this._downloader = new Downloader(background, this);
@ -2782,55 +2575,6 @@ UpdateService.prototype = {
return this._downloader && this._downloader.isBusy;
},
/**
* See nsIUpdateService.idl
*/
applyOsUpdate: function AUS_applyOsUpdate(aUpdate) {
if (!aUpdate.isOSUpdate || aUpdate.state != STATE_APPLIED) {
aUpdate.statusText = "fota-state-error";
throw Cr.NS_ERROR_FAILURE;
}
aUpdate.QueryInterface(Ci.nsIWritablePropertyBag);
let osApplyToDir = aUpdate.getProperty("osApplyToDir");
if (!osApplyToDir) {
LOG("UpdateService:applyOsUpdate - Error: osApplyToDir is not defined" +
"in the nsIUpdate!");
pingStateAndStatusCodes(aUpdate, false,
STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR);
handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR);
return;
}
let updateFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
updateFile.initWithPath(osApplyToDir + "/update.zip");
if (!updateFile.exists()) {
LOG("UpdateService:applyOsUpdate - Error: OS update is not found at " +
updateFile.path);
pingStateAndStatusCodes(aUpdate, false,
STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR);
handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR);
return;
}
writeStatusFile(getUpdatesDir(), aUpdate.state = STATE_APPLIED_OS);
LOG("UpdateService:applyOsUpdate - Rebooting into recovery to apply " +
"FOTA update: " + updateFile.path);
try {
let recoveryService = Cc["@mozilla.org/recovery-service;1"]
.getService(Ci.nsIRecoveryService);
recoveryService.installFotaUpdate(updateFile.path);
} catch (e) {
LOG("UpdateService:applyOsUpdate - Error: Couldn't reboot into recovery" +
" to apply FOTA update " + updateFile.path);
pingStateAndStatusCodes(aUpdate, false,
STATE_FAILED + ": " + FOTA_RECOVERY_ERROR);
writeStatusFile(getUpdatesDir(), aUpdate.state = STATE_APPLIED);
handleUpdateFailure(aUpdate, FOTA_RECOVERY_ERROR);
}
},
classID: UPDATESERVICE_CID,
classInfo: XPCOMUtils.generateCI({classID: UPDATESERVICE_CID,
contractID: UPDATESERVICE_CONTRACTID,
@ -3156,21 +2900,6 @@ UpdateManager.prototype = {
"the update was staged. topic: update-staged, status: " + update.state);
Services.obs.notifyObservers(update, "update-staged", update.state);
if (AppConstants.platform == "gonk") {
// Do this after everything else, since it will likely cause the app to
// shut down.
if (update.state == STATE_APPLIED) {
// Notify the user that an update has been staged and is ready for
// installation (i.e. that they should restart the application). We do
// not notify on failed update attempts.
let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateDownloaded(update, true);
} else {
releaseSDCardMountLock();
}
return;
}
// Only prompt when the UI isn't already open.
let windowType = getPref("getCharPref", PREF_APP_UPDATE_ALTWINDOWTYPE, null);
if (Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME) ||
@ -3522,9 +3251,6 @@ Downloader.prototype = {
if (this._request && this._request instanceof Ci.nsIRequest) {
this._request.cancel(cancelError);
}
if (AppConstants.platform == "gonk") {
releaseSDCardMountLock();
}
},
/**
@ -3653,19 +3379,10 @@ Downloader.prototype = {
LOG("Downloader:_selectPatch - resuming download");
return selectedPatch;
}
if (AppConstants.platform == "gonk") {
if (state == STATE_PENDING || state == STATE_APPLYING) {
LOG("Downloader:_selectPatch - resuming interrupted apply");
return selectedPatch;
}
if (state == STATE_APPLIED) {
LOG("Downloader:_selectPatch - already downloaded and staged");
return null;
}
} else if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
state == STATE_PENDING_ELEVATE) {
LOG("Downloader:_selectPatch - already downloaded and staged");
if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
state == STATE_PENDING_ELEVATE || state == STATE_APPLIED ||
state == STATE_APPLIED_SERVICE) {
LOG("Downloader:_selectPatch - already downloaded");
return null;
}
@ -3721,25 +3438,6 @@ Downloader.prototype = {
return this._request != null;
},
/**
* Get the nsIFile to use for downloading the active update's selected patch
*/
_getUpdateArchiveFile: function Downloader__getUpdateArchiveFile() {
var updateArchive;
if (AppConstants.platform == "gonk") {
try {
updateArchive = FileUtils.getDir(KEY_UPDATE_ARCHIVE_DIR, [], true);
} catch (e) {
return null;
}
} else {
updateArchive = getUpdatesDir().clone();
}
updateArchive.append(FILE_UPDATE_MAR);
return updateArchive;
},
/**
* Download and stage the given update.
* @param update
@ -3766,96 +3464,8 @@ Downloader.prototype = {
}
this.isCompleteUpdate = this._patch.type == "complete";
let patchFile = null;
// Only used by gonk
let status = STATE_NONE;
if (AppConstants.platform == "gonk") {
status = readStatusFile(updateDir);
if (isInterruptedUpdate(status)) {
LOG("Downloader:downloadUpdate - interruptted update");
// The update was interrupted. Try to locate the existing patch file.
// For an interrupted download, this allows a resume rather than a
// re-download.
patchFile = getFileFromUpdateLink(updateDir);
if (!patchFile) {
// No link file. We'll just assume that the update.mar is in the
// update directory.
patchFile = updateDir.clone();
patchFile.append(FILE_UPDATE_MAR);
}
if (patchFile.exists()) {
LOG("Downloader:downloadUpdate - resuming with patchFile " + patchFile.path);
if (patchFile.fileSize == this._patch.size) {
LOG("Downloader:downloadUpdate - patchFile appears to be fully downloaded");
// Bump the status along so that we don't try to redownload again.
if (getElevationRequired()) {
status = STATE_PENDING_ELEVATE;
} else {
status = STATE_PENDING;
}
}
} else {
LOG("Downloader:downloadUpdate - patchFile " + patchFile.path +
" doesn't exist - performing full download");
// The patchfile doesn't exist, we might as well treat this like
// a new download.
patchFile = null;
}
if (patchFile && status != STATE_DOWNLOADING) {
// It looks like the patch was downloaded, but got interrupted while it
// was being verified or applied. So we'll fake the downloading portion.
if (getElevationRequired()) {
writeStatusFile(updateDir, STATE_PENDING_ELEVATE);
} else {
writeStatusFile(updateDir, STATE_PENDING);
}
// Since the code expects the onStopRequest callback to happen
// asynchronously (And you have to call AUS_addDownloadListener
// after calling AUS_downloadUpdate) we need to defer this.
this._downloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._downloadTimer.initWithCallback(function() {
this._downloadTimer = null;
// Send a fake onStopRequest. Filling in the destination allows
// _verifyDownload to work, and then the update will be applied.
this._request = {destination: patchFile};
this.onStopRequest(this._request, null, Cr.NS_OK);
}.bind(this), 0, Ci.nsITimer.TYPE_ONE_SHOT);
// Returning STATE_DOWNLOADING makes UpdatePrompt think we're
// downloading. The onStopRequest that we spoofed above will make it
// look like the download finished.
return STATE_DOWNLOADING;
}
}
}
if (!patchFile) {
// Find a place to put the patchfile that we're going to download.
patchFile = this._getUpdateArchiveFile();
}
if (!patchFile) {
AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
AUSTLMY.DWNLD_ERR_NO_PATCH_FILE);
return STATE_NONE;
}
if (AppConstants.platform == "gonk") {
if (patchFile.path.indexOf(updateDir.path) != 0) {
// The patchFile is in a directory which is different from the
// updateDir, create a link file.
writeLinkFile(updateDir, patchFile);
if (!isInterruptedUpdate(status) && patchFile.exists()) {
// Remove stale patchFile
patchFile.remove(false);
}
}
}
let patchFile = getUpdatesDir().clone();
patchFile.append(FILE_UPDATE_MAR);
update.QueryInterface(Ci.nsIPropertyBag);
let interval = this.background ? update.getProperty("backgroundInterval")
: DOWNLOAD_FOREGROUND_INTERVAL;
@ -4127,13 +3737,6 @@ Downloader.prototype = {
this._update.statusText = getStatusTextFromCode(status,
Cr.NS_BINDING_FAILED);
if (AppConstants.platform == "gonk") {
// bug891009: On FirefoxOS, manaully retry OTA download will reuse
// the Update object. We need to remove selected patch so that download
// can be triggered again successfully.
this._update.selectedPatch.selected = false;
}
// Destroy the updates directory, since we're done with it.
cleanUpUpdatesDir();
@ -4213,13 +3816,6 @@ Downloader.prototype = {
}
}
if (AppConstants.platform == "gonk") {
// We always forward errors in B2G, since Gaia controls the update UI
let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateError(this._update);
}
// Prevent leaking the update object (bug 454964).
this._update = null;
}