Merge inbound to m-c a=merge

This commit is contained in:
Wes Kocher 2014-07-02 16:31:17 -07:00
commit 075abc4af3
72 changed files with 1511 additions and 927 deletions

View File

@ -2321,12 +2321,19 @@ let BrowserOnClick = {
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
let docshell = aOwnerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let securityInfo = docshell.failedChannel.securityInfo;
let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
switch (elmId) {
case "exceptionDialogButton":
if (isTopFrame) {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
}
let params = { exceptionAdded : false };
let params = { exceptionAdded : false,
sslStatus : sslStatus };
try {
switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {

View File

@ -33,7 +33,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
@ -193,13 +192,6 @@ let SessionFileInternal = {
}
},
/**
* The promise returned by the latest call to |write|.
* We use it to ensure that AsyncShutdown.profileBeforeChange cannot
* interrupt a call to |write|.
*/
_latestWrite: null,
/**
* |true| once we have decided to stop receiving write instructiosn
*/
@ -278,9 +270,17 @@ let SessionFileInternal = {
isFinalWrite = this._isClosed = true;
}
return this._latestWrite = Task.spawn(function* task() {
let deferredWritten = Promise.defer();
return Task.spawn(function* task() {
TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
// Ensure that we can write sessionstore.js cleanly before the profile
// becomes unaccessible.
AsyncShutdown.profileBeforeChange.addBlocker(
"SessionFile: Finish writing Session Restore data",
deferredWritten.promise
);
try {
let performShutdownCleanup = isFinalWrite &&
!sessionStartup.isAutomaticRestoreEnabled();
@ -296,14 +296,18 @@ let SessionFileInternal = {
let msg = yield promise;
this._recordTelemetry(msg.telemetry);
if (msg.ok && msg.ok.upgradeBackup) {
if (msg.result.upgradeBackup) {
// We have just completed a backup-on-upgrade, store the information
// in preferences.
Services.prefs.setCharPref(PREF_UPGRADE_BACKUP, Services.appinfo.platformBuildID);
}
deferredWritten.resolve();
} catch (ex) {
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
console.error("Could not write session state file ", ex, ex.stack);
deferredWritten.reject(ex);
} finally {
AsyncShutdown.profileBeforeChange.removeBlocker(deferredWritten.promise);
}
if (isFinalWrite) {
@ -332,11 +336,3 @@ let SessionFileInternal = {
}
}
};
// Ensure that we can write sessionstore.js cleanly before the profile
// becomes unaccessible.
AsyncShutdown.profileBeforeChange.addBlocker(
"SessionFile: Finish writing the latest sessionstore.js",
function() {
return SessionFileInternal._latestWrite;
});

View File

@ -10,48 +10,24 @@
importScripts("resource://gre/modules/osfile.jsm");
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
let File = OS.File;
let Encoder = new TextEncoder();
let Decoder = new TextDecoder();
/**
* Communications with the controller.
*
* Accepts messages:
* {fun:function_name, args:array_of_arguments_or_null, id: custom_id}
*
* Sends messages:
* {ok: result, id: custom_id, telemetry: {}} /
* {fail: serialized_form_of_OS.File.Error, id: custom_id}
*/
self.onmessage = function (msg) {
let data = msg.data;
if (!(data.fun in Agent)) {
throw new Error("Cannot find method " + data.fun);
}
let result;
let id = data.id;
try {
result = Agent[data.fun].apply(Agent, data.args) || {};
} catch (ex if ex instanceof OS.File.Error) {
// Instances of OS.File.Error know how to serialize themselves
// (deserialization ensures that we end up with OS-specific
// instances of |OS.File.Error|)
self.postMessage({fail: OS.File.Error.toMsg(ex), id: id});
return;
}
// Other exceptions do not, and should be propagated through DOM's
// built-in mechanism for uncaught errors, although this mechanism
// may lose interesting information.
self.postMessage({
ok: result.result,
id: id,
telemetry: result.telemetry || {}
});
let worker = new PromiseWorker.AbstractWorker();
worker.dispatch = function(method, args = []) {
return Agent[method](...args);
};
worker.postMessage = function(result, ...transfers) {
self.postMessage(result, ...transfers);
};
worker.close = function() {
self.close();
};
self.addEventListener("message", msg => worker.handleMessage(msg));
// The various possible states

View File

@ -13,31 +13,13 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
this.EXPORTED_SYMBOLS = ["SessionWorker"];
this.SessionWorker = (function () {
let worker = new PromiseWorker("resource:///modules/sessionstore/SessionWorker.js",
OS.Shared.LOG.bind("SessionWorker"));
return {
post: function post(...args) {
let promise = worker.post.apply(worker, args);
return promise.then(
null,
function onError(error) {
// Decode any serialized error
if (error instanceof PromiseWorker.WorkerError) {
throw OS.File.Error.fromMsg(error.data);
}
// Extract something meaningful from ErrorEvent
if (error instanceof ErrorEvent) {
throw new Error(error.message, error.filename, error.lineno);
}
throw error;
}
);
}
};
})();
this.SessionWorker = new BasePromiseWorker("resource:///modules/sessionstore/SessionWorker.js");
// As the Session Worker performs I/O, we can receive instances of
// OS.File.Error, so we need to install a decoder.
this.SessionWorker.ExceptionHandlers["OS.File.Error"] = OS.File.Error.fromMsg;

View File

@ -2153,9 +2153,9 @@ ia64*-hpux*)
# autoconf insists on passing $LDFLAGS to the compiler.
if test -z "$CLANG_CL"; then
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
fi
if test -z "$DEVELOPER_OPTIONS"; then
LDFLAGS="$LDFLAGS -RELEASE"
if test -z "$DEVELOPER_OPTIONS"; then
LDFLAGS="$LDFLAGS -RELEASE"
fi
fi
dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL"

View File

@ -15,8 +15,6 @@ class nsIURI;
{ 0xd753c84a, 0x17fd, 0x4d5f, \
{ 0xb2, 0xe9, 0x63, 0x52, 0x8c, 0x87, 0x99, 0x7a } }
class nsIStyleSheet;
namespace mozilla {
class CSSStyleSheet;
} // namespace mozilla
@ -37,10 +35,9 @@ public:
/**
* Used to obtain the style sheet linked in by this element.
*
* @param aStyleSheet out parameter that returns the style
* sheet associated with this element.
* @return the style sheet associated with this element.
*/
NS_IMETHOD GetStyleSheet(nsIStyleSheet*& aStyleSheet) = 0;
NS_IMETHOD_(mozilla::CSSStyleSheet*) GetStyleSheet() = 0;
/**
* Initialize the stylesheet linking element. If aDontLoadStyle is

View File

@ -77,13 +77,10 @@ nsStyleLinkElement::SetStyleSheet(CSSStyleSheet* aStyleSheet)
return NS_OK;
}
NS_IMETHODIMP
nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet)
NS_IMETHODIMP_(CSSStyleSheet*)
nsStyleLinkElement::GetStyleSheet()
{
aStyleSheet = mStyleSheet;
NS_IF_ADDREF(aStyleSheet);
return NS_OK;
return mStyleSheet;
}
NS_IMETHODIMP

View File

@ -41,7 +41,7 @@ public:
// nsIStyleSheetLinkingElement
NS_IMETHOD SetStyleSheet(mozilla::CSSStyleSheet* aStyleSheet) MOZ_OVERRIDE;
NS_IMETHOD GetStyleSheet(nsIStyleSheet*& aStyleSheet) MOZ_OVERRIDE;
NS_IMETHOD_(mozilla::CSSStyleSheet*) GetStyleSheet() MOZ_OVERRIDE;
NS_IMETHOD InitStyleLinkElement(bool aDontLoadStyle) MOZ_OVERRIDE;
NS_IMETHOD UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
bool* aWillNotify,

View File

@ -3100,7 +3100,7 @@ nsGenericHTMLElement::SetItemValue(JSContext* aCx, JS::Value aValue,
return;
}
binding_detail::FakeDependentString string;
nsDependentString string;
JS::Rooted<JS::Value> value(aCx, aValue);
if (!ConvertJSValueToString(aCx, value, &value, eStringify, eStringify, string)) {
aError.Throw(NS_ERROR_UNEXPECTED);

View File

@ -2628,9 +2628,12 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
}
#ifdef USE_CONTROLLERS_SHIM
// Note: We use |obj| rather than |aWin| to get the principal here, because
// this is called during Window setup when the Document isn't necessarily
// hooked up yet.
if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_CONTROLLERS) &&
!xpc::IsXrayWrapper(obj) &&
!nsContentUtils::IsSystemPrincipal(aWin->GetPrincipal()))
!nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)))
{
if (aWin->GetDoc()) {
aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Controllers);

View File

@ -2124,30 +2124,51 @@ ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
pval.set(JS::StringValue(s)); // Root the new string.
}
size_t length;
const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &length);
if (!chars) {
return false;
}
// Conversion from Javascript string to ByteString is only valid if all
// characters < 256.
for (size_t i = 0; i < length; i++) {
if (chars[i] > 255) {
// characters < 256. This is always the case for Latin1 strings.
size_t length;
if (!JS_StringHasLatin1Chars(s)) {
// ThrowErrorMessage can GC, so we first scan the string for bad chars
// and report the error outside the AutoCheckCannotGC scope.
bool foundBadChar = false;
size_t badCharIndex;
jschar badChar;
{
JS::AutoCheckCannotGC nogc;
const jschar* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
if (!chars) {
return false;
}
for (size_t i = 0; i < length; i++) {
if (chars[i] > 255) {
badCharIndex = i;
badChar = chars[i];
foundBadChar = true;
break;
}
}
}
if (foundBadChar) {
MOZ_ASSERT(badCharIndex < length);
MOZ_ASSERT(badChar > 255);
// The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
// 20 digits, plus one more for the null terminator.
char index[21];
static_assert(sizeof(size_t) <= 8, "index array too small");
PR_snprintf(index, sizeof(index), "%d", i);
PR_snprintf(index, sizeof(index), "%d", badCharIndex);
// A jschar is 16 bits long. The biggest unsigned 16 bit
// number (65,535) has 5 digits, plus one more for the null
// terminator.
char badChar[6];
static_assert(sizeof(jschar) <= 2, "badChar array too small");
PR_snprintf(badChar, sizeof(badChar), "%d", chars[i]);
ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badChar);
char badCharArray[6];
static_assert(sizeof(jschar) <= 2, "badCharArray too small");
PR_snprintf(badCharArray, sizeof(badCharArray), "%d", badChar);
ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
return false;
}
} else {
length = JS_GetStringLength(s);
}
if (length >= UINT32_MAX) {

View File

@ -12,6 +12,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Alignment.h"
#include "mozilla/Array.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CallbackObject.h"
#include "mozilla/dom/DOMJSClass.h"
@ -1055,16 +1056,16 @@ HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
template<bool Fatal>
inline bool
EnumValueNotFound(JSContext* cx, const jschar* chars, size_t length,
const char* type, const char* sourceDescription)
EnumValueNotFound(JSContext* cx, JSString* str, const char* type,
const char* sourceDescription)
{
return false;
}
template<>
inline bool
EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length,
const char* type, const char* sourceDescription)
EnumValueNotFound<false>(JSContext* cx, JSString* str, const char* type,
const char* sourceDescription)
{
// TODO: Log a warning to the console.
return true;
@ -1072,34 +1073,21 @@ EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length,
template<>
inline bool
EnumValueNotFound<true>(JSContext* cx, const jschar* chars, size_t length,
const char* type, const char* sourceDescription)
EnumValueNotFound<true>(JSContext* cx, JSString* str, const char* type,
const char* sourceDescription)
{
NS_LossyConvertUTF16toASCII deflated(static_cast<const char16_t*>(chars),
length);
JSAutoByteString deflated(cx, str);
if (!deflated) {
return false;
}
return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
deflated.get(), type);
deflated.ptr(), type);
}
template<bool InvalidValueFatal>
template<typename CharT>
inline int
FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values,
const char* type, const char* sourceDescription, bool* ok)
FindEnumStringIndexImpl(const CharT* chars, size_t length, const EnumEntry* values)
{
// JS_StringEqualsAscii is slow as molasses, so don't use it here.
JSString* str = JS::ToString(cx, v);
if (!str) {
*ok = false;
return 0;
}
JS::Anchor<JSString*> anchor(str);
size_t length;
const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
if (!chars) {
*ok = false;
return 0;
}
int i = 0;
for (const EnumEntry* value = values; value->value; ++value, ++i) {
if (length != value->length) {
@ -1116,13 +1104,54 @@ FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* val
}
if (equal) {
*ok = true;
return i;
}
}
*ok = EnumValueNotFound<InvalidValueFatal>(cx, chars, length, type,
sourceDescription);
return -1;
}
template<bool InvalidValueFatal>
inline int
FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values,
const char* type, const char* sourceDescription, bool* ok)
{
// JS_StringEqualsAscii is slow as molasses, so don't use it here.
JSString* str = JS::ToString(cx, v);
if (!str) {
*ok = false;
return 0;
}
JS::Anchor<JSString*> anchor(str);
{
int index;
size_t length;
JS::AutoCheckCannotGC nogc;
if (JS_StringHasLatin1Chars(str)) {
const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str,
&length);
if (!chars) {
*ok = false;
return 0;
}
index = FindEnumStringIndexImpl(chars, length, values);
} else {
const jschar* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str,
&length);
if (!chars) {
*ok = false;
return 0;
}
index = FindEnumStringIndexImpl(chars, length, values);
}
if (index >= 0) {
*ok = true;
return index;
}
}
*ok = EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
return -1;
}
@ -1700,8 +1729,8 @@ struct FakeDependentString {
{
}
void SetData(const nsDependentString::char_type* aData,
nsDependentString::size_type aLength) {
void Rebind(const nsDependentString::char_type* aData,
nsDependentString::size_type aLength) {
MOZ_ASSERT(mFlags == nsDependentString::F_TERMINATED);
mData = aData;
mLength = aLength;
@ -1712,7 +1741,9 @@ struct FakeDependentString {
mLength = 0;
}
void SetNull() {
void SetIsVoid(bool aValue) {
MOZ_ASSERT(aValue,
"We don't support SetIsVoid(false) on FakeDependentString!");
Truncate();
mFlags |= nsDependentString::F_VOIDED;
}
@ -1778,12 +1809,13 @@ enum StringificationBehavior {
};
// pval must not be null and must point to a rooted JS::Value
template<typename T>
static inline bool
ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
JS::MutableHandle<JS::Value> pval,
StringificationBehavior nullBehavior,
StringificationBehavior undefinedBehavior,
binding_detail::FakeDependentString& result)
T& result)
{
JSString *s;
if (v.isString()) {
@ -1802,7 +1834,7 @@ ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
if (behavior == eEmpty) {
result.Truncate();
} else {
result.SetNull();
result.SetIsVoid(true);
}
return true;
}
@ -1820,7 +1852,7 @@ ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
return false;
}
result.SetData(chars, len);
result.Rebind(chars, len);
return true;
}

View File

@ -4435,10 +4435,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isinstance(defaultValue, IDLNullValue):
assert(type.nullable())
defaultCode = "%s.SetNull()" % varName
defaultCode = "%s.SetIsVoid(true)" % varName
else:
defaultCode = handleDefaultStringValue(defaultValue,
"%s.SetData" % varName)
"%s.Rebind" % varName)
return handleDefault(conversionCode, defaultCode + ";\n")
if isMember:
@ -8508,7 +8508,7 @@ class CGUnionConversionStruct(CGThing):
[Argument("const nsDependentString::char_type*", "aData"),
Argument("nsDependentString::size_type", "aLength")],
inline=True, bodyInHeader=True,
body="RawSetAs%s().SetData(aData, aLength);\n" % t.name))
body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name))
if vars["holderType"] is not None:
holderType = CGTemplatedType("Maybe",
@ -9375,7 +9375,9 @@ class CGProxyNamedOperation(CGProxySpecialOperation):
unwrapString = fill(
"""
if (MOZ_LIKELY(JSID_IS_ATOM(${idName}))) {
${argName}.SetData(js::GetAtomChars(JSID_TO_ATOM(${idName})), js::GetAtomLength(JSID_TO_ATOM(${idName})));
JS::AutoCheckCannotGC nogc;
JSAtom* atom = JSID_TO_ATOM(${idName});
${argName}.Rebind(js::GetTwoByteAtomChars(nogc, atom), js::GetAtomLength(atom));
} else {
nameVal = js::IdToValue(${idName});
$*{unwrapString}

View File

@ -155,7 +155,15 @@ GetArrayIndexFromId(JSContext* cx, JS::Handle<jsid> id)
}
if (MOZ_LIKELY(JSID_IS_ATOM(id))) {
JSAtom* atom = JSID_TO_ATOM(id);
jschar s = *js::GetAtomChars(atom);
jschar s;
{
JS::AutoCheckCannotGC nogc;
if (js::AtomHasLatin1Chars(atom)) {
s = *js::GetLatin1AtomChars(nogc, atom);
} else {
s = *js::GetTwoByteAtomChars(nogc, atom);
}
}
if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
return -1;

View File

@ -12,6 +12,8 @@
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "XPCWrapper.h"
@ -123,6 +125,18 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
return false;
}
void
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
return;
}
Throw(jsapi.cx(), aRv, aMessage);
(void) JS_ReportPendingException(jsapi.cx());
}
already_AddRefed<Exception>
CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
{

View File

@ -13,6 +13,7 @@
#include "nsIException.h"
class nsIStackFrame;
class nsPIDOMWindow;
template <class T>
struct already_AddRefed;
@ -24,6 +25,11 @@ class Exception;
bool
Throw(JSContext* cx, nsresult rv, const char* sz = nullptr);
// Create, throw and report an exception to a given window.
void
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv,
const char* aMessage = nullptr);
bool
ThrowExceptionObject(JSContext* aCx, Exception* aException);

View File

@ -143,7 +143,7 @@ JSEventHandler::HandleEvent(nsIDOMEvent* aEvent)
ErrorEvent* scriptEvent = aEvent->InternalDOMEvent()->AsErrorEvent();
if (scriptEvent) {
scriptEvent->GetMessage(errorMsg);
msgOrEvent.SetAsString().SetData(errorMsg.Data(), errorMsg.Length());
msgOrEvent.SetAsString().Rebind(errorMsg.Data(), errorMsg.Length());
scriptEvent->GetFilename(file);
fileName = &file;

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ErrorResult.h"
#include "WorkerPrivate.h"
@ -217,16 +218,13 @@ protected:
{
AssertIsOnMainThread();
// Get the JSContext for the target window
nsCOMPtr<nsIScriptGlobalObject> sgo =
do_QueryInterface(static_cast<DOMEventTargetHelper*>
(mBackingStore.get())->GetOwner());
MOZ_ASSERT(sgo);
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
: nsContentUtils::GetSafeJSContext());
MOZ_ASSERT(cx);
// Initialise an AutoJSAPI with the target window.
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) {
mRv.Throw(NS_ERROR_UNEXPECTED);
return true;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {
@ -287,16 +285,13 @@ protected:
{
AssertIsOnMainThread();
// Get the JSContext for the target window
nsCOMPtr<nsIScriptGlobalObject> sgo =
do_QueryInterface(static_cast<DOMEventTargetHelper*>
(mBackingStore.get())->GetOwner());
MOZ_ASSERT(sgo);
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
: nsContentUtils::GetSafeJSContext());
MOZ_ASSERT(cx);
// Initialise an AutoJSAPI with the target window.
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) {
mRv.Throw(NS_ERROR_UNEXPECTED);
return true;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {

View File

@ -34,6 +34,7 @@
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Navigator.h"
@ -2070,14 +2071,11 @@ RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow)
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
return;
}
JSContext* cx = jsapi.cx();
for (uint32_t index = 0; index < workers.Length(); index++) {
WorkerPrivate*& worker = workers[index];
@ -2101,14 +2099,11 @@ RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
return;
}
JSContext* cx = jsapi.cx();
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->Suspend(cx, aWindow)) {
@ -2128,17 +2123,14 @@ RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
return;
}
JSContext* cx = jsapi.cx();
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) {
if (!workers[index]->SynchronizeAndResume(cx, aWindow)) {
JS_ReportPendingException(cx);
}
}

View File

@ -47,6 +47,7 @@
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/MessagePortList.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/Preferences.h"
#include "nsAlgorithm.h"
@ -218,17 +219,10 @@ private:
struct WindowAction
{
nsPIDOMWindow* mWindow;
JSContext* mJSContext;
bool mDefaultAction;
WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext)
: mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true)
{
MOZ_ASSERT(aJSContext);
}
WindowAction(nsPIDOMWindow* aWindow)
: mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true)
: mWindow(aWindow), mDefaultAction(true)
{ }
bool
@ -1843,14 +1837,11 @@ class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIScriptContext> mScriptContext;
public:
SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate,
nsPIDOMWindow* aWindow,
nsIScriptContext* aScriptContext)
: mWorkerPrivate(aWorkerPrivate), mWindow(aWindow),
mScriptContext(aScriptContext)
nsPIDOMWindow* aWindow)
: mWorkerPrivate(aWorkerPrivate), mWindow(aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
@ -1868,9 +1859,11 @@ private:
AssertIsOnMainThread();
if (mWorkerPrivate) {
AutoPushJSContext cx(mScriptContext ?
mScriptContext->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(mWindow))) {
return NS_OK;
}
JSContext* cx = jsapi.cx();
if (!mWorkerPrivate->Resume(cx, mWindow)) {
JS_ReportPendingException(cx);
@ -1889,7 +1882,6 @@ private:
mWorkerPrivate = nullptr;
mWindow = nullptr;
mScriptContext = nullptr;
}
};
@ -2609,8 +2601,7 @@ template <class Derived>
bool
WorkerPrivateParent<Derived>::SynchronizeAndResume(
JSContext* aCx,
nsPIDOMWindow* aWindow,
nsIScriptContext* aScriptContext)
nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(!GetParent());
@ -2623,8 +2614,7 @@ WorkerPrivateParent<Derived>::SynchronizeAndResume(
// the messages.
nsRefPtr<SynchronizeAndResumeRunnable> runnable =
new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow,
aScriptContext);
new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow);
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
JS_ReportError(aCx, "Failed to dispatch to current thread!");
return false;
@ -2824,16 +2814,11 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
return true;
}
nsCOMPtr<nsIScriptGlobalObject> sgo;
port->GetParentObject(getter_AddRefs(sgo));
MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!");
MOZ_ASSERT(sgo->GetGlobalJSObject());
nsCOMPtr<nsIScriptContext> scx = sgo->GetContext();
MOZ_ASSERT_IF(scx, scx->GetNativeContext());
AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx);
JSAutoCompartment(cx, sgo->GetGlobalJSObject());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
return false;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> data(cx);
if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
@ -3149,14 +3134,6 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
// May be null.
nsPIDOMWindow* window = sharedWorker->GetOwner();
size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(sharedWorker->GetOwner()))) {
continue;
}
JSContext* cx = jsapi.cx();
RootedDictionary<ErrorEventInit> errorInit(aCx);
errorInit.mBubbles = false;
errorInit.mCancelable = true;
@ -3169,8 +3146,7 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
errorInit);
if (!errorEvent) {
Throw(cx, NS_ERROR_UNEXPECTED);
JS_ReportPendingException(cx);
ThrowAndReport(window, NS_ERROR_UNEXPECTED);
continue;
}
@ -3179,8 +3155,7 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
bool defaultActionEnabled;
nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled);
if (NS_FAILED(rv)) {
Throw(cx, rv);
JS_ReportPendingException(cx);
ThrowAndReport(window, rv);
continue;
}
@ -3188,12 +3163,15 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
// Add the owning window to our list so that we will fire an error event
// at it later.
if (!windowActions.Contains(window)) {
windowActions.AppendElement(WindowAction(window, cx));
windowActions.AppendElement(WindowAction(window));
}
} else {
size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
if (actionsIndex != windowActions.NoIndex) {
// Any listener that calls preventDefault() will prevent the window from
// receiving the error event.
windowActions[actionsIndex].mDefaultAction = false;
}
} else if (actionsIndex != windowActions.NoIndex) {
// Any listener that calls preventDefault() will prevent the window from
// receiving the error event.
windowActions[actionsIndex].mDefaultAction = false;
}
}
@ -3214,10 +3192,6 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
continue;
}
JSContext* cx = windowAction.mJSContext;
AutoCxPusher autoPush(cx);
nsCOMPtr<nsIScriptGlobalObject> sgo =
do_QueryInterface(windowAction.mWindow);
MOZ_ASSERT(sgo);
@ -3233,8 +3207,7 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
nsEventStatus status = nsEventStatus_eIgnore;
rv = sgo->HandleScriptError(init, &status);
if (NS_FAILED(rv)) {
Throw(cx, rv);
JS_ReportPendingException(cx);
ThrowAndReport(windowAction.mWindow, rv);
continue;
}

View File

@ -339,6 +339,9 @@ public:
return Notify(aCx, Killing);
}
// We can assume that an nsPIDOMWindow will be available for Suspend, Resume
// and SynchronizeAndResume as these are only used for globals going in and
// out of the bfcache.
bool
Suspend(JSContext* aCx, nsPIDOMWindow* aWindow);
@ -346,8 +349,7 @@ public:
Resume(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow,
nsIScriptContext* aScriptContext);
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
Terminate(JSContext* aCx)

View File

@ -817,7 +817,7 @@ function ArrayReducePar(func, mode) {
var numSlices = slicesInfo.count;
var subreductions = NewDenseArray(numSlices);
ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode), null);
ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode), subreductions);
var accumulator = subreductions[0];
for (var i = 1; i < numSlices; i++)

View File

@ -419,6 +419,45 @@ function rpowhalf_object(i) {
return i;
}
var uceFault_min_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_number'));
function rmin_number(i) {
var x = Math.min(i, i-1, i-2.1);
if (uceFault_min_number(i) || uceFault_min_number(i))
assertEq(x, i-2.1);
return i;
}
var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object'));
function rmin_object(i) {
var t = i;
var o = { valueOf: function () { return t; } };
var x = Math.min(o, o-1, o-2.1)
t = 1000;
if (uceFault_min_object(i) || uceFault_min_object(i))
assertEq(x, i-2.1);
return i;
}
var uceFault_max_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_number'));
function rmax_number(i) {
var x = Math.max(i, i-1, i-2.1);
if (uceFault_max_number(i) || uceFault_max_number(i))
assertEq(x, i);
return i;
}
var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object'));
function rmax_object(i) {
var t = i;
var o = { valueOf: function () { return t; } };
var x = Math.max(o, o-1, o-2.1)
t = 1000;
if (uceFault_max_object(i) || uceFault_max_object(i))
assertEq(x, i);
return i;
}
for (i = 0; i < 100; i++) {
rbitnot_number(i);
rbitnot_object(i);
@ -464,6 +503,10 @@ for (i = 0; i < 100; i++) {
rpow_object(i);
rpowhalf_number(i);
rpowhalf_object(i);
rmin_number(i);
rmin_object(i);
rmax_number(i);
rmax_object(i);
}
// Test that we can refer multiple time to the same recover instruction, as well

View File

@ -0,0 +1,21 @@
// Failed to pass the subreductions as an assignment root to Array.prototype.reducePar,
// fuzzing test case discovered separately.
if (!getBuildConfiguration().parallelJS)
quit(0);
x = [];
y = x.mapPar(function() {});
Object.defineProperty(y, 3, {
get: (function( ... $8) {
try {
Int8Array(y);
x.reducePar(function() function q() 1);
} catch (e) {}
})
});
var x = [1,2,3];
function rsh() {
return y.toSource() >> 2.0;
}
rsh();

View File

@ -0,0 +1,13 @@
// Failed to pass the subreductions as an assignment root to Array.prototype.reducePar,
// fuzzing test case.
if (!getBuildConfiguration().parallelJS)
quit(0);
x = []
x[8] = ((function() {})());
for each(let a in [0, 0]) {
x.reducePar(function() {
return [0];
});
}

View File

@ -7769,8 +7769,9 @@ CodeGenerator::emitLoadElementT(LLoadElementT *lir, const T &source)
{
if (LIRGenerator::allowTypedElementHoleCheck()) {
if (lir->mir()->needsHoleCheck()) {
Assembler::Condition cond = masm.testMagic(Assembler::Equal, source);
if (!bailoutIf(cond, lir->snapshot()))
Label bail;
masm.branchTestMagic(Assembler::Equal, source, &bail);
if (!bailoutFrom(&bail, lir->snapshot()))
return false;
}
} else {

View File

@ -3936,6 +3936,10 @@ class MMinMax
return AliasSet::None();
}
void computeRange(TempAllocator &alloc);
bool writeRecoverData(CompactBufferWriter &writer) const;
bool canRecoverOnBailout() const {
return true;
}
};
class MAbs

View File

@ -710,6 +710,34 @@ RPowHalf::recover(JSContext *cx, SnapshotIterator &iter) const
return true;
}
bool
MMinMax::writeRecoverData(CompactBufferWriter &writer) const
{
MOZ_ASSERT(canRecoverOnBailout());
writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax));
writer.writeByte(isMax_);
return true;
}
RMinMax::RMinMax(CompactBufferReader &reader)
{
isMax_ = reader.readByte();
}
bool
RMinMax::recover(JSContext *cx, SnapshotIterator &iter) const
{
RootedValue a(cx, iter.read());
RootedValue b(cx, iter.read());
RootedValue result(cx);
if (!js_minmax_impl(cx, isMax_, a, b, &result))
return false;
iter.storeInstructionResult(result);
return true;
}
bool
MMathFunction::writeRecoverData(CompactBufferWriter &writer) const
{

View File

@ -39,6 +39,7 @@ namespace jit {
_(FromCharCode) \
_(Pow) \
_(PowHalf) \
_(MinMax) \
_(NewObject) \
_(NewDerivedTypedObject)
@ -372,6 +373,21 @@ class RPowHalf MOZ_FINAL : public RInstruction
bool recover(JSContext *cx, SnapshotIterator &iter) const;
};
class RMinMax MOZ_FINAL : public RInstruction
{
private:
bool isMax_;
public:
RINSTRUCTION_HEADER_(MinMax)
virtual uint32_t numOperands() const {
return 2;
}
bool recover(JSContext *cx, SnapshotIterator &iter) const;
};
class RNewObject MOZ_FINAL : public RInstruction
{
private:

View File

@ -793,18 +793,19 @@ CodeGeneratorMIPS::visitModMaskI(LModMaskI *ins)
{
Register src = ToRegister(ins->getOperand(0));
Register dest = ToRegister(ins->getDef(0));
Register tmp = ToRegister(ins->getTemp(0));
Register tmp0 = ToRegister(ins->getTemp(0));
Register tmp1 = ToRegister(ins->getTemp(1));
MMod *mir = ins->mir();
if (!mir->isTruncated() && mir->canBeNegativeDividend()) {
MOZ_ASSERT(mir->fallible());
Label bail;
masm.ma_mod_mask(src, dest, tmp, ins->shift(), &bail);
masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), &bail);
if (!bailoutFrom(&bail, ins->snapshot()))
return false;
} else {
masm.ma_mod_mask(src, dest, tmp, ins->shift(), nullptr);
masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), nullptr);
}
return true;
}

View File

@ -208,18 +208,20 @@ class LModPowTwoI : public LInstructionHelper<1, 1, 0>
}
};
class LModMaskI : public LInstructionHelper<1, 1, 1>
class LModMaskI : public LInstructionHelper<1, 1, 2>
{
const int32_t shift_;
public:
LIR_HEADER(ModMaskI);
LModMaskI(const LAllocation &lhs, const LDefinition &temp1, int32_t shift)
LModMaskI(const LAllocation &lhs, const LDefinition &temp0, const LDefinition &temp1,
int32_t shift)
: shift_(shift)
{
setOperand(0, lhs);
setTemp(0, temp1);
setTemp(0, temp0);
setTemp(1, temp1);
}
int32_t shift() const {

View File

@ -322,7 +322,9 @@ LIRGeneratorMIPS::lowerModI(MMod *mod)
return define(lir, mod);
} else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) {
LModMaskI *lir = new(alloc()) LModMaskI(useRegister(mod->lhs()),
temp(LDefinition::GENERAL), shift + 1);
temp(LDefinition::GENERAL),
temp(LDefinition::GENERAL),
shift + 1);
if (mod->fallible() && !assignSnapshot(lir, Bailout_DoubleOutput))
return false;
return define(lir, mod);

View File

@ -620,8 +620,8 @@ MacroAssemblerMIPS::ma_div_branch_overflow(Register rd, Register rs, Imm32 imm,
}
void
MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, int32_t shift,
Label *negZero)
MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, Register remain,
int32_t shift, Label *negZero)
{
// MATH:
// We wish to compute x % (1<<y) - 1 for a known constant, y.
@ -641,34 +641,32 @@ MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, int3
Label head, negative, sumSigned, done;
// hold holds -1 if the value was negative, 1 otherwise.
// ScratchRegister holds the remaining bits that have not been processed
// lr serves as a temporary location to store extracted bits into as well
// as holding the trial subtraction as a temp value dest is the
// accumulator (and holds the final result)
// remain holds the remaining bits that have not been processed
// SecondScratchReg serves as a temporary location to store extracted bits
// into as well as holding the trial subtraction as a temp value dest is
// the accumulator (and holds the final result)
// move the whole value into the scratch register, setting the codition
// codes so we can muck with them later.
ma_move(ScratchRegister, src);
// move the whole value into the remain.
ma_move(remain, src);
// Zero out the dest.
ma_subu(dest, dest, dest);
ma_li(dest, Imm32(0));
// Set the hold appropriately.
ma_b(ScratchRegister, ScratchRegister, &negative, Signed, ShortJump);
ma_b(remain, remain, &negative, Signed, ShortJump);
ma_li(hold, Imm32(1));
ma_b(&head, ShortJump);
bind(&negative);
ma_li(hold, Imm32(-1));
ma_negu(ScratchRegister, ScratchRegister);
ma_negu(remain, remain);
// Begin the main loop.
bind(&head);
// Extract the bottom bits into lr.
ma_and(SecondScratchReg, ScratchRegister, Imm32(mask));
// Extract the bottom bits into SecondScratchReg.
ma_and(SecondScratchReg, remain, Imm32(mask));
// Add those bits to the accumulator.
as_addu(dest, dest, SecondScratchReg);
// Do a trial subtraction, this is the same operation as cmp, but we
// store the dest
// Do a trial subtraction
ma_subu(SecondScratchReg, dest, Imm32(mask));
// If (sum - C) > 0, store sum - C back into sum, thus performing a
// modulus.
@ -676,9 +674,9 @@ MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, int3
ma_move(dest, SecondScratchReg);
bind(&sumSigned);
// Get rid of the bits that we extracted before.
as_srl(ScratchRegister, ScratchRegister, shift);
as_srl(remain, remain, shift);
// If the shift produced zero, finish, otherwise, continue in the loop.
ma_b(ScratchRegister, ScratchRegister, &head, NonZero, ShortJump);
ma_b(remain, remain, &head, NonZero, ShortJump);
// Check the hold to see if we need to negate the result.
ma_b(hold, hold, &done, NotSigned, ShortJump);
@ -1674,6 +1672,12 @@ MacroAssemblerMIPSCompat::not32(Register reg)
}
// Logical operations
void
MacroAssemblerMIPSCompat::and32(Register src, Register dest)
{
ma_and(dest, dest, src);
}
void
MacroAssemblerMIPSCompat::and32(Imm32 imm, Register dest)
{
@ -1688,6 +1692,13 @@ MacroAssemblerMIPSCompat::and32(Imm32 imm, const Address &dest)
store32(SecondScratchReg, dest);
}
void
MacroAssemblerMIPSCompat::and32(const Address &src, Register dest)
{
load32(src, SecondScratchReg);
ma_and(dest, SecondScratchReg);
}
void
MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address &dest)
{
@ -2024,6 +2035,12 @@ MacroAssemblerMIPSCompat::storePtr(Register src, const Address &address)
ma_sw(src, address);
}
void
MacroAssemblerMIPSCompat::storePtr(Register src, const BaseIndex &address)
{
ma_store(src, address, SizeWord);
}
void
MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
{
@ -2082,6 +2099,13 @@ MacroAssemblerMIPSCompat::subPtr(Imm32 imm, const Register dest)
ma_subu(dest, dest, imm);
}
void
MacroAssemblerMIPSCompat::subPtr(const Address &addr, const Register dest)
{
loadPtr(addr, SecondScratchReg);
subPtr(SecondScratchReg, dest);
}
void
MacroAssemblerMIPSCompat::subPtr(Register src, const Address &dest)
{
@ -2713,6 +2737,35 @@ MacroAssemblerMIPSCompat::getType(const Value &val)
return jv.s.tag;
}
template <typename T>
void
MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest,
MIRType slotType)
{
if (valueType == MIRType_Double) {
storeDouble(value.reg().typedReg().fpu(), dest);
return;
}
// Store the type tag if needed.
if (valueType != slotType)
storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
// Store the payload.
if (value.constant())
storePayload(value.value(), dest);
else
storePayload(value.reg().typedReg().gpr(), dest);
}
template void
MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address &dest,
MIRType slotType);
template void
MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex &dest,
MIRType slotType);
void
MacroAssemblerMIPSCompat::moveData(const Value &val, Register data)
{
@ -2904,10 +2957,11 @@ MacroAssemblerMIPSCompat::storePayload(Register src, Address dest)
}
void
MacroAssemblerMIPSCompat::storePayload(const Value &val, Register base, Register index,
int32_t shift)
MacroAssemblerMIPSCompat::storePayload(const Value &val, const BaseIndex &dest)
{
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg);
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
moveData(val, ScratchRegister);
@ -2915,9 +2969,11 @@ MacroAssemblerMIPSCompat::storePayload(const Value &val, Register base, Register
}
void
MacroAssemblerMIPSCompat::storePayload(Register src, Register base, Register index, int32_t shift)
MacroAssemblerMIPSCompat::storePayload(Register src, const BaseIndex &dest)
{
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg);
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET);
}
@ -2929,9 +2985,11 @@ MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest)
}
void
MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Register base, Register index, int32_t shift)
MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, const BaseIndex &dest)
{
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg);
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
ma_li(ScratchRegister, tag);
as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET);
}

View File

@ -200,8 +200,8 @@ class MacroAssemblerMIPS : public Assembler
// fast mod, uses scratch registers, and thus needs to be in the assembler
// implicitly assumes that we can overwrite dest at the beginning of the sequence
void ma_mod_mask(Register src, Register dest, Register hold, int32_t shift,
Label *negZero = nullptr);
void ma_mod_mask(Register src, Register dest, Register hold, Register remain,
int32_t shift, Label *negZero = nullptr);
// memory
// shortcut for when we know we're transferring 32 bits of data
@ -741,6 +741,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
ma_lw(SecondScratchReg, address);
branchTest32(cond, SecondScratchReg, imm, label);
}
void branchTest32(Condition cond, AbsoluteAddress address, Imm32 imm, Label *label) {
loadPtr(address, ScratchRegister);
branchTest32(cond, ScratchRegister, imm, label);
}
void branchTestPtr(Condition cond, Register lhs, Register rhs, Label *label) {
branchTest32(cond, lhs, rhs, label);
}
@ -847,6 +851,10 @@ public:
load32(address, dest.gpr());
}
template <typename T>
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T &dest,
MIRType slotType);
void moveValue(const Value &val, const ValueOperand &dest);
void moveValue(const ValueOperand &src, const ValueOperand &dest) {
@ -916,10 +924,10 @@ public:
}
void storePayload(const Value &val, Address dest);
void storePayload(Register src, Address dest);
void storePayload(const Value &val, Register base, Register index, int32_t shift = defaultShift);
void storePayload(Register src, Register base, Register index, int32_t shift = defaultShift);
void storePayload(const Value &val, const BaseIndex &dest);
void storePayload(Register src, const BaseIndex &dest);
void storeTypeTag(ImmTag tag, Address dest);
void storeTypeTag(ImmTag tag, Register base, Register index, int32_t shift = defaultShift);
void storeTypeTag(ImmTag tag, const BaseIndex &dest);
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
ma_sll(frameSizeReg, frameSizeReg, Imm32(FRAMESIZE_SHIFT));
@ -1035,8 +1043,10 @@ public:
}
}
void and32(Register src, Register dest);
void and32(Imm32 imm, Register dest);
void and32(Imm32 imm, const Address &dest);
void and32(const Address &src, Register dest);
void or32(Imm32 imm, const Address &dest);
void xor32(Imm32 imm, Register dest);
void xorPtr(Imm32 imm, Register dest);
@ -1112,6 +1122,7 @@ public:
void storePtr(ImmPtr imm, const Address &address);
void storePtr(ImmGCPtr imm, const Address &address);
void storePtr(Register src, const Address &address);
void storePtr(Register src, const BaseIndex &address);
void storePtr(Register src, AbsoluteAddress dest);
void storeDouble(FloatRegister src, Address addr) {
ma_sd(src, addr);
@ -1161,6 +1172,7 @@ public:
}
void subPtr(Imm32 imm, const Register dest);
void subPtr(const Address &addr, const Register dest);
void subPtr(Register src, const Address &dest);
void addPtr(Imm32 imm, const Register dest);
void addPtr(Imm32 imm, const Address &dest);
@ -1190,6 +1202,9 @@ public:
void rshiftPtr(Imm32 imm, Register dest) {
ma_srl(dest, dest, imm);
}
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
ma_sra(dest, dest, imm);
}
void lshiftPtr(Imm32 imm, Register dest) {
ma_sll(dest, dest, imm);
}

View File

@ -5321,6 +5321,12 @@ JS_GetStringLength(JSString *str)
return str->length();
}
JS_PUBLIC_API(bool)
JS_StringHasLatin1Chars(JSString *str)
{
return str->hasLatin1Chars();
}
JS_PUBLIC_API(const jschar *)
JS_GetStringCharsZ(JSContext *cx, JSString *str)
{
@ -5359,6 +5365,36 @@ JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength)
return linear->chars();
}
JS_PUBLIC_API(const JS::Latin1Char *)
JS_GetLatin1StringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
size_t *plength)
{
JS_ASSERT(plength);
AssertHeapIsIdleOrStringIsFlat(cx, str);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
JSLinearString *linear = str->ensureLinear(cx);
if (!linear)
return nullptr;
*plength = linear->length();
return linear->latin1Chars(nogc);
}
JS_PUBLIC_API(const jschar *)
JS_GetTwoByteStringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
size_t *plength)
{
JS_ASSERT(plength);
AssertHeapIsIdleOrStringIsFlat(cx, str);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
JSLinearString *linear = str->ensureLinear(cx);
if (!linear)
return nullptr;
*plength = linear->length();
return linear->twoByteChars(nogc);
}
JS_PUBLIC_API(const jschar *)
JS_GetInternedStringChars(JSString *str)
{
@ -5544,8 +5580,7 @@ JS_GetStringEncodingLength(JSContext *cx, JSString *str)
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
const jschar *chars = str->getChars(cx);
if (!chars)
if (!str->ensureLinear(cx))
return size_t(-1);
return str->length();
}
@ -5562,10 +5597,21 @@ JS_EncodeStringToBuffer(JSContext *cx, JSString *str, char *buffer, size_t lengt
* error.
*/
size_t writtenLength = length;
const jschar *chars = str->getChars(nullptr);
if (!chars)
return size_t(-1);
if (DeflateStringToBuffer(nullptr, chars, str->length(), buffer, &writtenLength)) {
JSLinearString *linear = str->ensureLinear(cx);
if (!linear)
return size_t(-1);
bool res;
if (linear->hasLatin1Chars()) {
JS::AutoCheckCannotGC nogc;
res = DeflateStringToBuffer(nullptr, linear->latin1Chars(nogc), linear->length(), buffer,
&writtenLength);
} else {
JS::AutoCheckCannotGC nogc;
res = DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), linear->length(), buffer,
&writtenLength);
}
if (res) {
JS_ASSERT(writtenLength <= length);
return writtenLength;
}
@ -5624,12 +5670,12 @@ JS_Stringify(JSContext *cx, MutableHandleValue vp, HandleObject replacer,
CHECK_REQUEST(cx);
assertSameCompartment(cx, replacer, space);
StringBuffer sb(cx);
if (!sb.ensureTwoByteChars())
return false;
if (!js_Stringify(cx, vp, replacer, space, sb))
return false;
if (sb.empty()) {
HandlePropertyName null = cx->names().null;
return callback(null->chars(), null->length(), data);
}
if (sb.empty() && !sb.append(cx->names().null))
return false;
return callback(sb.rawTwoByteBegin(), sb.length(), data);
}

View File

@ -4166,9 +4166,21 @@ JS_FileEscapedString(FILE *fp, JSString *str, char quote);
extern JS_PUBLIC_API(size_t)
JS_GetStringLength(JSString *str);
/* Returns true iff the string's characters are stored as Latin1. */
extern JS_PUBLIC_API(bool)
JS_StringHasLatin1Chars(JSString *str);
extern JS_PUBLIC_API(const jschar *)
JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *length);
extern JS_PUBLIC_API(const JS::Latin1Char *)
JS_GetLatin1StringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
size_t *length);
extern JS_PUBLIC_API(const jschar *)
JS_GetTwoByteStringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
size_t *length);
extern JS_PUBLIC_API(const jschar *)
JS_GetInternedStringChars(JSString *str);

View File

@ -761,25 +761,42 @@ GetObjectSlot(JSObject *obj, size_t slot)
return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
}
inline const jschar *
GetAtomChars(JSAtom *atom)
{
using shadow::Atom;
Atom *atom_ = reinterpret_cast<Atom *>(atom);
JS_ASSERT(!(atom_->flags & Atom::LATIN1_CHARS_BIT));
if (atom_->flags & Atom::INLINE_CHARS_BIT) {
char *p = reinterpret_cast<char *>(atom);
return reinterpret_cast<const jschar *>(p + offsetof(Atom, inlineStorageTwoByte));
}
return atom_->nonInlineCharsTwoByte;
}
inline size_t
GetAtomLength(JSAtom *atom)
{
return reinterpret_cast<shadow::Atom*>(atom)->length;
}
inline bool
AtomHasLatin1Chars(JSAtom *atom)
{
return reinterpret_cast<shadow::Atom *>(atom)->flags & shadow::Atom::LATIN1_CHARS_BIT;
}
inline const JS::Latin1Char *
GetLatin1AtomChars(const JS::AutoCheckCannotGC &nogc, JSAtom *atom)
{
MOZ_ASSERT(AtomHasLatin1Chars(atom));
using shadow::Atom;
Atom *atom_ = reinterpret_cast<Atom *>(atom);
if (atom_->flags & Atom::INLINE_CHARS_BIT)
return atom_->inlineStorageLatin1;
return atom_->nonInlineCharsLatin1;
}
inline const jschar *
GetTwoByteAtomChars(const JS::AutoCheckCannotGC &nogc, JSAtom *atom)
{
MOZ_ASSERT(!AtomHasLatin1Chars(atom));
using shadow::Atom;
Atom *atom_ = reinterpret_cast<Atom *>(atom);
if (atom_->flags & Atom::INLINE_CHARS_BIT)
return atom_->inlineStorageTwoByte;
return atom_->nonInlineCharsTwoByte;
}
inline JSLinearString *
AtomToLinearString(JSAtom *atom)
{

View File

@ -552,6 +552,15 @@ js::math_log(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static double
max_double(double x, double y)
{
// Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
return x;
return y;
}
bool
js_math_max(JSContext *cx, unsigned argc, Value *vp)
{
@ -562,14 +571,21 @@ js_math_max(JSContext *cx, unsigned argc, Value *vp)
double x;
if (!ToNumber(cx, args[i], &x))
return false;
// Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
if (x > maxval || IsNaN(x) || (x == maxval && IsNegative(maxval)))
maxval = x;
maxval = max_double(x, maxval);
}
args.rval().setNumber(maxval);
return true;
}
static double
min_double(double x, double y)
{
// Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
return x;
return y;
}
bool
js_math_min(JSContext *cx, unsigned argc, Value *vp)
{
@ -580,14 +596,30 @@ js_math_min(JSContext *cx, unsigned argc, Value *vp)
double x;
if (!ToNumber(cx, args[i], &x))
return false;
// Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
if (x < minval || IsNaN(x) || (x == minval && IsNegativeZero(x)))
minval = x;
minval = min_double(x, minval);
}
args.rval().setNumber(minval);
return true;
}
bool
js_minmax_impl(JSContext *cx, bool max, HandleValue a, HandleValue b, MutableHandleValue res)
{
double x, y;
if (!ToNumber(cx, a, &x))
return false;
if (!ToNumber(cx, b, &y))
return false;
if (max)
res.setNumber(max_double(x, y));
else
res.setNumber(min_double(x, y));
return true;
}
// Disable PGO for Math.pow() and related functions (see bug 791214).
#if defined(_MSC_VER)
# pragma optimize("g", off)

View File

@ -111,11 +111,16 @@ extern bool
js_math_sqrt(JSContext *cx, unsigned argc, js::Value *vp);
extern bool
js_math_pow_handle(JSContext *cx, js::HandleValue base, js::HandleValue power, js::MutableHandleValue result);
js_math_pow_handle(JSContext *cx, js::HandleValue base, js::HandleValue power,
js::MutableHandleValue result);
extern bool
js_math_pow(JSContext *cx, unsigned argc, js::Value *vp);
extern bool
js_minmax_impl(JSContext *cx, bool max, js::HandleValue a, js::HandleValue b,
js::MutableHandleValue res);
namespace js {
extern bool

View File

@ -38,9 +38,13 @@ js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id)
JSMSG_OBJECT_ACCESS_DENIED);
} else {
JSString *str = IdToString(cx, id);
const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
JSMSG_PROPERTY_ACCESS_DENIED, prop);
AutoStableStringChars chars(cx);
const jschar *prop = nullptr;
if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
prop = chars.twoByteChars();
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
prop);
}
}

View File

@ -4888,14 +4888,15 @@ js::InflateString(ThreadSafeContext *cx, const char *bytes, size_t *lengthp)
return nullptr;
}
template <typename CharT>
bool
js::DeflateStringToBuffer(JSContext *maybecx, const jschar *src, size_t srclen,
js::DeflateStringToBuffer(JSContext *maybecx, const CharT *src, size_t srclen,
char *dst, size_t *dstlenp)
{
size_t dstlen = *dstlenp;
if (srclen > dstlen) {
for (size_t i = 0; i < dstlen; i++)
dst[i] = (char) src[i];
dst[i] = char(src[i]);
if (maybecx) {
AutoSuppressGC suppress(maybecx);
JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr,
@ -4904,11 +4905,19 @@ js::DeflateStringToBuffer(JSContext *maybecx, const jschar *src, size_t srclen,
return false;
}
for (size_t i = 0; i < srclen; i++)
dst[i] = (char) src[i];
dst[i] = char(src[i]);
*dstlenp = srclen;
return true;
}
template bool
js::DeflateStringToBuffer(JSContext *maybecx, const Latin1Char *src, size_t srclen,
char *dst, size_t *dstlenp);
template bool
js::DeflateStringToBuffer(JSContext *maybecx, const jschar *src, size_t srclen,
char *dst, size_t *dstlenp);
#define ____ false
/*

View File

@ -319,8 +319,9 @@ CopyAndInflateChars(jschar *dst, const JS::Latin1Char *src, size_t srclen)
* must to be initialized with the buffer size and will contain on return the
* number of copied bytes.
*/
template <typename CharT>
extern bool
DeflateStringToBuffer(JSContext *maybecx, const jschar *chars,
DeflateStringToBuffer(JSContext *maybecx, const CharT *chars,
size_t charsLength, char *bytes, size_t *length);
/*

View File

@ -44,7 +44,9 @@ JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
// JSExtensibleString: count the full capacity, not just the used space.
if (isExtensible()) {
JSExtensibleString &extensible = asExtensible();
return mallocSizeOf(extensible.nonInlineChars());
return extensible.hasLatin1Chars()
? mallocSizeOf(extensible.rawLatin1Chars())
: mallocSizeOf(extensible.rawTwoByteChars());
}
// JSExternalString: don't count, the chars could be stored anywhere.
@ -59,7 +61,9 @@ JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
// JSUndependedString, there is no need to count the base string, for the
// same reason as JSDependentString above.
JSFlatString &flat = asFlat();
return mallocSizeOf(flat.chars());
return flat.hasLatin1Chars()
? mallocSizeOf(flat.rawLatin1Chars())
: mallocSizeOf(flat.rawTwoByteChars());
}
#ifdef DEBUG

View File

@ -39,7 +39,7 @@ function boom_391747() {
SimpleTest.finish();
}
setTimeout(boom_391747,400)
addLoadEvent(boom_391747);
SimpleTest.waitForExplicitFinish()
</script>

View File

@ -543,13 +543,12 @@ nsMediaList::Matches(nsPresContext* aPresContext,
return mArray.IsEmpty();
}
nsresult
void
nsMediaList::SetStyleSheet(CSSStyleSheet* aSheet)
{
NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
"multiple style sheets competing for one media list");
mStyleSheet = aSheet;
return NS_OK;
}
already_AddRefed<nsMediaList>

View File

@ -560,9 +560,7 @@ RuleHash::~RuleHash()
RuleValue* value = &(mUniversalRules[i]);
nsAutoString selectorText;
uint32_t lineNumber = value->mRule->GetLineNumber();
nsCOMPtr<nsIStyleSheet> sheet;
value->mRule->GetStyleSheet(*getter_AddRefs(sheet));
nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(sheet);
nsRefPtr<CSSStyleSheet> cssSheet = value->mRule->GetStyleSheet();
value->mSelector->ToString(selectorText, cssSheet);
printf(" line %d, %s\n",

View File

@ -176,7 +176,7 @@ public:
bool Matches(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey* aKey);
nsresult SetStyleSheet(mozilla::CSSStyleSheet* aSheet);
void SetStyleSheet(mozilla::CSSStyleSheet* aSheet);
void AppendQuery(nsAutoPtr<nsMediaQuery>& aQuery) {
// Takes ownership of aQuery
mArray.AppendElement(aQuery.forget());

View File

@ -274,17 +274,35 @@ void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags
Callback callback(this, aCallback, readonly, multithread);
if (!Open(callback, truncate, priority, bypassIfBusy)) {
// We get here when the callback wants to bypass cache when it's busy.
LOG((" writing or revalidating, callback wants to bypass cache"));
callback.mNotWanted = true;
InvokeAvailableCallback(callback);
}
}
bool CacheEntry::Open(Callback & aCallback, bool aTruncate,
bool aPriority, bool aBypassIfBusy)
{
mozilla::MutexAutoLock lock(mLock);
RememberCallback(callback, bypassIfBusy);
// Check state under the lock
if (aBypassIfBusy && (mState == WRITING || mState == REVALIDATING)) {
return false;
}
RememberCallback(aCallback);
// Load() opens the lock
if (Load(truncate, priority)) {
if (Load(aTruncate, aPriority)) {
// Loading is in progress...
return;
return true;
}
InvokeCallbacks();
return true;
}
bool CacheEntry::Load(bool aTruncate, bool aPriority)
@ -508,20 +526,13 @@ void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
}
}
void CacheEntry::RememberCallback(Callback & aCallback, bool aBypassIfBusy)
void CacheEntry::RememberCallback(Callback & aCallback)
{
mLock.AssertCurrentThreadOwns();
LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
this, aCallback.mCallback.get(), StateString(mState)));
if (aBypassIfBusy && (mState == WRITING || mState == REVALIDATING)) {
LOG((" writing or revalidating, callback wants to bypass cache"));
aCallback.mNotWanted = true;
InvokeAvailableCallback(aCallback);
return;
}
mCallbacks.AppendElement(aCallback);
}

View File

@ -204,11 +204,14 @@ private:
nsresult mRv;
};
// Starts the load or just invokes the callback, bypasses (when required)
// if busy. Returns true on job done, false on bypass.
bool Open(Callback & aCallback, bool aTruncate, bool aPriority, bool aBypassIfBusy);
// Loads from disk asynchronously
bool Load(bool aTruncate, bool aPriority);
void OnLoaded();
void RememberCallback(Callback & aCallback, bool aBypassIfBusy);
void RememberCallback(Callback & aCallback);
void InvokeCallbacksLock();
void InvokeCallbacks();
bool InvokeCallbacks(bool aReadOnly);

View File

@ -2163,56 +2163,39 @@ nsHttpChannel::ProcessPartialContent()
rv = InstallOfflineCacheListener(mLogicalOffset);
if (NS_FAILED(rv)) return rv;
}
// merge any new headers with the cached response headers
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
if (NS_FAILED(rv)) return rv;
// update the cached response head
nsAutoCString head;
mCachedResponseHead->Flatten(head, true);
rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
if (NS_FAILED(rv)) return rv;
UpdateInhibitPersistentCachingFlag();
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
mCachedContentIsPartial = false;
mConcurentCacheAccess = 0;
// notify observers interested in looking at a response that has been
// merged with any cached headers (http-on-examine-merged-response).
gHttpHandler->OnExamineMergedResponse(this);
}
else {
} else {
// suspend the current transaction
rv = mTransactionPump->Suspend();
if (NS_FAILED(rv)) return rv;
}
// merge any new headers with the cached response headers
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
if (NS_FAILED(rv)) return rv;
// merge any new headers with the cached response headers
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
if (NS_FAILED(rv)) return rv;
// update the cached response head
nsAutoCString head;
mCachedResponseHead->Flatten(head, true);
rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
if (NS_FAILED(rv)) return rv;
// update the cached response head
nsAutoCString head;
mCachedResponseHead->Flatten(head, true);
rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
if (NS_FAILED(rv)) return rv;
// make the cached response be the current response
mResponseHead = Move(mCachedResponseHead);
// make the cached response be the current response
mResponseHead = Move(mCachedResponseHead);
UpdateInhibitPersistentCachingFlag();
UpdateInhibitPersistentCachingFlag();
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
// notify observers interested in looking at a response that has been
// merged with any cached headers (http-on-examine-merged-response).
gHttpHandler->OnExamineMergedResponse(this);
// notify observers interested in looking at a response that has been
// merged with any cached headers (http-on-examine-merged-response).
gHttpHandler->OnExamineMergedResponse(this);
if (mConcurentCacheAccess) {
mCachedContentIsPartial = false;
mConcurentCacheAccess = 0;
// Now we continue reading the network response.
} else {
// the cached content is valid, although incomplete.
mCachedContentIsValid = true;
rv = ReadFromCache(false);

View File

@ -217,11 +217,6 @@ typedef HANDLE userland_thread_t;
typedef char* caddr_t;
int Win_getifaddrs(struct ifaddrs**);
#define getifaddrs(interfaces) (int)Win_getifaddrs(interfaces)
int win_if_nametoindex(const char *);
#define if_nametoindex(x) win_if_nametoindex(x)
#define bzero(buf, len) memset(buf, 0, len)
#define bcopy(srcKey, dstKey, len) memcpy(dstKey, srcKey, len)
#define snprintf(data, size, format, name) _snprintf_s(data, size, _TRUNCATE, format, name)
@ -399,6 +394,11 @@ struct udphdr {
unsigned __int16 uh_sum;
};
int Win_getifaddrs(struct ifaddrs**);
#define getifaddrs(interfaces) (int)Win_getifaddrs(interfaces)
int win_if_nametoindex(const char *);
#define if_nametoindex(x) win_if_nametoindex(x)
#else /* !defined(Userspace_os_Windows) */
#include <sys/cdefs.h> /* needed? added from old __FreeBSD__ */
#include <sys/socket.h>

View File

@ -41,7 +41,15 @@
#include <strings.h>
#include <stdint.h>
#else
#include "netinet/sctp_os_userspace.h"
#if defined(_MSC_VER) && _MSC_VER >= 1600
#include <stdint.h>
#elif defined(SCTP_STDINT_INCLUDE)
#include SCTP_STDINT_INCLUDE
#else
#define uint32_t unsigned __int32
#define uint64_t unsigned __int64
#endif
#include <winsock2.h>
#endif
#define MINALLOCSIZE UMA_SMALLEST_UNIT

View File

@ -4,6 +4,11 @@ function run_test()
{
do_get_profile();
if (!newCacheBackEndUsed()) {
do_check_true(true, "This test checks only cache2 specific behavior.");
return;
}
var mc = new MultipleCallbacks(2, function() {
var mem = getCacheStorage("memory");
var disk = getCacheStorage("disk");

View File

@ -730,7 +730,9 @@ class RunProgram(MachCommandBase):
help='Do not pass the -no-remote argument by default.')
@CommandArgument('+background', '+b', action='store_true',
help='Do not pass the -foreground argument by default on Mac')
def run(self, params, remote, background):
@CommandArgument('+profile', '+P', action='store_true',
help='Specifiy thr profile to use')
def run(self, params, remote, background, profile):
try:
args = [self.get_binary_path('app')]
except Exception as e:
@ -742,6 +744,12 @@ class RunProgram(MachCommandBase):
args.append('-no-remote')
if not background and sys.platform == 'darwin':
args.append('-foreground')
if '-profile' not in params and '-P' not in params:
path = os.path.join(self.topobjdir, 'tmp', 'scratch_user')
if not os.path.isdir(path):
os.makedirs(path)
args.append('-profile')
args.append(path)
if params:
args.extend(params)
return self.run_process(args=args, ensure_exit_code=False,
@ -763,6 +771,8 @@ class DebugProgram(MachCommandBase):
help='Name of debugger to launch')
@CommandArgument('+debugparams', default=None, metavar='params', type=str,
help='Command-line arguments to pass to GDB or LLDB itself; split as the Bourne shell would.')
@CommandArgument('+profile', '+P', action='store_true',
help='Specifiy thr profile to use')
# Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever
# segfaults induced by the slow-script-detecting logic for Ion/Odin JITted
# code. If we don't pass this, the user will need to periodically type
@ -770,7 +780,7 @@ class DebugProgram(MachCommandBase):
# automatic resuming; see the bug.
@CommandArgument('+slowscript', action='store_true',
help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code')
def debug(self, params, remote, background, debugger, debugparams, slowscript):
def debug(self, params, remote, background, profile, debugger, debugparams, slowscript):
import which
if debugger:
try:
@ -829,6 +839,12 @@ class DebugProgram(MachCommandBase):
args.append('-foreground')
if params:
args.extend(params)
if '-profile' not in params and '-P' not in params:
path = os.path.join(self.topobjdir, 'tmp', 'scratch_user')
if not os.path.isdir(path):
os.makedirs(path)
args.append('-profile')
args.append(path)
if not slowscript:
extra_env['JS_DISABLE_SLOW_SCRIPT_SIGNALS'] = '1'
return self.run_process(args=args, append_env=extra_env,

View File

@ -62,14 +62,18 @@ function initExceptionDialog() {
document.getElementById("locationTextBox").value = args[0].location;
document.getElementById('checkCertButton').disabled = false;
// We can optionally pre-fetch the certificate too. Don't do this
// synchronously, since it would prevent the window from appearing
// until the fetch is completed, which could be multiple seconds.
// Instead, let's use a timer to spawn the actual fetch, but update
// the dialog to "checking..." state right away, so that the UI
// is appropriately responsive. Bug 453855
if (args[0].prefetchCert) {
if (args[0].sslStatus) {
gSSLStatus = args[0].sslStatus;
gCert = gSSLStatus.serverCert;
gBroken = true;
updateCertStatus();
} else if (args[0].prefetchCert) {
// We can optionally pre-fetch the certificate too. Don't do this
// synchronously, since it would prevent the window from appearing
// until the fetch is completed, which could be multiple seconds.
// Instead, let's use a timer to spawn the actual fetch, but update
// the dialog to "checking..." state right away, so that the UI
// is appropriately responsive. Bug 453855
document.getElementById("checkCertButton").disabled = true;
gChecking = true;
updateCertStatus();
@ -83,35 +87,6 @@ function initExceptionDialog() {
}
}
// returns true if found and global status could be set
function findRecentBadCert(uri) {
try {
var certDB = Components.classes["@mozilla.org/security/x509certdb;1"]
.getService(Components.interfaces.nsIX509CertDB);
if (!certDB)
return false;
var recentCertsSvc = certDB.getRecentBadCerts(inPrivateBrowsingMode());
if (!recentCertsSvc)
return false;
var hostWithPort = uri.host + ":" + uri.port;
gSSLStatus = recentCertsSvc.getRecentBadCert(hostWithPort);
if (!gSSLStatus)
return false;
gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
if (!gCert)
return false;
gBroken = true;
}
catch (e) {
return false;
}
updateCertStatus();
return true;
}
/**
* Attempt to download the certificate for the location specified, and populate
* the Certificate Status section with the result.
@ -126,10 +101,6 @@ function checkCert() {
var uri = getURI();
// Is the cert already known in the list of recently seen bad certs?
if (findRecentBadCert(uri) == true)
return;
var req = new XMLHttpRequest();
try {
if(uri) {

View File

@ -33,6 +33,7 @@ PARALLEL_DIRS += [
'passwordmgr',
'perf',
'places',
'promiseworker',
'prompts',
'protobuf',
'reflect',

View File

@ -1,205 +0,0 @@
/**
* Thin wrapper around a ChromeWorker that wraps postMessage/onmessage/onerror
* as promises.
*
* Not for public use yet.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["PromiseWorker"];
// The library of promises.
Components.utils.import("resource://gre/modules/Promise.jsm", this);
/**
* An implementation of queues (FIFO).
*
* The current implementation uses one array, runs in O(n ^ 2), and is optimized
* for the case in which queues are generally short.
*/
let Queue = function Queue() {
this._array = [];
};
Queue.prototype = {
pop: function pop() {
return this._array.shift();
},
push: function push(x) {
return this._array.push(x);
},
isEmpty: function isEmpty() {
return this._array.length == 0;
}
};
/**
* An object responsible for dispatching messages to
* a chrome worker and routing the responses.
*
* @param {string} url The url containing the source code for this worker,
* as in constructor ChromeWorker.
* @param {Function} log A logging function.
*
* @constructor
*/
function PromiseWorker(url, log) {
if (typeof url != "string") {
throw new TypeError("Expecting a string");
}
if (typeof log !== "function") {
throw new TypeError("log is expected to be a function");
}
this._log = log;
this._url = url;
/**
* The queue of deferred, waiting for the completion of their
* respective job by the worker.
*
* Each item in the list may contain an additional field |closure|,
* used to store strong references to value that must not be
* garbage-collected before the reply has been received (e.g.
* arrays).
*
* @type {Queue<{deferred:deferred, closure:*=}>}
*/
this._queue = new Queue();
/**
* The number of the current message.
*
* Used for debugging purposes.
*/
this._id = 0;
/**
* The instant at which the worker was launched.
*/
this.launchTimeStamp = null;
/**
* Timestamps provided by the worker for statistics purposes.
*/
this.workerTimeStamps = null;
}
PromiseWorker.prototype = {
/**
* Instantiate the worker lazily.
*/
get _worker() {
delete this._worker;
let worker = new ChromeWorker(this._url);
let self = this;
Object.defineProperty(this, "_worker", {value:
worker
});
// We assume that we call to _worker for the purpose of calling
// postMessage().
this.launchTimeStamp = Date.now();
/**
* Receive errors that are not instances of OS.File.Error, propagate
* them to the listeners.
*
* The worker knows how to serialize errors that are instances
* of |OS.File.Error|. These are treated by |worker.onmessage|.
* However, for other errors, we rely on DOM's mechanism for
* serializing errors, which transmits these errors through
* |worker.onerror|.
*
* @param {Error} error Some JS error.
*/
worker.onerror = function onerror(error) {
self._log("Received uncaught error from worker", error.message, error.filename, error.lineno);
error.preventDefault();
let {deferred} = self._queue.pop();
deferred.reject(error);
};
/**
* Receive messages from the worker, propagate them to the listeners.
*
* Messages must have one of the following shapes:
* - {ok: some_value} in case of success
* - {fail: some_error} in case of error, where
* some_error is an instance of |PromiseWorker.WorkerError|
*
* Messages may also contain a field |id| to help
* with debugging.
*
* Messages may also optionally contain a field |durationMs|, holding
* the duration of the function call in milliseconds.
*
* @param {*} msg The message received from the worker.
*/
worker.onmessage = function onmessage(msg) {
self._log("Received message from worker", msg.data);
let handler = self._queue.pop();
let deferred = handler.deferred;
let data = msg.data;
if (data.id != handler.id) {
throw new Error("Internal error: expecting msg " + handler.id + ", " +
" got " + data.id + ": " + JSON.stringify(msg.data));
}
if ("timeStamps" in data) {
self.workerTimeStamps = data.timeStamps;
}
if ("ok" in data) {
// Pass the data to the listeners.
deferred.resolve(data);
} else if ("StopIteration" in data) {
// We have received a StopIteration error
deferred.reject(StopIteration);
} if ("fail" in data) {
// We have received an error that was serialized by the
// worker.
deferred.reject(new PromiseWorker.WorkerError(data.fail));
}
};
return worker;
},
/**
* Post a message to a worker.
*
* @param {string} fun The name of the function to call.
* @param {Array} array The contents of the message.
* @param {*=} closure An object holding references that should not be
* garbage-collected before the message treatment is complete.
*
* @return {promise}
*/
post: function post(fun, array, closure) {
let deferred = Promise.defer();
let id = ++this._id;
let message = {fun: fun, args: array, id: id};
this._log("Posting message", message);
try {
this._worker.postMessage(message);
} catch (ex if typeof ex == "number") {
this._log("Could not post message", message, "due to xpcom error", ex);
// handle raw xpcom errors (see eg bug 961317)
return Promise.reject(new Components.Exception("Error in postMessage", ex));
} catch (ex) {
this._log("Could not post message", message, "due to error", ex);
return Promise.reject(ex);
}
this._queue.push({deferred:deferred, closure: closure, id: id});
this._log("Message posted");
return deferred.promise;
}
};
/**
* An error that has been serialized by the worker.
*
* @constructor
*/
PromiseWorker.WorkerError = function WorkerError(data) {
this.data = data;
};
this.PromiseWorker = PromiseWorker;

View File

@ -7,7 +7,6 @@
JS_MODULES_PATH = 'modules/osfile'
EXTRA_JS_MODULES += [
'_PromiseWorker.jsm',
'osfile_async_front.jsm',
'osfile_async_worker.js',
'osfile_native.jsm',

View File

@ -54,43 +54,12 @@ Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
// The implementation of communications
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
let Native = Cu.import("resource://gre/modules/osfile/osfile_native.jsm", {});
/**
* Constructors for decoding standard exceptions
* received from the worker.
*/
const EXCEPTION_CONSTRUCTORS = {
EvalError: function(error) {
return new EvalError(error.message, error.fileName, error.lineNumber);
},
InternalError: function(error) {
return new InternalError(error.message, error.fileName, error.lineNumber);
},
RangeError: function(error) {
return new RangeError(error.message, error.fileName, error.lineNumber);
},
ReferenceError: function(error) {
return new ReferenceError(error.message, error.fileName, error.lineNumber);
},
SyntaxError: function(error) {
return new SyntaxError(error.message, error.fileName, error.lineNumber);
},
TypeError: function(error) {
return new TypeError(error.message, error.fileName, error.lineNumber);
},
URIError: function(error) {
return new URIError(error.message, error.fileName, error.lineNumber);
},
OSError: function(error) {
return OS.File.Error.fromMsg(error);
}
};
// It's possible for osfile.jsm to get imported before the profile is
// set up. In this case, some path constants aren't yet available.
@ -253,9 +222,11 @@ let Scheduler = {
*/
get worker() {
if (!this._worker) {
// Either the worker has never been created or it has been reset
this._worker = new PromiseWorker(
"resource://gre/modules/osfile/osfile_async_worker.js", LOG);
// Either the worker has never been created or it has been
// reset. In either case, it is time to instantiate the worker.
this._worker = new BasePromiseWorker("resource://gre/modules/osfile/osfile_async_worker.js");
this._worker.log = LOG;
this._worker.ExceptionHandlers["OS.File.Error"] = OSError.fromMsg;
}
return this._worker;
},
@ -329,12 +300,11 @@ let Scheduler = {
try {
Scheduler.latestReceived = [];
Scheduler.latestSent = [Date.now(), ...message];
let promise = this._worker.post(...message);
// Wait for result
let resources;
try {
resources = (yield promise).ok;
resources = yield this._worker.post(...message);
Scheduler.latestReceived = [Date.now(), message];
} catch (ex) {
@ -411,7 +381,7 @@ let Scheduler = {
* @return {Promise} A promise conveying the result/error caused by
* calling |method| with arguments |args|.
*/
post: function post(method, ...args) {
post: function post(method, args = undefined, closure = undefined) {
if (this.shutdown) {
LOG("OS.File is not available anymore. The following request has been rejected.",
method, args);
@ -426,12 +396,6 @@ let Scheduler = {
Scheduler.Debugging.messagesSent++;
}
// By convention, the last argument of any message may be an |options| object.
let options;
let methodArgs = args[0];
if (methodArgs) {
options = methodArgs[methodArgs.length - 1];
}
Scheduler.Debugging.messagesQueued++;
return this.push(Task.async(function*() {
if (this.shutdown) {
@ -443,78 +407,32 @@ let Scheduler = {
// Update debugging information. As |args| may be quite
// expensive, we only keep a shortened version of it.
Scheduler.Debugging.latestReceived = null;
Scheduler.Debugging.latestSent = [Date.now(), method, summarizeObject(methodArgs)];
Scheduler.Debugging.latestSent = [Date.now(), method, summarizeObject(args)];
// Don't kill the worker just yet
Scheduler.restartTimer();
let data;
let reply;
let isError = false;
try {
try {
Scheduler.Debugging.messagesSent++;
data = yield this.worker.post(method, ...args);
Scheduler.Debugging.latestSent = Scheduler.Debugging.latestSent.slice(0, 2);
reply = yield this.worker.post(method, args, closure);
Scheduler.Debugging.latestReceived = [Date.now(), summarizeObject(reply)];
return reply;
} finally {
Scheduler.Debugging.messagesReceived++;
}
reply = data;
} catch (error) {
reply = error;
isError = true;
if (error instanceof PromiseWorker.WorkerError) {
throw EXCEPTION_CONSTRUCTORS[error.data.exn || "OSError"](error.data);
}
if (error instanceof ErrorEvent) {
let message = error.message;
if (message == "uncaught exception: [object StopIteration]") {
isError = false;
throw StopIteration;
}
throw new Error(message, error.filename, error.lineno);
}
Scheduler.Debugging.latestReceived = [Date.now(), error.message, error.fileName, error.lineNumber];
throw error;
} finally {
Scheduler.Debugging.latestSent = Scheduler.Debugging.latestSent.slice(0, 2);
if (isError) {
Scheduler.Debugging.latestReceived = [Date.now(), reply.message, reply.fileName, reply.lineNumber];
} else {
Scheduler.Debugging.latestReceived = [Date.now(), summarizeObject(reply)];
}
if (firstLaunch) {
Scheduler._updateTelemetry();
}
Scheduler.restartTimer();
}
// Check for duration and return result.
if (!options) {
return data.ok;
}
// Check for options.outExecutionDuration.
if (typeof options !== "object" ||
!("outExecutionDuration" in options)) {
return data.ok;
}
// If data.durationMs is not present, return data.ok (there was an
// exception applying the method).
if (!("durationMs" in data)) {
return data.ok;
}
// Bug 874425 demonstrates that two successive calls to Date.now()
// can actually produce an interval with negative duration.
// We assume that this is due to an operation that is so short
// that Date.now() is not monotonic, so we round this up to 0.
let durationMs = Math.max(0, data.durationMs);
// Accumulate (or initialize) outExecutionDuration
if (typeof options.outExecutionDuration == "number") {
options.outExecutionDuration += durationMs;
} else {
options.outExecutionDuration = durationMs;
}
return data.ok;
}.bind(this)));
},

View File

@ -1,24 +1,13 @@
if (this.Components) {
throw new Error("This worker can only be loaded from a worker thread");
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Worker thread for osfile asynchronous front-end
if (this.Components) {
throw new Error("This worker can only be loaded from a worker thread");
}
// Exception names to be posted from the worker thread to main thread
const EXCEPTION_NAMES = {
EvalError: "EvalError",
InternalError: "InternalError",
RangeError: "RangeError",
ReferenceError: "ReferenceError",
SyntaxError: "SyntaxError",
TypeError: "TypeError",
URIError: "URIError"
};
// Worker thread for osfile asynchronous front-end
(function(exports) {
"use strict";
@ -33,108 +22,28 @@ const EXCEPTION_NAMES = {
importScripts("resource://gre/modules/osfile.jsm");
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
let LOG = SharedAll.LOG.bind(SharedAll, "Agent");
// Post a message to the parent, decorate it with statistics if
// necessary. Use this instead of self.postMessage.
function post(message, ...transfers) {
let worker = new PromiseWorker.AbstractWorker();
worker.dispatch = function(method, args = []) {
return Agent[method](...args);
},
worker.log = LOG;
worker.postMessage = function(message, ...transfers) {
if (timeStamps) {
message.timeStamps = timeStamps;
timeStamps = null;
}
self.postMessage(message, ...transfers);
}
/**
* Communications with the controller.
*
* Accepts messages:
* {fun:function_name, args:array_of_arguments_or_null, id:id}
*
* Sends messages:
* {ok: result, id:id} / {fail: serialized_form_of_OS.File.Error, id:id}
*/
self.onmessage = function onmessage(msg) {
let data = msg.data;
LOG("Received message", data);
let id = data.id;
let start;
let options;
if (data.args) {
options = data.args[data.args.length - 1];
}
// If |outExecutionDuration| option was supplied, start measuring the
// duration of the operation.
if (options && typeof options === "object" && "outExecutionDuration" in options) {
start = Date.now();
}
let result;
let exn;
let durationMs;
try {
let method = data.fun;
LOG("Calling method", method);
result = Agent[method].apply(Agent, data.args);
LOG("Method", method, "succeeded");
} catch (ex) {
exn = ex;
LOG("Error while calling agent method", exn, exn.moduleStack || exn.stack || "");
}
if (start) {
// Record duration
durationMs = Date.now() - start;
LOG("Method took", durationMs, "ms");
}
// Now, post a reply, possibly as an uncaught error.
// We post this message from outside the |try ... catch| block
// to avoid capturing errors that take place during |postMessage| and
// built-in serialization.
if (!exn) {
LOG("Sending positive reply", result, "id is", id);
if (result instanceof Meta) {
if ("transfers" in result.meta) {
// Take advantage of zero-copy transfers
post({ok: result.data, id: id, durationMs: durationMs},
result.meta.transfers);
} else {
post({ok: result.data, id:id, durationMs: durationMs});
}
if (result.meta.shutdown || false) {
// Time to close the worker
self.close();
}
} else {
post({ok: result, id:id, durationMs: durationMs});
}
} else if (exn == StopIteration) {
// StopIteration cannot be serialized automatically
LOG("Sending back StopIteration");
post({StopIteration: true, id: id, durationMs: durationMs});
} else if (exn instanceof exports.OS.File.Error) {
LOG("Sending back OS.File error", exn, "id is", id);
// Instances of OS.File.Error know how to serialize themselves
// (deserialization ensures that we end up with OS-specific
// instances of |OS.File.Error|)
post({fail: exports.OS.File.Error.toMsg(exn), id:id, durationMs: durationMs});
} else if (exn.constructor.name in EXCEPTION_NAMES) {
LOG("Sending back exception", exn.constructor.name);
post({fail: {exn: exn.constructor.name, message: exn.message,
fileName: exn.moduleName || exn.fileName, lineNumber: exn.lineNumber},
id: id, durationMs: durationMs});
} else {
// Other exceptions do not, and should be propagated through DOM's
// built-in mechanism for uncaught errors, although this mechanism
// may lose interesting information.
LOG("Sending back regular error", exn, exn.moduleStack || exn.stack, "id is", id);
throw exn;
}
};
worker.close = function() {
self.close();
};
let Meta = PromiseWorker.Meta;
self.addEventListener("message", msg => worker.handleMessage(msg));
/**
* A data structure used to track opened resources
@ -235,26 +144,6 @@ const EXCEPTION_NAMES = {
let File = exports.OS.File;
/**
* A constructor used to return data to the caller thread while
* also executing some specific treatment (e.g. shutting down
* the current thread, transmitting data instead of copying it).
*
* @param {object=} data The data to return to the caller thread.
* @param {object=} meta Additional instructions, as an object
* that may contain the following fields:
* - {bool} shutdown If |true|, shut down the current thread after
* having sent the result.
* - {Array} transfers An array of objects that should be transferred
* instead of being copied.
*
* @constructor
*/
let Meta = function Meta(data, meta) {
this.data = data;
this.meta = meta;
};
/**
* The agent.
*

View File

@ -91,6 +91,9 @@ OSError.prototype.toString = function toString() {
(this.path? " on file " + this.path : "") +
" (" + LazyBindings.strerror(this.unixErrno).readString() + ")";
};
OSError.prototype.toMsg = function toMsg() {
return OSError.toMsg(this);
};
/**
* |true| if the error was raised because a file or directory
@ -154,6 +157,10 @@ Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
*/
OSError.toMsg = function toMsg(error) {
return {
exn: "OS.File.Error",
fileName: error.moduleName,
lineNumber: error.lineNumber,
stack: error.moduleStack,
operation: error.operation,
unixErrno: error.unixErrno,
path: error.path
@ -164,7 +171,11 @@ OSError.toMsg = function toMsg(error) {
* Deserialize a message back to an instance of OSError
*/
OSError.fromMsg = function fromMsg(msg) {
return new OSError(msg.operation, msg.unixErrno, msg.path);
let error = new OSError(msg.operation, msg.unixErrno, msg.path);
error.stack = msg.stack;
error.fileName = msg.fileName;
error.lineNumber = msg.lineNumber;
return error;
};
exports.Error = OSError;

View File

@ -113,6 +113,9 @@ OSError.prototype.toString = function toString() {
+ this.operation + (this.path? " on file " + this.path : "") +
" (" + buf.readString() + ")";
};
OSError.prototype.toMsg = function toMsg() {
return OSError.toMsg(this);
};
/**
* |true| if the error was raised because a file or directory
@ -178,6 +181,10 @@ Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
*/
OSError.toMsg = function toMsg(error) {
return {
exn: "OS.File.Error",
fileName: error.moduleName,
lineNumber: error.lineNumber,
stack: error.moduleStack,
operation: error.operation,
winLastError: error.winLastError,
path: error.path
@ -188,7 +195,11 @@ OSError.toMsg = function toMsg(error) {
* Deserialize a message back to an instance of OSError
*/
OSError.fromMsg = function fromMsg(msg) {
return new OSError(msg.operation, msg.winLastError, msg.path);
let error = new OSError(msg.operation, msg.winLastError, msg.path);
error.stack = msg.stack;
error.fileName = msg.fileName;
error.lineNumber = msg.lineNumber;
return error;
};
exports.Error = OSError;

View File

@ -0,0 +1,342 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* A wrapper around ChromeWorker with extended capabilities designed
* to simplify main thread-to-worker thread asynchronous function calls.
*
* This wrapper:
* - groups requests and responses as a method `post` that returns a `Promise`;
* - ensures that exceptions thrown on the worker thread are correctly deserialized;
* - provides some utilities for benchmarking various operations.
*
* Generally, you should use PromiseWorker.jsm along with its worker-side
* counterpart PromiseWorker.js.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["BasePromiseWorker"];
const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
/**
* An implementation of queues (FIFO).
*
* The current implementation uses one array, runs in O(n ^ 2), and is optimized
* for the case in which queues are generally short.
*/
function Queue() {
this._array = [];
};
Queue.prototype = {
pop: function pop() {
return this._array.shift();
},
push: function push(x) {
return this._array.push(x);
},
isEmpty: function isEmpty() {
return this._array.length == 0;
}
};
/**
* Constructors for decoding standard exceptions received from the
* worker.
*/
const EXCEPTION_CONSTRUCTORS = {
EvalError: function(error) {
let result = new EvalError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
InternalError: function(error) {
let result = new InternalError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
RangeError: function(error) {
let result = new RangeError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
ReferenceError: function(error) {
let result = new ReferenceError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
SyntaxError: function(error) {
let result = new SyntaxError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
TypeError: function(error) {
let result = new TypeError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
URIError: function(error) {
let result = new URIError(error.message, error.fileName, error.lineNumber);
result.stack = error.stack;
return result;
},
StopIteration: function() {
return StopIteration;
}
};
/**
* An object responsible for dispatching messages to a chrome worker
* and routing the responses.
*
* Instances of this constructor who need logging may provide a method
* `log: function(...args) { ... }` in charge of printing out (or
* discarding) logs.
*
* Instances of this constructor may add exception handlers to
* `this.ExceptionHandlers`, if they need to handle custom exceptions.
*
* @param {string} url The url containing the source code for this worker,
* as in constructor ChromeWorker.
*
* @constructor
*/
this.BasePromiseWorker = function(url) {
if (typeof url != "string") {
throw new TypeError("Expecting a string");
}
this._url = url;
/**
* A set of methods, with the following
*
* ConstructorName: function({message, fileName, lineNumber}) {
* // Construct a new instance of ConstructorName based on
* // `message`, `fileName`, `lineNumber`
* }
*
* By default, this covers EvalError, InternalError, RangeError,
* ReferenceError, SyntaxError, TypeError, URIError, StopIteration.
*/
this.ExceptionHandlers = Object.create(EXCEPTION_CONSTRUCTORS);
/**
* The queue of deferred, waiting for the completion of their
* respective job by the worker.
*
* Each item in the list may contain an additional field |closure|,
* used to store strong references to value that must not be
* garbage-collected before the reply has been received (e.g.
* arrays).
*
* @type {Queue<{deferred:deferred, closure:*=}>}
*/
this._queue = new Queue();
/**
* The number of the current message.
*
* Used for debugging purposes.
*/
this._id = 0;
/**
* The instant at which the worker was launched.
*/
this.launchTimeStamp = null;
/**
* Timestamps provided by the worker for statistics purposes.
*/
this.workerTimeStamps = null;
};
this.BasePromiseWorker.prototype = {
log: function() {
// By Default, ignore all logs.
},
/**
* Instantiate the worker lazily.
*/
get _worker() {
delete this._worker;
let worker = new ChromeWorker(this._url);
Object.defineProperty(this, "_worker", {value:
worker
});
// We assume that we call to _worker for the purpose of calling
// postMessage().
this.launchTimeStamp = Date.now();
/**
* Receive errors that have been serialized by the built-in mechanism
* of DOM/Chrome Workers.
*
* PromiseWorker.js knows how to serialize a number of errors
* without losing information. These are treated by
* |worker.onmessage|. However, for other errors, we rely on
* DOM's mechanism for serializing errors, which transmits these
* errors through |worker.onerror|.
*
* @param {Error} error Some JS error.
*/
worker.onerror = error => {
this.log("Received uncaught error from worker", error.message, error.filename, error.lineno);
error.preventDefault();
let {deferred} = this._queue.pop();
deferred.reject(error);
};
/**
* Receive messages from the worker, propagate them to the listeners.
*
* Messages must have one of the following shapes:
* - {ok: some_value} in case of success
* - {fail: some_error} in case of error, where
* some_error is an instance of |PromiseWorker.WorkerError|
*
* Messages may also contain a field |id| to help
* with debugging.
*
* Messages may also optionally contain a field |durationMs|, holding
* the duration of the function call in milliseconds.
*
* @param {*} msg The message received from the worker.
*/
worker.onmessage = msg => {
this.log("Received message from worker", msg.data);
let handler = this._queue.pop();
let deferred = handler.deferred;
let data = msg.data;
if (data.id != handler.id) {
throw new Error("Internal error: expecting msg " + handler.id + ", " +
" got " + data.id + ": " + JSON.stringify(msg.data));
}
if ("timeStamps" in data) {
this.workerTimeStamps = data.timeStamps;
}
if ("ok" in data) {
// Pass the data to the listeners.
deferred.resolve(data);
} else if ("fail" in data) {
// We have received an error that was serialized by the
// worker.
deferred.reject(new WorkerError(data.fail));
}
};
return worker;
},
/**
* Post a message to a worker.
*
* @param {string} fun The name of the function to call.
* @param {Array} args The arguments to pass to `fun`. By convention,
* the last argument may be an object `options` with some of the following
* fields:
* - {number|null} outExecutionDuration A parameter to be filled with the
* duration of the off main thread execution for this call.
* @param {*=} closure An object holding references that should not be
* garbage-collected before the message treatment is complete.
*
* @return {promise}
*/
post: function(fun, args, closure) {
return Task.spawn(function* postMessage() {
let id = ++this._id;
let message = {fun: fun, args: args, id: id};
this.log("Posting message", message);
try {
this._worker.postMessage(message);
} catch (ex if typeof ex == "number") {
this.log("Could not post message", message, "due to xpcom error", ex);
// handle raw xpcom errors (see eg bug 961317)
throw new Components.Exception("Error in postMessage", ex);
} catch (ex) {
this.log("Could not post message", message, "due to error", ex);
throw ex;
}
let deferred = Promise.defer();
this._queue.push({deferred:deferred, closure: closure, id: id});
this.log("Message posted");
let reply;
let isError = false;
try {
this.log("Expecting reply");
reply = yield deferred.promise;
} catch (error) {
this.log("Got error", error);
reply = error;
isError = true;
if (error instanceof WorkerError) {
// We know how to deserialize most well-known errors
throw this.ExceptionHandlers[error.data.exn](error.data);
}
if (error instanceof ErrorEvent) {
// Other errors get propagated as instances of ErrorEvent
this.log("Error serialized by DOM", error.message, error.filename, error.lineno);
throw new Error(error.message, error.filename, error.lineno);
}
// We don't know about this kind of error
throw error;
}
// By convention, the last argument may be an object `options`.
let options = null;
if (args) {
options = args[args.length - 1];
}
// Check for duration and return result.
if (!options ||
typeof options !== "object" ||
!("outExecutionDuration" in options)) {
return reply.ok;
}
// If reply.durationMs is not present, just return the result,
// without updating durations (there was an error in the method
// dispatch).
if (!("durationMs" in reply)) {
return reply.ok;
}
// Bug 874425 demonstrates that two successive calls to Date.now()
// can actually produce an interval with negative duration.
// We assume that this is due to an operation that is so short
// that Date.now() is not monotonic, so we round this up to 0.
let durationMs = Math.max(0, reply.durationMs);
// Accumulate (or initialize) outExecutionDuration
if (typeof options.outExecutionDuration == "number") {
options.outExecutionDuration += durationMs;
} else {
options.outExecutionDuration = durationMs;
}
return reply.ok;
}.bind(this));
}
};
/**
* An error that has been serialized by the worker.
*
* @constructor
*/
function WorkerError(data) {
this.data = data;
};

View File

@ -0,0 +1,13 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PARALLEL_DIRS += [
'worker'
]
EXTRA_JS_MODULES = [
'PromiseWorker.jsm',
]

View File

@ -0,0 +1,206 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* A wrapper around `self` with extended capabilities designed
* to simplify main thread-to-worker thread asynchronous function calls.
*
* This wrapper:
* - groups requests and responses as a method `post` that returns a `Promise`;
* - ensures that exceptions thrown on the worker thread are correctly serialized;
* - provides some utilities for benchmarking various operations.
*
* Generally, you should use PromiseWorker.js along with its main thread-side
* counterpart PromiseWorker.jsm.
*/
"use strict";
if (typeof Components != "undefined") {
throw new Error("This module is meant to be used from the worker thread");
}
if (typeof require == "undefined" || typeof module == "undefined") {
throw new Error("this module is meant to be imported using the implementation of require() at resource://gre/modules/workers/require.js");
}
importScripts("resource://gre/modules/workers/require.js");
/**
* Built-in JavaScript exceptions that may be serialized without
* loss of information.
*/
const EXCEPTION_NAMES = {
EvalError: "EvalError",
InternalError: "InternalError",
RangeError: "RangeError",
ReferenceError: "ReferenceError",
SyntaxError: "SyntaxError",
TypeError: "TypeError",
URIError: "URIError",
};
/**
* A constructor used to return data to the caller thread while
* also executing some specific treatment (e.g. shutting down
* the current thread, transmitting data instead of copying it).
*
* @param {object=} data The data to return to the caller thread.
* @param {object=} meta Additional instructions, as an object
* that may contain the following fields:
* - {bool} shutdown If |true|, shut down the current thread after
* having sent the result.
* - {Array} transfers An array of objects that should be transferred
* instead of being copied.
*
* @constructor
*/
function Meta(data, meta) {
this.data = data;
this.meta = meta;
};
exports.Meta = Meta;
/**
* Base class for a worker.
*
* Derived classes are expected to provide the following methods:
* {
* dispatch: function(method, args) {
* // Dispatch a call to method `method` with args `args`
* },
* log: function(...msg) {
* // Log (or discard) messages (optional)
* },
* postMessage: function(message, ...transfers) {
* // Post a message to the main thread
* },
* close: function() {
* // Close the worker
* }
* }
*
* By default, the AbstractWorker is not connected to a message port,
* hence will not receive anything.
*
* To connect it, use `onmessage`, as follows:
* self.addEventListener("message", msg => myWorkerInstance.handleMessage(msg));
*/
function AbstractWorker(agent) {
this._agent = agent;
};
AbstractWorker.prototype = {
// Default logger: discard all messages
log: function() {
},
/**
* Handle a message.
*/
handleMessage: function(msg) {
let data = msg.data;
this.log("Received message", data);
let id = data.id;
let start;
let options;
if (data.args) {
options = data.args[data.args.length - 1];
}
// If |outExecutionDuration| option was supplied, start measuring the
// duration of the operation.
if (options && typeof options === "object" && "outExecutionDuration" in options) {
start = Date.now();
}
let result;
let exn;
let durationMs;
let method = data.fun;
try {
this.log("Calling method", method);
result = this.dispatch(method, data.args);
this.log("Method", method, "succeeded");
} catch (ex) {
exn = ex;
this.log("Error while calling agent method", method, exn, exn.moduleStack || exn.stack || "");
}
if (start) {
// Record duration
durationMs = Date.now() - start;
this.log("Method took", durationMs, "ms");
}
// Now, post a reply, possibly as an uncaught error.
// We post this message from outside the |try ... catch| block
// to avoid capturing errors that take place during |postMessage| and
// built-in serialization.
if (!exn) {
this.log("Sending positive reply", result, "id is", id);
if (result instanceof Meta) {
if ("transfers" in result.meta) {
// Take advantage of zero-copy transfers
this.postMessage({ok: result.data, id: id, durationMs: durationMs},
result.meta.transfers);
} else {
this.postMessage({ok: result.data, id:id, durationMs: durationMs});
}
if (result.meta.shutdown || false) {
// Time to close the worker
this.close();
}
} else {
this.postMessage({ok: result, id:id, durationMs: durationMs});
}
} else if (exn.constructor.name in EXCEPTION_NAMES) {
// Rather than letting the DOM mechanism [de]serialize built-in
// JS errors, which loses lots of information (in particular,
// the constructor name, the moduleName and the moduleStack),
// we [de]serialize them manually with a little more care.
this.log("Sending back exception", exn.constructor.name, "id is", id);
let error = {
exn: exn.constructor.name,
message: exn.message,
fileName: exn.moduleName || exn.fileName,
lineNumber: exn.lineNumber,
stack: exn.moduleStack
};
this.postMessage({fail: error, id: id, durationMs: durationMs});
} else if (exn == StopIteration) {
// StopIteration is a well-known singleton, and requires a
// slightly different treatment.
this.log("Sending back StopIteration, id is", id);
let error = {
exn: "StopIteration"
};
this.postMessage({fail: error, id: id, durationMs: durationMs});
} else if ("toMsg" in exn) {
// Extension mechanism for exception [de]serialization. We
// assume that any exception with a method `toMsg()` knows how
// to serialize itself. The other side is expected to have
// registered a deserializer using the `ExceptionHandlers`
// object.
this.log("Sending back an error that knows how to serialize itself", exn, "id is", id);
let msg = exn.toMsg();
this.postMessage({fail: msg, id:id, durationMs: durationMs});
} else {
// If we encounter an exception for which we have no
// serialization mechanism in place, we have no choice but to
// let the DOM handle said [de]serialization. We can just
// attempt to mitigate the data loss by injecting `moduleName` and
// `moduleStack`.
this.log("Sending back regular error", exn, exn.moduleStack || exn.stack, "id is", id);
try {
// Attempt to introduce human-readable filename and stack
exn.filename = exn.moduleName;
exn.stack = exn.moduleStack;
} catch (_) {
// Nothing we can do
}
throw exn;
}
}
};
exports.AbstractWorker = AbstractWorker;

View File

@ -0,0 +1,11 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
JS_MODULES_PATH = 'modules/workers'
EXTRA_JS_MODULES = [
'PromiseWorker.js',
]

View File

@ -33,7 +33,7 @@ const THUMBNAIL_DIRECTORY = "thumbnails";
const THUMBNAIL_BG_COLOR = "#fff";
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
@ -792,27 +792,10 @@ let PageThumbsExpiration = {
/**
* Interface to a dedicated thread handling I/O
*/
let PageThumbsWorker = (function() {
let worker = new PromiseWorker("resource://gre/modules/PageThumbsWorker.js",
OS.Shared.LOG.bind("PageThumbs"));
return {
post: function post(...args) {
let promise = worker.post.apply(worker, args);
return promise.then(
null,
function onError(error) {
// Decode any serialized error
if (error instanceof PromiseWorker.WorkerError) {
throw OS.File.Error.fromMsg(error.data);
} else {
throw error;
}
}
);
}
};
})();
let PageThumbsWorker = new BasePromiseWorker("resource://gre/modules/PageThumbsWorker.js");
// As the PageThumbsWorker performs I/O, we can receive instances of
// OS.File.Error, so we need to install a decoder.
PageThumbsWorker.ExceptionHandlers["OS.File.Error"] = OS.File.Error.fromMsg;
let PageThumbsHistoryObserver = {
onDeleteURI: function Thumbnails_onDeleteURI(aURI, aGUID) {

View File

@ -13,43 +13,23 @@
importScripts("resource://gre/modules/osfile.jsm");
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
let File = OS.File;
let Type = OS.Shared.Type;
/**
* Communications with the controller.
*
* Accepts messages:
* {fun:function_name, args:array_of_arguments_or_null}
*
* Sends messages:
* {ok: result} / {fail: serialized_form_of_OS.File.Error}
*/
self.onmessage = function onmessage(msg) {
let data = msg.data;
let id = data.id;
let result;
if (!(data.fun in Agent)) {
throw new Error("Cannot find method " + data.fun);
}
try {
result = Agent[data.fun].apply(Agent, data.args);
} catch (ex if ex instanceof StopIteration) {
// StopIteration cannot be serialized automatically
self.postMessage({StopIteration: true, id: id});
return;
} catch (ex if ex instanceof OS.File.Error) {
// Instances of OS.File.Error know how to serialize themselves
// (deserialization ensures that we end up with OS-specific
// instances of |OS.File.Error|)
self.postMessage({fail: OS.File.Error.toMsg(ex), id:id});
return;
}
// Other exceptions do not, and should be propagated through DOM's
// built-in mechanism for uncaught errors, although this mechanism
// may lose interesting information.
self.postMessage({ok: result, id:id});
let worker = new PromiseWorker.AbstractWorker();
worker.dispatch = function(method, args = []) {
return Agent[method](...args);
};
worker.postMessage = function(message, ...transfers) {
self.postMessage(message, ...transfers);
};
worker.close = function() {
self.close();
};
self.addEventListener("message", msg => worker.handleMessage(msg));
let Agent = {

View File

@ -0,0 +1,62 @@
// -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
"use strict";
/** This module wraps around navigator.battery (https://developer.mozilla.org/en-US/docs/Web/API/Navigator.battery).
* and provides a framework for spoofing battery values in test code.
* To spoof the battery values, set `Debugging.fake = true` after exporting this with a BackstagePass,
* after which you can spoof a property yb setting the relevant property of the Battery object.
*/
this.EXPORTED_SYMBOLS = ["Battery"];
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
// Load Services, for the BatteryManager API
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
// Values for the fake battery. See the documentation of Navigator.battery for the meaning of each field.
let gFakeBattery = {
charging: false,
chargingTime: 0,
dischargingTime: Infinity,
level: 1,
}
// BackendPass-exported object for toggling spoofing
this.Debugging = {
/**
* If `false`, use the DOM Battery implementation.
* Set it to `true` if you need to fake battery values
* for testing or debugging purposes.
*/
fake: false
}
this.Battery = {};
for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
let prop = k;
Object.defineProperty(this.Battery, prop, {
get: function() {
// Return fake value if spoofing is enabled, otherwise fetch the real value from the BatteryManager API
if (Debugging.fake) {
return gFakeBattery[prop];
}
return Services.appShell.hiddenDOMWindow.navigator.battery[prop];
},
set: function(fakeSetting) {
if (!Debugging.fake) {
throw new Error("Tried to set fake battery value when battery spoofing was disabled");
}
gFakeBattery[prop] = fakeSetting;
}
})
}

View File

@ -13,6 +13,7 @@ SPHINX_TREES['toolkit_modules'] = 'docs'
EXTRA_JS_MODULES += [
'AsyncShutdown.jsm',
'Battery.jsm',
'BinarySearch.jsm',
'BrowserUtils.jsm',
'CharsetMenu.jsm',

View File

@ -1,5 +1,6 @@
[DEFAULT]
[browser_Battery.js]
[browser_Deprecated.js]
[browser_Finder.js]
skip-if = e10s # Bug ?????? - test already uses content scripts, but still fails only under e10s.

View File

@ -0,0 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let imported = Components.utils.import("resource://gre/modules/Battery.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
function test() {
is(imported.Debugging.fake, false, "Battery spoofing is initially false")
for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
Assert.throws(() => Battery[k] = 10, "Setting battery " + k + "preference without spoofing enabled should throw");
ok(Battery[k] == Services.appShell.hiddenDOMWindow.navigator.battery[k], "Battery "+ k + " is correctly set");
}
imported.Debugging.fake = true;
Battery.charging = true;
Battery.chargingTime = 100;
Battery.level = 0.5;
ok(Battery.charging, "Test for charging setter");
is(Battery.chargingTime, 100, "Test for chargingTime setter");
is(Battery.level, 0.5, "Test for level setter");
Battery.charging = false;
Battery.dischargingTime = 50;
Battery.level = 0.7;
ok(!Battery.charging, "Test for charging setter");
is(Battery.dischargingTime, 50, "Test for dischargingTime setter");
is(Battery.level, 0.7, "Test for level setter");
}