mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-29 21:25:35 +00:00
merge mozilla-inbound to mozilla-central
This commit is contained in:
commit
46cde20331
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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({
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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, ¶m, 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,
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -308,7 +308,7 @@ uint16_t
|
||||
HTMLTrackElement::ReadyState() const
|
||||
{
|
||||
if (!mTrack) {
|
||||
return NONE;
|
||||
return READY_STATE_NONE;
|
||||
}
|
||||
|
||||
return mTrack->ReadyState();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
227
content/media/SharedThreadPool.cpp
Normal file
227
content/media/SharedThreadPool.cpp
Normal 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
|
77
content/media/SharedThreadPool.h
Normal file
77
content/media/SharedThreadPool.h
Normal 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_
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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',
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
49
editor/libeditor/html/tests/test_bug289384.html
Normal file
49
editor/libeditor/html/tests/test_bug289384.html
Normal 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>
|
@ -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 });
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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=", "]");
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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, " ");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
17
js/src/jit-test/tests/basic/offThreadCompileScript-02.js
Normal file
17
js/src/jit-test/tests/basic/offThreadCompileScript-02.js
Normal 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();
|
5
js/src/jit-test/tests/debug/Debugger-findScripts-19.js
Normal file
5
js/src/jit-test/tests/debug/Debugger-findScripts-19.js
Normal 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;
|
20
js/src/jit-test/tests/debug/Script-global-01.js
Normal file
20
js/src/jit-test/tests/debug/Script-global-01.js
Normal 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);
|
40
js/src/jit-test/tests/debug/Script-global-02.js
Normal file
40
js/src/jit-test/tests/debug/Script-global-02.js
Normal 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);
|
6
js/src/jit-test/tests/debug/Source-element-02.js
Normal file
6
js/src/jit-test/tests/debug/Source-element-02.js
Normal 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: {} });
|
11
js/src/jit-test/tests/debug/Source-elementAttributeName.js
Normal file
11
js/src/jit-test/tests/debug/Source-elementAttributeName.js
Normal 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);
|
@ -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);
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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 *)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -310,6 +310,7 @@ enum ThingRootKind
|
||||
THING_ROOT_TYPE,
|
||||
THING_ROOT_BINDINGS,
|
||||
THING_ROOT_PROPERTY_DESCRIPTOR,
|
||||
THING_ROOT_CUSTOM,
|
||||
THING_ROOT_LIMIT
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -2833,7 +2833,7 @@ ElementRestyler::SendAccessibilityNotifications()
|
||||
#endif
|
||||
}
|
||||
|
||||
inline nsIFrame*
|
||||
static inline nsIFrame*
|
||||
GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
|
||||
{
|
||||
NS_ASSERTION(!aFrame->GetPrevContinuation(),
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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)));
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user