зеркало из https://github.com/mozilla/gecko-dev.git
Merge last green changeset of mozilla-inbound to mozilla-central
This commit is contained in:
Коммит
1fe3dee652
|
@ -1064,3 +1064,6 @@ pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.prop
|
|||
pref("prompts.tab_modal.enabled", true);
|
||||
// Whether the Panorama should animate going in/out of tabs
|
||||
pref("browser.panorama.animate_zoom", true);
|
||||
|
||||
// Enable the DOM full-screen API.
|
||||
pref("full-screen-api.enabled", true);
|
||||
|
|
|
@ -3913,14 +3913,36 @@ var FullScreen = {
|
|||
},
|
||||
|
||||
enterDomFullScreen : function(event) {
|
||||
if (!document.mozFullScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We receive "mozfullscreenchange" events for each subdocument which
|
||||
// is an ancestor of the document containing the element which requested
|
||||
// full-screen. Only add listeners and show warning etc when the event we
|
||||
// receive is targeted at the chrome document, i.e. only once every time
|
||||
// we enter DOM full-screen mode.
|
||||
if (!document.mozFullScreen || event.target.ownerDocument != document) {
|
||||
let targetDoc = event.target.ownerDocument ? event.target.ownerDocument : event.target;
|
||||
if (targetDoc != document) {
|
||||
// However, if we receive a "mozfullscreenchange" event for a document
|
||||
// which is not a subdocument of the currently selected tab, we know that
|
||||
// we've switched tabs since the request to enter full-screen was made,
|
||||
// so we should exit full-screen since the "full-screen document" isn't
|
||||
// acutally visible.
|
||||
if (targetDoc.defaultView.top != gBrowser.contentWindow) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let focusManger = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
if (focusManger.activeWindow != window) {
|
||||
// The top-level window has lost focus since the request to enter
|
||||
// full-screen was made. Cancel full-screen.
|
||||
document.mozCancelFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
this.showWarning(true);
|
||||
|
||||
// Exit DOM full-screen mode upon open, close, or change tab.
|
||||
|
|
|
@ -639,8 +639,8 @@ BrowserGlue.prototype = {
|
|||
var currentVersion = Services.prefs.getIntPref("browser.rights.version");
|
||||
Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
|
||||
|
||||
var box = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
|
||||
box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
|
||||
var notification = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
|
||||
notification.persistence = -1; // Until user closes it
|
||||
},
|
||||
|
||||
_showUpdateNotification: function BG__showUpdateNotification() {
|
||||
|
@ -709,10 +709,10 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
];
|
||||
|
||||
let box = notifyBox.appendNotification(text, "post-update-notification",
|
||||
null, notifyBox.PRIORITY_INFO_LOW,
|
||||
buttons);
|
||||
box.persistence = 3;
|
||||
let notification = notifyBox.appendNotification(text, "post-update-notification",
|
||||
null, notifyBox.PRIORITY_INFO_LOW,
|
||||
buttons);
|
||||
notification.persistence = -1; // Until user closes it
|
||||
}
|
||||
|
||||
if (actions.indexOf("showAlert") == -1)
|
||||
|
@ -793,7 +793,7 @@ BrowserGlue.prototype = {
|
|||
|
||||
var buttons = [
|
||||
{
|
||||
label: browserBundle.GetStringFromName("telemetryYesButtonLabel"),
|
||||
label: browserBundle.GetStringFromName("telemetryYesButtonLabel2"),
|
||||
accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
|
||||
popup: null,
|
||||
callback: function(aNotificationBar, aButton) {
|
||||
|
@ -815,7 +815,7 @@ BrowserGlue.prototype = {
|
|||
|
||||
var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
|
||||
notification.setAttribute("hideclose", true);
|
||||
notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit
|
||||
notification.persistence = -1; // Until user closes it
|
||||
|
||||
let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
let link = notification.ownerDocument.createElementNS(XULNS, "label");
|
||||
|
@ -827,8 +827,9 @@ BrowserGlue.prototype = {
|
|||
// Remove the notification on which the user clicked
|
||||
notification.parentNode.removeNotification(notification, true);
|
||||
// Add a new notification to that tab, with no "Learn more" link
|
||||
var notifyBox = browser.getNotificationBox();
|
||||
notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
|
||||
notifyBox = browser.getNotificationBox();
|
||||
notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
|
||||
notification.persistence = -1; // Until user closes it
|
||||
}, false);
|
||||
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
|
||||
description.appendChild(link);
|
||||
|
@ -1092,10 +1093,10 @@ BrowserGlue.prototype = {
|
|||
];
|
||||
|
||||
var notifyBox = browser.getNotificationBox();
|
||||
var box = notifyBox.appendNotification(text, title, null,
|
||||
notifyBox.PRIORITY_CRITICAL_MEDIUM,
|
||||
buttons);
|
||||
box.persistence = -1; // Until user closes it
|
||||
var notification = notifyBox.appendNotification(text, title, null,
|
||||
notifyBox.PRIORITY_CRITICAL_MEDIUM,
|
||||
buttons);
|
||||
notification.persistence = -1; // Until user closes it
|
||||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
|
|
|
@ -331,7 +331,7 @@ syncPromoNotification.learnMoreLinkText=Learn More
|
|||
# and %2$S by the value of the toolkit.telemetry.server_owner preference.
|
||||
telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S?
|
||||
telemetryLinkLabel = Learn More
|
||||
telemetryYesButtonLabel = Yes
|
||||
telemetryYesButtonLabel2 = Yes, I want to help
|
||||
telemetryYesButtonAccessKey = Y
|
||||
telemetryNoButtonLabel = No
|
||||
telemetryNoButtonAccessKey = N
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
<!ENTITY updateHistory.label "Show Update History">
|
||||
<!ENTITY updateHistory.accesskey "p">
|
||||
|
||||
<!ENTITY useService.label "Use a background service to install updates">
|
||||
<!ENTITY useService.accesskey "b">
|
||||
|
||||
<!ENTITY updateOthers.label "Automatically update:">
|
||||
<!ENTITY enableAddonsUpdate3.label "Add-ons">
|
||||
<!ENTITY enableAddonsUpdate3.accesskey "o">
|
||||
|
|
|
@ -57,6 +57,10 @@ OPTIONS_PAGE_TITLE=Setup Type
|
|||
OPTIONS_PAGE_SUBTITLE=Choose setup options
|
||||
SHORTCUTS_PAGE_TITLE=Set Up Shortcuts
|
||||
SHORTCUTS_PAGE_SUBTITLE=Create Program Icons
|
||||
COMPONENTS_PAGE_TITLE=Set Up Optional Components
|
||||
COMPONENTS_PAGE_SUBTITLE=Optional Recommended Components
|
||||
OPTIONAL_COMPONENTS_DESC=The Maintenance Service will allow you to update $BrandShortName silently in the background.
|
||||
MAINTENANCE_SERVICE_CHECKBOX_DESC=Install &Maintenance Service
|
||||
SUMMARY_PAGE_TITLE=Summary
|
||||
SUMMARY_PAGE_SUBTITLE=Ready to start installing $BrandShortName
|
||||
SUMMARY_INSTALLED_TO=$BrandShortName will be installed to the following location:
|
||||
|
|
|
@ -428,7 +428,7 @@ user_pref("security.warn_viewing_mixed", false);
|
|||
user_pref("app.update.enabled", false);
|
||||
user_pref("browser.panorama.experienced_first_run", true); // Assume experienced
|
||||
user_pref("dom.w3c_touch_events.enabled", true);
|
||||
user_pref("toolkit.telemetry.prompted", true);
|
||||
user_pref("toolkit.telemetry.prompted", 2);
|
||||
|
||||
// Only load extensions from the application and user profile
|
||||
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
|
||||
|
|
Двоичные данные
build/pgo/certs/cert8.db
Двоичные данные
build/pgo/certs/cert8.db
Двоичный файл не отображается.
Двоичные данные
build/pgo/certs/key3.db
Двоичные данные
build/pgo/certs/key3.db
Двоичный файл не отображается.
|
@ -187,7 +187,7 @@ def createSSLServerCertificate(profileDir, srcDir):
|
|||
runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath])
|
||||
# Ignore the result, the certificate may not be present when new database is being built
|
||||
|
||||
status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "12", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath])
|
||||
status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath])
|
||||
if status != 0:
|
||||
return status
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ struct dirent *readdir(DIR *d)
|
|||
#endif
|
||||
|
||||
char *
|
||||
ino2name(ino_t ino, char *dir)
|
||||
ino2name(ino_t ino)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
|
@ -230,7 +230,7 @@ reversepath(char *inpath, char *name, int len, char *outpath)
|
|||
if (strcmp(buf, "..") == 0) {
|
||||
if (stat(".", &sb) < 0)
|
||||
fail("cannot stat current directory");
|
||||
name = ino2name(sb.st_ino, "..");
|
||||
name = ino2name(sb.st_ino);
|
||||
len = strlen(name);
|
||||
cp -= len + 1;
|
||||
strcpy(cp, name);
|
||||
|
|
|
@ -59,7 +59,7 @@ extern char *program;
|
|||
|
||||
extern void fail(char *format, ...);
|
||||
extern char *getcomponent(char *path, char *name);
|
||||
extern char *ino2name(ino_t ino, char *dir);
|
||||
extern char *ino2name(ino_t ino);
|
||||
extern void *xmalloc(size_t size);
|
||||
extern char *xstrdup(char *s);
|
||||
extern char *xbasename(char *path);
|
||||
|
|
|
@ -64,6 +64,7 @@ EXPORTS = \
|
|||
nsStubMutationObserver.h \
|
||||
nsTextFragment.h \
|
||||
mozAutoDocUpdate.h \
|
||||
nsFrameMessageManager.h \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_NAMESPACES = mozilla/dom
|
||||
|
|
|
@ -87,12 +87,14 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
|
|||
}
|
||||
|
||||
if (doc->IsBeingUsedAsImage()) {
|
||||
// Allow local resources for SVG-as-an-image documents, but disallow
|
||||
// everything else, to prevent data leakage
|
||||
// Only allow SVG-as-an-image to load local resources that inherit security
|
||||
// context (basically just data: URIs), to prevent data leakage.
|
||||
bool hasFlags;
|
||||
nsresult rv = NS_URIChainHasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
|
||||
&hasFlags);
|
||||
nsresult rv =
|
||||
NS_URIChainHasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE |
|
||||
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||
&hasFlags);
|
||||
if (NS_FAILED(rv) || !hasFlags) {
|
||||
// resource is not local (or we couldn't tell) - reject!
|
||||
*aDecision = nsIContentPolicy::REJECT_TYPE;
|
||||
|
|
|
@ -64,6 +64,11 @@
|
|||
#include "WebGLTexelConversions.h"
|
||||
#include "WebGLValidateStrings.h"
|
||||
|
||||
// needed to check if current OS is lower than 10.7
|
||||
#if defined(MOZ_WIDGET_COCOA)
|
||||
#include "nsCocoaFeatures.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static bool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize);
|
||||
|
@ -4417,12 +4422,6 @@ WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei heig
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#define WEBGL_OS_IS_MAC 1
|
||||
#else
|
||||
#define WEBGL_OS_IS_MAC 0
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::CompileShader(nsIWebGLShader *sobj)
|
||||
{
|
||||
|
@ -4480,9 +4479,11 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj)
|
|||
|
||||
int compileOptions = SH_OBJECT_CODE;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// work around bug 665578
|
||||
if (WEBGL_OS_IS_MAC && gl->Vendor() == gl::GLContext::VendorATI)
|
||||
if (!nsCocoaFeatures::OnLionOrLater() && gl->Vendor() == gl::GLContext::VendorATI)
|
||||
compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
|
||||
#endif
|
||||
|
||||
if (!ShCompile(compiler, &s, 1, compileOptions)) {
|
||||
int len = 0;
|
||||
|
|
|
@ -49,6 +49,7 @@ LIBXUL_LIBRARY = 1
|
|||
EXPORTS = \
|
||||
nsEventStateManager.h \
|
||||
nsEventListenerManager.h \
|
||||
nsDOMEventTargetHelper.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
|
|
|
@ -9,14 +9,14 @@ Test DOM full-screen API.
|
|||
<head>
|
||||
<title>Test for Bug 545812</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="fullScreenElement().mozRequestFullScreen();">
|
||||
|
||||
<body onload="start();">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 545812 **/
|
||||
|
@ -36,7 +36,7 @@ function is(a, b, msg) {
|
|||
</body>
|
||||
</html>
|
||||
*/
|
||||
var iframeContents = "data:text/html;charset=utf-8,<html><body onload%3D'document.body.mozRequestFullScreen()%3B'><iframe id%3D'inner-frame'><%2Fiframe><%2Fbody><%2Fhtml>";
|
||||
var iframeContents = "data:text/html;charset=utf-8,<html><body onload%3D'parent.SimpleTest.waitForFocus(function(){document.body.mozRequestFullScreen();});'><iframe id%3D'inner-frame'><%2Fiframe><%2Fbody><%2Fhtml>";
|
||||
|
||||
var iframe = null;
|
||||
var outOfDocElement = null;
|
||||
|
@ -45,6 +45,7 @@ var container = null;
|
|||
var button = null;
|
||||
var fullScreenChangeCount = 0;
|
||||
var fullscreendenied = false;
|
||||
var fullScreenErrorRun = false;
|
||||
|
||||
document.addEventListener("mozfullscreenerror", function(){fullscreendenied=true;}, false);
|
||||
|
||||
|
@ -96,11 +97,17 @@ function fullScreenChange(event) {
|
|||
"Event target should be full-screen element container");
|
||||
is(document.mozFullScreenElement, iframe,
|
||||
"Full-screen element should be iframe element.");
|
||||
var fse = fullScreenElement();
|
||||
fse.mozRequestFullScreen();
|
||||
|
||||
setTimeout(
|
||||
function() {
|
||||
SimpleTest.waitForFocus(function() {
|
||||
ok(document.mozFullScreen, "Should still be full-screen mode before focus.");
|
||||
SpecialPowers.focus(window);
|
||||
ok(document.mozFullScreen, "Should still be full-screen mode after focus.");
|
||||
|
||||
var fse = fullScreenElement();
|
||||
fse.mozRequestFullScreen();
|
||||
|
||||
// RequestFullScreen() is async, continue after it completes.
|
||||
setTimeout(function() {
|
||||
ok(document.mozFullScreen, "Should still be full-screen mode after re-requesting.");
|
||||
is(document.mozFullScreenElement, fse, "Full-screen element should have switched to requestee.");
|
||||
var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
|
||||
|
@ -114,7 +121,7 @@ function fullScreenChange(event) {
|
|||
|
||||
document.body.appendChild(fse);
|
||||
}, 0);
|
||||
|
||||
}, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
|
@ -132,6 +139,7 @@ function fullScreenChange(event) {
|
|||
function(e) {
|
||||
document.removeEventListener("mozfullscreenerror", f, false);
|
||||
ok(!document.mozFullScreen, "Requests for full-screen from not-in-doc elements should fail.");
|
||||
fullScreenErrorRun = true;
|
||||
|
||||
container = document.createElement("div");
|
||||
inDocElement = document.createElement("div");
|
||||
|
@ -148,6 +156,7 @@ function fullScreenChange(event) {
|
|||
case 4: {
|
||||
ok(document.mozFullScreen, "Should still be in full-screen mode (third time)");
|
||||
is(event.target, inDocElement, "Event target should be inDocElement");
|
||||
ok(fullScreenErrorRun, "Should have run fullscreenerror handler from previous case.");
|
||||
is(document.mozFullScreenElement, inDocElement,
|
||||
"FSE should be inDocElement.");
|
||||
|
||||
|
@ -227,6 +236,10 @@ function fullScreenChange(event) {
|
|||
|
||||
document.addEventListener("mozfullscreenchange", fullScreenChange, false);
|
||||
|
||||
function start() {
|
||||
SimpleTest.waitForFocus(function() {fullScreenElement().mozRequestFullScreen();});
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="full-screen-element"></div>
|
||||
|
|
|
@ -165,13 +165,6 @@ public:
|
|||
const PRUint32 mRate;
|
||||
};
|
||||
|
||||
static PRUint32 gStateMachineCount = 0;
|
||||
static nsIThread* gStateMachineThread = 0;
|
||||
|
||||
nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread() {
|
||||
return gStateMachineThread;
|
||||
}
|
||||
|
||||
// Shuts down a thread asynchronously.
|
||||
class ShutdownThreadEvent : public nsRunnable
|
||||
{
|
||||
|
@ -187,6 +180,161 @@ private:
|
|||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
// Owns the global state machine thread and counts of
|
||||
// state machine and decoder threads. There should
|
||||
// only be one instance of this class.
|
||||
class StateMachineTracker
|
||||
{
|
||||
private:
|
||||
StateMachineTracker() :
|
||||
mMonitor("media.statemachinetracker"),
|
||||
mStateMachineCount(0),
|
||||
mDecodeThreadCount(0),
|
||||
mStateMachineThread(nsnull)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StateMachineTracker);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
}
|
||||
|
||||
~StateMachineTracker()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
MOZ_COUNT_DTOR(StateMachineTracker);
|
||||
}
|
||||
|
||||
public:
|
||||
// Access singleton instance. This is initially called on the main
|
||||
// thread in the nsBuiltinDecoderStateMachine constructor resulting
|
||||
// in the global object being created lazily. Non-main thread
|
||||
// access always occurs after this and uses the monitor to
|
||||
// safely access the decode thread counts.
|
||||
static StateMachineTracker& Instance();
|
||||
|
||||
// Instantiate the global state machine thread if required.
|
||||
// Call on main thread only.
|
||||
void EnsureGlobalStateMachine();
|
||||
|
||||
// Destroy global state machine thread if required.
|
||||
// Call on main thread only.
|
||||
void CleanupGlobalStateMachine();
|
||||
|
||||
// Return the global state machine thread. Call from any thread.
|
||||
nsIThread* GetGlobalStateMachineThread()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!");
|
||||
return mStateMachineThread;
|
||||
}
|
||||
|
||||
// Maximum number of active decode threads allowed. When more
|
||||
// than this number are active the thread creation will fail.
|
||||
static const PRUint32 MAX_DECODE_THREADS = 25;
|
||||
|
||||
// Returns the number of active decode threads.
|
||||
// Call on any thread. Holds the internal monitor so don't
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
PRUint32 GetDecodeThreadCount();
|
||||
|
||||
// Keep track of the fact that a decode thread was created.
|
||||
// Call on any thread. Holds the internal monitor so don't
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
void NoteDecodeThreadCreated();
|
||||
|
||||
// Keep track of the fact that a decode thread was destroyed.
|
||||
// Call on any thread. Holds the internal monitor so don't
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
void NoteDecodeThreadDestroyed();
|
||||
|
||||
private:
|
||||
// Holds global instance of StateMachineTracker.
|
||||
// Writable on main thread only.
|
||||
static StateMachineTracker* mInstance;
|
||||
|
||||
// Reentrant monitor that must be obtained to access
|
||||
// the decode thread count member and methods.
|
||||
ReentrantMonitor mMonitor;
|
||||
|
||||
// Number of instances of nsBuiltinDecoderStateMachine
|
||||
// that are currently instantiated. Access on the
|
||||
// main thread only.
|
||||
PRUint32 mStateMachineCount;
|
||||
|
||||
// Number of instances of decoder threads that are
|
||||
// currently instantiated. Access only with the
|
||||
// mMonitor lock held. Can be used from any thread.
|
||||
PRUint32 mDecodeThreadCount;
|
||||
|
||||
// Global state machine thread. Write on the main thread
|
||||
// only, read from the decoder threads. Synchronized via
|
||||
// the mMonitor.
|
||||
nsIThread* mStateMachineThread;
|
||||
};
|
||||
|
||||
StateMachineTracker* StateMachineTracker::mInstance = nsnull;
|
||||
|
||||
StateMachineTracker& StateMachineTracker::Instance()
|
||||
{
|
||||
if (!mInstance) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mInstance = new StateMachineTracker();
|
||||
}
|
||||
return *mInstance;
|
||||
}
|
||||
|
||||
void StateMachineTracker::EnsureGlobalStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mStateMachineCount == 0) {
|
||||
NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!");
|
||||
nsresult res = NS_NewThread(&mStateMachineThread,
|
||||
nsnull);
|
||||
NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread");
|
||||
}
|
||||
mStateMachineCount++;
|
||||
}
|
||||
|
||||
void StateMachineTracker::CleanupGlobalStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ABORT_IF_FALSE(mStateMachineCount > 0,
|
||||
"State machine ref count must be > 0");
|
||||
mStateMachineCount--;
|
||||
if (mStateMachineCount == 0) {
|
||||
LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread);
|
||||
NS_RELEASE(mStateMachineThread);
|
||||
mStateMachineThread = nsnull;
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero.");
|
||||
mInstance = nsnull;
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachineTracker::NoteDecodeThreadCreated()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
++mDecodeThreadCount;
|
||||
}
|
||||
|
||||
void StateMachineTracker::NoteDecodeThreadDestroyed()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
--mDecodeThreadCount;
|
||||
}
|
||||
|
||||
PRUint32 StateMachineTracker::GetDecodeThreadCount()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
return mDecodeThreadCount;
|
||||
}
|
||||
|
||||
nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
|
||||
nsBuiltinDecoderReader* aReader,
|
||||
bool aRealTime) :
|
||||
|
@ -221,14 +369,8 @@ nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDe
|
|||
{
|
||||
MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (gStateMachineCount == 0) {
|
||||
NS_ASSERTION(!gStateMachineThread, "Should have null state machine thread!");
|
||||
nsresult res = NS_NewThread(&gStateMachineThread,
|
||||
nsnull,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread");
|
||||
}
|
||||
gStateMachineCount++;
|
||||
|
||||
StateMachineTracker::Instance().EnsureGlobalStateMachine();
|
||||
|
||||
// only enable realtime mode when "media.realtime_decoder.enabled" is true.
|
||||
if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
|
||||
|
@ -246,17 +388,7 @@ nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
|
|||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ABORT_IF_FALSE(gStateMachineCount > 0,
|
||||
"State machine ref count must be > 0");
|
||||
gStateMachineCount--;
|
||||
if (gStateMachineCount == 0) {
|
||||
LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
|
||||
nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(gStateMachineThread);
|
||||
NS_RELEASE(gStateMachineThread);
|
||||
gStateMachineThread = nsnull;
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
StateMachineTracker::Instance().CleanupGlobalStateMachine();
|
||||
}
|
||||
|
||||
bool nsBuiltinDecoderStateMachine::HasFutureAudio() const {
|
||||
|
@ -1056,6 +1188,7 @@ void nsBuiltinDecoderStateMachine::StopDecodeThread()
|
|||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
mDecodeThread->Shutdown();
|
||||
StateMachineTracker::Instance().NoteDecodeThreadDestroyed();
|
||||
}
|
||||
mDecodeThread = nsnull;
|
||||
mDecodeThreadIdle = false;
|
||||
|
@ -1082,23 +1215,48 @@ nsBuiltinDecoderStateMachine::StartDecodeThread()
|
|||
{
|
||||
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
PRUint32 count = 0;
|
||||
bool created = false;
|
||||
{
|
||||
ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
|
||||
count = StateMachineTracker::Instance().GetDecodeThreadCount();
|
||||
}
|
||||
|
||||
mStopDecodeThread = false;
|
||||
if ((mDecodeThread && !mDecodeThreadIdle) || mState >= DECODER_STATE_COMPLETED)
|
||||
return NS_OK;
|
||||
|
||||
if (!mDecodeThread && count > StateMachineTracker::MAX_DECODE_THREADS) {
|
||||
// Have to run one iteration of the state machine loop to ensure the
|
||||
// shutdown state is processed.
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mDecodeThread) {
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread),
|
||||
nsnull,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Have to run one iteration of the state machine loop to ensure the
|
||||
// shutdown state is processed.
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return rv;
|
||||
}
|
||||
created = true;
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun);
|
||||
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
mDecodeThreadIdle = false;
|
||||
|
||||
if (created) {
|
||||
ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
|
||||
StateMachineTracker::Instance().NoteDecodeThreadCreated();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1974,7 +2132,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine() {
|
|||
|
||||
nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) {
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
NS_ABORT_IF_FALSE(gStateMachineThread,
|
||||
NS_ABORT_IF_FALSE(GetStateMachineThread(),
|
||||
"Must have a state machine thread to schedule");
|
||||
|
||||
if (mState == DECODER_STATE_SHUTDOWN) {
|
||||
|
@ -2009,7 +2167,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) {
|
|||
// We're not currently running this state machine on the state machine
|
||||
// thread. Dispatch an event to run one cycle of the state machine.
|
||||
mDispatchedRunEvent = true;
|
||||
return gStateMachineThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
// We're not currently running this state machine on the state machine
|
||||
// thread, but something has already dispatched an event to run it again,
|
||||
|
@ -2023,7 +2181,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) {
|
|||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
|
||||
if (NS_FAILED(res)) return res;
|
||||
mTimer->SetTarget(gStateMachineThread);
|
||||
mTimer->SetTarget(GetStateMachineThread());
|
||||
}
|
||||
|
||||
res = mTimer->InitWithFuncCallback(::TimeoutExpired,
|
||||
|
@ -2032,3 +2190,15 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) {
|
|||
nsITimer::TYPE_ONE_SHOT);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool nsBuiltinDecoderStateMachine::OnStateMachineThread() const
|
||||
{
|
||||
return IsCurrentThread(GetStateMachineThread());
|
||||
}
|
||||
|
||||
nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread()
|
||||
{
|
||||
return StateMachineTracker::Instance().GetGlobalStateMachineThread();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -205,9 +205,7 @@ public:
|
|||
return IsCurrentThread(mAudioThread);
|
||||
}
|
||||
|
||||
bool OnStateMachineThread() const {
|
||||
return IsCurrentThread(GetStateMachineThread());
|
||||
}
|
||||
bool OnStateMachineThread() const;
|
||||
|
||||
nsresult GetBuffered(nsTimeRanges* aBuffered);
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
var ITERATIONS = 200;
|
||||
|
||||
function stoptest ()
|
||||
{
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
function boom()
|
||||
{
|
||||
for (var i = 0; i < ITERATIONS; ++i) {
|
||||
a = document.createElementNS("http://www.w3.org/1999/xhtml", "audio");
|
||||
a.addEventListener("loadedmetadata", stoptest);
|
||||
a.setAttributeNS(null, "autoplay", "");
|
||||
a.setAttributeNS(null, "src", "sound.ogg");
|
||||
}
|
||||
setTimeout(stoptest, 250);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
|
@ -8,3 +8,4 @@ load 493915-1.html
|
|||
skip-if(Android) load 495794-1.html
|
||||
load 492286-1.xhtml
|
||||
load 576612-1.html
|
||||
load 691096-1.html
|
||||
|
|
|
@ -233,6 +233,7 @@ ContentChild* ContentChild::sSingleton;
|
|||
ContentChild::ContentChild()
|
||||
#ifdef ANDROID
|
||||
: mScreenSize(0, 0)
|
||||
, mID(PRUint64(-1))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
@ -803,5 +804,15 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvSetID(const PRUint64 &id)
|
||||
{
|
||||
if (mID != PRUint64(-1)) {
|
||||
NS_WARNING("Setting content child's ID twice?");
|
||||
}
|
||||
mID = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
virtual bool RecvCycleCollect();
|
||||
|
||||
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID);
|
||||
virtual bool RecvSetID(const PRUint64 &id);
|
||||
|
||||
#ifdef ANDROID
|
||||
gfxIntSize GetScreenSize() { return mScreenSize; }
|
||||
|
@ -177,6 +178,8 @@ public:
|
|||
// cache the value
|
||||
nsString &GetIndexedDBPath();
|
||||
|
||||
PRUint64 GetID() { return mID; }
|
||||
|
||||
private:
|
||||
NS_OVERRIDE
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
|
@ -198,6 +201,15 @@ private:
|
|||
|
||||
AppInfo mAppInfo;
|
||||
|
||||
/**
|
||||
* An ID unique to the process containing our corresponding
|
||||
* content parent.
|
||||
*
|
||||
* We expect our content parent to set this ID immediately after opening a
|
||||
* channel to us.
|
||||
*/
|
||||
PRUint64 mID;
|
||||
|
||||
static ContentChild* sSingleton;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
|
||||
|
|
|
@ -162,6 +162,9 @@ MemoryReportRequestParent::~MemoryReportRequestParent()
|
|||
|
||||
nsTArray<ContentParent*>* ContentParent::gContentParents;
|
||||
|
||||
// The first content child has ID 1, so the chrome process can have ID 0.
|
||||
static PRUint64 gContentChildID = 1;
|
||||
|
||||
ContentParent*
|
||||
ContentParent::GetNewOrUsed()
|
||||
{
|
||||
|
@ -424,6 +427,7 @@ ContentParent::ContentParent()
|
|||
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content);
|
||||
mSubprocess->AsyncLaunch();
|
||||
Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
|
||||
unused << SendSetID(gContentChildID++);
|
||||
|
||||
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
|
||||
nsChromeRegistryChrome* chromeRegistry =
|
||||
|
|
|
@ -147,6 +147,8 @@ child:
|
|||
|
||||
AppInfo(nsCString version, nsCString buildID);
|
||||
|
||||
SetID(PRUint64 id);
|
||||
|
||||
parent:
|
||||
PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
|
||||
|
||||
|
|
|
@ -303,6 +303,14 @@ GetTabChildFrom(nsIPresShell* aPresShell)
|
|||
return GetTabChildFrom(docShell);
|
||||
}
|
||||
|
||||
inline TabChild*
|
||||
GetTabChildFrom(nsIDOMWindow* aWindow)
|
||||
{
|
||||
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
|
||||
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
|
||||
return GetTabChildFrom(docShell);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ TabParent::TabParent()
|
|||
, mIMECompositionEnding(false)
|
||||
, mIMESeqno(0)
|
||||
, mDPI(0)
|
||||
, mActive(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -217,15 +218,23 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size)
|
|||
void
|
||||
TabParent::Activate()
|
||||
{
|
||||
mActive = true;
|
||||
unused << SendActivate();
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::Deactivate()
|
||||
{
|
||||
mActive = false;
|
||||
unused << SendDeactivate();
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::Active()
|
||||
{
|
||||
return mActive;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::Init(nsIDOMWindow *window)
|
||||
{
|
||||
|
|
|
@ -131,6 +131,13 @@ public:
|
|||
void UpdateDimensions(const nsRect& rect, const nsIntSize& size);
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
/**
|
||||
* Is this object active? That is, was Activate() called more recently than
|
||||
* Deactivate()?
|
||||
*/
|
||||
bool Active();
|
||||
|
||||
void SendMouseEvent(const nsAString& aType, float aX, float aY,
|
||||
PRInt32 aButton, PRInt32 aClickCount,
|
||||
PRInt32 aModifiers, bool aIgnoreRootScrollFrame);
|
||||
|
@ -221,6 +228,7 @@ protected:
|
|||
PRUint32 mIMESeqno;
|
||||
|
||||
float mDPI;
|
||||
bool mActive;
|
||||
|
||||
private:
|
||||
already_AddRefed<nsFrameLoader> GetFrameLoader() const;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.location" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
|
||||
|
|
|
@ -1043,6 +1043,22 @@ public class GeckoAppShell
|
|||
HapticFeedbackConstants.VIRTUAL_KEY);
|
||||
}
|
||||
|
||||
private static Vibrator vibrator() {
|
||||
return (Vibrator) GeckoApp.surfaceView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
public static void vibrate(long milliseconds) {
|
||||
vibrator().vibrate(milliseconds);
|
||||
}
|
||||
|
||||
public static void vibrate(long[] pattern, int repeat) {
|
||||
vibrator().vibrate(pattern, repeat);
|
||||
}
|
||||
|
||||
public static void cancelVibrate() {
|
||||
vibrator().cancel();
|
||||
}
|
||||
|
||||
public static void showInputMethodPicker() {
|
||||
InputMethodManager imm = (InputMethodManager) GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showInputMethodPicker();
|
||||
|
@ -1615,4 +1631,17 @@ public class GeckoAppShell
|
|||
public static float[] getCurrentBatteryInformation() {
|
||||
return GeckoBatteryManager.getCurrentInformation();
|
||||
}
|
||||
|
||||
public static boolean isTablet() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 9) {
|
||||
Configuration config = GeckoApp.mAppContext.getResources().getConfiguration();
|
||||
// xlarge is defined by android as screens larger than 960dp x 720dp
|
||||
// and should include most devices ~7in and up.
|
||||
// http://developer.android.com/guide/practices/screens_support.html
|
||||
if ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
214
hal/Hal.cpp
214
hal/Hal.cpp
|
@ -1,5 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: sw=2 ts=8 et ft=cpp : */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -38,10 +38,24 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "Hal.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/Observer.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsITabChild.h"
|
||||
#include "nsIDocShell.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::services;
|
||||
|
||||
#define PROXY_IF_SANDBOXED(_call) \
|
||||
do { \
|
||||
|
@ -55,23 +69,213 @@
|
|||
namespace mozilla {
|
||||
namespace hal {
|
||||
|
||||
static void
|
||||
PRLogModuleInfo *sHalLog = PR_LOG_DEFINE("hal");
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
AssertMainThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
static bool
|
||||
bool
|
||||
InSandbox()
|
||||
{
|
||||
return GeckoProcessType_Content == XRE_GetProcessType();
|
||||
}
|
||||
|
||||
bool
|
||||
WindowIsActive(nsIDOMWindow *window)
|
||||
{
|
||||
NS_ENSURE_TRUE(window, false);
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> doc;
|
||||
window->GetDocument(getter_AddRefs(doc));
|
||||
NS_ENSURE_TRUE(doc, false);
|
||||
|
||||
bool hidden = true;
|
||||
doc->GetMozHidden(&hidden);
|
||||
return !hidden;
|
||||
}
|
||||
|
||||
nsAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
|
||||
|
||||
// This observer makes sure we delete gLastIDToVibrate, so we don't
|
||||
// leak.
|
||||
class ShutdownObserver : public nsIObserver
|
||||
{
|
||||
public:
|
||||
ShutdownObserver() {}
|
||||
virtual ~ShutdownObserver() {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
|
||||
gLastIDToVibrate = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
|
||||
|
||||
void InitLastIDToVibrate()
|
||||
{
|
||||
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService = GetObserverService();
|
||||
if (!observerService) {
|
||||
NS_WARNING("Could not get observer service!");
|
||||
return;
|
||||
}
|
||||
|
||||
ShutdownObserver *obs = new ShutdownObserver();
|
||||
observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
WindowIdentifier::WindowIdentifier()
|
||||
: mWindow(nsnull)
|
||||
, mIsEmpty(true)
|
||||
{
|
||||
}
|
||||
|
||||
WindowIdentifier::WindowIdentifier(nsIDOMWindow *window)
|
||||
: mWindow(window)
|
||||
, mIsEmpty(false)
|
||||
{
|
||||
mID.AppendElement(GetWindowID());
|
||||
}
|
||||
|
||||
WindowIdentifier::WindowIdentifier(nsCOMPtr<nsIDOMWindow> &window)
|
||||
: mWindow(window)
|
||||
, mIsEmpty(false)
|
||||
{
|
||||
mID.AppendElement(GetWindowID());
|
||||
}
|
||||
|
||||
WindowIdentifier::WindowIdentifier(const nsTArray<uint64> &id, nsIDOMWindow *window)
|
||||
: mID(id)
|
||||
, mWindow(window)
|
||||
, mIsEmpty(false)
|
||||
{
|
||||
mID.AppendElement(GetWindowID());
|
||||
}
|
||||
|
||||
WindowIdentifier::WindowIdentifier(const WindowIdentifier &other)
|
||||
: mID(other.mID)
|
||||
, mWindow(other.mWindow)
|
||||
, mIsEmpty(other.mIsEmpty)
|
||||
{
|
||||
}
|
||||
|
||||
const InfallibleTArray<uint64>&
|
||||
WindowIdentifier::AsArray() const
|
||||
{
|
||||
MOZ_ASSERT(!mIsEmpty);
|
||||
return mID;
|
||||
}
|
||||
|
||||
bool
|
||||
WindowIdentifier::HasTraveledThroughIPC() const
|
||||
{
|
||||
MOZ_ASSERT(!mIsEmpty);
|
||||
return mID.Length() >= 2;
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern)
|
||||
WindowIdentifier::AppendProcessID()
|
||||
{
|
||||
MOZ_ASSERT(!mIsEmpty);
|
||||
mID.AppendElement(ContentChild::GetSingleton()->GetID());
|
||||
}
|
||||
|
||||
uint64
|
||||
WindowIdentifier::GetWindowID() const
|
||||
{
|
||||
MOZ_ASSERT(!mIsEmpty);
|
||||
nsCOMPtr<nsPIDOMWindow> pidomWindow = do_QueryInterface(mWindow);
|
||||
if (!pidomWindow) {
|
||||
return uint64(-1);
|
||||
}
|
||||
return pidomWindow->WindowID();
|
||||
}
|
||||
|
||||
nsIDOMWindow*
|
||||
WindowIdentifier::GetWindow() const
|
||||
{
|
||||
MOZ_ASSERT(!mIsEmpty);
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
PROXY_IF_SANDBOXED(Vibrate(pattern));
|
||||
|
||||
// Only active windows may start vibrations. If |id| hasn't gone
|
||||
// through the IPC layer -- that is, if our caller is the outside
|
||||
// world, not hal_proxy -- check whether the window is active. If
|
||||
// |id| has gone through IPC, don't check the window's visibility;
|
||||
// only the window corresponding to the bottommost process has its
|
||||
// visibility state set correctly.
|
||||
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
|
||||
HAL_LOG(("Vibrate: Window is inactive, dropping vibrate."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (InSandbox()) {
|
||||
hal_sandbox::Vibrate(pattern, id);
|
||||
}
|
||||
else {
|
||||
if (!gLastIDToVibrate)
|
||||
InitLastIDToVibrate();
|
||||
*gLastIDToVibrate = id.AsArray();
|
||||
|
||||
HAL_LOG(("Vibrate: Forwarding to hal_impl."));
|
||||
|
||||
// hal_impl doesn't need |id|. Send it an empty id, which will
|
||||
// assert if it's used.
|
||||
hal_impl::Vibrate(pattern, WindowIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
|
||||
// Although only active windows may start vibrations, a window may
|
||||
// cancel its own vibration even if it's no longer active.
|
||||
//
|
||||
// After a window is marked as inactive, it sends a CancelVibrate
|
||||
// request. We want this request to cancel a playing vibration
|
||||
// started by that window, so we certainly don't want to reject the
|
||||
// cancellation request because the window is now inactive.
|
||||
//
|
||||
// But it could be the case that, after this window became inactive,
|
||||
// some other window came along and started a vibration. We don't
|
||||
// want this window's cancellation request to cancel that window's
|
||||
// actively-playing vibration!
|
||||
//
|
||||
// To solve this problem, we keep track of the id of the last window
|
||||
// to start a vibration, and only accepts cancellation requests from
|
||||
// the same window. All other cancellation requests are ignored.
|
||||
|
||||
if (InSandbox()) {
|
||||
hal_sandbox::CancelVibrate(id);
|
||||
}
|
||||
else if (*gLastIDToVibrate == id.AsArray()) {
|
||||
// Don't forward our ID to hal_impl. It doesn't need it, and we
|
||||
// don't want it to be tempted to read it. The empty identifier
|
||||
// will assert if it's used.
|
||||
HAL_LOG(("CancelVibrate: Forwarding to hal_impl."));
|
||||
hal_impl::CancelVibrate(WindowIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
class BatteryObserversManager
|
||||
|
|
143
hal/Hal.h
143
hal/Hal.h
|
@ -1,5 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: sw=2 ts=8 et ft=cpp : */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -43,9 +43,120 @@
|
|||
#include "base/basictypes.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "prlog.h"
|
||||
#include "mozilla/dom/battery/Types.h"
|
||||
|
||||
#ifndef MOZ_HAL_NAMESPACE
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class TabChild;
|
||||
class PBrowserChild;
|
||||
}
|
||||
}
|
||||
|
||||
// Only include this hunk of code once, and include it before
|
||||
// HalImpl.h and HalSandbox.h.
|
||||
namespace mozilla {
|
||||
namespace hal {
|
||||
|
||||
extern PRLogModuleInfo *sHalLog;
|
||||
#define HAL_LOG(msg) PR_LOG(sHalLog, PR_LOG_DEBUG, msg)
|
||||
|
||||
/**
|
||||
* This class serves two purposes.
|
||||
*
|
||||
* First, this class wraps a pointer to a window.
|
||||
*
|
||||
* Second, WindowIdentifier lets us uniquely identify a window across
|
||||
* processes. A window exposes an ID which is unique only within its
|
||||
* process. Thus to identify a window, we need to know the ID of the
|
||||
* process which contains it. But the scope of a process's ID is its
|
||||
* parent; that is, two processes with different parents might have
|
||||
* the same ID.
|
||||
*
|
||||
* So to identify a window, we need its ID plus the IDs of all the
|
||||
* processes in the path from the window's process to the root
|
||||
* process. We throw in the IDs of the intermediate windows (a
|
||||
* content window is contained in a window at each level of the
|
||||
* process tree) for good measures.
|
||||
*
|
||||
* You can access this list of IDs by calling AsArray().
|
||||
*/
|
||||
class WindowIdentifier
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create an empty WindowIdentifier. Calls to any of this object's
|
||||
* public methods will assert -- an empty WindowIdentifier may be
|
||||
* used only as a placeholder to code which promises not to touch
|
||||
* the object.
|
||||
*/
|
||||
WindowIdentifier();
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
WindowIdentifier(const WindowIdentifier& other);
|
||||
|
||||
/**
|
||||
* Wrap the given window in a WindowIdentifier. These two
|
||||
* constructors automatically grab the window's ID and append it to
|
||||
* the array of IDs.
|
||||
*
|
||||
* Note that these constructors allow an implicit conversion to a
|
||||
* WindowIdentifier.
|
||||
*/
|
||||
WindowIdentifier(nsIDOMWindow* window);
|
||||
WindowIdentifier(nsCOMPtr<nsIDOMWindow> &window);
|
||||
|
||||
/**
|
||||
* Create a new WindowIdentifier with the given id array and window.
|
||||
* This automatically grabs the window's ID and appends it to the
|
||||
* array.
|
||||
*/
|
||||
WindowIdentifier(const nsTArray<uint64>& id, nsIDOMWindow* window);
|
||||
|
||||
/**
|
||||
* Get the list of window and process IDs we contain.
|
||||
*/
|
||||
typedef InfallibleTArray<uint64> IDArrayType;
|
||||
const IDArrayType& AsArray() const;
|
||||
|
||||
/**
|
||||
* Append the ID of the ContentChild singleton to our array of
|
||||
* window/process IDs.
|
||||
*/
|
||||
void AppendProcessID();
|
||||
|
||||
/**
|
||||
* Does this WindowIdentifier identify both a window and the process
|
||||
* containing that window? If so, we say it has traveled through
|
||||
* IPC.
|
||||
*/
|
||||
bool HasTraveledThroughIPC() const;
|
||||
|
||||
/**
|
||||
* Get the window this object wraps.
|
||||
*/
|
||||
nsIDOMWindow* GetWindow() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get the ID of the window object we wrap.
|
||||
*/
|
||||
uint64 GetWindowID() const;
|
||||
|
||||
AutoInfallibleTArray<uint64, 3> mID;
|
||||
nsCOMPtr<nsIDOMWindow> mWindow;
|
||||
bool mIsEmpty;
|
||||
};
|
||||
|
||||
} // namespace hal
|
||||
} // namespace mozilla
|
||||
|
||||
// This goop plays some cpp tricks to ensure a uniform API across the
|
||||
// API entry point, "sandbox" implementations (for content processes),
|
||||
// and "impl" backends where the real work happens. After this runs
|
||||
|
@ -78,8 +189,32 @@ namespace MOZ_HAL_NAMESPACE /*hal*/ {
|
|||
* |pattern| is an "on" element, the next is "off", and so on.
|
||||
*
|
||||
* If |pattern| is empty, any in-progress vibration is canceled.
|
||||
*
|
||||
* Only an active window within an active tab may call Vibrate; calls
|
||||
* from inactive windows and windows on inactive tabs do nothing.
|
||||
*
|
||||
* If you're calling hal::Vibrate from the outside world, pass an
|
||||
* nsIDOMWindow* or an nsCOMPtr<nsIDOMWindow>& in place of the
|
||||
* WindowIdentifier parameter. It'll be converted to a WindowIdentifier
|
||||
* automatically.
|
||||
*/
|
||||
void Vibrate(const nsTArray<uint32>& pattern);
|
||||
void Vibrate(const nsTArray<uint32>& pattern,
|
||||
const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Cancel a vibration started by the content window identified by
|
||||
* WindowIdentifier.
|
||||
*
|
||||
* If the window was the last window to start a vibration, the
|
||||
* cancellation request will go through even if the window is not
|
||||
* active.
|
||||
*
|
||||
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from
|
||||
* the outside world, pass an nsIDOMWindow* or an
|
||||
* nsCOMPtr<nsIDOMWindow>&. This will automatically be converted to a
|
||||
* WindowIdentifier object.
|
||||
*/
|
||||
void CancelVibrate(const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Inform the battery backend there is a new battery observer.
|
||||
|
@ -122,8 +257,8 @@ void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
|||
*/
|
||||
void NotifyBatteryChange(const hal::BatteryInformation& aBatteryInfo);
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace MOZ_HAL_NAMESPACE
|
||||
} // namespace mozilla
|
||||
|
||||
#ifdef MOZ_DEFINED_HAL_NAMESPACE
|
||||
# undef MOZ_DEFINED_HAL_NAMESPACE
|
||||
|
|
|
@ -38,12 +38,39 @@
|
|||
#include "Hal.h"
|
||||
#include "AndroidBridge.h"
|
||||
|
||||
using mozilla::hal::WindowIdentifier;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern)
|
||||
{}
|
||||
Vibrate(const nsTArray<uint32> &pattern, const WindowIdentifier &)
|
||||
{
|
||||
// Ignore the WindowIdentifier parameter; it's here only because hal::Vibrate,
|
||||
// hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same
|
||||
// signature.
|
||||
|
||||
AndroidBridge* b = AndroidBridge::Bridge();
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pattern.Length() == 0) {
|
||||
b->CancelVibrate();
|
||||
} else {
|
||||
b->Vibrate(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &)
|
||||
{
|
||||
// Ignore WindowIdentifier parameter.
|
||||
|
||||
AndroidBridge* b = AndroidBridge::Bridge();
|
||||
if (b)
|
||||
b->CancelVibrate();
|
||||
}
|
||||
|
||||
void
|
||||
EnableBatteryNotifications()
|
||||
|
|
|
@ -40,11 +40,17 @@
|
|||
#include "Hal.h"
|
||||
#include "mozilla/dom/battery/Constants.h"
|
||||
|
||||
using mozilla::hal::WindowIdentifier;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern)
|
||||
Vibrate(const nsTArray<uint32>& pattern, const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
void
|
||||
CancelVibrate(const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
void
|
||||
|
|
|
@ -45,7 +45,11 @@ namespace mozilla {
|
|||
namespace hal_impl {
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern)
|
||||
Vibrate(const nsTArray<uint32>& pattern, const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
void
|
||||
CancelVibrate(const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
#ifndef MOZ_ENABLE_DBUS
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
include protocol PContent;
|
||||
include protocol PBrowser;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -57,7 +58,8 @@ child:
|
|||
NotifyBatteryChange(BatteryInformation aBatteryInfo);
|
||||
|
||||
parent:
|
||||
Vibrate(uint32[] pattern);
|
||||
Vibrate(uint32[] pattern, uint64[] id, PBrowser browser);
|
||||
CancelVibrate(uint64[] id, PBrowser browser);
|
||||
|
||||
EnableBatteryNotifications();
|
||||
DisableBatteryNotifications();
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/hal_sandbox/PHalChild.h"
|
||||
#include "mozilla/hal_sandbox/PHalParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/battery/Types.h"
|
||||
#include "mozilla/Observer.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
@ -63,10 +65,25 @@ Hal()
|
|||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32>& pattern)
|
||||
Vibrate(const nsTArray<uint32>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG(("Vibrate: Sending to parent process."));
|
||||
|
||||
AutoInfallibleTArray<uint32, 8> p(pattern);
|
||||
Hal()->SendVibrate(p);
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendVibrate(p, newID.AsArray(), GetTabChildFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG(("CancelVibrate: Sending to parent process."));
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendCancelVibrate(newID.AsArray(), GetTabChildFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -91,11 +108,41 @@ class HalParent : public PHalParent
|
|||
, public BatteryObserver {
|
||||
public:
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvVibrate(const InfallibleTArray<unsigned int>& pattern) {
|
||||
RecvVibrate(const InfallibleTArray<unsigned int>& pattern,
|
||||
const InfallibleTArray<uint64> &id,
|
||||
PBrowserParent *browserParent)
|
||||
{
|
||||
// Check whether browserParent is active. We should have already
|
||||
// checked that the corresponding window is active, but this check
|
||||
// isn't redundant. A window may be inactive in an active
|
||||
// browser. And a window is not notified synchronously when it's
|
||||
// deactivated, so the window may think it's active when the tab
|
||||
// is actually inactive.
|
||||
TabParent *tabParent = static_cast<TabParent*>(browserParent);
|
||||
if (!tabParent->Active()) {
|
||||
HAL_LOG(("RecvVibrate: Tab is not active. Cancelling."));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forward to hal::, not hal_impl::, because we might be a
|
||||
// subprocess of another sandboxed process. The hal:: entry point
|
||||
// will do the right thing.
|
||||
hal::Vibrate(pattern);
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(tabParent->GetBrowserDOMWindow());
|
||||
WindowIdentifier newID(id, window);
|
||||
hal::Vibrate(pattern, newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvCancelVibrate(const InfallibleTArray<uint64> &id,
|
||||
PBrowserParent *browserParent)
|
||||
{
|
||||
TabParent *tabParent = static_cast<TabParent*>(browserParent);
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(tabParent->GetBrowserDOMWindow());
|
||||
WindowIdentifier newID(id, window);
|
||||
hal::CancelVibrate(newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Stuart Parmenter <stuart@mozilla.com>
|
||||
* Federico Mena-Quintero <federico@novell.com>
|
||||
* Bobby Holley <bobbyholley@gmail.com>
|
||||
* Yury Delendik
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -72,6 +73,7 @@ ycc_rgb_convert_argb (j_decompress_ptr cinfo,
|
|||
}
|
||||
|
||||
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
|
||||
static void cmyk_convert_rgb_inverted(JSAMPROW row, JDIMENSION width);
|
||||
|
||||
namespace mozilla {
|
||||
namespace imagelib {
|
||||
|
@ -99,6 +101,32 @@ GetICCProfile(struct jpeg_decompress_struct &info)
|
|||
return profile;
|
||||
}
|
||||
|
||||
#define JPEG_EMBED_MARKER (JPEG_APP0 + 12)
|
||||
|
||||
/*
|
||||
* Check for the EMBED marker for Adobe's CMYK images. When Adobe products save
|
||||
* an image with YCCK components standalone, the CMYK values are inverted;
|
||||
* however, when an image is embedded within a PDF or EPS, it contains normal
|
||||
* values. This is described in Adobe tech note #5116.
|
||||
*/
|
||||
static bool IsEmbedMarkerPresent(struct jpeg_decompress_struct *info)
|
||||
{
|
||||
jpeg_saved_marker_ptr marker;
|
||||
// Looking for the APP12 marker that has only 'EMBED\0' in its data.
|
||||
for (marker = info->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker->marker == JPEG_EMBED_MARKER &&
|
||||
marker->data_length == 6 &&
|
||||
marker->data[0] == 0x45 &&
|
||||
marker->data[1] == 0x4D &&
|
||||
marker->data[2] == 0x42 &&
|
||||
marker->data[3] == 0x45 &&
|
||||
marker->data[4] == 0x44 &&
|
||||
marker->data[5] == 0x00)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
METHODDEF(void) init_source (j_decompress_ptr jd);
|
||||
METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd);
|
||||
METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
|
||||
|
@ -194,7 +222,7 @@ nsJPEGDecoder::InitInternal()
|
|||
mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
|
||||
mSourceMgr.term_source = term_source;
|
||||
|
||||
/* Record app markers for ICC data */
|
||||
/* Record app markers for ICC data and EMBED marker */
|
||||
for (PRUint32 m = 0; m < 16; m++)
|
||||
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
|
||||
}
|
||||
|
@ -297,13 +325,14 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
|
|||
if (profileSpace == icSigRgbData)
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
else
|
||||
// qcms doesn't support ycbcr
|
||||
// qcms doesn't support ycbcr
|
||||
mismatch = true;
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
// qcms doesn't support cmyk
|
||||
mismatch = true;
|
||||
// qcms doesn't support ycck
|
||||
mInvertedCMYK = !IsEmbedMarkerPresent(&mInfo);
|
||||
mismatch = true;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
|
@ -371,6 +400,7 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
|
|||
case JCS_YCCK:
|
||||
/* libjpeg can convert from YCCK to CMYK, but not to RGB */
|
||||
mInfo.out_color_space = JCS_CMYK;
|
||||
mInvertedCMYK = !IsEmbedMarkerPresent(&mInfo);
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
|
@ -626,7 +656,12 @@ nsJPEGDecoder::OutputScanlines(bool* suspend)
|
|||
/* Convert from CMYK to RGB */
|
||||
/* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */
|
||||
/* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */
|
||||
cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
|
||||
/* When EMBED marker is present, the conversion produces the normal (not inverted) CMYK values. */
|
||||
if (mInvertedCMYK) {
|
||||
cmyk_convert_rgb_inverted((JSAMPROW)imageRow, mInfo.output_width);
|
||||
} else {
|
||||
cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
|
||||
}
|
||||
sampleRow += mInfo.output_width;
|
||||
}
|
||||
if (mCMSMode == eCMSMode_All) {
|
||||
|
@ -1168,14 +1203,14 @@ ycc_rgb_convert_argb (j_decompress_ptr cinfo,
|
|||
}
|
||||
|
||||
|
||||
/**************** Inverted CMYK -> RGB conversion **************/
|
||||
/**************** CMYK -> RGB conversions **************/
|
||||
/*
|
||||
* Input is (Inverted) CMYK stored as 4 bytes per pixel.
|
||||
* Input is inverted CMYK stored as 4 bytes per pixel.
|
||||
* Output is RGB stored as 3 bytes per pixel.
|
||||
* @param row Points to row buffer containing the CMYK bytes for each pixel in the row.
|
||||
* @param width Number of pixels in the row.
|
||||
*/
|
||||
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
|
||||
static void cmyk_convert_rgb_inverted(JSAMPROW row, JDIMENSION width)
|
||||
{
|
||||
/* Work from end to front to shrink from 4 bytes per pixel to 3 */
|
||||
JSAMPROW in = row + width*4;
|
||||
|
@ -1213,3 +1248,32 @@ static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
|
|||
out[2] = iY*iK/255; // Blue
|
||||
}
|
||||
}
|
||||
|
||||
/**************** CMYK -> RGB conversions **************/
|
||||
/*
|
||||
* Input is CMYK stored as 4 bytes per pixel.
|
||||
* Output is RGB stored as 3 bytes per pixel.
|
||||
* @param row Points to row buffer containing the CMYK bytes for each pixel in the row.
|
||||
* @param width Number of pixels in the row.
|
||||
*/
|
||||
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
|
||||
{
|
||||
/* Work from end to front to shrink from 4 bytes per pixel to 3 */
|
||||
JSAMPROW in = row + width*4;
|
||||
JSAMPROW out = in;
|
||||
|
||||
for (PRUint32 i = width; i > 0; i--) {
|
||||
in -= 4;
|
||||
out -= 3;
|
||||
|
||||
// Convert from Normal CMYK (0..255) to RGB (0..255)
|
||||
// (see also cmyk_convert_rgb_inverted above)
|
||||
const PRUint32 iC = 255 - in[0];
|
||||
const PRUint32 iM = 255 - in[1];
|
||||
const PRUint32 iY = 255 - in[2];
|
||||
const PRUint32 iK = 255 - in[3];
|
||||
out[0] = iC*iK/255; // Red
|
||||
out[1] = iM*iK/255; // Green
|
||||
out[2] = iY*iK/255; // Blue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
qcms_transform *mTransform;
|
||||
|
||||
bool mReading;
|
||||
bool mInvertedCMYK;
|
||||
|
||||
PRUint32 mCMSMode;
|
||||
};
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 229 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 161 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 237 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.2 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 227 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.7 KiB |
|
@ -24,6 +24,10 @@
|
|||
== jpg-cmyk-1.jpg jpg-cmyk-1.png
|
||||
== jpg-cmyk-2.jpg jpg-cmyk-2.png
|
||||
== jpg-srgb-icc.jpg jpg-srgb-icc.png
|
||||
# YCCK images
|
||||
== jpg-ycck.jpg jpg-ycck.png
|
||||
== jpg-ycck-embed.jpg jpg-ycck-embed.png
|
||||
== jpg-cmyk-embed.jpg jpg-cmyk-embed.png
|
||||
|
||||
# webcam-simulacrum.mjpg is a hand-edited file containing red.jpg and blue.jpg,
|
||||
# concatenated together with the relevant headers for
|
||||
|
|
|
@ -171,10 +171,6 @@ template <> struct IsPodType<double> { static const bool result =
|
|||
template <> struct IsPodType<wchar_t> { static const bool result = true; };
|
||||
template <typename T> struct IsPodType<T *> { static const bool result = true; };
|
||||
|
||||
/* Return the size/end of an array without using macros. */
|
||||
template <class T, size_t N> inline T *ArraySize(T (&)[N]) { return N; }
|
||||
template <class T, size_t N> inline T *ArrayEnd(T (&arr)[N]) { return arr + N; }
|
||||
|
||||
template <bool cond, typename T, T v1, T v2> struct If { static const T result = v1; };
|
||||
template <typename T, T v1, T v2> struct If<false, T, v1, v2> { static const T result = v2; };
|
||||
|
||||
|
|
|
@ -206,14 +206,6 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
|
|||
return sb.empty() ? unescaped : sb.finishString();
|
||||
}
|
||||
|
||||
static bool
|
||||
ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
|
||||
JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
|
||||
{
|
||||
flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
|
||||
return reobj->reset(cx, str, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile a new |RegExpPrivate| for the |RegExpObject|.
|
||||
*
|
||||
|
@ -226,12 +218,15 @@ ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
|
|||
* flags := ToString(flags) if defined(flags) else ''
|
||||
*/
|
||||
static bool
|
||||
CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval)
|
||||
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
|
||||
uintN argc, Value *argv, Value *rval)
|
||||
{
|
||||
if (argc == 0) {
|
||||
if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString))
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -249,9 +244,10 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!obj->reset(cx, sourceObj.asRegExp()))
|
||||
RegExpObject *reobj = builder.build(sourceObj.asRegExp());
|
||||
if (!reobj)
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -285,9 +281,12 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
|||
if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
|
||||
return false;
|
||||
|
||||
if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
|
||||
if (!reobj)
|
||||
return NULL;
|
||||
|
||||
*rval = ObjectValue(*reobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -301,8 +300,8 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp)
|
|||
if (!obj)
|
||||
return ok;
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
|
||||
RegExpObjectBuilder builder(cx, obj->asRegExp());
|
||||
return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval());
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -322,14 +321,11 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp)
|
|||
}
|
||||
}
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
RegExpObjectBuilder builder(cx);
|
||||
if (!CompileRegExpObject(cx, builder, argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
|
||||
if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
|
||||
*vp = ObjectValue(*obj);
|
||||
*vp = ObjectValue(*builder.reobj());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -462,9 +458,11 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj)
|
|||
JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
proto->setPrivate(NULL);
|
||||
|
||||
RegExpObject *reproto = proto->asRegExp();
|
||||
if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
|
||||
RegExpObjectBuilder builder(cx, reproto);
|
||||
if (!builder.build(cx->runtime->emptyString, RegExpFlag(0)))
|
||||
return NULL;
|
||||
|
||||
if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
|
||||
|
@ -526,6 +524,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
|||
* have to take a defensive refcount here.
|
||||
*/
|
||||
AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(rep));
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
|
||||
/* Step 2. */
|
||||
|
|
|
@ -128,7 +128,7 @@ struct dirent *readdir(DIR *d)
|
|||
#endif
|
||||
|
||||
char *
|
||||
ino2name(ino_t ino, char *dir)
|
||||
ino2name(ino_t ino)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
|
@ -230,7 +230,7 @@ reversepath(char *inpath, char *name, int len, char *outpath)
|
|||
if (strcmp(buf, "..") == 0) {
|
||||
if (stat(".", &sb) < 0)
|
||||
fail("cannot stat current directory");
|
||||
name = ino2name(sb.st_ino, "..");
|
||||
name = ino2name(sb.st_ino);
|
||||
len = strlen(name);
|
||||
cp -= len + 1;
|
||||
strcpy(cp, name);
|
||||
|
|
|
@ -59,7 +59,7 @@ extern char *program;
|
|||
|
||||
extern void fail(char *format, ...);
|
||||
extern char *getcomponent(char *path, char *name);
|
||||
extern char *ino2name(ino_t ino, char *dir);
|
||||
extern char *ino2name(ino_t ino);
|
||||
extern void *xmalloc(size_t size);
|
||||
extern char *xstrdup(char *s);
|
||||
extern char *xbasename(char *path);
|
||||
|
|
|
@ -5265,7 +5265,7 @@ EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
static bool
|
||||
EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *&pn)
|
||||
EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
/*
|
||||
* pn represents one of these syntactic constructs:
|
||||
|
@ -5394,7 +5394,7 @@ EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
#endif
|
||||
|
||||
static bool
|
||||
EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok)
|
||||
EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
StmtInfo stmtInfo;
|
||||
StmtInfo *stmt;
|
||||
|
@ -5446,12 +5446,11 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok)
|
|||
if (!EmitLeaveBlock(cx, bce, op, objbox))
|
||||
return false;
|
||||
|
||||
ok = PopStatementBCE(cx, bce);
|
||||
return true;
|
||||
return PopStatementBCE(cx, bce);
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok)
|
||||
EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
StmtInfo stmtInfo;
|
||||
if (!EmitTree(cx, bce, pn->pn_left))
|
||||
|
@ -5467,8 +5466,7 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok)
|
|||
return false;
|
||||
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
|
||||
return false;
|
||||
ok = PopStatementBCE(cx, bce);
|
||||
return true;
|
||||
return PopStatementBCE(cx, bce);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -6125,8 +6123,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
|
||||
case TOK_WITH:
|
||||
if (!EmitWith(cx, bce, pn, ok))
|
||||
return false;
|
||||
ok = EmitWith(cx, bce, pn);
|
||||
break;
|
||||
|
||||
case TOK_TRY:
|
||||
|
@ -6910,8 +6907,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
|
||||
case TOK_LEXICALSCOPE:
|
||||
if (!EmitLexicalScope(cx, bce, pn, ok))
|
||||
return false;
|
||||
ok = EmitLexicalScope(cx, bce, pn);
|
||||
break;
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
|
|
|
@ -3076,60 +3076,70 @@ Parser::forStatement()
|
|||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
#ifdef JS_HAS_BLOCK_SCOPE
|
||||
bool let = false;
|
||||
#endif
|
||||
|
||||
ParseNode *pn1;
|
||||
if (tt == TOK_SEMI) {
|
||||
if (pn->pn_iflags & JSITER_FOREACH) {
|
||||
reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* True if we have 'for (var/let/const ...)', except in the oddball case
|
||||
* where 'let' begins a let-expression in 'for (let (...) ...)'.
|
||||
*/
|
||||
bool forDecl = false;
|
||||
|
||||
/* No initializer -- set first kid of left sub-node to null. */
|
||||
pn1 = NULL;
|
||||
} else {
|
||||
/*
|
||||
* Set pn1 to a var list or an initializing expression.
|
||||
*
|
||||
* Set the TCF_IN_FOR_INIT flag during parsing of the first clause
|
||||
* of the for statement. This flag will be used by the RelExpr
|
||||
* production; if it is set, then the 'in' keyword will not be
|
||||
* recognized as an operator, leaving it available to be parsed as
|
||||
* part of a for/in loop.
|
||||
*
|
||||
* A side effect of this restriction is that (unparenthesized)
|
||||
* expressions involving an 'in' operator are illegal in the init
|
||||
* clause of an ordinary for loop.
|
||||
*/
|
||||
tc->flags |= TCF_IN_FOR_INIT;
|
||||
if (tt == TOK_VAR) {
|
||||
(void) tokenStream.getToken();
|
||||
pn1 = variables(false);
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
} else if (tt == TOK_LET) {
|
||||
let = true;
|
||||
(void) tokenStream.getToken();
|
||||
if (tokenStream.peekToken() == TOK_LP) {
|
||||
pn1 = letBlock(JS_FALSE);
|
||||
tt = TOK_LEXICALSCOPE;
|
||||
} else {
|
||||
pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
|
||||
if (!pnlet)
|
||||
return NULL;
|
||||
blockInfo.flags |= SIF_FOR_BLOCK;
|
||||
pn1 = variables(false);
|
||||
/* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
|
||||
ParseNode *pn1;
|
||||
|
||||
{
|
||||
TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt == TOK_SEMI) {
|
||||
if (pn->pn_iflags & JSITER_FOREACH) {
|
||||
reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
pn1 = NULL;
|
||||
} else {
|
||||
pn1 = expr();
|
||||
/*
|
||||
* Set pn1 to a var list or an initializing expression.
|
||||
*
|
||||
* Set the TCF_IN_FOR_INIT flag during parsing of the first clause
|
||||
* of the for statement. This flag will be used by the RelExpr
|
||||
* production; if it is set, then the 'in' keyword will not be
|
||||
* recognized as an operator, leaving it available to be parsed as
|
||||
* part of a for/in loop.
|
||||
*
|
||||
* A side effect of this restriction is that (unparenthesized)
|
||||
* expressions involving an 'in' operator are illegal in the init
|
||||
* clause of an ordinary for loop.
|
||||
*/
|
||||
tc->flags |= TCF_IN_FOR_INIT;
|
||||
if (tt == TOK_VAR) {
|
||||
forDecl = true;
|
||||
(void) tokenStream.getToken();
|
||||
pn1 = variables(false);
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
} else if (tt == TOK_LET) {
|
||||
let = true;
|
||||
(void) tokenStream.getToken();
|
||||
if (tokenStream.peekToken() == TOK_LP) {
|
||||
pn1 = letBlock(JS_FALSE);
|
||||
} else {
|
||||
forDecl = true;
|
||||
pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
|
||||
if (!pnlet)
|
||||
return NULL;
|
||||
blockInfo.flags |= SIF_FOR_BLOCK;
|
||||
pn1 = variables(false);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
pn1 = expr();
|
||||
}
|
||||
tc->flags &= ~TCF_IN_FOR_INIT;
|
||||
if (!pn1)
|
||||
return NULL;
|
||||
}
|
||||
tc->flags &= ~TCF_IN_FOR_INIT;
|
||||
if (!pn1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3154,8 +3164,7 @@ Parser::forStatement()
|
|||
stmtInfo.type = STMT_FOR_IN_LOOP;
|
||||
|
||||
/* Check that the left side of the 'in' is valid. */
|
||||
JS_ASSERT(!TokenKindIsDecl(tt) || pn1->isKind(tt));
|
||||
if (TokenKindIsDecl(tt)
|
||||
if (forDecl
|
||||
? (pn1->pn_count > 1 || pn1->isOp(JSOP_DEFCONST)
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|| (versionNumber() == JSVERSION_1_7 &&
|
||||
|
@ -3196,7 +3205,7 @@ Parser::forStatement()
|
|||
*/
|
||||
pn2 = NULL;
|
||||
uintN dflag = PND_ASSIGNED;
|
||||
if (TokenKindIsDecl(tt)) {
|
||||
if (forDecl) {
|
||||
/* Tell EmitVariables that pn1 is part of a for/in. */
|
||||
pn1->pn_xflags |= PNX_FORINVAR;
|
||||
|
||||
|
@ -3211,10 +3220,10 @@ Parser::forStatement()
|
|||
*
|
||||
* Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
|
||||
* 'const' to hoist the initializer or the entire decl out of
|
||||
* the loop head. TOK_VAR is the type for both 'var' and 'const'.
|
||||
* the loop head.
|
||||
*/
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
if (tt == TOK_LET) {
|
||||
if (let) {
|
||||
reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3322,7 +3331,7 @@ Parser::forStatement()
|
|||
|
||||
/* Parse the loop condition or null into pn2. */
|
||||
MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
|
||||
tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt == TOK_SEMI) {
|
||||
pn2 = NULL;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// don't assert
|
||||
var m = "aaaa".match(/(?:|a)*/);
|
|
@ -0,0 +1,2 @@
|
|||
// don't hang
|
||||
var m = "aaaa".match(/(?:a?)*/);
|
|
@ -3279,256 +3279,423 @@ array_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
|
|||
return array_indexOfHelper(cx, LastIndexOf, args);
|
||||
}
|
||||
|
||||
/* Order is important; extras that take a predicate funarg must follow MAP. */
|
||||
typedef enum ArrayExtraMode {
|
||||
FOREACH,
|
||||
REDUCE,
|
||||
REDUCE_RIGHT,
|
||||
MAP,
|
||||
FILTER,
|
||||
SOME,
|
||||
EVERY
|
||||
} ArrayExtraMode;
|
||||
|
||||
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
|
||||
|
||||
static JSBool
|
||||
array_extra(JSContext *cx, ArrayExtraMode mode, CallArgs &args)
|
||||
/* ECMA 15.4.4.16-15.4.4.18. */
|
||||
class ArrayForEachBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval) { return false; }
|
||||
static Value lateExitValue() { return UndefinedValue(); }
|
||||
};
|
||||
|
||||
class ArrayEveryBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval)
|
||||
{
|
||||
if (!js_ValueToBoolean(callval)) {
|
||||
*rval = BooleanValue(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static Value lateExitValue() { return BooleanValue(true); }
|
||||
};
|
||||
|
||||
class ArraySomeBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval)
|
||||
{
|
||||
if (js_ValueToBoolean(callval)) {
|
||||
*rval = BooleanValue(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static Value lateExitValue() { return BooleanValue(false); }
|
||||
};
|
||||
|
||||
template <class Behavior>
|
||||
static inline bool
|
||||
array_readonlyCommon(JSContext *cx, CallArgs &args)
|
||||
{
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
jsuint length;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* First, get or compute our callee, so that we error out consistently
|
||||
* when passed a non-callable object.
|
||||
*/
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Set our initial return condition, used for zero-length array cases
|
||||
* (and pre-size our map return to match our known length, for all cases).
|
||||
*/
|
||||
jsuint newlen;
|
||||
JSObject *newarr;
|
||||
TypeObject *newtype = NULL;
|
||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||
newlen = 0;
|
||||
newarr = NULL;
|
||||
#endif
|
||||
jsuint start = 0, end = length;
|
||||
jsint step = 1;
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
switch (mode) {
|
||||
case REDUCE_RIGHT:
|
||||
start = length - 1, end = -1, step = -1;
|
||||
/* FALL THROUGH */
|
||||
case REDUCE:
|
||||
if (length == 0 && args.length() == 1) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (args.length() >= 2) {
|
||||
args.rval() = args[1];
|
||||
} else {
|
||||
JSBool hole;
|
||||
do {
|
||||
if (!GetElement(cx, obj, start, &hole, &args.rval()))
|
||||
return JS_FALSE;
|
||||
start += step;
|
||||
} while (hole && start != end);
|
||||
/* Step 6. */
|
||||
uint32 k = 0;
|
||||
|
||||
if (hole && start == end) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MAP:
|
||||
case FILTER:
|
||||
newlen = (mode == MAP) ? length : 0;
|
||||
newarr = NewDenseAllocatedArray(cx, newlen);
|
||||
if (!newarr)
|
||||
return JS_FALSE;
|
||||
newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return JS_FALSE;
|
||||
newarr->setType(newtype);
|
||||
args.rval().setObject(*newarr);
|
||||
break;
|
||||
case SOME:
|
||||
args.rval().setBoolean(false);
|
||||
break;
|
||||
case EVERY:
|
||||
args.rval().setBoolean(true);
|
||||
break;
|
||||
case FOREACH:
|
||||
args.rval().setUndefined();
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return JS_TRUE;
|
||||
|
||||
Value thisv = (args.length() > 1 && !REDUCE_MODE(mode)) ? args[1] : UndefinedValue();
|
||||
|
||||
/*
|
||||
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
||||
* requires 4 args (accum, value, index, array).
|
||||
*/
|
||||
uintN agArgc = 3 + REDUCE_MODE(mode);
|
||||
|
||||
MUST_FLOW_THROUGH("out");
|
||||
JSBool ok = JS_TRUE;
|
||||
JSBool cond;
|
||||
|
||||
Value objv = ObjectValue(*obj);
|
||||
AutoValueRooter tvr(cx);
|
||||
/* Step 7. */
|
||||
InvokeArgsGuard ag;
|
||||
for (jsuint i = start; i != end; i += step) {
|
||||
JSBool hole;
|
||||
ok = JS_CHECK_OPERATION_LIMIT(cx) &&
|
||||
GetElement(cx, obj, i, &hole, tvr.addr());
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (hole)
|
||||
continue;
|
||||
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, agArgc, &ag))
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Push callable and 'this', then args. We must do this for every
|
||||
* iteration around the loop since Invoke clobbers its arguments.
|
||||
*/
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = thisv;
|
||||
uintN argi = 0;
|
||||
if (REDUCE_MODE(mode))
|
||||
ag[argi++] = args.rval();
|
||||
ag[argi++] = tvr.value();
|
||||
ag[argi++] = Int32Value(i);
|
||||
ag[argi] = objv;
|
||||
/* Step a, b, and c.i. */
|
||||
Value kValue;
|
||||
JSBool kNotPresent;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Do the call. */
|
||||
ok = Invoke(cx, ag);
|
||||
if (!ok)
|
||||
break;
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
|
||||
const Value &rval = ag.rval();
|
||||
|
||||
if (mode > MAP)
|
||||
cond = js_ValueToBoolean(rval);
|
||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||
else
|
||||
cond = JS_FALSE;
|
||||
#endif
|
||||
|
||||
switch (mode) {
|
||||
case FOREACH:
|
||||
break;
|
||||
case REDUCE:
|
||||
case REDUCE_RIGHT:
|
||||
args.rval() = rval;
|
||||
break;
|
||||
case MAP:
|
||||
if (!ok)
|
||||
goto out;
|
||||
ok = SetArrayElement(cx, newarr, i, rval);
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
case FILTER:
|
||||
if (!cond)
|
||||
break;
|
||||
/* The element passed the filter, so push it onto our result. */
|
||||
if (!ok)
|
||||
goto out;
|
||||
ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
case SOME:
|
||||
if (cond) {
|
||||
args.rval().setBoolean(true);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case EVERY:
|
||||
if (!cond) {
|
||||
args.rval().setBoolean(false);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
if (Behavior::shouldExit(ag.rval(), &args.rval()))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ok && mode == FILTER)
|
||||
ok = js_SetLengthProperty(cx, newarr, newlen);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_forEach(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, FOREACH, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_map(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, MAP, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_reduce(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, REDUCE, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_reduceRight(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, REDUCE_RIGHT, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_filter(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, FILTER, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_some(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, SOME, args);
|
||||
}
|
||||
/* Step 8. */
|
||||
args.rval() = Behavior::lateExitValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.16. */
|
||||
static JSBool
|
||||
array_every(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, EVERY, args);
|
||||
return array_readonlyCommon<ArrayEveryBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.17. */
|
||||
static JSBool
|
||||
array_some(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_readonlyCommon<ArraySomeBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.18. */
|
||||
static JSBool
|
||||
array_forEach(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_readonlyCommon<ArrayForEachBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.19. */
|
||||
static JSBool
|
||||
array_map(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
/* Step 6. */
|
||||
JSObject *arr = NewDenseAllocatedArray(cx, len);
|
||||
if (!arr)
|
||||
return false;
|
||||
TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return false;
|
||||
arr->setType(newtype);
|
||||
|
||||
/* Step 7. */
|
||||
uint32 k = 0;
|
||||
|
||||
/* Step 8. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
if(!SetArrayElement(cx, arr, k, ag.rval()))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Step 9. */
|
||||
args.rval().setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.20. */
|
||||
static JSBool
|
||||
array_filter(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
/* Step 6. */
|
||||
JSObject *arr = NewDenseAllocatedArray(cx, 0);
|
||||
if (!arr)
|
||||
return false;
|
||||
TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return false;
|
||||
arr->setType(newtype);
|
||||
|
||||
/* Step 7. */
|
||||
uint32 k = 0;
|
||||
|
||||
/* Step 8. */
|
||||
uint32 to = 0;
|
||||
|
||||
/* Step 9. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
|
||||
if (js_ValueToBoolean(ag.rval())) {
|
||||
if(!SetArrayElement(cx, arr, to, kValue))
|
||||
return false;
|
||||
to++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Step 10. */
|
||||
args.rval().setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.21-15.4.4.22. */
|
||||
class ArrayReduceBehavior
|
||||
{
|
||||
public:
|
||||
static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step)
|
||||
{
|
||||
*start = 0;
|
||||
*step = 1;
|
||||
*end = len;
|
||||
}
|
||||
};
|
||||
|
||||
class ArrayReduceRightBehavior
|
||||
{
|
||||
public:
|
||||
static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step)
|
||||
{
|
||||
*start = len - 1;
|
||||
*step = -1;
|
||||
/*
|
||||
* We rely on (well defined) unsigned integer underflow to check our
|
||||
* end condition after visiting the full range (including 0).
|
||||
*/
|
||||
*end = (uint32)-1;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Behavior>
|
||||
static inline bool
|
||||
array_reduceCommon(JSContext *cx, CallArgs &args)
|
||||
{
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
if (len == 0 && args.length() < 2) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step 6. */
|
||||
uint32 k, end;
|
||||
int32 step;
|
||||
Behavior::initialize(len, &k, &end, &step);
|
||||
|
||||
/* Step 7-8. */
|
||||
Value accumulator;
|
||||
if (args.length() >= 2) {
|
||||
accumulator = args[1];
|
||||
} else {
|
||||
JSBool kNotPresent = true;
|
||||
while (kNotPresent && k != end) {
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &accumulator))
|
||||
return false;
|
||||
k += step;
|
||||
}
|
||||
if (kNotPresent) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 9. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k != end) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag))
|
||||
return false;
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = UndefinedValue();
|
||||
ag[0] = accumulator;
|
||||
ag[1] = kValue;
|
||||
ag[2] = NumberValue(k);
|
||||
ag[3] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
accumulator = ag.rval();
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k += step;
|
||||
}
|
||||
|
||||
/* Step 10. */
|
||||
args.rval() = accumulator;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.21. */
|
||||
static JSBool
|
||||
array_reduce(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_reduceCommon<ArrayReduceBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.22. */
|
||||
static JSBool
|
||||
array_reduceRight(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_reduceCommon<ArrayReduceRightBehavior>(cx, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
|
|
@ -111,6 +111,7 @@ ThreadData::ThreadData()
|
|||
#endif
|
||||
waiveGCQuota(false),
|
||||
tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
repCache(NULL),
|
||||
dtoaState(NULL),
|
||||
nativeStackBase(GetNativeStackBase()),
|
||||
pendingProxyOperation(NULL),
|
||||
|
@ -153,6 +154,31 @@ ThreadData::triggerOperationCallback(JSRuntime *rt)
|
|||
#endif
|
||||
}
|
||||
|
||||
RegExpPrivateCache *
|
||||
ThreadData::createRegExpPrivateCache(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(!repCache);
|
||||
RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
|
||||
|
||||
if (!newCache || !newCache->init()) {
|
||||
rt->delete_<RegExpPrivateCache>(newCache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
repCache = newCache;
|
||||
return repCache;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadData::purgeRegExpPrivateCache(JSRuntime *rt)
|
||||
{
|
||||
if (!repCache)
|
||||
return;
|
||||
|
||||
rt->delete_<RegExpPrivateCache>(repCache);
|
||||
repCache = NULL;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSScript *
|
||||
|
@ -315,6 +341,24 @@ js_PurgeThreads(JSContext *cx)
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js_PurgeThreads_PostGlobalSweep(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
for (JSThread::Map::Enum e(cx->runtime->threads);
|
||||
!e.empty();
|
||||
e.popFront())
|
||||
{
|
||||
JSThread *thread = e.front().value;
|
||||
|
||||
JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList));
|
||||
thread->data.purgeRegExpPrivateCache(cx->runtime);
|
||||
}
|
||||
#else
|
||||
cx->runtime->threadData.purgeRegExpPrivateCache(cx->runtime);
|
||||
#endif
|
||||
}
|
||||
|
||||
JSContext *
|
||||
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
{
|
||||
|
|
|
@ -197,6 +197,25 @@ struct ThreadData {
|
|||
static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
|
||||
LifoAlloc tempLifoAlloc;
|
||||
|
||||
private:
|
||||
js::RegExpPrivateCache *repCache;
|
||||
|
||||
js::RegExpPrivateCache *createRegExpPrivateCache(JSRuntime *rt);
|
||||
|
||||
public:
|
||||
js::RegExpPrivateCache *getRegExpPrivateCache() { return repCache; }
|
||||
|
||||
/* N.B. caller is responsible for reporting OOM. */
|
||||
js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSRuntime *rt) {
|
||||
if (repCache)
|
||||
return repCache;
|
||||
|
||||
return createRegExpPrivateCache(rt);
|
||||
}
|
||||
|
||||
/* Called at the end of the global GC sweep phase to deallocate repCache memory. */
|
||||
void purgeRegExpPrivateCache(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* The GSN cache is per thread since even multi-cx-per-thread embeddings
|
||||
* do not interleave js_GetSrcNote calls.
|
||||
|
@ -1216,6 +1235,8 @@ struct JSContext
|
|||
js::GCHelperThread *gcBackgroundFree;
|
||||
#endif
|
||||
|
||||
js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
|
||||
|
||||
inline void* malloc_(size_t bytes) {
|
||||
return runtime->malloc_(bytes, this);
|
||||
}
|
||||
|
@ -1892,6 +1913,9 @@ js_FinishThreads(JSRuntime *rt);
|
|||
extern void
|
||||
js_PurgeThreads(JSContext *cx);
|
||||
|
||||
extern void
|
||||
js_PurgeThreads_PostGlobalSweep(JSContext *cx);
|
||||
|
||||
namespace js {
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
|
|
@ -2915,6 +2915,9 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||
|
||||
MarkAndSweep(cx, gckind);
|
||||
|
||||
if (!comp)
|
||||
js_PurgeThreads_PostGlobalSweep(cx);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
|
||||
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
||||
|
|
|
@ -137,6 +137,14 @@ const char *js_CodeName[] = {
|
|||
|
||||
#define COUNTS_LEN 16
|
||||
|
||||
typedef Vector<char, 8> DupBuffer;
|
||||
|
||||
static bool
|
||||
Dup(const char *chars, DupBuffer *cb)
|
||||
{
|
||||
return cb->append(chars, strlen(chars) + 1);
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
|
||||
{
|
||||
|
@ -1288,7 +1296,7 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
|
|||
}
|
||||
|
||||
static jsbytecode *
|
||||
Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
|
||||
Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
|
||||
|
||||
static JSBool
|
||||
DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
||||
|
@ -1318,7 +1326,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
|||
jp->indent += 2;
|
||||
js_printf(jp, "\t%s:\n", js_default_str);
|
||||
jp->indent += 2;
|
||||
if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
|
||||
if (!Decompile(ss, pc + defaultOffset, diff))
|
||||
return JS_FALSE;
|
||||
jp->indent -= 4;
|
||||
}
|
||||
|
@ -1341,10 +1349,8 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
|||
nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
|
||||
nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
|
||||
jp->indent += 2;
|
||||
if (!Decompile(ss, pc + caseExprOff,
|
||||
nextCaseExprOff - caseExprOff, JSOP_NOP)) {
|
||||
if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff))
|
||||
return JS_FALSE;
|
||||
}
|
||||
caseExprOff = nextCaseExprOff;
|
||||
|
||||
/* Balance the stack as if this JSOP_CASE matched. */
|
||||
|
@ -1391,7 +1397,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
|||
if (off <= defaultOffset && defaultOffset < off2) {
|
||||
diff = defaultOffset - off;
|
||||
if (diff != 0) {
|
||||
if (!Decompile(ss, pc + off, diff, JSOP_NOP))
|
||||
if (!Decompile(ss, pc + off, diff))
|
||||
return JS_FALSE;
|
||||
off = defaultOffset;
|
||||
}
|
||||
|
@ -1399,7 +1405,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
|
|||
js_printf(jp, "\t%s:\n", js_default_str);
|
||||
jp->indent += 2;
|
||||
}
|
||||
if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
|
||||
if (!Decompile(ss, pc + off, off2 - off))
|
||||
return JS_FALSE;
|
||||
jp->indent -= 4;
|
||||
|
||||
|
@ -1535,25 +1541,23 @@ static jsbytecode *
|
|||
DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
||||
JSBool *hole)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSPrinter *jp;
|
||||
JSOp op;
|
||||
const JSCodeSpec *cs;
|
||||
uintN oplen;
|
||||
jsint i;
|
||||
const char *lval, *xval;
|
||||
ptrdiff_t todo;
|
||||
JSAtom *atom;
|
||||
|
||||
*hole = JS_FALSE;
|
||||
cx = ss->sprinter.context;
|
||||
jp = ss->printer;
|
||||
LOAD_OP_DATA(pc);
|
||||
|
||||
switch (op) {
|
||||
case JSOP_POP:
|
||||
*hole = JS_TRUE;
|
||||
todo = SprintPut(&ss->sprinter, ", ", 2);
|
||||
if (SprintPut(&ss->sprinter, ", ", 2) < 0)
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case JSOP_DUP:
|
||||
|
@ -1564,9 +1568,8 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
return pc;
|
||||
LOAD_OP_DATA(pc);
|
||||
lval = PopStr(ss, JSOP_NOP);
|
||||
todo = SprintCString(&ss->sprinter, lval);
|
||||
if (op == JSOP_POPN)
|
||||
return pc;
|
||||
if (SprintCString(&ss->sprinter, lval) < 0)
|
||||
return NULL;
|
||||
LOCAL_ASSERT(*pc == JSOP_POP);
|
||||
break;
|
||||
|
||||
|
@ -1576,36 +1579,19 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
/* FALL THROUGH */
|
||||
|
||||
case JSOP_SETLOCALPOP:
|
||||
atom = NULL;
|
||||
lval = NULL;
|
||||
if (op == JSOP_SETARG) {
|
||||
atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
|
||||
LOCAL_ASSERT(atom);
|
||||
} else if (IsVarSlot(jp, pc, &i)) {
|
||||
if (IsVarSlot(jp, pc, &i)) {
|
||||
atom = GetArgOrVarAtom(jp, i);
|
||||
LOCAL_ASSERT(atom);
|
||||
if (!QuoteString(&ss->sprinter, atom, 0))
|
||||
return NULL;
|
||||
} else {
|
||||
lval = GetLocal(ss, i);
|
||||
}
|
||||
{
|
||||
JSAutoByteString bytes;
|
||||
if (atom)
|
||||
lval = js_AtomToPrintableString(cx, atom, &bytes);
|
||||
LOCAL_ASSERT(lval);
|
||||
todo = SprintCString(&ss->sprinter, lval);
|
||||
}
|
||||
if (op != JSOP_SETLOCALPOP) {
|
||||
pc += oplen;
|
||||
if (pc == endpc)
|
||||
return pc;
|
||||
LOAD_OP_DATA(pc);
|
||||
if (op == JSOP_POPN)
|
||||
return pc;
|
||||
LOCAL_ASSERT(op == JSOP_POP);
|
||||
if (!lval || SprintCString(&ss->sprinter, lval) < 0)
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
default: {
|
||||
/*
|
||||
* We may need to auto-parenthesize the left-most value decompiled
|
||||
* here, so add back PAREN_SLOP temporarily. Then decompile until the
|
||||
|
@ -1613,9 +1599,9 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
* pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
|
||||
* the nb parameter.
|
||||
*/
|
||||
todo = ss->sprinter.offset;
|
||||
ptrdiff_t todo = ss->sprinter.offset;
|
||||
ss->sprinter.offset = todo + PAREN_SLOP;
|
||||
pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
|
||||
pc = Decompile(ss, pc, -((intN)ss->top));
|
||||
if (!pc)
|
||||
return NULL;
|
||||
if (pc == endpc)
|
||||
|
@ -1638,12 +1624,12 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
: "%s[%s]",
|
||||
lval, xval);
|
||||
}
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
|
||||
LOCAL_ASSERT(pc < endpc);
|
||||
pc += oplen;
|
||||
return pc;
|
||||
|
@ -1744,9 +1730,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
|
|||
}
|
||||
break;
|
||||
|
||||
case JSOP_CALLPROP:
|
||||
case JSOP_GETPROP:
|
||||
case JSOP_LENGTH:
|
||||
{
|
||||
LOAD_ATOM(0);
|
||||
*OFF2STR(&ss->sprinter, head) = '{';
|
||||
|
@ -1902,6 +1886,73 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, false)
|
||||
|
||||
typedef Vector<JSAtom *, 8> AtomVector;
|
||||
|
||||
/*
|
||||
* The names of the vars of a let block/expr are stored as the ids of the
|
||||
* shapes of the block object. Shapes are stored in a singly-linked list in
|
||||
* reverse order of addition. This function takes care of putting the names
|
||||
* back in declaration order.
|
||||
*/
|
||||
static bool
|
||||
GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms)
|
||||
{
|
||||
size_t numAtoms = OBJ_BLOCK_COUNT(cx, blockObj);
|
||||
LOCAL_ASSERT(numAtoms > 0);
|
||||
if (!atoms->resize(numAtoms))
|
||||
return false;
|
||||
|
||||
uintN i = numAtoms;
|
||||
for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
LOCAL_ASSERT(shape.hasShortID());
|
||||
--i;
|
||||
LOCAL_ASSERT((uintN)shape.shortid == i);
|
||||
(*atoms)[i] = JSID_TO_ATOM(shape.propid);
|
||||
}
|
||||
|
||||
LOCAL_ASSERT(i == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOCAL_ASSERT
|
||||
|
||||
static bool
|
||||
PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
|
||||
{
|
||||
for (size_t i = 0; i < atoms.length(); i++) {
|
||||
const char *name = QuoteString(&ss->sprinter, atoms[i], 0);
|
||||
if (!name || !PushOff(ss, STR2OFF(&ss->sprinter, name), JSOP_ENTERBLOCK))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
SprintLet(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength,
|
||||
const char *headChars)
|
||||
{
|
||||
if (pc[bodyLength] == JSOP_LEAVEBLOCK) {
|
||||
js_printf(jp, "\tlet (%s) {\n", headChars);
|
||||
jp->indent += 4;
|
||||
if (!Decompile(ss, pc, bodyLength))
|
||||
return -1;
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
LOCAL_ASSERT_RV(pc[bodyLength] == JSOP_LEAVEBLOCKEXPR, -1);
|
||||
if (!Decompile(ss, pc, bodyLength))
|
||||
return -1;
|
||||
|
||||
const char *bodyChars = PopStr(ss, JSOP_SETNAME);
|
||||
const char *format = *bodyChars == '{' ? "let (%s) (%s)" : "let (%s) %s";
|
||||
return Sprint(&ss->sprinter, format, headChars, bodyChars);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
|
||||
{
|
||||
|
@ -1928,14 +1979,9 @@ InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
|
|||
* If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
|
||||
* the decompiler starts at pc and continues until it reaches an opcode for
|
||||
* which decompiling would result in the stack depth equaling -(nb + 1).
|
||||
*
|
||||
* The nextop parameter is either JSOP_NOP or the "next" opcode in order of
|
||||
* abstract interpretation (not necessarily physically next in a bytecode
|
||||
* vector). So nextop is JSOP_POP for the last operand in a comma expression,
|
||||
* or JSOP_AND for the right operand of &&.
|
||||
*/
|
||||
static jsbytecode *
|
||||
Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSPrinter *jp, *jp2;
|
||||
|
@ -1980,9 +2026,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
* Local macros
|
||||
*/
|
||||
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
|
||||
#define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb, JSOP_NOP)) cleanup
|
||||
#define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb)) cleanup
|
||||
#define DECOMPILE_CODE(pc,nb) DECOMPILE_CODE_CLEANUP(pc,nb,return NULL)
|
||||
#define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
|
||||
#define TOP_STR() GetStr(ss, ss->top - 1)
|
||||
#define POP_STR() PopStr(ss, op)
|
||||
#define POP_STR_PREC(prec) PopStrPrec(ss, prec)
|
||||
|
@ -2647,7 +2692,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
pc += js_GetSrcNoteOffset(sn, 0);
|
||||
len = 0;
|
||||
|
||||
if (!Decompile(ss, done, pc - done, JSOP_POP)) {
|
||||
if (!Decompile(ss, done, pc - done)) {
|
||||
cx->free_((char *)lval);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2664,41 +2709,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
break;
|
||||
|
||||
case SRC_DECL:
|
||||
{
|
||||
/* This pop is at the end of the let block/expr head. */
|
||||
pc += JSOP_POP_LENGTH;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
do_letheadbody:
|
||||
#endif
|
||||
DupBuffer head(cx);
|
||||
if (!Dup(POP_STR(), &head))
|
||||
return NULL;
|
||||
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
if (pc[len] == JSOP_LEAVEBLOCK) {
|
||||
js_printf(jp, "\tlet (%s) {\n", POP_STR());
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc, len);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
todo = -2;
|
||||
} else {
|
||||
LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
|
||||
|
||||
lval = JS_strdup(cx, PopStr(ss, JSOP_NOP));
|
||||
if (!lval)
|
||||
return NULL;
|
||||
|
||||
/* Set saveop to reflect what we will push. */
|
||||
saveop = JSOP_LEAVEBLOCKEXPR;
|
||||
if (!Decompile(ss, pc, len, saveop)) {
|
||||
cx->free_((char *)lval);
|
||||
return NULL;
|
||||
}
|
||||
rval = PopStr(ss, JSOP_SETNAME);
|
||||
todo = Sprint(&ss->sprinter,
|
||||
(*rval == '{')
|
||||
? "let (%s) (%s)"
|
||||
: "let (%s) %s",
|
||||
lval, rval);
|
||||
cx->free_((char *)lval);
|
||||
}
|
||||
break;
|
||||
saveop = (JSOp) pc[len];
|
||||
todo = SprintLet(cx, jp, ss, pc, len, head.begin());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Turn off parens around a yield statement. */
|
||||
|
@ -2752,39 +2777,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
|
||||
case JSOP_ENTERBLOCK:
|
||||
{
|
||||
JSAtom **atomv, *smallv[5];
|
||||
|
||||
LOAD_OBJECT(0);
|
||||
argc = OBJ_BLOCK_COUNT(cx, obj);
|
||||
if ((size_t)argc <= ArrayLength(smallv)) {
|
||||
atomv = smallv;
|
||||
} else {
|
||||
atomv = (JSAtom **) cx->malloc_(argc * sizeof(JSAtom *));
|
||||
if (!atomv)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MUST_FLOW_THROUGH("enterblock_out");
|
||||
#define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
|
||||
goto enterblock_out)
|
||||
for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
|
||||
if (!shape.hasShortID())
|
||||
continue;
|
||||
LOCAL_ASSERT_OUT(shape.shortid < argc);
|
||||
atomv[shape.shortid] = JSID_TO_ATOM(shape.propid);
|
||||
}
|
||||
ok = JS_TRUE;
|
||||
for (i = 0; i < argc; i++) {
|
||||
atom = atomv[i];
|
||||
rval = QuoteString(&ss->sprinter, atom, 0);
|
||||
if (!rval ||
|
||||
!PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
|
||||
ok = JS_FALSE;
|
||||
goto enterblock_out;
|
||||
}
|
||||
}
|
||||
AtomVector atoms(cx);
|
||||
if (!GetBlockNames(cx, obj, &atoms) || !PushBlockNames(cx, ss, atoms))
|
||||
return NULL;
|
||||
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
|
||||
|
@ -2793,10 +2789,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
js_printf(jp, "\t{\n");
|
||||
jp->indent += 4;
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP)
|
||||
!= NULL;
|
||||
if (!ok)
|
||||
goto enterblock_out;
|
||||
if (!Decompile(ss, pc + oplen, len - oplen))
|
||||
return NULL;
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
break;
|
||||
|
@ -2808,13 +2802,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
|
||||
pc2 = pc;
|
||||
pc += oplen;
|
||||
LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION);
|
||||
LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
|
||||
pc += JSOP_EXCEPTION_LENGTH;
|
||||
todo = Sprint(&ss->sprinter, exception_cookie);
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) {
|
||||
ok = JS_FALSE;
|
||||
goto enterblock_out;
|
||||
}
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
|
||||
return NULL;
|
||||
|
||||
if (*pc == JSOP_DUP) {
|
||||
sn2 = js_GetSrcNote(jp->script, pc);
|
||||
|
@ -2824,39 +2816,31 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
* It is emitted only when the catch head contains
|
||||
* an exception guard.
|
||||
*/
|
||||
LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0);
|
||||
LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0);
|
||||
pc += JSOP_DUP_LENGTH;
|
||||
todo = Sprint(&ss->sprinter, exception_cookie);
|
||||
if (todo < 0 ||
|
||||
!PushOff(ss, todo, JSOP_EXCEPTION)) {
|
||||
ok = JS_FALSE;
|
||||
goto enterblock_out;
|
||||
}
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (*pc == JSOP_DUP) {
|
||||
pc = DecompileDestructuring(ss, pc, endpc);
|
||||
if (!pc) {
|
||||
ok = JS_FALSE;
|
||||
goto enterblock_out;
|
||||
}
|
||||
LOCAL_ASSERT_OUT(*pc == JSOP_POP);
|
||||
if (!pc)
|
||||
return NULL;
|
||||
LOCAL_ASSERT(*pc == JSOP_POP);
|
||||
pc += JSOP_POP_LENGTH;
|
||||
lval = PopStr(ss, JSOP_NOP);
|
||||
js_puts(jp, lval);
|
||||
} else {
|
||||
#endif
|
||||
LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP);
|
||||
i = GET_SLOTNO(pc) - jp->script->nfixed;
|
||||
LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
|
||||
pc += JSOP_SETLOCALPOP_LENGTH;
|
||||
atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
|
||||
str = atom;
|
||||
if (!QuoteString(&jp->sprinter, str, 0)) {
|
||||
ok = JS_FALSE;
|
||||
goto enterblock_out;
|
||||
}
|
||||
LOCAL_ASSERT(OBJ_BLOCK_COUNT(cx, obj) == 1);
|
||||
atom = JSID_TO_ATOM(obj->lastProperty()->propid);
|
||||
if (!QuoteString(&jp->sprinter, atom, 0))
|
||||
return NULL;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
}
|
||||
#endif
|
||||
|
@ -2866,19 +2850,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
* guarded catch head) off the stack now.
|
||||
*/
|
||||
rval = PopStr(ss, JSOP_NOP);
|
||||
LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0);
|
||||
LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
|
||||
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
if (len) {
|
||||
len -= pc - pc2;
|
||||
LOCAL_ASSERT_OUT(len > 0);
|
||||
LOCAL_ASSERT(len > 0);
|
||||
js_printf(jp, " if ");
|
||||
ok = Decompile(ss, pc, len, JSOP_NOP) != NULL;
|
||||
if (!ok)
|
||||
goto enterblock_out;
|
||||
if (!Decompile(ss, pc, len))
|
||||
return NULL;
|
||||
js_printf(jp, "%s", POP_STR());
|
||||
pc += len;
|
||||
LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
|
||||
LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
|
||||
pc += js_CodeSpec[*pc].length;
|
||||
}
|
||||
|
||||
|
@ -2886,18 +2869,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
jp->indent += 4;
|
||||
len = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
todo = -2;
|
||||
|
||||
#undef LOCAL_ASSERT_OUT
|
||||
enterblock_out:
|
||||
if (atomv != smallv)
|
||||
cx->free_(atomv);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3490,7 +3465,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
done = pc + GetJumpOffset(pc, pc);
|
||||
pc += len;
|
||||
len = done - pc;
|
||||
if (!Decompile(ss, pc, len, op)) {
|
||||
if (!Decompile(ss, pc, len)) {
|
||||
cx->free_((char *)lval);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -4096,8 +4071,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
* Decompile only the main bytecode, to avoid tripping over
|
||||
* new prolog ops that have stack effects.
|
||||
*/
|
||||
ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset,
|
||||
JSOP_NOP)
|
||||
ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset)
|
||||
!= NULL;
|
||||
jp->script = outer;
|
||||
jp->fun = outerfun;
|
||||
|
@ -4791,7 +4765,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||
*/
|
||||
#undef inXML
|
||||
#undef DECOMPILE_CODE
|
||||
#undef NEXT_OP
|
||||
#undef TOP_STR
|
||||
#undef POP_STR
|
||||
#undef POP_STR_PREC
|
||||
|
@ -4842,7 +4815,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
|
|||
/* Call recursive subroutine to do the hard work. */
|
||||
JSScript *oldscript = jp->script;
|
||||
jp->script = script;
|
||||
bool ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL;
|
||||
bool ok = Decompile(&ss, pc, len) != NULL;
|
||||
jp->script = oldscript;
|
||||
|
||||
/* If the given code didn't empty the stack, do it now. */
|
||||
|
|
|
@ -124,6 +124,7 @@ struct Class;
|
|||
|
||||
class RegExpObject;
|
||||
class RegExpPrivate;
|
||||
class RegExpObjectBuilder;
|
||||
class RegExpStatics;
|
||||
class MatchPairs;
|
||||
|
||||
|
@ -232,6 +233,9 @@ typedef HashMap<jsbytecode *, BreakpointSite *, DefaultHasher<jsbytecode *>, Run
|
|||
class Debugger;
|
||||
class WatchpointMap;
|
||||
|
||||
typedef HashMap<JSAtom *, RegExpPrivate *, DefaultHasher<JSAtom *>, RuntimeAllocPolicy>
|
||||
RegExpPrivateCache;
|
||||
|
||||
typedef JSNative Native;
|
||||
typedef JSPropertyOp PropertyOp;
|
||||
typedef JSStrictPropertyOp StrictPropertyOp;
|
||||
|
|
|
@ -1264,8 +1264,14 @@ class FlatMatch
|
|||
int32 match() const { return match_; }
|
||||
};
|
||||
|
||||
/*
|
||||
* Some string methods operate on a RegExpObject, if it is present, but if it
|
||||
* is absent create an internal regular expression matcher. This unifies the
|
||||
* interface.
|
||||
*/
|
||||
class RegExpPair
|
||||
{
|
||||
JSContext *cx;
|
||||
AutoRefCount<RegExpPrivate> rep_;
|
||||
RegExpObject *reobj_;
|
||||
|
||||
|
@ -1273,9 +1279,10 @@ class RegExpPair
|
|||
void operator=(const RegExpPair &);
|
||||
|
||||
public:
|
||||
explicit RegExpPair(JSContext *cx) : rep_(cx) {}
|
||||
explicit RegExpPair(JSContext *cx) : cx(cx), rep_(cx) {}
|
||||
|
||||
bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
|
||||
JS_ASSERT(cx == this->cx);
|
||||
reobj_ = reobj;
|
||||
RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
|
@ -1302,9 +1309,8 @@ class RegExpPair
|
|||
/*
|
||||
* RegExpGuard factors logic out of String regexp operations.
|
||||
*
|
||||
* @param optarg Indicates in which argument position RegExp
|
||||
* flags will be found, if present. This is a Mozilla
|
||||
* extension and not part of any ECMA spec.
|
||||
* |optarg| indicates in which argument position RegExp flags will be found, if
|
||||
* present. This is a Mozilla extension and not part of any ECMA spec.
|
||||
*/
|
||||
class RegExpGuard
|
||||
{
|
||||
|
@ -1368,8 +1374,9 @@ class RegExpGuard
|
|||
* Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
|
||||
* pattern string, or a lengthy pattern string can thwart this process.
|
||||
*
|
||||
* @param checkMetaChars Look for regexp metachars in the pattern string.
|
||||
* @return Whether flat matching could be used.
|
||||
* |checkMetaChars| looks for regexp metachars in the pattern string.
|
||||
*
|
||||
* Return whether flat matching could be used.
|
||||
*
|
||||
* N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
|
||||
*/
|
||||
|
|
|
@ -77,7 +77,7 @@ class StringBuffer
|
|||
bool append(const jschar *chars, size_t len);
|
||||
bool append(const jschar *begin, const jschar *end);
|
||||
bool append(JSString *str);
|
||||
bool append(JSAtom *atom);
|
||||
bool append(JSLinearString *str);
|
||||
bool appendN(const jschar c, size_t n);
|
||||
bool appendInflated(const char *cstr, size_t len);
|
||||
|
||||
|
@ -173,19 +173,14 @@ StringBuffer::append(JSString *str)
|
|||
JSLinearString *linear = str->ensureLinear(context());
|
||||
if (!linear)
|
||||
return false;
|
||||
size_t strLen = linear->length();
|
||||
if (!checkLength(cb.length() + strLen))
|
||||
return false;
|
||||
return cb.append(linear->chars(), strLen);
|
||||
return append(linear);
|
||||
}
|
||||
|
||||
inline bool
|
||||
StringBuffer::append(JSAtom *atom)
|
||||
StringBuffer::append(JSLinearString *str)
|
||||
{
|
||||
size_t strLen = atom->length();
|
||||
if (!checkLength(cb.length() + strLen))
|
||||
return false;
|
||||
return cb.append(atom->chars(), strLen);
|
||||
JS::Anchor<JSString *> anch(str);
|
||||
return cb.append(str->chars(), str->length());
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -89,6 +89,12 @@ HasRegExpMetaChars(const jschar *chars, size_t length)
|
|||
return false;
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpObject::setPrivate(RegExpPrivate *rep)
|
||||
{
|
||||
JSObject::setPrivate(rep);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
|
@ -101,57 +107,44 @@ inline RegExpObject *
|
|||
RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
{
|
||||
JSLinearString *source = js_NewStringCopyN(cx, chars, length);
|
||||
JSAtom *source = js_AtomizeChars(cx, chars, length);
|
||||
if (!source)
|
||||
return NULL;
|
||||
|
||||
/* |NewBuiltinClassInstance| can GC. */
|
||||
JS::Anchor<JSString *> anchor(source);
|
||||
return createNoStatics(cx, source, flags, tokenStream);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
|
||||
TokenStream *tokenStream)
|
||||
{
|
||||
if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return builder.build(source, flags);
|
||||
}
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
return reobj->reset(cx, source, flags) ? reobj : NULL;
|
||||
inline void
|
||||
RegExpObject::purge(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate()) {
|
||||
rep->decref(cx);
|
||||
setPrivate(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpObject::finalize(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
rep->decref(cx);
|
||||
purge(cx);
|
||||
#ifdef DEBUG
|
||||
setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
|
||||
setPrivate((RegExpPrivate *) 0x1); /* Non-null but still in the zero page. */
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
if (!reset(cx, rep->getSource(), rep->getFlags()))
|
||||
return false;
|
||||
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, RegExpObject *other)
|
||||
{
|
||||
if (RegExpPrivate *rep = other->getPrivate()) {
|
||||
rep->incref(cx);
|
||||
return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
|
||||
}
|
||||
|
||||
return reset(cx, other->getSource(), other->getFlags());
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
{
|
||||
if (nativeEmpty()) {
|
||||
const js::Shape **shapep = &cx->compartment->initialRegExpShape;
|
||||
|
@ -174,8 +167,8 @@ RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
|||
MULTILINE_FLAG_SLOT);
|
||||
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
|
||||
|
||||
JS_ASSERT(!getPrivate());
|
||||
zeroLastIndex();
|
||||
setPrivate(NULL);
|
||||
setSource(source);
|
||||
setGlobal(flags & GlobalFlag);
|
||||
setIgnoreCase(flags & IgnoreCaseFlag);
|
||||
|
@ -191,11 +184,46 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T
|
|||
{
|
||||
typedef AlreadyIncRefed<RegExpPrivate> RetType;
|
||||
|
||||
/*
|
||||
* We choose to only cache |RegExpPrivate|s who have atoms as
|
||||
* sources, under the unverified premise that non-atoms will have a
|
||||
* low hit rate (both hit ratio and absolute number of hits).
|
||||
*/
|
||||
bool cacheable = source->isAtom();
|
||||
|
||||
/*
|
||||
* Refcount note: not all |RegExpPrivate|s are cached so we need to
|
||||
* keep a refcount. The cache holds a "weak ref", where the
|
||||
* |RegExpPrivate|'s deallocation decref will first cause it to
|
||||
* remove itself from the cache.
|
||||
*/
|
||||
|
||||
JSRuntime *rt = cx->runtime;
|
||||
RegExpPrivateCache *cache = NULL; /* Quell "may be used uninitialized". */
|
||||
RegExpPrivateCache::AddPtr addPtr;
|
||||
if (cacheable) {
|
||||
cache = cx->threadData()->getOrCreateRegExpPrivateCache(rt);
|
||||
if (!cache) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return RetType(NULL);
|
||||
}
|
||||
|
||||
addPtr = cache->lookupForAdd(&source->asAtom());
|
||||
if (addPtr) {
|
||||
RegExpPrivate *cached = addPtr->value;
|
||||
if (cached->getFlags() == flags) {
|
||||
cached->incref(cx);
|
||||
return RetType(cached);
|
||||
}
|
||||
/* Note: on flag mismatch, we clobber the existing entry. */
|
||||
}
|
||||
}
|
||||
|
||||
JSLinearString *flatSource = source->ensureLinear(cx);
|
||||
if (!flatSource)
|
||||
return RetType(NULL);
|
||||
|
||||
RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags, cx->compartment);
|
||||
RegExpPrivate *self = cx->new_<RegExpPrivate>(flatSource, flags);
|
||||
if (!self)
|
||||
return RetType(NULL);
|
||||
|
||||
|
@ -204,6 +232,18 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T
|
|||
return RetType(NULL);
|
||||
}
|
||||
|
||||
if (cacheable) {
|
||||
if (addPtr) {
|
||||
JS_ASSERT(addPtr->key == &self->getSource()->asAtom());
|
||||
addPtr->value = self;
|
||||
} else {
|
||||
if (!cache->add(addPtr, &source->asAtom(), self)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return RetType(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RetType(self);
|
||||
}
|
||||
|
||||
|
@ -331,40 +371,23 @@ RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, si
|
|||
inline void
|
||||
RegExpPrivate::incref(JSContext *cx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assertSameCompartment(cx, compartment);
|
||||
#endif
|
||||
++refCount;
|
||||
}
|
||||
|
||||
inline void
|
||||
RegExpPrivate::decref(JSContext *cx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assertSameCompartment(cx, compartment);
|
||||
#endif
|
||||
if (--refCount == 0)
|
||||
cx->delete_(this);
|
||||
}
|
||||
if (--refCount != 0)
|
||||
return;
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getOrCreatePrivate(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
return rep;
|
||||
RegExpPrivateCache *cache;
|
||||
if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
|
||||
RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
|
||||
if (ptr && ptr->value == this)
|
||||
cache->remove(ptr);
|
||||
}
|
||||
|
||||
return makePrivate(cx) ? getPrivate() : NULL;
|
||||
}
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getPrivate() const
|
||||
{
|
||||
RegExpPrivate *rep = static_cast<RegExpPrivate *>(JSObject::getPrivate());
|
||||
#ifdef DEBUG
|
||||
if (rep)
|
||||
CompartmentChecker::check(compartment(), rep->compartment);
|
||||
#endif
|
||||
return rep;
|
||||
cx->delete_(this);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -59,6 +59,106 @@ JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
|
|||
JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
|
||||
JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
|
||||
|
||||
/* RegExpObjectBuilder */
|
||||
|
||||
bool
|
||||
RegExpObjectBuilder::getOrCreate()
|
||||
{
|
||||
if (reobj_)
|
||||
return true;
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
return false;
|
||||
obj->setPrivate(NULL);
|
||||
|
||||
reobj_ = obj->asRegExp();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
|
||||
{
|
||||
JS_ASSERT(!reobj_);
|
||||
|
||||
JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
|
||||
if (!clone)
|
||||
return false;
|
||||
clone->setPrivate(NULL);
|
||||
|
||||
reobj_ = clone->asRegExp();
|
||||
return true;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
if (!getOrCreate()) {
|
||||
rep->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
reobj_->purge(cx);
|
||||
if (!reobj_->init(cx, rep->getSource(), rep->getFlags())) {
|
||||
rep->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
reobj_->setPrivate(rep.get());
|
||||
|
||||
return reobj_;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
|
||||
{
|
||||
if (!getOrCreate())
|
||||
return NULL;
|
||||
|
||||
reobj_->purge(cx);
|
||||
return reobj_->init(cx, source, flags) ? reobj_ : NULL;
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::build(RegExpObject *other)
|
||||
{
|
||||
RegExpPrivate *rep = other->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
return NULL;
|
||||
|
||||
/* Now, incref it for the RegExpObject being built. */
|
||||
rep->incref(cx);
|
||||
return build(AlreadyIncRefed<RegExpPrivate>(rep));
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
|
||||
{
|
||||
if (!getOrCreateClone(proto))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Check that the RegExpPrivate for the original is okay to use in
|
||||
* the clone -- if the |RegExpStatics| provides more flags we'll
|
||||
* need a different |RegExpPrivate|.
|
||||
*/
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpFlag origFlags = other->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
return build(other->getSource(), newFlags);
|
||||
}
|
||||
|
||||
RegExpPrivate *toShare = other->getOrCreatePrivate(cx);
|
||||
if (!toShare)
|
||||
return NULL;
|
||||
|
||||
toShare->incref(cx);
|
||||
return build(AlreadyIncRefed<RegExpPrivate>(toShare));
|
||||
}
|
||||
|
||||
/* MatchPairs */
|
||||
|
||||
MatchPairs *
|
||||
MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
|
||||
{
|
||||
|
@ -130,16 +230,16 @@ RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t
|
|||
return RegExpRunStatus_Success;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpPrivate *
|
||||
RegExpObject::makePrivate(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!getPrivate());
|
||||
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
|
||||
if (!rep)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
return rep.get();
|
||||
}
|
||||
|
||||
RegExpRunStatus
|
||||
|
@ -209,13 +309,11 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
|
|||
if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
|
||||
return false;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
JS::Anchor<JSString *> anchor(source);
|
||||
const jschar *chars = source->getChars(xdr->cx);
|
||||
if (!chars)
|
||||
JSAtom *atom = js_AtomizeString(xdr->cx, source);
|
||||
if (!atom)
|
||||
return false;
|
||||
size_t len = source->length();
|
||||
RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, chars, len,
|
||||
RegExpFlag(flagsword), NULL);
|
||||
RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword),
|
||||
NULL);
|
||||
if (!reobj)
|
||||
return false;
|
||||
|
||||
|
@ -238,6 +336,12 @@ regexp_finalize(JSContext *cx, JSObject *obj)
|
|||
obj->asRegExp()->finalize(cx);
|
||||
}
|
||||
|
||||
static void
|
||||
regexp_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
obj->asRegExp()->purge(trc->context);
|
||||
}
|
||||
|
||||
Class js::RegExpClass = {
|
||||
js_RegExp_str,
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
|
@ -257,7 +361,7 @@ Class js::RegExpClass = {
|
|||
NULL, /* construct */
|
||||
js_XDRRegExpObject,
|
||||
NULL, /* hasInstance */
|
||||
NULL /* trace */
|
||||
regexp_trace
|
||||
};
|
||||
|
||||
#if ENABLE_YARR_JIT
|
||||
|
@ -378,54 +482,14 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenSt
|
|||
return create(cx, str, flags, ts);
|
||||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
|
||||
{
|
||||
JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* This clone functionality does not duplicate the JIT'd code blob,
|
||||
* which is necessary for cross-compartment cloning functionality.
|
||||
*/
|
||||
assertSameCompartment(cx, reobj, clone);
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reclone = clone->asRegExp();
|
||||
|
||||
/*
|
||||
* Check that the RegExpPrivate for the original is okay to use in
|
||||
* the clone -- if the |RegExpStatics| provides more flags we'll
|
||||
* need a different |RegExpPrivate|.
|
||||
*/
|
||||
RegExpFlag origFlags = reobj->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL;
|
||||
}
|
||||
|
||||
RegExpPrivate *toShare = reobj->getOrCreatePrivate(cx);
|
||||
if (!toShare)
|
||||
return NULL;
|
||||
|
||||
toShare->incref(cx);
|
||||
if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
|
||||
toShare->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return reclone;
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(obj->isRegExp());
|
||||
JS_ASSERT(proto->isRegExp());
|
||||
|
||||
return RegExpObject::clone(cx, obj->asRegExp(), proto->asRegExp());
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return builder.clone(obj->asRegExp(), proto->asRegExp());
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
|
|
@ -82,13 +82,14 @@ class RegExpObject : public ::JSObject
|
|||
*/
|
||||
static inline RegExpObject *
|
||||
create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags,
|
||||
TokenStream *ts);
|
||||
TokenStream *tokenStream);
|
||||
|
||||
static inline RegExpObject *
|
||||
createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
|
||||
TokenStream *ts);
|
||||
TokenStream *tokenStream);
|
||||
|
||||
static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto);
|
||||
static inline RegExpObject *
|
||||
createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *tokenStream);
|
||||
|
||||
/* Note: fallible. */
|
||||
JSFlatString *toString(JSContext *cx) const;
|
||||
|
@ -148,27 +149,35 @@ class RegExpObject : public ::JSObject
|
|||
bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
|
||||
bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
|
||||
|
||||
/*
|
||||
* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
|
||||
* |reset| for re-initialization.
|
||||
*/
|
||||
|
||||
inline bool reset(JSContext *cx, RegExpObject *other);
|
||||
inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
|
||||
|
||||
inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
|
||||
inline void finalize(JSContext *cx);
|
||||
|
||||
/* Clear out lazy |RegExpPrivate|. */
|
||||
inline void purge(JSContext *x);
|
||||
|
||||
RegExpPrivate *getOrCreatePrivate(JSContext *cx) {
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
return rep;
|
||||
|
||||
return makePrivate(cx);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class RegExpObjectBuilder;
|
||||
|
||||
inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags);
|
||||
|
||||
/* The |RegExpPrivate| is lazily created at the time of use. */
|
||||
inline RegExpPrivate *getPrivate() const;
|
||||
RegExpPrivate *getPrivate() const {
|
||||
return static_cast<RegExpPrivate *>(JSObject::getPrivate());
|
||||
}
|
||||
|
||||
inline void setPrivate(RegExpPrivate *rep);
|
||||
|
||||
/*
|
||||
* Precondition: the syntax for |source| has already been validated.
|
||||
* Side effect: sets the private field.
|
||||
*/
|
||||
bool makePrivate(JSContext *cx);
|
||||
RegExpPrivate *makePrivate(JSContext *cx);
|
||||
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
|
||||
|
@ -184,6 +193,32 @@ class RegExpObject : public ::JSObject
|
|||
RegExpObject &operator=(const RegExpObject &reo);
|
||||
}; /* class RegExpObject */
|
||||
|
||||
/* Either builds a new RegExpObject or re-initializes an existing one. */
|
||||
class RegExpObjectBuilder
|
||||
{
|
||||
JSContext *cx;
|
||||
RegExpObject *reobj_;
|
||||
|
||||
bool getOrCreate();
|
||||
bool getOrCreateClone(RegExpObject *proto);
|
||||
|
||||
public:
|
||||
RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL)
|
||||
: cx(cx), reobj_(reobj)
|
||||
{ }
|
||||
|
||||
RegExpObject *reobj() { return reobj_; }
|
||||
|
||||
/* Note: In case of failure, |rep| will be decrefed. */
|
||||
RegExpObject *build(AlreadyIncRefed<RegExpPrivate> rep);
|
||||
|
||||
RegExpObject *build(JSLinearString *str, RegExpFlag flags);
|
||||
RegExpObject *build(RegExpObject *other);
|
||||
|
||||
/* Perform a VM-internal clone. */
|
||||
RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
|
||||
};
|
||||
|
||||
/* Abstracts away the gross |RegExpPrivate| backend details. */
|
||||
class RegExpPrivateCode
|
||||
{
|
||||
|
@ -243,12 +278,6 @@ class RegExpPrivateCode
|
|||
static void reportPCREError(JSContext *cx, int error);
|
||||
#endif
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount);
|
||||
|
||||
static size_t getOutputSize(size_t pairCount) {
|
||||
#if ENABLE_YARR_JIT
|
||||
return pairCount * 2;
|
||||
|
@ -256,6 +285,13 @@ class RegExpPrivateCode
|
|||
return pairCount * 3; /* Should be x2, but PCRE has... needs. */
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
|
||||
inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -265,8 +301,8 @@ class RegExpPrivateCode
|
|||
* compilation.
|
||||
*
|
||||
* Non-atomic refcounting is used, so single-thread invariants must be
|
||||
* maintained: we check regexp operations are performed in a single
|
||||
* compartment.
|
||||
* maintained. |RegExpPrivate|s are currently shared within a single
|
||||
* |ThreadData|.
|
||||
*
|
||||
* Note: refCount cannot overflow because that would require more
|
||||
* referring regexp objects than there is space for in addressable
|
||||
|
@ -280,12 +316,9 @@ class RegExpPrivate
|
|||
uintN parenCount;
|
||||
RegExpFlag flags;
|
||||
|
||||
public:
|
||||
DebugOnly<JSCompartment *> compartment;
|
||||
|
||||
private:
|
||||
RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
|
||||
: source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
|
||||
RegExpPrivate(JSLinearString *source, RegExpFlag flags)
|
||||
: source(source), refCount(1), parenCount(0), flags(flags)
|
||||
{ }
|
||||
|
||||
JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
|
||||
|
|
|
@ -2089,6 +2089,22 @@ class YarrGenerator : private MacroAssembler {
|
|||
alternativeEndOpCode = OpNestedAlternativeEnd;
|
||||
}
|
||||
} else if (term->parentheses.isTerminal) {
|
||||
// Terminal groups are optimized on the assumption that matching will never
|
||||
// backtrack into the terminal group. But this is false if there is more
|
||||
// than one alternative and one of the alternatives can match empty. In that
|
||||
// case, the empty match is counted as a failure, so we would need to backtrack.
|
||||
// The backtracking code doesn't handle this case correctly, so we fall back
|
||||
// to the interpreter.
|
||||
Vector<PatternAlternative*>& alternatives = term->parentheses.disjunction->m_alternatives;
|
||||
if (alternatives.size() != 1) {
|
||||
for (unsigned i = 0; i < alternatives.size(); ++i) {
|
||||
if (alternatives[i]->m_minimumSize == 0) {
|
||||
m_shouldFallBack = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Select the 'Terminal' nodes.
|
||||
parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin;
|
||||
parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd;
|
||||
|
|
|
@ -1862,14 +1862,15 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
if (aBuilder->IsBackgroundOnly())
|
||||
return NS_OK;
|
||||
|
||||
if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
|
||||
nsIFrame* child = aChild;
|
||||
if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
|
||||
return NS_OK;
|
||||
|
||||
// true if this is a real or pseudo stacking context
|
||||
bool pseudoStackingContext =
|
||||
(aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
|
||||
if ((aFlags & DISPLAY_CHILD_INLINE) &&
|
||||
!aChild->IsFrameOfType(eLineParticipant)) {
|
||||
!child->IsFrameOfType(eLineParticipant)) {
|
||||
// child is a non-inline frame in an inline context, i.e.,
|
||||
// it acts like inline-block or inline-table. Therefore it is a
|
||||
// pseudo-stacking-context.
|
||||
|
@ -1877,24 +1878,28 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
// dirty rect in child-relative coordinates
|
||||
nsRect dirty = aDirtyRect - aChild->GetOffsetTo(this);
|
||||
nsRect dirty = aDirtyRect - child->GetOffsetTo(this);
|
||||
|
||||
nsIAtom* childType = aChild->GetType();
|
||||
nsIAtom* childType = child->GetType();
|
||||
if (childType == nsGkAtoms::placeholderFrame) {
|
||||
nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aChild);
|
||||
aChild = placeholder->GetOutOfFlowFrame();
|
||||
NS_ASSERTION(aChild, "No out of flow frame?");
|
||||
if (!aChild || nsLayoutUtils::IsPopup(aChild))
|
||||
nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child);
|
||||
child = placeholder->GetOutOfFlowFrame();
|
||||
NS_ASSERTION(child, "No out of flow frame?");
|
||||
// If 'child' is a pushed float then it's owned by a block that's not an
|
||||
// ancestor of the placeholder, and it will be painted by that block and
|
||||
// should not be painted through the placeholder.
|
||||
if (!child || nsLayoutUtils::IsPopup(child) ||
|
||||
(child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
|
||||
return NS_OK;
|
||||
// Make sure that any attempt to use childType below is disappointed. We
|
||||
// could call GetType again but since we don't currently need it, let's
|
||||
// avoid the virtual call.
|
||||
childType = nsnull;
|
||||
// Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
|
||||
if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
|
||||
if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
|
||||
return NS_OK;
|
||||
nsRect* savedDirty = static_cast<nsRect*>
|
||||
(aChild->Properties().Get(nsDisplayListBuilder::OutOfFlowDirtyRectProperty()));
|
||||
(child->Properties().Get(nsDisplayListBuilder::OutOfFlowDirtyRectProperty()));
|
||||
if (savedDirty) {
|
||||
dirty = *savedDirty;
|
||||
} else {
|
||||
|
@ -1907,30 +1912,30 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
// Mark the display list items for absolutely positioned children
|
||||
aChild->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
|
||||
child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
|
||||
|
||||
if (childType != nsGkAtoms::placeholderFrame &&
|
||||
aBuilder->GetSelectedFramesOnly() &&
|
||||
aChild->IsLeaf() &&
|
||||
!(aChild->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) {
|
||||
child->IsLeaf() &&
|
||||
!(child->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aBuilder->GetIncludeAllOutOfFlows() &&
|
||||
(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
|
||||
dirty = aChild->GetVisualOverflowRect();
|
||||
} else if (!(aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
|
||||
// No need to descend into aChild to catch placeholders for visible
|
||||
(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
|
||||
dirty = child->GetVisualOverflowRect();
|
||||
} else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
|
||||
// No need to descend into child to catch placeholders for visible
|
||||
// positioned stuff. So see if we can short-circuit frame traversal here.
|
||||
|
||||
// We can stop if aChild's frame subtree's intersection with the
|
||||
// We can stop if child's frame subtree's intersection with the
|
||||
// dirty area is empty.
|
||||
// If the child is a scrollframe that we want to ignore, then we need
|
||||
// to descend into it because its scrolled child may intersect the dirty
|
||||
// area even if the scrollframe itself doesn't.
|
||||
if (aChild != aBuilder->GetIgnoreScrollFrame()) {
|
||||
if (child != aBuilder->GetIgnoreScrollFrame()) {
|
||||
nsRect childDirty;
|
||||
if (!childDirty.IntersectRect(dirty, aChild->GetVisualOverflowRect()))
|
||||
if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect()))
|
||||
return NS_OK;
|
||||
// Usually we could set dirty to childDirty now but there's no
|
||||
// benefit, and it can be confusing. It can especially confuse
|
||||
|
@ -1951,10 +1956,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
|
||||
// Child is composited if it's transformed, partially transparent, or has
|
||||
// SVG effects.
|
||||
const nsStyleDisplay* disp = aChild->GetStyleDisplay();
|
||||
const nsStyleDisplay* disp = child->GetStyleDisplay();
|
||||
bool isVisuallyAtomic = disp->mOpacity != 1.0f
|
||||
|| aChild->IsTransformed()
|
||||
|| nsSVGIntegrationUtils::UsingEffectsForFrame(aChild);
|
||||
|| child->IsTransformed()
|
||||
|| nsSVGIntegrationUtils::UsingEffectsForFrame(child);
|
||||
|
||||
bool isPositioned = disp->IsPositioned();
|
||||
if (isVisuallyAtomic || isPositioned || disp->IsFloating() ||
|
||||
|
@ -1966,9 +1971,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
nsRect overflowClip;
|
||||
nscoord overflowClipRadii[8];
|
||||
bool applyOverflowClip =
|
||||
ApplyOverflowClipping(aBuilder, aChild, disp, &overflowClip);
|
||||
ApplyOverflowClipping(aBuilder, child, disp, &overflowClip);
|
||||
if (applyOverflowClip) {
|
||||
aChild->GetPaddingBoxBorderRadii(overflowClipRadii);
|
||||
child->GetPaddingBoxBorderRadii(overflowClipRadii);
|
||||
}
|
||||
// Don't use overflowClip to restrict the dirty rect, since some of the
|
||||
// descendants may not be clipped by it. Even if we end up with unnecessary
|
||||
|
@ -1984,34 +1989,34 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
// Not a pseudo or real stacking context. Do the simple thing and
|
||||
// return early.
|
||||
if (applyOverflowClip) {
|
||||
rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, dirty, aLists,
|
||||
rv = BuildDisplayListWithOverflowClip(aBuilder, child, dirty, aLists,
|
||||
overflowClip, overflowClipRadii);
|
||||
} else {
|
||||
rv = aChild->BuildDisplayList(aBuilder, dirty, aLists);
|
||||
rv = child->BuildDisplayList(aBuilder, dirty, aLists);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aBuilder->DisplayCaret(aChild, dirty, aLists.Content());
|
||||
rv = aBuilder->DisplayCaret(child, dirty, aLists.Content());
|
||||
}
|
||||
}
|
||||
#ifdef NS_DEBUG
|
||||
DisplayDebugBorders(aBuilder, aChild, aLists);
|
||||
DisplayDebugBorders(aBuilder, child, aLists);
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsDisplayList list;
|
||||
nsDisplayList extraPositionedDescendants;
|
||||
const nsStylePosition* pos = aChild->GetStylePosition();
|
||||
const nsStylePosition* pos = child->GetStylePosition();
|
||||
if ((isPositioned && pos->mZIndex.GetUnit() == eStyleUnit_Integer) ||
|
||||
isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
|
||||
// True stacking context
|
||||
rv = aChild->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
|
||||
rv = child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aBuilder->DisplayCaret(aChild, dirty, &list);
|
||||
rv = aBuilder->DisplayCaret(child, dirty, &list);
|
||||
}
|
||||
} else {
|
||||
nsRect clipRect;
|
||||
bool applyAbsPosClipping =
|
||||
ApplyAbsPosClipping(aBuilder, disp, aChild, &clipRect);
|
||||
ApplyAbsPosClipping(aBuilder, disp, child, &clipRect);
|
||||
// A pseudo-stacking context (e.g., a positioned element with z-index auto).
|
||||
// We allow positioned descendants of the child to escape to our parent
|
||||
// stacking context's positioned descendant list, because they might be
|
||||
|
@ -2020,26 +2025,26 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
nsRect clippedDirtyRect = dirty;
|
||||
if (applyAbsPosClipping) {
|
||||
// clipRect is in builder-reference-frame coordinates,
|
||||
// dirty/clippedDirtyRect are in aChild coordinates
|
||||
// dirty/clippedDirtyRect are in child coordinates
|
||||
clippedDirtyRect.IntersectRect(clippedDirtyRect,
|
||||
clipRect - aBuilder->ToReferenceFrame(aChild));
|
||||
clipRect - aBuilder->ToReferenceFrame(child));
|
||||
}
|
||||
|
||||
if (applyOverflowClip) {
|
||||
rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, clippedDirtyRect,
|
||||
rv = BuildDisplayListWithOverflowClip(aBuilder, child, clippedDirtyRect,
|
||||
pseudoStack, overflowClip,
|
||||
overflowClipRadii);
|
||||
} else {
|
||||
rv = aChild->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack);
|
||||
rv = child->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aBuilder->DisplayCaret(aChild, dirty, pseudoStack.Content());
|
||||
rv = aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (applyAbsPosClipping) {
|
||||
nsAbsPosClipWrapper wrapper(clipRect);
|
||||
rv = wrapper.WrapListsInPlace(aBuilder, aChild, pseudoStack);
|
||||
rv = wrapper.WrapListsInPlace(aBuilder, child, pseudoStack);
|
||||
}
|
||||
}
|
||||
list.AppendToTop(pseudoStack.BorderBackground());
|
||||
|
@ -2049,7 +2054,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
list.AppendToTop(pseudoStack.Outlines());
|
||||
extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
|
||||
#ifdef NS_DEBUG
|
||||
DisplayDebugBorders(aBuilder, aChild, aLists);
|
||||
DisplayDebugBorders(aBuilder, child, aLists);
|
||||
#endif
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -2059,11 +2064,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
|
||||
// go in this level.
|
||||
rv = aLists.PositionedDescendants()->AppendNewToTop(new (aBuilder)
|
||||
nsDisplayWrapList(aBuilder, aChild, &list));
|
||||
nsDisplayWrapList(aBuilder, child, &list));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (disp->IsFloating()) {
|
||||
rv = aLists.Floats()->AppendNewToTop(new (aBuilder)
|
||||
nsDisplayWrapList(aBuilder, aChild, &list));
|
||||
nsDisplayWrapList(aBuilder, child, &list));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
aLists.Content()->AppendToTop(&list);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html><body>
|
||||
<div style="float:left; width:200px;">X</div>
|
||||
<div style="float:left; background:rgba(0,0,0,0.5); width:210px;">Y</div>
|
||||
</body></html>
|
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html><body>
|
||||
<div style="-moz-column-count:3; -moz-column-gap:0; width:600px;">X<div style="float:left; background:rgba(0,0,0,0.5);"><div style="position:absolute;">Y</div><span style="padding-left:210px;"></span></div></div>
|
||||
</body></html>
|
|
@ -1670,4 +1670,5 @@ fails-if(layersGPUAccelerated&&cocoaWidget) == 654950-1.html 654950-1-ref.html #
|
|||
== 670467-2.html 670467-2-ref.html
|
||||
!= 691087-1.html 691087-1-ref.html
|
||||
== 691571-1.html 691571-1-ref.html
|
||||
== 696307-1.html 696307-1-ref.html
|
||||
== 696739-1.html 696739-1-ref.html
|
||||
|
|
|
@ -109,11 +109,11 @@ random == img-and-image-1.html img-and-image-1-ref.svg # bug 645267
|
|||
# tests for external resources vs. data URIs in SVG as an image
|
||||
== svg-image-datauri-1.html lime100x100.svg
|
||||
HTTP == svg-image-datauri-1.html lime100x100.svg
|
||||
fails-if(Android) == svg-image-external-1.html lime100x100.svg
|
||||
== svg-image-external-1.html blue100x100.svg
|
||||
HTTP == svg-image-external-1.html blue100x100.svg
|
||||
== svg-stylesheet-datauri-1.html lime100x100.svg
|
||||
HTTP == svg-stylesheet-datauri-1.html lime100x100.svg
|
||||
random == svg-stylesheet-external-1.html lime100x100.svg # see bug 629885 comment 9
|
||||
== svg-stylesheet-external-1.html blue100x100.svg
|
||||
HTTP == svg-stylesheet-external-1.html blue100x100.svg
|
||||
|
||||
# test that :visited status is ignored in image documents
|
||||
|
|
|
@ -6878,24 +6878,7 @@ zone_destroy(malloc_zone_t *zone)
|
|||
static size_t
|
||||
zone_good_size(malloc_zone_t *zone, size_t size)
|
||||
{
|
||||
size_t ret;
|
||||
void *p;
|
||||
|
||||
/*
|
||||
* Actually create an object of the appropriate size, then find out
|
||||
* how large it could have been without moving up to the next size
|
||||
* class.
|
||||
*
|
||||
* I sure hope this doesn't get called often.
|
||||
*/
|
||||
p = malloc(size);
|
||||
if (p != NULL) {
|
||||
ret = isalloc(p);
|
||||
free(p);
|
||||
} else
|
||||
ret = size;
|
||||
|
||||
return (ret);
|
||||
return je_malloc_usable_size_in_advance(size);
|
||||
}
|
||||
|
||||
static size_t
|
||||
|
|
|
@ -615,6 +615,8 @@ nsHttpHandler::BuildUserAgent()
|
|||
mAppName.Length() +
|
||||
mAppVersion.Length() +
|
||||
mCompatFirefox.Length() +
|
||||
mDeviceType.Length() +
|
||||
mDeviceName.Length() +
|
||||
13);
|
||||
|
||||
// Application portion
|
||||
|
@ -632,6 +634,10 @@ nsHttpHandler::BuildUserAgent()
|
|||
mUserAgent += mOscpu;
|
||||
mUserAgent.AppendLiteral("; ");
|
||||
mUserAgent += mMisc;
|
||||
if (!mDeviceName.IsEmpty()) {
|
||||
mUserAgent.AppendLiteral("; ");
|
||||
mUserAgent += mDeviceName;
|
||||
}
|
||||
mUserAgent += ')';
|
||||
|
||||
// Product portion
|
||||
|
@ -646,6 +652,11 @@ nsHttpHandler::BuildUserAgent()
|
|||
mUserAgent += mCompatFirefox;
|
||||
}
|
||||
|
||||
if (!mDeviceType.IsEmpty()) {
|
||||
mUserAgent += ' ';
|
||||
mUserAgent += mDeviceType;
|
||||
}
|
||||
|
||||
// App portion
|
||||
mUserAgent += ' ';
|
||||
mUserAgent += mAppName;
|
||||
|
@ -683,6 +694,32 @@ nsHttpHandler::InitUserAgentComponents()
|
|||
#endif
|
||||
);
|
||||
|
||||
#if defined(ANDROID)
|
||||
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
|
||||
NS_ASSERTION(infoService, "Could not find a system info service");
|
||||
|
||||
bool isTablet;
|
||||
infoService->GetPropertyAsBool(NS_LITERAL_STRING("isTablet"), &isTablet);
|
||||
if (!isTablet) {
|
||||
mDeviceType.AssignLiteral("Mobile");
|
||||
}
|
||||
infoService->GetPropertyAsACString(NS_LITERAL_STRING("device"),
|
||||
mDeviceName);
|
||||
nsXPIDLCString buildid;
|
||||
infoService->GetPropertyAsACString(NS_LITERAL_STRING("buildid"),
|
||||
buildid);
|
||||
if (!buildid.IsEmpty()) {
|
||||
mDeviceName += " Build/";
|
||||
mDeviceName += buildid;
|
||||
}
|
||||
|
||||
nsXPIDLCString shellVersion;
|
||||
infoService->GetPropertyAsACString(NS_LITERAL_STRING("shellVersion"),
|
||||
shellVersion);
|
||||
mPlatform += " ";
|
||||
mPlatform += shellVersion;
|
||||
#endif
|
||||
|
||||
// Gather OS/CPU.
|
||||
#if defined(XP_OS2)
|
||||
ULONG os2ver = 0;
|
||||
|
|
|
@ -313,6 +313,8 @@ private:
|
|||
nsXPIDLCString mProductSub;
|
||||
nsXPIDLCString mAppName;
|
||||
nsXPIDLCString mAppVersion;
|
||||
nsXPIDLCString mDeviceType;
|
||||
nsXPIDLCString mDeviceName;
|
||||
nsCString mCompatFirefox;
|
||||
|
||||
nsCString mUserAgent;
|
||||
|
|
|
@ -152,7 +152,7 @@ nsHtml5Highlighter::Start()
|
|||
if (mTabSize > 0) {
|
||||
nsString* style = new nsString(NS_LITERAL_STRING("-moz-tab-size: "));
|
||||
style->AppendInt(mTabSize);
|
||||
bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_CLASS, style);
|
||||
bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_STYLE, style);
|
||||
}
|
||||
|
||||
Push(nsGkAtoms::body, bodyAttrs);
|
||||
|
@ -776,6 +776,9 @@ nsHtml5Highlighter::AppendCharacters(const PRUnichar* aBuffer,
|
|||
void
|
||||
nsHtml5Highlighter::AddClass(const PRUnichar* aClass)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
|
||||
}
|
||||
|
||||
|
@ -795,6 +798,9 @@ nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue)
|
|||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(CurrentNode(), aMsgId);
|
||||
|
@ -803,6 +809,9 @@ nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
|
|||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
@ -813,6 +822,9 @@ void
|
|||
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
||||
nsIAtom* aName)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
@ -824,6 +836,9 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
|||
nsIAtom* aName,
|
||||
nsIAtom* aOther)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
@ -833,6 +848,9 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
|||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
@ -842,6 +860,9 @@ nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
|
|||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mSlash, "Adding error to slash without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
1
|
||||
2
|
||||
3
|
|
@ -0,0 +1,3 @@
|
|||
1
|
||||
2
|
||||
3
|
|
@ -15,3 +15,4 @@
|
|||
== view-source:bug673094-1.html view-source:bug673094-1-ref.html
|
||||
== bug696651-1.html bug696651-1-ref.html
|
||||
== bug696651-2.html bug696651-2-ref.html
|
||||
== view-source:bug700260-1.html view-source:bug700260-1-ref.html
|
||||
|
|
|
@ -80,29 +80,16 @@
|
|||
|
||||
// Set when the database file was found corrupt by a previous maintenance.
|
||||
#define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceOnStartup"
|
||||
// Set to the calculated optimal size of the database. This is a target size
|
||||
// used to evaluate history limits. It's not mandatory.
|
||||
#define PREF_OPTIMAL_DATABASE_SIZE "places.history.expiration.transient_optimal_database_size"
|
||||
|
||||
// To calculate the cache size we take into account the available physical
|
||||
// memory and the current database size. This is the percentage of memory
|
||||
// we reserve for the former case.
|
||||
#define DATABASE_CACHE_TO_MEMORY_PERC 2
|
||||
// The wanted size of the cache. This is calculated based on current database
|
||||
// size and clamped to the limits specified below.
|
||||
#define DATABASE_CACHE_TO_DATABASE_PERC 10
|
||||
// The minimum size of the cache. We should never work without a cache, since
|
||||
// that would badly hurt WAL journaling mode.
|
||||
#define DATABASE_CACHE_MIN_BYTES (PRUint64)5242880 // 5MiB
|
||||
// We calculate an optimal database size, based on hardware specs. This
|
||||
// pertains more to expiration, but the code is pretty much the same used for
|
||||
// cache_size, so it's here to reduce code duplication.
|
||||
// This percentage of disk size is used to protect against calculating a too
|
||||
// large size on disks with tiny quota or available space.
|
||||
#define DATABASE_TO_DISK_PERC 2
|
||||
// Maximum size of the optimal database. High-end hardware has plenty of
|
||||
// memory and disk space, but performances don't grow linearly.
|
||||
#define DATABASE_MAX_SIZE (PRInt64)167772160 // 160MiB
|
||||
// If the physical memory size is not available, use MEMSIZE_FALLBACK_BYTES
|
||||
// instead. Must stay in sync with the code in nsPlacesExpiration.js.
|
||||
#define MEMSIZE_FALLBACK_BYTES 268435456 // 256 M
|
||||
#define DATABASE_CACHE_MIN_BYTES (PRUint64)4194304 // 4MiB
|
||||
// The maximum size of the cache. This is the maximum memory that each
|
||||
// connection may use.
|
||||
#define DATABASE_CACHE_MAX_BYTES (PRUint64)8388608 // 8MiB
|
||||
|
||||
// Maximum size for the WAL file. It should be small enough since in case of
|
||||
// crashes we could lose all the transactions in the file. But a too small
|
||||
|
@ -498,42 +485,6 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
|||
"PRAGMA temp_store = MEMORY"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We want to work with a cache that is at a maximum half of the database
|
||||
// size. We also want it to respect the available memory size.
|
||||
|
||||
// Calculate memory size, fallback to a meaningful value if it fails.
|
||||
PRUint64 memSizeBytes = PR_GetPhysicalMemorySize();
|
||||
if (memSizeBytes == 0) {
|
||||
memSizeBytes = MEMSIZE_FALLBACK_BYTES;
|
||||
}
|
||||
|
||||
PRUint64 cacheSize = memSizeBytes * DATABASE_CACHE_TO_MEMORY_PERC / 100;
|
||||
|
||||
// Calculate an optimal database size for expiration purposes.
|
||||
// We usually want to work with a cache that is half the database size.
|
||||
// Limit the size to avoid extreme values on high-end hardware.
|
||||
PRInt64 optimalDatabaseSize = NS_MIN(static_cast<PRInt64>(cacheSize) * 2,
|
||||
DATABASE_MAX_SIZE);
|
||||
|
||||
// Protect against a full disk or tiny quota.
|
||||
PRInt64 diskAvailableBytes = 0;
|
||||
nsCOMPtr<nsIFile> databaseFile;
|
||||
mMainConn->GetDatabaseFile(getter_AddRefs(databaseFile));
|
||||
NS_ENSURE_STATE(databaseFile);
|
||||
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(databaseFile);
|
||||
if (localFile &&
|
||||
NS_SUCCEEDED(localFile->GetDiskSpaceAvailable(&diskAvailableBytes)) &&
|
||||
diskAvailableBytes > 0) {
|
||||
optimalDatabaseSize = NS_MIN(optimalDatabaseSize,
|
||||
diskAvailableBytes * DATABASE_TO_DISK_PERC / 100);
|
||||
}
|
||||
|
||||
// Share the calculated size if it's meaningful.
|
||||
if (optimalDatabaseSize < PR_INT32_MAX) {
|
||||
(void)Preferences::SetInt(PREF_OPTIMAL_DATABASE_SIZE,
|
||||
static_cast<PRInt32>(optimalDatabaseSize));
|
||||
}
|
||||
|
||||
// Get the current database size. Due to chunked growth we have to use
|
||||
// page_count to evaluate it.
|
||||
PRUint64 databaseSizeBytes = 0;
|
||||
|
@ -552,10 +503,11 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
|||
databaseSizeBytes = pageCount * mDBPageSize;
|
||||
}
|
||||
|
||||
// Set cache to a maximum of half the database size.
|
||||
cacheSize = NS_MIN(cacheSize, databaseSizeBytes / 2);
|
||||
// Ensure we never work without a minimum cache.
|
||||
cacheSize = NS_MAX(cacheSize, DATABASE_CACHE_MIN_BYTES);
|
||||
// Clamp the cache size to a percentage of the database size, forcing
|
||||
// meaningful limits.
|
||||
PRInt64 cacheSize = clamped(databaseSizeBytes * DATABASE_CACHE_TO_DATABASE_PERC / 100,
|
||||
DATABASE_CACHE_MIN_BYTES,
|
||||
DATABASE_CACHE_MAX_BYTES);
|
||||
|
||||
// Set the number of cached pages.
|
||||
// We don't use PRAGMA default_cache_size, since the database could be moved
|
||||
|
|
|
@ -222,7 +222,6 @@ NS_INTERFACE_MAP_BEGIN(nsNavHistory)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIBrowserHistory)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICharsetResolver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIPlacesDatabase)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIPlacesHistoryListenersNotifier)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageVacuumParticipant)
|
||||
|
@ -5599,44 +5598,3 @@ nsNavHistory::GetDateFormatBundle()
|
|||
}
|
||||
return mDateFormatBundle;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsICharsetResolver
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::RequestCharset(nsIWebNavigation* aWebNavigation,
|
||||
nsIChannel* aChannel,
|
||||
bool* aWantCharset,
|
||||
nsISupports** aClosure,
|
||||
nsACString& aResult)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
|
||||
NS_ENSURE_ARG(aChannel);
|
||||
NS_ENSURE_ARG_POINTER(aWantCharset);
|
||||
NS_ENSURE_ARG_POINTER(aClosure);
|
||||
|
||||
*aWantCharset = false;
|
||||
*aClosure = nsnull;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv))
|
||||
return NS_OK;
|
||||
|
||||
nsAutoString charset;
|
||||
rv = GetCharsetForURI(uri, charset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
CopyUTF16toUTF8(charset, aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::NotifyResolvedCharset(const nsACString& aCharset,
|
||||
nsISupports* aClosure)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
|
||||
|
||||
NS_ERROR("Unexpected call to NotifyResolvedCharset -- we never set aWantCharset to true!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "nsICollation.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsICharsetResolver.h"
|
||||
#include "nsMaybeWeakPtr.h"
|
||||
#include "nsCategoryCache.h"
|
||||
#include "nsNetCID.h"
|
||||
|
@ -111,7 +110,6 @@ class nsNavHistory : public nsSupportsWeakReference
|
|||
, public nsIObserver
|
||||
, public nsIBrowserHistory
|
||||
, public nsIDownloadHistory
|
||||
, public nsICharsetResolver
|
||||
, public nsPIPlacesDatabase
|
||||
, public nsPIPlacesHistoryListenersNotifier
|
||||
, public mozIStorageVacuumParticipant
|
||||
|
@ -501,8 +499,6 @@ protected:
|
|||
*/
|
||||
nsresult DecayFrecency();
|
||||
|
||||
NS_DECL_NSICHARSETRESOLVER
|
||||
|
||||
nsresult CalculateFrecency(PRInt64 aPageID, PRInt32 aTyped, PRInt32 aVisitCount, nsCAutoString &aURL, PRInt32 *aFrecency);
|
||||
nsresult CalculateFrecencyInternal(PRInt64 aPageID, PRInt32 aTyped, PRInt32 aVisitCount, bool aIsBookmarked, PRInt32 *aFrecency);
|
||||
|
||||
|
|
|
@ -110,10 +110,20 @@ const PREF_READONLY_CALCULATED_MAX_URIS = "transient_current_max_pages";
|
|||
const PREF_INTERVAL_SECONDS = "interval_seconds";
|
||||
const PREF_INTERVAL_SECONDS_NOTSET = 3 * 60;
|
||||
|
||||
// An optimal database size calculated by history. Used to evaluate a limit
|
||||
// to the number of pages we may retain before hitting performance issues.
|
||||
const PREF_OPTIMAL_DATABASE_SIZE = "transient_optimal_database_size";
|
||||
const PREF_OPTIMAL_DATABASE_SIZE_NOTSET = 167772160; // 160MiB
|
||||
// We calculate an optimal database size, based on hardware specs.
|
||||
// This percentage of memory size is used to protect against calculating a too
|
||||
// large database size on systems with small memory.
|
||||
const DATABASE_TO_MEMORY_PERC = 4;
|
||||
// This percentage of disk size is used to protect against calculating a too
|
||||
// large database size on disks with tiny quota or available space.
|
||||
const DATABASE_TO_DISK_PERC = 2;
|
||||
// Maximum size of the optimal database. High-end hardware has plenty of
|
||||
// memory and disk space, but performances don't grow linearly.
|
||||
const DATABASE_MAX_SIZE = 167772160; // 160MiB
|
||||
// If the physical memory size is bogus, fallback to this.
|
||||
const MEMSIZE_FALLBACK_BYTES = 268435456; // 256 MiB
|
||||
// If the disk available space is bogus, fallback to this.
|
||||
const DISKSIZE_FALLBACK_BYTES = 268435456; // 256 MiB
|
||||
|
||||
// Max number of entries to expire at each expiration step.
|
||||
// This value is globally used for different kind of data we expire, can be
|
||||
|
@ -782,12 +792,36 @@ nsPlacesExpiration.prototype = {
|
|||
// Calculate the number of unique places that may fit an optimal database
|
||||
// size on this hardware. If there are more than these unique pages,
|
||||
// some will be expired.
|
||||
let optimalDatabaseSize = PREF_OPTIMAL_DATABASE_SIZE_NOTSET;
|
||||
|
||||
let memSizeBytes = MEMSIZE_FALLBACK_BYTES;
|
||||
try {
|
||||
optimalDatabaseSize = this._prefBranch.getIntPref(PREF_OPTIMAL_DATABASE_SIZE);
|
||||
// Limit the size on systems with small memory.
|
||||
memSizeBytes = this._sys.getProperty("memsize");
|
||||
} catch (ex) {}
|
||||
if (memSizeBytes <= 0) {
|
||||
memsize = MEMSIZE_FALLBACK_BYTES;
|
||||
}
|
||||
|
||||
let diskAvailableBytes = DISKSIZE_FALLBACK_BYTES;
|
||||
try {
|
||||
// Protect against a full disk or tiny quota.
|
||||
let dbFile = this._db.databaseFile;
|
||||
dbFile.QueryInterface(Ci.nsILocalFile);
|
||||
diskAvailableBytes = dbFile.diskSpaceAvailable;
|
||||
} catch (ex) {}
|
||||
if (diskAvailableBytes <= 0) {
|
||||
diskAvailableBytes = DISKSIZE_FALLBACK_BYTES;
|
||||
}
|
||||
|
||||
let optimalDatabaseSize = Math.min(
|
||||
memSizeBytes * DATABASE_TO_MEMORY_PERC / 100,
|
||||
diskAvailableBytes * DATABASE_TO_DISK_PERC / 100,
|
||||
DATABASE_MAX_SIZE
|
||||
);
|
||||
|
||||
this._urisLimit = Math.ceil(optimalDatabaseSize / URIENTRY_AVG_SIZE);
|
||||
}
|
||||
|
||||
// Expose the calculated limit to other components.
|
||||
this._prefBranch.setIntPref(PREF_READONLY_CALCULATED_MAX_URIS,
|
||||
this._urisLimit);
|
||||
|
|
|
@ -163,9 +163,7 @@ FormAutoComplete.prototype = {
|
|||
*/
|
||||
autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) {
|
||||
function sortBytotalScore (a, b) {
|
||||
let x = a.totalScore;
|
||||
let y = b.totalScore;
|
||||
return ((x > y) ? -1 : ((x < y) ? 1 : 0));
|
||||
return b.totalScore - a.totalScore;
|
||||
}
|
||||
|
||||
if (!this._enabled)
|
||||
|
|
|
@ -138,8 +138,8 @@ FormHistory.prototype = {
|
|||
this.messageManager.addMessageListener("FormHistory:FormSubmitEntries", this);
|
||||
|
||||
// Add observers
|
||||
Services.obs.addObserver(function() { self.expireOldEntries() }, "idle-daily", false);
|
||||
Services.obs.addObserver(function() { self.expireOldEntries() }, "formhistory-expire-now", false);
|
||||
Services.obs.addObserver(this, "idle-daily", false);
|
||||
Services.obs.addObserver(this, "formhistory-expire-now", false);
|
||||
},
|
||||
|
||||
/* ---- message listener ---- */
|
||||
|
@ -395,10 +395,18 @@ FormHistory.prototype = {
|
|||
|
||||
|
||||
observe : function (subject, topic, data) {
|
||||
if (topic == "nsPref:changed")
|
||||
switch(topic) {
|
||||
case "nsPref:changed":
|
||||
this.updatePrefs();
|
||||
else
|
||||
break;
|
||||
case "idle-daily":
|
||||
case "formhistory-expire-now":
|
||||
this.expireOldEntries();
|
||||
break;
|
||||
default:
|
||||
this.log("Oops! Unexpected notification: " + topic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -51,10 +51,10 @@ const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
|
|||
// under the "accessibility.*" branch.
|
||||
const PREFS_WHITELIST = [
|
||||
"accessibility.",
|
||||
"browser.display.",
|
||||
"browser.fixup.",
|
||||
"browser.history_expire_",
|
||||
"browser.link.open_newwindow",
|
||||
"browser.mousewheel.",
|
||||
"browser.places.",
|
||||
"browser.startup.homepage",
|
||||
"browser.tabs.",
|
||||
|
@ -63,6 +63,7 @@ const PREFS_WHITELIST = [
|
|||
"extensions.checkCompatibility",
|
||||
"extensions.lastAppVersion",
|
||||
"font.",
|
||||
"general.autoScroll",
|
||||
"general.useragent.",
|
||||
"gfx.",
|
||||
"html5.",
|
||||
|
@ -70,7 +71,9 @@ const PREFS_WHITELIST = [
|
|||
"javascript.",
|
||||
"keyword.",
|
||||
"layout.css.dpi",
|
||||
"mousewheel.",
|
||||
"network.",
|
||||
"permissions.default.image",
|
||||
"places.",
|
||||
"plugin.",
|
||||
"plugins.",
|
||||
|
|
|
@ -46,6 +46,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/ctypes.jsm")
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -198,6 +199,113 @@ XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
|
|||
}
|
||||
|
||||
if (osVersion) {
|
||||
#ifdef XP_WIN
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.jschar;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if(kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor
|
||||
+ "." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting processor architecture. Exception: " + e);
|
||||
} finally {
|
||||
osVersion += " (" + arch + ")";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/ctypes.jsm")
|
||||
|
||||
/* General URL Construction Tests */
|
||||
|
||||
const URL_PREFIX = URL_HOST + URL_PATH + "/";
|
||||
|
@ -228,12 +230,132 @@ function run_test_pt9() {
|
|||
gUpdateChecker.checkForUpdates(updateCheckListener, true);
|
||||
}
|
||||
|
||||
function getServicePack() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.jschar;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 === GetVersionEx(winVer.address())) {
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Failure in GetVersionEx (returned 0)");
|
||||
}
|
||||
|
||||
return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
function getProcArchitecture() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
return "x64";
|
||||
case 6:
|
||||
return "IA64";
|
||||
case 0:
|
||||
return "x86";
|
||||
default:
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
function check_test_pt9() {
|
||||
var osVersion;
|
||||
var sysInfo = AUS_Cc["@mozilla.org/system-info;1"].
|
||||
getService(AUS_Ci.nsIPropertyBag2);
|
||||
osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
|
||||
|
||||
if(IS_WIN) {
|
||||
try {
|
||||
let servicePack = getServicePack();
|
||||
osVersion += "." + servicePack;
|
||||
} catch (e) {
|
||||
do_throw("Failure obtaining service pack: " + e);
|
||||
}
|
||||
|
||||
if("5.0" === sysInfo.getProperty("version")) { // Win2K
|
||||
osVersion += " (unknown)";
|
||||
} else {
|
||||
try {
|
||||
osVersion += " (" + getProcArchitecture() + ")";
|
||||
} catch (e) {
|
||||
do_throw("Failed to obtain processor architecture: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
|
|
|
@ -141,6 +141,9 @@ AndroidBridge::Init(JNIEnv *jEnv,
|
|||
jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V");
|
||||
jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V");
|
||||
jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V");
|
||||
jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V");
|
||||
jVibrateA = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "([JI)V");
|
||||
jCancelVibrate = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "cancelVibrate", "()V");
|
||||
jSetKeepScreenOn = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setKeepScreenOn", "(Z)V");
|
||||
jIsNetworkLinkUp = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkUp", "()Z");
|
||||
jIsNetworkLinkKnown = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkKnown", "()Z");
|
||||
|
@ -154,6 +157,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
|
|||
jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V");
|
||||
jInitCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "initCamera", "(Ljava/lang/String;III)[I");
|
||||
jCloseCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "closeCamera", "()V");
|
||||
jIsTablet = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isTablet", "()Z");
|
||||
jEnableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableBatteryNotifications", "()V");
|
||||
jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V");
|
||||
jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[F");
|
||||
|
@ -435,9 +439,9 @@ AndroidBridge::GetHandlersForMimeType(const char *aMimeType,
|
|||
|
||||
bool
|
||||
AndroidBridge::GetHandlersForURL(const char *aURL,
|
||||
nsIMutableArray* aHandlersArray,
|
||||
nsIHandlerApp **aDefaultApp,
|
||||
const nsAString& aAction)
|
||||
nsIMutableArray* aHandlersArray,
|
||||
nsIHandlerApp **aDefaultApp,
|
||||
const nsAString& aAction)
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::GetHandlersForURL");
|
||||
|
||||
|
@ -675,6 +679,62 @@ AndroidBridge::PerformHapticFeedback(bool aIsLongPress)
|
|||
jPerformHapticFeedback, aIsLongPress);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::Vibrate(const nsTArray<PRUint32>& aPattern)
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::Vibrate");
|
||||
|
||||
PRUint32 len = aPattern.Length();
|
||||
if (!len) {
|
||||
ALOG_BRIDGE(" invalid 0-length array");
|
||||
return;
|
||||
}
|
||||
|
||||
// It's clear if this worth special-casing, but it creates less
|
||||
// java junk, so dodges the GC.
|
||||
if (len == 1) {
|
||||
jlong d = aPattern[0];
|
||||
if (d < 0) {
|
||||
ALOG_BRIDGE(" invalid vibration duration < 0");
|
||||
return;
|
||||
}
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrate1, d);
|
||||
return;
|
||||
}
|
||||
|
||||
// First element of the array vibrate() expects is how long to wait
|
||||
// *before* vibrating. For us, this is always 0.
|
||||
|
||||
jlongArray array = mJNIEnv->NewLongArray(len + 1);
|
||||
if (!array) {
|
||||
ALOG_BRIDGE(" failed to allocate array");
|
||||
return;
|
||||
}
|
||||
|
||||
jlong* elts = mJNIEnv->GetLongArrayElements(array, nsnull);
|
||||
elts[0] = 0;
|
||||
for (PRUint32 i = 0; i < aPattern.Length(); ++i) {
|
||||
jlong d = aPattern[i];
|
||||
if (d < 0) {
|
||||
ALOG_BRIDGE(" invalid vibration duration < 0");
|
||||
mJNIEnv->ReleaseLongArrayElements(array, elts, JNI_ABORT);
|
||||
return;
|
||||
}
|
||||
elts[i + 1] = d;
|
||||
}
|
||||
mJNIEnv->ReleaseLongArrayElements(array, elts, 0);
|
||||
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrateA,
|
||||
array, -1/*don't repeat*/);
|
||||
// GC owns |array| now?
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::CancelVibrate()
|
||||
{
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jCancelVibrate);
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidBridge::IsNetworkLinkUp()
|
||||
{
|
||||
|
@ -1317,3 +1377,9 @@ AndroidBridge::UnlockWindow(void* window)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidBridge::IsTablet()
|
||||
{
|
||||
return mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
|
||||
}
|
||||
|
|
|
@ -199,6 +199,9 @@ public:
|
|||
|
||||
void PerformHapticFeedback(bool aIsLongPress);
|
||||
|
||||
void Vibrate(const nsTArray<PRUint32>& aPattern);
|
||||
void CancelVibrate();
|
||||
|
||||
void SetFullScreen(bool aFullScreen);
|
||||
|
||||
void ShowInputMethodPicker();
|
||||
|
@ -295,6 +298,8 @@ public:
|
|||
void DisableBatteryNotifications();
|
||||
void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
||||
|
||||
bool IsTablet();
|
||||
|
||||
protected:
|
||||
static AndroidBridge *sBridge;
|
||||
|
||||
|
@ -353,6 +358,9 @@ protected:
|
|||
jmethodID jShowInputMethodPicker;
|
||||
jmethodID jHideProgressDialog;
|
||||
jmethodID jPerformHapticFeedback;
|
||||
jmethodID jVibrate1;
|
||||
jmethodID jVibrateA;
|
||||
jmethodID jCancelVibrate;
|
||||
jmethodID jSetKeepScreenOn;
|
||||
jmethodID jIsNetworkLinkUp;
|
||||
jmethodID jIsNetworkLinkKnown;
|
||||
|
@ -366,6 +374,7 @@ protected:
|
|||
jmethodID jPostToJavaThread;
|
||||
jmethodID jInitCamera;
|
||||
jmethodID jCloseCamera;
|
||||
jmethodID jIsTablet;
|
||||
jmethodID jEnableBatteryNotifications;
|
||||
jmethodID jDisableBatteryNotifications;
|
||||
jmethodID jGetCurrentBatteryInformation;
|
||||
|
|
|
@ -3288,7 +3288,7 @@ nsWindow::GetLayerManager(PLayersChild* aShadowManager,
|
|||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_D3D10_LAYER
|
||||
if (!prefs.mPreferD3D9) {
|
||||
if (!prefs.mPreferD3D9 && !prefs.mPreferOpenGL) {
|
||||
nsRefPtr<mozilla::layers::LayerManagerD3D10> layerManager =
|
||||
new mozilla::layers::LayerManagerD3D10(this);
|
||||
if (layerManager->Initialize()) {
|
||||
|
@ -5271,7 +5271,11 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
|
|||
break;
|
||||
|
||||
case WM_KILLFOCUS:
|
||||
if (sJustGotDeactivate) {
|
||||
if (sJustGotDeactivate || !wParam) {
|
||||
// Note: wParam is FALSE when the window has lost focus. Sometimes
|
||||
// We can receive WM_KILLFOCUS with !wParam while changing to
|
||||
// full-screen mode and we won't receive an WM_ACTIVATE/WA_INACTIVE
|
||||
// message, so inform the focus manager that we've lost focus now.
|
||||
result = DispatchFocusToTopLevelWindow(NS_DEACTIVATE);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -181,22 +181,20 @@ nsSystemInfo::Init()
|
|||
SetPropertyAsAString(NS_LITERAL_STRING("device"), str);
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", str))
|
||||
SetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), str);
|
||||
|
||||
PRInt32 version;
|
||||
if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &version))
|
||||
version = 0;
|
||||
if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str))
|
||||
SetPropertyAsAString(NS_LITERAL_STRING("hardware"), str);
|
||||
if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "ID", str))
|
||||
SetPropertyAsAString(NS_LITERAL_STRING("buildid"), str);
|
||||
|
||||
SetPropertyAsAString(NS_LITERAL_STRING("shellName"), NS_LITERAL_STRING("Android"));
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "CODENAME", str)) {
|
||||
if (version) {
|
||||
str.Append(NS_LITERAL_STRING(" ("));
|
||||
str.AppendInt(version);
|
||||
str.Append(NS_LITERAL_STRING(")"));
|
||||
}
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "RELEASE", str))
|
||||
SetPropertyAsAString(NS_LITERAL_STRING("shellVersion"), str);
|
||||
}
|
||||
|
||||
|
||||
SetPropertyAsBool(NS_LITERAL_STRING("isTablet"),
|
||||
mozilla::AndroidBridge::Bridge()->IsTablet());
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
|
|
Загрузка…
Ссылка в новой задаче