Merge m-c to f-t

This commit is contained in:
Phil Ringnalda 2014-05-31 20:39:49 -07:00
commit 4512a7cdf3
170 changed files with 5127 additions and 1294 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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]

View File

@ -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>

View File

@ -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"]

View File

@ -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]

View File

@ -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();
}

View File

@ -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>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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) \

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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__

View File

@ -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',

View File

@ -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")

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<body>redirect</body>

View File

@ -0,0 +1,2 @@
HTTP 301 Moved Permanently
Location: file_iframe_sandbox_redirect_target.html

View File

@ -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>

View File

@ -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]

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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>

View File

@ -31,6 +31,7 @@ XPIDL_SOURCES += [
'nsIFrameRequestCallback.idl',
'nsIIdleObserver.idl',
'nsIQueryContentEventResult.idl',
'nsIServiceWorkerManager.idl',
'nsIStructuredCloneContainer.idl',
'nsITabChild.idl',
'nsITabParent.idl',

View File

@ -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"
%}

View File

@ -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;

View File

@ -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")) {

View File

@ -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.

View File

@ -982,7 +982,7 @@ ParticularProcessPriorityManager::ComputePriority()
}
if (isVisible) {
return HasAppType("keyboard") ?
return HasAppType("inputmethod") ?
PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
PROCESS_PRIORITY_FOREGROUND;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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>

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -1 +1,2 @@
[test_installation_simple.html]
[test_navigator.html]

View File

@ -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>

View File

@ -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;
}

View File

@ -308,18 +308,29 @@ CompositorOGL::Initialize()
0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 2.0f,
1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, 2.0f,
1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, 2.0f,
1.0f, 1.0f, 0.0f, 2.0f,
0.0f, 0.0f, 0.0f, 3.0f,
1.0f, 0.0f, 0.0f, 3.0f,
0.0f, 1.0f, 0.0f, 3.0f,
1.0f, 0.0f, 0.0f, 3.0f,
0.0f, 1.0f, 0.0f, 3.0f,
1.0f, 1.0f, 0.0f, 3.0f,
};
HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
@ -1525,7 +1536,9 @@ CompositorOGL::BindAndDrawQuads(ShaderProgramOGL *aProg,
aProg->SetTextureRects(aTextureRects);
}
mGLContext->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4 * aQuads);
// We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
// process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
}
GLuint

View File

@ -65,7 +65,7 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
}
}
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
if (!ok) {
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}

View File

@ -3972,10 +3972,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) {

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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 =

View File

@ -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(),

View File

@ -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()
{

View File

@ -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

View File

@ -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');

View File

@ -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)

View File

@ -100,7 +100,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(uint32_t flag) volatile {
@ -125,7 +125,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.

View File

@ -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;

View File

@ -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 */

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -137,7 +137,7 @@ class gcstats::StatisticsSerializer
return nullptr;
}
InflateStringToBuffer(buf, nchars, out);
CopyAndInflateChars(out, buf, nchars);
js_free(buf);
out[nchars] = 0;

View File

@ -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);

View File

@ -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();

View File

@ -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),

View File

@ -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(&notInline);
}
masm.bind(&isLatin1);
{
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
&isFatInlineLatin1);
}
masm.bind(&notInline);
// 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;

View File

@ -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)
{

View File

@ -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());

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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];
};
};

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -1047,7 +1047,7 @@ HelperThread::handleGCHelperWorkload()
void
HelperThread::threadLoop()
{
JS::AutoAssertNoGC nogc;
JS::AutoSuppressGCAnalysis nogc;
AutoLockHelperThreadState lock;
js::TlsPerThreadData.set(threadData.addr());

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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 &regs)
{
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 &regs)
{
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);
}

View File

@ -143,7 +143,7 @@ SPSProfiler::markEvent(const char *event)
{
JS_ASSERT(enabled());
if (eventMarker_) {
JS::AutoAssertNoGC nogc;
JS::AutoSuppressGCAnalysis nogc;
eventMarker_(event);
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;
}

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