merge mozilla-inbound to mozilla-central

This commit is contained in:
Carsten "Tomcat" Book 2014-01-23 10:18:03 +01:00
commit 46cde20331
121 changed files with 3734 additions and 1334 deletions

View File

@ -331,18 +331,16 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
break;
}
// This offset no longer applies because the passed-in text object is not a child
// of the hypertext. This happens when there are nested hypertexts, e.g.
// <div>abc<h1>def</h1>ghi</div>
// If the passed-in DOM point was not on a direct child of the hypertext, we will
// return the offset for that entire hypertext
if (aIsEndOffset) {
// Not inclusive, the indicated char comes at index before this offset
// If the end offset is after the first character of the passed in object, use 1 for
// addTextOffset, to put us after the embedded object char. We'll only treat the offset as
// before the embedded object char if we end at the very beginning of the child.
addTextOffset = addTextOffset > 0;
} else
// This offset no longer applies because the passed-in text object is not
// a child of the hypertext. This happens when there are nested hypertexts,
// e.g. <div>abc<h1>def</h1>ghi</div>. Thus we need to adjust the offset
// to make it relative the hypertext.
// If the end offset is not supposed to be inclusive and the original point
// is not at 0 offset then the returned offset should be after an embedded
// character the original point belongs to.
if (aIsEndOffset)
addTextOffset = (addTextOffset > 0 || descendantAcc->IndexInParent() > 0) ? 1 : 0;
else
addTextOffset = 0;
descendantAcc = parentAcc;

View File

@ -120,6 +120,12 @@
testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
[ [ 0, 5, kDiscBulletChar + "Item", 0, 5 ] ]);
//////////////////////////////////////////////////////////////////////////
// Nested hypertexts
testTextAtOffset(["ht_5" ], BOUNDARY_LINE_START,
[ [ 0, 0, kEmbedChar, 0, 1 ] ]);
SimpleTest.finish();
}
@ -190,5 +196,12 @@ two words
<ul>
<li id="li1">Item</li>
</ul>
<div id="ht_5">
<div>
<p>sectiounus</p>
<p>seciofarus</p>
</div>
</div>
</body>
</html>

View File

@ -28,6 +28,9 @@ Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
Cu.import('resource://gre/modules/DownloadsAPI.jsm');
Cu.import('resource://gre/modules/Webapps.jsm');
DOMApplicationRegistry.allAppsLaunchable = true;
XPCOMUtils.defineLazyServiceGetter(Services, 'env',
'@mozilla.org/process/environment;1',
'nsIEnvironment');
@ -268,7 +271,6 @@ var shell = {
alert(msg);
return;
}
let manifestURL = this.manifestURL;
// <html:iframe id="systemapp"
// mozbrowser="true" allowfullscreen="true"
@ -613,9 +615,6 @@ var shell = {
this.reportCrash(true);
Cu.import('resource://gre/modules/Webapps.jsm');
DOMApplicationRegistry.allAppsLaunchable = true;
this.sendEvent(window, 'ContentStart');
Services.obs.notifyObservers(null, 'content-start', null);
@ -668,13 +667,12 @@ Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data)
fullscreenorigin: data });
}, "fullscreen-origin-change", false);
Services.obs.addObserver(function onWebappsStart(subject, topic, data) {
DOMApplicationRegistry.registryStarted.then(function () {
shell.sendChromeEvent({ type: 'webapps-registry-start' });
}, 'webapps-registry-start', false);
Services.obs.addObserver(function onWebappsReady(subject, topic, data) {
});
DOMApplicationRegistry.registryReady.then(function () {
shell.sendChromeEvent({ type: 'webapps-registry-ready' });
}, 'webapps-registry-ready', false);
});
Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
shell.sendChromeEvent({

View File

@ -54,6 +54,18 @@
<key>weight</key>
<real>10</real>
</dict>
<key>^Updated.app/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^updating/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
</dict>
</dict>
</plist>

View File

@ -3448,6 +3448,21 @@ var XULBrowserWindow = {
LinkTargetDisplay.update();
},
showTooltip: function (x, y, tooltip) {
// The x,y coordinates are relative to the <browser> element using
// the chrome zoom level.
let elt = document.getElementById("remoteBrowserTooltip");
elt.label = tooltip;
let anchor = gBrowser.selectedBrowser;
elt.openPopupAtScreen(anchor.boxObject.screenX + x, anchor.boxObject.screenY + y, false, null);
},
hideTooltip: function () {
let elt = document.getElementById("remoteBrowserTooltip");
elt.hidePopup();
},
updateStatusField: function () {
var text, type, types = ["overLink"];
if (this._busyUI)

View File

@ -129,6 +129,7 @@
oncommand="gotoHistoryIndex(event); event.stopPropagation();"
onclick="checkForMiddleClick(this, event);"/>
<tooltip id="aHTMLTooltip" page="true"/>
<tooltip id="remoteBrowserTooltip"/>
<!-- for search and content formfill/pw manager -->
<panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>

View File

@ -6,7 +6,6 @@
const {classes: Cc, interfaces: Ci, manager: Cm, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
const PRIVATE_PREF_PREFIX = "capability."; // Tag to prevent exposing private preferences
const INITIAL_PAGE_DELAY = 500; // Initial pause on program start for scroll alignment
const PREFS_BUFFER_MAX = 100; // Max prefs buffer size for getPrefsBuffer()
const PAGE_SCROLL_TRIGGER = 200; // Triggers additional getPrefsBuffer() on user scroll-to-bottom
@ -86,12 +85,6 @@ var NewPrefDialog = {
return;
}
// Prevent addition of new "private" preferences
if (aPrefName.startsWith(PRIVATE_PREF_PREFIX)) {
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.privateButton");
return;
}
// If item already in list, it's being changed, else added
let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
if (item) {
@ -206,10 +199,7 @@ var AboutConfig = {
this._prefsContainer = document.getElementById("prefs-container");
this._loadingContainer = document.getElementById("loading-container");
let list = Services.prefs.getChildList("", {}).filter(function(aElement) {
// Prevent display of "private" preferences
return !aElement.startsWith(PRIVATE_PREF_PREFIX);
});
let list = Services.prefs.getChildList("");
this._list = list.sort().map( function AC_getMapPref(aPref) {
return new Pref(aPref);
}, this);
@ -461,8 +451,8 @@ var AboutConfig = {
observe: function AC_observe(aSubject, aTopic, aPrefName) {
let pref = new Pref(aPrefName);
// Ignore uninteresting preference changes, and external changes to "private" preferences
if ((aTopic != "nsPref:changed") || pref.name.startsWith(PRIVATE_PREF_PREFIX)) {
// Ignore uninteresting changes, and avoid "private" preferences
if (aTopic != "nsPref:changed") {
return;
}

View File

@ -634,50 +634,8 @@ class Automation(object):
return
self.haveDumpedScreen = True;
automationutils.dumpScreen(utilityPath)
# Need to figure out what tool and whether it write to a file or stdout
if self.UNIXISH:
utility = [os.path.join(utilityPath, "screentopng")]
imgoutput = 'stdout'
elif self.IS_MAC:
utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
imgoutput = 'file'
elif self.IS_WIN32:
utility = [os.path.join(utilityPath, "screenshot.exe")]
imgoutput = 'file'
# Run the capture correctly for the type of capture
try:
if imgoutput == 'file':
tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
os.close(tmpfd)
dumper = self.Process(utility + [imgfilename])
elif imgoutput == 'stdout':
dumper = self.Process(utility, bufsize=-1,
stdout=subprocess.PIPE, close_fds=True)
except OSError, err:
self.log.info("Failed to start %s for screenshot: %s",
utility[0], err.strerror)
return
# Check whether the capture utility ran successfully
dumper_out, dumper_err = dumper.communicate()
if dumper.returncode != 0:
self.log.info("%s exited with code %d", utility, dumper.returncode)
return
try:
if imgoutput == 'stdout':
image = dumper_out
elif imgoutput == 'file':
with open(imgfilename, 'rb') as imgfile:
image = imgfile.read()
except IOError, err:
self.log.info("Failed to read image from %s", imgoutput)
import base64
encoded = base64.b64encode(image)
self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
def killAndGetStack(self, processPID, utilityPath, debuggerInfo):
"""Kill the process, preferrably in a way that gets us a stack trace.

View File

@ -484,58 +484,40 @@ def environment(xrePath, env=None, crashreporter=True, debugger=False, dmdPath=N
return env
def dumpScreen(utilityPath):
"""dumps the screen to the log file as a data URI"""
"""dumps a screenshot of the entire screen to a directory specified by
the MOZ_UPLOAD_DIR environment variable"""
import mozfile
# Need to figure out what tool and whether it write to a file or stdout
# Need to figure out which OS-dependent tool to use
if mozinfo.isUnix:
utility = [os.path.join(utilityPath, "screentopng")]
imgoutput = 'stdout'
elif mozinfo.isMac:
utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
imgoutput = 'file'
elif mozinfo.isWin:
utility = [os.path.join(utilityPath, "screenshot.exe")]
imgoutput = 'file'
else:
log.warn("Unable to dump screen on platform '%s'", sys.platform)
# Run the capture correctly for the type of capture
kwargs = {'stdout': subprocess.PIPE}
if imgoutput == 'file':
tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
os.close(tmpfd)
utility.append(imgfilename)
elif imgoutput == 'stdout':
kwargs.update(dict(bufsize=-1, close_fds=True))
# Get dir where to write the screenshot file
parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
if not parent_dir:
log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
return
# Run the capture
try:
dumper = subprocess.Popen(utility, **kwargs)
with mozfile.NamedTemporaryFile(suffix='.png',
prefix='mozilla-test-fail-screenshot_',
dir=parent_dir,
delete=False) as f:
returncode = subprocess.call(utility + [f.name])
except OSError, err:
log.info("Failed to start %s for screenshot: %s",
utility[0], err.strerror)
return
# Check whether the capture utility ran successfully
stdout, _ = dumper.communicate()
returncode = dumper.poll()
if returncode:
if returncode != 0:
log.info("%s exited with code %d", utility, returncode)
return
try:
if imgoutput == 'stdout':
image = stdout
elif imgoutput == 'file':
with open(imgfilename, 'rb') as imgfile:
image = imgfile.read()
except IOError, err:
log.info("Failed to read image from %s", imgoutput)
encoded = base64.b64encode(image)
uri = "data:image/png;base64,%s" % encoded
log.info("SCREENSHOT: %s", uri)
return uri
class ShutdownLeaks(object):
"""

View File

@ -59,6 +59,12 @@ bits
Optional.
buildapp
The path to the XUL application being built.
For desktop Firefox, this is ``browser``. For Fennec, it's
``mobile/android``. For B2G, it's ``b2g``.
crashreporter
Whether the crash reporter is enabled for this build.

View File

@ -306,7 +306,7 @@ CSPRep.fromString = function(aStr, self, reportOnly, docRequest, csp) {
dir = dir.trim();
if (dir.length < 1) continue;
var dirname = dir.split(/\s+/)[0];
var dirname = dir.split(/\s+/)[0].toLowerCase();
var dirvalue = dir.substring(dirname.length).trim();
if (aCSPR._directives.hasOwnProperty(dirname)) {
@ -564,7 +564,7 @@ CSPRep.fromStringSpecCompliant = function(aStr, self, reportOnly, docRequest, cs
dir = dir.trim();
if (dir.length < 1) continue;
var dirname = dir.split(/\s+/)[0];
var dirname = dir.split(/\s+/)[0].toLowerCase();
var dirvalue = dir.substring(dirname.length).trim();
dirs[dirname] = dirvalue;
}
@ -1003,7 +1003,7 @@ CSPSourceList.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
slObj._CSPRep = aCSPRep;
aStr = aStr.trim();
// w3 specifies case insensitive equality
if (aStr.toUpperCase() === "'NONE'") {
if (aStr.toLowerCase() === "'none'") {
slObj._permitAllSources = false;
return slObj;
}
@ -1071,7 +1071,7 @@ CSPSourceList.prototype = {
// sort both arrays and compare like a zipper
// XXX (sid): I think we can make this more efficient
var sortfn = function(a,b) {
return a.toString() > b.toString();
return a.toString.toLowerCase() > b.toString.toLowerCase();
};
var a_sorted = this._sources.sort(sortfn);
var b_sorted = that._sources.sort(sortfn);
@ -1418,7 +1418,7 @@ CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
return CSPHashSource.fromString(aStr, aCSPRep);
// check for 'self' (case insensitive)
if (aStr.toUpperCase() === "'SELF'") {
if (aStr.toLowerCase() === "'self'") {
if (!self) {
cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData"));
return null;
@ -1429,13 +1429,13 @@ CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
}
// check for 'unsafe-inline' (case insensitive)
if (aStr.toUpperCase() === "'UNSAFE-INLINE'"){
if (aStr.toLowerCase() === "'unsafe-inline'"){
sObj._allowUnsafeInline = true;
return sObj;
}
// check for 'unsafe-eval' (case insensitive)
if (aStr.toUpperCase() === "'UNSAFE-EVAL'"){
if (aStr.toLowerCase() === "'unsafe-eval'"){
sObj._allowUnsafeEval = true;
return sObj;
}
@ -1556,7 +1556,7 @@ CSPSource.prototype = {
aSource = CSPSource.create(aSource, this._CSPRep);
// verify scheme
if (this.scheme != aSource.scheme)
if (this.scheme.toLowerCase() != aSource.scheme.toLowerCase())
return false;
// port is defined in 'this' (undefined means it may not be relevant
@ -1592,14 +1592,14 @@ CSPSource.prototype = {
// 2. ports match
// 3. either both hosts are undefined, or one equals the other.
if (resolveSelf)
return this.scheme === that.scheme
&& this.port === that.port
return this.scheme.toLowerCase() === that.scheme.toLowerCase()
&& this.port === that.port
&& (!(this.host || that.host) ||
(this.host && this.host.equals(that.host)));
// otherwise, compare raw (non-self-resolved values)
return this._scheme === that._scheme
&& this._port === that._port
return this._scheme.toLowerCase() === that._scheme.toLowerCase()
&& this._port === that._port
&& (!(this._host || that._host) ||
(this._host && this._host.equals(that._host)));
},
@ -1688,7 +1688,9 @@ CSPHost.prototype = {
*/
permits:
function(aHost) {
if (!aHost) aHost = CSPHost.fromString("*");
if (!aHost) {
aHost = CSPHost.fromString("*");
}
if (!(aHost instanceof CSPHost)) {
// -- compare CSPHost to String
@ -1714,7 +1716,8 @@ CSPHost.prototype = {
// * Compare from right to left.
for (var i=1; i <= thislen; i++) {
if (this._segments[thislen-i] != "*" &&
(this._segments[thislen-i] != aHost._segments[thatlen-i])) {
(this._segments[thislen-i].toLowerCase() !=
aHost._segments[thatlen-i].toLowerCase())) {
return false;
}
}
@ -1737,8 +1740,10 @@ CSPHost.prototype = {
return false;
for (var i=0; i<this._segments.length; i++) {
if (this._segments[i] != that._segments[i])
if (this._segments[i].toLowerCase() !=
that._segments[i].toLowerCase()) {
return false;
}
}
return true;
}

View File

@ -916,6 +916,66 @@ test(
});
test(function test_equals_does_case_insensitive_comparison() {
// NOTE: For scheme, host and keyword-host:
// (1) compare the same lower-case in two distinct objects
// (2) compare upper-case with lower-case inputs
// to test case insensitivity.
// CSPSource equals ignores case
var upperCaseHost = "http://FOO.COM";
var lowerCaseHost = "http://foo.com";
src1 = CSPSource.fromString(lowerCaseHost);
src2 = CSPSource.fromString(lowerCaseHost);
do_check_true(src1.equals(src2))
src3 = CSPSource.fromString(upperCaseHost);
do_check_true(src1.equals(src3))
// CSPHost equals ignores case
var upperCaseScheme = "HTTP";
var lowerCaseScheme = "http";
src1 = CSPHost.fromString(lowerCaseScheme);
src2 = CSPHost.fromString(lowerCaseScheme);
do_check_true(src1.equals(src2));
src3 = CSPHost.fromString(upperCaseScheme);
do_check_true(src1.equals(src3));
// CSPSourceList equals (mainly for testing keywords)
var upperCaseKeywords = "'SELF'";
var lowerCaseKeywords = "'self'";
src1 = CSPSourceList.fromString(lowerCaseKeywords);
src2 = CSPSourceList.fromString(lowerCaseKeywords);
do_check_true(src1.equals(src2))
src3 = CSPSourceList.fromString(upperCaseKeywords);
do_check_true(src1.equals(src3))
});
test(function test_csp_permits_case_insensitive() {
var cspr;
var SD = CSPRep.SRC_DIRECTIVES_NEW;
// checks directives can be case-insensitive
var selfHost = "http://self.com";
var testPolicy1 = "DEFAULT-src 'self';";
cspr = CSPRep.fromString(testPolicy1, URI(selfHost));
do_check_true(cspr.permits(URI("http://self.com"), SD.DEFAULT_SRC));
// checks hosts can be case-insensitive
var testPolicy2 = "default-src 'self' http://FOO.COM";
cspr = CSPRep.fromString(testPolicy2, URI(selfHost));
do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
// checks schemes can be case-insensitive
var testPolicy3 = "default-src 'self' HTTP://foo.com";
cspr = CSPRep.fromString(testPolicy3, URI(selfHost));
do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
// checks keywords can be case-insensitive
var testPolicy4 = "default-src 'NONE'";
cspr = CSPRep.fromString(testPolicy4, URI(selfHost));
do_check_false(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
});
/*
test(function test_CSPRep_fromPolicyURI_failswhenmixed() {

View File

@ -39,10 +39,10 @@
class nsIDocShell;
/*
/*
* Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
* https://bugzilla.mozilla.org/show_bug.cgi?id=686732
*
*
* Exceptions: some of the following values are set to higher values than in the spec because
* the values in the spec are ridiculously low. They are explicitly marked below
*/
@ -230,7 +230,8 @@ public:
// all context resources to be lost.
uint32_t Generation() { return mGeneration.value(); }
const WebGLRectangleObject *FramebufferRectangleObject() const;
// Returns null if the current bound FB is not likely complete.
const WebGLRectangleObject* CurValidFBRectObject() const;
static const size_t sMaxColorAttachments = 16;
@ -442,7 +443,7 @@ public:
void TexParameteri(GLenum target, GLenum pname, GLint param) {
TexParameter_base(target, pname, &param, nullptr);
}
void TexSubImage2D(GLenum target, GLint level,
GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format,
@ -476,7 +477,7 @@ public:
data->Stride(), format, type,
data->GetData(), byteLength,
-1, srcFormat, mPixelStorePremultiplyAlpha);
}
void Uniform1i(WebGLUniformLocation* location, GLint x);
@ -492,7 +493,7 @@ public:
GLfloat z);
void Uniform4f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
GLfloat z, GLfloat w);
void Uniform1iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
Uniform1iv_base(location, arr.Length(), arr.Data());
@ -525,7 +526,7 @@ public:
}
void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
const GLint* data);
void Uniform4iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
Uniform4iv_base(location, arr.Length(), arr.Data());
@ -569,7 +570,7 @@ public:
}
void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
const GLfloat* data);
void Uniform4fv(WebGLUniformLocation* location,
const dom::Float32Array& arr) {
Uniform4fv_base(location, arr.Length(), arr.Data());
@ -817,8 +818,8 @@ protected:
void UndoFakeVertexAttrib0();
void InvalidateFakeVertexAttrib0();
static CheckedUint32 GetImageSize(GLsizei height,
GLsizei width,
static CheckedUint32 GetImageSize(GLsizei height,
GLsizei width,
uint32_t pixelSize,
uint32_t alignment);
@ -952,7 +953,7 @@ protected:
bool ValidateDrawModeEnum(GLenum mode, const char *info);
bool ValidateAttribIndex(GLuint index, const char *info);
bool ValidateStencilParamsForDrawCall();
bool ValidateGLSLVariableName(const nsAString& name, const char *info);
bool ValidateGLSLCharacter(char16_t c);
bool ValidateGLSLString(const nsAString& string, const char *info);
@ -1048,7 +1049,7 @@ protected:
int32_t MaxTextureSizeForTarget(GLenum target) const {
return target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize;
}
/** like glBufferData but if the call may change the buffer size, checks any GL error generated
* by this glBufferData call and returns it */
GLenum CheckedBufferData(GLenum target,

View File

@ -53,9 +53,22 @@ using namespace mozilla::gfx;
static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2);
const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject()
: static_cast<const WebGLRectangleObject*>(this);
const WebGLRectangleObject*
WebGLContext::CurValidFBRectObject() const
{
const WebGLRectangleObject* rect = nullptr;
if (mBoundFramebuffer) {
// We don't really need to ask the driver.
// Use 'precheck' to just check that our internal state looks good.
GLenum precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus();
if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
rect = &mBoundFramebuffer->RectangleObject();
} else {
rect = static_cast<const WebGLRectangleObject*>(this);
}
return rect;
}
WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
@ -108,7 +121,7 @@ WebGLContext::FakeBlackTexture::~FakeBlackTexture()
void
WebGLContext::ActiveTexture(GLenum texture)
{
if (IsContextLost())
if (IsContextLost())
return;
if (texture < LOCAL_GL_TEXTURE0 ||
@ -168,7 +181,7 @@ WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location,
NS_LossyConvertUTF16toASCII cname(name);
nsCString mappedName;
prog->MapIdentifier(cname, &mappedName);
MakeContextCurrent();
gl->fBindAttribLocation(progname, location, mappedName.get());
}
@ -347,11 +360,8 @@ GLenum
WebGLContext::CheckFramebufferStatus(GLenum target)
{
if (IsContextLost())
{
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
}
MakeContextCurrent();
if (target != LOCAL_GL_FRAMEBUFFER) {
ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
return 0;
@ -359,42 +369,8 @@ WebGLContext::CheckFramebufferStatus(GLenum target)
if (!mBoundFramebuffer)
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
if(mBoundFramebuffer->HasDepthStencilConflict())
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
bool hasImages = false;
hasImages |= mBoundFramebuffer->DepthAttachment().IsDefined();
hasImages |= mBoundFramebuffer->StencilAttachment().IsDefined();
hasImages |= mBoundFramebuffer->DepthStencilAttachment().IsDefined();
if (!hasImages) {
int32_t colorAttachmentCount = mBoundFramebuffer->mColorAttachments.Length();
for(int32_t i = 0; i < colorAttachmentCount; i++) {
if (mBoundFramebuffer->ColorAttachment(i).IsDefined()) {
hasImages = true;
break;
}
}
/* http://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf section 4.4.5 (page 118)
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
No images are attached to the framebuffer.
*/
if (!hasImages) {
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
}
}
if(mBoundFramebuffer->HasIncompleteAttachment())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
if(mBoundFramebuffer->HasAttachmentsOfMismatchedDimensions())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
// Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
mBoundFramebuffer->FinalizeAttachments();
return gl->fCheckFramebufferStatus(target);
return mBoundFramebuffer->CheckFramebufferStatus();
}
void
@ -409,7 +385,7 @@ WebGLContext::CopyTexSubImage2D_base(GLenum target,
GLsizei height,
bool sub)
{
const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
@ -443,7 +419,7 @@ WebGLContext::CopyTexSubImage2D_base(GLenum target,
if (!ValidateTexFormatAndType(internalformat, LOCAL_GL_UNSIGNED_BYTE, -1, &texelSize, info))
return;
CheckedUint32 checked_neededByteLength =
CheckedUint32 checked_neededByteLength =
GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
if (!checked_neededByteLength.isValid())
@ -914,7 +890,7 @@ WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
"with %d vertices. Try reducing the number of vertices.", vertexCount);
return false;
}
GLuint dataSize = checked_dataSize.value();
if (!mFakeVertexAttrib0BufferObject) {
@ -960,7 +936,7 @@ WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
}
UpdateWebGLErrorAndClearGLError(&error);
gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
// note that we do this error checking and early return AFTER having restored the buffer binding above
@ -973,7 +949,7 @@ WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
return true;
}
@ -1195,14 +1171,14 @@ WebGLContext::GenerateMipmap(GLenum target)
if (!tex)
return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
GLenum imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D
: LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
if (!tex->HasImageInfoAt(imageTarget, 0))
{
return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
}
if (!tex->IsFirstImagePowerOfTwo())
return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
@ -1321,7 +1297,7 @@ WebGLContext::GetAttribLocation(WebGLProgram *prog, const nsAString& name)
return -1;
if (!ValidateGLSLVariableName(name, "getAttribLocation"))
return -1;
return -1;
NS_LossyConvertUTF16toASCII cname(name);
nsCString mappedName;
@ -1630,7 +1606,7 @@ WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsACString& retval)
retval.Truncate();
return;
}
GLuint progname = prog->GLName();
MakeContextCurrent();
@ -2175,7 +2151,7 @@ WebGLContext::LinkProgram(WebGLProgram *program)
for (size_t i = 0; i < program->AttachedShaders().Length(); i++) {
WebGLShader* shader = program->AttachedShaders()[i];
if (shader->CompileStatus())
continue;
@ -2269,7 +2245,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
if (pixels.IsNull())
return ErrorInvalidValue("readPixels: null destination buffer");
const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
@ -2407,7 +2383,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
// no need to check again for integer overflow here, since we already know the sizes aren't greater than before
uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel;
// There are checks above to ensure that this doesn't overflow.
uint32_t subrect_alignedRowSize =
uint32_t subrect_alignedRowSize =
RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value();
uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
@ -2470,7 +2446,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
NS_WARNING("Unhandled case, how'd we get here?");
return rv.Throw(NS_ERROR_FAILURE);
}
}
}
}
}
@ -3126,7 +3102,7 @@ WebGLContext::CompileShader(WebGLShader *shader)
#endif
}
// We're storing an actual instance of StripComments because, if we don't, the
// We're storing an actual instance of StripComments because, if we don't, the
// cleanSource nsAString instance will be destroyed before the reference is
// actually used.
StripComments stripComments(shader->Source());
@ -3141,7 +3117,7 @@ WebGLContext::CompileShader(WebGLShader *shader)
if (gl->WorkAroundDriverBugs()) {
const uint32_t maxSourceLength = 0x3ffff;
if (sourceCString.Length() > maxSourceLength)
return ErrorInvalidValue("compileShader: source has more than %d characters",
return ErrorInvalidValue("compileShader: source has more than %d characters",
maxSourceLength);
}
@ -3658,7 +3634,7 @@ WebGLContext::ShaderSource(WebGLShader *shader, const nsAString& source)
if (!ValidateObject("shaderSource: shader", shader))
return;
// We're storing an actual instance of StripComments because, if we don't, the
// We're storing an actual instance of StripComments because, if we don't, the
// cleanSource nsAString instance will be destroyed before the reference is
// actually used.
StripComments stripComments(source);
@ -3685,7 +3661,7 @@ GLenum WebGLContext::CheckedTexImage2D(GLenum target,
MOZ_ASSERT(tex != nullptr, "no texture bound");
bool sizeMayChange = true;
if (tex->HasImageInfoAt(target, level)) {
const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
sizeMayChange = width != imageInfo.Width() ||
@ -3693,7 +3669,7 @@ GLenum WebGLContext::CheckedTexImage2D(GLenum target,
format != imageInfo.InternalFormat() ||
type != imageInfo.Type();
}
if (sizeMayChange) {
UpdateWebGLErrorAndClearGLError();
gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
@ -3764,7 +3740,7 @@ WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
CheckedUint32 checked_neededByteLength =
CheckedUint32 checked_neededByteLength =
GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
@ -3890,7 +3866,7 @@ WebGLContext::TexImage2D(GLenum target, GLint level,
// Spec says to generate an INVALID_VALUE error
return ErrorInvalidValue("texImage2D: null ImageData");
}
Uint8ClampedArray arr(pixels->GetDataObject());
return TexImage2D_base(target, level, internalformat, pixels->Width(),
pixels->Height(), 4*pixels->Width(), 0,
@ -3948,19 +3924,19 @@ WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
if (width == 0 || height == 0)
return; // ES 2.0 says it has no effect, we better return right now
CheckedUint32 checked_neededByteLength =
CheckedUint32 checked_neededByteLength =
GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
CheckedUint32 checked_alignedRowSize =
CheckedUint32 checked_alignedRowSize =
RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
if (!checked_neededByteLength.isValid())
return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
uint32_t bytesNeeded = checked_neededByteLength.value();
if (byteLength < bytesNeeded)
return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
@ -3971,11 +3947,11 @@ WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
if (!tex->HasImageInfoAt(target, level))
return ErrorInvalidOperation("texSubImage2D: no texture image previously defined for this level and face");
const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.Width(), imageInfo.Height()))
return ErrorInvalidValue("texSubImage2D: subtexture rectangle out of bounds");
// Require the format and type in texSubImage2D to match that of the existing texture as created by texImage2D
if (imageInfo.InternalFormat() != format || imageInfo.Type() != type)
return ErrorInvalidOperation("texSubImage2D: format or type doesn't match the existing texture");
@ -4236,8 +4212,8 @@ InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2)
return LOCAL_GL_DEPTH_COMPONENT16;
else if (type == LOCAL_GL_UNSIGNED_INT)
return LOCAL_GL_DEPTH_COMPONENT32;
}
}
if (format == LOCAL_GL_DEPTH_STENCIL) {
if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
return LOCAL_GL_DEPTH24_STENCIL8;

View File

@ -735,7 +735,7 @@ void WebGLContext::Draw_cleanup()
}
// Let's check the viewport
const WebGLRectangleObject* rect = FramebufferRectangleObject();
const WebGLRectangleObject* rect = CurValidFBRectObject();
if (rect) {
if (mViewportWidth > rect->Width() ||
mViewportHeight > rect->Height())

View File

@ -12,16 +12,18 @@
#include "WebGLTexture.h"
#include "WebGLRenderbuffer.h"
#include "GLContext.h"
#include "WebGLContextUtils.h"
using namespace mozilla;
using namespace mozilla::gl;
JSObject*
WebGLFramebuffer::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) {
WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
{
return dom::WebGLFramebufferBinding::Wrap(cx, scope, this);
}
WebGLFramebuffer::WebGLFramebuffer(WebGLContext *context)
WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context)
: WebGLContextBoundObject(context)
, mHasEverBeenBound(false)
, mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
@ -38,14 +40,16 @@ WebGLFramebuffer::WebGLFramebuffer(WebGLContext *context)
}
bool
WebGLFramebuffer::Attachment::IsDeleteRequested() const {
WebGLFramebuffer::Attachment::IsDeleteRequested() const
{
return Texture() ? Texture()->IsDeleteRequested()
: Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
: false;
}
bool
WebGLFramebuffer::Attachment::HasAlpha() const {
WebGLFramebuffer::Attachment::HasAlpha() const
{
GLenum format = 0;
if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
@ -55,7 +59,8 @@ WebGLFramebuffer::Attachment::HasAlpha() const {
}
void
WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture *tex, GLenum target, GLint level) {
WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level)
{
mTexturePtr = tex;
mRenderbufferPtr = nullptr;
mTexImageTarget = target;
@ -63,50 +68,68 @@ WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture *tex, GLenum target, GLin
}
bool
WebGLFramebuffer::Attachment::HasUninitializedImageData() const {
if (mRenderbufferPtr) {
return mRenderbufferPtr->HasUninitializedImageData();
} else if (mTexturePtr) {
if (!mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
return false;
return mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData();
} else {
WebGLFramebuffer::Attachment::HasUninitializedImageData() const
{
if (!HasImage())
return false;
if (Renderbuffer()) {
return Renderbuffer()->HasUninitializedImageData();
} else if (Texture()) {
MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData();
}
MOZ_ASSERT(false, "Should not get here.");
return false;
}
void
WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus) {
WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus)
{
if (!HasImage())
return;
if (mRenderbufferPtr) {
mRenderbufferPtr->SetImageDataStatus(newStatus);
return;
} else if (mTexturePtr) {
mTexturePtr->SetImageDataStatus(mTexImageTarget, mTexImageLevel, newStatus);
} else {
MOZ_ASSERT(false); // should not get here, worth crashing a debug build.
return;
}
}
const WebGLRectangleObject*
WebGLFramebuffer::Attachment::RectangleObject() const {
if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
return &Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
else if (Renderbuffer())
return Renderbuffer();
else
return nullptr;
MOZ_ASSERT(false, "Should not get here.");
}
bool
WebGLFramebuffer::Attachment::HasSameDimensionsAs(const Attachment& other) const {
const WebGLRectangleObject *thisRect = RectangleObject();
const WebGLRectangleObject *otherRect = other.RectangleObject();
return thisRect &&
otherRect &&
thisRect->HasSameDimensionsAs(*otherRect);
WebGLFramebuffer::Attachment::HasImage() const
{
if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
return true;
else if (Renderbuffer())
return true;
return false;
}
const WebGLRectangleObject&
WebGLFramebuffer::Attachment::RectangleObject() const
{
MOZ_ASSERT(HasImage(), "Make sure it has an image before requesting the rectangle.");
if (Texture()) {
MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
} else if (Renderbuffer()) {
return *Renderbuffer();
}
MOZ_CRASH("Should not get here.");
}
static inline bool
IsValidAttachedTextureColorFormat(GLenum format) {
IsValidAttachedTextureColorFormat(GLenum format)
{
return (
/* linear 8-bit formats */
format == LOCAL_GL_ALPHA ||
@ -126,31 +149,34 @@ IsValidAttachedTextureColorFormat(GLenum format) {
}
bool
WebGLFramebuffer::Attachment::IsComplete() const {
const WebGLRectangleObject *thisRect = RectangleObject();
if (!thisRect ||
!thisRect->Width() ||
!thisRect->Height())
WebGLFramebuffer::Attachment::IsComplete() const
{
if (!HasImage())
return false;
if (mTexturePtr) {
if (!mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
return false;
const WebGLRectangleObject& rect = RectangleObject();
if (!rect.Width() ||
!rect.Height())
{
return false;
}
if (mTexturePtr) {
MOZ_ASSERT(mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
GLenum format = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) {
return format == LOCAL_GL_DEPTH_COMPONENT;
}
else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
} else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
return format == LOCAL_GL_DEPTH_STENCIL;
}
else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments)) {
} else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments))
{
return IsValidAttachedTextureColorFormat(format);
}
MOZ_CRASH("Invalid WebGL attachment poin?");
MOZ_ASSERT(false, "Invalid WebGL attachment point?");
return false;
}
if (mRenderbufferPtr) {
@ -158,29 +184,31 @@ WebGLFramebuffer::Attachment::IsComplete() const {
if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) {
return format == LOCAL_GL_DEPTH_COMPONENT16;
}
else if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) {
} else if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) {
return format == LOCAL_GL_STENCIL_INDEX8;
}
else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
} else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
return format == LOCAL_GL_DEPTH_STENCIL;
} else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments))
{
return format == LOCAL_GL_RGB565 ||
format == LOCAL_GL_RGB5_A1 ||
format == LOCAL_GL_RGBA4 ||
format == LOCAL_GL_SRGB8_ALPHA8_EXT;
}
else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments)) {
return (format == LOCAL_GL_RGB565 ||
format == LOCAL_GL_RGB5_A1 ||
format == LOCAL_GL_RGBA4 ||
format == LOCAL_GL_SRGB8_ALPHA8_EXT);
}
MOZ_CRASH("Invalid WebGL attachment poin?");
MOZ_ASSERT(false, "Invalid WebGL attachment point?");
return false;
}
MOZ_ASSERT(false); // should never get there
MOZ_ASSERT(false, "Should not get here.");
return false;
}
void
WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const {
WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const
{
MOZ_ASSERT(HasImage());
if (Texture()) {
GLContext* gl = Texture()->Context()->gl;
if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
@ -200,16 +228,17 @@ WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const {
return;
}
// Neither?
MOZ_ASSERT(false, "FB attachment without a tex or RB.");
MOZ_ASSERT(false, "Should not get here.");
}
void
WebGLFramebuffer::Delete() {
WebGLFramebuffer::Delete()
{
mColorAttachments.Clear();
mDepthAttachment.Reset();
mStencilAttachment.Reset();
mDepthStencilAttachment.Reset();
mContext->MakeContextCurrent();
mContext->gl->fDeleteFramebuffers(1, &mGLName);
LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
@ -217,15 +246,13 @@ WebGLFramebuffer::Delete() {
void
WebGLFramebuffer::FramebufferRenderbuffer(GLenum target,
GLenum attachment,
GLenum rbtarget,
WebGLRenderbuffer *wrb)
GLenum attachment,
GLenum rbtarget,
WebGLRenderbuffer* wrb)
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb))
{
return;
}
if (target != LOCAL_GL_FRAMEBUFFER)
return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
@ -258,10 +285,10 @@ WebGLFramebuffer::FramebufferRenderbuffer(GLenum target,
void
WebGLFramebuffer::FramebufferTexture2D(GLenum target,
GLenum attachment,
GLenum textarget,
WebGLTexture *wtex,
GLint level)
GLenum attachment,
GLenum textarget,
WebGLTexture* wtex,
GLint level)
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
@ -276,7 +303,9 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum target,
if (textarget != LOCAL_GL_TEXTURE_2D &&
(textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
{
return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
}
if (wtex) {
bool isTexture2D = wtex->Target() == LOCAL_GL_TEXTURE_2D;
@ -300,9 +329,8 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum target,
mDepthStencilAttachment.SetTexImage(wtex, textarget, level);
break;
default:
if (!CheckColorAttachementNumber(attachment, "framebufferTexture2D")){
if (!CheckColorAttachementNumber(attachment, "framebufferTexture2D"))
return;
}
size_t colorAttachmentId = size_t(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
EnsureColorAttachments(colorAttachmentId);
@ -311,42 +339,9 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum target,
}
}
bool
WebGLFramebuffer::HasIncompleteAttachment() const {
int32_t colorAttachmentCount = mColorAttachments.Length();
for (int32_t i = 0; i < colorAttachmentCount; i++)
{
if (mColorAttachments[i].IsDefined() && !mColorAttachments[i].IsComplete())
{
return true;
}
}
return ((mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) ||
(mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) ||
(mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()));
}
bool
WebGLFramebuffer::HasAttachmentsOfMismatchedDimensions() const {
int32_t colorAttachmentCount = mColorAttachments.Length();
for (int32_t i = 1; i < colorAttachmentCount; i++)
{
if (mColorAttachments[i].IsDefined() && !mColorAttachments[i].HasSameDimensionsAs(mColorAttachments[0]))
{
return true;
}
}
return ((mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachments[0])) ||
(mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachments[0])) ||
(mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachments[0])));
}
const WebGLFramebuffer::Attachment&
WebGLFramebuffer::GetAttachment(GLenum attachment) const {
WebGLFramebuffer::GetAttachment(GLenum attachment) const
{
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
return mDepthStencilAttachment;
if (attachment == LOCAL_GL_DEPTH_ATTACHMENT)
@ -359,8 +354,7 @@ WebGLFramebuffer::GetAttachment(GLenum attachment) const {
return mColorAttachments[0];
}
uint32_t colorAttachmentId = uint32_t(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0;
if (colorAttachmentId >= mColorAttachments.Length()) {
MOZ_ASSERT(false);
return mColorAttachments[0];
@ -370,10 +364,9 @@ WebGLFramebuffer::GetAttachment(GLenum attachment) const {
}
void
WebGLFramebuffer::DetachTexture(const WebGLTexture *tex) {
int32_t colorAttachmentCount = mColorAttachments.Length();
for (int32_t i = 0; i < colorAttachmentCount; i++) {
WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
{
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
if (mColorAttachments[i].Texture() == tex) {
FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0);
// a texture might be attached more that once while editing the framebuffer
@ -389,10 +382,9 @@ WebGLFramebuffer::DetachTexture(const WebGLTexture *tex) {
}
void
WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer *rb) {
int32_t colorAttachmentCount = mColorAttachments.Length();
for (int32_t i = 0; i < colorAttachmentCount; i++) {
WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
{
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
if (mColorAttachments[0].Renderbuffer() == rb) {
FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr);
// a renderbuffer might be attached more that once while editing the framebuffer
@ -408,62 +400,191 @@ WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer *rb) {
}
bool
WebGLFramebuffer::CheckAndInitializeAttachments()
WebGLFramebuffer::HasDefinedAttachments() const
{
bool hasAttachments = false;
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
hasAttachments |= mColorAttachments[i].IsDefined();
}
hasAttachments |= mDepthAttachment.IsDefined();
hasAttachments |= mStencilAttachment.IsDefined();
hasAttachments |= mDepthStencilAttachment.IsDefined();
return hasAttachments;
}
static bool
IsIncomplete(const WebGLFramebuffer::Attachment& cur)
{
return cur.IsDefined() && !cur.IsComplete();
}
bool
WebGLFramebuffer::HasIncompleteAttachments() const
{
bool hasIncomplete = false;
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
hasIncomplete |= IsIncomplete(mColorAttachments[i]);
}
hasIncomplete |= IsIncomplete(mDepthAttachment);
hasIncomplete |= IsIncomplete(mStencilAttachment);
hasIncomplete |= IsIncomplete(mDepthStencilAttachment);
return hasIncomplete;
}
const WebGLRectangleObject&
WebGLFramebuffer::GetAnyRectObject() const
{
MOZ_ASSERT(HasDefinedAttachments());
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
if (mColorAttachments[i].HasImage())
return mColorAttachments[i].RectangleObject();
}
if (mDepthAttachment.HasImage())
return mDepthAttachment.RectangleObject();
if (mStencilAttachment.HasImage())
return mStencilAttachment.RectangleObject();
if (mDepthStencilAttachment.HasImage())
return mDepthStencilAttachment.RectangleObject();
MOZ_CRASH("Should not get here.");
}
static bool
RectsMatch(const WebGLFramebuffer::Attachment& attachment,
const WebGLRectangleObject& rect)
{
return attachment.RectangleObject().HasSameDimensionsAs(rect);
}
bool
WebGLFramebuffer::AllImageRectsMatch() const
{
MOZ_ASSERT(HasDefinedAttachments());
MOZ_ASSERT(!HasIncompleteAttachments());
const WebGLRectangleObject& rect = GetAnyRectObject();
// Alright, we have *a* rect, let's check all the others.
bool imageRectsMatch = true;
for (size_t i = 0; i < mColorAttachments.Length(); i++) {
if (mColorAttachments[i].HasImage())
imageRectsMatch &= RectsMatch(mColorAttachments[i], rect);
}
if (mDepthAttachment.HasImage())
imageRectsMatch &= RectsMatch(mDepthAttachment, rect);
if (mStencilAttachment.HasImage())
imageRectsMatch &= RectsMatch(mStencilAttachment, rect);
if (mDepthStencilAttachment.HasImage())
imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect);
return imageRectsMatch;
}
const WebGLRectangleObject&
WebGLFramebuffer::RectangleObject() const
{
// If we're using this as the RectObj of an FB, we need to be sure the FB
// has a consistent rect.
MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?");
return GetAnyRectObject();
}
GLenum
WebGLFramebuffer::PrecheckFramebufferStatus() const
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
// enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not
// generate the INVALID_FRAMEBUFFER_OPERATION that we need here
if (!HasDefinedAttachments())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments
if (HasIncompleteAttachments())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
if (!AllImageRectsMatch())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // No consistent size
if (HasDepthStencilConflict())
return false;
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
if (HasIncompleteAttachment())
return false;
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
}
GLenum
WebGLFramebuffer::CheckFramebufferStatus() const
{
GLenum precheckStatus = PrecheckFramebufferStatus();
if (precheckStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return precheckStatus;
// Looks good on our end. Let's ask the driver.
mContext->MakeContextCurrent();
// Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
FinalizeAttachments();
return mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
}
bool
WebGLFramebuffer::CheckAndInitializeAttachments()
{
MOZ_ASSERT(mContext->mBoundFramebuffer == this);
if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
// Cool! We've checked out ok. Just need to initialize.
size_t colorAttachmentCount = size_t(mColorAttachments.Length());
// Check if we need to initialize anything
{
bool hasUnitializedAttachments = false;
bool hasUninitializedAttachments = false;
for (size_t i = 0; i < colorAttachmentCount; i++) {
hasUnitializedAttachments |= mColorAttachments[i].HasUninitializedImageData();
if (mColorAttachments[i].HasImage())
hasUninitializedAttachments |= mColorAttachments[i].HasUninitializedImageData();
}
if (!hasUnitializedAttachments &&
!mDepthAttachment.HasUninitializedImageData() &&
!mStencilAttachment.HasUninitializedImageData() &&
!mDepthStencilAttachment.HasUninitializedImageData())
{
if (mDepthAttachment.HasImage())
hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData();
if (mStencilAttachment.HasImage())
hasUninitializedAttachments |= mStencilAttachment.HasUninitializedImageData();
if (mDepthStencilAttachment.HasImage())
hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData();
if (!hasUninitializedAttachments)
return true;
}
}
// ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case
const WebGLRectangleObject *rect = mColorAttachments[0].RectangleObject();
if (!rect ||
!rect->Width() ||
!rect->Height())
return false;
GLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
// Get buffer-bit-mask and color-attachment-mask-list
uint32_t mask = 0;
bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = { false };
MOZ_ASSERT(colorAttachmentCount <= WebGLContext::sMaxColorAttachments);
MOZ_ASSERT( colorAttachmentCount <= WebGLContext::sMaxColorAttachments );
for (size_t i = 0; i < colorAttachmentCount; i++)
{
colorAttachmentsMask[i] = mColorAttachments[i].HasUninitializedImageData();
if (colorAttachmentsMask[i]) {
mask |= LOCAL_GL_COLOR_BUFFER_BIT;
for (size_t i = 0; i < colorAttachmentCount; i++) {
if (mColorAttachments[i].HasUninitializedImageData()) {
colorAttachmentsMask[i] = true;
mask |= LOCAL_GL_COLOR_BUFFER_BIT;
}
}
@ -479,10 +600,11 @@ WebGLFramebuffer::CheckAndInitializeAttachments()
mask |= LOCAL_GL_STENCIL_BUFFER_BIT;
}
// Clear!
mContext->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask);
for (size_t i = 0; i < colorAttachmentCount; i++)
{
// Mark all the uninitialized images as initialized.
for (size_t i = 0; i < colorAttachmentCount; i++) {
if (mColorAttachments[i].HasUninitializedImageData())
mColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
}
@ -497,30 +619,25 @@ WebGLFramebuffer::CheckAndInitializeAttachments()
return true;
}
bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char * functionName) const
bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char* functionName) const
{
const char* const errorFormating = "%s: attachment: invalid enum value 0x%x";
if (mContext->IsExtensionEnabled(WebGLContext::WEBGL_draw_buffers))
{
if (mContext->IsExtensionEnabled(WebGLContext::WEBGL_draw_buffers)) {
if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments))
{
mContext->ErrorInvalidEnum(errorFormating, functionName, attachment);
return false;
}
}
else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
{
} else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) {
if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 &&
attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
{
mContext->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x. "
"Try the WEBGL_draw_buffers extension if supported.", functionName, attachment);
return false;
}
else
{
} else {
mContext->ErrorInvalidEnum(errorFormating, functionName, attachment);
return false;
}
@ -529,14 +646,13 @@ bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char
return true;
}
void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) {
void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId)
{
MOZ_ASSERT(colorAttachmentId < WebGLContext::sMaxColorAttachments);
size_t currentAttachmentCount = mColorAttachments.Length();
if (mColorAttachments.Length() > colorAttachmentId) {
if (colorAttachmentId < currentAttachmentCount)
return;
}
MOZ_ASSERT( colorAttachmentId < WebGLContext::sMaxColorAttachments );
mColorAttachments.SetLength(colorAttachmentId + 1);
@ -546,7 +662,8 @@ void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) {
}
void
WebGLFramebuffer::FinalizeAttachments() const {
WebGLFramebuffer::FinalizeAttachments() const
{
for (size_t i = 0; i < ColorAttachmentCount(); i++) {
if (ColorAttachment(i).IsDefined())
ColorAttachment(i).FinalizeAttachment(LOCAL_GL_COLOR_ATTACHMENT0 + i);

View File

@ -27,7 +27,7 @@ class WebGLFramebuffer MOZ_FINAL
, public WebGLContextBoundObject
{
public:
WebGLFramebuffer(WebGLContext *context);
WebGLFramebuffer(WebGLContext* context);
~WebGLFramebuffer() {
DeleteOnce();
@ -54,21 +54,21 @@ public:
bool HasAlpha() const;
void SetTexImage(WebGLTexture *tex, GLenum target, GLint level);
void SetRenderbuffer(WebGLRenderbuffer *rb) {
void SetTexImage(WebGLTexture* tex, GLenum target, GLint level);
void SetRenderbuffer(WebGLRenderbuffer* rb) {
mTexturePtr = nullptr;
mRenderbufferPtr = rb;
}
const WebGLTexture *Texture() const {
const WebGLTexture* Texture() const {
return mTexturePtr;
}
WebGLTexture *Texture() {
WebGLTexture* Texture() {
return mTexturePtr;
}
const WebGLRenderbuffer *Renderbuffer() const {
const WebGLRenderbuffer* Renderbuffer() const {
return mRenderbufferPtr;
}
WebGLRenderbuffer *Renderbuffer() {
WebGLRenderbuffer* Renderbuffer() {
return mRenderbufferPtr;
}
GLenum TexImageTarget() const {
@ -86,9 +86,9 @@ public:
mRenderbufferPtr = nullptr;
}
const WebGLRectangleObject* RectangleObject() const;
bool HasSameDimensionsAs(const Attachment& other) const;
const WebGLRectangleObject& RectangleObject() const;
bool HasImage() const;
bool IsComplete() const;
void FinalizeAttachment(GLenum attachmentLoc) const;
@ -103,15 +103,23 @@ public:
void FramebufferRenderbuffer(GLenum target,
GLenum attachment,
GLenum rbtarget,
WebGLRenderbuffer *wrb);
WebGLRenderbuffer* wrb);
void FramebufferTexture2D(GLenum target,
GLenum attachment,
GLenum textarget,
WebGLTexture *wtex,
WebGLTexture* wtex,
GLint level);
bool HasIncompleteAttachment() const;
private:
const WebGLRectangleObject& GetAnyRectObject() const;
public:
bool HasDefinedAttachments() const;
bool HasIncompleteAttachments() const;
bool AllImageRectsMatch() const;
GLenum PrecheckFramebufferStatus() const;
GLenum CheckFramebufferStatus() const;
bool HasDepthStencilConflict() const {
return int(mDepthAttachment.IsDefined()) +
@ -119,12 +127,10 @@ public:
int(mDepthStencilAttachment.IsDefined()) >= 2;
}
bool HasAttachmentsOfMismatchedDimensions() const;
const size_t ColorAttachmentCount() const {
size_t ColorAttachmentCount() const {
return mColorAttachments.Length();
}
const Attachment& ColorAttachment(uint32_t colorAttachmentId) const {
const Attachment& ColorAttachment(size_t colorAttachmentId) const {
return mColorAttachments[colorAttachmentId];
}
@ -142,21 +148,19 @@ public:
const Attachment& GetAttachment(GLenum attachment) const;
void DetachTexture(const WebGLTexture *tex);
void DetachTexture(const WebGLTexture* tex);
void DetachRenderbuffer(const WebGLRenderbuffer *rb);
void DetachRenderbuffer(const WebGLRenderbuffer* rb);
const WebGLRectangleObject *RectangleObject() {
return mColorAttachments[0].RectangleObject();
}
const WebGLRectangleObject& RectangleObject() const;
WebGLContext *GetParentObject() const {
WebGLContext* GetParentObject() const {
return Context();
}
void FinalizeAttachments() const;
virtual JSObject* WrapObject(JSContext *cx,
virtual JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
@ -164,7 +168,7 @@ public:
bool CheckAndInitializeAttachments();
bool CheckColorAttachementNumber(GLenum attachment, const char * functionName) const;
bool CheckColorAttachementNumber(GLenum attachment, const char* functionName) const;
GLuint mGLName;
bool mHasEverBeenBound;

View File

@ -308,7 +308,7 @@ uint16_t
HTMLTrackElement::ReadyState() const
{
if (!mTrack) {
return NONE;
return READY_STATE_NONE;
}
return mTrack->ReadyState();

View File

@ -97,10 +97,10 @@ public:
// Constants for numeric readyState property values.
enum {
NONE = 0U,
LOADING = 1U,
LOADED = 2U,
ERROR = 3U
READY_STATE_NONE = 0U,
READY_STATE_LOADING = 1U,
READY_STATE_LOADED = 2U,
READY_STATE_ERROR = 3U
};
uint16_t ReadyState() const;

View File

@ -61,7 +61,7 @@ TextTrackManager::AddTextTrack(TextTrackKind aKind, const nsAString& aLabel,
{
nsRefPtr<TextTrack> ttrack =
mTextTracks->AddTextTrack(mMediaElement, aKind, aLabel, aLanguage);
ttrack->SetReadyState(HTMLTrackElement::LOADED);
ttrack->SetReadyState(HTMLTrackElement::READY_STATE_LOADED);
AddCues(ttrack);
return ttrack.forget();
}
@ -155,7 +155,7 @@ TextTrackManager::PopulatePendingList()
for (uint32_t index = 0; index < len; ++index) {
TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
if (ttrack && ttrack->Mode() != TextTrackMode::Disabled &&
ttrack->ReadyState() == HTMLTrackElement::LOADING) {
ttrack->ReadyState() == HTMLTrackElement::READY_STATE_LOADING) {
mPendingTextTracks->AddTextTrack(ttrack);
}
}

View File

@ -0,0 +1,227 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "SharedThreadPool.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPtr.h"
#include "nsDataHashtable.h"
#include "VideoUtils.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Preferences.h"
#ifdef XP_WIN
// Required to init MSCOM by MSCOMInitThreadPoolListener.
#include <Objbase.h>
#endif
namespace mozilla {
// Created and destroyed on the main thread.
static StaticAutoPtr<ReentrantMonitor> sMonitor;
// Hashtable, maps thread pool name to SharedThreadPool instance.
// Modified only on the main thread.
static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools;
static already_AddRefed<nsIThreadPool>
CreateThreadPool(const nsCString& aName);
static void
DestroySharedThreadPoolHashTable();
void
SharedThreadPool::EnsureInitialized()
{
MOZ_ASSERT(NS_IsMainThread());
if (sMonitor || sPools) {
// Already initalized.
return;
}
sMonitor = new ReentrantMonitor("SharedThreadPool");
sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
}
class ShutdownPoolsEvent : public nsRunnable {
public:
NS_IMETHODIMP Run() {
MOZ_ASSERT(NS_IsMainThread());
DestroySharedThreadPoolHashTable();
return NS_OK;
}
};
static void
DestroySharedThreadPoolHashTable()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sMonitor && sPools);
if (!sPools->Count()) {
// No more SharedThreadPool singletons. Delete the hash table.
// Note we don't need to lock sMonitor, since we only modify the
// hash table on the main thread, and if the hash table is empty
// there are no external references into its contents.
sPools = nullptr;
sMonitor = nullptr;
}
}
TemporaryRef<SharedThreadPool>
SharedThreadPool::Get(const nsCString& aName)
{
MOZ_ASSERT(NS_IsMainThread());
EnsureInitialized();
MOZ_ASSERT(sMonitor);
ReentrantMonitorAutoEnter mon(*sMonitor);
SharedThreadPool* pool = nullptr;
if (!sPools->Get(aName, &pool)) {
nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
NS_ENSURE_TRUE(threadPool, nullptr);
pool = new SharedThreadPool(aName, threadPool);
sPools->Put(aName, pool);
}
MOZ_ASSERT(pool);
RefPtr<SharedThreadPool> instance(pool);
return instance.forget();
}
NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::AddRef(void)
{
MOZ_ASSERT(sMonitor);
ReentrantMonitorAutoEnter mon(*sMonitor);
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
nsrefcnt count = ++mRefCnt;
NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
return count;
}
NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::Release(void)
{
MOZ_ASSERT(sMonitor);
bool dispatchShutdownEvent;
{
ReentrantMonitorAutoEnter mon(*sMonitor);
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, count, "SharedThreadPool");
if (count) {
return count;
}
// Zero refcount. Must shutdown and then delete the thread pool.
// First, dispatch an event to the main thread to call Shutdown() on
// the nsIThreadPool. The Runnable here will add a refcount to the pool,
// and when the Runnable releases the nsIThreadPool it will be deleted.
RefPtr<nsIRunnable> r = NS_NewRunnableMethod(mPool, &nsIThreadPool::Shutdown);
NS_DispatchToMainThread(r);
// Remove SharedThreadPool from table of pools.
sPools->Remove(mName);
MOZ_ASSERT(!sPools->Get(mName));
// Stabilize refcount, so that if something in the dtor QIs,
// it won't explode.
mRefCnt = 1;
delete this;
dispatchShutdownEvent = sPools->Count() == 0;
}
if (dispatchShutdownEvent) {
// No more SharedThreadPools alive. Destroy the hash table.
// Ensure that we only run on the main thread.
// Do this in an event so that if something holds the monitor we won't
// be deleting the monitor while it's held.
NS_DispatchToMainThread(new ShutdownPoolsEvent(), NS_DISPATCH_NORMAL);
}
return 0;
}
NS_IMPL_QUERY_INTERFACE1(SharedThreadPool, nsIThreadPool)
SharedThreadPool::SharedThreadPool(const nsCString& aName,
nsIThreadPool* aPool)
: mName(aName)
, mPool(aPool)
, mRefCnt(0)
{
mEventTarget = do_QueryInterface(aPool);
}
SharedThreadPool::~SharedThreadPool()
{
}
#ifdef XP_WIN
// Thread pool listener which ensures that MSCOM is initialized and
// deinitialized on the thread pool thread. We may call into WMF or
// DirectShow on this thread, so we need MSCOM working.
class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADPOOLLISTENER
};
NS_IMPL_ISUPPORTS1(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadCreated()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
}
return NS_OK;
}
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadShuttingDown()
{
CoUninitialize();
return NS_OK;
}
#endif // XP_WIN
static already_AddRefed<nsIThreadPool>
CreateThreadPool(const nsCString& aName)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = pool->SetName(aName);
NS_ENSURE_SUCCESS(rv, nullptr);
// We limit the number of threads that we use for media. Note that the
// default thread limit is the same as the idle limit so that we're not
// constantly creating and destroying threads (see Bug 881954). When the
// thread pool threads shutdown they dispatch an event to the main thread
// to call nsIThread::Shutdown(), and if we're very busy that can take a
// while to run, and we end up with dozens of extra threads. Note that
// threads that are idle for 60 seconds are shutdown naturally.
rv = pool->SetThreadLimit(
Preferences::GetUint("media.thread-pool.thread-limit", 4));
NS_ENSURE_SUCCESS(rv, nullptr);
rv = pool->SetIdleThreadLimit(
Preferences::GetUint("media.thread-pool.idle-thread-limit", 4));
NS_ENSURE_SUCCESS(rv, nullptr);
#ifdef XP_WIN
// Ensure MSCOM is initialized on the thread pools threads.
nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
rv = pool->SetListener(listener);
NS_ENSURE_SUCCESS(rv, nullptr);
#endif
return pool.forget();
}
} // namespace mozilla

View File

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 SharedThreadPool_h_
#define SharedThreadPool_h_
#include <queue>
#include "mozilla/RefPtr.h"
#include "nsThreadUtils.h"
#include "nsIThreadPool.h"
#include "nsISupports.h"
#include "nsISupportsImpl.h"
#include "nsCOMPtr.h"
namespace mozilla {
// Wrapper that makes an nsIThreadPool a singleton, and provides a
// consistent threadsafe interface to get instances. Callers simply get a
// SharedThreadPool by the name of its nsIThreadPool. All get requests of
// the same name get the same SharedThreadPool. Users must store a reference
// to the pool, and when the last reference to a SharedThreadPool is dropped
// the pool is shutdown and deleted. Users aren't required to manually
// shutdown the pool, and can release references on any thread. On Windows
// all threads in the pool have MSCOM initialized with COINIT_MULTITHREADED.
class SharedThreadPool : public nsIThreadPool {
public:
// Gets (possibly creating) the shared thread pool singleton instance with
// thread pool named aName.
// *Must* be called on the main thread.
static TemporaryRef<SharedThreadPool> Get(const nsCString& aName);
// We implement custom threadsafe AddRef/Release pair, that destroys the
// the shared pool singleton when the refcount drops to 0. The addref/release
// are implemented using locking, so it's not recommended that you use them
// in a tight loop.
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
// Forward behaviour to wrapped thread pool implementation.
NS_FORWARD_SAFE_NSITHREADPOOL(mPool);
NS_FORWARD_SAFE_NSIEVENTTARGET(mEventTarget);
private:
// Creates necessary statics.
// Main thread only.
static void EnsureInitialized();
// Creates a singleton SharedThreadPool wrapper around aPool.
// aName is the name of the aPool, and is used to lookup the
// SharedThreadPool in the hash table of all created pools.
SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool);
virtual ~SharedThreadPool();
// Name of mPool.
const nsCString mName;
// Thread pool being wrapped.
nsCOMPtr<nsIThreadPool> mPool;
// Refcount. We implement custom ref counting so that the thread pool is
// shutdown in a threadsafe manner and singletonness is preserved.
nsrefcnt mRefCnt;
// mPool QI'd to nsIEventTarget. We cache this, so that we can use
// NS_FORWARD_SAFE_NSIEVENTTARGET above.
nsCOMPtr<nsIEventTarget> mEventTarget;
};
} // namespace mozilla
#endif // SharedThreadPool_h_

View File

@ -69,7 +69,7 @@ TextTrack::SetDefaultSettings()
mRegionList = new TextTrackRegionList(mParent);
mCuePos = 0;
mDirty = false;
mReadyState = HTMLTrackElement::NONE;
mReadyState = HTMLTrackElement::READY_STATE_NONE;
}
JSObject*
@ -195,8 +195,8 @@ void
TextTrack::SetReadyState(uint16_t aState)
{
mReadyState = aState;
if (mMediaElement && (mReadyState == HTMLTrackElement::LOADED ||
mReadyState == HTMLTrackElement::ERROR)) {
if (mMediaElement && (mReadyState == HTMLTrackElement::READY_STATE_LOADED ||
mReadyState == HTMLTrackElement::READY_STATE_ERROR)) {
mMediaElement->RemoveTextTrack(this, true);
}
}

View File

@ -78,7 +78,7 @@ WebVTTListener::LoadResource()
rv = mParserWrapper->Watch(this);
NS_ENSURE_SUCCESS(rv, rv);
mElement->mTrack->SetReadyState(HTMLTrackElement::LOADING);
mElement->mTrack->SetReadyState(HTMLTrackElement::READY_STATE_LOADING);
return NS_OK;
}
@ -106,9 +106,9 @@ WebVTTListener::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatus)
{
if (mElement->ReadyState() != HTMLTrackElement::ERROR) {
if (mElement->ReadyState() != HTMLTrackElement::READY_STATE_ERROR) {
TextTrack* track = mElement->Track();
track->SetReadyState(HTMLTrackElement::LOADED);
track->SetReadyState(HTMLTrackElement::READY_STATE_LOADED);
}
// Attempt to parse any final data the parser might still have.
mParserWrapper->Flush();

View File

@ -85,6 +85,7 @@ EXPORTS += [
'MP3FrameParser.h',
'RtspMediaResource.h',
'SharedBuffer.h',
'SharedThreadPool.h',
'StreamBuffer.h',
'TimeVarying.h',
'TrackUnionStream.h',
@ -131,6 +132,7 @@ UNIFIED_SOURCES += [
'MediaStreamTrack.cpp',
'MP3FrameParser.cpp',
'RtspMediaResource.cpp',
'SharedThreadPool.cpp',
'StreamBuffer.cpp',
'TextTrack.cpp',
'TextTrackCue.cpp',

View File

@ -19,6 +19,7 @@
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/DebugOnly.h"
#include "SharedThreadPool.h"
#include <algorithm>
#include <cassert>
@ -31,75 +32,6 @@ PRLogModuleInfo* gWMFByteStreamLog = nullptr;
#define WMF_BS_LOG(...)
#endif
// Limit the number of threads that we use for IO.
static const uint32_t NumWMFIoThreads = 4;
// Thread pool listener which ensures that MSCOM is initialized and
// deinitialized on the thread pool thread. We can call back into WMF
// on this thread, so we need MSCOM working.
class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADPOOLLISTENER
};
NS_IMPL_ISUPPORTS1(ThreadPoolListener, nsIThreadPoolListener)
NS_IMETHODIMP
ThreadPoolListener::OnThreadCreated()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
}
return NS_OK;
}
NS_IMETHODIMP
ThreadPoolListener::OnThreadShuttingDown()
{
CoUninitialize();
return NS_OK;
}
// Thread pool on which read requests are processed.
// This is created and destroyed on the main thread only.
static nsIThreadPool* sThreadPool = nullptr;
// Counter of the number of WMFByteStreams that are instantiated and that need
// the thread pool. This is read/write on the main thread only.
static int32_t sThreadPoolRefCnt = 0;
class ReleaseWMFByteStreamResourcesEvent MOZ_FINAL : public nsRunnable {
public:
ReleaseWMFByteStreamResourcesEvent(already_AddRefed<MediaResource> aResource)
: mResource(aResource) {}
virtual ~ReleaseWMFByteStreamResourcesEvent() {}
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
// Explicitly release the MediaResource reference. We *must* do this on
// the main thread, so we must explicitly release it here, we can't rely
// on the destructor to release it, since if this event runs before its
// dispatch call returns the destructor may run on the non-main thread.
mResource = nullptr;
NS_ASSERTION(sThreadPoolRefCnt > 0, "sThreadPoolRefCnt Should be non-negative");
sThreadPoolRefCnt--;
if (sThreadPoolRefCnt == 0) {
NS_ASSERTION(sThreadPool != nullptr, "Should have thread pool ref if sThreadPoolRefCnt==0.");
// Note: store ref to thread pool, then clear global ref, then
// Shutdown() using the stored ref. Events can run during the Shutdown()
// call, so if we release after calling Shutdown(), another event may
// have incremented the refcnt in the meantime, and have a dangling
// pointer to the now destroyed threadpool!
nsCOMPtr<nsIThreadPool> pool = sThreadPool;
NS_IF_RELEASE(sThreadPool);
pool->Shutdown();
}
return NS_OK;
}
nsRefPtr<MediaResource> mResource;
};
WMFByteStream::WMFByteStream(MediaResource* aResource,
WMFSourceReaderCallback* aSourceReaderCallback)
: mSourceReaderCallback(aSourceReaderCallback),
@ -123,12 +55,6 @@ WMFByteStream::WMFByteStream(MediaResource* aResource,
WMFByteStream::~WMFByteStream()
{
MOZ_COUNT_DTOR(WMFByteStream);
// The WMFByteStream can be deleted from a thread pool thread, so we
// dispatch an event to the main thread to deref the thread pool and
// deref the MediaResource.
nsCOMPtr<nsIRunnable> event =
new ReleaseWMFByteStreamResourcesEvent(mResource.forget());
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
WMF_BS_LOG("[%p] WMFByteStream DTOR", this);
}
@ -137,39 +63,8 @@ WMFByteStream::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
if (!sThreadPool) {
nsresult rv;
nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
sThreadPool = pool;
NS_ADDREF(sThreadPool);
rv = sThreadPool->SetName(NS_LITERAL_CSTRING("WMFByteStream Async Read Pool"));
NS_ENSURE_SUCCESS(rv, rv);
// We limit the number of threads that we use for IO. Note that the thread
// limit is the same as the idle limit so that we're not constantly creating
// and destroying threads. When the thread pool threads shutdown they
// dispatch an event to the main thread to call nsIThread::Shutdown(),
// and if we're very busy that can take a while to run, and we end up with
// dozens of extra threads. Note that threads that are idle for 60 seconds
// are shutdown naturally.
rv = sThreadPool->SetThreadLimit(NumWMFIoThreads);
NS_ENSURE_SUCCESS(rv, rv);
rv = sThreadPool->SetIdleThreadLimit(NumWMFIoThreads);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIThreadPoolListener> listener = new ThreadPoolListener();
rv = sThreadPool->SetListener(listener);
NS_ENSURE_SUCCESS(rv, rv);
}
sThreadPoolRefCnt++;
// Store a ref to the thread pool, so that we keep the pool alive as long as
// we're alive.
mThreadPool = sThreadPool;
mThreadPool = SharedThreadPool::Get(NS_LITERAL_CSTRING("WMFByteStream IO"));
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 contentTypeUTF16(mResource->GetContentType());
if (!contentTypeUTF16.IsEmpty()) {

View File

@ -15,13 +15,12 @@
#include "nsAutoPtr.h"
#include "mozilla/RefPtr.h"
class nsIThreadPool;
namespace mozilla {
class MediaResource;
class ReadRequest;
class WMFSourceReaderCallback;
class SharedThreadPool;
// Wraps a MediaResource around an IMFByteStream interface, so that it can
// be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
@ -123,7 +122,7 @@ private:
// Reference to the thread pool in which we perform the reads asynchronously.
// Note this is pool is shared amongst all active WMFByteStreams.
nsCOMPtr<nsIThreadPool> mThreadPool;
RefPtr<SharedThreadPool> mThreadPool;
// Reference to the source reader's callback. We use this reference to
// notify threads waiting on a ReadSample() callback to stop waiting

View File

@ -71,7 +71,7 @@ this.AppsUtils = {
installOrigin: aApp.installOrigin,
origin: aApp.origin,
#ifdef MOZ_ANDROID_SYNTHAPKS
packageName: aApp.packageName,
apkPackageName: aApp.apkPackageName,
#endif
receipts: aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null,
installTime: aApp.installTime,

View File

@ -241,16 +241,28 @@ this.DOMApplicationRegistry = {
},
// Notify we are starting with registering apps.
_registryStarted: Promise.defer(),
notifyAppsRegistryStart: function notifyAppsRegistryStart() {
Services.obs.notifyObservers(this, "webapps-registry-start", null);
this._registryStarted.resolve();
},
get registryStarted() {
return this._registryStarted.promise;
},
// Notify we are done with registering apps and save a copy of the registry.
_registryReady: Promise.defer(),
notifyAppsRegistryReady: function notifyAppsRegistryReady() {
this._registryReady.resolve();
Services.obs.notifyObservers(this, "webapps-registry-ready", null);
this._saveApps();
},
get registryReady() {
return this._registryReady.promise;
},
// Ensure that the .to property in redirects is a relative URL.
sanitizeRedirects: function sanitizeRedirects(aSource) {
if (!aSource) {
@ -967,9 +979,17 @@ this.DOMApplicationRegistry = {
channel.contentType = "application/json";
NetUtil.asyncFetch(channel, function(aStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
deferred.resolve(null);
if (aResult == Cr.NS_ERROR_FILE_NOT_FOUND) {
// We expect this under certain circumstances, like for webapps.json
// on firstrun, so we return early without reporting an error.
return;
}
Cu.reportError("DOMApplicationRegistry: Could not read from json file "
+ aPath);
deferred.resolve(null);
return;
}
try {

View File

@ -212,6 +212,13 @@ parent:
*/
SetStatus(uint32_t type, nsString status);
/**
* Show/hide a tooltip when the mouse hovers over an element in the content
* document.
*/
ShowTooltip(uint32_t x, uint32_t y, nsString tooltip);
HideTooltip();
/**
* Initiates an asynchronous request for permission for the
* provided principal.

View File

@ -735,6 +735,7 @@ NS_INTERFACE_MAP_BEGIN(TabChild)
NS_INTERFACE_MAP_ENTRY(nsIDialogCreator)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(TabChild)
@ -2512,6 +2513,20 @@ TabChild::GetFrom(nsIPresShell* aPresShell)
return GetFrom(docShell);
}
NS_IMETHODIMP
TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText)
{
nsString str(aTipText);
SendShowTooltip(aXCoords, aYCoords, str);
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnHideTooltip()
{
SendHideTooltip();
return NS_OK;
}
TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
: mTabChild(aTabChild)

View File

@ -29,6 +29,7 @@
#include "nsIScriptObjectPrincipal.h"
#include "nsWeakReference.h"
#include "nsITabChild.h"
#include "nsITooltipListener.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/EventForwards.h"
@ -157,7 +158,8 @@ class TabChild : public PBrowserChild,
public nsITabChild,
public nsIObserver,
public ipc::MessageManagerCallback,
public TabContext
public TabContext,
public nsITooltipListener
{
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
@ -191,6 +193,7 @@ public:
NS_DECL_NSIDIALOGCREATOR
NS_DECL_NSITABCHILD
NS_DECL_NSIOBSERVER
NS_DECL_NSITOOLTIPLISTENER
/**
* MessageManagerCallback methods that we override.

View File

@ -933,36 +933,75 @@ TabParent::RecvSetBackgroundColor(const nscolor& aColor)
return true;
}
nsIXULBrowserWindow*
TabParent::GetXULBrowserWindow()
{
nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement);
if (!frame) {
return nullptr;
}
nsCOMPtr<nsIDocShell> docShell = frame->OwnerDoc()->GetDocShell();
if (!docShell) {
return nullptr;
}
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
if (!treeOwner) {
return nullptr;
}
nsCOMPtr<nsIXULWindow> window = do_GetInterface(treeOwner);
if (!window) {
return nullptr;
}
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
return xulBrowserWindow;
}
bool
TabParent::RecvSetStatus(const uint32_t& aType, const nsString& aStatus)
{
nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement);
if (frame) {
nsCOMPtr<nsIDocShell> docShell = frame->OwnerDoc()->GetDocShell();
if (!docShell)
return true;
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
if (!treeOwner)
return true;
nsCOMPtr<nsIXULWindow> window = do_GetInterface(treeOwner);
if (window) {
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
if (xulBrowserWindow) {
switch (aType)
{
case nsIWebBrowserChrome::STATUS_SCRIPT:
xulBrowserWindow->SetJSStatus(aStatus);
break;
case nsIWebBrowserChrome::STATUS_LINK:
xulBrowserWindow->SetOverLink(aStatus, nullptr);
break;
}
}
}
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
if (!xulBrowserWindow) {
return true;
}
switch (aType) {
case nsIWebBrowserChrome::STATUS_SCRIPT:
xulBrowserWindow->SetJSStatus(aStatus);
break;
case nsIWebBrowserChrome::STATUS_LINK:
xulBrowserWindow->SetOverLink(aStatus, nullptr);
break;
}
return true;
}
bool
TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip)
{
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
if (!xulBrowserWindow) {
return true;
}
xulBrowserWindow->ShowTooltip(aX, aY, aTooltip);
return true;
}
bool
TabParent::RecvHideTooltip()
{
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
if (!xulBrowserWindow) {
return true;
}
xulBrowserWindow->HideTooltip();
return true;
}

View File

@ -17,6 +17,7 @@
#include "nsIDialogParamBlock.h"
#include "nsISecureBrowserUI.h"
#include "nsITabParent.h"
#include "nsIXULBrowserWindow.h"
#include "Units.h"
#include "js/TypeDecls.h"
@ -83,6 +84,8 @@ public:
already_AddRefed<nsILoadContext> GetLoadContext();
nsIXULBrowserWindow* GetXULBrowserWindow();
/**
* Return the TabParent that has decided it wants to capture an
* event series for fast-path dispatch to its subprocess, if one
@ -166,6 +169,8 @@ public:
virtual bool RecvSetCursor(const uint32_t& aValue) MOZ_OVERRIDE;
virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE;
virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE;
virtual bool RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip);
virtual bool RecvHideTooltip();
virtual bool RecvGetDPI(float* aValue) MOZ_OVERRIDE;
virtual bool RecvGetDefaultScale(double* aValue) MOZ_OVERRIDE;
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) MOZ_OVERRIDE;

View File

@ -14,10 +14,7 @@
#include "nsGkAtoms.h"
#include "nsXBLDocumentInfo.h"
#include "nsIDOMElement.h"
#include "nsINativeKeyBindings.h"
#include "nsIController.h"
#include "nsFocusManager.h"
#include "nsPIWindowRoot.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsContentUtils.h"
@ -30,12 +27,12 @@
#include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h"
#include "nsEventStateManager.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
using namespace mozilla;
using namespace mozilla::dom;
static nsINativeKeyBindings *sNativeEditorBindings = nullptr;
class nsXBLSpecialDocInfo : public nsIObserver
{
public:
@ -230,15 +227,12 @@ BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
// to a particular element rather than the document
//
nsresult
nsXBLWindowKeyHandler::EnsureHandlers(bool *aIsEditor)
nsXBLWindowKeyHandler::EnsureHandlers()
{
nsCOMPtr<Element> el = GetElement();
NS_ENSURE_STATE(!mWeakPtrForElement || el);
if (el) {
// We are actually a XUL <keyset>.
if (aIsEditor)
*aIsEditor = false;
if (mHandler)
return NS_OK;
@ -252,59 +246,17 @@ nsXBLWindowKeyHandler::EnsureHandlers(bool *aIsEditor)
sXBLSpecialDocInfo->LoadDocInfo();
// Now determine which handlers we should be using.
bool isEditor = IsEditor();
if (isEditor) {
if (IsHTMLEditableFieldFocused()) {
sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
}
else {
sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
}
if (aIsEditor)
*aIsEditor = isEditor;
}
return NS_OK;
}
static nsINativeKeyBindings*
GetEditorKeyBindings()
{
static bool noBindings = false;
if (!sNativeEditorBindings && !noBindings) {
CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor",
&sNativeEditorBindings);
if (!sNativeEditorBindings) {
noBindings = true;
}
}
return sNativeEditorBindings;
}
static void
DoCommandCallback(const char *aCommand, void *aData)
{
nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(static_cast<EventTarget*>(aData));
if (!root) {
return;
}
nsCOMPtr<nsIController> controller;
root->GetControllerForCommand(aCommand, getter_AddRefs(controller));
if (!controller) {
return;
}
bool commandEnabled;
nsresult rv = controller->IsCommandEnabled(aCommand, &commandEnabled);
NS_ENSURE_SUCCESS_VOID(rv);
if (commandEnabled) {
controller->DoCommand(aCommand);
}
}
nsresult
nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType)
{
@ -320,8 +272,7 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
if (!trustedEvent)
return NS_OK;
bool isEditor;
nsresult rv = EnsureHandlers(&isEditor);
nsresult rv = EnsureHandlers();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> el = GetElement();
@ -343,45 +294,6 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
WalkHandlersInternal(aKeyEvent, aEventType, mHandler);
aKeyEvent->GetDefaultPrevented(&prevent);
if (prevent) {
return NS_OK;
}
// XXX Shouldn't we prefer the native key binding rather than our key
// bindings? I.e., should we call WalkHandlersInternal() after this
// block?
if (isEditor && GetEditorKeyBindings()) {
WidgetKeyboardEvent* keyEvent =
aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
MOZ_ASSERT(keyEvent,
"DOM key event's internal event must be WidgetKeyboardEvent");
bool handled = false;
switch (keyEvent->message) {
case NS_KEY_PRESS:
handled = sNativeEditorBindings->KeyPress(*keyEvent,
DoCommandCallback,
mTarget);
break;
case NS_KEY_UP:
handled = sNativeEditorBindings->KeyUp(*keyEvent,
DoCommandCallback,
mTarget);
break;
case NS_KEY_DOWN:
handled = sNativeEditorBindings->KeyDown(*keyEvent,
DoCommandCallback,
mTarget);
break;
default:
MOZ_CRASH("Unknown key message");
}
if (handled)
aKeyEvent->PreventDefault();
}
return NS_OK;
}
@ -422,23 +334,9 @@ nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler* inHandler,
aIgnoreShiftKey);
}
/* static */ void
nsXBLWindowKeyHandler::ShutDown()
{
NS_IF_RELEASE(sNativeEditorBindings);
}
//
// IsEditor
//
// Determine if the document we're working with is Editor or Browser
//
bool
nsXBLWindowKeyHandler::IsEditor()
nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
{
// XXXndeakin even though this is only used for key events which should be
// going to the focused frame anyway, this doesn't seem like the right way
// to determine if something is an editor.
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm)
return false;
@ -450,12 +348,31 @@ nsXBLWindowKeyHandler::IsEditor()
nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(focusedWindow));
nsIDocShell *docShell = piwin->GetDocShell();
nsCOMPtr<nsIPresShell> presShell;
if (docShell)
presShell = docShell->GetPresShell();
if (!docShell) {
return false;
}
if (presShell) {
return presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
nsCOMPtr<nsIEditor> editor;
docShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) {
return false;
}
nsCOMPtr<nsIDOMElement> focusedElement;
fm->GetFocusedElement(getter_AddRefs(focusedElement));
nsCOMPtr<nsINode> focusedNode = do_QueryInterface(focusedElement);
if (focusedNode) {
// If there is a focused element, make sure it's in the active editing host.
// Note that GetActiveEditingHost finds the current editing host based on
// the document's selection. Even though the document selection is usually
// collapsed to where the focus is, but the page may modify the selection
// without our knowledge, in which case this check will do something useful.
nsCOMPtr<Element> activeEditingHost = htmlEditor->GetActiveEditingHost();
if (!activeEditingHost) {
return false;
}
return nsContentUtils::ContentIsDescendantOf(focusedNode, activeEditingHost);
}
return false;

View File

@ -31,9 +31,6 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
// release globals
static NS_HIDDEN_(void) ShutDown();
protected:
nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
@ -49,15 +46,15 @@ protected:
// lazily load the handlers. Overridden to handle being attached
// to a particular element rather than the document
nsresult EnsureHandlers(bool *aIsEditor);
nsresult EnsureHandlers();
// check if the given handler cares about the given key event
bool EventMatched(nsXBLPrototypeHandler* inHandler, nsIAtom* inEventType,
nsIDOMKeyEvent* inEvent, uint32_t aCharCode,
bool aIgnoreShiftKey);
// are we working with editor or browser?
bool IsEditor() ;
// Is an HTML editable element focused
bool IsHTMLEditableFieldFocused();
// Returns the element which was passed as a parameter to the constructor,
// unless the element has been removed from the document.

View File

@ -17,6 +17,7 @@
#include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::input
#include "nsIClipboard.h" // for nsIClipboard, etc
#include "nsIContent.h" // for nsIContent
#include "nsIController.h" // for nsIController
#include "nsID.h"
#include "nsIDOMDOMStringList.h" // for nsIDOMDOMStringList
#include "nsIDOMDataTransfer.h" // for nsIDOMDataTransfer
@ -35,7 +36,9 @@
#include "nsIEditorMailSupport.h" // for nsIEditorMailSupport
#include "nsIFocusManager.h" // for nsIFocusManager
#include "nsIFormControl.h" // for nsIFormControl, etc
#include "nsIHTMLEditor.h" // for nsIHTMLEditor
#include "nsIMEStateManager.h" // for nsIMEStateManager
#include "nsINativeKeyBindings.h" // for nsINativeKeyBindings
#include "nsINode.h" // for nsINode, ::NODE_IS_EDITABLE, etc
#include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
#include "nsIPresShell.h" // for nsIPresShell
@ -46,6 +49,7 @@
#include "nsISelectionPrivate.h" // for nsISelectionPrivate
#include "nsITransferable.h" // for kFileMime, kHTMLMime, etc
#include "nsLiteralString.h" // for NS_LITERAL_STRING
#include "nsPIWindowRoot.h" // for nsPIWindowRoot
#include "nsServiceManagerUtils.h" // for do_GetService
#include "nsString.h" // for nsAutoString
#ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
@ -58,6 +62,51 @@ class nsPresContext;
using namespace mozilla;
using mozilla::dom::EventTarget;
static nsINativeKeyBindings *sNativeEditorBindings = nullptr;
static nsINativeKeyBindings*
GetEditorKeyBindings()
{
static bool noBindings = false;
if (!sNativeEditorBindings && !noBindings) {
CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor",
&sNativeEditorBindings);
if (!sNativeEditorBindings) {
noBindings = true;
}
}
return sNativeEditorBindings;
}
static void
DoCommandCallback(const char *aCommand, void *aData)
{
nsIDocument* doc = static_cast<nsIDocument*>(aData);
nsPIDOMWindow* win = doc->GetWindow();
if (!win) {
return;
}
nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
if (!root) {
return;
}
nsCOMPtr<nsIController> controller;
root->GetControllerForCommand(aCommand, getter_AddRefs(controller));
if (!controller) {
return;
}
bool commandEnabled;
nsresult rv = controller->IsCommandEnabled(aCommand, &commandEnabled);
NS_ENSURE_SUCCESS_VOID(rv);
if (commandEnabled) {
controller->DoCommand(aCommand);
}
}
nsEditorEventListener::nsEditorEventListener() :
mEditor(nullptr), mCommitText(false),
mInTransaction(false)
@ -77,6 +126,12 @@ nsEditorEventListener::~nsEditorEventListener()
}
}
/* static */ void
nsEditorEventListener::ShutDown()
{
NS_IF_RELEASE(sNativeEditorBindings);
}
nsresult
nsEditorEventListener::Connect(nsEditor* aEditor)
{
@ -449,7 +504,33 @@ nsEditorEventListener::KeyPress(nsIDOMEvent* aKeyEvent)
return NS_OK;
}
return mEditor->HandleKeyPressEvent(keyEvent);
nsresult rv = mEditor->HandleKeyPressEvent(keyEvent);
NS_ENSURE_SUCCESS(rv, rv);
aKeyEvent->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
return NS_OK;
}
if (GetEditorKeyBindings() && ShouldHandleNativeKeyBindings(aKeyEvent)) {
// Now, ask the native key bindings to handle the event.
// XXX Note that we're not passing the keydown/keyup events to the native
// key bindings, which should be OK since those events are only handled on
// Windows for now, where we don't have native key bindings.
WidgetKeyboardEvent* keyEvent =
aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
MOZ_ASSERT(keyEvent,
"DOM key event's internal event must be WidgetKeyboardEvent");
nsCOMPtr<nsIDocument> doc = mEditor->GetDocument();
bool handled = sNativeEditorBindings->KeyPress(*keyEvent,
DoCommandCallback,
doc);
if (handled) {
aKeyEvent->PreventDefault();
}
}
return NS_OK;
}
NS_IMETHODIMP
@ -929,3 +1010,35 @@ nsEditorEventListener::IsFileControlTextBox()
return false;
}
bool
nsEditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMEvent* aKeyEvent)
{
// Only return true if the target of the event is a desendant of the active
// editing host in order to match the similar decision made in
// nsXBLWindowKeyHandler.
// Note that IsAcceptableInputEvent doesn't check for the active editing
// host for keyboard events, otherwise this check would have been
// unnecessary. IsAcceptableInputEvent currently makes a similar check for
// mouse events.
nsCOMPtr<nsIDOMEventTarget> target;
aKeyEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
if (!targetContent) {
return false;
}
nsCOMPtr<nsIHTMLEditor> htmlEditor =
do_QueryInterface(static_cast<nsIEditor*>(mEditor));
if (!htmlEditor) {
return false;
}
nsIContent* editingHost = htmlEditor->GetActiveEditingHost();
if (!editingHost) {
return false;
}
return nsContentUtils::ContentIsDescendantOf(targetContent, editingHost);
}

View File

@ -59,6 +59,8 @@ public:
void SpellCheckIfNeeded();
static NS_HIDDEN_(void) ShutDown();
protected:
nsresult InstallToEditor();
void UninstallFromEditor();
@ -72,6 +74,7 @@ protected:
void CleanupDragDropCaret();
already_AddRefed<nsIPresShell> GetPresShell();
bool IsFileControlTextBox();
bool ShouldHandleNativeKeyBindings(nsIDOMEvent* aKeyEvent);
protected:
nsEditor* mEditor; // weak

View File

@ -65,6 +65,8 @@ support-files =
[test_CF_HTML_clipboard.html]
[test_bug200416.html]
[test_bug289384.html]
skip-if = os != "mac"
[test_bug290026.html]
[test_bug291780.html]
[test_bug316447.html]

View File

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=289384
-->
<head>
<title>Test for Bug 289384</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=289384">Mozilla Bug 289384</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
var win = window.open("data:text/html,<a href=\"data:text/html,<body contenteditable onload='opener.continueTest(window);'>foo bar</body>\">link</a>", "", "test-289384");
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad);
win.document.querySelector("a").click();
}, false);
});
function continueTest(win) {
SimpleTest.waitForFocus(function() {
var doc = win.document;
var sel = win.getSelection();
doc.body.focus();
sel.collapse(doc.body.firstChild, 3);
SimpleTest.executeSoon(function() {
synthesizeKey("VK_LEFT", {accelKey: true}, win);
ok(sel.isCollapsed, "The selection must be collapsed");
is(sel.anchorNode, doc.body.firstChild, "The anchor node should be the body element's text node");
is(sel.anchorOffset, 0, "The anchor offset should be 0");
win.close();
SimpleTest.finish();
});
}, win);
}
</script>
</pre>
</body>
</html>

View File

@ -173,7 +173,7 @@ function runTests()
reset("");
synthesizeKey("VK_BACK_SPACE", { altKey: true });
check(aDescription + "Alt+Backspace", true, true, aIsReadonly);
check(aDescription + "Alt+Backspace", true, true, aIsReadonly || kIsMac);
reset("");
synthesizeKey("VK_BACK_SPACE", { metaKey: true });
@ -189,11 +189,11 @@ function runTests()
// Otherwise, editor doesn't consume the event.
reset("");
synthesizeKey("VK_DELETE", { });
check(aDescription + "Delete", true, true, !aIsReadonly);
check(aDescription + "Delete", true, true, !aIsReadonly || kIsMac);
reset("");
synthesizeKey("VK_DELETE", { shiftKey: true });
check(aDescription + "Shift+Delete", true, true, false);
check(aDescription + "Shift+Delete", true, true, kIsMac);
reset("");
synthesizeKey("VK_DELETE", { ctrlKey: true });
@ -201,7 +201,7 @@ function runTests()
reset("");
synthesizeKey("VK_DELETE", { altKey: true });
check(aDescription + "Alt+Delete", true, true, false);
check(aDescription + "Alt+Delete", true, true, kIsMac);
reset("");
synthesizeKey("VK_DELETE", { metaKey: true });

View File

@ -880,7 +880,7 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& a
/* This function sets the aTransformToApzcOut and aTransformToGeckoOut out-parameters
to some useful transformations that input events may need applied. This is best
illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
is the layer that corresponds to the returned APZC instance, and layer R is the root
is the layer that corresponds to the argument |aApzc|, and layer R is the root
of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
When layer L is displayed to the screen by the compositor, the set of transforms that
are applied to L are (in order from top to bottom):
@ -918,8 +918,15 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& a
Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
out all of the async transforms that are involved in this chain. This is because async
transforms are stored only in the compositor and gecko does not account for them when
doing display-list-based hit-testing for event dispatching. Therefore, given a user input
in screen space, the following transforms need to be applied (in order from top to bottom):
doing display-list-based hit-testing for event dispatching.
Furthermore, because these input events are processed by Gecko in a FIFO queue that
includes other things (specifically paint requests), it is possible that by time the
input event reaches gecko, it will have painted something else. Therefore, we need to
apply another transform to the input events to account for the possible disparity between
what we know gecko last painted and the last paint request we sent to gecko. Let this
transform be represented by LD, MD, ... RD.
Therefore, given a user input in screen space, the following transforms need to be applied
(in order from top to bottom):
RC.Inverse()
RN.Inverse()
RT.Inverse()
@ -930,28 +937,36 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& a
LC.Inverse()
LN.Inverse()
LT.Inverse()
LD
LC
MD
MC
...
RD
RC
This sequence can be simplified and refactored to the following:
aTransformToApzcOut
LT.Inverse()
LD
LC
MD
MC
...
RD
RC
Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToGeckoOut
to the remaining transforms (LT.Inverse() * LC * ... * RC), so that the caller code can
to the remaining transforms (LT.Inverse() * LD * ... * RC), so that the caller code can
combine it with aTransformToApzcOut to get the final transform required in this case.
Note that for many of these layers, there will be no AsyncPanZoomController attached, and
so the async transform will be the identity transform. So, in the example above, if layers
L and P have APZC instances attached, MT, MN, NT, NN, OT, ON, QT, QN, RT and RN will be
identity transforms.
L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
RN and RD will be identity transforms.
Additionally, for space-saving purposes, each APZC instance stores its layer's individual
CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
The APZC instances track the last dispatched paint request and so are able to calculate LD and
PD using those internally stored values.
The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
required can be generated.
*/
@ -978,8 +993,8 @@ APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix&
// aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
aTransformToApzcOut = ancestorUntransform * aApzc->GetCSSTransform().Inverse() * nontransientAsyncTransform.Inverse();
// aTransformToGeckoOut is initialized to LT.Inverse() * LC * MC * NC * OC
aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
// aTransformToGeckoOut is initialized to LT.Inverse() * LD * LC * MC * NC * OC
aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
// ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
@ -991,12 +1006,12 @@ APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix&
// aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
// aTransformToGeckoOut is LT.Inverse() * LC * MC * NC * OC * PC * QC * RC
aTransformToGeckoOut = aTransformToGeckoOut * parent->GetCSSTransform() * parent->GetAncestorTransform();
// aTransformToGeckoOut is LT.Inverse() * LD * LC * MC * NC * OC * PD * PC * QC * RC
aTransformToGeckoOut = aTransformToGeckoOut * parent->GetTransformToLastDispatchedPaint() * parent->GetCSSTransform() * parent->GetAncestorTransform();
// The above values for aTransformToApzcOut and aTransformToGeckoOut when parent == P match
// the required output as explained in the comment above GetTargetAPZC. Note that any missing terms
// are async transforms that are guaranteed to be identity transforms.
// the required output as explained in the comment above this method. Note that any missing
// terms are guaranteed to be identity transforms.
}
}

View File

@ -249,8 +249,13 @@ TextureHost::PrintInfo(nsACString& aTo, const char* aPrefix)
{
aTo += aPrefix;
aTo += nsPrintfCString("%s (0x%p)", Name(), this);
AppendToString(aTo, GetSize(), " [size=", "]");
AppendToString(aTo, GetFormat(), " [format=", "]");
// Note: the TextureHost needs to be locked before it is safe to call
// GetSize() and GetFormat() on it.
if (Lock()) {
AppendToString(aTo, GetSize(), " [size=", "]");
AppendToString(aTo, GetFormat(), " [format=", "]");
Unlock();
}
AppendToString(aTo, mFlags, " [flags=", "]");
}

View File

@ -1338,8 +1338,12 @@ void AsyncPanZoomController::ScheduleComposite() {
}
void AsyncPanZoomController::RequestContentRepaint() {
mFrameMetrics.mDisplayPort =
CalculatePendingDisplayPort(mFrameMetrics,
RequestContentRepaint(mFrameMetrics);
}
void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
aFrameMetrics.mDisplayPort =
CalculatePendingDisplayPort(aFrameMetrics,
GetVelocityVector(),
GetAccelerationVector(),
mPaintThrottler.AverageDuration().ToSeconds());
@ -1348,32 +1352,37 @@ void AsyncPanZoomController::RequestContentRepaint() {
// request since it's a pointless paint.
CSSRect oldDisplayPort = mLastPaintRequestMetrics.mDisplayPort
+ mLastPaintRequestMetrics.mScrollOffset;
CSSRect newDisplayPort = mFrameMetrics.mDisplayPort
+ mFrameMetrics.mScrollOffset;
CSSRect newDisplayPort = aFrameMetrics.mDisplayPort
+ aFrameMetrics.mScrollOffset;
if (fabsf(oldDisplayPort.x - newDisplayPort.x) < EPSILON &&
fabsf(oldDisplayPort.y - newDisplayPort.y) < EPSILON &&
fabsf(oldDisplayPort.width - newDisplayPort.width) < EPSILON &&
fabsf(oldDisplayPort.height - newDisplayPort.height) < EPSILON &&
fabsf(mLastPaintRequestMetrics.mScrollOffset.x -
mFrameMetrics.mScrollOffset.x) < EPSILON &&
aFrameMetrics.mScrollOffset.x) < EPSILON &&
fabsf(mLastPaintRequestMetrics.mScrollOffset.y -
mFrameMetrics.mScrollOffset.y) < EPSILON &&
mFrameMetrics.mZoom == mLastPaintRequestMetrics.mZoom &&
fabsf(mFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON &&
fabsf(mFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) {
aFrameMetrics.mScrollOffset.y) < EPSILON &&
aFrameMetrics.mZoom == mLastPaintRequestMetrics.mZoom &&
fabsf(aFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON &&
fabsf(aFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) {
return;
}
SendAsyncScrollEvent();
ScheduleContentRepaint(mFrameMetrics);
mPaintThrottler.PostTask(
FROM_HERE,
NewRunnableMethod(this,
&AsyncPanZoomController::DispatchRepaintRequest,
aFrameMetrics),
GetFrameTime());
aFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
mLastPaintRequestMetrics = aFrameMetrics;
}
void
AsyncPanZoomController::ScheduleContentRepaint(FrameMetrics &aFrameMetrics) {
// This message is compressed, so fire whether or not we already have a paint
// queued up. We need to know whether or not a paint was requested anyways,
// for the purposes of content calling window.scrollTo().
AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (controller) {
APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
@ -1381,15 +1390,9 @@ AsyncPanZoomController::ScheduleContentRepaint(FrameMetrics &aFrameMetrics) {
LogRendertraceRect(GetGuid(), "requested displayport", "yellow",
aFrameMetrics.mDisplayPort + aFrameMetrics.mScrollOffset);
mPaintThrottler.PostTask(
FROM_HERE,
NewRunnableMethod(controller.get(),
&GeckoContentController::RequestContentRepaint,
aFrameMetrics),
GetFrameTime());
controller->RequestContentRepaint(aFrameMetrics);
mLastDispatchedPaintMetrics = aFrameMetrics;
}
aFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
mLastPaintRequestMetrics = aFrameMetrics;
}
void
@ -1530,6 +1533,14 @@ gfx3DMatrix AsyncPanZoomController::GetNontransientAsyncTransform() {
1.0f);
}
gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
ReentrantMonitorAutoEnter lock(mMonitor);
CSSPoint scrollChange = mLastContentPaintMetrics.mScrollOffset - mLastDispatchedPaintMetrics.mScrollOffset;
float zoomChange = mLastContentPaintMetrics.mZoom.scale / mLastDispatchedPaintMetrics.mZoom.scale;
return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
}
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
ReentrantMonitorAutoEnter lock(mMonitor);
@ -1557,15 +1568,18 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
}
if (aIsFirstPaint || isDefault) {
// Initialize our internal state to something sane when the content
// that was just painted is something we knew nothing about previously
mPaintThrottler.ClearHistory();
mPaintThrottler.SetMaxDurations(gNumPaintDurationSamples);
mX.CancelTouch();
mY.CancelTouch();
SetState(NOTHING);
mFrameMetrics = aLayerMetrics;
mLastDispatchedPaintMetrics = aLayerMetrics;
ShareCompositorFrameMetrics();
SetState(NOTHING);
} else {
// If we're not taking the aLayerMetrics wholesale we still need to pull
// in some things into our local mFrameMetrics because these things are
@ -1697,7 +1711,7 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
// Schedule a repaint now, so the new displayport will be painted before the
// animation finishes.
ScheduleContentRepaint(endZoomToMetrics);
RequestContentRepaint(endZoomToMetrics);
}
}

View File

@ -222,6 +222,15 @@ public:
*/
gfx3DMatrix GetNontransientAsyncTransform();
/**
* Returns the transform to take something from the coordinate space of the
* last thing we know gecko painted, to the coordinate space of the last thing
* we asked gecko to paint. In cases where that last request has not yet been
* processed, this is needed to transform input events properly into a space
* gecko will understand.
*/
gfx3DMatrix GetTransformToLastDispatchedPaint();
/**
* Recalculates the displayport. Ideally, this should paint an area bigger
* than the composite-to dimensions so that when you scroll down, you don't
@ -482,7 +491,12 @@ protected:
* Tell the paint throttler to request a content repaint with the given
* metrics. (Helper function used by RequestContentRepaint.)
*/
void ScheduleContentRepaint(FrameMetrics &aFrameMetrics);
void RequestContentRepaint(FrameMetrics& aFrameMetrics);
/**
* Actually send the next pending paint request to gecko.
*/
void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics);
/**
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
@ -635,6 +649,11 @@ private:
// that we're not requesting a paint of the same thing that's already drawn.
// If we don't do this check, we don't get a ShadowLayersUpdated back.
FrameMetrics mLastPaintRequestMetrics;
// The last metrics that we actually sent to Gecko. This allows us to transform
// inputs into a coordinate space that Gecko knows about. This assumes the pipe
// through which input events and repaint requests are sent to Gecko operates
// in a FIFO manner.
FrameMetrics mLastDispatchedPaintMetrics;
nsTArray<MultiTouchInput> mTouchQueue;

View File

@ -221,6 +221,8 @@ void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t a
EXPECT_EQ(pointOut, ScreenPoint());
EXPECT_EQ(viewTransformOut, ViewTransform());
apzc->Destroy();
}
static void
@ -315,6 +317,8 @@ TEST(AsyncPanZoomController, Pinch) {
EXPECT_EQ(fm.mZoom.scale, 1.0f);
EXPECT_EQ(fm.mScrollOffset.x, 880);
EXPECT_EQ(fm.mScrollOffset.y, 0);
apzc->Destroy();
}
TEST(AsyncPanZoomController, PinchWithTouchActionNone) {
@ -878,9 +882,13 @@ TEST(APZCTreeManager, HitTesting2) {
// of -50 to be set on the root layer.
int time = 0;
// Silence GMock warnings about "uninteresting mock function calls".
EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(1);
EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(AtLeast(1));
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
// This first pan will move the APZC by 50 pixels, and dispatch a paint request.
// Since this paint request is in the queue to Gecko, transformToGecko will
// take it into account.
ApzcPan(apzcroot, manager, time, 100, 50);
// Hit where layers[3] used to be. It should now hit the root.
@ -888,18 +896,44 @@ TEST(APZCTreeManager, HitTesting2) {
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
// but transformToGecko does
EXPECT_EQ(gfxPoint(75, 125), transformToGecko.Transform(gfxPoint(75, 75)));
// and transformToGecko unapplies it and then reapplies it, because by the
// time the event being transformed reaches Gecko the new paint request will
// have been handled.
EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(75, 75)));
// Hit where layers[1] used to be and where layers[3] should now be.
hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko);
EXPECT_EQ(apzc3, hit.get());
// transformToApzc unapplies both layers[2]'s css transform and the root's
// async trasnform
// async transform
EXPECT_EQ(gfxPoint(12.5, 75), transformToApzc.Transform(gfxPoint(25, 25)));
// transformToGecko reapplies the css transform only (since Gecko doesn't
// know about async transforms)
EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(12.5, 75)));
// transformToGecko reapplies both the css transform and the async transform
// because we have already issued a paint request with it.
EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(12.5, 75)));
// This second pan will move the APZC by another 50 pixels but since the paint
// request dispatched above has not "completed", we will not dispatch another
// one yet. Now we have an async transform on top of the pending paint request
// transform.
ApzcPan(apzcroot, manager, time, 100, 50);
// Hit where layers[3] used to be. It should now hit the root.
hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
// transformToGecko unapplies the full async transform of -100 pixels, and then
// reapplies the "D" transform of -50 leading to an overall adjustment of +50
EXPECT_EQ(gfxPoint(75, 125), transformToGecko.Transform(gfxPoint(75, 75)));
// Hit where layers[1] used to be. It should now hit the root.
hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko);
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(gfxPoint(25, 25), transformToApzc.Transform(gfxPoint(25, 25)));
// transformToGecko unapplies the full async transform of -100 pixels, and then
// reapplies the "D" transform of -50 leading to an overall adjustment of +50
EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(25, 25)));
manager->ClearTree();
}

View File

@ -637,7 +637,7 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
PR_LOG(GetImgLog(), PR_LOG_WARNING,
("[this=%p] imgRequest::OnStartRequest -- "
"RetargetDeliveryTo rv %d=%s\n",
this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv));
this, rv, NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
}
return NS_OK;

View File

@ -7,6 +7,7 @@
#ifndef imgRequestProxy_h__
#define imgRequestProxy_h__
#include "mozilla/WeakPtr.h"
#include "imgIRequest.h"
#include "nsISecurityInfoProvider.h"
@ -43,7 +44,8 @@ class ImageURL;
class imgRequestProxy : public imgIRequest,
public nsISupportsPriority,
public nsISecurityInfoProvider,
public nsITimedChannel
public nsITimedChannel,
public mozilla::SupportsWeakPtr<imgRequestProxy>
{
public:
typedef mozilla::image::ImageURL ImageURL;

View File

@ -18,6 +18,7 @@
#include "mozilla/Services.h"
using namespace mozilla::image;
using mozilla::WeakPtr;
class imgStatusTrackerObserver : public imgDecoderObserver
{
@ -143,7 +144,7 @@ public:
}
private:
mozilla::WeakPtr<imgStatusTracker> mTracker;
WeakPtr<imgStatusTracker> mTracker;
};
// imgStatusTracker methods
@ -357,17 +358,17 @@ imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
#define NOTIFY_IMAGE_OBSERVERS(func) \
do { \
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(proxies); \
ProxyArray::ForwardIterator iter(proxies); \
while (iter.HasMore()) { \
nsRefPtr<imgRequestProxy> proxy = iter.GetNext(); \
if (!proxy->NotificationsDeferred()) { \
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \
if (proxy && !proxy->NotificationsDeferred()) { \
proxy->func; \
} \
} \
} while (false);
/* static */ void
imgStatusTracker::SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
imgStatusTracker::SyncNotifyState(ProxyArray& proxies,
bool hasImage, uint32_t state,
nsIntRect& dirtyRect, bool hadLastPart)
{
@ -505,13 +506,13 @@ imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff)
mInvalidRect.SetEmpty();
if (diff.unblockedOnload) {
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
// Hold on to a reference to this proxy, since notifying the state can
// cause it to disappear.
nsRefPtr<imgRequestProxy> proxy = iter.GetNext();
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (!proxy->NotificationsDeferred()) {
if (proxy && !proxy->NotificationsDeferred()) {
SendUnblockOnload(proxy);
}
}
@ -550,8 +551,8 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
r = mImage->FrameRect(imgIContainer::FRAME_CURRENT);
}
nsTObserverArray<imgRequestProxy*> array;
array.AppendElement(proxy);
ProxyArray array;
array.AppendElement(proxy->asWeakPtr());
SyncNotifyState(array, !!mImage, mState, r, mHadLastPart);
}
@ -582,7 +583,7 @@ void
imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer)
{
MOZ_ASSERT(NS_IsMainThread());
mConsumers.AppendElementUnlessExists(aConsumer);
mConsumers.AppendElementUnlessExists(aConsumer->asWeakPtr());
}
// XXX - The last argument should go away.
@ -610,6 +611,20 @@ imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus)
return removed;
}
bool
imgStatusTracker::FirstConsumerIs(imgRequestProxy* aConsumer)
{
MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
return proxy.get() == aConsumer;
}
}
return false;
}
void
imgStatusTracker::RecordCancel()
{
@ -781,9 +796,12 @@ imgStatusTracker::OnUnlockedDraw()
{
MOZ_ASSERT(NS_IsMainThread());
RecordUnlockedDraw();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendUnlockedDraw(iter.GetNext());
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendUnlockedDraw(proxy);
}
}
}
@ -838,9 +856,12 @@ imgStatusTracker::OnStartRequest()
{
MOZ_ASSERT(NS_IsMainThread());
RecordStartRequest();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendStartRequest(iter.GetNext());
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendStartRequest(proxy);
}
}
}
@ -909,9 +930,12 @@ imgStatusTracker::OnStopRequest(bool aLastPart,
RecordStopRequest(aLastPart, aStatus);
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mConsumers);
ProxyArray::ForwardIterator srIter(mConsumers);
while (srIter.HasMore()) {
SendStopRequest(srIter.GetNext(), aLastPart, aStatus);
nsRefPtr<imgRequestProxy> proxy = srIter.GetNext().get();
if (proxy) {
SendStopRequest(proxy, aLastPart, aStatus);
}
}
if (NS_FAILED(aStatus) && !preexistingError) {
@ -926,9 +950,12 @@ imgStatusTracker::OnDiscard()
RecordDiscard();
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendDiscard(iter.GetNext());
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendDiscard(proxy);
}
}
}
@ -939,9 +966,12 @@ imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect)
RecordFrameChanged(aDirtyRect);
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendFrameChanged(iter.GetNext(), aDirtyRect);
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendFrameChanged(proxy, aDirtyRect);
}
}
}
@ -952,9 +982,12 @@ imgStatusTracker::OnStopFrame()
RecordStopFrame();
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendStopFrame(iter.GetNext());
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendStopFrame(proxy);
}
}
}
@ -970,9 +1003,12 @@ imgStatusTracker::OnDataAvailable()
return;
}
// Notify any imgRequestProxys that are observing us that we have an Image.
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
iter.GetNext()->SetHasImage();
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
proxy->SetHasImage();
}
}
}
@ -1021,9 +1057,12 @@ imgStatusTracker::MaybeUnblockOnload()
RecordUnblockOnload();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendUnblockOnload(iter.GetNext());
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy) {
SendUnblockOnload(proxy);
}
}
}

View File

@ -9,7 +9,6 @@
class imgDecoderObserver;
class imgIContainer;
class imgRequestProxy;
class imgStatusNotifyRunnable;
class imgRequestNotifyRunnable;
class imgStatusTrackerObserver;
@ -21,6 +20,7 @@ class nsIRunnable;
#include "nsTObserverArray.h"
#include "nsThreadUtils.h"
#include "nsRect.h"
#include "imgRequestProxy.h"
namespace mozilla {
namespace image {
@ -173,10 +173,7 @@ public:
// This is intentionally non-general because its sole purpose is to support an
// some obscure network priority logic in imgRequest. That stuff could probably
// be improved, but it's too scary to mess with at the moment.
bool FirstConsumerIs(imgRequestProxy* aConsumer) {
MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
return mConsumers.SafeElementAt(0, nullptr) == aConsumer;
}
bool FirstConsumerIs(imgRequestProxy* aConsumer);
void AdoptConsumers(imgStatusTracker* aTracker) {
MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
@ -295,6 +292,7 @@ public:
nsIntRect GetInvalidRect() const { return mInvalidRect; }
private:
typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray;
friend class imgStatusNotifyRunnable;
friend class imgRequestNotifyRunnable;
friend class imgStatusTrackerObserver;
@ -306,7 +304,7 @@ private:
// Main thread only, since imgRequestProxy calls are expected on the main
// thread, and mConsumers is not threadsafe.
static void SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
static void SyncNotifyState(ProxyArray& proxies,
bool hasImage, uint32_t state,
nsIntRect& dirtyRect, bool hadLastPart);
@ -322,7 +320,7 @@ private:
// List of proxies attached to the image. Each proxy represents a consumer
// using the image. Array and/or individual elements should only be accessed
// on the main thread.
nsTObserverArray<imgRequestProxy*> mConsumers;
ProxyArray mConsumers;
mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;

View File

@ -917,6 +917,61 @@ class SkipRoot
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/*
* RootedGeneric<T> allows a class to instantiate its own Rooted type by
* including the following two methods:
*
* static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
* void trace(JSTracer *trc);
*
* The trace() method must trace all of the class's fields.
*
* Implementation:
*
* RootedGeneric<T> works by placing a pointer to its 'rooter' field into the
* usual list of rooters when it is instantiated. When marking, it backs up
* from this pointer to find a vtable containing a type-appropriate trace()
* method.
*/
template <typename GCType>
class JS_PUBLIC_API(RootedGeneric)
{
public:
JS::Rooted<GCType> rooter;
SkipRoot skip;
RootedGeneric(js::ContextFriendFields *cx)
: rooter(cx), skip(cx, rooter.address())
{
}
RootedGeneric(js::ContextFriendFields *cx, const GCType &initial)
: rooter(cx, initial), skip(cx, rooter.address())
{
}
virtual inline void trace(JSTracer *trc);
operator const GCType&() const { return rooter.get(); }
GCType operator->() const { return rooter.get(); }
};
template <typename GCType>
inline void RootedGeneric<GCType>::trace(JSTracer *trc)
{
rooter->trace(trc);
}
// We will instantiate RootedGeneric<void*> in RootMarking.cpp, and MSVC will
// notice that void*s have no trace() method defined on them and complain (even
// though it's never called.) MSVC's complaint is not unreasonable, so
// specialize for void*.
template <>
inline void RootedGeneric<void*>::trace(JSTracer *trc)
{
MOZ_ASSUME_UNREACHABLE("RootedGeneric<void*>::trace()");
}
/* Interface substitute for Rooted<T> which does not root the variable's memory. */
template <typename T>
class FakeRooted : public RootedBase<T>

View File

@ -52,9 +52,9 @@ var gcPointers = {};
text = snarf(gcTypesFile).split("\n");
for (var line of text) {
if (match = /GCThing: (.*)/.exec(line))
if (match = /^GCThing: (.*)/.exec(line))
gcThings[match[1]] = true;
if (match = /GCPointer: (.*)/.exec(line))
if (match = /^GCPointer: (.*)/.exec(line))
gcPointers[match[1]] = true;
}
text = null;

View File

@ -11,38 +11,39 @@ function processCSU(csu, body)
return;
for (var field of body.DataField) {
var type = field.Field.Type;
var fieldName = field.Field.Name[0];
if (type.Kind == "Pointer") {
var target = type.Type;
if (target.Kind == "CSU")
addNestedPointer(csu, target.Name);
addNestedPointer(csu, target.Name, fieldName);
}
if (type.Kind == "CSU") {
// Ignore nesting in classes which are AutoGCRooters. We only consider
// types with fields that may not be properly rooted.
if (type.Name == "JS::AutoGCRooter" || type.Name == "JS::CustomAutoRooter")
return;
addNestedStructure(csu, type.Name);
addNestedStructure(csu, type.Name, fieldName);
}
}
}
function addNestedStructure(csu, inner)
var structureParents = {}; // Map from field => list of <parent, fieldName>
var pointerParents = {}; // Map from field => list of <parent, fieldName>
function addNestedStructure(csu, inner, field)
{
if (!(inner in structureParents))
structureParents[inner] = [];
structureParents[inner].push(csu);
structureParents[inner].push([ csu, field ]);
}
function addNestedPointer(csu, inner)
function addNestedPointer(csu, inner, field)
{
if (!(inner in pointerParents))
pointerParents[inner] = [];
pointerParents[inner].push(csu);
pointerParents[inner].push([ csu, field ]);
}
var structureParents = {};
var pointerParents = {};
var xdb = xdbLibrary();
xdb.open("src_comp.xdb");
@ -60,32 +61,66 @@ for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
xdb.free_string(data);
}
function addGCType(name)
var gcTypes = {}; // map from parent struct => Set of GC typed children
var gcPointers = {}; // map from parent struct => Set of GC typed children
var gcFields = {};
function addGCType(name, child, why)
{
if (!why)
why = '<annotation>';
if (!child)
child = 'annotation';
if (isRootedTypeName(name))
return;
print("GCThing: " + name);
if (!(name in gcTypes))
gcTypes[name] = Set();
gcTypes[name].add(why);
if (!(name in gcFields))
gcFields[name] = Map();
gcFields[name].set(why, child);
if (name in structureParents) {
for (var holder of structureParents[name])
addGCType(holder);
for (var field of structureParents[name]) {
var [ holder, fieldName ] = field;
addGCType(holder, name, fieldName);
}
}
if (name in pointerParents) {
for (var holder of pointerParents[name])
addGCPointer(holder);
for (var field of pointerParents[name]) {
var [ holder, fieldName ] = field;
addGCPointer(holder, name, fieldName);
}
}
}
function addGCPointer(name)
function addGCPointer(name, child, why)
{
// Ignore types which are properly rooted.
if (!why)
why = '<annotation>';
if (!child)
child = 'annotation';
// Ignore types that are properly rooted.
if (isRootedPointerTypeName(name))
return;
print("GCPointer: " + name);
if (!(name in gcPointers))
gcPointers[name] = Set();
gcPointers[name].add(why);
if (!(name in gcFields))
gcFields[name] = Map();
gcFields[name].set(why, child);
if (name in structureParents) {
for (var holder of structureParents[name])
addGCPointer(holder);
for (var field of structureParents[name]) {
var [ holder, fieldName ] = field;
addGCPointer(holder, name, fieldName);
}
}
}
@ -98,3 +133,32 @@ addGCType('js::LazyScript');
addGCType('js::ion::IonCode');
addGCPointer('JS::Value');
addGCPointer('jsid');
function explain(csu, indent, seen) {
if (!seen)
seen = Set();
seen.add(csu);
if (!(csu in gcFields))
return;
if (gcFields[csu].has('<annotation>')) {
print(indent + "because I said so");
return;
}
for (var [ field, child ] of gcFields[csu]) {
var inherit = "";
if (field == "field:0")
inherit = " (probably via inheritance)";
print(indent + "contains field '" + field + "' of type " + child + inherit);
if (!seen.has(child))
explain(child, indent + " ", seen);
}
}
for (var csu in gcTypes) {
print("GCThing: " + csu);
explain(csu, " ");
}
for (var csu in gcPointers) {
print("GCPointer: " + csu);
explain(csu, " ");
}

View File

@ -314,6 +314,12 @@ EmitBackPatchOp(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t *lastp)
return EmitJump(cx, bce, JSOP_BACKPATCH, delta);
}
static inline unsigned
LengthOfSetLine(unsigned line)
{
return 1 /* SN_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
}
/* Updates line number notes, not column notes. */
static inline bool
UpdateLineNumberNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offset)
@ -336,7 +342,7 @@ UpdateLineNumberNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offse
*/
bce->current->currentLine = line;
bce->current->lastColumn = 0;
if (delta >= (unsigned)(2 + ((line > SN_3BYTE_OFFSET_MASK)<<1))) {
if (delta >= LengthOfSetLine(line)) {
if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)line) < 0)
return false;
} else {
@ -6712,8 +6718,8 @@ SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, uns
JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
for (sn++; which; sn++, which--) {
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn += 2;
if (*sn & SN_4BYTE_OFFSET_FLAG)
sn += 3;
}
/*
@ -6721,55 +6727,58 @@ SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, uns
* the offset has already been inflated (in which case, we need to stay big
* to not break the srcnote encoding if this isn't the last srcnote).
*/
if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) {
if (offset > (ptrdiff_t)SN_4BYTE_OFFSET_MASK || (*sn & SN_4BYTE_OFFSET_FLAG)) {
/* Maybe this offset was already set to a three-byte value. */
if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
if (!(*sn & SN_4BYTE_OFFSET_FLAG)) {
/* Insert two dummy bytes that will be overwritten shortly. */
jssrcnote dummy = 0;
if (!(sn = notes.insert(sn, dummy)) ||
!(sn = notes.insert(sn, dummy)) ||
!(sn = notes.insert(sn, dummy)))
{
js_ReportOutOfMemory(cx);
return false;
}
}
*sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
*sn++ = (jssrcnote)(SN_4BYTE_OFFSET_FLAG | (offset >> 24));
*sn++ = (jssrcnote)(offset >> 16);
*sn++ = (jssrcnote)(offset >> 8);
}
*sn = (jssrcnote)offset;
return true;
}
#ifdef DEBUG_notme
#define DEBUG_srcnotesize
#endif
#ifdef DEBUG_srcnotesize
#define NBINS 10
static uint32_t hist[NBINS];
static void
DumpSrcNoteSizeHist()
/*
* Finish taking source notes in cx's notePool, copying final notes to the new
* stable store allocated by the caller and passed in via notes. Return false
* on malloc failure, which means this function reported an error.
*
* Use this to compute the number of jssrcnotes to allocate and pass in via
* notes. This method knows a lot about details of FinishTakingSrcNotes, so
* DON'T CHANGE js::frontend::FinishTakingSrcNotes WITHOUT CHECKING WHETHER
* THIS METHOD NEEDS CORRESPONDING CHANGES!
*/
ptrdiff_t
BytecodeEmitter::countFinalSourceNotes()
{
static FILE *fp;
int i, n;
if (!fp) {
fp = fopen("/tmp/srcnotes.hist", "w");
if (!fp)
return;
setvbuf(fp, nullptr, _IONBF, 0);
ptrdiff_t diff = prologOffset() - prolog.lastNoteOffset;
ptrdiff_t cnt = prolog.notes.length() + main.notes.length() + 1;
if (prolog.notes.length() && prolog.currentLine != firstLine) {
if (diff > SN_DELTA_MASK)
cnt += JS_HOWMANY(diff - SN_DELTA_MASK, SN_XDELTA_MASK);
cnt += LengthOfSetLine(firstLine);
} else if (diff > 0) {
if (main.notes.length()) {
jssrcnote *sn = main.notes.begin();
diff -= SN_IS_XDELTA(sn)
? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
: SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
}
if (diff > 0)
cnt += JS_HOWMANY(diff, SN_XDELTA_MASK);
}
fprintf(fp, "SrcNote size histogram:\n");
for (i = 0; i < NBINS; i++) {
fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
fputc('*', fp);
fputc('\n', fp);
}
fputc('\n', fp);
return cnt;
}
#endif
/*
* Fill in the storage at notes with prolog and main srcnotes; the space at
@ -7032,8 +7041,8 @@ js_SrcNoteLength(jssrcnote *sn)
arity = SrcNoteArity(sn);
for (base = sn++; arity; sn++, arity--) {
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn += 2;
if (*sn & SN_4BYTE_OFFSET_FLAG)
sn += 3;
}
return sn - base;
}
@ -7045,13 +7054,14 @@ js_GetSrcNoteOffset(jssrcnote *sn, unsigned which)
JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
JS_ASSERT((int) which < SrcNoteArity(sn));
for (sn++; which; sn++, which--) {
if (*sn & SN_3BYTE_OFFSET_FLAG)
sn += 2;
if (*sn & SN_4BYTE_OFFSET_FLAG)
sn += 3;
}
if (*sn & SN_3BYTE_OFFSET_FLAG) {
return (ptrdiff_t)(((uint32_t)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
| (sn[1] << 8)
| sn[2]);
if (*sn & SN_4BYTE_OFFSET_FLAG) {
return (ptrdiff_t)(((uint32_t)(sn[0] & SN_4BYTE_OFFSET_MASK) << 24)
| (sn[1] << 16)
| (sn[2] << 8)
| sn[3]);
}
return (ptrdiff_t)*sn;
}

View File

@ -216,7 +216,7 @@ struct BytecodeEmitter
unsigned currentLine() const { return current->currentLine; }
unsigned lastColumn() const { return current->lastColumn; }
inline ptrdiff_t countFinalSourceNotes();
ptrdiff_t countFinalSourceNotes();
bool reportError(ParseNode *pn, unsigned errorNumber, ...);
bool reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...);
@ -282,38 +282,6 @@ AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptr
bool
FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *notes);
/*
* Finish taking source notes in cx's notePool, copying final notes to the new
* stable store allocated by the caller and passed in via notes. Return false
* on malloc failure, which means this function reported an error.
*
* Use this to compute the number of jssrcnotes to allocate and pass in via
* notes. This method knows a lot about details of FinishTakingSrcNotes, so
* DON'T CHANGE js::frontend::FinishTakingSrcNotes WITHOUT CHECKING WHETHER
* THIS METHOD NEEDS CORRESPONDING CHANGES!
*/
inline ptrdiff_t
BytecodeEmitter::countFinalSourceNotes()
{
ptrdiff_t diff = prologOffset() - prolog.lastNoteOffset;
ptrdiff_t cnt = prolog.notes.length() + main.notes.length() + 1;
if (prolog.notes.length() && prolog.currentLine != firstLine) {
if (diff > SN_DELTA_MASK)
cnt += JS_HOWMANY(diff - SN_DELTA_MASK, SN_XDELTA_MASK);
cnt += 2 + ((firstLine > SN_3BYTE_OFFSET_MASK) << 1);
} else if (diff > 0) {
if (main.notes.length()) {
jssrcnote *sn = main.notes.begin();
diff -= SN_IS_XDELTA(sn)
? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
: SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
}
if (diff > 0)
cnt += JS_HOWMANY(diff, SN_XDELTA_MASK);
}
return cnt;
}
} /* namespace frontend */
} /* namespace js */

View File

@ -130,11 +130,11 @@ SN_IS_TERMINATOR(jssrcnote *sn)
/*
* Offset fields follow certain notes and are frequency-encoded: an offset in
* [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and
* [0,0x7f] consumes one byte, an offset in [0x80,0x7fffffff] takes four, and
* the high bit of the first byte is set.
*/
#define SN_3BYTE_OFFSET_FLAG 0x80
#define SN_3BYTE_OFFSET_MASK 0x7f
#define SN_4BYTE_OFFSET_FLAG 0x80
#define SN_4BYTE_OFFSET_MASK 0x7f
/*
* Negative SRC_COLSPAN offsets are rare, but can arise with for(;;) loops and
@ -143,13 +143,13 @@ SN_IS_TERMINATOR(jssrcnote *sn)
* failures are bugs to fix.
*
* Source note offsets in general must be non-negative and less than 0x800000,
* per the above SN_3BYTE_* definitions. To encode negative colspans, we bias
* per the above SN_4BYTE_* definitions. To encode negative colspans, we bias
* them by the offset domain size and restrict non-negative colspans to less
* than half this domain.
*/
#define SN_COLSPAN_DOMAIN ptrdiff_t(SN_3BYTE_OFFSET_FLAG << 16)
#define SN_COLSPAN_DOMAIN ptrdiff_t(1 << 23)
#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16) - 1)
#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_4BYTE_OFFSET_FLAG << 24) - 1)
#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \
: js_SrcNoteLength(sn))

View File

@ -65,6 +65,13 @@ MarkExactStackRoot(JSTracer *trc, Rooted<void*> *rooter, ThingRootKind kind)
case THING_ROOT_PROPERTY_ID: MarkIdRoot(trc, &((js::PropertyId *)addr)->asId(), "exact-propertyid"); break;
case THING_ROOT_BINDINGS: ((Bindings *)addr)->trace(trc); break;
case THING_ROOT_PROPERTY_DESCRIPTOR: ((JSPropertyDescriptor *)addr)->trace(trc); break;
case THING_ROOT_CUSTOM: {
// 'rooter' is a member within a class containing a vtable. Back up
// to the vtable and call trace() through it.
const size_t rooterOffset = offsetof(RootedGeneric<void*>, rooter);
reinterpret_cast< RootedGeneric<void*>* >(uintptr_t(rooter) - rooterOffset)->trace(trc);
break;
}
default: MOZ_ASSUME_UNREACHABLE("Invalid THING_ROOT kind"); break;
}
}
@ -593,11 +600,11 @@ AutoHashableValueRooter::trace(JSTracer *trc)
}
void
StackShape::AutoRooter::trace(JSTracer *trc)
StackShape::trace(JSTracer *trc)
{
if (shape->base)
MarkBaseShapeRoot(trc, (BaseShape**) &shape->base, "StackShape::AutoRooter base");
MarkIdRoot(trc, (jsid*) &shape->propid, "StackShape::AutoRooter id");
if (base)
MarkBaseShapeRoot(trc, (BaseShape**) &base, "StackShape base");
MarkIdRoot(trc, (jsid*) &propid, "StackShape id");
}
void

View File

@ -0,0 +1,17 @@
// Test offThreadCompileScript option handling.
offThreadCompileScript('Error()');
assertEq(!!runOffThreadScript().stack.match(/^@<string>:1\n/), true);
offThreadCompileScript('Error()',
{ fileName: "candelabra", lineNumber: 6502 });
assertEq(!!runOffThreadScript().stack.match(/^@candelabra:6502\n/), true);
var element = {};
offThreadCompileScript('Error()', { element: element }); // shouldn't crash
runOffThreadScript();
var elementAttributeName = "molybdenum";
elementAttributeName += elementAttributeName + elementAttributeName + elementAttributeName;
offThreadCompileScript('Error()', { elementAttributeName: elementAttributeName }); // shouldn't crash
runOffThreadScript();

View File

@ -0,0 +1,5 @@
var g = newGlobal();
var dbg = new Debugger(g);
try { g.eval('function drag(ev) {'); } catch (ex) { }
for (s of dbg.findScripts())
s.lineCount;

View File

@ -0,0 +1,20 @@
// Debugger.Script.prototype.script returns the global the script runs in.
var g = newGlobal();
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
var log = '';
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.global, gw);
}
g.eval('debugger;');
assertEq(log, 'd');
g.eval('function f() { debugger; }');
g.f();
assertEq(log, 'dd');
assertEq(gw.getOwnPropertyDescriptor('f').value.global, gw);

View File

@ -0,0 +1,40 @@
// Debugger.Script.prototype.script returns the global the script runs in.
// Multi-global version.
var dbg = new Debugger;
var g1 = newGlobal();
var g1w = dbg.addDebuggee(g1);
var g2 = newGlobal();
var g2w = dbg.addDebuggee(g2);
var g3 = newGlobal();
var g3w = dbg.addDebuggee(g3);
var log = '';
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.global, g1w);
assertEq(frame.older.script.global, g2w);
assertEq(frame.older.older.script.global, g3w);
assertEq(frame.older.older.older.script.global, g1w);
}
g1.eval('function f() { debugger; }');
g2.g1 = g1;
g2.eval('function g() { g1.f(); }');
g3.g2 = g2;
g3.eval('function h() { g2.g(); }');
g1.g3 = g3;
g1.eval('function i() { g3.h(); }');
g1.i();
assertEq(log, 'd');
assertEq(g1w.getOwnPropertyDescriptor('f').value.global, g1w);
assertEq(g2w.getOwnPropertyDescriptor('g').value.global, g2w);
assertEq(g3w.getOwnPropertyDescriptor('h').value.global, g3w);

View File

@ -0,0 +1,6 @@
// Specifying an owning element in a cross-global evaluation shouldn't crash.
// That is, when 'evaluate' switches compartments, it should properly wrap
// the CompileOptions members that will become cross-compartment
// references.
evaluate('42 + 1729', { global: newGlobal(), element: {} });

View File

@ -0,0 +1,11 @@
// Source.prototype.elementAttributeName can be a string or undefined.
var g = newGlobal('new-compartment');
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
g.evaluate("function f(x) { return 2*x; }", {elementAttributeName: "src"});
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.elementAttributeName, "src");
g.evaluate("function f(x) { return 2*x; }");
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.elementAttributeName, undefined);

View File

@ -1,11 +0,0 @@
// Source.prototype.elementProperty can be a string or undefined.
var g = newGlobal('new-compartment');
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
g.evaluate("function f(x) { return 2*x; }", {elementProperty: "src"});
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.elementProperty, "src");
g.evaluate("function f(x) { return 2*x; }");
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.elementProperty, undefined);

View File

@ -23,11 +23,11 @@ assertThrowsInstanceOf(function () {
}, TypeError);
assertThrowsInstanceOf(function () {
Debugger.Source.prototype.elementProperty.call(42)
Debugger.Source.prototype.elementAttributeName.call(42)
}, TypeError);
assertThrowsInstanceOf(function () {
Debugger.Source.prototype.elementProperty.call({})
Debugger.Source.prototype.elementAttributeName.call({})
}, TypeError);
assertThrowsInstanceOf(function () {
Debugger.Source.prototype.elementProperty.call(Debugger.Source.prototype)
Debugger.Source.prototype.elementAttributeName.call(Debugger.Source.prototype)
}, TypeError);

View File

@ -4100,6 +4100,8 @@ IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, Boo
case InliningDecision_Inline:
inlineable = true;
break;
default:
MOZ_ASSUME_UNREACHABLE("Unhandled InliningDecision value!");
}
// Enforce a maximum inlined bytecode limit at the callsite.

View File

@ -4309,7 +4309,7 @@ JS::OwningCompileOptions::OwningCompileOptions(JSContext *cx)
: ReadOnlyCompileOptions(),
runtime(GetRuntime(cx)),
elementRoot(cx),
elementPropertyRoot(cx)
elementAttributeNameRoot(cx)
{
}
@ -4339,7 +4339,7 @@ JS::OwningCompileOptions::copy(JSContext *cx, const ReadOnlyCompileOptions &rhs)
}
bool
JS::OwningCompileOptions::setFileAndLine(JSContext *cx, const char *f, unsigned l)
JS::OwningCompileOptions::setFile(JSContext *cx, const char *f)
{
char *copy = nullptr;
if (f) {
@ -4352,6 +4352,15 @@ JS::OwningCompileOptions::setFileAndLine(JSContext *cx, const char *f, unsigned
js_free(const_cast<char *>(filename_));
filename_ = copy;
return true;
}
bool
JS::OwningCompileOptions::setFileAndLine(JSContext *cx, const char *f, unsigned l)
{
if (!setFile(cx, f))
return false;
lineno = l;
return true;
}
@ -4373,8 +4382,20 @@ JS::OwningCompileOptions::setSourceMapURL(JSContext *cx, const jschar *s)
return true;
}
bool
JS::OwningCompileOptions::wrap(JSContext *cx, JSCompartment *compartment)
{
if (!compartment->wrap(cx, &elementRoot))
return false;
if (elementAttributeNameRoot) {
if (!compartment->wrap(cx, elementAttributeNameRoot.address()))
return false;
}
return true;
}
JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
: ReadOnlyCompileOptions(), elementRoot(cx), elementPropertyRoot(cx)
: ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx)
{
this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
@ -4386,6 +4407,18 @@ JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
asmJSOption = cx->options().asmJS();
}
bool
JS::CompileOptions::wrap(JSContext *cx, JSCompartment *compartment)
{
if (!compartment->wrap(cx, &elementRoot))
return false;
if (elementAttributeNameRoot) {
if (!compartment->wrap(cx, elementAttributeNameRoot.address()))
return false;
}
return true;
}
JSScript *
JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
const jschar *chars, size_t length)

View File

@ -3482,7 +3482,7 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
const char *filename() const { return filename_; }
const jschar *sourceMapURL() const { return sourceMapURL_; }
virtual JSObject *element() const = 0;
virtual JSString *elementProperty() const = 0;
virtual JSString *elementAttributeName() const = 0;
// POD options.
JSVersion version;
@ -3506,6 +3506,10 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
SAVE_SOURCE
} sourcePolicy;
// Wrap any compilation option values that need it as appropriate for
// use from |compartment|.
virtual bool wrap(JSContext *cx, JSCompartment *compartment) = 0;
private:
static JSObject * const nullObjectPtr;
void operator=(const ReadOnlyCompileOptions &) MOZ_DELETE;
@ -3528,7 +3532,7 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
{
JSRuntime *runtime;
PersistentRootedObject elementRoot;
PersistentRootedString elementPropertyRoot;
PersistentRootedString elementAttributeNameRoot;
public:
// A minimal constructor, for use with OwningCompileOptions::copy. This
@ -3539,18 +3543,26 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
~OwningCompileOptions();
JSObject *element() const MOZ_OVERRIDE { return elementRoot; }
JSString *elementProperty() const MOZ_OVERRIDE { return elementPropertyRoot; }
JSString *elementAttributeName() const MOZ_OVERRIDE { return elementAttributeNameRoot; }
// Set this to a copy of |rhs|. Return false on OOM.
bool copy(JSContext *cx, const ReadOnlyCompileOptions &rhs);
/* These setters make copies of their string arguments, and are fallible. */
bool setFile(JSContext *cx, const char *f);
bool setFileAndLine(JSContext *cx, const char *f, unsigned l);
bool setSourceMapURL(JSContext *cx, const jschar *s);
/* These setters are infallible, and can be chained. */
OwningCompileOptions &setElement(JSObject *e) { elementRoot = e; return *this; }
OwningCompileOptions &setElementProperty(JSString *p) { elementPropertyRoot = p; return *this; }
OwningCompileOptions &setLine(unsigned l) { lineno = l; return *this; }
OwningCompileOptions &setElement(JSObject *e) {
elementRoot = e;
return *this;
}
OwningCompileOptions &setElementAttributeName(JSString *p) {
elementAttributeNameRoot = p;
return *this;
}
OwningCompileOptions &setPrincipals(JSPrincipals *p) {
if (p) JS_HoldPrincipals(p);
if (principals_) JS_DropPrincipals(runtime, principals_);
@ -3576,6 +3588,8 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
};
/*
@ -3588,12 +3602,12 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOptions
{
RootedObject elementRoot;
RootedString elementPropertyRoot;
RootedString elementAttributeNameRoot;
public:
explicit CompileOptions(JSContext *cx, JSVersion version = JSVERSION_UNKNOWN);
CompileOptions(js::ContextFriendFields *cx, const ReadOnlyCompileOptions &rhs)
: ReadOnlyCompileOptions(), elementRoot(cx), elementPropertyRoot(cx)
: ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx)
{
copyPODOptions(rhs);
@ -3602,19 +3616,27 @@ class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOpti
filename_ = rhs.filename();
sourceMapURL_ = rhs.sourceMapURL();
elementRoot = rhs.element();
elementPropertyRoot = rhs.elementProperty();
elementAttributeNameRoot = rhs.elementAttributeName();
}
JSObject *element() const MOZ_OVERRIDE { return elementRoot; }
JSString *elementProperty() const MOZ_OVERRIDE { return elementPropertyRoot; }
JSString *elementAttributeName() const MOZ_OVERRIDE { return elementAttributeNameRoot; }
CompileOptions &setFile(const char *f) { filename_ = f; return *this; }
CompileOptions &setLine(unsigned l) { lineno = l; return *this; }
CompileOptions &setFileAndLine(const char *f, unsigned l) {
filename_ = f; lineno = l; return *this;
}
CompileOptions &setSourceMapURL(const jschar *s) { sourceMapURL_ = s; return *this; }
CompileOptions &setElement(JSObject *e) { elementRoot = e; return *this; }
CompileOptions &setElementProperty(JSString *p) { elementPropertyRoot = p; return *this; }
CompileOptions &setPrincipals(JSPrincipals *p) { principals_ = p; return *this; }
CompileOptions &setElementAttributeName(JSString *p) {
elementAttributeNameRoot = p;
return *this;
}
CompileOptions &setPrincipals(JSPrincipals *p) {
principals_ = p;
return *this;
}
CompileOptions &setOriginPrincipals(JSPrincipals *p) {
originPrincipals_ = p;
return *this;
@ -3632,6 +3654,8 @@ class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOpti
CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
};
extern JS_PUBLIC_API(JSScript *)

View File

@ -1130,14 +1130,14 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
Reverse(shapes.begin(), shapes.end());
for (size_t i = 0; i < shapes.length(); i++) {
StackShape child(shapes[i]);
StackShape::AutoRooter rooter(cx, &child);
child.attrs |= getSealedOrFrozenAttributes(child.attrs, it);
StackShape unrootedChild(shapes[i]);
RootedGeneric<StackShape*> child(cx, &unrootedChild);
child->attrs |= getSealedOrFrozenAttributes(child->attrs, it);
if (!JSID_IS_EMPTY(child.propid) && it == FREEZE)
MarkTypePropertyNonWritable(cx, obj, child.propid);
if (!JSID_IS_EMPTY(child->propid) && it == FREEZE)
MarkTypePropertyNonWritable(cx, obj, child->propid);
last = cx->compartment()->propertyTree.getChild(cx, last, child);
last = cx->compartment()->propertyTree.getChild(cx, last, *child);
if (!last)
return false;
}

View File

@ -187,8 +187,8 @@ SET_UINT32_INDEX(jsbytecode *pc, uint32_t index)
(pc)[3] = (jsbytecode)(uint32_t(i) >> 8), \
(pc)[4] = (jsbytecode)uint32_t(i))
/* Index limit is determined by SN_3BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */
#define INDEX_LIMIT_LOG2 23
/* Index limit is determined by SN_4BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */
#define INDEX_LIMIT_LOG2 31
#define INDEX_LIMIT (uint32_t(1) << INDEX_LIMIT_LOG2)
#define ARGC_HI(argc) UINT16_HI(argc)

View File

@ -126,7 +126,7 @@ Shape::removeChild(Shape *child)
}
Shape *
PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, const StackShape &child)
PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, StackShape &unrootedChild)
{
RootedShape parent(cx, parentArg);
JS_ASSERT(parent);
@ -144,11 +144,11 @@ PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, const StackShape
KidsPointer *kidp = &parent->kids;
if (kidp->isShape()) {
Shape *kid = kidp->toShape();
if (kid->matches(child))
existingShape = kid;
if (kid->matches(unrootedChild))
existingShape = kid;
} else if (kidp->isHash()) {
if (KidsHash::Ptr p = kidp->toHash()->lookup(child))
existingShape = *p;
if (KidsHash::Ptr p = kidp->toHash()->lookup(unrootedChild))
existingShape = *p;
} else {
/* If kidp->isNull(), we always insert. */
}
@ -181,13 +181,13 @@ PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, const StackShape
if (existingShape)
return existingShape;
StackShape::AutoRooter childRoot(cx, &child);
RootedGeneric<StackShape*> child(cx, &unrootedChild);
Shape *shape = newShape(cx);
if (!shape)
return nullptr;
new (shape) Shape(child, parent->numFixedSlots());
new (shape) Shape(*child, parent->numFixedSlots());
if (!insertChild(cx, parent, shape))
return nullptr;

View File

@ -97,7 +97,7 @@ class PropertyTree
JSCompartment *compartment() { return compartment_; }
Shape *newShape(ExclusiveContext *cx);
Shape *getChild(ExclusiveContext *cx, Shape *parent, const StackShape &child);
Shape *getChild(ExclusiveContext *cx, Shape *parent, StackShape &child);
Shape *lookupChild(ThreadSafeContext *cx, Shape *parent, const StackShape &child);
};

View File

@ -310,6 +310,7 @@ enum ThingRootKind
THING_ROOT_TYPE,
THING_ROOT_BINDINGS,
THING_ROOT_PROPERTY_DESCRIPTOR,
THING_ROOT_CUSTOM,
THING_ROOT_LIMIT
};

View File

@ -574,7 +574,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
/*
* We use this CompileOptions only to initialize the
* ScriptSourceObject. Most CompileOptions fields aren't used by
* ScriptSourceObject, and those that are (element; elementProperty)
* ScriptSourceObject, and those that are (element; elementAttributeName)
* aren't preserved by XDR. So this can be simple.
*/
CompileOptions options(cx);
@ -990,8 +990,15 @@ ScriptSourceObject::element() const
return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
}
void
ScriptSourceObject::initElement(HandleObject element)
{
JS_ASSERT(getReservedSlot(ELEMENT_SLOT).isNull());
setReservedSlot(ELEMENT_SLOT, ObjectOrNullValue(element));
}
const Value &
ScriptSourceObject::elementProperty() const
ScriptSourceObject::elementAttributeName() const
{
const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT);
JS_ASSERT(prop.isUndefined() || prop.isString());
@ -1031,8 +1038,8 @@ ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source,
source->incref();
sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source));
sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element()));
if (options.elementProperty())
sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementProperty()));
if (options.elementAttributeName())
sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName()));
else
sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue());
@ -2281,16 +2288,13 @@ js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp)
return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp);
}
/* The line number limit is the same as the jssrcnote offset limit. */
#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
jsbytecode *
js_LineNumberToPC(JSScript *script, unsigned target)
{
ptrdiff_t offset = 0;
ptrdiff_t best = -1;
unsigned lineno = script->lineno();
unsigned bestdiff = SN_LINE_LIMIT;
unsigned bestdiff = SN_MAX_OFFSET;
for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
/*
* Exact-match only if offset is not in the prolog; otherwise use

View File

@ -497,7 +497,8 @@ class ScriptSourceObject : public JSObject
void setSource(ScriptSource *source);
JSObject *element() const;
const Value &elementProperty() const;
void initElement(HandleObject element);
const Value &elementAttributeName() const;
private:
static const uint32_t SOURCE_SLOT = 0;

View File

@ -190,22 +190,24 @@ ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSC
const jschar *chars, size_t length, JSObject *scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData)
: cx(cx), options(initCx), chars(chars), length(length),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(scopeChain),
exclusiveContextGlobal(exclusiveContextGlobal), callback(callback),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain),
exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx), callback(callback),
callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false)
{
JSRuntime *rt = scopeChain->runtimeFromMainThread();
if (!AddObjectRoot(rt, &this->scopeChain, "ParseTask::scopeChain"))
MOZ_CRASH();
if (!AddObjectRoot(rt, &this->exclusiveContextGlobal, "ParseTask::exclusiveContextGlobal"))
MOZ_CRASH();
}
bool
ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
{
return this->options.copy(cx, options);
if (!this->options.copy(cx, options))
return false;
// Save those compilation options that the ScriptSourceObject can't
// point at while it's in the compilation's temporary compartment.
optionsElement = this->options.element();
this->options.setElement(nullptr);
return true;
}
void
@ -215,13 +217,19 @@ ParseTask::activate(JSRuntime *rt)
cx->enterCompartment(exclusiveContextGlobal->compartment());
}
void
ParseTask::finish()
{
if (script) {
// Initialize the ScriptSourceObject slots that we couldn't while the SSO
// was in the temporary compartment.
ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
sso.initElement(optionsElement);
}
}
ParseTask::~ParseTask()
{
JSRuntime *rt = scopeChain->runtimeFromMainThread();
JS_RemoveObjectRootRT(rt, &scopeChain);
JS_RemoveObjectRootRT(rt, &exclusiveContextGlobal);
// ParseTask takes over ownership of its input exclusive context.
js_delete(cx);
@ -651,6 +659,7 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
// Move the parsed script and all its contents into the desired compartment.
gc::MergeCompartments(parseTask->cx->compartment(), parseTask->scopeChain->compartment());
parseTask->finish();
RootedScript script(rt, parseTask->script);

View File

@ -342,10 +342,17 @@ struct ParseTask
// Rooted pointer to the scope in the target compartment which the
// resulting script will be merged into. This is not safe to use off the
// main thread.
JSObject *scopeChain;
PersistentRootedObject scopeChain;
// Rooted pointer to the global object used by 'cx'.
JSObject *exclusiveContextGlobal;
PersistentRootedObject exclusiveContextGlobal;
// Saved GC-managed CompileOptions fields that will populate slots in
// the ScriptSourceObject. We create the ScriptSourceObject in the
// compilation's temporary compartment, so storing these values there
// at that point would create cross-compartment references. Instead we
// hold them here, and install them after merging the compartments.
PersistentRootedObject optionsElement;
// Callback invoked off the main thread when the parse finishes.
JS::OffThreadCompileCallback callback;
@ -367,6 +374,7 @@ struct ParseTask
bool init(JSContext *cx, const ReadOnlyCompileOptions &options);
void activate(JSRuntime *rt);
void finish();
~ParseTask();
};

View File

@ -765,6 +765,87 @@ LoadScriptRelativeToScript(JSContext *cx, unsigned argc, jsval *vp)
return LoadScript(cx, argc, vp, true);
}
// Populate |options| with the options given by |opts|'s properties. If we
// need to convert a filename to a C string, let fileNameBytes own the
// bytes.
static bool
ParseCompileOptions(JSContext *cx, CompileOptions &options, HandleObject opts,
JSAutoByteString &fileNameBytes)
{
RootedValue v(cx);
RootedString s(cx);
if (!JS_GetProperty(cx, opts, "compileAndGo", &v))
return false;
if (!v.isUndefined())
options.setCompileAndGo(ToBoolean(v));
if (!JS_GetProperty(cx, opts, "noScriptRval", &v))
return false;
if (!v.isUndefined())
options.setNoScriptRval(ToBoolean(v));
if (!JS_GetProperty(cx, opts, "fileName", &v))
return false;
if (v.isNull()) {
options.setFile(nullptr);
} else if (!v.isUndefined()) {
s = ToString(cx, v);
if (!s)
return false;
char *fileName = fileNameBytes.encodeLatin1(cx, s);
if (!fileName)
return false;
options.setFile(fileName);
}
if (!JS_GetProperty(cx, opts, "element", &v))
return false;
if (v.isObject())
options.setElement(&v.toObject());
if (!JS_GetProperty(cx, opts, "elementAttributeName", &v))
return false;
if (!v.isUndefined()) {
s = ToString(cx, v);
if (!s)
return false;
options.setElementAttributeName(s);
}
if (!JS_GetProperty(cx, opts, "lineNumber", &v))
return false;
if (!v.isUndefined()) {
uint32_t u;
if (!ToUint32(cx, v, &u))
return false;
options.setLine(u);
}
if (!JS_GetProperty(cx, opts, "sourcePolicy", &v))
return false;
if (!v.isUndefined()) {
JSString *s = ToString(cx, v);
if (!s)
return false;
char *policy = JS_EncodeStringToUTF8(cx, s);
if (!policy)
return false;
if (strcmp(policy, "NO_SOURCE") == 0) {
options.setSourcePolicy(CompileOptions::NO_SOURCE);
} else if (strcmp(policy, "LAZY_SOURCE") == 0) {
options.setSourcePolicy(CompileOptions::LAZY_SOURCE);
} else if (strcmp(policy, "SAVE_SOURCE") == 0) {
options.setSourcePolicy(CompileOptions::SAVE_SOURCE);
} else {
JS_ReportError(cx, "bad 'sourcePolicy' option: '%s'", policy);
return false;
}
}
return true;
}
class AutoNewContext
{
private:
@ -849,21 +930,17 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
bool newContext = false;
bool compileAndGo = true;
bool noScriptRval = false;
const char *fileName = "@evaluate";
RootedObject element(cx);
RootedString elementProperty(cx);
CompileOptions options(cx);
JSAutoByteString fileNameBytes;
bool newContext = false;
RootedString displayURL(cx);
RootedString sourceMapURL(cx);
unsigned lineNumber = 1;
RootedObject global(cx, nullptr);
bool catchTermination = false;
bool saveFrameChain = false;
RootedObject callerGlobal(cx, cx->global());
CompileOptions::SourcePolicy sourcePolicy = CompileOptions::SAVE_SOURCE;
options.setFileAndLine("@evaluate", 1);
global = JS_GetGlobalForObject(cx, &args.callee());
if (!global)
@ -873,47 +950,14 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
RootedObject opts(cx, &args[1].toObject());
RootedValue v(cx);
if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
return false;
if (!JS_GetProperty(cx, opts, "newContext", &v))
return false;
if (!v.isUndefined())
newContext = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "compileAndGo", &v))
return false;
if (!v.isUndefined())
compileAndGo = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "noScriptRval", &v))
return false;
if (!v.isUndefined())
noScriptRval = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "fileName", &v))
return false;
if (v.isNull()) {
fileName = nullptr;
} else if (!v.isUndefined()) {
JSString *s = ToString(cx, v);
if (!s)
return false;
fileName = fileNameBytes.encodeLatin1(cx, s);
if (!fileName)
return false;
}
if (!JS_GetProperty(cx, opts, "element", &v))
return false;
if (v.isObject())
element = &v.toObject();
if (!JS_GetProperty(cx, opts, "elementProperty", &v))
return false;
if (!v.isUndefined()) {
elementProperty = ToString(cx, v);
if (!elementProperty)
return false;
}
if (!JS_GetProperty(cx, opts, "displayURL", &v))
return false;
if (!v.isUndefined()) {
@ -930,15 +974,6 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
if (!JS_GetProperty(cx, opts, "lineNumber", &v))
return false;
if (!v.isUndefined()) {
uint32_t u;
if (!ToUint32(cx, v, &u))
return false;
lineNumber = u;
}
if (!JS_GetProperty(cx, opts, "global", &v))
return false;
if (!v.isUndefined()) {
@ -963,28 +998,6 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
return false;
if (!v.isUndefined())
saveFrameChain = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "sourcePolicy", &v))
return false;
if (!v.isUndefined()) {
JSString *s = ToString(cx, v);
if (!s)
return false;
char *policy = JS_EncodeStringToUTF8(cx, s);
if (!policy)
return false;
if (strcmp(policy, "NO_SOURCE") == 0) {
sourcePolicy = CompileOptions::NO_SOURCE;
} else if (strcmp(policy, "LAZY_SOURCE") == 0) {
sourcePolicy = CompileOptions::LAZY_SOURCE;
} else if (strcmp(policy, "SAVE_SOURCE") == 0) {
sourcePolicy = CompileOptions::SAVE_SOURCE;
} else {
JS_ReportError(cx, "bad 'sourcePolicy' option passed to 'evaluate': '%s'",
policy);
return false;
}
}
}
RootedString code(cx, args[0].toString());
@ -1009,16 +1022,12 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
JSAutoCompartment ac(cx, global);
RootedScript script(cx);
if (!options.wrap(cx, cx->compartment()))
return false;
{
JS::AutoSaveContextOptions asco(cx);
JS::ContextOptionsRef(cx).setNoScriptRval(noScriptRval);
CompileOptions options(cx);
options.setFileAndLine(fileName, lineNumber)
.setElement(element)
.setElementProperty(elementProperty)
.setSourcePolicy(sourcePolicy)
.setCompileAndGo(compileAndGo);
JS::ContextOptionsRef(cx).setNoScriptRval(options.noScriptRval);
script = JS::Compile(cx, global, options, codeChars, codeLength);
if (!script)
@ -3298,16 +3307,30 @@ OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JSString *scriptContents = args[0].toString();
JSAutoByteString fileNameBytes;
CompileOptions options(cx);
options.setFileAndLine("<string>", 1)
.setCompileAndGo(true)
options.setFileAndLine("<string>", 1);
if (args.length() >= 2) {
if (args[1].isPrimitive()) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
return false;
}
RootedObject opts(cx, &args[1].toObject());
if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
return false;
}
// These option settings must override whatever the caller requested.
options.setCompileAndGo(true)
.setSourcePolicy(CompileOptions::SAVE_SOURCE);
// We assume the caller wants caching if at all possible, ignoring
// heuristics that make sense for a real browser.
options.forceAsync = true;
JSString *scriptContents = args[0].toString();
const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
if (!chars)
return false;
@ -4041,13 +4064,13 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" catchTermination: if true, catch termination (failure without\n"
" an exception value, as for slow scripts or out-of-memory)\n"
" and return 'terminated'\n"
" element: if present with value |v|, convert |v| to an object |o| mark\n"
" the source as being attached to the DOM element |o|. If the\n"
" element: if present with value |v|, convert |v| to an object |o| and\n"
" mark the source as being attached to the DOM element |o|. If the\n"
" property is omitted or |v| is null, don't attribute the source to\n"
" any DOM element.\n"
" elementProperty: if present and not undefined, the name of property\n"
" of 'element' that holds this code. This is what Debugger.Source\n"
" .prototype.elementProperty returns.\n"
" elementAttributeName: if present and not undefined, the name of\n"
" property of 'element' that holds this code. This is what\n"
" Debugger.Source.prototype.elementAttributeName returns.\n"
" sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
" provide that as the code's source map URL. If omitted, attach no\n"
" source map URL to the code (although the code may provide one itself,\n"
@ -4236,14 +4259,26 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
#ifdef JS_THREADSAFE
JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
"offThreadCompileScript(code)",
" Trigger an off thread parse/emit for the input string"),
"offThreadCompileScript(code[, options])",
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
" and run the code, call |runOffThreadScript|. If present, |options| may\n"
" have properties saying how the code should be compiled:\n"
" noScriptRval: use the no-script-rval compiler option (default: false)\n"
" fileName: filename for error messages and debug info\n"
" lineNumber: starting line number for error messages and debug info\n"
" element: if present with value |v|, convert |v| to an object |o| and\n"
" mark the source as being attached to the DOM element |o|. If the\n"
" property is omitted or |v| is null, don't attribute the source to\n"
" any DOM element.\n"
" elementAttributeName: if present and not undefined, the name of\n"
" property of 'element' that holds this code. This is what\n"
" Debugger.Source.prototype.elementAttributeName returns.\n"),
JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
"runOffThreadScript()",
" Wait for off-thread compilation to complete. If an error occurred,\n"
" throw the appropriate exception; otherwise, run the script and return\n"
" its value."),
" its value."),
#endif

View File

@ -3003,6 +3003,19 @@ DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static bool
DebuggerScript_getGlobal(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get global)", args, obj, script);
Debugger *dbg = Debugger::fromChildJSObject(obj);
RootedValue v(cx, ObjectValue(script->global()));
if (!dbg->wrapDebuggeeValue(cx, &v))
return false;
args.rval().set(v);
return true;
}
static bool
DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
{
@ -3666,6 +3679,7 @@ static const JSPropertySpec DebuggerScript_properties[] = {
JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0),
JS_PSG("staticLevel", DebuggerScript_getStaticLevel, 0),
JS_PSG("sourceMapURL", DebuggerScript_getSourceMapUrl, 0),
JS_PSG("global", DebuggerScript_getGlobal, 0),
JS_PS_END
};
@ -3878,8 +3892,8 @@ DebuggerSource_getElement(JSContext *cx, unsigned argc, Value *vp)
static bool
DebuggerSource_getElementProperty(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementProperty)", args, obj, sourceObject);
args.rval().set(sourceObject->elementProperty());
THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, sourceObject);
args.rval().set(sourceObject->elementAttributeName());
return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
}
@ -3888,7 +3902,7 @@ static const JSPropertySpec DebuggerSource_properties[] = {
JS_PSG("url", DebuggerSource_getUrl, 0),
JS_PSG("element", DebuggerSource_getElement, 0),
JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
JS_PSG("elementProperty", DebuggerSource_getElementProperty, 0),
JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
JS_PS_END
};

View File

@ -234,22 +234,6 @@ AutoRooterGetterSetter::AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t at
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
inline
StackBaseShape::AutoRooter::AutoRooter(ThreadSafeContext *cx, const StackBaseShape *base_
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: CustomAutoRooter(cx), base(base_), skip(cx, base_)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
inline
StackShape::AutoRooter::AutoRooter(ThreadSafeContext *cx, const StackShape *shape_
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: CustomAutoRooter(cx), shape(shape_), skip(cx, shape_)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
} /* namespace js */
#endif /* vm_Shape_inl_h */

View File

@ -306,7 +306,7 @@ ShapeTable::grow(ThreadSafeContext *cx)
}
/* static */ Shape *
Shape::replaceLastProperty(ExclusiveContext *cx, const StackBaseShape &base,
Shape::replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base,
TaggedProto proto, HandleShape shape)
{
JS_ASSERT(!shape->inDictionary());
@ -363,15 +363,15 @@ JSObject::getChildPropertyOnDictionary(ThreadSafeContext *cx, JS::HandleObject o
if (obj->inDictionaryMode()) {
JS_ASSERT(parent == obj->lastProperty());
StackShape::AutoRooter childRoot(cx, &child);
RootedGeneric<StackShape*> childRoot(cx, &child);
shape = js_NewGCShape(cx);
if (!shape)
return nullptr;
if (child.hasSlot() && child.slot() >= obj->lastProperty()->base()->slotSpan()) {
if (!JSObject::setSlotSpan(cx, obj, child.slot() + 1))
if (childRoot->hasSlot() && childRoot->slot() >= obj->lastProperty()->base()->slotSpan()) {
if (!JSObject::setSlotSpan(cx, obj, childRoot->slot() + 1))
return nullptr;
}
shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
shape->initDictionaryShape(*childRoot, obj->numFixedSlots(), &obj->shape_);
}
return shape;
@ -379,13 +379,13 @@ JSObject::getChildPropertyOnDictionary(ThreadSafeContext *cx, JS::HandleObject o
/* static */ Shape *
JSObject::getChildProperty(ExclusiveContext *cx,
HandleObject obj, HandleShape parent, StackShape &child)
HandleObject obj, HandleShape parent, StackShape &unrootedChild)
{
StackShape::AutoRooter childRoot(cx, &child);
RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, child));
RootedGeneric<StackShape*> child(cx, &unrootedChild);
RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, *child));
if (!obj->inDictionaryMode()) {
shape = cx->compartment()->propertyTree.getChild(cx, parent, child);
shape = cx->compartment()->propertyTree.getChild(cx, parent, *child);
if (!shape)
return nullptr;
//JS_ASSERT(shape->parent == parent);
@ -399,15 +399,15 @@ JSObject::getChildProperty(ExclusiveContext *cx,
/* static */ Shape *
JSObject::lookupChildProperty(ThreadSafeContext *cx,
HandleObject obj, HandleShape parent, StackShape &child)
HandleObject obj, HandleShape parent, StackShape &unrootedChild)
{
StackShape::AutoRooter childRoot(cx, &child);
RootedGeneric<StackShape*> child(cx, &unrootedChild);
JS_ASSERT(cx->isThreadLocal(obj));
RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, child));
RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, *child));
if (!obj->inDictionaryMode()) {
shape = cx->compartment_->propertyTree.lookupChild(cx, parent, child);
shape = cx->compartment_->propertyTree.lookupChild(cx, parent, *child);
if (!shape)
return nullptr;
if (!JSObject::setLastProperty(cx, obj, shape))
@ -546,7 +546,7 @@ ShouldConvertToDictionary(JSObject *obj)
template <ExecutionMode mode>
static inline UnownedBaseShape *
GetOrLookupUnownedBaseShape(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
const StackBaseShape &base)
StackBaseShape &base)
{
if (mode == ParallelExecution)
return BaseShape::lookupUnowned(cx, base);
@ -1439,28 +1439,28 @@ StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
}
void
StackBaseShape::AutoRooter::trace(JSTracer *trc)
StackBaseShape::trace(JSTracer *trc)
{
if (base->parent) {
gc::MarkObjectRoot(trc, (JSObject**)&base->parent,
"StackBaseShape::AutoRooter parent");
if (parent) {
gc::MarkObjectRoot(trc, (JSObject**)&parent,
"StackBaseShape parent");
}
if (base->metadata) {
gc::MarkObjectRoot(trc, (JSObject**)&base->metadata,
"StackBaseShape::AutoRooter metadata");
if (metadata) {
gc::MarkObjectRoot(trc, (JSObject**)&metadata,
"StackBaseShape metadata");
}
if ((base->flags & BaseShape::HAS_GETTER_OBJECT) && base->rawGetter) {
gc::MarkObjectRoot(trc, (JSObject**)&base->rawGetter,
"StackBaseShape::AutoRooter getter");
if ((flags & BaseShape::HAS_GETTER_OBJECT) && rawGetter) {
gc::MarkObjectRoot(trc, (JSObject**)&rawGetter,
"StackBaseShape getter");
}
if ((base->flags & BaseShape::HAS_SETTER_OBJECT) && base->rawSetter) {
gc::MarkObjectRoot(trc, (JSObject**)&base->rawSetter,
"StackBaseShape::AutoRooter setter");
if ((flags & BaseShape::HAS_SETTER_OBJECT) && rawSetter) {
gc::MarkObjectRoot(trc, (JSObject**)&rawSetter,
"StackBaseShape setter");
}
}
/* static */ UnownedBaseShape*
BaseShape::getUnowned(ExclusiveContext *cx, const StackBaseShape &base)
BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base)
{
BaseShapeSet &table = cx->compartment()->baseShapes;
@ -1471,17 +1471,17 @@ BaseShape::getUnowned(ExclusiveContext *cx, const StackBaseShape &base)
if (p)
return *p;
StackBaseShape::AutoRooter root(cx, &base);
RootedGeneric<StackBaseShape*> root(cx, &base);
BaseShape *nbase_ = js_NewGCBaseShape<CanGC>(cx);
if (!nbase_)
return nullptr;
new (nbase_) BaseShape(base);
new (nbase_) BaseShape(*root);
UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
if (!p.add(cx, table, &base, nbase))
if (!p.add(cx, table, root, nbase))
return nullptr;
return nbase;

View File

@ -715,7 +715,7 @@ class BaseShape : public gc::BarrieredCell<BaseShape>
* Lookup base shapes from the compartment's baseShapes table, adding if
* not already found.
*/
static UnownedBaseShape* getUnowned(ExclusiveContext *cx, const StackBaseShape &base);
static UnownedBaseShape* getUnowned(ExclusiveContext *cx, StackBaseShape &base);
/*
* Lookup base shapes from the compartment's baseShapes table, returning
@ -847,19 +847,9 @@ struct StackBaseShape
static inline HashNumber hash(const StackBaseShape *lookup);
static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
class AutoRooter : private JS::CustomAutoRooter
{
public:
inline AutoRooter(ThreadSafeContext *cx, const StackBaseShape *base_
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
private:
virtual void trace(JSTracer *trc);
const StackBaseShape *base;
SkipRoot skip;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
// For RootedGeneric<StackBaseShape*>
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
void trace(JSTracer *trc);
};
inline
@ -961,7 +951,7 @@ class Shape : public gc::BarrieredCell<Shape>
}
/* Replace the base shape of the last shape in a non-dictionary lineage with base. */
static Shape *replaceLastProperty(ExclusiveContext *cx, const StackBaseShape &base,
static Shape *replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base,
TaggedProto proto, HandleShape shape);
/*
@ -1556,19 +1546,9 @@ struct StackShape
return hash;
}
class AutoRooter : private JS::CustomAutoRooter
{
public:
inline AutoRooter(ThreadSafeContext *cx, const StackShape *shape_
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
private:
virtual void trace(JSTracer *trc);
const StackShape *shape;
SkipRoot skip;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
// For RootedGeneric<StackShape*>
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
void trace(JSTracer *trc);
};
} /* namespace js */

View File

@ -171,8 +171,8 @@ File(JSContext *cx, unsigned argc, Value *vp)
return false;
}
nsXPConnect* xpc = nsXPConnect::XPConnect();
JSObject* glob = CurrentGlobalOrNull(cx);
nsXPConnect *xpc = nsXPConnect::XPConnect();
JSObject *glob = CurrentGlobalOrNull(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
@ -206,8 +206,8 @@ Blob(JSContext *cx, unsigned argc, Value *vp)
return false;
}
nsXPConnect* xpc = nsXPConnect::XPConnect();
JSObject* glob = CurrentGlobalOrNull(cx);
nsXPConnect *xpc = nsXPConnect::XPConnect();
JSObject *glob = CurrentGlobalOrNull(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
@ -1141,9 +1141,9 @@ mozJSComponentLoader::Import(const nsACString& registryLocation,
/* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
in JSObjectPtr targetObj); */
NS_IMETHODIMP
mozJSComponentLoader::ImportInto(const nsACString & aLocation,
mozJSComponentLoader::ImportInto(const nsACString &aLocation,
JSObject *aTargetObj,
nsAXPCNativeCallContext * cc,
nsAXPCNativeCallContext *cc,
JSObject **_retval)
{
JSContext *callercx;

View File

@ -93,25 +93,22 @@ ReportError(JSContext *cx, const char *msg)
nsresult
mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObjArg,
const nsAString& charset, const char *uriStr,
const nsAString &charset, const char *uriStr,
nsIIOService *serv, nsIPrincipal *principal,
bool reuseGlobal, JSScript **scriptp,
JSFunction **functionp)
{
RootedObject target_obj(cx, targetObjArg);
nsCOMPtr<nsIChannel> chan;
nsCOMPtr<nsIInputStream> instream;
JSErrorReporter er;
*scriptp = nullptr;
*functionp = nullptr;
nsresult rv;
// Instead of calling NS_OpenURI, we create the channel ourselves and call
// SetContentType, to avoid expensive MIME type lookups (bug 632490).
rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
nullptr, nullptr, nsIRequest::LOAD_NORMAL);
nsCOMPtr<nsIChannel> chan;
nsCOMPtr<nsIInputStream> instream;
nsresult rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
nullptr, nullptr, nsIRequest::LOAD_NORMAL);
if (NS_SUCCEEDED(rv)) {
chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
rv = chan->Open(getter_AddRefs(instream));
@ -139,7 +136,7 @@ mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObj
/* set our own error reporter so we can report any bad things as catchable
* exceptions, including the source/line number */
er = JS_SetErrorReporter(cx, xpc::SystemErrorReporter);
JSErrorReporter er = JS_SetErrorReporter(cx, xpc::SystemErrorReporter);
JS::CompileOptions options(cx);
options.setPrincipals(nsJSPrincipals::get(principal))
@ -183,9 +180,9 @@ mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObj
}
NS_IMETHODIMP
mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
mozJSSubScriptLoader::LoadSubScript(const nsAString &url,
HandleValue target,
const nsAString& charset,
const nsAString &charset,
JSContext *cx,
MutableHandleValue retval)
{
@ -208,7 +205,7 @@ mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
NS_IMETHODIMP
mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString& url,
mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString &url,
HandleValue optionsVal,
JSContext *cx,
MutableHandleValue retval)
@ -222,8 +219,8 @@ mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString& url,
}
nsresult
mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
LoadSubScriptOptions& options,
mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString &url,
LoadSubScriptOptions &options,
JSContext *cx,
MutableHandleValue retval)
{
@ -242,7 +239,7 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
}
RootedObject targetObj(cx);
mozJSComponentLoader* loader = mozJSComponentLoader::Get();
mozJSComponentLoader *loader = mozJSComponentLoader::Get();
rv = loader->FindTargetObject(cx, &targetObj);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -33,14 +33,14 @@ public:
private:
nsresult ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj,
const nsAString& charset, const char *uriStr,
const nsAString &charset, const char *uriStr,
nsIIOService *serv, nsIPrincipal *principal,
bool reuseGlobal, JSScript **scriptp,
JSFunction **functionp);
nsresult DoLoadSubScriptWithOptions(const nsAString& url,
LoadSubScriptOptions& options,
JSContext* cx,
nsresult DoLoadSubScriptWithOptions(const nsAString &url,
LoadSubScriptOptions &options,
JSContext *cx,
JS::MutableHandle<JS::Value> retval);
nsCOMPtr<nsIPrincipal> mSystemPrincipal;

View File

@ -538,7 +538,7 @@ xpc::HasInstance(JSContext *cx, HandleObject objArg, const nsID *iid, bool *bp)
/* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
NS_IMETHODIMP
nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
JSContext * cx, JSObject * /* unused */,
JSContext *cx, JSObject * /* unused */,
HandleValue val, bool *bp, bool *_retval)
{
*bp = false;
@ -671,7 +671,7 @@ GetWrapperObject(MutableHandleObject obj)
/* nsISupports createInstance (); */
NS_IMETHODIMP
nsJSCID::CreateInstance(HandleValue iidval, JSContext* cx,
nsJSCID::CreateInstance(HandleValue iidval, JSContext *cx,
uint8_t optionalArgc, MutableHandleValue retval)
{
if (!mDetails.IsValid())
@ -714,8 +714,8 @@ nsJSCID::CreateInstance(HandleValue iidval, JSContext* cx,
/* nsISupports getService (); */
NS_IMETHODIMP
nsJSCID::GetService(HandleValue iidval, JSContext* cx,
uint8_t optionalArgc, MutableHandleValue retval)
nsJSCID::GetService(HandleValue iidval, JSContext *cx, uint8_t optionalArgc,
MutableHandleValue retval)
{
if (!mDetails.IsValid())
return NS_ERROR_XPC_BAD_CID;
@ -726,7 +726,7 @@ nsJSCID::GetService(HandleValue iidval, JSContext* cx,
return NS_ERROR_UNEXPECTED;
}
nsIXPCSecurityManager* sm;
nsIXPCSecurityManager *sm;
sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
if (sm && NS_FAILED(sm->CanCreateInstance(cx, mDetails.ID()))) {
MOZ_ASSERT(JS_IsExceptionPending(cx),
@ -735,7 +735,7 @@ nsJSCID::GetService(HandleValue iidval, JSContext* cx,
}
// If an IID was passed in then use it
const nsID* iid = GetIIDArg(optionalArgc, iidval, cx);
const nsID *iid = GetIIDArg(optionalArgc, iidval, cx);
if (!iid)
return NS_ERROR_XPC_BAD_IID;
@ -750,22 +750,19 @@ nsJSCID::GetService(HandleValue iidval, JSContext* cx,
if (NS_FAILED(rv) || !srvc)
return NS_ERROR_XPC_GS_RETURNED_FAILURE;
RootedObject instJSObj(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = nsXPConnect::XPConnect()->WrapNative(cx, obj, srvc, *iid, getter_AddRefs(holder));
if (NS_FAILED(rv) || !holder ||
// Assign, not compare
!(instJSObj = holder->GetJSObject()))
if (NS_FAILED(rv) || !holder || !holder->GetJSObject())
return NS_ERROR_XPC_CANT_CREATE_WN;
retval.setObject(*instJSObj);
retval.setObject(*holder->GetJSObject());
return NS_OK;
}
/* bool construct (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in uint32_t argc, in JSValPtr argv, in JSValPtr vp); */
NS_IMETHODIMP
nsJSCID::Construct(nsIXPConnectWrappedNative *wrapper,
JSContext * cx, JSObject * objArg,
JSContext *cx, JSObject *objArg,
const CallArgs &args, bool *_retval)
{
RootedObject obj(cx, objArg);
@ -785,24 +782,24 @@ nsJSCID::Construct(nsIXPConnectWrappedNative *wrapper,
/* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
NS_IMETHODIMP
nsJSCID::HasInstance(nsIXPConnectWrappedNative *wrapper,
JSContext * cx, JSObject * /* unused */,
JSContext *cx, JSObject * /* unused */,
HandleValue val, bool *bp, bool *_retval)
{
*bp = false;
nsresult rv = NS_OK;
if (!JSVAL_IS_PRIMITIVE(val)) {
if (val.isObject()) {
// we have a JSObject
RootedObject obj(cx, &val.toObject());
MOZ_ASSERT(obj, "when is an object not an object?");
// is this really a native xpcom object with a wrapper?
nsIClassInfo* ci = nullptr;
nsIClassInfo *ci = nullptr;
obj = FindObjectForHasInstance(cx, obj);
if (!obj || !IS_WN_REFLECTOR(obj))
return rv;
if (XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj))
if (XPCWrappedNative *other_wrapper = XPCWrappedNative::Get(obj))
ci = other_wrapper->GetClassInfo();
// We consider CID equality to be the thing that matters here.
@ -813,6 +810,7 @@ nsJSCID::HasInstance(nsIXPConnectWrappedNative *wrapper,
*bp = cid.Equals(mDetails.ID());
}
}
return rv;
}
@ -826,7 +824,7 @@ xpc_NewIDObject(JSContext *cx, HandleObject jsobj, const nsID& aID)
nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aID);
if (iid) {
nsXPConnect* xpc = nsXPConnect::XPConnect();
nsXPConnect *xpc = nsXPConnect::XPConnect();
if (xpc) {
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = xpc->WrapNative(cx, jsobj,
@ -843,13 +841,13 @@ xpc_NewIDObject(JSContext *cx, HandleObject jsobj, const nsID& aID)
// note: returned pointer is only valid while |obj| remains alive!
const nsID*
xpc_JSObjectToID(JSContext *cx, JSObject* obj)
xpc_JSObjectToID(JSContext *cx, JSObject *obj)
{
if (!cx || !obj)
return nullptr;
// NOTE: this call does NOT addref
XPCWrappedNative* wrapper = nullptr;
XPCWrappedNative *wrapper = nullptr;
obj = js::CheckedUnwrap(obj);
if (obj && IS_WN_REFLECTOR(obj))
wrapper = XPCWrappedNative::Get(obj);
@ -863,11 +861,11 @@ xpc_JSObjectToID(JSContext *cx, JSObject* obj)
}
bool
xpc_JSObjectIsID(JSContext *cx, JSObject* obj)
xpc_JSObjectIsID(JSContext *cx, JSObject *obj)
{
MOZ_ASSERT(cx && obj, "bad param");
// NOTE: this call does NOT addref
XPCWrappedNative* wrapper = nullptr;
XPCWrappedNative *wrapper = nullptr;
obj = js::CheckedUnwrap(obj);
if (obj && IS_WN_REFLECTOR(obj))
wrapper = XPCWrappedNative::Get(obj);

View File

@ -538,25 +538,27 @@ Parent(JSContext *cx, unsigned argc, jsval *vp)
}
static bool
Atob(JSContext *cx, unsigned argc, jsval *vp)
Atob(JSContext *cx, unsigned argc, Value *vp)
{
if (!argc)
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.length())
return true;
return xpc::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
return xpc::Base64Decode(cx, args[0], args.rval());
}
static bool
Btoa(JSContext *cx, unsigned argc, jsval *vp)
Btoa(JSContext *cx, unsigned argc, Value *vp)
{
if (!argc)
return true;
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.length())
return true;
return xpc::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
return xpc::Base64Encode(cx, args[0], args.rval());
}
static bool
Blob(JSContext *cx, unsigned argc, jsval *vp)
Blob(JSContext *cx, unsigned argc, Value *vp)
{
JS::CallArgs args = CallArgsFromVp(argc, vp);
@ -582,7 +584,7 @@ Blob(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JSObject* global = JS::CurrentGlobalOrNull(cx);
JSObject *global = JS::CurrentGlobalOrNull(cx);
rv = xpc->WrapNativeToJSVal(cx, global, native, nullptr,
&NS_GET_IID(nsISupports), true,
args.rval());
@ -595,7 +597,7 @@ Blob(JSContext *cx, unsigned argc, jsval *vp)
}
static bool
File(JSContext *cx, unsigned argc, jsval *vp)
File(JSContext *cx, unsigned argc, Value *vp)
{
JS::CallArgs args = CallArgsFromVp(argc, vp);
@ -621,7 +623,7 @@ File(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JSObject* global = JS::CurrentGlobalOrNull(cx);
JSObject *global = JS::CurrentGlobalOrNull(cx);
rv = xpc->WrapNativeToJSVal(cx, global, native, nullptr,
&NS_GET_IID(nsISupports), true,
args.rval());

View File

@ -573,11 +573,11 @@ nsXPConnect::WrapNative(JSContext * aJSContext,
/* void wrapNativeToJSVal (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, out jsval aVal, out nsIXPConnectJSObjectHolder aHolder); */
NS_IMETHODIMP
nsXPConnect::WrapNativeToJSVal(JSContext * aJSContext,
JSObject * aScopeArg,
nsXPConnect::WrapNativeToJSVal(JSContext *aJSContext,
JSObject *aScopeArg,
nsISupports *aCOMObj,
nsWrapperCache *aCache,
const nsIID * aIID,
const nsIID *aIID,
bool aAllowWrapping,
MutableHandleValue aVal)
{
@ -616,7 +616,7 @@ nsXPConnect::WrapJS(JSContext * aJSContext,
NS_IMETHODIMP
nsXPConnect::JSValToVariant(JSContext *cx,
HandleValue aJSVal,
nsIVariant ** aResult)
nsIVariant **aResult)
{
NS_PRECONDITION(aResult, "bad param");
@ -629,10 +629,10 @@ nsXPConnect::JSValToVariant(JSContext *cx,
/* void wrapJSAggregatedToNative (in nsISupports aOuter, in JSContextPtr aJSContext, in JSObjectPtr aJSObj, in nsIIDRef aIID, [iid_is (aIID), retval] out nsQIResult result); */
NS_IMETHODIMP
nsXPConnect::WrapJSAggregatedToNative(nsISupports *aOuter,
JSContext * aJSContext,
JSObject * aJSObjArg,
const nsIID & aIID,
void * *result)
JSContext *aJSContext,
JSObject *aJSObjArg,
const nsIID &aIID,
void **result)
{
MOZ_ASSERT(aOuter, "bad param");
MOZ_ASSERT(aJSContext, "bad param");
@ -673,8 +673,8 @@ nsXPConnect::GetWrappedNativeOfJSObject(JSContext * aJSContext,
/* nsISupports getNativeOfWrapper(in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */
NS_IMETHODIMP_(nsISupports*)
nsXPConnect::GetNativeOfWrapper(JSContext * aJSContext,
JSObject * aJSObj)
nsXPConnect::GetNativeOfWrapper(JSContext *aJSContext,
JSObject *aJSObj)
{
MOZ_ASSERT(aJSContext, "bad param");
MOZ_ASSERT(aJSObj, "bad param");
@ -1331,10 +1331,9 @@ nsXPConnect::HoldObject(JSContext *aJSContext, JSObject *aObjectArg,
namespace xpc {
NS_EXPORT_(bool)
Base64Encode(JSContext *cx, JS::Value val, JS::Value *out)
Base64Encode(JSContext *cx, HandleValue val, MutableHandleValue out)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(out);
JS::RootedValue root(cx, val);
xpc_qsACString encodedString(cx, root, &root, false,
@ -1353,15 +1352,14 @@ Base64Encode(JSContext *cx, JS::Value val, JS::Value *out)
if (!str)
return false;
*out = STRING_TO_JSVAL(str);
out.setString(str);
return true;
}
NS_EXPORT_(bool)
Base64Decode(JSContext *cx, JS::Value val, JS::Value *out)
Base64Decode(JSContext *cx, HandleValue val, MutableHandleValue out)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(out);
JS::RootedValue root(cx, val);
xpc_qsACString encodedString(cx, root, &root, false,
@ -1380,7 +1378,7 @@ Base64Decode(JSContext *cx, JS::Value val, JS::Value *out)
if (!str)
return false;
*out = STRING_TO_JSVAL(str);
out.setString(str);
return true;
}
@ -1419,12 +1417,12 @@ nsXPConnect::GetTelemetryValue(JSContext *cx, MutableHandleValue rval)
unsigned attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
size_t i = JS_SetProtoCalled(cx);
RootedValue v(cx, DOUBLE_TO_JSVAL(i));
RootedValue v(cx, DoubleValue(i));
if (!JS_DefineProperty(cx, obj, "setProto", v, nullptr, nullptr, attrs))
return NS_ERROR_OUT_OF_MEMORY;
i = JS_GetCustomIteratorCount(cx);
v = DOUBLE_TO_JSVAL(i);
v.setDouble(i);
if (!JS_DefineProperty(cx, obj, "customIter", v, nullptr, nullptr, attrs))
return NS_ERROR_OUT_OF_MEMORY;
@ -1652,21 +1650,23 @@ JS_EXPORT_API(void) DumpCompleteHeap()
namespace xpc {
bool
Atob(JSContext *cx, unsigned argc, jsval *vp)
Atob(JSContext *cx, unsigned argc, Value *vp)
{
if (!argc)
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.length())
return true;
return xpc::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
return xpc::Base64Decode(cx, args[0], args.rval());
}
bool
Btoa(JSContext *cx, unsigned argc, jsval *vp)
Btoa(JSContext *cx, unsigned argc, Value *vp)
{
if (!argc)
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.length())
return true;
return xpc::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
return xpc::Base64Encode(cx, args[0], args.rval());
}
bool

View File

@ -247,8 +247,8 @@ private:
namespace xpc {
// If these functions return false, then an exception will be set on cx.
NS_EXPORT_(bool) Base64Encode(JSContext *cx, JS::Value val, JS::Value *out);
NS_EXPORT_(bool) Base64Decode(JSContext *cx, JS::Value val, JS::Value *out);
NS_EXPORT_(bool) Base64Encode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out);
NS_EXPORT_(bool) Base64Decode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out);
/**
* Convert an nsString to jsval, returning true on success.

View File

@ -285,7 +285,7 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
&NS_GET_IID(nsISupports), false, &v);
NS_ENSURE_SUCCESS(rv, nullptr);
obj = JSVAL_TO_OBJECT(v);
obj.set(&v.toObject());
MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
// Because the underlying native didn't have a PreCreate hook, we had

View File

@ -2833,7 +2833,7 @@ ElementRestyler::SendAccessibilityNotifications()
#endif
}
inline nsIFrame*
static inline nsIFrame*
GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(),

View File

@ -23,6 +23,7 @@
#include "nsCSSRendering.h"
#include "mozilla/dom/Attr.h"
#include "nsDOMClassInfo.h"
#include "nsEditorEventListener.h"
#include "nsEventListenerManager.h"
#include "nsFrame.h"
#include "nsGlobalWindow.h"
@ -38,7 +39,6 @@
#include "nsStackLayout.h"
#include "nsStyleSet.h"
#include "nsTextControlFrame.h"
#include "nsXBLWindowKeyHandler.h"
#include "nsXBLService.h"
#include "txMozillaXSLTProcessor.h"
#include "nsTreeSanitizer.h"
@ -350,7 +350,7 @@ nsLayoutStatics::Shutdown()
nsGlobalWindow::ShutDown();
nsDOMClassInfo::ShutDown();
nsListControlFrame::Shutdown();
nsXBLWindowKeyHandler::ShutDown();
nsEditorEventListener::ShutDown();
nsXBLService::Shutdown();
nsAutoCopyListener::Shutdown();
FrameLayerBuilder::Shutdown();

View File

@ -643,7 +643,7 @@ public abstract class GeckoApp
mCurrentResponse = GeckoAppShell.preInstallWebApp(name, manifestURL, origin).toString();
} else if (event.equals("WebApps:PostInstall")) {
if (AppConstants.MOZ_ANDROID_SYNTHAPKS) {
GeckoAppShell.postInstallWebApp(message.getString("packageName"), message.getString("origin"));
GeckoAppShell.postInstallWebApp(message.getString("apkPackageName"), message.getString("origin"));
} else {
String name = message.getString("name");
String manifestURL = message.getString("manifestURL");

View File

@ -81,7 +81,7 @@ public class InstallHelper implements GeckoEventListener {
GeckoProfile profile = GeckoProfile.get(mContext, profileName);
try {
message.put("packageName", mApkResources.getPackageName());
message.put("apkPackageName", mApkResources.getPackageName());
message.put("manifestUrl", mApkResources.getManifestUrl());
message.put("title", mApkResources.getAppName());
message.put("manifest", new JSONObject(mApkResources.getManifest(mContext)));

View File

@ -47,7 +47,7 @@ public class UninstallListener extends BroadcastReceiver {
JSONArray packageNames = new JSONArray();
try {
packageNames.put(packageName);
message.put("packages", packageNames);
message.put("apkPackageNames", packageNames);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Webapps:AutoUninstall", message.toString()));
} catch (JSONException e) {
Log.e(LOGTAG, "JSON EXCEPTION " + e);
@ -84,7 +84,7 @@ public class UninstallListener extends BroadcastReceiver {
for (String packageName : uninstalledPackages) {
packageNames.put(packageName);
}
message.put("packages", packageNames);
message.put("apkPackageNames", packageNames);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Webapps:AutoUninstall", message.toString()));
} catch (JSONException e) {
Log.e(LOGTAG, "JSON EXCEPTION " + e);

View File

@ -292,9 +292,7 @@ public class WebAppImpl extends GeckoApp implements InstallCallback {
if (event.equals("WebApps:PostInstall")) {
String origin = message.optString("origin");
String manifestUrl = message.optString("manifestURL");
String name = message.optString("name", "WebApp");
launchWebApp(origin, manifestUrl, name);
launchWebApp(origin, mApkResources.getManifestUrl(), mApkResources.getAppName());
}
}

View File

@ -64,28 +64,30 @@ let WebAppRT = {
},
getManifestFor: function (aUrl, aCallback) {
let request = navigator.mozApps.mgmt.getAll();
request.onsuccess = function() {
let apps = request.result;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let manifest = new ManifestHelper(app.manifest, app.origin);
DOMApplicationRegistry.registryReady.then(() => {
let request = navigator.mozApps.mgmt.getAll();
request.onsuccess = function() {
let apps = request.result;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let manifest = new ManifestHelper(app.manifest, app.origin);
// if this is a path to the manifest, or the launch path, then we have a hit.
if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) {
aCallback(manifest, app);
return;
// if this is a path to the manifest, or the launch path, then we have a hit.
if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) {
aCallback(manifest, app);
return;
}
}
}
// Otherwise, once we loop through all of them, we have a miss.
aCallback(undefined);
};
// Otherwise, once we loop through all of them, we have a miss.
aCallback(undefined);
};
request.onerror = function() {
// Treat an error like a miss. We can't find the manifest.
aCallback(undefined);
};
request.onerror = function() {
// Treat an error like a miss. We can't find the manifest.
aCallback(undefined);
};
});
},
findManifestUrlFor: function(aUrl, aCallback) {

View File

@ -86,12 +86,6 @@ var NewPrefDialog = {
return;
}
// Avoid "private" preferences
if (/^capability\./.test(aPrefName)) {
this._positiveButton.textContent = "Private";
return;
}
// If item already in list, it's being changed, else added
let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
if (item) {
@ -207,10 +201,7 @@ var AboutConfig = {
this._prefsContainer = document.getElementById("prefs-container");
this._loadingContainer = document.getElementById("loading-container");
let list = Services.prefs.getChildList("", {}).filter(function(aElement) {
// Avoid "private" preferences
return !(/^capability\./.test(aElement));
});
let list = Services.prefs.getChildList("");
this._list = list.sort().map( function AC_getMapPref(aPref) {
return new Pref(aPref);
}, this);
@ -464,7 +455,7 @@ var AboutConfig = {
let pref = new Pref(aPrefName);
// Ignore uninteresting changes, and avoid "private" preferences
if ((aTopic != "nsPref:changed") || /^capability\./.test(pref.name)) {
if (aTopic != "nsPref:changed") {
return;
}

View File

@ -96,7 +96,7 @@ this.WebappManager = {
// aData.app.origin may now point to the app: url that hosts this app.
sendMessageToJava({
type: "WebApps:PostInstall",
packageName: aData.app.packageName,
apkPackageName: aData.app.apkPackageName,
origin: aData.app.origin,
});
@ -148,7 +148,7 @@ this.WebappManager = {
// Thus, we should take the APK as the source of truth.
message.app.manifestURL = aData.manifestUrl;
message.app.manifest = aData.manifest;
message.app.packageName = aData.packageName;
message.app.apkPackageName = aData.apkPackageName;
message.profilePath = aData.profilePath;
message.autoInstall = true;
message.mm = mm;
@ -172,12 +172,12 @@ this.WebappManager = {
dump("autoUninstall sendAsyncMessage " + aMessageName + ": " + JSON.stringify(aData));
}
};
let installedPackages = {};
DOMApplicationRegistry.doGetAll(installedPackages, mm);
let installed = {};
DOMApplicationRegistry.doGetAll(installed, mm);
for (let app in installedPackages.apps) {
if (aData.packages.indexOf(installedPackages.apps[app].packageName) > -1) {
let appToRemove = installedPackages.apps[app];
for (let app in installed.apps) {
if (aData.apkPackageNames.indexOf(installed.apps[app].apkPackageName) > -1) {
let appToRemove = installed.apps[app];
dump("should remove: " + appToRemove.name);
DOMApplicationRegistry.uninstall(appToRemove.manifestURL, function() {
dump(appToRemove.name + " uninstalled");

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