diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 3bb3ec6d5b..fc265ba4a1 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1273,13 +1273,10 @@ public: nsAutoScriptBlocker scriptBlocker; uint32_t len = mSubtreeRoots.Length(); if (len) { - PRTime start = PR_Now(); for (uint32_t i = 0; i < len; ++i) { UnbindSubtree(mSubtreeRoots[i]); } mSubtreeRoots.Clear(); - Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND, - uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC); } nsCycleCollector_dispatchDeferredDeletion(); if (this == sContentUnbinder) { diff --git a/dom/base/UseCounters.conf b/dom/base/UseCounters.conf index 5b4965921b..998e6dd2c3 100644 --- a/dom/base/UseCounters.conf +++ b/dom/base/UseCounters.conf @@ -18,12 +18,18 @@ // // method . // attribute . -// property +// property +// +// The |CSS property method name| should be identical to the |method| +// argument to CSS_PROP and related macros. The method name is +// identical to the name of the property, except that all hyphens are +// removed and CamelCase naming is used. See nsCSSPropList.h for +// further details. // // To actually cause use counters to be incremented, DOM methods // and attributes must have a [UseCounter] extended attribute in -// the Web IDL file, and CSS properties must be declared with -// the CSS_PROPERTY_HAS_USE_COUNTER flag in nsCSSPropList.h. +// the Web IDL file. CSS properties require no special treatment +// beyond being listed below. // // You might reasonably ask why we have this file and we require // annotating things with [UseCounter] in the relevant WebIDL file as @@ -31,3 +37,8 @@ // dependencies were correct would have been rather difficult, and // annotating the WebIDL files does nothing for identifying CSS // property usage, which we would also like to track. + +method SVGSVGElement.getElementById +attribute SVGSVGElement.currentScale +property Fill +property FillOpacity diff --git a/dom/base/gen-usecounters.py b/dom/base/gen-usecounters.py index 867c3d5103..ee1299a8f5 100644 --- a/dom/base/gen-usecounters.py +++ b/dom/base/gen-usecounters.py @@ -45,7 +45,7 @@ def generate_list(f, counters): print('USE_COUNTER_DOM_ATTRIBUTE(%s, %s)' % (counter['interface_name'], counter['attribute_name']), file=f) elif counter['type'] == 'property': prop = counter['property_name'] - print('USE_COUNTER_CSS_PROPERTY(%s, %s)' % (prop, prop.replace('-', '_')), file=f) + print('USE_COUNTER_CSS_PROPERTY(%s, %s)' % (prop, prop), file=f) print_optional_macro_undeclare('USE_COUNTER_DOM_METHOD') print_optional_macro_undeclare('USE_COUNTER_DOM_ATTRIBUTE') @@ -55,19 +55,20 @@ def generate_property_map(f, counters): print(AUTOGENERATED_WARNING_COMMENT, file=f) print(''' enum { - // XXX is this the right define? + #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_ #define CSS_PROP_LIST_INCLUDE_LOGICAL #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \\ kwtable_, stylestruct_, stylestructoffset_, animtype_) \\ - USE_COUNTER_FOR_CSS_PROPERTY_##id_ = eUseCounter_UNKNOWN, + USE_COUNTER_FOR_CSS_PROPERTY_##method_ = eUseCounter_UNKNOWN, #include "nsCSSPropList.h" #undef CSS_PROP #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP_PUBLIC_OR_PRIVATE }; ''', file=f) for counter in counters: if counter['type'] == 'property': - prop = counter['property_name'].replace('-', '_') + prop = counter['property_name'] print('#define USE_COUNTER_FOR_CSS_PROPERTY_%s eUseCounter_property_%s' % (prop, prop), file=f) def use_counter_list(output_header, conf_filename): diff --git a/dom/base/usecounters.py b/dom/base/usecounters.py index bbdbf2761a..86334627f8 100644 --- a/dom/base/usecounters.py +++ b/dom/base/usecounters.py @@ -41,7 +41,7 @@ def read_conf(conf_filename): 'interface_name': interface_name, 'attribute_name': attribute_name } continue - m = re.match(r'property ([a-z0-9-]+)$', line) + m = re.match(r'property ([A-Za-z0-9]+)$', line) if m: property_name = m.group(1) yield { 'type': 'property', diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index 216c8adafa..e31a963f35 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -1187,9 +1187,27 @@ MappedAttrParser::ParseMappedAttrValue(nsIAtom* aMappedAttrName, nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName), nsCSSProps::eEnabledForAllContent); if (propertyID != eCSSProperty_UNKNOWN) { - bool changed; // outparam for ParseProperty. (ignored) + bool changed = false; // outparam for ParseProperty. mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI, mElement->NodePrincipal(), mDecl, &changed, false, true); + if (changed) { + // The normal reporting of use counters by the nsCSSParser won't happen + // since it doesn't have a sheet. + if (nsCSSProps::IsShorthand(propertyID)) { + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, propertyID, + nsCSSProps::eEnabledForAllContent) { + UseCounter useCounter = nsCSSProps::UseCounterFor(*subprop); + if (useCounter != eUseCounter_UNKNOWN) { + mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); + } + } + } else { + UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID); + if (useCounter != eUseCounter_UNKNOWN) { + mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); + } + } + } return; } MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang, diff --git a/dom/webidl/SVGSVGElement.webidl b/dom/webidl/SVGSVGElement.webidl index 87818ecc7f..639c897bff 100644 --- a/dom/webidl/SVGSVGElement.webidl +++ b/dom/webidl/SVGSVGElement.webidl @@ -33,6 +33,7 @@ interface SVGSVGElement : SVGGraphicsElement { readonly attribute float screenPixelToMillimeterY; readonly attribute boolean useCurrentView; // readonly attribute SVGViewSpec currentView; + [UseCounter] attribute float currentScale; readonly attribute SVGPoint currentTranslate; @@ -70,6 +71,7 @@ interface SVGSVGElement : SVGGraphicsElement { SVGTransform createSVGTransform(); [NewObject] SVGTransform createSVGTransformFromMatrix(SVGMatrix matrix); + [UseCounter] Element? getElementById(DOMString elementId); }; diff --git a/ipc/glue/MessagePump.cpp b/ipc/glue/MessagePump.cpp index 0bbf28c57a..45fe96d81b 100644 --- a/ipc/glue/MessagePump.cpp +++ b/ipc/glue/MessagePump.cpp @@ -23,10 +23,6 @@ #include "nsXULAppAPI.h" #include "prthread.h" -#ifdef MOZ_WIDGET_ANDROID -#include "AndroidBridge.h" -#endif - #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #endif @@ -111,15 +107,6 @@ MessagePump::Run(MessagePump::Delegate* aDelegate) // equal priority, we sensitively rely on processing exactly one // Task per DoWorkRunnable XPCOM event. -#ifdef MOZ_WIDGET_ANDROID - // This processes messages in the Android Looper. Note that we only - // get here if the normal Gecko event loop has been awoken above. - // Bug 750713 - if (MOZ_LIKELY(AndroidBridge::HasEnv())) { - did_work |= mozilla::widget::GeckoAppShell::PumpMessageLoop(); - } -#endif - did_work |= aDelegate->DoDelayedWork(&delayed_work_time_); if (did_work && delayed_work_time_.is_null() diff --git a/js/xpconnect/src/ExportHelpers.cpp b/js/xpconnect/src/ExportHelpers.cpp index 387c958ca4..e7d462aa22 100644 --- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -271,7 +271,17 @@ StackScopedClone(JSContext* cx, StackScopedCloneOptions& options, } // Now recreate the clones in the target compartment. - return buffer.read(cx, val, &gStackScopedCloneCallbacks, &data); + if (!buffer.read(cx, val, &gStackScopedCloneCallbacks, &data)) + return false; + + // Deep-freeze if requested. + if (options.deepFreeze && val.isObject()) { + RootedObject obj(cx, &val.toObject()); + if (!JS_DeepFreezeObject(cx, obj)) + return false; + } + + return true; } // Note - This function mirrors the logic of CheckPassToChrome in diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 645913b66d..4b620e41f7 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1029,8 +1029,9 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin if (!sandbox) return NS_ERROR_FAILURE; - CompartmentPrivate::Get(sandbox)->writeToGlobalPrototype = - options.writeToGlobalPrototype; + CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox); + priv->allowWaivers = options.allowWaivers; + priv->writeToGlobalPrototype = options.writeToGlobalPrototype; // Set up the wantXrays flag, which indicates whether xrays are desired even // for same-origin access. @@ -1041,7 +1042,7 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin // Arguably we should just flip the default for chrome and still honor the // flag, but such a change would break code in subtle ways for minimal // benefit. So we just switch it off here. - CompartmentPrivate::Get(sandbox)->wantXrays = + priv->wantXrays = AccessCheck::isChrome(sandbox) ? false : options.wantXrays; { @@ -1480,6 +1481,7 @@ SandboxOptions::Parse() { bool ok = ParseObject("sandboxPrototype", &proto) && ParseBoolean("wantXrays", &wantXrays) && + ParseBoolean("allowWaivers", &allowWaivers) && ParseBoolean("wantComponents", &wantComponents) && ParseBoolean("wantExportHelpers", &wantExportHelpers) && ParseString("sandboxName", sandboxName) && diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index d22d09ab5b..fc1b54624b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -563,24 +563,6 @@ void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc) static_cast(e)->TraceJS(trc); } -// static -void -XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative* wrapper, - nsCycleCollectionNoteRootCallback& cb) -{ - if (!wrapper->IsValid() || wrapper->IsWrapperExpired()) - return; - - MOZ_ASSERT(NS_IsMainThread(), - "Suspecting wrapped natives from non-main thread"); - - // Only record objects that might be part of a cycle as roots, unless - // the callback wants all traces (a debug feature). - JSObject* obj = wrapper->GetFlatJSObjectPreserveColor(); - if (JS::ObjectIsMarkedGray(obj) || cb.WantAllTraces()) - cb.NoteJSRoot(obj); -} - void XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) { diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index dbc3d0fcd4..39c91e22ad 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -607,7 +607,7 @@ nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx, uint16_t methodIndex, uint8_t paramIndex, nsXPTCMiniVariant* nativeParams, - uint32_t* result) + uint32_t* result) const { uint8_t argnum; nsresult rv; @@ -638,7 +638,7 @@ nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx, uint16_t methodIndex, const nsXPTType& type, nsXPTCMiniVariant* nativeParams, - nsID* result) + nsID* result) const { uint8_t type_tag = type.TagPart(); @@ -676,7 +676,7 @@ nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx, return false; } -void +/* static */ void nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type, uint32_t array_count, void** arrayp) @@ -696,7 +696,7 @@ nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type, } } -void +/* static */ void nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type, void** pp) { @@ -710,6 +710,56 @@ nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type, } } +void +nsXPCWrappedJSClass::CleanupOutparams(JSContext* cx, uint16_t methodIndex, + const nsXPTMethodInfo* info, nsXPTCMiniVariant* nativeParams, + bool inOutOnly, uint8_t n) const +{ + // clean up any 'out' params handed in + for (uint8_t i = 0; i < n; i++) { + const nsXPTParamInfo& param = info->params[i]; + if (!param.IsOut()) + continue; + + const nsXPTType& type = param.GetType(); + if (!type.deprecated_IsPointer()) + continue; + void* p = nativeParams[i].val.p; + if (!p) + continue; + + // The inOutOnly flag was introduced when consolidating two very + // similar code paths in CallMethod in bug 1175513. I don't know + // if and why the difference is necessary. + if (!inOutOnly || param.IsIn()) { + if (type.IsArray()) { + void** pp = *static_cast(p); + if (pp) { + // we need to get the array length and iterate the items + uint32_t array_count; + nsXPTType datum_type; + + if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, + 1, &datum_type)) && + datum_type.deprecated_IsPointer() && + GetArraySizeFromParam(cx, info, param, methodIndex, + i, nativeParams, &array_count) && + array_count) { + + CleanupPointerArray(datum_type, array_count, pp); + } + + // always release the array if it is inout + free(pp); + } + } else { + CleanupPointerTypeObject(type, static_cast(p)); + } + } + *static_cast(p) = nullptr; + } +} + nsresult nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, const char * aPropertyName, @@ -1148,45 +1198,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, pre_call_clean_up: // clean up any 'out' params handed in - for (i = 0; i < paramCount; i++) { - const nsXPTParamInfo& param = info->params[i]; - if (!param.IsOut()) - continue; - - const nsXPTType& type = param.GetType(); - if (!type.deprecated_IsPointer()) - continue; - void* p; - if (!(p = nativeParams[i].val.p)) - continue; - - if (param.IsIn()) { - if (type.IsArray()) { - void** pp; - if (nullptr != (pp = *((void***)p))) { - - // we need to get the array length and iterate the items - uint32_t array_count; - nsXPTType datum_type; - - if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, - 1, &datum_type)) && - datum_type.deprecated_IsPointer() && - GetArraySizeFromParam(cx, info, param, methodIndex, - i, nativeParams, &array_count) && - array_count) { - - CleanupPointerArray(datum_type, array_count, pp); - } - - // always release the array if it is inout - free(pp); - } - } else - CleanupPointerTypeObject(type, (void**)p); - } - *((void**)p) = nullptr; - } + CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ true, paramCount); // Make sure "this" doesn't get deleted during this call. nsCOMPtr kungFuDeathGrip(this); @@ -1392,40 +1404,7 @@ pre_call_clean_up: if (i != paramCount) { // We didn't manage all the result conversions! // We have to cleanup any junk that *did* get converted. - - for (uint8_t k = 0; k < i; k++) { - const nsXPTParamInfo& param = info->params[k]; - if (!param.IsOut()) - continue; - const nsXPTType& type = param.GetType(); - if (!type.deprecated_IsPointer()) - continue; - void* p; - if (!(p = nativeParams[k].val.p)) - continue; - - if (type.IsArray()) { - void** pp; - if (nullptr != (pp = *((void***)p))) { - // we need to get the array length and iterate the items - uint32_t array_count; - nsXPTType datum_type; - - if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, - 1, &datum_type)) && - datum_type.deprecated_IsPointer() && - GetArraySizeFromParam(cx, info, param, methodIndex, - k, nativeParams, &array_count) && - array_count) { - - CleanupPointerArray(datum_type, array_count, pp); - } - free(pp); - } - } else - CleanupPointerTypeObject(type, (void**)p); - *((void**)p) = nullptr; - } + CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ false, i); } else { // set to whatever the JS code might have set as the result retval = xpcc->GetPendingResult(); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index e822d6db42..a7420b55f1 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -17,6 +17,7 @@ #include "XrayWrapper.h" #include "nsContentUtils.h" +#include "nsCycleCollectionNoteRootCallback.h" #include #include "mozilla/DeferredFinalize.h" @@ -67,7 +68,7 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get()) } - if (tmp->mRefCnt.get() > 1) { + if (tmp->HasExternalReference()) { // If our refcount is > 1, our reference to the flat JS object is // considered "strong", and we're going to traverse it. @@ -93,6 +94,25 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse return NS_OK; } +void +XPCWrappedNative::Suspect(nsCycleCollectionNoteRootCallback& cb) +{ + if (!IsValid() || IsWrapperExpired()) + return; + + MOZ_ASSERT(NS_IsMainThread(), + "Suspecting wrapped natives from non-main thread"); + + // Only record objects that might be part of a cycle as roots, unless + // the callback wants all traces (a debug feature). Do this even if + // the XPCWN doesn't own the JS reflector object in case the reflector + // keeps alive other C++ things. This is safe because if the reflector + // had died the reference from the XPCWN to it would have been cleared. + JSObject* obj = GetFlatJSObjectPreserveColor(); + if (JS::ObjectIsMarkedGray(obj) || cb.WantAllTraces()) + cb.NoteJSRoot(obj); +} + void XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb) { diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index 42686ef0a9..c2faf5b1ca 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -503,10 +503,7 @@ XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt, { for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) { - auto entry = static_cast(i.Get()); - XPCWrappedNative* wrapper = entry->value; - if (wrapper->HasExternalReference()) - XPCJSRuntime::SuspectWrappedNative(wrapper, cb); + static_cast(i.Get())->value->Suspect(cb); } if (cur->mDOMExpandoSet) { diff --git a/js/xpconnect/src/XPCWrapper.cpp b/js/xpconnect/src/XPCWrapper.cpp index 7de8081183..a6b331017f 100644 --- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -33,16 +33,14 @@ UnwrapNW(JSContext* cx, unsigned argc, Value* vp) } JS::RootedValue v(cx, args[0]); - if (!v.isObject() || !js::IsWrapper(&v.toObject())) { + if (!v.isObject() || !js::IsCrossCompartmentWrapper(&v.toObject()) || + !WrapperFactory::AllowWaiver(&v.toObject())) { args.rval().set(v); return true; } - if (AccessCheck::wrapperSubsumes(&v.toObject())) { - bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); - NS_ENSURE_TRUE(ok, false); - } - + bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); + NS_ENSURE_TRUE(ok, false); args.rval().set(v); return true; } diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp index a7e7360c6a..e8a0366145 100644 --- a/js/xpconnect/src/nsScriptError.cpp +++ b/js/xpconnect/src/nsScriptError.cpp @@ -18,9 +18,7 @@ #include "nsILoadContext.h" #include "nsIDocShell.h" -NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError) - -nsScriptError::nsScriptError() +nsScriptErrorBase::nsScriptErrorBase() : mMessage(), mSourceName(), mLineNumber(0), @@ -36,10 +34,10 @@ nsScriptError::nsScriptError() { } -nsScriptError::~nsScriptError() {} +nsScriptErrorBase::~nsScriptErrorBase() {} void -nsScriptError::InitializeOnMainThread() +nsScriptErrorBase::InitializeOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mInitializedOnMainThread); @@ -70,7 +68,7 @@ nsScriptError::InitializeOnMainThread() // nsIConsoleMessage methods NS_IMETHODIMP -nsScriptError::GetMessageMoz(char16_t** result) { +nsScriptErrorBase::GetMessageMoz(char16_t** result) { nsresult rv; nsAutoCString message; @@ -87,7 +85,7 @@ nsScriptError::GetMessageMoz(char16_t** result) { NS_IMETHODIMP -nsScriptError::GetLogLevel(uint32_t* aLogLevel) +nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel) { if (mFlags & (uint32_t)nsIScriptError::infoFlag) { *aLogLevel = nsIConsoleMessage::info; @@ -101,66 +99,66 @@ nsScriptError::GetLogLevel(uint32_t* aLogLevel) // nsIScriptError methods NS_IMETHODIMP -nsScriptError::GetErrorMessage(nsAString& aResult) { +nsScriptErrorBase::GetErrorMessage(nsAString& aResult) { aResult.Assign(mMessage); return NS_OK; } NS_IMETHODIMP -nsScriptError::GetSourceName(nsAString& aResult) { +nsScriptErrorBase::GetSourceName(nsAString& aResult) { aResult.Assign(mSourceName); return NS_OK; } NS_IMETHODIMP -nsScriptError::GetSourceLine(nsAString& aResult) { +nsScriptErrorBase::GetSourceLine(nsAString& aResult) { aResult.Assign(mSourceLine); return NS_OK; } NS_IMETHODIMP -nsScriptError::GetLineNumber(uint32_t* result) { +nsScriptErrorBase::GetLineNumber(uint32_t* result) { *result = mLineNumber; return NS_OK; } NS_IMETHODIMP -nsScriptError::GetColumnNumber(uint32_t* result) { +nsScriptErrorBase::GetColumnNumber(uint32_t* result) { *result = mColumnNumber; return NS_OK; } NS_IMETHODIMP -nsScriptError::GetFlags(uint32_t* result) { +nsScriptErrorBase::GetFlags(uint32_t* result) { *result = mFlags; return NS_OK; } NS_IMETHODIMP -nsScriptError::GetCategory(char** result) { +nsScriptErrorBase::GetCategory(char** result) { *result = ToNewCString(mCategory); return NS_OK; } NS_IMETHODIMP -nsScriptError::GetStack(JS::MutableHandleValue aStack) { +nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) { aStack.setUndefined(); return NS_OK; } NS_IMETHODIMP -nsScriptError::SetStack(JS::HandleValue aStack) { +nsScriptErrorBase::SetStack(JS::HandleValue aStack) { return NS_OK; } NS_IMETHODIMP -nsScriptError::Init(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const char* category) +nsScriptErrorBase::Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) { return InitWithWindowID(message, sourceName, sourceLine, lineNumber, columnNumber, flags, @@ -170,14 +168,14 @@ nsScriptError::Init(const nsAString& message, } NS_IMETHODIMP -nsScriptError::InitWithWindowID(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const nsACString& category, - uint64_t aInnerWindowID) +nsScriptErrorBase::InitWithWindowID(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const nsACString& category, + uint64_t aInnerWindowID) { mMessage.Assign(message); mSourceName.Assign(sourceName); @@ -197,7 +195,7 @@ nsScriptError::InitWithWindowID(const nsAString& message, } NS_IMETHODIMP -nsScriptError::ToString(nsACString& /*UTF8*/ aResult) +nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult) { static const char format0[] = "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]"; @@ -258,7 +256,7 @@ nsScriptError::ToString(nsACString& /*UTF8*/ aResult) } NS_IMETHODIMP -nsScriptError::GetOuterWindowID(uint64_t* aOuterWindowID) +nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID) { NS_WARN_IF_FALSE(NS_IsMainThread() || mInitializedOnMainThread, "This can't be safely determined off the main thread, " @@ -273,21 +271,21 @@ nsScriptError::GetOuterWindowID(uint64_t* aOuterWindowID) } NS_IMETHODIMP -nsScriptError::GetInnerWindowID(uint64_t* aInnerWindowID) +nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID) { *aInnerWindowID = mInnerWindowID; return NS_OK; } NS_IMETHODIMP -nsScriptError::GetTimeStamp(int64_t* aTimeStamp) +nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp) { *aTimeStamp = mTimeStamp; return NS_OK; } NS_IMETHODIMP -nsScriptError::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) +nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) { NS_WARN_IF_FALSE(NS_IsMainThread() || mInitializedOnMainThread, "This can't be safely determined off the main thread, " @@ -300,3 +298,5 @@ nsScriptError::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) *aIsFromPrivateWindow = mIsFromPrivateWindow; return NS_OK; } + +NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError) diff --git a/js/xpconnect/src/nsScriptErrorWithStack.cpp b/js/xpconnect/src/nsScriptErrorWithStack.cpp index aacb20a464..a5717bbc8b 100644 --- a/js/xpconnect/src/nsScriptErrorWithStack.cpp +++ b/js/xpconnect/src/nsScriptErrorWithStack.cpp @@ -6,7 +6,7 @@ /* * nsScriptErrorWithStack implementation. - * a main-thread-only, cycle-collected subclass of nsScriptError + * a main-thread-only, cycle-collected subclass of nsScriptErrorBase * that can store a SavedFrame stack trace object. */ @@ -40,9 +40,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack) NS_INTERFACE_MAP_END nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack) - : nsScriptError(), - mStack(aStack) + : mStack(aStack) { + MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers."); mozilla::HoldJSObjects(this); } diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 3b0d0e958e..9e7a2ef803 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -39,7 +39,6 @@ using namespace JS; NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect, - nsISupportsWeakReference, nsIThreadObserver) nsXPConnect* nsXPConnect::gSelf = nullptr; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index c13ba7d19e..b0f3bac75a 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -247,8 +247,7 @@ static inline bool IS_WN_REFLECTOR(JSObject* obj) // to this rule are noted explicitly. class nsXPConnect final : public nsIXPConnect, - public nsIThreadObserver, - public nsSupportsWeakReference + public nsIThreadObserver { public: // all the interface method declarations... @@ -587,9 +586,6 @@ public: inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS); inline void AddObjectHolderRoot(XPCJSObjectHolder* holder); - static void SuspectWrappedNative(XPCWrappedNative* wrapper, - nsCycleCollectionNoteRootCallback& cb); - void DebugDump(int16_t depth); void SystemIsBeingShutDown(); @@ -2252,6 +2248,7 @@ public: bool HasExternalReference() const {return mRefCnt > 1;} + void Suspect(nsCycleCollectionNoteRootCallback& cb); void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); // Make ctor and dtor protected (rather than private) to placate nsCOMPtr. @@ -2398,7 +2395,7 @@ private: uint16_t methodIndex, uint8_t paramIndex, nsXPTCMiniVariant* params, - uint32_t* result); + uint32_t* result) const; bool GetInterfaceTypeFromParam(JSContext* cx, const XPTMethodDescriptor* method, @@ -2406,14 +2403,17 @@ private: uint16_t methodIndex, const nsXPTType& type, nsXPTCMiniVariant* params, - nsID* result); + nsID* result) const; - void CleanupPointerArray(const nsXPTType& datum_type, - uint32_t array_count, - void** arrayp); + static void CleanupPointerArray(const nsXPTType& datum_type, + uint32_t array_count, + void** arrayp); - void CleanupPointerTypeObject(const nsXPTType& type, - void** pp); + static void CleanupPointerTypeObject(const nsXPTType& type, + void** pp); + + void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info, + nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const; private: XPCJSRuntime* mRuntime; @@ -2993,18 +2993,17 @@ xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals, // Definition of nsScriptError, defined here because we lack a place to put // XPCOM objects associated with the JavaScript engine. -class nsScriptError : public nsIScriptError { +class nsScriptErrorBase : public nsIScriptError { public: - nsScriptError(); + nsScriptErrorBase(); // TODO - do something reasonable on getting null from these babies. - NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICONSOLEMESSAGE NS_DECL_NSISCRIPTERROR protected: - virtual ~nsScriptError(); + virtual ~nsScriptErrorBase(); void InitializeOnMainThread(); @@ -3026,7 +3025,16 @@ protected: bool mIsFromPrivateWindow; }; -class nsScriptErrorWithStack : public nsScriptError { +class nsScriptError final : public nsScriptErrorBase { +public: + nsScriptError() {} + NS_DECL_THREADSAFE_ISUPPORTS + +private: + virtual ~nsScriptError() {} +}; + +class nsScriptErrorWithStack : public nsScriptErrorBase { public: explicit nsScriptErrorWithStack(JS::HandleObject); @@ -3470,6 +3478,7 @@ public: JSObject* options = nullptr) : OptionsBase(cx, options) , wantXrays(true) + , allowWaivers(true) , wantComponents(true) , wantExportHelpers(false) , proto(cx) @@ -3485,6 +3494,7 @@ public: virtual bool Parse(); bool wantXrays; + bool allowWaivers; bool wantComponents; bool wantExportHelpers; JS::RootedObject proto; @@ -3569,11 +3579,13 @@ public: : OptionsBase(cx, options) , wrapReflectors(false) , cloneFunctions(false) + , deepFreeze(false) { } virtual bool Parse() { return ParseBoolean("wrapReflectors", &wrapReflectors) && - ParseBoolean("cloneFunctions", &cloneFunctions); + ParseBoolean("cloneFunctions", &cloneFunctions) && + ParseBoolean("deepFreeze", &deepFreeze); } // When a reflector is encountered, wrap it rather than aborting the clone. @@ -3582,6 +3594,9 @@ public: // When a function is encountered, clone it (exportFunction-style) rather than // aborting the clone. bool cloneFunctions; + + // If true, the resulting object is deep-frozen after being cloned. + bool deepFreeze; }; JSObject* @@ -3681,6 +3696,7 @@ public: explicit CompartmentPrivate(JSCompartment* c) : wantXrays(false) + , allowWaivers(true) , writeToGlobalPrototype(false) , skipWriteToGlobalPrototype(false) , universalXPConnectEnabled(false) @@ -3708,9 +3724,17 @@ public: return Get(compartment); } - + // Controls whether this compartment gets Xrays to same-origin. This behavior + // is deprecated, but is still the default for sandboxes for compatibity + // reasons. bool wantXrays; + // Controls whether this compartment is allowed to waive Xrays to content + // that it subsumes. This should generally be true, except in cases where we + // want to prevent code from depending on Xray Waivers (which might make it + // more portable to other browser architectures). + bool allowWaivers; + // This flag is intended for a very specific use, internal to Gecko. It may // go away or change behavior at any time. It should not be added to any // documentation and it should not be used without consulting the XPConnect diff --git a/js/xpconnect/tests/unit/test_allowWaivers.js b/js/xpconnect/tests/unit/test_allowWaivers.js new file mode 100644 index 0000000000..66f70694a3 --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowWaivers.js @@ -0,0 +1,30 @@ +const Cu = Components.utils; +function checkWaivers(from, allowed) { + var sb = new Cu.Sandbox('http://example.com'); + from.test = sb.eval('var o = {prop: 2, f: function() {return 42;}}; o'); + + // Make sure that |from| has Xrays to sb. + do_check_eq(from.eval('test.prop'), 2); + do_check_eq(from.eval('test.f'), undefined); + + // Make sure that waivability works as expected. + do_check_eq(from.eval('!!test.wrappedJSObject'), allowed); + do_check_eq(from.eval('XPCNativeWrapper.unwrap(test) !== test'), allowed); + + // Make a sandbox with the same principal as |from|, but without any waiver + // restrictions, and make sure that the waiver does not transfer. + var friend = new Cu.Sandbox(Cu.getObjectPrincipal(from)); + friend.test = from.test; + friend.eval('var waived = test.wrappedJSObject;'); + do_check_true(friend.eval('waived.f()'), 42); + friend.from = from; + friend.eval('from.waived = waived'); + do_check_eq(from.eval('!!waived.f'), allowed); +} + +function run_test() { + checkWaivers(new Cu.Sandbox('http://example.com'), true); + checkWaivers(new Cu.Sandbox('http://example.com', {allowWaivers: false}), false); + checkWaivers(new Cu.Sandbox(['http://example.com']), true); + checkWaivers(new Cu.Sandbox(['http://example.com'], {allowWaivers: false}), false); +} diff --git a/js/xpconnect/tests/unit/test_deepFreezeClone.js b/js/xpconnect/tests/unit/test_deepFreezeClone.js new file mode 100644 index 0000000000..67ea4e66df --- /dev/null +++ b/js/xpconnect/tests/unit/test_deepFreezeClone.js @@ -0,0 +1,33 @@ +const Cu = Components.utils; + +function checkThrows(f, rgxp) { try { f(); do_check_false(); } catch (e) { do_check_true(rgxp.test(e)); } } + +var o = { foo: 42, bar : { tick: 'tock' } }; +function checkClone(clone, frozen) { + var waived = Cu.waiveXrays(clone); + function touchFoo() { "use strict"; waived.foo = 12; do_check_eq(waived.foo, 12); } + function touchBar() { "use strict"; waived.bar.tick = 'tack'; do_check_eq(waived.bar.tick, 'tack'); } + function addProp() { "use strict"; waived.newProp = 100; do_check_eq(waived.newProp, 100); } + if (!frozen) { + touchFoo(); + touchBar(); + addProp(); + } else { + checkThrows(touchFoo, /read-only/); + checkThrows(touchBar, /read-only/); + checkThrows(addProp, /extensible/); + } + + var desc = Object.getOwnPropertyDescriptor(waived, 'foo'); + do_check_eq(desc.writable, !frozen); + do_check_eq(desc.configurable, !frozen); + desc = Object.getOwnPropertyDescriptor(waived.bar, 'tick'); + do_check_eq(desc.writable, !frozen); + do_check_eq(desc.configurable, !frozen); +} + +function run_test() { + var sb = new Cu.Sandbox(null); + checkClone(Cu.waiveXrays(Cu.cloneInto(o, sb)), false); + checkClone(Cu.cloneInto(o, sb, { deepFreeze: true }), true); +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 78ffc4b8c9..cbe82fdf9f 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -18,6 +18,7 @@ support-files = recursive_importB.jsm syntax_error.jsm +[test_allowWaivers.js] [test_bogus_files.js] [test_bug408412.js] [test_bug451678.js] @@ -61,6 +62,7 @@ support-files = [test_bug1170311.js] [test_bug_442086.js] [test_callFunctionWithAsyncStack.js] +[test_deepFreezeClone.js] [test_file.js] [test_blob.js] [test_blob2.js] diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 8e6da86bd3..942b52d9e1 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -106,6 +106,20 @@ WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg) return CreateXrayWaiver(cx, obj); } +/* static */ bool +WrapperFactory::AllowWaiver(JSCompartment* target, JSCompartment* origin) +{ + return CompartmentPrivate::Get(target)->allowWaivers && + AccessCheck::subsumes(target, origin); +} + +/* static */ bool +WrapperFactory::AllowWaiver(JSObject* wrapper) { + MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper)); + return AllowWaiver(js::GetObjectCompartment(wrapper), + js::GetObjectCompartment(js::UncheckedUnwrap(wrapper))); +} + inline bool ShouldWaiveXray(JSContext* cx, JSObject* originalObj) { @@ -468,8 +482,10 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security - // wrappers. - bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj); + // wrappers (unless explicitly forbidden from doing so). + bool waiveXrays = wantXrays && !securityWrapper && + CompartmentPrivate::Get(target)->allowWaivers && + HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. @@ -542,7 +558,7 @@ WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleObject argObj) // to things in |obj|'s compartment. JSCompartment* target = js::GetContextCompartment(cx); JSCompartment* origin = js::GetObjectCompartment(obj); - obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; + obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj; if (!obj) return false; diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index 3609472edc..515d6bf054 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -37,6 +37,13 @@ class WrapperFactory { static JSObject* CreateXrayWaiver(JSContext* cx, JS::HandleObject obj); static JSObject* WaiveXray(JSContext* cx, JSObject* obj); + // Computes whether we should allow the creation of an Xray waiver from + // |target| to |origin|. + static bool AllowWaiver(JSCompartment* target, JSCompartment* origin); + + // Convenience method for the above, operating on a wrapper. + static bool AllowWaiver(JSObject* wrapper); + // Prepare a given object for wrapping in a new compartment. static JSObject* PrepareForWrapping(JSContext* cx, JS::HandleObject scope, diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 033acbb778..80b8775ca1 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1318,7 +1318,7 @@ wrappedJSObject_getter(JSContext* cx, unsigned argc, Value* vp) } RootedObject wrapper(cx, &args.thisv().toObject()); if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) || - !AccessCheck::wrapperSubsumes(wrapper)) { + !WrapperFactory::AllowWaiver(wrapper)) { JS_ReportError(cx, "Unexpected object"); return false; } @@ -1381,7 +1381,7 @@ XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, // Handle .wrappedJSObject for subsuming callers. This should move once we // sort out own-ness for the holder. if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && - AccessCheck::wrapperSubsumes(wrapper)) + WrapperFactory::AllowWaiver(wrapper)) { if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) return false; diff --git a/layout/style/nsCSSDataBlock.cpp b/layout/style/nsCSSDataBlock.cpp index e2851fd37f..9d63be15fc 100644 --- a/layout/style/nsCSSDataBlock.cpp +++ b/layout/style/nsCSSDataBlock.cpp @@ -614,12 +614,14 @@ nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, bool aIsImportant, bool aOverrideImportant, bool aMustCallValueAppended, - css::Declaration* aDeclaration) + css::Declaration* aDeclaration, + nsIDocument* aSheetDocument) { if (!nsCSSProps::IsShorthand(aPropID)) { return DoTransferFromBlock(aFromBlock, aPropID, aIsImportant, aOverrideImportant, - aMustCallValueAppended, aDeclaration); + aMustCallValueAppended, aDeclaration, + aSheetDocument); } // We can pass eIgnoreEnabledState (here, and in ClearProperty above) rather @@ -631,7 +633,8 @@ nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, aEnabledState) { changed |= DoTransferFromBlock(aFromBlock, *p, aIsImportant, aOverrideImportant, - aMustCallValueAppended, aDeclaration); + aMustCallValueAppended, aDeclaration, + aSheetDocument); } return changed; } @@ -642,7 +645,8 @@ nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, bool aIsImportant, bool aOverrideImportant, bool aMustCallValueAppended, - css::Declaration* aDeclaration) + css::Declaration* aDeclaration, + nsIDocument* aSheetDocument) { bool changed = false; MOZ_ASSERT(aFromBlock.HasPropertyBit(aPropID), "oops"); @@ -670,6 +674,13 @@ nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, aDeclaration->ValueAppended(aPropID); } + if (aSheetDocument) { + UseCounter useCounter = nsCSSProps::UseCounterFor(aPropID); + if (useCounter != eUseCounter_UNKNOWN) { + aSheetDocument->SetDocumentAndPageUseCounter(useCounter); + } + } + SetPropertyBit(aPropID); aFromBlock.ClearPropertyBit(aPropID); diff --git a/layout/style/nsCSSDataBlock.h b/layout/style/nsCSSDataBlock.h index fcc562f968..c8067fe00b 100644 --- a/layout/style/nsCSSDataBlock.h +++ b/layout/style/nsCSSDataBlock.h @@ -19,6 +19,7 @@ struct nsRuleData; class nsCSSExpandedDataBlock; +class nsIDocument; namespace mozilla { namespace css { @@ -249,6 +250,8 @@ public: * Returns true if something changed, false otherwise. Calls * |ValueAppended| on |aDeclaration| if the property was not * previously set, or in any case if |aMustCallValueAppended| is true. + * Calls |SetDocumentAndPageUseCounter| on |aSheetDocument| if it is + * non-null and |aPropID| has a use counter. */ bool TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, nsCSSProperty aPropID, @@ -256,7 +259,8 @@ public: bool aIsImportant, bool aOverrideImportant, bool aMustCallValueAppended, - mozilla::css::Declaration* aDeclaration); + mozilla::css::Declaration* aDeclaration, + nsIDocument* aSheetDocument); /** * Copies the values for aPropID into the specified aRuleData object. @@ -290,7 +294,8 @@ private: bool aIsImportant, bool aOverrideImportant, bool aMustCallValueAppended, - mozilla::css::Declaration* aDeclaration); + mozilla::css::Declaration* aDeclaration, + nsIDocument* aSheetDocument); #ifdef DEBUG void DoAssertInitialState(); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 2387f909c2..3be37cc349 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -111,6 +111,8 @@ public: nsresult SetStyleSheet(CSSStyleSheet* aSheet); + nsIDocument* GetDocument(); + nsresult SetQuirkMode(bool aQuirkMode); nsresult SetChildLoader(mozilla::css::Loader* aChildLoader); @@ -1341,6 +1343,15 @@ CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet) return NS_OK; } +nsIDocument* +CSSParserImpl::GetDocument() +{ + if (!mSheet) { + return nullptr; + } + return mSheet->GetDocument(); +} + nsresult CSSParserImpl::SetQuirkMode(bool aQuirkMode) { @@ -1693,7 +1704,8 @@ CSSParserImpl::ParseProperty(const nsCSSProperty aPropID, aDeclaration->ExpandTo(&mData); *aChanged = mData.TransferFromBlock(mTempData, aPropID, PropertyEnabledState(), aIsImportant, - true, false, aDeclaration); + true, false, aDeclaration, + GetDocument()); aDeclaration->CompressFrom(&mData); } CLEAR_ERROR(); @@ -6906,7 +6918,7 @@ CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration, PropertyEnabledState(), status == ePriority_Important, false, aMustCallValueAppended, - aDeclaration); + aDeclaration, GetDocument()); } return true; diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 5a7c377c8c..f39176dfc0 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2989,6 +2989,21 @@ nsCSSProps::gPropertyEnabled[eCSSProperty_COUNT_with_aliases] = { #undef CSS_PROP_ALIAS }; +#include "../../dom/base/PropertyUseCounterMap.inc" + +/* static */ const UseCounter +nsCSSProps::gPropertyUseCounter[eCSSProperty_COUNT_no_shorthands] = { + #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_ + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + static_cast(USE_COUNTER_FOR_CSS_PROPERTY_##method_), + #include "nsCSSPropList.h" + #undef CSS_PROP + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP_PUBLIC_OR_PRIVATE +}; + // Check that all logical property flags are used appropriately. #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ kwtable_, stylestruct_, stylestructoffset_, animtype_) \ diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 9f15fef5bf..56cfee82e7 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -15,6 +15,7 @@ #include "nsCSSProperty.h" #include "nsStyleStructFwd.h" #include "nsCSSKeywords.h" +#include "mozilla/UseCounter.h" // Length of the "--" prefix on custom names (such as custom property names, // and, in the future, custom media query names). @@ -516,6 +517,20 @@ public: return gPropertyEnabled[aProperty]; } +private: + // A table for the use counter associated with each CSS property. If a + // property does not have a use counter defined in UseCounters.conf, then + // its associated entry is |eUseCounter_UNKNOWN|. + static const mozilla::UseCounter gPropertyUseCounter[eCSSProperty_COUNT_no_shorthands]; + +public: + + static mozilla::UseCounter UseCounterFor(nsCSSProperty aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + return gPropertyUseCounter[aProperty]; + } + static bool IsEnabled(nsCSSProperty aProperty, EnabledState aEnabled) { if (IsEnabled(aProperty)) { diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 8f41185a69..690692d54b 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -213,14 +213,7 @@ nsJARChannel::nsJARChannel() nsJARChannel::~nsJARChannel() { - if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); - } + NS_ReleaseOnMainThread(mLoadInfo); // release owning reference to the jar handler nsJARProtocolHandler *handler = gJarHandler; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 4e04b17ec5..01c68b3306 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -105,14 +105,7 @@ HttpBaseChannel::~HttpBaseChannel() { LOG(("Destroying HttpBaseChannel @%x\n", this)); - if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); - } + NS_ReleaseOnMainThread(mLoadInfo); // Make sure we don't leak CleanRedirectCacheChainIfNecessary(); diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 0e8e16c5bc..fd00ae6343 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -1185,33 +1185,13 @@ WebSocketChannel::~WebSocketChannel() while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront())) delete mCurrentOut; - nsCOMPtr mainThread; - nsIURI *forgettable; - NS_GetMainThread(getter_AddRefs(mainThread)); - - if (mURI) { - mURI.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); - } - - if (mOriginalURI) { - mOriginalURI.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); - } + NS_ReleaseOnMainThread(mURI); + NS_ReleaseOnMainThread(mOriginalURI); mListenerMT = nullptr; - if (mLoadGroup) { - nsILoadGroup *forgettableGroup; - mLoadGroup.forget(&forgettableGroup); - NS_ProxyRelease(mainThread, forgettableGroup, false); - } - - if (mLoadInfo) { - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); - } + NS_ReleaseOnMainThread(mLoadGroup); + NS_ReleaseOnMainThread(mLoadInfo); } NS_IMETHODIMP diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 5f65f8731f..ed76827481 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -64,12 +64,17 @@ #include "mozilla/dom/DOMJSClass.h" #include "mozilla/dom/ScriptSettings.h" #include "jsprf.h" +#include "js/Debug.h" #include "nsCycleCollectionNoteRootCallback.h" #include "nsCycleCollectionParticipant.h" #include "nsCycleCollector.h" #include "nsDOMJSUtils.h" #include "nsJSUtils.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif + #include "nsIException.h" #include "nsThreadUtils.h" #include "xpcpublic.h" @@ -424,6 +429,8 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime, }; SetDOMCallbacks(mJSRuntime, &DOMcallbacks); + JS::dbg::SetDebuggerMallocSizeOf(mJSRuntime, moz_malloc_size_of); + nsCycleCollector_registerJSRuntime(this); } diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 23aa4900f1..dfe8997619 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -102,6 +102,7 @@ struct CycleCollectorResults { mForcedGC = false; mMergedZones = false; + mAnyManual = false; mVisitedRefCounted = 0; mVisitedGCed = 0; mFreedRefCounted = 0; @@ -114,6 +115,7 @@ struct CycleCollectorResults bool mForcedGC; bool mMergedZones; + bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown. uint32_t mVisitedRefCounted; uint32_t mVisitedGCed; uint32_t mFreedRefCounted; @@ -206,6 +208,7 @@ private: virtual void TraceNativeBlackRoots(JSTracer* aTracer) { }; void TraceNativeGrayRoots(JSTracer* aTracer); +public: enum DeferredFinalizeType { FinalizeIncrementally, FinalizeNow, @@ -213,7 +216,6 @@ private: void FinalizeDeferredThings(DeferredFinalizeType aType); -public: // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in // crash reports. Here are the values that can appear in the reports: enum class OOMState : uint32_t { @@ -221,7 +223,7 @@ public: OK, // We are currently reporting the given condition. - // + // // Suppose a crash report contains "JSLargeAllocationFailure: // Reporting". This means we crashed while executing memory-pressure // observers, trying to shake loose some memory. The large allocation in diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index 36b2193fd7..cbfc4754bc 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -20,6 +20,7 @@ #include "nsIClassInfoImpl.h" #include "nsIConsoleListener.h" #include "nsPrintfCString.h" +#include "nsProxyRelease.h" #include "nsIScriptError.h" #include "mozilla/Preferences.h" @@ -67,6 +68,8 @@ nsConsoleService::nsConsoleService() NS_IMETHODIMP nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + // Remove the messages related to this window for (uint32_t i = 0; i < mBufferSize && mMessages[i]; i++) { // Only messages implementing nsIScriptError interface exposes the inner window ID @@ -104,6 +107,8 @@ nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID) nsConsoleService::~nsConsoleService() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + uint32_t i = 0; while (i < mBufferSize && mMessages[i]) { NS_RELEASE(mMessages[i]); @@ -200,6 +205,7 @@ nsConsoleService::LogMessage(nsIConsoleMessage* aMessage) return LogMessageWithMode(aMessage, OutputToLog); } +// This can be called off the main thread. nsresult nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage, nsConsoleService::OutputMode aOutputMode) @@ -313,7 +319,10 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage, } if (retiredMessage) { - NS_RELEASE(retiredMessage); + // Release |retiredMessage| on the main thread in case it is an instance of + // a mainthread-only class like nsScriptErrorWithStack and we're off the + // main thread. + NS_ReleaseOnMainThread(retiredMessage); } if (r) { @@ -353,6 +362,8 @@ NS_IMETHODIMP nsConsoleService::GetMessageArray(uint32_t* aCount, nsIConsoleMessage*** aMessages) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + nsIConsoleMessage** messageArray; /* @@ -449,6 +460,8 @@ nsConsoleService::UnregisterListener(nsIConsoleListener* aListener) NS_IMETHODIMP nsConsoleService::Reset() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + /* * Make sure nobody trips into the buffer while it's being reset */ diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 4aad85fa49..024e7fb917 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3551,6 +3551,9 @@ nsCycleCollector::CleanupAfterCollection() #endif if (mJSRuntime) { + mJSRuntime->FinalizeDeferredThings(mResults.mAnyManual + ? CycleCollectedJSRuntime::FinalizeNow + : CycleCollectedJSRuntime::FinalizeIncrementally); mJSRuntime->EndCycleCollectionCallback(mResults); timeLog.Checkpoint("CleanupAfterCollection::EndCycleCollectionCallback()"); } @@ -3613,6 +3616,10 @@ nsCycleCollector::Collect(ccType aCCType, timeLog.Checkpoint("Collect::FreeSnowWhite"); } + if (aCCType != SliceCC) { + mResults.mAnyManual = true; + } + ++mResults.mNumSlices; bool continueSlice = aBudget.isUnlimited() || !aPreferShorterSlices; @@ -3810,6 +3817,7 @@ nsCycleCollector::BeginCollection(ccType aCCType, // Set up the data structures for building the graph. mGraph.Init(); mResults.Init(); + mResults.mAnyManual = (aCCType != SliceCC); bool mergeZones = ShouldMergeZones(aCCType); mResults.mMergedZones = mergeZones; diff --git a/xpcom/glue/nsProxyRelease.h b/xpcom/glue/nsProxyRelease.h index 5d95780d22..6783b313c4 100644 --- a/xpcom/glue/nsProxyRelease.h +++ b/xpcom/glue/nsProxyRelease.h @@ -57,7 +57,7 @@ NS_ProxyRelease(nsIEventTarget* aTarget, nsRefPtr& aDoomed, * the doomed object; the object to be released on the target thread. * @param aAlwaysProxy * normally, if NS_ProxyRelease is called on the target thread, then the - * doomed object will released directly. however, if this parameter is + * doomed object will be released directly. However, if this parameter is * true, then an event will always be posted to the target thread for * asynchronous release. */ @@ -65,6 +65,62 @@ nsresult NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, bool aAlwaysProxy = false); +/** + * Ensure that a nsCOMPtr is released on the main thread. + * + * @see NS_ReleaseOnMainThread( nsISupports*, bool) + */ +template +inline NS_HIDDEN_(nsresult) +NS_ReleaseOnMainThread(nsCOMPtr& aDoomed, + bool aAlwaysProxy = false) +{ + T* raw = nullptr; + aDoomed.swap(raw); + return NS_ReleaseOnMainThread(raw, aAlwaysProxy); +} + +/** + * Ensure that a nsRefPtr is released on the main thread. + * + * @see NS_ReleaseOnMainThread(nsISupports*, bool) + */ +template +inline NS_HIDDEN_(nsresult) +NS_ReleaseOnMainThread(nsRefPtr& aDoomed, + bool aAlwaysProxy = false) +{ + T* raw = nullptr; + aDoomed.swap(raw); + return NS_ReleaseOnMainThread(raw, aAlwaysProxy); +} + +/** + * Ensures that the delete of a nsISupports object occurs on the main thread. + * + * @param aDoomed + * the doomed object; the object to be released on the main thread. + * @param aAlwaysProxy + * normally, if NS_ReleaseOnMainThread is called on the main thread, + * then the doomed object will be released directly. However, if this + * parameter is true, then an event will always be posted to the main + * thread for asynchronous release. + */ +inline nsresult +NS_ReleaseOnMainThread(nsISupports* aDoomed, + bool aAlwaysProxy = false) +{ + // NS_ProxyRelease treats a null event target as "the current thread". So a + // handle on the main thread is only necessary when we're not already on the + // main thread or the release must happen asynchronously. + nsCOMPtr mainThread; + if (!NS_IsMainThread() || aAlwaysProxy) { + NS_GetMainThread(getter_AddRefs(mainThread)); + } + + return NS_ProxyRelease(mainThread, aDoomed, aAlwaysProxy); +} + /** * Class to safely handle main-thread-only pointers off the main thread. * diff --git a/xpcom/tests/unit/test_debugger_malloc_size_of.js b/xpcom/tests/unit/test_debugger_malloc_size_of.js new file mode 100644 index 0000000000..f69eba3b1d --- /dev/null +++ b/xpcom/tests/unit/test_debugger_malloc_size_of.js @@ -0,0 +1,34 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* 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/. */ + +// This is just a sanity test that Gecko is giving SpiderMonkey a MallocSizeOf +// function for new JSRuntimes. There is more extensive testing around the +// expected byte sizes within SpiderMonkey's jit-tests, we just want to make +// sure that Gecko is providing SpiderMonkey with the callback it needs. + +const Cu = Components.utils; +const { byteSize } = Cu.getJSTestingFunctions(); + +function run_test() +{ + const objects = [ + {}, + { w: 1, x: 2, y: 3, z:4, a: 5 }, + [], + Array(10).fill(null), + new RegExp("(2|two) problems", "g"), + new Date(), + new Uint8Array(64), + Promise.resolve(1), + function f() {}, + Object + ]; + + for (let obj of objects) { + do_print(uneval(obj)); + ok(byteSize(obj), "We should get some (non-zero) byte size"); + } +} diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini index 82db76e2ba..858488a5ef 100644 --- a/xpcom/tests/unit/xpcshell.ini +++ b/xpcom/tests/unit/xpcshell.ini @@ -27,6 +27,7 @@ fail-if = os == "android" # Bug 676998: test fails consistently on Android fail-if = os == "android" [test_bug725015.js] +[test_debugger_malloc_size_of.js] [test_compmgr_warnings.js] # Bug 676998: test fails consistently on Android fail-if = os == "android" diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 0777d8e3ca..34266647bc 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -769,6 +769,28 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) } } + +#ifdef MOZ_CRASHREPORTER + if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) { + // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently, + // but save memory reports (expensive, ~75ms) less frequently. + const size_t LOW_MEMORY_CHECK_SECONDS = 30; + const size_t LOW_MEMORY_SAVE_SECONDS = 3 * 60; + + static TimeStamp nextCheck = TimeStamp::NowLoRes() + + TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS); + + TimeStamp now = TimeStamp::NowLoRes(); + if (now >= nextCheck) { + if (SaveMemoryReportNearOOM()) { + nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_SAVE_SECONDS); + } else { + nextCheck = now + TimeDuration::FromSeconds(LOW_MEMORY_CHECK_SECONDS); + } + } + } +#endif + bool notifyMainThreadObserver = (MAIN_THREAD == mIsMainThread) && sMainThreadObserver; if (notifyMainThreadObserver) {