зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c
This commit is contained in:
Коммит
00ff571d69
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const ContentWorker = Object.freeze({
|
||||
Object.freeze({
|
||||
// TODO: Bug 727854 Use same implementation than common JS modules,
|
||||
// i.e. EventEmitter module
|
||||
|
||||
|
@ -70,6 +70,7 @@ const ContentWorker = Object.freeze({
|
|||
* onChromeEvent --> callback registered through pipe.on
|
||||
*/
|
||||
createPipe: function createPipe(emitToChrome) {
|
||||
let ContentWorker = this;
|
||||
function onEvent(type, ...args) {
|
||||
// JSON.stringify is buggy with cross-sandbox values,
|
||||
// it may return "{}" on functions. Use a replacer to match them correctly.
|
||||
|
@ -271,6 +272,7 @@ const ContentWorker = Object.freeze({
|
|||
|
||||
injectMessageAPI: function injectMessageAPI(exports, pipe, console) {
|
||||
|
||||
let ContentWorker = this;
|
||||
let { eventEmitter: port, emit : portEmit } =
|
||||
ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
|
||||
pipe.on("event", portEmit);
|
||||
|
@ -322,6 +324,7 @@ const ContentWorker = Object.freeze({
|
|||
},
|
||||
|
||||
inject: function (exports, chromeAPI, emitToChrome, options) {
|
||||
let ContentWorker = this;
|
||||
let { pipe, onChromeEvent, hasListenerFor } =
|
||||
ContentWorker.createPipe(emitToChrome);
|
||||
|
||||
|
|
|
@ -132,11 +132,6 @@ const WorkerSandbox = Class({
|
|||
principals = EXPANDED_PRINCIPALS.concat(window);
|
||||
}
|
||||
|
||||
// Instantiate trusted code in another Sandbox in order to prevent content
|
||||
// script from messing with standard classes used by proxy and API code.
|
||||
let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window });
|
||||
apiSandbox.console = console;
|
||||
|
||||
// Create the sandbox and bind it to window in order for content scripts to
|
||||
// have access to all standard globals (window, document, ...)
|
||||
let content = sandbox(principals, {
|
||||
|
@ -171,9 +166,7 @@ const WorkerSandbox = Class({
|
|||
});
|
||||
|
||||
// Load trusted code that will inject content script API.
|
||||
// We need to expose JS objects defined in same principal in order to
|
||||
// avoid having any kind of wrapper.
|
||||
load(apiSandbox, CONTENT_WORKER_URL);
|
||||
let ContentWorker = load(content, CONTENT_WORKER_URL);
|
||||
|
||||
// prepare a clean `self.options`
|
||||
let options = 'contentScriptOptions' in worker ?
|
||||
|
@ -189,9 +182,8 @@ const WorkerSandbox = Class({
|
|||
// content priviledges
|
||||
// https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers
|
||||
let onEvent = onContentEvent.bind(null, this);
|
||||
// `ContentWorker` is defined in CONTENT_WORKER_URL file
|
||||
let chromeAPI = createChromeAPI();
|
||||
let result = apiSandbox.ContentWorker.inject(content, chromeAPI, onEvent, options);
|
||||
let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
|
||||
|
||||
// Merge `emitToContent` and `hasListenerFor` into our private
|
||||
// model of the WorkerSandbox so we can communicate with content
|
||||
|
|
|
@ -145,11 +145,6 @@ const WorkerSandbox = EventEmitter.compose({
|
|||
wantGlobalProperties.push("XMLHttpRequest");
|
||||
}
|
||||
|
||||
// Instantiate trusted code in another Sandbox in order to prevent content
|
||||
// script from messing with standard classes used by proxy and API code.
|
||||
let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window });
|
||||
apiSandbox.console = console;
|
||||
|
||||
// Create the sandbox and bind it to window in order for content scripts to
|
||||
// have access to all standard globals (window, document, ...)
|
||||
let content = this._sandbox = sandbox(principals, {
|
||||
|
@ -181,9 +176,7 @@ const WorkerSandbox = EventEmitter.compose({
|
|||
});
|
||||
|
||||
// Load trusted code that will inject content script API.
|
||||
// We need to expose JS objects defined in same principal in order to
|
||||
// avoid having any kind of wrapper.
|
||||
load(apiSandbox, CONTENT_WORKER_URL);
|
||||
let ContentWorker = load(content, CONTENT_WORKER_URL);
|
||||
|
||||
// prepare a clean `self.options`
|
||||
let options = 'contentScriptOptions' in worker ?
|
||||
|
@ -223,8 +216,7 @@ const WorkerSandbox = EventEmitter.compose({
|
|||
}
|
||||
};
|
||||
let onEvent = this._onContentEvent.bind(this);
|
||||
// `ContentWorker` is defined in CONTENT_WORKER_URL file
|
||||
let result = apiSandbox.ContentWorker.inject(content, chromeAPI, onEvent, options);
|
||||
let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
|
||||
this._emitToContent = result.emitToContent;
|
||||
this._hasListenerFor = result.hasListenerFor;
|
||||
|
||||
|
|
|
@ -42,6 +42,39 @@ XPCOMUtils.defineLazyServiceGetter(this,
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
|
||||
"resource://gre/modules/SystemAppProxy.jsm");
|
||||
|
||||
/**
|
||||
* Determine if a permission should be prompt to user or not.
|
||||
*
|
||||
* @param aPerm requested permission
|
||||
* @param aAction the action according to principal
|
||||
* @return true if prompt is required
|
||||
*/
|
||||
function shouldPrompt(aPerm, aAction) {
|
||||
return ((aAction == Ci.nsIPermissionManager.PROMPT_ACTION) ||
|
||||
(aAction == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the default choices for the requested permissions
|
||||
*
|
||||
* @param aTypesInfo requested permissions
|
||||
* @return the default choices for permissions with options, return
|
||||
* undefined if no option in all requested permissions.
|
||||
*/
|
||||
function buildDefaultChoices(aTypesInfo) {
|
||||
let choices;
|
||||
for (let type of aTypesInfo) {
|
||||
if (type.options.length > 0) {
|
||||
if (!choices) {
|
||||
choices = {};
|
||||
}
|
||||
choices[type.access] = type.options[0];
|
||||
}
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* aTypesInfo is an array of {permission, access, action, deny} which keeps
|
||||
* the information of each permission. This arrary is initialized in
|
||||
|
@ -62,9 +95,7 @@ function rememberPermission(aTypesInfo, aPrincipal, aSession)
|
|||
{
|
||||
let type =
|
||||
permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
|
||||
if (type == Ci.nsIPermissionManager.PROMPT_ACTION ||
|
||||
(type == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) {
|
||||
if (shouldPrompt(aPerm, type)) {
|
||||
debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
|
||||
if (!aSession) {
|
||||
permissionManager.addFromPrincipal(aPrincipal,
|
||||
|
@ -104,22 +135,23 @@ ContentPermissionPrompt.prototype = {
|
|||
type.action =
|
||||
Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
type.access);
|
||||
if (type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(type.access) >= 0) {
|
||||
if (shouldPrompt(type.access, type.action)) {
|
||||
type.action = Ci.nsIPermissionManager.PROMPT_ACTION;
|
||||
}
|
||||
});
|
||||
|
||||
// If all permissions are allowed already, call allow() without prompting.
|
||||
// If all permissions are allowed already and no more than one option,
|
||||
// call allow() without prompting.
|
||||
let checkAllowPermission = function(type) {
|
||||
if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION &&
|
||||
type.options.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (typesInfo.every(checkAllowPermission)) {
|
||||
debug("all permission requests are allowed");
|
||||
request.allow();
|
||||
request.allow(buildDefaultChoices(typesInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -185,7 +217,8 @@ ContentPermissionPrompt.prototype = {
|
|||
}
|
||||
return !type.deny;
|
||||
}
|
||||
if (typesInfo.filter(notDenyAppPrincipal).length === 0) {
|
||||
// Cancel the entire request if one of the requested permissions is denied
|
||||
if (!typesInfo.every(notDenyAppPrincipal)) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
|
@ -206,11 +239,6 @@ ContentPermissionPrompt.prototype = {
|
|||
|
||||
_id: 0,
|
||||
prompt: function(request) {
|
||||
if (secMan.isSystemPrincipal(request.principal)) {
|
||||
request.allow();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the typesInfo and set the default value.
|
||||
let typesInfo = [];
|
||||
let perms = request.types.QueryInterface(Ci.nsIArray);
|
||||
|
@ -234,6 +262,12 @@ ContentPermissionPrompt.prototype = {
|
|||
typesInfo.push(tmp);
|
||||
}
|
||||
|
||||
if (secMan.isSystemPrincipal(request.principal)) {
|
||||
request.allow(buildDefaultChoices(typesInfo));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (typesInfo.length == 0) {
|
||||
request.cancel();
|
||||
return;
|
||||
|
@ -254,11 +288,9 @@ ContentPermissionPrompt.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// prompt PROMPT_ACTION request only.
|
||||
typesInfo.forEach(function(aType, aIndex) {
|
||||
if (aType.action != Ci.nsIPermissionManager.PROMPT_ACTION || aType.deny) {
|
||||
typesInfo.splice(aIndex);
|
||||
}
|
||||
// prompt PROMPT_ACTION request or request with options.
|
||||
typesInfo = typesInfo.filter(function(type) {
|
||||
return !type.deny && (type.action == Ci.nsIPermissionManager.PROMPT_ACTION || type.options.length > 0) ;
|
||||
});
|
||||
|
||||
let frame = request.element;
|
||||
|
@ -366,6 +398,9 @@ ContentPermissionPrompt.prototype = {
|
|||
principal.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED)
|
||||
? true
|
||||
: request.remember;
|
||||
let isGranted = typesInfo.every(function(type) {
|
||||
return type.action == Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
});
|
||||
let permissions = {};
|
||||
for (let i in typesInfo) {
|
||||
debug("prompt " + typesInfo[i].permission);
|
||||
|
@ -378,7 +413,8 @@ ContentPermissionPrompt.prototype = {
|
|||
id: requestId,
|
||||
origin: principal.origin,
|
||||
isApp: isApp,
|
||||
remember: remember
|
||||
remember: remember,
|
||||
isGranted: isGranted,
|
||||
};
|
||||
|
||||
if (isApp) {
|
||||
|
|
|
@ -11,4 +11,6 @@ run-if = toolkit == "gonk"
|
|||
run-if = toolkit == "gonk"
|
||||
[test_permission_deny.html]
|
||||
run-if = toolkit == "gonk"
|
||||
[test_permission_gum_remember.html]
|
||||
run-if = toolkit == "gonk"
|
||||
[test_systemapp.html]
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=978660
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>gUM Remember Permission Test</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978660">Test remembering gUM Permission</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const PROMPT_ACTION = SpecialPowers.Ci.nsIPermissionManager.PROMPT_ACTION;
|
||||
|
||||
var gUrl = SimpleTest.getTestFileURL('permission_handler_chrome.js');
|
||||
var gScript = SpecialPowers.loadChromeScript(gUrl);
|
||||
gScript.addMessageListener('permission-request', function(detail) {
|
||||
ok(false, 'unexpected mozChromeEvent for permission prompt');
|
||||
let response = {
|
||||
id: detail.id,
|
||||
type: 'permission-deny',
|
||||
remember: false,
|
||||
};
|
||||
gScript.sendAsyncMessage('permission-response', response);
|
||||
});
|
||||
|
||||
var gTests = [
|
||||
{
|
||||
'audio': true,
|
||||
'video': {facingMode: 'environment', required: ['facingMode']},
|
||||
},
|
||||
{
|
||||
'video': {facingMode: 'environment', required: ['facingMode']},
|
||||
},
|
||||
{
|
||||
'audio': true,
|
||||
},
|
||||
];
|
||||
|
||||
function testGranted() {
|
||||
info('test remember permission granted');
|
||||
return new Promise(function(resolve, reject) {
|
||||
let steps = [].concat(gTests);
|
||||
function nextStep() {
|
||||
if (steps.length > 0) {
|
||||
let requestedType = steps.shift();
|
||||
info('getUserMedia for ' + JSON.stringify(requestedType));
|
||||
navigator.mozGetUserMedia(requestedType, function success() {
|
||||
ok(true, 'expected gUM success');
|
||||
nextStep();
|
||||
}, function failure(err) {
|
||||
ok(false, 'unexpected gUM fail: ' + err);
|
||||
nextStep();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'video-capture', allow: true, context: document},
|
||||
{type: 'audio-capture', allow: true, context: document},
|
||||
], nextStep);
|
||||
});
|
||||
}
|
||||
|
||||
function testDenied() {
|
||||
info('test remember permission denied');
|
||||
return new Promise(function(resolve, reject) {
|
||||
let steps = [].concat(gTests);
|
||||
function nextStep() {
|
||||
if (steps.length > 0) {
|
||||
let requestedType = steps.shift();
|
||||
info('getUserMedia for ' + JSON.stringify(requestedType));
|
||||
navigator.mozGetUserMedia(requestedType, function success() {
|
||||
ok(false, 'unexpected gUM success');
|
||||
nextStep();
|
||||
}, function failure(err) {
|
||||
ok(true, 'expected gUM fail: ' + err);
|
||||
nextStep();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'video-capture', allow: false, context: document},
|
||||
{type: 'audio-capture', allow: false, context: document},
|
||||
], nextStep);
|
||||
});
|
||||
}
|
||||
|
||||
function testPartialDeniedAudio() {
|
||||
info('test remember permission partial denied: audio');
|
||||
return new Promise(function(resolve, reject) {
|
||||
info('getUserMedia for video and audio');
|
||||
function nextStep() {
|
||||
navigator.mozGetUserMedia({video: {facingMode: 'environment', required: ['facingMode']},
|
||||
audio: true}, function success() {
|
||||
ok(false, 'unexpected gUM success');
|
||||
resolve();
|
||||
}, function failure(err) {
|
||||
ok(true, 'expected gUM fail: ' + err);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'video-capture', allow: true, context: document},
|
||||
{type: 'audio-capture', allow: false, context: document},
|
||||
], nextStep);
|
||||
});
|
||||
}
|
||||
|
||||
function testPartialDeniedVideo() {
|
||||
info('test remember permission partial denied: video');
|
||||
return new Promise(function(resolve, reject) {
|
||||
info('getUserMedia for video and audio');
|
||||
function nextStep() {
|
||||
navigator.mozGetUserMedia({video: {facingMode: 'environment', required: ['facingMode']},
|
||||
audio: true}, function success() {
|
||||
ok(false, 'unexpected gUM success');
|
||||
resolve();
|
||||
}, function failure(err) {
|
||||
ok(true, 'expected gUM fail: ' + err);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'video-capture', allow: false, context: document},
|
||||
{type: 'audio-capture', allow: true, context: document},
|
||||
], nextStep);
|
||||
});
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testGranted()
|
||||
.then(testDenied)
|
||||
.then(testPartialDeniedAudio)
|
||||
.then(testPartialDeniedVideo)
|
||||
.then(function() {
|
||||
info('test finished, teardown');
|
||||
gScript.sendAsyncMessage('teardown', '');
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
'set': [
|
||||
['media.navigator.permission.disabled', false],
|
||||
]
|
||||
}, runTests);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -431,9 +431,6 @@ var gAdvancedPane = {
|
|||
{
|
||||
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationCacheService);
|
||||
if (!groups)
|
||||
groups = cacheService.getGroups();
|
||||
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
|
@ -462,9 +459,14 @@ var gAdvancedPane = {
|
|||
list.removeChild(list.firstChild);
|
||||
}
|
||||
|
||||
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationCacheService);
|
||||
var groups = cacheService.getGroups();
|
||||
var groups;
|
||||
try {
|
||||
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationCacheService);
|
||||
groups = cacheService.getGroups();
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bundle = document.getElementById("bundlePreferences");
|
||||
|
||||
|
@ -520,18 +522,20 @@ var gAdvancedPane = {
|
|||
return;
|
||||
|
||||
// clear offline cache entries
|
||||
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationCacheService);
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
var groups = cacheService.getGroups();
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var uri = ios.newURI(groups[i], null, null);
|
||||
if (uri.asciiHost == host) {
|
||||
var cache = cacheService.getActiveCache(groups[i]);
|
||||
cache.discard();
|
||||
}
|
||||
}
|
||||
try {
|
||||
var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationCacheService);
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
var groups = cacheService.getGroups();
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var uri = ios.newURI(groups[i], null, null);
|
||||
if (uri.asciiHost == host) {
|
||||
var cache = cacheService.getActiveCache(groups[i]);
|
||||
cache.discard();
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// remove the permission
|
||||
var pm = Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
|
|
|
@ -6,6 +6,7 @@ support-files =
|
|||
[browser_advanced_update.js]
|
||||
[browser_bug410900.js]
|
||||
[browser_bug731866.js]
|
||||
[browser_bug795764_cachedisabled.js]
|
||||
[browser_connection.js]
|
||||
[browser_connection_bug388287.js]
|
||||
[browser_healthreport.js]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.cache.offline.enable");
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("browser.cache.offline.enable", false);
|
||||
|
||||
open_preferences(runTest);
|
||||
}
|
||||
|
||||
function runTest(win) {
|
||||
is(gBrowser.currentURI.spec, "about:preferences", "about:preferences loaded");
|
||||
|
||||
let tab = win.document;
|
||||
let elements = tab.getElementById("mainPrefPane").children;
|
||||
|
||||
// Test if advanced pane is opened correctly
|
||||
win.gotoPref("paneAdvanced");
|
||||
for (let element of elements) {
|
||||
let attributeValue = element.getAttribute("data-category");
|
||||
if (attributeValue == "paneAdvanced") {
|
||||
is_element_visible(element, "Advanced elements should be visible");
|
||||
} else {
|
||||
is_element_hidden(element, "Non-Advanced elements should be hidden");
|
||||
}
|
||||
}
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
win.close();
|
||||
finish();
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- 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/. -->
|
||||
|
@ -7,12 +5,9 @@
|
|||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="30px"
|
||||
height="31px"
|
||||
preserveAspectRatio="none"
|
||||
xml:space="preserve">
|
||||
preserveAspectRatio="none">
|
||||
<defs>
|
||||
<style><![CDATA[
|
||||
|
||||
|
@ -45,11 +40,9 @@
|
|||
]]></style>
|
||||
|
||||
%include ../../base/content/tab-shape.inc.svg
|
||||
</defs>
|
||||
|
||||
<foreignObject class="node" x="0" y="0" width="30" height="31" clip-path="url(#tab-curve-clip-path-@TAB_SIDE@)">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml" style="all:unset">
|
||||
<div id="tab-background-fill"></div>
|
||||
</body>
|
||||
</defs>
|
||||
<foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-@TAB_SIDE@)">
|
||||
<div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
|
||||
</foreignObject>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.6 KiB После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -6,6 +6,8 @@ CXX := @CXX@
|
|||
CXXFLAGS := @CXXFLAGS@
|
||||
LDFLAGS := @LDFLAGS@
|
||||
VPATH := @srcdir@
|
||||
DSO_LDOPTS := @DSO_LDOPTS@
|
||||
DLL_SUFFIX := @DLL_SUFFIX@
|
||||
|
||||
# Helper for end
|
||||
NULL :=
|
||||
|
@ -24,7 +26,7 @@ TESTSRCS := \
|
|||
OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS))
|
||||
TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS))
|
||||
|
||||
PLUGIN := libclang-plugin.so
|
||||
PLUGIN := libclang-plugin.$(DLL_SUFFIX)
|
||||
|
||||
all: $(PLUGIN) $(TESTS)
|
||||
|
||||
|
@ -33,7 +35,7 @@ $(OBJS): %.o: %.cpp Makefile
|
|||
|
||||
$(PLUGIN): $(OBJS)
|
||||
rm -f $@
|
||||
$(CXX) -shared -o $@ $(CXXFLAGS) $(LDFLAGS) $(OBJS) -lclangASTMatchers
|
||||
$(CXX) $(DSO_LDOPTS) -o $@ $(CXXFLAGS) $(OBJS) $(LDFLAGS)
|
||||
|
||||
TESTFLAGS := -fsyntax-only -Xclang -verify \
|
||||
-Xclang -load -Xclang $(CURDIR)/$(PLUGIN) \
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
PLATFORM=`uname`
|
||||
|
||||
# Default srcdir to this directory
|
||||
srcdir=
|
||||
|
||||
|
@ -35,14 +37,31 @@ fi
|
|||
|
||||
echo "$LLVMCONFIG"
|
||||
|
||||
LLVMLIBS="core mc analysis asmparser mcparser bitreader"
|
||||
LLVMCXXFLAGS=`$LLVMCONFIG --cxxflags`
|
||||
LLVMLDFLAGS=`$LLVMCONFIG --ldflags`
|
||||
LLVMLDFLAGS="$LLVMLDFLAGS `$LLVMCONFIG --libs $LLVMLIBS`"
|
||||
|
||||
if [ $PLATFORM == Darwin ]; then
|
||||
DSO_LDOPTS="-dynamiclib -shared"
|
||||
CLANGLDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization \
|
||||
-lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST \
|
||||
-lclangLex -lclangBasic -lclangASTMatchers"
|
||||
DLL_SUFFIX="dylib"
|
||||
else
|
||||
DSO_LDOPTS="-shared"
|
||||
CLANGLDFLAGS=-lclangASTMatchers
|
||||
DLL_SUFFIX="so"
|
||||
fi
|
||||
|
||||
CXXFLAGS="$CXXFLAGS $LLVMCXXFLAGS -fno-rtti -fno-exceptions"
|
||||
LDFLAGS="$LDFLAGS $LLVMLDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $LLVMLDFLAGS $CLANGLDFLAGS"
|
||||
|
||||
cat $srcdir/Makefile.in | sed \
|
||||
-e "s%@CXX@%$CXX%" \
|
||||
-e "s%@CXXFLAGS@%$CXXFLAGS%" \
|
||||
-e "s%@LDFLAGS@%$LDFLAGS%" \
|
||||
-e "s%@srcdir@%$srcdir%" \
|
||||
-e "s%@DSO_LDOPTS@%$DSO_LDOPTS%" \
|
||||
-e "s%@DLL_SUFFIX@%$DLL_SUFFIX%" \
|
||||
> Makefile
|
||||
|
|
|
@ -8,10 +8,12 @@ if [ -d "$topsrcdir/clang" ]; then
|
|||
# mozilla-central based build
|
||||
export CC=$topsrcdir/clang/bin/clang
|
||||
export CXX=$topsrcdir/clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
|
||||
elif [ -d "$topsrcdir/../clang" ]; then
|
||||
# comm-central based build
|
||||
export CC=$topsrcdir/../clang/bin/clang
|
||||
export CXX=$topsrcdir/../clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config
|
||||
fi
|
||||
|
||||
# If not set use the system default clang
|
||||
|
|
|
@ -8131,8 +8131,14 @@ AC_SUBST(MOZ_PIXMAN_LIBS)
|
|||
# Check for headers defining standard int types.
|
||||
if test -n "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h)
|
||||
|
||||
if test "${ac_cv_header_inttypes_h}" = "yes"; then
|
||||
HAVE_INTTYPES_H=1
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(HAVE_INTTYPES_H)
|
||||
|
||||
if test "$MOZ_TREE_CAIRO"; then
|
||||
MOZ_CAIRO_CFLAGS='-I$(LIBXUL_DIST)/include/cairo'
|
||||
AC_DEFINE(MOZ_TREE_CAIRO)
|
||||
|
|
|
@ -37,7 +37,7 @@ interface nsIFrame;
|
|||
* interface to mirror this interface when changing it.
|
||||
*/
|
||||
|
||||
[scriptable, builtinclass, uuid(e3968acd-b796-4ca3-aec0-e7f0880f2219)]
|
||||
[scriptable, builtinclass, uuid(256a5283-ebb5-4430-8e15-5ada92156ef7)]
|
||||
interface nsIImageLoadingContent : imgINotificationObserver
|
||||
{
|
||||
/**
|
||||
|
@ -160,6 +160,13 @@ interface nsIImageLoadingContent : imgINotificationObserver
|
|||
*/
|
||||
void forceImageState(in boolean aForce, in unsigned long long aState);
|
||||
|
||||
/**
|
||||
* The intrinsic size and width of this content. May differ from actual image
|
||||
* size due to things like responsive image density.
|
||||
*/
|
||||
readonly attribute unsigned long naturalWidth;
|
||||
readonly attribute unsigned long naturalHeight;
|
||||
|
||||
/**
|
||||
* A visible count is stored, if it is non-zero then this image is considered
|
||||
* visible. These methods increment, decrement, or return the visible coount.
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/ResponsiveImageSelector.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS0(ResponsiveImageSelector);
|
||||
|
||||
ResponsiveImageSelector::ResponsiveImageSelector(nsIContent *aContent)
|
||||
: mContent(aContent),
|
||||
mBestCandidateIndex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
ResponsiveImageSelector::~ResponsiveImageSelector()
|
||||
{}
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#processing-the-image-candidates
|
||||
bool
|
||||
ResponsiveImageSelector::SetCandidatesFromSourceSet(const nsAString & aSrcSet)
|
||||
{
|
||||
nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
|
||||
nsCOMPtr<nsIURI> docBaseURI = mContent ? mContent->GetBaseURI() : nullptr;
|
||||
|
||||
if (!mContent || !doc || !docBaseURI) {
|
||||
MOZ_ASSERT(false,
|
||||
"Should not be parsing SourceSet without a content and document");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preserve the default source if we have one, it has a separate setter.
|
||||
uint32_t prevNumCandidates = mCandidates.Length();
|
||||
nsCOMPtr<nsIURI> defaultURL;
|
||||
if (prevNumCandidates && (mCandidates[prevNumCandidates - 1].Type() ==
|
||||
ResponsiveImageCandidate::eCandidateType_Default)) {
|
||||
defaultURL = mCandidates[prevNumCandidates - 1].URL();
|
||||
}
|
||||
|
||||
mCandidates.Clear();
|
||||
|
||||
nsAString::const_iterator iter, end;
|
||||
aSrcSet.BeginReading(iter);
|
||||
aSrcSet.EndReading(end);
|
||||
|
||||
// Read URL / descriptor pairs
|
||||
while (iter != end) {
|
||||
nsAString::const_iterator url, desc;
|
||||
|
||||
// Skip whitespace
|
||||
for (; iter != end && nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
|
||||
|
||||
if (iter == end) {
|
||||
break;
|
||||
}
|
||||
|
||||
url = iter;
|
||||
|
||||
// Find end of url
|
||||
for (;iter != end && !nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
|
||||
|
||||
desc = iter;
|
||||
|
||||
// Find end of descriptor
|
||||
for (; iter != end && *iter != char16_t(','); ++iter);
|
||||
const nsDependentSubstring &descriptor = Substring(desc, iter);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> candidateURL;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(candidateURL),
|
||||
Substring(url, desc),
|
||||
doc,
|
||||
docBaseURI);
|
||||
if (NS_SUCCEEDED(rv) && candidateURL) {
|
||||
NS_TryToSetImmutable(candidateURL);
|
||||
ResponsiveImageCandidate candidate;
|
||||
if (candidate.SetParamaterFromDescriptor(descriptor)) {
|
||||
candidate.SetURL(candidateURL);
|
||||
AppendCandidateIfUnique(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
// Advance past comma
|
||||
if (iter != end) {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
bool parsedCandidates = mCandidates.Length() > 0;
|
||||
|
||||
// Re-add default to end of list
|
||||
if (defaultURL) {
|
||||
AppendDefaultCandidate(defaultURL);
|
||||
}
|
||||
|
||||
return parsedCandidates;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ResponsiveImageSelector::SetDefaultSource(const nsAString & aSpec)
|
||||
{
|
||||
nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
|
||||
nsCOMPtr<nsIURI> docBaseURI = mContent ? mContent->GetBaseURI() : nullptr;
|
||||
|
||||
if (!mContent || !doc || !docBaseURI) {
|
||||
MOZ_ASSERT(false,
|
||||
"Should not be calling this without a content and document");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (aSpec.IsEmpty()) {
|
||||
SetDefaultSource(nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> candidateURL;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(candidateURL),
|
||||
aSpec, doc, docBaseURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SetDefaultSource(candidateURL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageSelector::SetDefaultSource(nsIURI *aURL)
|
||||
{
|
||||
// Check if the last element of our candidates is a default
|
||||
int32_t candidates = mCandidates.Length();
|
||||
if (candidates && (mCandidates[candidates - 1].Type() ==
|
||||
ResponsiveImageCandidate::eCandidateType_Default)) {
|
||||
mCandidates.RemoveElementAt(candidates - 1);
|
||||
if (mBestCandidateIndex == candidates - 1) {
|
||||
mBestCandidateIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new default if set
|
||||
if (aURL) {
|
||||
AppendDefaultCandidate(aURL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageSelector::AppendCandidateIfUnique(const ResponsiveImageCandidate & aCandidate)
|
||||
{
|
||||
// Discard candidates with identical parameters, they will never match
|
||||
int numCandidates = mCandidates.Length();
|
||||
for (int i = 0; i < numCandidates; i++) {
|
||||
if (mCandidates[i].HasSameParameter(aCandidate)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mBestCandidateIndex = -1;
|
||||
mCandidates.AppendElement(aCandidate);
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageSelector::AppendDefaultCandidate(nsIURI *aURL)
|
||||
{
|
||||
NS_ENSURE_TRUE(aURL, /* void */);
|
||||
|
||||
ResponsiveImageCandidate defaultCandidate;
|
||||
defaultCandidate.SetParameterDefault();
|
||||
defaultCandidate.SetURL(aURL);
|
||||
// We don't use MaybeAppend since we want to keep this even if it can never
|
||||
// match, as it may if the source set changes.
|
||||
mBestCandidateIndex = -1;
|
||||
mCandidates.AppendElement(defaultCandidate);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
ResponsiveImageSelector::GetSelectedImageURL()
|
||||
{
|
||||
int bestIndex = GetBestCandidateIndex();
|
||||
if (bestIndex < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> bestURL = mCandidates[bestIndex].URL();
|
||||
MOZ_ASSERT(bestURL, "Shouldn't have candidates with no URL in the array");
|
||||
return bestURL.forget();
|
||||
}
|
||||
|
||||
double
|
||||
ResponsiveImageSelector::GetSelectedImageDensity()
|
||||
{
|
||||
int bestIndex = GetBestCandidateIndex();
|
||||
if (bestIndex < 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return mCandidates[bestIndex].Density();
|
||||
}
|
||||
|
||||
int
|
||||
ResponsiveImageSelector::GetBestCandidateIndex()
|
||||
{
|
||||
if (mBestCandidateIndex != -1) {
|
||||
return mBestCandidateIndex;
|
||||
}
|
||||
|
||||
int numCandidates = mCandidates.Length();
|
||||
if (!numCandidates) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
|
||||
nsIPresShell *shell = doc ? doc->GetShell() : nullptr;
|
||||
nsPresContext *pctx = shell ? shell->GetPresContext() : nullptr;
|
||||
|
||||
if (!pctx) {
|
||||
MOZ_ASSERT(false, "Unable to find document prescontext");
|
||||
return -1;
|
||||
}
|
||||
|
||||
double displayDensity = pctx->CSSPixelsToDevPixels(1);
|
||||
|
||||
// Per spec, "In a UA-specific manner, choose one image source"
|
||||
// - For now, select the lowest density greater than displayDensity, otherwise
|
||||
// the greatest density available
|
||||
|
||||
int bestIndex = 0; // First index will always be the best so far
|
||||
double bestDensity = mCandidates[bestIndex].Density();
|
||||
for (int i = 1; i < numCandidates; i++) {
|
||||
double candidateDensity = mCandidates[i].Density();
|
||||
// - If bestIndex is below display density, pick anything larger.
|
||||
// - Otherwise, prefer if less dense than bestDensity but still above
|
||||
// displayDensity.
|
||||
if ((bestDensity < displayDensity && candidateDensity > bestDensity) ||
|
||||
(candidateDensity > displayDensity && candidateDensity < bestDensity)) {
|
||||
bestIndex = i;
|
||||
bestDensity = candidateDensity;
|
||||
}
|
||||
}
|
||||
|
||||
mBestCandidateIndex = bestIndex;
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
ResponsiveImageCandidate::ResponsiveImageCandidate()
|
||||
{
|
||||
mType = eCandidateType_Invalid;
|
||||
mValue.mDensity = 1.0;
|
||||
}
|
||||
|
||||
ResponsiveImageCandidate::ResponsiveImageCandidate(nsIURI *aURL,
|
||||
double aDensity)
|
||||
: mURL(aURL)
|
||||
{
|
||||
mType = eCandidateType_Density;
|
||||
mValue.mDensity = aDensity;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ResponsiveImageCandidate::SetURL(nsIURI *aURL)
|
||||
{
|
||||
mURL = aURL;
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageCandidate::SetParameterDefault()
|
||||
{
|
||||
MOZ_ASSERT(mType == eCandidateType_Invalid, "double setting candidate type");
|
||||
|
||||
mType = eCandidateType_Default;
|
||||
// mValue shouldn't actually be used for this type, but set it to default
|
||||
// anyway
|
||||
mValue.mDensity = 1.0;
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageCandidate::SetParameterAsDensity(double aDensity)
|
||||
{
|
||||
MOZ_ASSERT(mType == eCandidateType_Invalid, "double setting candidate type");
|
||||
|
||||
mType = eCandidateType_Density;
|
||||
mValue.mDensity = aDensity;
|
||||
}
|
||||
|
||||
bool
|
||||
ResponsiveImageCandidate::SetParamaterFromDescriptor(const nsAString & aDescriptor)
|
||||
{
|
||||
// Valid input values must be positive, using -1 for not-set
|
||||
double density = -1.0;
|
||||
|
||||
nsAString::const_iterator iter, end;
|
||||
aDescriptor.BeginReading(iter);
|
||||
aDescriptor.EndReading(end);
|
||||
|
||||
// Parse descriptor list
|
||||
// We currently only support a single density descriptor of the form:
|
||||
// <floating-point number>x
|
||||
// Silently ignore other descriptors in the list for forward-compat
|
||||
while (iter != end) {
|
||||
// Skip initial whitespace
|
||||
for (; iter != end && nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
|
||||
if (iter == end) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Find end of type
|
||||
nsAString::const_iterator start = iter;
|
||||
for (; iter != end && !nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
|
||||
|
||||
if (start == iter) {
|
||||
// Empty descriptor
|
||||
break;
|
||||
}
|
||||
|
||||
// Iter is at end of descriptor, type is single character previous to that.
|
||||
// Safe because we just verified that iter > start
|
||||
--iter;
|
||||
nsAString::const_iterator type(iter);
|
||||
++iter;
|
||||
|
||||
const nsDependentSubstring& valStr = Substring(start, type);
|
||||
if (*type == char16_t('x')) {
|
||||
nsresult rv;
|
||||
double possibleDensity = PromiseFlatString(valStr).ToDouble(&rv);
|
||||
if (density == -1.0 && NS_SUCCEEDED(rv) && possibleDensity > 0.0) {
|
||||
density = possibleDensity;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not explicitly set -> 1.0
|
||||
if (density == -1.0) {
|
||||
density = 1.0;
|
||||
}
|
||||
|
||||
SetParameterAsDensity(density);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ResponsiveImageCandidate::HasSameParameter(const ResponsiveImageCandidate & aOther) const
|
||||
{
|
||||
if (aOther.mType != mType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mType == eCandidateType_Default) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mType == eCandidateType_Density) {
|
||||
return aOther.mValue.mDensity == mValue.mDensity;
|
||||
}
|
||||
|
||||
if (mType == eCandidateType_Invalid) {
|
||||
MOZ_ASSERT(false, "Comparing invalid candidates?");
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Somebody forgot to check for all uses of this enum");
|
||||
return false;
|
||||
}
|
||||
|
||||
double
|
||||
ResponsiveImageCandidate::Density() const
|
||||
{
|
||||
// When we support 'sizes' this will get more interesting
|
||||
if (mType == eCandidateType_Invalid) {
|
||||
MOZ_ASSERT(false, "Getting density for uninitialized candidate");
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (mType == eCandidateType_Default) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (mType == eCandidateType_Density) {
|
||||
return mValue.mDensity;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Bad candidate type in Density()");
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
ResponsiveImageCandidate::URL() const
|
||||
{
|
||||
MOZ_ASSERT(mType != eCandidateType_Invalid,
|
||||
"Getting URL of incomplete candidate");
|
||||
nsCOMPtr<nsIURI> url = mURL;
|
||||
return url.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_responsiveimageselector_h__
|
||||
#define mozilla_dom_responsiveimageselector_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ResponsiveImageCandidate;
|
||||
|
||||
class ResponsiveImageSelector : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
ResponsiveImageSelector(nsIContent *aContent);
|
||||
virtual ~ResponsiveImageSelector();
|
||||
|
||||
// Given a srcset string, parse and replace current candidates (does not
|
||||
// replace default source)
|
||||
bool SetCandidatesFromSourceSet(const nsAString & aSrcSet);
|
||||
|
||||
// Set the default source, treated as the least-precedence 1.0 density source.
|
||||
nsresult SetDefaultSource(const nsAString & aSpec);
|
||||
void SetDefaultSource(nsIURI *aURL);
|
||||
|
||||
// Get the URL for the selected best candidate
|
||||
already_AddRefed<nsIURI> GetSelectedImageURL();
|
||||
double GetSelectedImageDensity();
|
||||
|
||||
private:
|
||||
// Append a candidate unless its selector is duplicated by a higher priority
|
||||
// candidate
|
||||
void AppendCandidateIfUnique(const ResponsiveImageCandidate &aCandidate);
|
||||
|
||||
// Append a default candidate with this URL. Does not check if the array
|
||||
// already contains one, use SetDefaultSource instead.
|
||||
void AppendDefaultCandidate(nsIURI *aURL);
|
||||
|
||||
// Get index of best candidate
|
||||
int GetBestCandidateIndex();
|
||||
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
// If this array contains an eCandidateType_Default, it should be the last
|
||||
// element, such that the Setters can preserve/replace it respectively.
|
||||
nsTArray<ResponsiveImageCandidate> mCandidates;
|
||||
int mBestCandidateIndex;
|
||||
};
|
||||
|
||||
class ResponsiveImageCandidate {
|
||||
public:
|
||||
ResponsiveImageCandidate();
|
||||
ResponsiveImageCandidate(nsIURI *aURL, double aDensity);
|
||||
|
||||
void SetURL(nsIURI *aURL);
|
||||
// Set this as a default-candidate. This behaves the same as density 1.0, but
|
||||
// has a differing type such that it can be replaced by subsequent
|
||||
// SetDefaultSource calls.
|
||||
void SetParameterDefault();
|
||||
|
||||
// Set this candidate as a by-density candidate with specified density.
|
||||
void SetParameterAsDensity(double aDensity);
|
||||
|
||||
// Fill from a valid candidate descriptor. Returns false descriptor is
|
||||
// invalid.
|
||||
bool SetParamaterFromDescriptor(const nsAString & aDescriptor);
|
||||
|
||||
// Check if our parameter (which does not include the url) is identical
|
||||
bool HasSameParameter(const ResponsiveImageCandidate & aOther) const;
|
||||
|
||||
already_AddRefed<nsIURI> URL() const;
|
||||
double Density() const;
|
||||
|
||||
enum eCandidateType {
|
||||
eCandidateType_Invalid,
|
||||
eCandidateType_Density,
|
||||
// Treated as 1.0 density, but a separate type so we can update the
|
||||
// responsive candidates and default separately
|
||||
eCandidateType_Default,
|
||||
};
|
||||
|
||||
eCandidateType Type() const { return mType; }
|
||||
|
||||
private:
|
||||
|
||||
nsCOMPtr<nsIURI> mURL;
|
||||
eCandidateType mType;
|
||||
union {
|
||||
double mDensity;
|
||||
} mValue;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_responsiveimageselector_h__
|
|
@ -73,6 +73,7 @@ EXPORTS.mozilla.dom += [
|
|||
'ImportManager.h',
|
||||
'Link.h',
|
||||
'NodeIterator.h',
|
||||
'ResponsiveImageSelector.h',
|
||||
'ShadowRoot.h',
|
||||
'StyleSheetList.h',
|
||||
'Text.h',
|
||||
|
@ -167,6 +168,7 @@ UNIFIED_SOURCES += [
|
|||
'nsXMLContentSerializer.cpp',
|
||||
'nsXMLHttpRequest.cpp',
|
||||
'nsXMLNameSpaceMap.cpp',
|
||||
'ResponsiveImageSelector.cpp',
|
||||
'ShadowRoot.cpp',
|
||||
'StyleSheetList.cpp',
|
||||
'Text.cpp',
|
||||
|
|
|
@ -1044,6 +1044,7 @@ GK_ATOM(spring, "spring")
|
|||
GK_ATOM(src, "src")
|
||||
GK_ATOM(srcdoc, "srcdoc")
|
||||
GK_ATOM(srclang, "srclang")
|
||||
GK_ATOM(srcset, "srcset")
|
||||
GK_ATOM(stack, "stack")
|
||||
GK_ATOM(standalone, "standalone")
|
||||
GK_ATOM(standby, "standby")
|
||||
|
|
|
@ -877,6 +877,46 @@ nsImageLoadingContent::ForceImageState(bool aForce,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoadingContent::GetNaturalWidth(uint32_t* aNaturalWidth)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNaturalWidth);
|
||||
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
if (mCurrentRequest) {
|
||||
mCurrentRequest->GetImage(getter_AddRefs(image));
|
||||
}
|
||||
|
||||
int32_t width;
|
||||
if (image && NS_SUCCEEDED(image->GetWidth(&width))) {
|
||||
*aNaturalWidth = width;
|
||||
} else {
|
||||
*aNaturalWidth = 0;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoadingContent::GetNaturalHeight(uint32_t* aNaturalHeight)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNaturalHeight);
|
||||
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
if (mCurrentRequest) {
|
||||
mCurrentRequest->GetImage(getter_AddRefs(image));
|
||||
}
|
||||
|
||||
int32_t height;
|
||||
if (image && NS_SUCCEEDED(image->GetHeight(&height))) {
|
||||
*aNaturalHeight = height;
|
||||
} else {
|
||||
*aNaturalHeight = 0;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EventStates
|
||||
nsImageLoadingContent::ImageState() const
|
||||
{
|
||||
|
|
|
@ -18,39 +18,45 @@ using namespace mozilla::gl;
|
|||
/* static */ const char*
|
||||
WebGLContext::GetExtensionString(WebGLExtensionID ext)
|
||||
{
|
||||
// must match WebGLExtensionID.
|
||||
// Once we can use variadic templates, EnumeratedArray should get a constructor
|
||||
// allowing to initialize it directly without using this auxiliary plain array.
|
||||
static const char *kExtensionNames[] = {
|
||||
"ANGLE_instanced_arrays",
|
||||
"EXT_color_buffer_half_float",
|
||||
"EXT_frag_depth",
|
||||
"EXT_sRGB",
|
||||
"EXT_texture_filter_anisotropic",
|
||||
"OES_element_index_uint",
|
||||
"OES_standard_derivatives",
|
||||
"OES_texture_float",
|
||||
"OES_texture_float_linear",
|
||||
"OES_texture_half_float",
|
||||
"OES_texture_half_float_linear",
|
||||
"OES_vertex_array_object",
|
||||
"WEBGL_color_buffer_float",
|
||||
"WEBGL_compressed_texture_atc",
|
||||
"WEBGL_compressed_texture_etc1",
|
||||
"WEBGL_compressed_texture_pvrtc",
|
||||
"WEBGL_compressed_texture_s3tc",
|
||||
"WEBGL_debug_renderer_info",
|
||||
"WEBGL_debug_shaders",
|
||||
"WEBGL_depth_texture",
|
||||
"WEBGL_draw_buffers",
|
||||
"WEBGL_lose_context"
|
||||
};
|
||||
|
||||
typedef EnumeratedArray<WebGLExtensionID, WebGLExtensionID::Max, const char*>
|
||||
names_array_t;
|
||||
static const names_array_t kExtensionNamesEnumeratedArray(kExtensionNames);
|
||||
static names_array_t sExtensionNamesEnumeratedArray;
|
||||
|
||||
return kExtensionNamesEnumeratedArray[ext];
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
#define WEBGL_EXTENSION_IDENTIFIER(x) \
|
||||
sExtensionNamesEnumeratedArray[WebGLExtensionID::x] = #x;
|
||||
|
||||
WEBGL_EXTENSION_IDENTIFIER(ANGLE_instanced_arrays)
|
||||
WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
|
||||
WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
|
||||
WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
|
||||
WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
|
||||
WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_atc)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc1)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_pvrtc)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_s3tc)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_renderer_info)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_shaders)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_depth_texture)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_draw_buffers)
|
||||
WEBGL_EXTENSION_IDENTIFIER(WEBGL_lose_context)
|
||||
|
||||
#undef WEBGL_EXTENSION_IDENTIFIER
|
||||
}
|
||||
|
||||
return sExtensionNamesEnumeratedArray[ext];
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled";
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -86,9 +89,36 @@ NS_IMPL_BOOL_ATTR(HTMLImageElement, IsMap, ismap)
|
|||
NS_IMPL_URI_ATTR(HTMLImageElement, LongDesc, longdesc)
|
||||
NS_IMPL_STRING_ATTR(HTMLImageElement, Lowsrc, lowsrc)
|
||||
NS_IMPL_URI_ATTR(HTMLImageElement, Src, src)
|
||||
NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
|
||||
NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
|
||||
NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
|
||||
|
||||
bool
|
||||
HTMLImageElement::IsSrcsetEnabled()
|
||||
{
|
||||
return Preferences::GetBool(kPrefSrcsetEnabled, false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLImageElement::GetCurrentSrc(nsAString& aValue)
|
||||
{
|
||||
if (!IsSrcsetEnabled()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
GetCurrentURI(getter_AddRefs(currentURI));
|
||||
if (currentURI) {
|
||||
nsAutoCString spec;
|
||||
currentURI->GetSpec(spec);
|
||||
CopyUTF8toUTF16(spec, aValue);
|
||||
} else {
|
||||
SetDOMStringToNull(aValue);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::GetItemValueText(nsAString& aValue)
|
||||
{
|
||||
|
@ -317,23 +347,37 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
nsDependentAtomString(aValue->GetAtomValue()));
|
||||
}
|
||||
|
||||
if (aNameSpaceID == kNameSpaceID_None &&
|
||||
aName == nsGkAtoms::src &&
|
||||
!aValue) {
|
||||
CancelImageRequests(aNotify);
|
||||
}
|
||||
|
||||
// If aNotify is false, we are coming from the parser or some such place;
|
||||
// we'll get bound after all the attributes have been set, so we'll do the
|
||||
// image load from BindToTree. Skip the LoadImage call in that case.
|
||||
if (aNotify &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aName == nsGkAtoms::crossorigin) {
|
||||
// Handle src/srcset/crossorigin updates. If aNotify is false, we are coming
|
||||
// from the parser or some such place; we'll get bound after all the
|
||||
// attributes have been set, so we'll do the image load from BindToTree.
|
||||
if (aName == nsGkAtoms::src &&
|
||||
aNameSpaceID == kNameSpaceID_None) {
|
||||
// SetAttr handles setting src in the non-responsive case, so only handle it
|
||||
// for responsive mode or unsetting
|
||||
if (!aValue) {
|
||||
CancelImageRequests(aNotify);
|
||||
} else if (mResponsiveSelector) {
|
||||
mResponsiveSelector->SetDefaultSource(aValue ? aValue->GetStringValue()
|
||||
: EmptyString());
|
||||
LoadSelectedImage(false, aNotify);
|
||||
}
|
||||
} else if (aName == nsGkAtoms::srcset &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aNotify &&
|
||||
AsContent()->IsInDoc() &&
|
||||
IsSrcsetEnabled()) {
|
||||
// We currently don't handle responsive mode until BindToTree
|
||||
UpdateSourceSet(aValue->GetStringValue());
|
||||
LoadSelectedImage(false, aNotify);
|
||||
} else if (aName == nsGkAtoms::crossorigin &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aNotify) {
|
||||
// We want aForce == true in this LoadImage call, because we want to force
|
||||
// a new load of the image with the new cross origin policy.
|
||||
nsAutoString uri;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri);
|
||||
LoadImage(uri, true, aNotify);
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
|
||||
LoadImage(currentURI, true, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
|
||||
|
@ -411,7 +455,10 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
// If aNotify is false, we are coming from the parser or some such place;
|
||||
// we'll get bound after all the attributes have been set, so we'll do the
|
||||
// image load from BindToTree. Skip the LoadImage call in that case.
|
||||
if (aNotify &&
|
||||
//
|
||||
// If we are in responsive mode, we drop the forced reload behavior, and
|
||||
// handle updates in AfterSetAttr
|
||||
if (aNotify && !mResponsiveSelector &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aName == nsGkAtoms::src) {
|
||||
|
||||
|
@ -454,11 +501,27 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
UpdateFormOwner();
|
||||
}
|
||||
|
||||
if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
bool haveSrcset = IsSrcsetEnabled() &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::srcset);
|
||||
if (haveSrcset || HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
// FIXME: Bug 660963 it would be nice if we could just have
|
||||
// ClearBrokenState update our state and do it fast...
|
||||
ClearBrokenState();
|
||||
RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
|
||||
|
||||
// We don't handle responsive changes when not bound to a tree, update them
|
||||
// now if necessary
|
||||
if (haveSrcset) {
|
||||
nsAutoString srcset;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::srcset, srcset);
|
||||
UpdateSourceSet(srcset);
|
||||
if (mResponsiveSelector) {
|
||||
nsAutoString src;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
|
||||
mResponsiveSelector->SetDefaultSource(src);
|
||||
}
|
||||
}
|
||||
|
||||
// If loading is temporarily disabled, don't even launch MaybeLoadImage.
|
||||
// Otherwise MaybeLoadImage may run later when someone has reenabled
|
||||
// loading.
|
||||
|
@ -482,6 +545,8 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
}
|
||||
}
|
||||
|
||||
mResponsiveSelector = nullptr;
|
||||
|
||||
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
@ -516,13 +581,15 @@ HTMLImageElement::UpdateFormOwner()
|
|||
void
|
||||
HTMLImageElement::MaybeLoadImage()
|
||||
{
|
||||
// Our base URI may have changed; claim that our URI changed, and the
|
||||
// nsImageLoadingContent will decide whether a new image load is warranted.
|
||||
// Our base URI may have changed, or we may have had responsive parameters
|
||||
// change while not bound to the tree. Re-parse src/srcset and call LoadImage,
|
||||
// which is a no-op if it resolves to the same effective URI without aForce.
|
||||
|
||||
// Note, check LoadingEnabled() after LoadImage call.
|
||||
nsAutoString uri;
|
||||
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) &&
|
||||
(NS_FAILED(LoadImage(uri, false, true)) ||
|
||||
!LoadingEnabled())) {
|
||||
|
||||
nsresult rv = LoadSelectedImage(false, true);
|
||||
|
||||
if (NS_FAILED(rv) || !LoadingEnabled()) {
|
||||
CancelImageRequests(true);
|
||||
}
|
||||
}
|
||||
|
@ -575,60 +642,56 @@ HTMLImageElement::Image(const GlobalObject& aGlobal,
|
|||
uint32_t
|
||||
HTMLImageElement::NaturalHeight()
|
||||
{
|
||||
if (!mCurrentRequest) {
|
||||
uint32_t height;
|
||||
nsresult rv = nsImageLoadingContent::GetNaturalHeight(&height);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "GetNaturalHeight should not fail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
mCurrentRequest->GetImage(getter_AddRefs(image));
|
||||
if (!image) {
|
||||
return 0;
|
||||
if (mResponsiveSelector) {
|
||||
double density = mResponsiveSelector->GetSelectedImageDensity();
|
||||
MOZ_ASSERT(IsFinite(density) && density > 0.0);
|
||||
height = NSToIntRound(double(height) / density);
|
||||
height = std::max(height, 0u);
|
||||
}
|
||||
|
||||
int32_t height;
|
||||
if (NS_SUCCEEDED(image->GetHeight(&height))) {
|
||||
return height;
|
||||
}
|
||||
return 0;
|
||||
return height;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLImageElement::GetNaturalHeight(uint32_t* aNaturalHeight)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNaturalHeight);
|
||||
|
||||
*aNaturalHeight = NaturalHeight();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HTMLImageElement::NaturalWidth()
|
||||
{
|
||||
if (!mCurrentRequest) {
|
||||
uint32_t width;
|
||||
nsresult rv = nsImageLoadingContent::GetNaturalWidth(&width);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "GetNaturalWidth should not fail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
mCurrentRequest->GetImage(getter_AddRefs(image));
|
||||
if (!image) {
|
||||
return 0;
|
||||
if (mResponsiveSelector) {
|
||||
double density = mResponsiveSelector->GetSelectedImageDensity();
|
||||
MOZ_ASSERT(IsFinite(density) && density > 0.0);
|
||||
width = NSToIntRound(double(width) / density);
|
||||
width = std::max(width, 0u);
|
||||
}
|
||||
|
||||
int32_t width;
|
||||
if (NS_SUCCEEDED(image->GetWidth(&width))) {
|
||||
return width;
|
||||
}
|
||||
return 0;
|
||||
return width;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLImageElement::GetNaturalWidth(uint32_t* aNaturalWidth)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNaturalWidth);
|
||||
|
||||
*aNaturalWidth = NaturalWidth();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -703,5 +766,66 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
|
|||
mForm = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
if (mResponsiveSelector) {
|
||||
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
|
||||
if (url) {
|
||||
rv = LoadImage(url, aForce, aNotify);
|
||||
} else {
|
||||
CancelImageRequests(aNotify);
|
||||
rv = NS_OK;
|
||||
}
|
||||
} else {
|
||||
nsAutoString src;
|
||||
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
||||
CancelImageRequests(aNotify);
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
rv = LoadImage(src, aForce, aNotify);
|
||||
if (NS_FAILED(rv)) {
|
||||
CancelImageRequests(aNotify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::DestroyContent()
|
||||
{
|
||||
mResponsiveSelector = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::UpdateSourceSet(const nsAString & aSrcset)
|
||||
{
|
||||
MOZ_ASSERT(IsSrcsetEnabled());
|
||||
|
||||
bool haveSrcset = !aSrcset.IsEmpty();
|
||||
|
||||
if (haveSrcset && !mResponsiveSelector) {
|
||||
mResponsiveSelector = new ResponsiveImageSelector(this);
|
||||
mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);
|
||||
|
||||
// src may have been set before we decided we were responsive
|
||||
nsAutoString src;
|
||||
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && src.Length()) {
|
||||
mResponsiveSelector->SetDefaultSource(src);
|
||||
}
|
||||
|
||||
} else if (haveSrcset) {
|
||||
mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);
|
||||
} else if (mResponsiveSelector) {
|
||||
// Clearing srcset, don't need responsive selector anymore
|
||||
mResponsiveSelector = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsIDOMHTMLImageElement.h"
|
||||
#include "imgRequestProxy.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/dom/ResponsiveImageSelector.h"
|
||||
|
||||
namespace mozilla {
|
||||
class EventChainPreVisitor;
|
||||
|
@ -79,6 +80,8 @@ public:
|
|||
|
||||
void MaybeLoadImage();
|
||||
|
||||
static bool IsSrcsetEnabled();
|
||||
|
||||
bool IsMap()
|
||||
{
|
||||
return GetBoolAttr(nsGkAtoms::ismap);
|
||||
|
@ -132,6 +135,10 @@ public:
|
|||
{
|
||||
SetHTMLAttr(nsGkAtoms::src, aSrc, aError);
|
||||
}
|
||||
void SetSrcset(const nsAString& aSrcset, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::srcset, aSrcset, aError);
|
||||
}
|
||||
void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
|
||||
|
@ -171,7 +178,16 @@ public:
|
|||
void SetForm(nsIDOMHTMLFormElement* aForm);
|
||||
void ClearForm(bool aRemoveFromForm);
|
||||
|
||||
virtual void DestroyContent() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
// Resolve and load the current mResponsiveSelector (responsive mode) or src
|
||||
// attr image.
|
||||
nsresult LoadSelectedImage(bool aForce, bool aNotify);
|
||||
|
||||
// Update/create/destroy mResponsiveSelector
|
||||
void UpdateSourceSet(const nsAString & aSrcset);
|
||||
|
||||
CSSIntPoint GetXY();
|
||||
virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
|
||||
virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
|
||||
|
@ -189,6 +205,9 @@ protected:
|
|||
// cooperate in maintaining.
|
||||
HTMLFormElement* mForm;
|
||||
|
||||
// Created when we're tracking responsive image state
|
||||
nsRefPtr<ResponsiveImageSelector> mResponsiveSelector;
|
||||
|
||||
private:
|
||||
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
nsRuleData* aData);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<body>redirect</body>
|
|
@ -0,0 +1,2 @@
|
|||
HTTP 301 Moved Permanently
|
||||
Location: file_iframe_sandbox_redirect_target.html
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<script>
|
||||
onmessage = function(event) {
|
||||
parent.postMessage(event.data + " redirect target", "*");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>I have been redirected</body>
|
|
@ -135,6 +135,9 @@ support-files =
|
|||
file_iframe_sandbox_open_window_fail.html
|
||||
file_iframe_sandbox_open_window_pass.html
|
||||
file_iframe_sandbox_pass.js
|
||||
file_iframe_sandbox_redirect.html
|
||||
file_iframe_sandbox_redirect.html^headers^
|
||||
file_iframe_sandbox_redirect_target.html
|
||||
file_iframe_sandbox_top_navigation_fail.html
|
||||
file_iframe_sandbox_top_navigation_pass.html
|
||||
file_iframe_sandbox_window_form_fail.html
|
||||
|
@ -445,6 +448,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(plugins not su
|
|||
skip-if = buildapp == 'b2g' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
|
||||
[test_iframe_sandbox_popups_inheritance.html]
|
||||
skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
|
||||
[test_iframe_sandbox_redirect.html]
|
||||
[test_iframe_sandbox_same_origin.html]
|
||||
[test_iframe_sandbox_workers.html]
|
||||
[test_img_attributes_reflection.html]
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=985135
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 985135</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 985135 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
try {
|
||||
var doc = frames[0].document;
|
||||
ok(false, "Should not be able to get the document");
|
||||
isnot(doc.body.textContent.slice(0, -1), "I have been redirected",
|
||||
"Should not happen");
|
||||
SimpleTest.finish();
|
||||
} catch (e) {
|
||||
// Check that we got the right document
|
||||
window.onmessage = function(event) {
|
||||
is(event.data, "who are you? redirect target",
|
||||
"Should get the message we expect");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
frames[0].postMessage("who are you?", "*");
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=985135">Mozilla Bug 985135</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<iframe src="file_iframe_sandbox_redirect.html" sandbox="allow-scripts"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -2717,15 +2717,12 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
|
|||
JS::DontFireOnNewGlobalHook, aOptions));
|
||||
if (!aGlobal) {
|
||||
NS_WARNING("Failed to create global");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSAutoCompartment ac(aCx, aGlobal);
|
||||
|
||||
{
|
||||
JS::AutoAssertNoGC nogc;
|
||||
|
||||
// The setup of our global needs to be done before a GC happens.
|
||||
js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aNative));
|
||||
NS_ADDREF(aNative);
|
||||
|
||||
|
|
|
@ -81,7 +81,10 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
|||
nsIGlobalObject* globalObject = nullptr;
|
||||
|
||||
{
|
||||
JS::AutoAssertNoGC nogc;
|
||||
// Bug 955660: we cannot do "proper" rooting here because we need the
|
||||
// global to get a context. Everything here is simple getters that cannot
|
||||
// GC, so just paper over the necessary dataflow inversion.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
if (mIsMainThread) {
|
||||
// Now get the global and JSContext for this callback.
|
||||
nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
|
||||
|
|
|
@ -16,6 +16,7 @@ support-files = file_HighPriority.html
|
|||
[test_BackgroundLRU.html]
|
||||
[test_Audio.html]
|
||||
support-files = file_Audio.html silence.ogg
|
||||
[test_Keyboard.html]
|
||||
[test_MultipleFrames.html]
|
||||
support-files = file_MultipleFrames.html
|
||||
# This test is disabled temporarily in bug 968604. It will be enabled after bug 987164 is fixed.
|
||||
|
@ -28,4 +29,3 @@ support-files = file_NestedFramesOuter.html
|
|||
# This test disabled due to bug 865844. In fact, it was never enabled!
|
||||
skip-if = true
|
||||
support-files = file_WebGLContextLost.html
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that frames with mozapptype=inputmethod gets the keyboard-specific
|
||||
priority level when in the foreground.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.setAttribute('mozapptype', 'inputmethod');
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
}).then(function() {
|
||||
return expectPriorityChange(childID, 'FOREGROUND_KEYBOARD');
|
||||
}).then(function() {
|
||||
return expectMozbrowserEvent(iframe, 'loadend');
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND');
|
||||
|
||||
/* We wait until mozbrowserloadend before calling setVisible, because
|
||||
* setVisible isn't available until mozbrowser has loaded. In practice,
|
||||
* that means we can call setVisible once we've gotten /any/ mozbrowser
|
||||
* event. */
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'FOREGROUND_KEYBOARD');
|
||||
iframe.setVisible(true);
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -31,6 +31,7 @@ XPIDL_SOURCES += [
|
|||
'nsIFrameRequestCallback.idl',
|
||||
'nsIIdleObserver.idl',
|
||||
'nsIQueryContentEventResult.idl',
|
||||
'nsIServiceWorkerManager.idl',
|
||||
'nsIStructuredCloneContainer.idl',
|
||||
'nsITabChild.idl',
|
||||
'nsITabParent.idl',
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[uuid(d9539ecb-6665-452c-bae7-4e42f25d23aa)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
// Returns a Promise
|
||||
nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
|
||||
|
||||
// Returns a Promise
|
||||
nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
|
||||
|
||||
};
|
||||
|
||||
%{ C++
|
||||
#define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
|
||||
%}
|
|
@ -16,11 +16,12 @@
|
|||
* http://www.whatwg.org/specs/web-apps/current-work/
|
||||
*/
|
||||
|
||||
[uuid(d3e488b9-3b29-410a-bcf4-18fb874c170a)]
|
||||
[uuid(939f4ea1-cb8d-49d0-a4e1-23bce758f4af)]
|
||||
interface nsIDOMHTMLImageElement : nsISupports
|
||||
{
|
||||
attribute DOMString alt;
|
||||
attribute DOMString src;
|
||||
attribute DOMString srcset;
|
||||
attribute DOMString crossOrigin;
|
||||
attribute DOMString useMap;
|
||||
attribute boolean isMap;
|
||||
|
@ -34,11 +35,13 @@ interface nsIDOMHTMLImageElement : nsISupports
|
|||
attribute DOMString name;
|
||||
attribute DOMString align;
|
||||
attribute DOMString border;
|
||||
attribute long hspace;
|
||||
attribute long hspace;
|
||||
attribute DOMString longDesc;
|
||||
attribute long vspace;
|
||||
attribute DOMString lowsrc;
|
||||
|
||||
readonly attribute DOMString currentSrc;
|
||||
|
||||
// These attributes are offsets from the closest view (to mimic
|
||||
// NS4's "offset-from-layer" behavior).
|
||||
readonly attribute long x;
|
||||
|
|
|
@ -673,7 +673,7 @@ ContentParent::JoinAllSubprocesses()
|
|||
}
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::GetNewOrUsed(bool aForBrowserElement)
|
||||
ContentParent::GetNewOrUsed(bool aForBrowserElement, ProcessPriority aPriority)
|
||||
{
|
||||
if (!sNonAppContentParents)
|
||||
sNonAppContentParents = new nsTArray<ContentParent*>();
|
||||
|
@ -704,7 +704,7 @@ ContentParent::GetNewOrUsed(bool aForBrowserElement)
|
|||
p = new ContentParent(/* app = */ nullptr,
|
||||
aForBrowserElement,
|
||||
/* isForPreallocated = */ false,
|
||||
PROCESS_PRIORITY_FOREGROUND);
|
||||
aPriority);
|
||||
p->Init();
|
||||
}
|
||||
|
||||
|
@ -723,7 +723,7 @@ ContentParent::GetInitialProcessPriority(Element* aFrameElement)
|
|||
}
|
||||
|
||||
if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
|
||||
NS_LITERAL_STRING("keyboard"), eCaseMatters)) {
|
||||
NS_LITERAL_STRING("inputmethod"), eCaseMatters)) {
|
||||
return PROCESS_PRIORITY_FOREGROUND_KEYBOARD;
|
||||
} else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
|
||||
NS_LITERAL_STRING("critical"), eCaseMatters)) {
|
||||
|
@ -767,8 +767,13 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
|
||||
|
||||
if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) {
|
||||
if (nsRefPtr<ContentParent> cp = GetNewOrUsed(aContext.IsBrowserElement())) {
|
||||
nsRefPtr<ContentParent> cp = GetNewOrUsed(aContext.IsBrowserElement(),
|
||||
initialPriority);
|
||||
|
||||
if (cp) {
|
||||
uint32_t chromeFlags = 0;
|
||||
|
||||
// Propagate the private-browsing status of the element's parent
|
||||
|
@ -819,7 +824,6 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
|
||||
nsRefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);
|
||||
|
||||
if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) {
|
||||
|
|
|
@ -94,7 +94,9 @@ public:
|
|||
static void RunAfterPreallocatedProcessReady(nsIRunnable* aRequest);
|
||||
|
||||
static already_AddRefed<ContentParent>
|
||||
GetNewOrUsed(bool aForBrowserElement = false);
|
||||
GetNewOrUsed(bool aForBrowserElement = false,
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
|
||||
|
||||
/**
|
||||
* Create a subprocess suitable for use as a preallocated app process.
|
||||
|
|
|
@ -982,7 +982,7 @@ ParticularProcessPriorityManager::ComputePriority()
|
|||
}
|
||||
|
||||
if (isVisible) {
|
||||
return HasAppType("keyboard") ?
|
||||
return HasAppType("inputmethod") ?
|
||||
PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
|
||||
PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ interface HTMLImageElement : HTMLElement {
|
|||
attribute DOMString alt;
|
||||
[SetterThrows]
|
||||
attribute DOMString src;
|
||||
// attribute DOMString srcset;
|
||||
[SetterThrows, Pref="dom.image.srcset.enabled"]
|
||||
attribute DOMString srcset;
|
||||
[SetterThrows]
|
||||
attribute DOMString crossOrigin;
|
||||
[SetterThrows]
|
||||
|
@ -55,6 +56,13 @@ partial interface HTMLImageElement {
|
|||
[TreatNullAs=EmptyString,SetterThrows] attribute DOMString border;
|
||||
};
|
||||
|
||||
// [Update me: not in whatwg spec yet]
|
||||
// http://picture.responsiveimages.org/#the-img-element
|
||||
[Pref="dom.image.srcset.enabled"]
|
||||
partial interface HTMLImageElement {
|
||||
readonly attribute DOMString? currentSrc;
|
||||
};
|
||||
|
||||
// Mozilla extensions.
|
||||
partial interface HTMLImageElement {
|
||||
attribute DOMString lowsrc;
|
||||
|
|
|
@ -44,3 +44,13 @@ ServiceWorker::WrapObject(JSContext* aCx)
|
|||
|
||||
return ServiceWorkerBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
ServiceWorker::GetWorkerPrivate() const
|
||||
{
|
||||
// At some point in the future, this may be optimized to terminate a worker
|
||||
// that hasn't been used in a certain amount of time or when there is memory
|
||||
// pressure or similar.
|
||||
MOZ_ASSERT(mSharedWorker);
|
||||
return mSharedWorker->GetWorkerPrivate();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,9 @@ public:
|
|||
aURL = mURL;
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetWorkerPrivate() const;
|
||||
|
||||
private:
|
||||
// This class can only be created from the RuntimeService.
|
||||
ServiceWorker(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker);
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
#include "ServiceWorkerContainer.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerContainerBinding.h"
|
||||
|
@ -37,18 +40,46 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
|
|||
const RegistrationOptionList& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// FIXME(nsm): Bug 984048
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = swm->Register(mWindow, aOptions.mScope, aScriptURL, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::Unregister(const nsAString& aScope,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// FIXME(nsm): Bug 984048
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = swm->Unregister(mWindow, aScope, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<workers::ServiceWorker>
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow)
|
||||
: mWindow(aWindow)
|
||||
{
|
||||
// FIXME(nsm): Bug 983497. Here the NSW should hook into SWM to be notified of events.
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
|
@ -82,7 +83,9 @@ public:
|
|||
ErrorResult& aRv);
|
||||
private:
|
||||
~ServiceWorkerContainer()
|
||||
{ }
|
||||
{
|
||||
// FIXME(nsm): Bug 983497. Unhook from events.
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
/* 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 "ServiceWorkerManager.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "WorkerInlines.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
//////////////////////////
|
||||
// ServiceWorkerManager //
|
||||
//////////////////////////
|
||||
|
||||
NS_IMPL_ADDREF(ServiceWorkerManager)
|
||||
NS_IMPL_RELEASE(ServiceWorkerManager)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
|
||||
if (aIID.Equals(NS_GET_IID(ServiceWorkerManager)))
|
||||
foundInterface = static_cast<nsIServiceWorkerManager*>(this);
|
||||
else
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
ServiceWorkerManager::ServiceWorkerManager()
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerManager::~ServiceWorkerManager()
|
||||
{
|
||||
// The map will assert if it is not empty when destroyed.
|
||||
mDomainMap.EnumerateRead(CleanupServiceWorkerInformation, nullptr);
|
||||
mDomainMap.Clear();
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator
|
||||
ServiceWorkerManager::CleanupServiceWorkerInformation(const nsACString& aDomain,
|
||||
ServiceWorkerDomainInfo* aDomainInfo,
|
||||
void *aUnused)
|
||||
{
|
||||
aDomainInfo->mServiceWorkerRegistrations.Clear();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the async aspects of the register algorithm.
|
||||
*/
|
||||
class RegisterRunnable : public nsRunnable
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
const nsCString mScope;
|
||||
nsCOMPtr<nsIURI> mScriptURI;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
public:
|
||||
RegisterRunnable(nsPIDOMWindow* aWindow, const nsCString aScope,
|
||||
nsIURI* aScriptURI, Promise* aPromise)
|
||||
: mWindow(aWindow), mScope(aScope), mScriptURI(aScriptURI),
|
||||
mPromise(aPromise)
|
||||
{ }
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
nsCString domain;
|
||||
nsresult rv = mScriptURI->GetHost(domain);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mPromise->MaybeReject(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
|
||||
swm->mDomainMap.Get(domain);
|
||||
// FIXME(nsm): Refactor this pattern.
|
||||
if (!swm->mDomainMap.Get(domain, &domainInfo)) {
|
||||
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
|
||||
swm->mDomainMap.Put(domain, domainInfo);
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerRegistration> registration = domainInfo->GetRegistration(mScope);
|
||||
|
||||
nsCString spec;
|
||||
rv = mScriptURI->GetSpec(spec);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mPromise->MaybeReject(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (registration) {
|
||||
registration->mPendingUninstall = false;
|
||||
if (spec.Equals(registration->mScriptSpec)) {
|
||||
// FIXME(nsm): Force update on Shift+Reload. Algorithm not specified for
|
||||
// that yet.
|
||||
|
||||
// There is an existing update in progress. Resolve with whatever it
|
||||
// results in.
|
||||
if (registration->HasUpdatePromise()) {
|
||||
registration->AddUpdatePromiseObserver(mPromise);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// There is no update in progress and since SW updating is upto the UA, we
|
||||
// will not update right now. Simply resolve with whatever worker we
|
||||
// have.
|
||||
ServiceWorkerInfo info = registration->Newest();
|
||||
if (info.IsValid()) {
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv =
|
||||
swm->CreateServiceWorkerForWindow(mWindow,
|
||||
info.GetScriptSpec(),
|
||||
registration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(serviceWorker);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
registration = domainInfo->CreateNewRegistration(mScope);
|
||||
}
|
||||
|
||||
registration->mScriptSpec = spec;
|
||||
|
||||
// FIXME(nsm): Call Update. Same bug, different patch.
|
||||
// For now if the registration reaches this spot, the promise remains
|
||||
// unresolved.
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// If we return an error code here, the ServiceWorkerContainer will
|
||||
// automatically reject the Promise.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
const nsAString& aScriptURL, nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
// XXXnsm Don't allow chrome callers for now, we don't support chrome
|
||||
// ServiceWorkers.
|
||||
MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
||||
if (!window) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
|
||||
nsRefPtr<Promise> promise = new Promise(sgo);
|
||||
|
||||
nsCOMPtr<nsIURI> documentURI = window->GetDocumentURI();
|
||||
if (!documentURI) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Although the spec says that the same-origin checks should also be done
|
||||
// asynchronously, we do them in sync because the Promise created by the
|
||||
// WebIDL infrastructure due to a returned error will be resolved
|
||||
// asynchronously. We aren't making any internal state changes in these
|
||||
// checks, so ordering of multiple calls is not affected.
|
||||
|
||||
nsresult rv;
|
||||
// FIXME(nsm): Bug 1003991. Disable check when devtools are open.
|
||||
if (!Preferences::GetBool("dom.serviceWorkers.testing.enabled")) {
|
||||
bool isHttps;
|
||||
rv = documentURI->SchemeIs("https", &isHttps);
|
||||
if (NS_FAILED(rv) || !isHttps) {
|
||||
NS_WARNING("ServiceWorker registration from insecure websites is not allowed.");
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> documentPrincipal;
|
||||
if (window->GetExtantDoc()) {
|
||||
documentPrincipal = window->GetExtantDoc()->NodePrincipal();
|
||||
} else {
|
||||
documentPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI;
|
||||
rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, documentURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/262
|
||||
// allowIfInheritsPrincipal: allow data: URLs for now.
|
||||
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
|
||||
true /* allowIfInheritsPrincipal */);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, documentURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
|
||||
false /* allowIfInheritsPrinciple */);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCString cleanedScope;
|
||||
rv = scopeURI->GetSpec(cleanedScope);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> registerRunnable =
|
||||
new RegisterRunnable(window, cleanedScope, scriptURI, promise);
|
||||
promise.forget(aPromise);
|
||||
return NS_DispatchToCurrentThread(registerRunnable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
|
||||
nsPIDOMWindow* aWindow)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we return an error, ServiceWorkerContainer will reject the Promise.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
// XXXnsm Don't allow chrome callers for now.
|
||||
MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
|
||||
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<ServiceWorkerManager>
|
||||
ServiceWorkerManager::GetInstance()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
||||
return concrete.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsACString& aScope,
|
||||
ServiceWorker** aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
RuntimeService* rs = RuntimeService::GetOrCreateService();
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(aWindow);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> jsGlobal(cx, sgo->GetGlobalJSObject());
|
||||
JSAutoCompartment ac(cx, jsGlobal);
|
||||
|
||||
GlobalObject global(cx, jsGlobal);
|
||||
nsresult rv = rs->CreateServiceWorker(global,
|
||||
NS_ConvertUTF8toUTF16(aScriptSpec),
|
||||
aScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
serviceWorker.forget(aServiceWorker);
|
||||
return rv;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
|
@ -0,0 +1,211 @@
|
|||
/* 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 mozilla_dom_workers_serviceworkermanager_h
|
||||
#define mozilla_dom_workers_serviceworkermanager_h
|
||||
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include "nsTWeakRef.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo
|
||||
{
|
||||
const nsCString mScriptSpec;
|
||||
public:
|
||||
|
||||
bool
|
||||
IsValid() const
|
||||
{
|
||||
return !mScriptSpec.IsVoid();
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetScriptSpec() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo()
|
||||
{ }
|
||||
|
||||
explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
|
||||
: mScriptSpec(aScriptSpec)
|
||||
{ }
|
||||
};
|
||||
|
||||
class ServiceWorkerRegistration
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration)
|
||||
|
||||
nsCString mScope;
|
||||
// The scriptURL for the registration. This may be completely different from
|
||||
// the URLs of the following three workers.
|
||||
nsCString mScriptSpec;
|
||||
|
||||
ServiceWorkerInfo mCurrentWorker;
|
||||
ServiceWorkerInfo mWaitingWorker;
|
||||
ServiceWorkerInfo mInstallingWorker;
|
||||
|
||||
bool mHasUpdatePromise;
|
||||
|
||||
void
|
||||
AddUpdatePromiseObserver(Promise* aPromise)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
}
|
||||
|
||||
bool
|
||||
HasUpdatePromise()
|
||||
{
|
||||
return mHasUpdatePromise;
|
||||
}
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
// removed since documents may be controlled. It is marked as
|
||||
// pendingUninstall and when all controlling documents go away, removed.
|
||||
bool mPendingUninstall;
|
||||
|
||||
explicit ServiceWorkerRegistration(const nsACString& aScope)
|
||||
: mScope(aScope),
|
||||
mHasUpdatePromise(false),
|
||||
mPendingUninstall(false)
|
||||
{ }
|
||||
|
||||
ServiceWorkerInfo
|
||||
Newest() const
|
||||
{
|
||||
if (mInstallingWorker.IsValid()) {
|
||||
return mInstallingWorker;
|
||||
} else if (mWaitingWorker.IsValid()) {
|
||||
return mWaitingWorker;
|
||||
} else {
|
||||
return mCurrentWorker;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
|
||||
{ /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
|
||||
0xf4f8755a, \
|
||||
0x69ca, \
|
||||
0x46e8, \
|
||||
{ 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 } \
|
||||
}
|
||||
|
||||
/*
|
||||
* The ServiceWorkerManager is a per-process global that deals with the
|
||||
* installation, querying and event dispatch of ServiceWorkers for all the
|
||||
* origins in the process.
|
||||
*/
|
||||
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
||||
{
|
||||
friend class RegisterRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERMANAGER
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
|
||||
|
||||
static ServiceWorkerManager* FactoryCreate()
|
||||
{
|
||||
ServiceWorkerManager* res = new ServiceWorkerManager;
|
||||
NS_ADDREF(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This struct is used for passive ServiceWorker management.
|
||||
* Actively running ServiceWorkers use the SharedWorker infrastructure in
|
||||
* RuntimeService for execution and lifetime management.
|
||||
*/
|
||||
struct ServiceWorkerDomainInfo
|
||||
{
|
||||
// Scope to registration.
|
||||
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistration> mServiceWorkerRegistrations;
|
||||
|
||||
ServiceWorkerDomainInfo()
|
||||
{ }
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistration>
|
||||
GetRegistration(const nsCString& aScope) const
|
||||
{
|
||||
nsRefPtr<ServiceWorkerRegistration> reg;
|
||||
mServiceWorkerRegistrations.Get(aScope, getter_AddRefs(reg));
|
||||
return reg.forget();
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration*
|
||||
CreateNewRegistration(const nsCString& aScope)
|
||||
{
|
||||
ServiceWorkerRegistration* registration =
|
||||
new ServiceWorkerRegistration(aScope);
|
||||
// From now on ownership of registration is with
|
||||
// mServiceWorkerRegistrations.
|
||||
mServiceWorkerRegistrations.Put(aScope, registration);
|
||||
return registration;
|
||||
}
|
||||
};
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
||||
|
||||
// FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*"
|
||||
// }) and then another page calls register("/bar_worker.js", { scope: "/*" })
|
||||
// while the install is in progress. The async install steps for register
|
||||
// bar_worker.js could finish before foo_worker.js, but bar_worker still has
|
||||
// to be the winning controller.
|
||||
// FIXME(nsm): Move this into per domain?
|
||||
|
||||
static already_AddRefed<ServiceWorkerManager>
|
||||
GetInstance();
|
||||
|
||||
private:
|
||||
ServiceWorkerManager();
|
||||
~ServiceWorkerManager();
|
||||
|
||||
NS_IMETHOD
|
||||
Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
|
||||
|
||||
NS_IMETHODIMP
|
||||
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsACString& aScope,
|
||||
ServiceWorker** aServiceWorker);
|
||||
|
||||
static PLDHashOperator
|
||||
CleanupServiceWorkerInformation(const nsACString& aDomain,
|
||||
ServiceWorkerDomainInfo* aDomainInfo,
|
||||
void *aUnused);
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
|
||||
NS_SERVICEWORKERMANAGER_IMPL_IID);
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkermanager_h
|
|
@ -15,6 +15,7 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
EXPORTS.mozilla.dom.workers += [
|
||||
'ServiceWorkerManager.h',
|
||||
'Workers.h',
|
||||
]
|
||||
|
||||
|
@ -50,6 +51,7 @@ SOURCES += [
|
|||
'ServiceWorker.cpp',
|
||||
'ServiceWorkerContainer.cpp',
|
||||
'ServiceWorkerEvents.cpp',
|
||||
'ServiceWorkerManager.cpp',
|
||||
'SharedWorker.cpp',
|
||||
'URL.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
[test_installation_simple.html]
|
||||
[test_navigator.html]
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 930348 - test stub Navigator ServiceWorker utilities.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function simpleRegister() {
|
||||
var p = navigator.serviceWorker.register("/fake_worker.js");
|
||||
ok(p instanceof Promise, "register() should return a Promise");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function sameOriginWorker() {
|
||||
p = navigator.serviceWorker.register("http://some-other-origin/worker.js");
|
||||
return p.then(function(w) {
|
||||
ok(false, "Worker from different origin should fail");
|
||||
}, function(e) {
|
||||
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
||||
});
|
||||
}
|
||||
|
||||
function sameOriginScope() {
|
||||
p = navigator.serviceWorker.register("worker.js", { scope: "http://www.example.com/*" });
|
||||
return p.then(function(w) {
|
||||
ok(false, "Worker controlling scope for different origin should fail");
|
||||
}, function(e) {
|
||||
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
||||
});
|
||||
}
|
||||
|
||||
function httpsOnly() {
|
||||
var promise = new Promise(function(resolve) {
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.serviceWorkers.testing.enabled", false]] }, resolve);
|
||||
});
|
||||
|
||||
return promise.then(function() {
|
||||
return navigator.serviceWorker.register("/worker.js");
|
||||
}).then(function(w) {
|
||||
ok(false, "non-HTTPS pages cannot register ServiceWorkers");
|
||||
}, function(e) {
|
||||
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
simpleRegister()
|
||||
.then(sameOriginWorker)
|
||||
.then(sameOriginScope)
|
||||
.then(httpsOnly)
|
||||
// put more tests here.
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
}).catch(function(e) {
|
||||
ok(false, "Some test failed with error " + e);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -375,8 +375,15 @@ FindInflectionPoints(const BezierControlPoints &aControlPoints,
|
|||
// Instead of a linear acceleration change we have a constant
|
||||
// acceleration change. This means the equation has no solution
|
||||
// and there are no inflection points, unless the constant is 0.
|
||||
// In that case the curve is a straight line, but we'll let
|
||||
// FlattenBezierCurveSegment deal with this.
|
||||
// In that case the curve is a straight line, essentially that means
|
||||
// the easiest way to deal with is is by saying there's an inflection
|
||||
// point at t == 0. The inflection point approximation range found will
|
||||
// automatically extend into infinity.
|
||||
if (c == 0) {
|
||||
*aCount = 1;
|
||||
*aT1 = 0;
|
||||
return;
|
||||
}
|
||||
*aCount = 0;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
|
|||
}
|
||||
}
|
||||
|
||||
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
|
||||
if (!ok) {
|
||||
if (!mHarfBuzzShaper) {
|
||||
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
|
||||
}
|
||||
|
|
|
@ -3970,10 +3970,8 @@ gfxFont::ShapeText(gfxContext *aContext,
|
|||
}
|
||||
|
||||
if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
|
||||
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
|
||||
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
aScript, aShapedText);
|
||||
}
|
||||
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
aScript, aShapedText);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
|
|
|
@ -143,8 +143,6 @@ NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference)
|
|||
|
||||
#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
|
||||
|
||||
#define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts"
|
||||
#define HARFBUZZ_SCRIPTS_DEFAULT mozilla::unicode::SHAPING_DEFAULT
|
||||
#define GFX_PREF_FALLBACK_USE_CMAPS "gfx.font_rendering.fallback.always_use_cmaps"
|
||||
|
||||
#define GFX_PREF_OPENTYPE_SVG "gfx.font_rendering.opentype_svg.enabled"
|
||||
|
@ -262,7 +260,6 @@ gfxPlatform::gfxPlatform()
|
|||
: mAzureCanvasBackendCollector(MOZ_THIS_IN_INITIALIZER_LIST(),
|
||||
&gfxPlatform::GetAzureBackendInfo)
|
||||
{
|
||||
mUseHarfBuzzScripts = UNINITIALIZED_VALUE;
|
||||
mAllowDownloadableFonts = UNINITIALIZED_VALUE;
|
||||
mFallbackUsesCmaps = UNINITIALIZED_VALUE;
|
||||
|
||||
|
@ -1123,18 +1120,6 @@ gfxPlatform::UseGraphiteShaping()
|
|||
return mGraphiteShapingEnabled;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatform::UseHarfBuzzForScript(int32_t aScriptCode)
|
||||
{
|
||||
if (mUseHarfBuzzScripts == UNINITIALIZED_VALUE) {
|
||||
mUseHarfBuzzScripts = Preferences::GetInt(GFX_PREF_HARFBUZZ_SCRIPTS, HARFBUZZ_SCRIPTS_DEFAULT);
|
||||
}
|
||||
|
||||
int32_t shapingType = mozilla::unicode::ScriptShapingType(aScriptCode);
|
||||
|
||||
return (mUseHarfBuzzScripts & shapingType) != 0;
|
||||
}
|
||||
|
||||
gfxFontEntry*
|
||||
gfxPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
|
||||
const uint8_t *aFontData,
|
||||
|
@ -1868,9 +1853,6 @@ gfxPlatform::FontsPrefsChanged(const char *aPref)
|
|||
} else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) {
|
||||
mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
|
||||
FlushFontAndWordCaches();
|
||||
} else if (!strcmp(GFX_PREF_HARFBUZZ_SCRIPTS, aPref)) {
|
||||
mUseHarfBuzzScripts = UNINITIALIZED_VALUE;
|
||||
FlushFontAndWordCaches();
|
||||
} else if (!strcmp(BIDI_NUMERAL_PREF, aPref)) {
|
||||
mBidiNumeralOption = UNINITIALIZED_VALUE;
|
||||
} else if (!strcmp(GFX_PREF_OPENTYPE_SVG, aPref)) {
|
||||
|
|
|
@ -407,13 +407,6 @@ public:
|
|||
*/
|
||||
bool UseGraphiteShaping();
|
||||
|
||||
/**
|
||||
* Whether to use the harfbuzz shaper (depending on script complexity).
|
||||
*
|
||||
* This allows harfbuzz to be enabled selectively via the preferences.
|
||||
*/
|
||||
bool UseHarfBuzzForScript(int32_t aScriptCode);
|
||||
|
||||
// check whether format is supported on a platform or not (if unclear, returns true)
|
||||
virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) { return false; }
|
||||
|
||||
|
@ -641,9 +634,6 @@ protected:
|
|||
// when doing system font fallback
|
||||
int8_t mFallbackUsesCmaps;
|
||||
|
||||
// which scripts should be shaped with harfbuzz
|
||||
int32_t mUseHarfBuzzScripts;
|
||||
|
||||
// max character limit for words in word cache
|
||||
int32_t mWordCacheCharLimit;
|
||||
|
||||
|
|
|
@ -238,6 +238,10 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
|
|||
RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(IntSize(mClip.width, mClip.height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!target) {
|
||||
NS_ERROR("Could not create a DrawTarget");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(target);
|
||||
|
||||
|
|
|
@ -113,6 +113,11 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
|
|||
mozilla::RefPtr<DrawTarget> target =
|
||||
gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
|
||||
if (!target) {
|
||||
NS_ERROR("Could not create a DrawTarget");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Create our drawable.
|
||||
RefPtr<SourceSurface> innerSurface =
|
||||
|
|
|
@ -720,6 +720,11 @@ VectorImage::GetFrame(uint32_t aWhichFrame,
|
|||
CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
|
||||
imageIntSize.height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!dt) {
|
||||
NS_ERROR("Could not create a DrawTarget");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(dt);
|
||||
|
||||
nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
|
||||
|
|
|
@ -249,64 +249,6 @@ IsClusterExtender(uint32_t aCh, uint8_t aCategory)
|
|||
(aCh >= 0xff9e && aCh <= 0xff9f)); // katakana sound marks
|
||||
}
|
||||
|
||||
// TODO: replace this with a properties file or similar;
|
||||
// expect this to evolve as harfbuzz shaping support matures.
|
||||
//
|
||||
// The "shaping type" of each script run, as returned by this
|
||||
// function, is compared to the bits set in the
|
||||
// gfx.font_rendering.harfbuzz.scripts
|
||||
// preference to decide whether to use the harfbuzz shaper.
|
||||
//
|
||||
int32_t
|
||||
ScriptShapingType(int32_t aScriptCode)
|
||||
{
|
||||
switch (aScriptCode) {
|
||||
default:
|
||||
return SHAPING_DEFAULT; // scripts not explicitly listed here are
|
||||
// assumed to just use default shaping
|
||||
|
||||
case MOZ_SCRIPT_ARABIC:
|
||||
case MOZ_SCRIPT_SYRIAC:
|
||||
case MOZ_SCRIPT_NKO:
|
||||
case MOZ_SCRIPT_MANDAIC:
|
||||
return SHAPING_ARABIC; // bidi scripts with Arabic-style shaping
|
||||
|
||||
case MOZ_SCRIPT_HEBREW:
|
||||
return SHAPING_HEBREW;
|
||||
|
||||
case MOZ_SCRIPT_HANGUL:
|
||||
return SHAPING_HANGUL;
|
||||
|
||||
case MOZ_SCRIPT_MONGOLIAN: // to be supported by the Arabic shaper?
|
||||
return SHAPING_MONGOLIAN;
|
||||
|
||||
case MOZ_SCRIPT_THAI: // no complex OT features, but MS engines like to do
|
||||
// sequence checking
|
||||
return SHAPING_THAI;
|
||||
|
||||
case MOZ_SCRIPT_BENGALI:
|
||||
case MOZ_SCRIPT_DEVANAGARI:
|
||||
case MOZ_SCRIPT_GUJARATI:
|
||||
case MOZ_SCRIPT_GURMUKHI:
|
||||
case MOZ_SCRIPT_KANNADA:
|
||||
case MOZ_SCRIPT_MALAYALAM:
|
||||
case MOZ_SCRIPT_ORIYA:
|
||||
case MOZ_SCRIPT_SINHALA:
|
||||
case MOZ_SCRIPT_TAMIL:
|
||||
case MOZ_SCRIPT_TELUGU:
|
||||
case MOZ_SCRIPT_KHMER:
|
||||
case MOZ_SCRIPT_LAO:
|
||||
case MOZ_SCRIPT_TIBETAN:
|
||||
case MOZ_SCRIPT_NEW_TAI_LUE:
|
||||
case MOZ_SCRIPT_TAI_LE:
|
||||
case MOZ_SCRIPT_MYANMAR:
|
||||
case MOZ_SCRIPT_PHAGS_PA:
|
||||
case MOZ_SCRIPT_BATAK:
|
||||
case MOZ_SCRIPT_BRAHMI:
|
||||
return SHAPING_INDIC; // scripts that require Indic or other "special" shaping
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClusterIterator::Next()
|
||||
{
|
||||
|
|
|
@ -119,18 +119,6 @@ uint32_t GetLowercase(uint32_t aCh);
|
|||
uint32_t GetTitlecaseForLower(uint32_t aCh); // maps LC to titlecase, UC unchanged
|
||||
uint32_t GetTitlecaseForAll(uint32_t aCh); // maps both UC and LC to titlecase
|
||||
|
||||
enum ShapingType {
|
||||
SHAPING_DEFAULT = 0x0001,
|
||||
SHAPING_ARABIC = 0x0002,
|
||||
SHAPING_HEBREW = 0x0004,
|
||||
SHAPING_HANGUL = 0x0008,
|
||||
SHAPING_MONGOLIAN = 0x0010,
|
||||
SHAPING_INDIC = 0x0020,
|
||||
SHAPING_THAI = 0x0040
|
||||
};
|
||||
|
||||
int32_t ScriptShapingType(int32_t aScriptCode);
|
||||
|
||||
// A simple iterator for a string of char16_t codepoints that advances
|
||||
// by Unicode grapheme clusters
|
||||
class ClusterIterator
|
||||
|
|
|
@ -25,35 +25,35 @@ namespace JS {
|
|||
* byte is treated as a 2-byte character, and there is no way to pass in a
|
||||
* string containing characters beyond U+00FF.
|
||||
*/
|
||||
class Latin1Chars : public mozilla::Range<unsigned char>
|
||||
class Latin1Chars : public mozilla::Range<Latin1Char>
|
||||
{
|
||||
typedef mozilla::Range<unsigned char> Base;
|
||||
typedef mozilla::Range<Latin1Char> Base;
|
||||
|
||||
public:
|
||||
Latin1Chars() : Base() {}
|
||||
Latin1Chars(char *aBytes, size_t aLength) : Base(reinterpret_cast<unsigned char *>(aBytes), aLength) {}
|
||||
Latin1Chars(char *aBytes, size_t aLength) : Base(reinterpret_cast<Latin1Char *>(aBytes), aLength) {}
|
||||
Latin1Chars(const char *aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<unsigned char *>(const_cast<char *>(aBytes)), aLength)
|
||||
: Base(reinterpret_cast<Latin1Char *>(const_cast<char *>(aBytes)), aLength)
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* A Latin1Chars, but with \0 termination for C compatibility.
|
||||
*/
|
||||
class Latin1CharsZ : public mozilla::RangedPtr<unsigned char>
|
||||
class Latin1CharsZ : public mozilla::RangedPtr<Latin1Char>
|
||||
{
|
||||
typedef mozilla::RangedPtr<unsigned char> Base;
|
||||
typedef mozilla::RangedPtr<Latin1Char> Base;
|
||||
|
||||
public:
|
||||
Latin1CharsZ() : Base(nullptr, 0) {}
|
||||
|
||||
Latin1CharsZ(char *aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<unsigned char *>(aBytes), aLength)
|
||||
: Base(reinterpret_cast<Latin1Char *>(aBytes), aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
}
|
||||
|
||||
Latin1CharsZ(unsigned char *aBytes, size_t aLength)
|
||||
Latin1CharsZ(Latin1Char *aBytes, size_t aLength)
|
||||
: Base(aBytes, aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
|
|
|
@ -369,9 +369,27 @@ extern JS_FRIEND_API(void)
|
|||
ShrinkGCBuffers(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* Assert if any GC occured while this guard object was live. This is most
|
||||
* useful to help the exact rooting hazard analysis in complex regions, since
|
||||
* it cannot understand dataflow.
|
||||
* Assert if a GC occurs while this class is live. This class does not disable
|
||||
* the static rooting hazard analysis.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertOnGC)
|
||||
{
|
||||
JSRuntime *runtime;
|
||||
size_t gcNumber;
|
||||
|
||||
public:
|
||||
AutoAssertOnGC();
|
||||
explicit AutoAssertOnGC(JSRuntime *rt);
|
||||
~AutoAssertOnGC();
|
||||
|
||||
static void VerifyIsSafeToGC(JSRuntime *rt);
|
||||
};
|
||||
|
||||
/*
|
||||
* Disable the static rooting hazard analysis in the live region, but assert if
|
||||
* any GC occurs while this guard object is live. This is most useful to help
|
||||
* the exact rooting hazard analysis in complex regions, since it cannot
|
||||
* understand dataflow.
|
||||
*
|
||||
* Note: GC behavior is unpredictable even when deterministice and is generally
|
||||
* non-deterministic in practice. The fact that this guard has not
|
||||
|
@ -381,22 +399,28 @@ ShrinkGCBuffers(JSRuntime *rt);
|
|||
* that the hazard analysis is correct for that code, rather than relying
|
||||
* on this class.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertNoGC)
|
||||
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertOnGC
|
||||
{
|
||||
#ifdef JS_DEBUG
|
||||
JSRuntime *runtime;
|
||||
size_t gcNumber;
|
||||
public:
|
||||
AutoSuppressGCAnalysis() : AutoAssertOnGC() {}
|
||||
explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertOnGC(rt) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* Place AutoCheckCannotGC in scopes that you believe can never GC. These
|
||||
* annotations will be verified both dynamically via AutoAssertOnGC, and
|
||||
* statically with the rooting hazard analysis (implemented by making the
|
||||
* analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
|
||||
* complain if it is live across a GC call.) It is useful when dealing with
|
||||
* internal pointers to GC things where the GC thing itself may not be present
|
||||
* for the static analysis: e.g. acquiring inline chars from a JSString* on the
|
||||
* heap.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC
|
||||
{
|
||||
public:
|
||||
AutoAssertNoGC();
|
||||
AutoAssertNoGC(JSRuntime *rt);
|
||||
~AutoAssertNoGC();
|
||||
#else
|
||||
public:
|
||||
/* Prevent unreferenced local warnings in opt builds. */
|
||||
AutoAssertNoGC() {}
|
||||
explicit AutoAssertNoGC(JSRuntime *) {}
|
||||
#endif
|
||||
AutoCheckCannotGC() : AutoAssertOnGC() {}
|
||||
explicit AutoCheckCannotGC(JSRuntime *rt) : AutoAssertOnGC(rt) {}
|
||||
};
|
||||
|
||||
class JS_PUBLIC_API(ObjectPtr)
|
||||
|
|
|
@ -84,7 +84,7 @@ class ProfileEntry
|
|||
void setCppFrame(void *aSp, uint32_t aLine) volatile {
|
||||
flags |= IS_CPP_ENTRY;
|
||||
spOrScript = aSp;
|
||||
lineOrPc = aLine;
|
||||
lineOrPc = static_cast<int32_t>(aLine);
|
||||
}
|
||||
|
||||
void setFlag(Flags flag) volatile {
|
||||
|
@ -109,7 +109,7 @@ class ProfileEntry
|
|||
}
|
||||
uint32_t line() const volatile {
|
||||
MOZ_ASSERT(!isJs());
|
||||
return lineOrPc;
|
||||
return static_cast<uint32_t>(lineOrPc);
|
||||
}
|
||||
|
||||
// We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
|
||||
|
|
|
@ -34,6 +34,8 @@ typedef char16_t jschar;
|
|||
|
||||
namespace JS {
|
||||
|
||||
typedef unsigned char Latin1Char;
|
||||
|
||||
class Value;
|
||||
template <typename T> class Handle;
|
||||
template <typename T> class MutableHandle;
|
||||
|
|
|
@ -41,6 +41,8 @@ class SourceBufferHolder;
|
|||
|
||||
class HandleValueArray;
|
||||
|
||||
class AutoCheckCannotGC;
|
||||
|
||||
}
|
||||
|
||||
// Do the importing.
|
||||
|
@ -64,6 +66,7 @@ using JS::UndefinedValue;
|
|||
|
||||
using JS::IsPoisonedPtr;
|
||||
|
||||
using JS::Latin1Char;
|
||||
using JS::Latin1CharsZ;
|
||||
using JS::ConstTwoByteChars;
|
||||
using JS::TwoByteChars;
|
||||
|
@ -130,6 +133,8 @@ using JS::HandleValueArray;
|
|||
|
||||
using JS::Zone;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* NamespaceImports_h */
|
||||
|
|
|
@ -138,7 +138,7 @@ var ignoreFunctions = {
|
|||
"PR_ExplodeTime" : true,
|
||||
"PR_ErrorInstallTable" : true,
|
||||
"PR_SetThreadPrivate" : true,
|
||||
"JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoAssertNoGC instead
|
||||
"JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoSuppressGCAnalysis instead
|
||||
"uint8 NS_IsMainThread()" : true,
|
||||
|
||||
// FIXME!
|
||||
|
@ -161,7 +161,7 @@ var ignoreFunctions = {
|
|||
|
||||
// Bug 948646 - the only thing AutoJSContext's constructor calls
|
||||
// is an Init() routine whose entire body is covered with an
|
||||
// AutoAssertNoGC. AutoSafeJSContext is the same thing, just with
|
||||
// AutoSuppressGCAnalysis. AutoSafeJSContext is the same thing, just with
|
||||
// a different value for the 'aSafe' parameter.
|
||||
"void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true,
|
||||
"void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true,
|
||||
|
@ -230,7 +230,7 @@ function isSuppressConstructor(name)
|
|||
{
|
||||
return name.indexOf("::AutoSuppressGC") != -1
|
||||
|| name.indexOf("::AutoEnterAnalysis") != -1
|
||||
|| name.indexOf("::AutoAssertNoGC") != -1
|
||||
|| name.indexOf("::AutoSuppressGCAnalysis") != -1
|
||||
|| name.indexOf("::AutoIgnoreRootingHazards") != -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ addGCType('js::LazyScript');
|
|||
addGCType('js::ion::IonCode');
|
||||
addGCPointer('JS::Value');
|
||||
addGCPointer('jsid');
|
||||
addGCPointer('JS::AutoCheckCannotGC');
|
||||
|
||||
function explain(csu, indent, seen) {
|
||||
if (!seen)
|
||||
|
|
|
@ -537,6 +537,14 @@ class GCRuntime
|
|||
/* Strong references on scripts held for PCCount profiling API. */
|
||||
js::ScriptAndCountsVector *scriptAndCountsVector;
|
||||
|
||||
/*
|
||||
* Some regions of code are hard for the static rooting hazard analysis to
|
||||
* understand. In those cases, we trade the static analysis for a dynamic
|
||||
* analysis. When this is non-zero, we should assert if we trigger, or
|
||||
* might trigger, a GC.
|
||||
*/
|
||||
int inUnsafeRegion;
|
||||
|
||||
private:
|
||||
/* Always preserve JIT code during GCs, for testing. */
|
||||
bool alwaysPreserveCode;
|
||||
|
|
|
@ -172,7 +172,7 @@ js::Nursery::allocate(size_t size)
|
|||
JS_ASSERT(!runtime()->isHeapBusy());
|
||||
JS_ASSERT(position() >= currentStart_);
|
||||
|
||||
if (position() + size > currentEnd()) {
|
||||
if (currentEnd() < position() + size) {
|
||||
if (currentChunk_ + 1 == numActiveChunks_)
|
||||
return nullptr;
|
||||
setCurrentChunk(currentChunk_ + 1);
|
||||
|
|
|
@ -137,7 +137,7 @@ class gcstats::StatisticsSerializer
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
InflateStringToBuffer(buf, nchars, out);
|
||||
CopyAndInflateChars(out, buf, nchars);
|
||||
js_free(buf);
|
||||
|
||||
out[nchars] = 0;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
function testCharCodeAt() {
|
||||
var s = toLatin1("abcdefghijklm1234567891000");
|
||||
for (var i=0; i<10; i++)
|
||||
assertEq(s.charCodeAt(i), 97 + i);
|
||||
|
||||
var rope = s + toLatin1("blah");
|
||||
assertEq(rope.charCodeAt(s.length + 3), 104);
|
||||
|
||||
rope = s + "Foo987";
|
||||
assertEq(rope.charCodeAt(s.length + 4), 56);
|
||||
|
||||
rope = "twoByte\u0580" + s;
|
||||
assertEq(rope.charCodeAt(7), 0x580);
|
||||
assertEq(rope.charCodeAt(14), 103);
|
||||
}
|
||||
testCharCodeAt();
|
||||
|
||||
function testCharAt() {
|
||||
var s = toLatin1("abcdefghijklm100000002345");
|
||||
assertEq(s.charAt(0), "a");
|
||||
assertEq(s.charAt(s.length-1), "5");
|
||||
assertEq(s.charAt(s.length), "");
|
||||
|
||||
var rope = s + toLatin1("abcZYX");
|
||||
assertEq(rope.charAt(s.length + 3), "Z");
|
||||
|
||||
rope = s + "Foo987";
|
||||
assertEq(rope.charAt(s.length + 4), "8");
|
||||
|
||||
rope = "twoByte\u0580" + s;
|
||||
assertEq(rope.charAt(7), "\u0580");
|
||||
assertEq(rope.charAt(14), "g");
|
||||
}
|
||||
testCharAt();
|
||||
|
||||
function testIndex(s) {
|
||||
assertEq(s[0], "a");
|
||||
assertEq(s[s.length-1], "6");
|
||||
|
||||
rope = "twoByte\u0512" + s
|
||||
assertEq(rope[7], "\u0512");
|
||||
}
|
||||
|
||||
var s = toLatin1("abcdefghijklm123456");
|
||||
testIndex(s);
|
||||
testIndex(s);
|
||||
testIndex(s);
|
|
@ -3,3 +3,113 @@ assertEq(isLatin1(s), false);
|
|||
|
||||
s = toLatin1(s);
|
||||
assertEq(isLatin1(s), true);
|
||||
|
||||
function testEq(s) {
|
||||
assertEq(isLatin1(s), true);
|
||||
assertEq(s === "foo02", false);
|
||||
assertEq(s == "foo02", false);
|
||||
|
||||
// Non-atomized to force char comparison.
|
||||
var nonAtomized = "\u1234foo01\u00c7".substr(1);
|
||||
assertEq(isLatin1(nonAtomized), false);
|
||||
assertEq(s === nonAtomized, true);
|
||||
assertEq(nonAtomized !== s, false);
|
||||
assertEq(nonAtomized == s, true);
|
||||
assertEq(s, nonAtomized);
|
||||
|
||||
nonAtomized = "\u1234foo02".substr(1);
|
||||
assertEq(isLatin1(nonAtomized), false);
|
||||
assertEq(s === nonAtomized, false);
|
||||
assertEq(nonAtomized == s, false);
|
||||
}
|
||||
|
||||
s = "foo01\u00c7";
|
||||
s = toLatin1(s);
|
||||
testEq(s);
|
||||
testEq(s);
|
||||
|
||||
function testConcat() {
|
||||
function concat(s1, s2) {
|
||||
return s1 + s2;
|
||||
}
|
||||
|
||||
// Following tests create fat inline strings.
|
||||
assertEq(concat("abc", "def"), "abcdef");
|
||||
var s1 = toLatin1("ABC");
|
||||
var s2 = toLatin1("DEF");
|
||||
assertEq(concat(s1, s2), "ABCDEF");
|
||||
assertEq(concat(s1, "GHI\u0580"), "ABCGHI\u0580");
|
||||
assertEq(concat("GHI\u0580", s2), "GHI\u0580DEF");
|
||||
assertEq(concat(concat("GHI\u0580", s2), s1), "GHI\u0580DEFABC");
|
||||
assertEq(isLatin1(s1), true);
|
||||
assertEq(isLatin1(s2), true);
|
||||
|
||||
// Create a Latin1 rope.
|
||||
var s3 = toLatin1("0123456789012345678901234567890123456789");
|
||||
var rope = concat(s1, s3);
|
||||
assertEq(isLatin1(rope), true);
|
||||
assertEq(rope, "ABC0123456789012345678901234567890123456789");
|
||||
assertEq(isLatin1(rope), true); // Still Latin1 after flattening.
|
||||
|
||||
// Latin1 + TwoByte => TwoByte rope.
|
||||
assertEq(isLatin1(s3), true);
|
||||
rope = concat(s3, "someTwoByteString\u0580");
|
||||
assertEq(isLatin1(rope), false);
|
||||
assertEq(rope, "0123456789012345678901234567890123456789someTwoByteString\u0580");
|
||||
assertEq(isLatin1(rope), false);
|
||||
|
||||
assertEq(isLatin1(s3), true);
|
||||
rope = concat("twoByteString\u0580", concat(s3, "otherTwoByte\u0580"));
|
||||
assertEq(isLatin1(rope), false);
|
||||
assertEq(rope, "twoByteString\u05800123456789012345678901234567890123456789otherTwoByte\u0580");
|
||||
assertEq(isLatin1(rope), false);
|
||||
|
||||
// Build a Latin1 rope with left-most string an extensible string.
|
||||
var s4 = toLatin1("adsfasdfjkasdfkjasdfasasdfasdf");
|
||||
for (var i=0; i<5; i++) {
|
||||
s4 = concat(s4, s1);
|
||||
assertEq(s4 === ".".repeat(s4.length), false); // Flatten rope.
|
||||
}
|
||||
|
||||
assertEq(isLatin1(s4), true);
|
||||
|
||||
// Appending a TwoByte string must inflate.
|
||||
s4 = concat(s4, "--\u0580");
|
||||
assertEq(s4, "adsfasdfjkasdfkjasdfasasdfasdfABCABCABCABCABC--\u0580");
|
||||
}
|
||||
testConcat();
|
||||
|
||||
function testFlattenDependent() {
|
||||
function concat(s1, s2) {
|
||||
return s1 + s2;
|
||||
}
|
||||
|
||||
// Create some latin1 strings.
|
||||
var s1 = toLatin1("Foo0123456789bar012345---");
|
||||
var s2 = toLatin1("Foo0123456789bar012345+++");
|
||||
assertEq(isLatin1(s1), true);
|
||||
assertEq(isLatin1(s2), true);
|
||||
|
||||
// And some ropes.
|
||||
var rope1 = concat(s1, s1);
|
||||
assertEq(isLatin1(rope1), true);
|
||||
|
||||
var rope2 = concat(rope1, s2);
|
||||
assertEq(isLatin1(rope2), true);
|
||||
|
||||
var rope3 = concat("twoByte\u0581", rope2);
|
||||
assertEq(isLatin1(rope3), false);
|
||||
|
||||
// Flatten everything.
|
||||
assertEq(rope3, "twoByte\u0581Foo0123456789bar012345---Foo0123456789bar012345---Foo0123456789bar012345+++");
|
||||
assertEq(isLatin1(rope3), false);
|
||||
|
||||
// rope1 and rope2 were Latin1, but flattening rope3 turned them into
|
||||
// dependent strings, so rope1 and rope2 must also be TwoByte now.
|
||||
assertEq(isLatin1(rope1), false);
|
||||
assertEq(isLatin1(rope2), false);
|
||||
|
||||
assertEq(rope1, "Foo0123456789bar012345---Foo0123456789bar012345---");
|
||||
assertEq(rope2, "Foo0123456789bar012345---Foo0123456789bar012345---Foo0123456789bar012345+++");
|
||||
}
|
||||
testFlattenDependent();
|
||||
|
|
|
@ -4425,8 +4425,7 @@ ICGetElem_String::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
key, &failure);
|
||||
|
||||
// Get char code.
|
||||
masm.loadStringChars(str, scratchReg);
|
||||
masm.load16ZeroExtend(BaseIndex(scratchReg, key, TimesTwo, 0), scratchReg);
|
||||
masm.loadStringChar(str, key, scratchReg);
|
||||
|
||||
// Check if char code >= UNIT_STATIC_LIMIT.
|
||||
masm.branch32(Assembler::AboveOrEqual, scratchReg, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
|
||||
|
|
|
@ -5154,7 +5154,8 @@ CodeGenerator::visitConcatPar(LConcatPar *lir)
|
|||
}
|
||||
|
||||
static void
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch)
|
||||
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
|
||||
size_t fromWidth, size_t toWidth)
|
||||
{
|
||||
// Copy |len| jschars from |from| to |to|. Assumes len > 0 (checked below in
|
||||
// debug builds), and when done |to| must point to the next available char.
|
||||
|
@ -5166,17 +5167,125 @@ CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
|
|||
masm.bind(&ok);
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(jschar) == 2);
|
||||
MOZ_ASSERT(fromWidth == 1 || fromWidth == 2);
|
||||
MOZ_ASSERT(toWidth == 1 || toWidth == 2);
|
||||
MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1);
|
||||
|
||||
Label start;
|
||||
masm.bind(&start);
|
||||
masm.load16ZeroExtend(Address(from, 0), scratch);
|
||||
masm.store16(scratch, Address(to, 0));
|
||||
masm.addPtr(Imm32(2), from);
|
||||
masm.addPtr(Imm32(2), to);
|
||||
if (fromWidth == 2)
|
||||
masm.load16ZeroExtend(Address(from, 0), scratch);
|
||||
else
|
||||
masm.load8ZeroExtend(Address(from, 0), scratch);
|
||||
if (toWidth == 2)
|
||||
masm.store16(scratch, Address(to, 0));
|
||||
else
|
||||
masm.store8(scratch, Address(to, 0));
|
||||
masm.addPtr(Imm32(fromWidth), from);
|
||||
masm.addPtr(Imm32(toWidth), to);
|
||||
masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
|
||||
}
|
||||
|
||||
static void
|
||||
CopyStringCharsMaybeInflate(MacroAssembler &masm, Register input, Register destChars,
|
||||
Register temp1, Register temp2)
|
||||
{
|
||||
// destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
|
||||
// have to inflate.
|
||||
|
||||
Label isLatin1, done;
|
||||
masm.loadStringLength(input, temp1);
|
||||
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
{
|
||||
masm.loadStringChars(input, input);
|
||||
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(jschar), sizeof(jschar));
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(&isLatin1);
|
||||
{
|
||||
masm.loadStringChars(input, input);
|
||||
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(jschar));
|
||||
}
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
static void
|
||||
ConcatFatInlineString(MacroAssembler &masm, Register lhs, Register rhs, Register output,
|
||||
Register temp1, Register temp2, Register temp3, Register forkJoinContext,
|
||||
ExecutionMode mode, Label *failure, Label *failurePopTemps, bool isTwoByte)
|
||||
{
|
||||
// State: result length in temp2.
|
||||
|
||||
// Ensure both strings are linear.
|
||||
masm.branchIfRope(lhs, failure);
|
||||
masm.branchIfRope(rhs, failure);
|
||||
|
||||
// Allocate a JSFatInlineString.
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
masm.newGCFatInlineString(output, temp1, failure);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
masm.push(temp1);
|
||||
masm.push(temp2);
|
||||
masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
masm.pop(temp1);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Store length and flags.
|
||||
uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
|
||||
if (!isTwoByte)
|
||||
flags |= JSString::LATIN1_CHARS_BIT;
|
||||
masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Load chars pointer in temp2.
|
||||
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
|
||||
|
||||
{
|
||||
// We use temp3 in this block, which in parallel execution also holds
|
||||
// a live ForkJoinContext pointer. If we are compiling for parallel
|
||||
// execution, be sure to save and restore the ForkJoinContext.
|
||||
if (mode == ParallelExecution)
|
||||
masm.push(temp3);
|
||||
|
||||
// Copy lhs chars. Note that this advances temp2 to point to the next
|
||||
// char. This also clobbers the lhs register.
|
||||
if (isTwoByte) {
|
||||
CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3);
|
||||
} else {
|
||||
masm.loadStringLength(lhs, temp3);
|
||||
masm.loadStringChars(lhs, lhs);
|
||||
CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char));
|
||||
}
|
||||
|
||||
// Copy rhs chars. Clobbers the rhs register.
|
||||
if (isTwoByte) {
|
||||
CopyStringCharsMaybeInflate(masm, rhs, temp2, temp1, temp3);
|
||||
} else {
|
||||
masm.loadStringLength(rhs, temp3);
|
||||
masm.loadStringChars(rhs, rhs);
|
||||
CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char));
|
||||
}
|
||||
|
||||
// Null-terminate.
|
||||
if (isTwoByte)
|
||||
masm.store16(Imm32(0), Address(temp2, 0));
|
||||
else
|
||||
masm.store8(Imm32(0), Address(temp2, 0));
|
||||
|
||||
if (mode == ParallelExecution)
|
||||
masm.pop(temp3);
|
||||
}
|
||||
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
|
@ -5208,10 +5317,27 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
|
||||
masm.add32(temp1, temp2);
|
||||
|
||||
// Check if we can use a JSFatInlineString.
|
||||
Label isFatInline;
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
|
||||
&isFatInline);
|
||||
// Check if we can use a JSFatInlineString. The result is a Latin1 string if
|
||||
// lhs and rhs are both Latin1, so we AND the flags.
|
||||
Label isFatInlineTwoByte, isFatInlineLatin1;
|
||||
masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
|
||||
masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
|
||||
|
||||
Label isLatin1, notInline;
|
||||
masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
{
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
|
||||
&isFatInlineTwoByte);
|
||||
masm.jump(¬Inline);
|
||||
}
|
||||
masm.bind(&isLatin1);
|
||||
{
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
|
||||
&isFatInlineLatin1);
|
||||
}
|
||||
masm.bind(¬Inline);
|
||||
|
||||
// Keep AND'ed flags in temp1.
|
||||
|
||||
// Ensure result length <= JSString::MAX_LENGTH.
|
||||
masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
|
||||
|
@ -5232,8 +5358,12 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Store lengthAndFlags.
|
||||
masm.store32(Imm32(JSString::ROPE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
// Store rope length and flags. temp1 still holds the result of AND'ing the
|
||||
// lhs and rhs flags, so we just have to clear the other flags to get our
|
||||
// rope flags (Latin1 if both lhs and rhs are Latin1).
|
||||
static_assert(JSString::ROPE_FLAGS == 0, "Rope flags must be 0");
|
||||
masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
|
||||
masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Store left and right nodes.
|
||||
|
@ -5249,62 +5379,13 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
masm.mov(lhs, output);
|
||||
masm.ret();
|
||||
|
||||
masm.bind(&isFatInline);
|
||||
masm.bind(&isFatInlineTwoByte);
|
||||
ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
|
||||
mode, &failure, &failurePopTemps, true);
|
||||
|
||||
// State: lhs length in temp1, result length in temp2.
|
||||
|
||||
// Ensure both strings are linear.
|
||||
masm.branchIfRope(lhs, &failure);
|
||||
masm.branchIfRope(rhs, &failure);
|
||||
|
||||
// Allocate a JSFatInlineString.
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
masm.newGCFatInlineString(output, temp3, &failure);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
masm.push(temp1);
|
||||
masm.push(temp2);
|
||||
masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
masm.pop(temp1);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Set length and flags.
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Load chars pointer in temp2.
|
||||
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
|
||||
|
||||
{
|
||||
// We use temp3 in this block, which in parallel execution also holds
|
||||
// a live ForkJoinContext pointer. If we are compiling for parallel
|
||||
// execution, be sure to save and restore the ForkJoinContext.
|
||||
if (mode == ParallelExecution)
|
||||
masm.push(temp3);
|
||||
|
||||
// Copy lhs chars. Temp1 still holds the lhs length. Note that this
|
||||
// advances temp2 to point to the next char. Note that this also
|
||||
// repurposes the lhs register.
|
||||
masm.loadStringChars(lhs, lhs);
|
||||
CopyStringChars(masm, temp2, lhs, temp1, temp3);
|
||||
|
||||
// Copy rhs chars.
|
||||
masm.loadStringLength(rhs, temp1);
|
||||
masm.loadStringChars(rhs, rhs);
|
||||
CopyStringChars(masm, temp2, rhs, temp1, temp3);
|
||||
|
||||
if (mode == ParallelExecution)
|
||||
masm.pop(temp3);
|
||||
}
|
||||
|
||||
// Null-terminate.
|
||||
masm.store16(Imm32(0), Address(temp2, 0));
|
||||
masm.ret();
|
||||
masm.bind(&isFatInlineLatin1);
|
||||
ConcatFatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, forkJoinContext,
|
||||
mode, &failure, &failurePopTemps, false);
|
||||
|
||||
masm.bind(&failurePopTemps);
|
||||
masm.pop(temp2);
|
||||
|
@ -5406,9 +5487,7 @@ CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
|
|||
return false;
|
||||
|
||||
masm.branchIfRope(str, ool->entry());
|
||||
|
||||
masm.loadStringChars(str, output);
|
||||
masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
|
||||
masm.loadStringChar(str, index, output);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
|
|
@ -484,7 +484,7 @@ MacroAssembler::nurseryAllocate(Register result, Register slots, gc::AllocKind a
|
|||
int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
|
||||
loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
|
||||
computeEffectiveAddress(Address(result, totalSize), temp);
|
||||
branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail);
|
||||
branchPtr(Assembler::Below, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail);
|
||||
storePtr(temp, AbsoluteAddress(nursery.addressOfPosition()));
|
||||
|
||||
if (nDynamicSlots)
|
||||
|
@ -881,6 +881,26 @@ MacroAssembler::loadStringChars(Register str, Register dest)
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::loadStringChar(Register str, Register index, Register output)
|
||||
{
|
||||
MOZ_ASSERT(str != output);
|
||||
MOZ_ASSERT(index != output);
|
||||
|
||||
loadStringChars(str, output);
|
||||
|
||||
Label isLatin1, done;
|
||||
branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
load16ZeroExtend(BaseIndex(output, index, TimesTwo), output);
|
||||
jump(&done);
|
||||
|
||||
bind(&isLatin1);
|
||||
load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
|
||||
{
|
||||
|
|
|
@ -381,6 +381,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
|
||||
void loadStringChars(Register str, Register dest);
|
||||
void loadStringChar(Register str, Register index, Register output);
|
||||
|
||||
void branchIfRope(Register str, Label *label) {
|
||||
Address flags(str, JSString::offsetOfFlags());
|
||||
|
|
|
@ -357,11 +357,12 @@ CompareStringsPar(ForkJoinContext *cx, JSString *left, JSString *right, int32_t
|
|||
{
|
||||
ScopedThreadSafeStringInspector leftInspector(left);
|
||||
ScopedThreadSafeStringInspector rightInspector(right);
|
||||
if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
|
||||
AutoCheckCannotGC nogc;
|
||||
if (!leftInspector.ensureChars(cx, nogc) || !rightInspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
*res = CompareChars(leftInspector.chars(), left->length(),
|
||||
rightInspector.chars(), right->length());
|
||||
*res = CompareChars(leftInspector.twoByteChars(), left->length(),
|
||||
rightInspector.twoByteChars(), right->length());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1936,6 +1936,13 @@ MacroAssemblerARMCompat::and32(Imm32 imm, Register dest)
|
|||
ma_and(imm, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::and32(const Address &src, Register dest)
|
||||
{
|
||||
load32(src, ScratchRegister);
|
||||
ma_and(ScratchRegister, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::addPtr(Register src, Register dest)
|
||||
{
|
||||
|
|
|
@ -1299,6 +1299,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void and32(Register src, Register dest);
|
||||
void and32(Imm32 imm, Register dest);
|
||||
void and32(Imm32 imm, const Address &dest);
|
||||
void and32(const Address &src, Register dest);
|
||||
void or32(Imm32 imm, const Address &dest);
|
||||
void xorPtr(Imm32 imm, Register dest);
|
||||
void xorPtr(Register src, Register dest);
|
||||
|
|
|
@ -111,6 +111,9 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
void and32(Register src, Register dest) {
|
||||
andl(src, dest);
|
||||
}
|
||||
void and32(const Address &src, Register dest) {
|
||||
andl(Operand(src), dest);
|
||||
}
|
||||
void and32(Imm32 imm, Register dest) {
|
||||
andl(imm, dest);
|
||||
}
|
||||
|
|
|
@ -21,3 +21,17 @@ BEGIN_TEST(testGCExactRooting)
|
|||
return true;
|
||||
}
|
||||
END_TEST(testGCExactRooting)
|
||||
|
||||
BEGIN_TEST(testGCSuppressions)
|
||||
{
|
||||
JS::AutoAssertOnGC nogc;
|
||||
JS::AutoCheckCannotGC checkgc;
|
||||
JS::AutoSuppressGCAnalysis noanalysis;
|
||||
|
||||
JS::AutoAssertOnGC nogcRt(cx->runtime());
|
||||
JS::AutoCheckCannotGC checkgcRt(cx->runtime());
|
||||
JS::AutoSuppressGCAnalysis noanalysisRt(cx->runtime());
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCSuppressions)
|
||||
|
|
|
@ -5597,14 +5597,14 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_
|
|||
size_t dstlen = *dstlenp;
|
||||
|
||||
if (srclen > dstlen) {
|
||||
InflateStringToBuffer(src, dstlen, dst);
|
||||
CopyAndInflateChars(dst, src, dstlen);
|
||||
|
||||
AutoSuppressGC suppress(cx);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
|
||||
return false;
|
||||
}
|
||||
|
||||
InflateStringToBuffer(src, srclen, dst);
|
||||
CopyAndInflateChars(dst, src, srclen);
|
||||
*dstlenp = srclen;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -452,7 +452,7 @@ js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavi
|
|||
* js::AtomizeString rarely has to copy the temp string we make.
|
||||
*/
|
||||
jschar inflated[ATOMIZE_BUF_MAX];
|
||||
InflateStringToBuffer(bytes, length, inflated);
|
||||
CopyAndInflateChars(inflated, bytes, length);
|
||||
return AtomizeAndCopyChars(cx, inflated, length, ib);
|
||||
}
|
||||
|
||||
|
|
|
@ -589,9 +589,9 @@ struct Atom {
|
|||
uint32_t flags;
|
||||
uint32_t length;
|
||||
union {
|
||||
const char *nonInlineCharsLatin1;
|
||||
const JS::Latin1Char *nonInlineCharsLatin1;
|
||||
const jschar *nonInlineCharsTwoByte;
|
||||
char inlineStorageLatin1[1];
|
||||
JS::Latin1Char inlineStorageLatin1[1];
|
||||
jschar inlineStorageTwoByte[1];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -188,6 +188,7 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsscript.h"
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
@ -1114,13 +1115,14 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||
mallocBytes(0),
|
||||
mallocGCTriggered(false),
|
||||
scriptAndCountsVector(nullptr),
|
||||
helperState(rt),
|
||||
alwaysPreserveCode(false),
|
||||
#ifdef DEBUG
|
||||
noGCOrAllocationCheck(0),
|
||||
#endif
|
||||
lock(nullptr),
|
||||
lockOwner(nullptr),
|
||||
helperState(rt)
|
||||
inUnsafeRegion(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -4313,6 +4315,9 @@ AutoGCSession::AutoGCSession(GCRuntime *gc)
|
|||
// It's ok if threads other than the main thread have suppressGC set, as
|
||||
// they are operating on zones which will not be collected from here.
|
||||
JS_ASSERT(!gc->rt->mainThread.suppressGC);
|
||||
|
||||
// Assert if this is a GC unsafe region.
|
||||
JS::AutoAssertOnGC::VerifyIsSafeToGC(gc->rt);
|
||||
}
|
||||
|
||||
AutoGCSession::~AutoGCSession()
|
||||
|
@ -5603,32 +5608,50 @@ JS::GetGCNumber()
|
|||
return 0;
|
||||
return rt->gc.number;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS::AutoAssertNoGC::AutoAssertNoGC()
|
||||
JS::AutoAssertOnGC::AutoAssertOnGC()
|
||||
: runtime(nullptr), gcNumber(0)
|
||||
{
|
||||
js::PerThreadData *data = js::TlsPerThreadData.get();
|
||||
if (data) {
|
||||
/*
|
||||
* GC's from off-thread will always assert, so off-thread is implicitly
|
||||
* AutoAssertNoGC. We still need to allow AutoAssertNoGC to be used in
|
||||
* AutoAssertOnGC. We still need to allow AutoAssertOnGC to be used in
|
||||
* code that works from both threads, however. We also use this to
|
||||
* annotate the off thread run loops.
|
||||
*/
|
||||
runtime = data->runtimeIfOnOwnerThread();
|
||||
if (runtime)
|
||||
if (runtime) {
|
||||
gcNumber = runtime->gc.number;
|
||||
++runtime->gc.inUnsafeRegion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS::AutoAssertNoGC::AutoAssertNoGC(JSRuntime *rt)
|
||||
JS::AutoAssertOnGC::AutoAssertOnGC(JSRuntime *rt)
|
||||
: runtime(rt), gcNumber(rt->gc.number)
|
||||
{
|
||||
++rt->gc.inUnsafeRegion;
|
||||
}
|
||||
|
||||
JS::AutoAssertNoGC::~AutoAssertNoGC()
|
||||
JS::AutoAssertOnGC::~AutoAssertOnGC()
|
||||
{
|
||||
if (runtime)
|
||||
MOZ_ASSERT(gcNumber == runtime->gc.number, "GC ran inside an AutoAssertNoGC scope.");
|
||||
if (runtime) {
|
||||
--runtime->gc.inUnsafeRegion;
|
||||
MOZ_ASSERT(runtime->gc.inUnsafeRegion >= 0);
|
||||
|
||||
/*
|
||||
* The following backstop assertion should never fire: if we bumped the
|
||||
* gcNumber, we should have asserted because inUnsafeRegion was true.
|
||||
*/
|
||||
MOZ_ASSERT(gcNumber == runtime->gc.number, "GC ran inside an AutoAssertOnGC scope.");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gc.inUnsafeRegion > 0)
|
||||
MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -504,6 +504,10 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
|
|||
JS_ASSERT(rt->gc.isAllocAllowed());
|
||||
#endif
|
||||
|
||||
// Crash if we perform a GC action when it is not safe.
|
||||
if (allowGC && !rt->mainThread.suppressGC)
|
||||
JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
|
||||
|
||||
// For testing out of memory conditions
|
||||
if (!PossiblyFail()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
|
|
|
@ -605,7 +605,7 @@ js::Int32ToString(ThreadSafeContext *cx, int32_t si)
|
|||
jschar *start = BackfillInt32InBuffer(si, buffer,
|
||||
JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
|
||||
|
||||
PodCopy(str->init(length), start, length + 1);
|
||||
PodCopy(str->initTwoByte(length), start, length + 1);
|
||||
|
||||
CacheNumber(cx, si, str);
|
||||
return str;
|
||||
|
@ -1437,7 +1437,7 @@ js::IndexToString(JSContext *cx, uint32_t index)
|
|||
*end = '\0';
|
||||
RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
|
||||
|
||||
jschar *dst = str->init(end - start);
|
||||
jschar *dst = str->initTwoByte(end - start);
|
||||
PodCopy(dst, start.get(), end - start + 1);
|
||||
|
||||
c->dtoaCache.cache(10, index, str);
|
||||
|
@ -1533,11 +1533,12 @@ CharsToNumber(ThreadSafeContext *cx, const jschar *chars, size_t length, double
|
|||
bool
|
||||
js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector inspector(str);
|
||||
if (!inspector.ensureChars(cx))
|
||||
if (!inspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
return CharsToNumber(cx, inspector.chars(), str->length(), result);
|
||||
return CharsToNumber(cx, inspector.twoByteChars(), str->length(), result);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1203,7 +1203,7 @@ inline JSObject *
|
|||
GetInnerObject(JSObject *obj)
|
||||
{
|
||||
if (js::InnerObjectOp op = obj->getClass()->ext.innerObject) {
|
||||
JS::AutoAssertNoGC nogc;
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
return op(obj);
|
||||
}
|
||||
return obj;
|
||||
|
|
|
@ -2780,8 +2780,8 @@ FlattenSubstrings(JSContext *cx, const jschar *chars,
|
|||
JSFatInlineString *str = js_NewGCFatInlineString<CanGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
jschar *buf = str->init(outputLen);
|
||||
|
||||
jschar *buf = str->initTwoByte(outputLen);
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < rangesLen; i++) {
|
||||
PodCopy(buf + pos, chars + ranges[i].start, ranges[i].length);
|
||||
|
@ -4198,6 +4198,37 @@ js::StringToSource(JSContext *cx, JSString *str)
|
|||
return js_QuoteString(cx, str, '"');
|
||||
}
|
||||
|
||||
static bool
|
||||
EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
|
||||
{
|
||||
for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
|
||||
if (jschar(*s1) != *s2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EqualChars(JSLinearString *str1, JSLinearString *str2)
|
||||
{
|
||||
MOZ_ASSERT(str1->length() == str2->length());
|
||||
|
||||
size_t len = str1->length();
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
if (str1->hasTwoByteChars()) {
|
||||
if (str2->hasTwoByteChars())
|
||||
return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
|
||||
|
||||
return EqualCharsLatin1TwoByte(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
|
||||
}
|
||||
|
||||
if (str2->hasLatin1Chars())
|
||||
return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
|
||||
|
||||
return EqualCharsLatin1TwoByte(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
|
||||
}
|
||||
|
||||
bool
|
||||
js::EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result)
|
||||
{
|
||||
|
@ -4219,7 +4250,7 @@ js::EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result)
|
|||
if (!linear2)
|
||||
return false;
|
||||
|
||||
*result = PodEqual(linear1->chars(), linear2->chars(), length1);
|
||||
*result = EqualChars(linear1, linear2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4233,7 +4264,7 @@ js::EqualStrings(JSLinearString *str1, JSLinearString *str2)
|
|||
if (length1 != str2->length())
|
||||
return false;
|
||||
|
||||
return PodEqual(str1->chars(), str2->chars(), length1);
|
||||
return EqualChars(str1, str2);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -256,12 +256,19 @@ InflateString(ThreadSafeContext *cx, const char *bytes, size_t *length);
|
|||
* enough for 'srclen' jschars. The buffer is NOT null-terminated.
|
||||
*/
|
||||
inline void
|
||||
InflateStringToBuffer(const char *src, size_t srclen, jschar *dst)
|
||||
CopyAndInflateChars(jschar *dst, const char *src, size_t srclen)
|
||||
{
|
||||
for (size_t i = 0; i < srclen; i++)
|
||||
dst[i] = (unsigned char) src[i];
|
||||
}
|
||||
|
||||
inline void
|
||||
CopyAndInflateChars(jschar *dst, const JS::Latin1Char *src, size_t srclen)
|
||||
{
|
||||
for (size_t i = 0; i < srclen; i++)
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for
|
||||
* 'length chars. The buffer is NOT null-terminated. The destination length
|
||||
|
|
|
@ -165,7 +165,7 @@ ObjectValueMap::findZoneEdges()
|
|||
* edge to ensure that the delegate zone does finish marking after the key
|
||||
* zone.
|
||||
*/
|
||||
JS::AutoAssertNoGC nogc;
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
Zone *mapZone = compartment->zone();
|
||||
for (Range r = all(); !r.empty(); r.popFront()) {
|
||||
JSObject *key = r.front().key();
|
||||
|
|
|
@ -1047,7 +1047,7 @@ HelperThread::handleGCHelperWorkload()
|
|||
void
|
||||
HelperThread::threadLoop()
|
||||
{
|
||||
JS::AutoAssertNoGC nogc;
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
AutoLockHelperThreadState lock;
|
||||
|
||||
js::TlsPerThreadData.set(threadData.addr());
|
||||
|
|
|
@ -132,9 +132,11 @@ DeflateStringToUTF8Buffer(js::ThreadSafeContext *cx, const jschar *src, size_t s
|
|||
|
||||
bufferTooSmall:
|
||||
*dstlenp = (origDstlen - dstlen);
|
||||
if (cx->isJSContext())
|
||||
if (cx->isJSContext()) {
|
||||
js::gc::AutoSuppressGC suppress(cx->asJSContext());
|
||||
JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
|
||||
JSMSG_BUFFER_TOO_SMALL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1503,15 +1503,15 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
|||
// WARNING: This code runs ON THE PARALLEL WORKER THREAD.
|
||||
// Be careful when accessing cx_.
|
||||
|
||||
// ForkJoinContext already contains an AutoAssertNoGC; however, the analysis
|
||||
// does not propagate this type information. We duplicate the assertion
|
||||
// here for maximum clarity.
|
||||
JS::AutoAssertNoGC nogc(runtime());
|
||||
|
||||
Allocator *allocator = allocators_[worker->id()];
|
||||
ForkJoinContext cx(perThread, worker, allocator, this, &records_[worker->id()]);
|
||||
AutoSetForkJoinContext autoContext(&cx);
|
||||
|
||||
// ForkJoinContext already contains an AutoSuppressGCAnalysis; however, the
|
||||
// analysis does not propagate this type information. We duplicate the
|
||||
// assertion here for maximum clarity.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Set the maximum worker and slice number for prettier spewing.
|
||||
cx.maxWorkerId = threadPool_->numWorkers();
|
||||
|
@ -1622,7 +1622,7 @@ ForkJoinContext::ForkJoinContext(PerThreadData *perThreadData, ThreadPoolWorker
|
|||
shared_(shared),
|
||||
worker_(worker),
|
||||
acquiredJSContext_(false),
|
||||
nogc_(shared->runtime())
|
||||
nogc_()
|
||||
{
|
||||
/*
|
||||
* Unsafely set the zone. This is used to track malloc counters and to
|
||||
|
|
|
@ -426,7 +426,7 @@ class ForkJoinContext : public ThreadSafeContext
|
|||
|
||||
// ForkJoinContext is allocated on the stack. It would be dangerous to GC
|
||||
// with it live because of the GC pointer fields stored in the context.
|
||||
JS::AutoAssertNoGC nogc_;
|
||||
JS::AutoSuppressGCAnalysis nogc_;
|
||||
};
|
||||
|
||||
// Locks a JSContext for its scope. Be very careful, because locking a
|
||||
|
|
|
@ -60,28 +60,8 @@ using mozilla::NumberEqualsInt32;
|
|||
using mozilla::PodCopy;
|
||||
using JS::ForOfIterator;
|
||||
|
||||
/*
|
||||
* Note: when Clang 3.2 (32-bit) inlines the two functions below in Interpret,
|
||||
* the conservative stack scanner leaks a ton of memory and this negatively
|
||||
* influences performance. The MOZ_NEVER_INLINE is a temporary workaround until
|
||||
* we can remove the conservative scanner. See bug 849526 for more info.
|
||||
*/
|
||||
#if defined(__clang__) && defined(JS_CPU_X86)
|
||||
static MOZ_NEVER_INLINE bool
|
||||
#else
|
||||
static bool
|
||||
#endif
|
||||
ToBooleanOp(const InterpreterRegs ®s)
|
||||
{
|
||||
return ToBoolean(regs.stackHandleAt(-1));
|
||||
}
|
||||
|
||||
template <bool Eq>
|
||||
#if defined(__clang__) && defined(JS_CPU_X86)
|
||||
static MOZ_NEVER_INLINE bool
|
||||
#else
|
||||
static bool
|
||||
#endif
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
LooseEqualityOp(JSContext *cx, InterpreterRegs ®s)
|
||||
{
|
||||
HandleValue rval = regs.stackHandleAt(-1);
|
||||
|
@ -1850,7 +1830,7 @@ CASE(JSOP_GOTO)
|
|||
|
||||
CASE(JSOP_IFEQ)
|
||||
{
|
||||
bool cond = ToBooleanOp(REGS);
|
||||
bool cond = ToBoolean(REGS.stackHandleAt(-1));
|
||||
REGS.sp--;
|
||||
if (!cond)
|
||||
BRANCH(GET_JUMP_OFFSET(REGS.pc));
|
||||
|
@ -1859,7 +1839,7 @@ END_CASE(JSOP_IFEQ)
|
|||
|
||||
CASE(JSOP_IFNE)
|
||||
{
|
||||
bool cond = ToBooleanOp(REGS);
|
||||
bool cond = ToBoolean(REGS.stackHandleAt(-1));
|
||||
REGS.sp--;
|
||||
if (cond)
|
||||
BRANCH(GET_JUMP_OFFSET(REGS.pc));
|
||||
|
@ -1868,7 +1848,7 @@ END_CASE(JSOP_IFNE)
|
|||
|
||||
CASE(JSOP_OR)
|
||||
{
|
||||
bool cond = ToBooleanOp(REGS);
|
||||
bool cond = ToBoolean(REGS.stackHandleAt(-1));
|
||||
if (cond)
|
||||
ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc));
|
||||
}
|
||||
|
@ -1876,7 +1856,7 @@ END_CASE(JSOP_OR)
|
|||
|
||||
CASE(JSOP_AND)
|
||||
{
|
||||
bool cond = ToBooleanOp(REGS);
|
||||
bool cond = ToBoolean(REGS.stackHandleAt(-1));
|
||||
if (!cond)
|
||||
ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc));
|
||||
}
|
||||
|
@ -2273,7 +2253,7 @@ END_CASE(JSOP_MOD)
|
|||
|
||||
CASE(JSOP_NOT)
|
||||
{
|
||||
bool cond = ToBooleanOp(REGS);
|
||||
bool cond = ToBoolean(REGS.stackHandleAt(-1));
|
||||
REGS.sp--;
|
||||
PUSH_BOOLEAN(!cond);
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ SPSProfiler::markEvent(const char *event)
|
|||
{
|
||||
JS_ASSERT(enabled());
|
||||
if (eventMarker_) {
|
||||
JS::AutoAssertNoGC nogc;
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
eventMarker_(event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,11 +279,12 @@ intrinsic_ParallelSpew(ThreadSafeContext *cx, unsigned argc, Value *vp)
|
|||
JS_ASSERT(args.length() == 1);
|
||||
JS_ASSERT(args[0].isString());
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector inspector(args[0].toString());
|
||||
if (!inspector.ensureChars(cx))
|
||||
if (!inspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.range()).c_str());
|
||||
ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.twoByteRange()).c_str());
|
||||
parallel::Spew(parallel::SpewOps, bytes);
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
|
|
@ -559,7 +559,7 @@ FrameIter::settleOnActivation()
|
|||
if (data_.principals_) {
|
||||
JSContext *cx = data_.cx_->asJSContext();
|
||||
if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
|
||||
JS::AutoAssertNoGC nogc;
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
if (!subsumes(data_.principals_, activation->compartment()->principals)) {
|
||||
++data_.activations_;
|
||||
continue;
|
||||
|
|
|
@ -32,12 +32,12 @@ NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
|
|||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
p = str->init(len);
|
||||
p = str->initTwoByte(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
p = fatstr->init(len);
|
||||
p = fatstr->initTwoByte(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
|
@ -65,12 +65,12 @@ NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
|
|||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
storage = str->init(len);
|
||||
storage = str->initTwoByte(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
storage = fatstr->init(len);
|
||||
storage = fatstr->initTwoByte(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,8 @@ JSRope::init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t
|
|||
{
|
||||
d.u1.length = length;
|
||||
d.u1.flags = ROPE_FLAGS;
|
||||
if (left->hasLatin1Chars() && right->hasLatin1Chars())
|
||||
d.u1.flags |= LATIN1_CHARS_BIT;
|
||||
d.s.u2.left = left;
|
||||
d.s.u3.right = right;
|
||||
js::StringWriteBarrierPost(cx, &d.s.u2.left);
|
||||
|
@ -248,7 +250,7 @@ JSInlineString::new_(js::ThreadSafeContext *cx)
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSInlineString::init(size_t length)
|
||||
JSInlineString::initTwoByte(size_t length)
|
||||
{
|
||||
JS_ASSERT(twoByteLengthFits(length));
|
||||
d.u1.length = length;
|
||||
|
@ -256,8 +258,17 @@ JSInlineString::init(size_t length)
|
|||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JS::Latin1Char *
|
||||
JSInlineString::initLatin1(size_t length)
|
||||
{
|
||||
JS_ASSERT(latin1LengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_INLINE_FLAGS | LATIN1_CHARS_BIT;
|
||||
return d.inlineStorageLatin1;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSFatInlineString::init(size_t length)
|
||||
JSFatInlineString::initTwoByte(size_t length)
|
||||
{
|
||||
JS_ASSERT(twoByteLengthFits(length));
|
||||
d.u1.length = length;
|
||||
|
@ -265,6 +276,15 @@ JSFatInlineString::init(size_t length)
|
|||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JS::Latin1Char *
|
||||
JSFatInlineString::initLatin1(size_t length)
|
||||
{
|
||||
JS_ASSERT(latin1LengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT;
|
||||
return d.inlineStorageLatin1;
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
MOZ_ALWAYS_INLINE JSFatInlineString *
|
||||
JSFatInlineString::new_(js::ThreadSafeContext *cx)
|
||||
|
@ -339,7 +359,7 @@ JSFlatString::finalize(js::FreeOp *fop)
|
|||
JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -348,7 +368,7 @@ JSFatInlineString::finalize(js::FreeOp *fop)
|
|||
JS_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -358,7 +378,7 @@ JSAtom::finalize(js::FreeOp *fop)
|
|||
JS_ASSERT(JSString::isFlat());
|
||||
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
fop->free_(nonInlineCharsRaw());
|
||||
}
|
||||
|
||||
inline void
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
|
||||
|
@ -18,6 +19,7 @@
|
|||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::IsSame;
|
||||
using mozilla::PodCopy;
|
||||
using mozilla::RangedPtr;
|
||||
using mozilla::RoundUpPow2;
|
||||
|
@ -137,8 +139,9 @@ JSLinearString::debugUnsafeConvertToLatin1()
|
|||
d.u1.flags |= LATIN1_CHARS_BIT;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
AllocChars(ThreadSafeContext *maybecx, size_t length, jschar **chars, size_t *capacity)
|
||||
AllocChars(ThreadSafeContext *maybecx, size_t length, CharT **chars, size_t *capacity)
|
||||
{
|
||||
/*
|
||||
* String length doesn't include the null char, so include it here before
|
||||
|
@ -158,9 +161,9 @@ AllocChars(ThreadSafeContext *maybecx, size_t length, jschar **chars, size_t *ca
|
|||
/* Like length, capacity does not include the null char, so take it out. */
|
||||
*capacity = numChars - 1;
|
||||
|
||||
JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
|
||||
size_t bytes = numChars * sizeof(jschar);
|
||||
*chars = (jschar *)(maybecx ? maybecx->malloc_(bytes) : js_malloc(bytes));
|
||||
JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(CharT) < UINT32_MAX);
|
||||
size_t bytes = numChars * sizeof(CharT);
|
||||
*chars = (CharT *)(maybecx ? maybecx->malloc_(bytes) : js_malloc(bytes));
|
||||
return *chars != nullptr;
|
||||
}
|
||||
|
||||
|
@ -220,7 +223,30 @@ JSRope::copyNonPureCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<jschar>
|
|||
return true;
|
||||
}
|
||||
|
||||
template<JSRope::UsingBarrier b>
|
||||
template <typename CharT>
|
||||
static void
|
||||
CopyChars(CharT *dest, const JSLinearString &str);
|
||||
|
||||
template <>
|
||||
void
|
||||
CopyChars(jschar *dest, const JSLinearString &str)
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
if (str.hasTwoByteChars())
|
||||
PodCopy(dest, str.twoByteChars(nogc), str.length());
|
||||
else
|
||||
CopyAndInflateChars(dest, str.latin1Chars(nogc), str.length());
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
CopyChars(Latin1Char *dest, const JSLinearString &str)
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
PodCopy(dest, str.latin1Chars(nogc), str.length());
|
||||
}
|
||||
|
||||
template<JSRope::UsingBarrier b, typename CharT>
|
||||
JSFlatString *
|
||||
JSRope::flattenInternal(ExclusiveContext *maybecx)
|
||||
{
|
||||
|
@ -257,9 +283,9 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
*/
|
||||
const size_t wholeLength = length();
|
||||
size_t wholeCapacity;
|
||||
jschar *wholeChars;
|
||||
CharT *wholeChars;
|
||||
JSString *str = this;
|
||||
jschar *pos;
|
||||
CharT *pos;
|
||||
|
||||
/*
|
||||
* JSString::flattenData is a tagged pointer to the parent node.
|
||||
|
@ -269,6 +295,8 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
static const uintptr_t Tag_FinishNode = 0x0;
|
||||
static const uintptr_t Tag_VisitRightChild = 0x1;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
|
||||
/* Find the left most string, containing the first string. */
|
||||
JSRope *leftMostRope = this;
|
||||
while (leftMostRope->leftChild()->isRope())
|
||||
|
@ -277,7 +305,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
if (leftMostRope->leftChild()->isExtensible()) {
|
||||
JSExtensibleString &left = leftMostRope->leftChild()->asExtensible();
|
||||
size_t capacity = left.capacity();
|
||||
if (capacity >= wholeLength) {
|
||||
if (capacity >= wholeLength && left.hasTwoByteChars() == IsSame<CharT, jschar>::value) {
|
||||
/*
|
||||
* Simulate a left-most traversal from the root to leftMost->leftChild()
|
||||
* via first_visit_node
|
||||
|
@ -298,9 +326,9 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
JSString::writeBarrierPre(str->d.s.u2.left);
|
||||
JSString::writeBarrierPre(str->d.s.u3.right);
|
||||
}
|
||||
str->d.s.u2.nonInlineCharsTwoByte = left.nonInlineChars();
|
||||
str->setNonInlineChars(left.nonInlineChars<CharT>(nogc));
|
||||
wholeCapacity = capacity;
|
||||
wholeChars = const_cast<jschar *>(left.nonInlineChars());
|
||||
wholeChars = const_cast<CharT *>(left.nonInlineChars<CharT>(nogc));
|
||||
pos = wholeChars + left.d.u1.length;
|
||||
JS_STATIC_ASSERT(!(EXTENSIBLE_FLAGS & DEPENDENT_FLAGS));
|
||||
left.d.u1.flags ^= (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS);
|
||||
|
@ -322,7 +350,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
}
|
||||
|
||||
JSString &left = *str->d.s.u2.left;
|
||||
str->d.s.u2.nonInlineCharsTwoByte = pos;
|
||||
str->setNonInlineChars(pos);
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
|
||||
if (left.isRope()) {
|
||||
/* Return to this node when 'left' done, then goto visit_right_child. */
|
||||
|
@ -330,9 +358,8 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
str = &left;
|
||||
goto first_visit_node;
|
||||
}
|
||||
size_t len = left.length();
|
||||
PodCopy(pos, left.asLinear().chars(), len);
|
||||
pos += len;
|
||||
CopyChars(pos, left.asLinear());
|
||||
pos += left.length();
|
||||
}
|
||||
visit_right_child: {
|
||||
JSString &right = *str->d.s.u3.right;
|
||||
|
@ -342,25 +369,30 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
str = &right;
|
||||
goto first_visit_node;
|
||||
}
|
||||
size_t len = right.length();
|
||||
PodCopy(pos, right.asLinear().chars(), len);
|
||||
pos += len;
|
||||
CopyChars(pos, right.asLinear());
|
||||
pos += right.length();
|
||||
}
|
||||
finish_node: {
|
||||
if (str == this) {
|
||||
JS_ASSERT(pos == wholeChars + wholeLength);
|
||||
*pos = '\0';
|
||||
str->d.u1.length = wholeLength;
|
||||
str->d.u1.flags = EXTENSIBLE_FLAGS;
|
||||
str->d.s.u2.nonInlineCharsTwoByte = wholeChars;
|
||||
if (IsSame<CharT, jschar>::value)
|
||||
str->d.u1.flags = EXTENSIBLE_FLAGS;
|
||||
else
|
||||
str->d.u1.flags = EXTENSIBLE_FLAGS | LATIN1_CHARS_BIT;
|
||||
str->setNonInlineChars(wholeChars);
|
||||
str->d.s.u3.capacity = wholeCapacity;
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u3.right);
|
||||
return &this->asFlat();
|
||||
}
|
||||
uintptr_t flattenData = str->d.u1.flattenData;
|
||||
str->d.u1.flags = DEPENDENT_FLAGS;
|
||||
str->d.u1.length = pos - str->d.s.u2.nonInlineCharsTwoByte;
|
||||
if (IsSame<CharT, jschar>::value)
|
||||
str->d.u1.flags = DEPENDENT_FLAGS;
|
||||
else
|
||||
str->d.u1.flags = DEPENDENT_FLAGS | LATIN1_CHARS_BIT;
|
||||
str->d.u1.length = pos - str->asLinear().nonInlineChars<CharT>(nogc);
|
||||
str->d.s.u3.base = (JSLinearString *)this; /* will be true on exit */
|
||||
StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u3.base);
|
||||
str = (JSString *)(flattenData & ~Tag_Mask);
|
||||
|
@ -371,17 +403,23 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
}
|
||||
}
|
||||
|
||||
template<JSRope::UsingBarrier b>
|
||||
JSFlatString *
|
||||
JSRope::flattenInternal(ExclusiveContext *maybecx)
|
||||
{
|
||||
if (hasTwoByteChars())
|
||||
return flattenInternal<b, jschar>(maybecx);
|
||||
return flattenInternal<b, Latin1Char>(maybecx);
|
||||
}
|
||||
|
||||
JSFlatString *
|
||||
JSRope::flatten(ExclusiveContext *maybecx)
|
||||
{
|
||||
#if JSGC_INCREMENTAL
|
||||
#ifdef JSGC_INCREMENTAL
|
||||
if (zone()->needsBarrier())
|
||||
return flattenInternal<WithIncrementalBarrier>(maybecx);
|
||||
else
|
||||
return flattenInternal<NoBarrier>(maybecx);
|
||||
#else
|
||||
return flattenInternal<NoBarrier>(maybecx);
|
||||
#endif
|
||||
return flattenInternal<NoBarrier>(maybecx);
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
|
@ -405,21 +443,39 @@ js::ConcatStrings(ThreadSafeContext *cx,
|
|||
if (!JSString::validateLength(cx, wholeLength))
|
||||
return nullptr;
|
||||
|
||||
if (JSFatInlineString::twoByteLengthFits(wholeLength) && cx->isJSContext()) {
|
||||
bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
|
||||
bool canUseFatInline = isLatin1
|
||||
? JSFatInlineString::latin1LengthFits(wholeLength)
|
||||
: JSFatInlineString::twoByteLengthFits(wholeLength);
|
||||
if (canUseFatInline && cx->isJSContext()) {
|
||||
JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
ScopedThreadSafeStringInspector leftInspector(left);
|
||||
ScopedThreadSafeStringInspector rightInspector(right);
|
||||
if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
|
||||
if (!leftInspector.ensureChars(cx, nogc) || !rightInspector.ensureChars(cx, nogc))
|
||||
return nullptr;
|
||||
|
||||
jschar *buf = str->init(wholeLength);
|
||||
PodCopy(buf, leftInspector.chars(), leftLen);
|
||||
PodCopy(buf + leftLen, rightInspector.chars(), rightLen);
|
||||
if (isLatin1) {
|
||||
Latin1Char *buf = str->initLatin1(wholeLength);
|
||||
PodCopy(buf, leftInspector.latin1Chars(), leftLen);
|
||||
PodCopy(buf + leftLen, rightInspector.latin1Chars(), rightLen);
|
||||
buf[wholeLength] = 0;
|
||||
} else {
|
||||
jschar *buf = str->initTwoByte(wholeLength);
|
||||
if (leftInspector.hasTwoByteChars())
|
||||
PodCopy(buf, leftInspector.twoByteChars(), leftLen);
|
||||
else
|
||||
CopyAndInflateChars(buf, leftInspector.latin1Chars(), leftLen);
|
||||
if (rightInspector.hasTwoByteChars())
|
||||
PodCopy(buf + leftLen, rightInspector.twoByteChars(), rightLen);
|
||||
else
|
||||
CopyAndInflateChars(buf + leftLen, rightInspector.latin1Chars(), rightLen);
|
||||
buf[wholeLength] = 0;
|
||||
}
|
||||
|
||||
buf[wholeLength] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -530,27 +586,35 @@ JSFlatString::isIndexSlow(uint32_t *indexp) const
|
|||
}
|
||||
|
||||
bool
|
||||
ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx)
|
||||
ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx, const AutoCheckCannotGC &nogc)
|
||||
{
|
||||
if (chars_)
|
||||
if (state_ != Uninitialized)
|
||||
return true;
|
||||
|
||||
if (cx->isExclusiveContext()) {
|
||||
JSLinearString *linear = str_->ensureLinear(cx->asExclusiveContext());
|
||||
if (!linear)
|
||||
return false;
|
||||
chars_ = linear->chars();
|
||||
if (linear->hasTwoByteChars()) {
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = linear->twoByteChars(nogc);
|
||||
} else {
|
||||
state_ = Latin1;
|
||||
latin1Chars_ = linear->latin1Chars(nogc);
|
||||
}
|
||||
} else {
|
||||
if (str_->hasPureChars()) {
|
||||
chars_ = str_->pureChars();
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = str_->pureChars();
|
||||
} else {
|
||||
if (!str_->copyNonPureChars(cx, scopedChars_))
|
||||
return false;
|
||||
chars_ = scopedChars_;
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = scopedChars_;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(chars_);
|
||||
MOZ_ASSERT(state_ != Uninitialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "gc/Marking.h"
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class JSDependentString;
|
||||
|
@ -152,12 +153,12 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
union {
|
||||
union {
|
||||
/* JS(Fat)InlineString */
|
||||
char inlineStorageLatin1[NUM_INLINE_CHARS_LATIN1];
|
||||
JS::Latin1Char inlineStorageLatin1[NUM_INLINE_CHARS_LATIN1];
|
||||
jschar inlineStorageTwoByte[NUM_INLINE_CHARS_TWO_BYTE];
|
||||
};
|
||||
struct {
|
||||
union {
|
||||
const char *nonInlineCharsLatin1; /* JSLinearString, except JS(Fat)InlineString */
|
||||
const JS::Latin1Char *nonInlineCharsLatin1; /* JSLinearString, except JS(Fat)InlineString */
|
||||
const jschar *nonInlineCharsTwoByte;/* JSLinearString, except JS(Fat)InlineString */
|
||||
JSString *left; /* JSRope */
|
||||
} u2;
|
||||
|
@ -296,6 +297,11 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
/* Avoid lame compile errors in JSRope::flatten */
|
||||
friend class JSRope;
|
||||
|
||||
protected:
|
||||
template <typename CharT>
|
||||
MOZ_ALWAYS_INLINE
|
||||
void setNonInlineChars(const CharT *chars);
|
||||
|
||||
public:
|
||||
/* All strings have length. */
|
||||
|
||||
|
@ -531,6 +537,10 @@ class JSRope : public JSString
|
|||
bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
|
||||
|
||||
enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
|
||||
|
||||
template<UsingBarrier b, typename CharT>
|
||||
JSFlatString *flattenInternal(js::ExclusiveContext *cx);
|
||||
|
||||
template<UsingBarrier b>
|
||||
JSFlatString *flattenInternal(js::ExclusiveContext *cx);
|
||||
|
||||
|
@ -577,6 +587,17 @@ class JSLinearString : public JSString
|
|||
bool isLinear() const MOZ_DELETE;
|
||||
JSLinearString &asLinear() const MOZ_DELETE;
|
||||
|
||||
protected:
|
||||
/* Returns void pointer to latin1/twoByte chars, for finalizers. */
|
||||
MOZ_ALWAYS_INLINE
|
||||
void *nonInlineCharsRaw() const {
|
||||
JS_ASSERT(!isInline());
|
||||
static_assert(offsetof(JSLinearString, d.s.u2.nonInlineCharsTwoByte) ==
|
||||
offsetof(JSLinearString, d.s.u2.nonInlineCharsLatin1),
|
||||
"nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
|
||||
return (void *)d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *nonInlineChars() const {
|
||||
|
@ -585,14 +606,45 @@ class JSLinearString : public JSString
|
|||
return d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
MOZ_ALWAYS_INLINE
|
||||
const CharT *nonInlineChars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const JS::Latin1Char *nonInlineLatin1Chars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(!isInline());
|
||||
JS_ASSERT(hasLatin1Chars());
|
||||
return d.s.u2.nonInlineCharsLatin1;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *nonInlineTwoByteChars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(!isInline());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
return d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *chars() const;
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
|
||||
JS::TwoByteChars range() const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return JS::TwoByteChars(chars(), length());
|
||||
}
|
||||
|
||||
jschar latin1OrTwoByteChar(size_t index) const {
|
||||
MOZ_ASSERT(JSString::isLinear());
|
||||
MOZ_ASSERT(index < length());
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index];
|
||||
}
|
||||
|
||||
/* Temporary, unsafe helper function for bug 998392. Don't use for anything else. */
|
||||
void debugUnsafeConvertToLatin1();
|
||||
};
|
||||
|
@ -710,22 +762,34 @@ class JSInlineString : public JSFlatString
|
|||
static const size_t MAX_LENGTH_LATIN1 = NUM_INLINE_CHARS_LATIN1 - 1;
|
||||
static const size_t MAX_LENGTH_TWO_BYTE = NUM_INLINE_CHARS_TWO_BYTE - 1;
|
||||
|
||||
/* Hide chars(), inlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
public:
|
||||
template <js::AllowGC allowGC>
|
||||
static inline JSInlineString *new_(js::ThreadSafeContext *cx);
|
||||
|
||||
inline jschar *init(size_t length);
|
||||
inline jschar *initTwoByte(size_t length);
|
||||
inline JS::Latin1Char *initLatin1(size_t length);
|
||||
|
||||
inline void resetLength(size_t length);
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *inlineChars() const {
|
||||
const jschar *chars() const {
|
||||
JS_ASSERT(JSString::isInline());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
const char *p = reinterpret_cast<const char *>(this);
|
||||
return reinterpret_cast<const jschar *>(p + offsetOfInlineStorage());
|
||||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(JSString::isInline());
|
||||
JS_ASSERT(hasLatin1Chars());
|
||||
return d.inlineStorageLatin1;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(JSString::isInline());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
return d.inlineStorageTwoByte;
|
||||
}
|
||||
|
||||
static bool latin1LengthFits(size_t length) {
|
||||
|
@ -791,7 +855,8 @@ class JSFatInlineString : public JSInlineString
|
|||
INLINE_EXTENSION_CHARS_TWO_BYTE
|
||||
-1 /* null terminator */;
|
||||
|
||||
inline jschar *init(size_t length);
|
||||
inline jschar *initTwoByte(size_t length);
|
||||
inline JS::Latin1Char *initLatin1(size_t length);
|
||||
|
||||
static bool latin1LengthFits(size_t length) {
|
||||
return length <= MAX_LENGTH_LATIN1;
|
||||
|
@ -893,24 +958,36 @@ class ScopedThreadSafeStringInspector
|
|||
private:
|
||||
JSString *str_;
|
||||
ScopedJSFreePtr<jschar> scopedChars_;
|
||||
const jschar *chars_;
|
||||
union {
|
||||
const jschar *twoByteChars_;
|
||||
const JS::Latin1Char *latin1Chars_;
|
||||
};
|
||||
enum State { Uninitialized, Latin1, TwoByte };
|
||||
State state_;
|
||||
|
||||
public:
|
||||
explicit ScopedThreadSafeStringInspector(JSString *str)
|
||||
: str_(str),
|
||||
chars_(nullptr)
|
||||
state_(Uninitialized)
|
||||
{ }
|
||||
|
||||
bool ensureChars(ThreadSafeContext *cx);
|
||||
bool ensureChars(ThreadSafeContext *cx, const JS::AutoCheckCannotGC &nogc);
|
||||
|
||||
const jschar *chars() {
|
||||
JS_ASSERT(chars_);
|
||||
return chars_;
|
||||
bool hasTwoByteChars() const { return state_ == TwoByte; }
|
||||
bool hasLatin1Chars() const { return state_ == Latin1; }
|
||||
|
||||
const jschar *twoByteChars() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return twoByteChars_;
|
||||
}
|
||||
const JS::Latin1Char *latin1Chars() const {
|
||||
MOZ_ASSERT(state_ == Latin1);
|
||||
return latin1Chars_;
|
||||
}
|
||||
|
||||
JS::TwoByteChars range() {
|
||||
JS_ASSERT(chars_);
|
||||
return JS::TwoByteChars(chars_, str_->length());
|
||||
JS::TwoByteChars twoByteRange() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return JS::TwoByteChars(twoByteChars_, str_->length());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1088,23 +1165,23 @@ JSString::getChar(js::ExclusiveContext *cx, size_t index, jschar *code)
|
|||
* test.charCodeAt(x + 1)
|
||||
* }
|
||||
*/
|
||||
const jschar *chars;
|
||||
JSString *str;
|
||||
if (isRope()) {
|
||||
JSRope *rope = &asRope();
|
||||
if (uint32_t(index) < rope->leftChild()->length()) {
|
||||
chars = rope->leftChild()->getChars(cx);
|
||||
str = rope->leftChild();
|
||||
} else {
|
||||
chars = rope->rightChild()->getChars(cx);
|
||||
str = rope->rightChild();
|
||||
index -= rope->leftChild()->length();
|
||||
}
|
||||
} else {
|
||||
chars = getChars(cx);
|
||||
str = this;
|
||||
}
|
||||
|
||||
if (!chars)
|
||||
if (!str->ensureLinear(cx))
|
||||
return false;
|
||||
|
||||
*code = chars[index];
|
||||
*code = str->asLinear().latin1OrTwoByteChar(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1172,12 +1249,56 @@ JSString::base() const
|
|||
return d.s.u3.base;
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
return nonInlineTwoByteChars(nogc);
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_ALWAYS_INLINE const JS::Latin1Char *
|
||||
JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
return nonInlineLatin1Chars(nogc);
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_ALWAYS_INLINE void
|
||||
JSString::setNonInlineChars(const jschar *chars)
|
||||
{
|
||||
d.s.u2.nonInlineCharsTwoByte = chars;
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_ALWAYS_INLINE void
|
||||
JSString::setNonInlineChars(const JS::Latin1Char *chars)
|
||||
{
|
||||
d.s.u2.nonInlineCharsLatin1 = chars;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::chars() const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
return isInline() ? asInline().inlineChars() : nonInlineChars();
|
||||
return isInline() ? asInline().chars() : nonInlineChars();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const JS::Latin1Char *
|
||||
JSLinearString::latin1Chars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
JS_ASSERT(hasLatin1Chars());
|
||||
return isInline() ? asInline().latin1Chars(nogc) : nonInlineLatin1Chars(nogc);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::twoByteChars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
return isInline() ? asInline().twoByteChars(nogc) : nonInlineTwoByteChars(nogc);
|
||||
}
|
||||
|
||||
inline js::PropertyName *
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче