Merge last green changeset of mozilla-inbound to mozilla-central

This commit is contained in:
Ed Morley 2011-11-08 08:01:18 +00:00
Родитель 48197e4006 dc33fffa65
Коммит 1fe3dee652
91 изменённых файлов: 2443 добавлений и 941 удалений

Просмотреть файл

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

Просмотреть файл

@ -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
Просмотреть файл

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

Двоичные данные
image/test/reftest/jpeg/jpg-cmyk-embed.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 229 B

Двоичные данные
image/test/reftest/jpeg/jpg-cmyk-embed.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 161 B

Двоичные данные
image/test/reftest/jpeg/jpg-ycck-embed.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 237 B

Двоичные данные
image/test/reftest/jpeg/jpg-ycck-embed.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.2 KiB

Двоичные данные
image/test/reftest/jpeg/jpg-ycck.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 227 B

Двоичные данные
image/test/reftest/jpeg/jpg-ycck.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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;