diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index 138d8f675e4..02b3d8d0e9e 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -250,6 +250,64 @@ public: return OwnerDoc()->IsHTML(); } + /** + * Get the namespace that this element's tag is defined in + * @return the namespace + */ + inline PRInt32 GetNameSpaceID() const + { + return mNodeInfo->NamespaceID(); + } + + /** + * Get the NodeInfo for this element + * @return the nodes node info + */ + inline nsINodeInfo* NodeInfo() const + { + return mNodeInfo; + } + + inline bool IsInNamespace(PRInt32 aNamespace) const + { + return mNodeInfo->NamespaceID() == aNamespace; + } + + inline bool IsHTML() const + { + return IsInNamespace(kNameSpaceID_XHTML); + } + + inline bool IsHTML(nsIAtom* aTag) const + { + return mNodeInfo->Equals(aTag, kNameSpaceID_XHTML); + } + + inline bool IsSVG() const + { + return IsInNamespace(kNameSpaceID_SVG); + } + + inline bool IsSVG(nsIAtom* aTag) const + { + return mNodeInfo->Equals(aTag, kNameSpaceID_SVG); + } + + inline bool IsXUL() const + { + return IsInNamespace(kNameSpaceID_XUL); + } + + inline bool IsMathML() const + { + return IsInNamespace(kNameSpaceID_MathML); + } + + inline bool IsMathML(nsIAtom* aTag) const + { + return mNodeInfo->Equals(aTag, kNameSpaceID_MathML); + } + /** * Returns an atom holding the name of the attribute of type ID on * this content node (if applicable). Returns null for non-element diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index e903e5f0ac2..ef833f52de0 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -476,15 +476,6 @@ public: return mNodeInfo->LocalName(); } - /** - * Get the namespace that this element's tag is defined in - * @return the namespace - */ - PRInt32 GetNameSpaceID() const - { - return mNodeInfo->NamespaceID(); - } - /** * Get the tag for this element. This will always return a non-null atom * pointer (as implied by the naming of the method). For elements this is @@ -496,55 +487,6 @@ public: return mNodeInfo->NameAtom(); } - /** - * Get the NodeInfo for this element - * @return the nodes node info - */ - nsINodeInfo* NodeInfo() const - { - return mNodeInfo; - } - - bool IsInNamespace(PRInt32 aNamespace) const - { - return mNodeInfo->NamespaceID() == aNamespace; - } - - bool IsHTML() const - { - return IsInNamespace(kNameSpaceID_XHTML); - } - - bool IsHTML(nsIAtom* aTag) const - { - return mNodeInfo->Equals(aTag, kNameSpaceID_XHTML); - } - - bool IsSVG() const - { - return IsInNamespace(kNameSpaceID_SVG); - } - - bool IsSVG(nsIAtom* aTag) const - { - return mNodeInfo->Equals(aTag, kNameSpaceID_SVG); - } - - bool IsXUL() const - { - return IsInNamespace(kNameSpaceID_XUL); - } - - bool IsMathML() const - { - return IsInNamespace(kNameSpaceID_MathML); - } - - bool IsMathML(nsIAtom* aTag) const - { - return mNodeInfo->Equals(aTag, kNameSpaceID_MathML); - } - nsINode* InsertBefore(nsINode *aNewChild, nsINode *aRefChild, nsresult *aReturn) { diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 2635bbe54a5..c0c34e2e96b 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -78,98 +78,327 @@ XBLFinalize(JSFreeOp *fop, JSObject *obj) c->Drop(); } -static JSBool -XBLResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags, - JSObject **objp) +// XBL fields are represented on elements inheriting that field a bit trickily. +// Initially the element itself won't have a property for the field. When an +// attempt is made to access the field, the element's resolve hook won't find +// it. But the XBL prototype object, in the prototype chain of the element, +// will resolve an accessor property for the field on the XBL prototype object. +// That accessor, when used, will then (via InstallXBLField below) reify a +// property for the field onto the actual XBL-backed element. +// +// The accessor property is a plain old property backed by a getter function and +// a setter function. These properties are backed by the FieldGetter and +// FieldSetter natives; they're created by XBLResolve. The precise field to be +// reified is identified using two extra slots on the getter/setter functions. +// XBLPROTO_SLOT stores the XBL prototype object that provides the field. +// FIELD_SLOT stores the name of the field, i.e. its JavaScript property name. +// +// This two-step field installation process -- reify an accessor on the +// prototype, then have that reify an own property on the actual element -- is +// admittedly convoluted. Better would be for XBL-backed elements to be proxies +// that could resolve fields onto themselves. But given that XBL bindings are +// associated with elements mutably -- you can add/remove/change -moz-binding +// whenever you want, alas -- doing so would require all elements to be proxies, +// which isn't performant now. So we do this two-step instead. +static const uint32_t XBLPROTO_SLOT = 0; +static const uint32_t FIELD_SLOT = 1; + +static bool +ObjectHasISupportsPrivate(JS::Handle obj) { - // Note: if we get here, that means that the implementation for some binding - // was installed, which means that AllowScripts() tested true. Hence no need - // to do checks like that here. - - // Default to not resolving things. - NS_ASSERTION(*objp, "Must have starting object"); + JSClass* clasp = ::JS_GetClass(obj); + const uint32_t HAS_PRIVATE_NSISUPPORTS = + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; + return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS; +} - JSObject* origObj = *objp; - *objp = NULL; +// Define a shadowing property on |this| for the XBL field defined by the +// contents of the callee's reserved slots. If the property was defined, +// *installed will be true, and idp will be set to the property name that was +// defined. +static JSBool +InstallXBLField(JSContext* cx, + JS::Handle callee, JS::Handle thisObj, + jsid* idp, bool* installed) +{ + *installed = false; - if (!JSID_IS_STRING(id)) { - return JS_TRUE; - } - - nsDependentJSString fieldName(id); - - jsval slotVal = ::JS_GetReservedSlot(obj, 0); - NS_ASSERTION(!JSVAL_IS_VOID(slotVal), "How did that happen?"); - - nsXBLPrototypeBinding* protoBinding = - static_cast(JSVAL_TO_PRIVATE(slotVal)); - NS_ASSERTION(protoBinding, "Must have prototype binding!"); - - nsXBLProtoImplField* field = protoBinding->FindField(fieldName); - if (!field) { - return JS_TRUE; - } - - // We have this field. Time to install it. Get our node. - JSClass* nodeClass = ::JS_GetClass(origObj); - if (!nodeClass) { - return JS_FALSE; - } - - if (~nodeClass->flags & - (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) { - xpc::Throw(cx, NS_ERROR_UNEXPECTED); - return JS_FALSE; - } + // First ensure |this| is a reasonable XBL bound node. + // + // FieldAccessorGuard already determined whether |thisObj| was acceptable as + // |this| in terms of not throwing a TypeError. Assert this for good measure. + MOZ_ASSERT(ObjectHasISupportsPrivate(thisObj)); + // But there are some cases where we must accept |thisObj| but not install a + // property on it, or otherwise touch it. Hence this split of |this|-vetting + // duties. nsCOMPtr xpcWrapper = - do_QueryInterface(static_cast(::JS_GetPrivate(origObj))); + do_QueryInterface(static_cast(::JS_GetPrivate(thisObj))); if (!xpcWrapper) { - // Looks like whatever |origObj| is it's not our nsIContent. It might well + // Looks like whatever |thisObj| is it's not our nsIContent. It might well // be the proto our binding installed, however, where the private is the // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception // here. - // We could make this stricter by checking the class maybe, but whatever - return JS_TRUE; + // + // We could make this stricter by checking the class maybe, but whatever. + return true; } - nsCOMPtr content = do_QueryWrappedNative(xpcWrapper); - if (!content) { + nsCOMPtr xblNode = do_QueryWrappedNative(xpcWrapper); + if (!xblNode) { xpc::Throw(cx, NS_ERROR_UNEXPECTED); - return JS_FALSE; + return false; } + // Now that |this| is okay, actually install the field. Some of this + // installation work could have been done in XBLResolve, but this splitting + // of work seems simplest to implement and friendliest regarding lifetimes + // and potential cycles. + + // Because of the possibility (due to XBL binding inheritance, because each + // XBL binding lives in its own global object) that |this| might be in a + // different compartment from the callee (not to mention that this method can + // be called with an arbitrary |this| regardless of how insane XBL is), and + // because in this method we've entered |this|'s compartment (see in + // Field[GS]etter where we attempt a cross-compartment call), we must enter + // the callee's compartment to access its reserved slots. + nsXBLPrototypeBinding* protoBinding; + nsDependentJSString fieldName; + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, callee)) { + return false; + } + + JS::Rooted xblProto(cx); + xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject(); + + JS::Value name = js::GetFunctionNativeReserved(callee, FIELD_SLOT); + JSFlatString* fieldStr = JS_ASSERT_STRING_IS_FLAT(name.toString()); + fieldName.init(fieldStr); + + MOZ_ALWAYS_TRUE(JS_ValueToId(cx, name, idp)); + + JS::Value slotVal = ::JS_GetReservedSlot(xblProto, 0); + protoBinding = static_cast(slotVal.toPrivate()); + MOZ_ASSERT(protoBinding); + } + + nsXBLProtoImplField* field = protoBinding->FindField(fieldName); + MOZ_ASSERT(field); + // This mirrors code in nsXBLProtoImpl::InstallImplementation - nsIDocument* doc = content->OwnerDoc(); - - nsIScriptGlobalObject* global = doc->GetScriptGlobalObject(); + nsIScriptGlobalObject* global = xblNode->OwnerDoc()->GetScriptGlobalObject(); if (!global) { - return JS_TRUE; + return true; } nsCOMPtr context = global->GetContext(); if (!context) { - return JS_TRUE; + return true; } + nsresult rv = field->InstallField(context, thisObj, xblNode->NodePrincipal(), + protoBinding->DocURI(), installed); + if (NS_SUCCEEDED(rv)) { + return true; + } - // Now we either resolve or fail - bool didInstall; - nsresult rv = field->InstallField(context, origObj, - content->NodePrincipal(), - protoBinding->DocURI(), - &didInstall); - if (NS_FAILED(rv)) { + if (!::JS_IsExceptionPending(cx)) { xpc::Throw(cx, rv); - return JS_FALSE; + } + return false; +} + +// Determine whether the |this| passed to this method is valid for an XBL field +// access (which is to say, an object with an nsISupports private), taking into +// account proxies and/or wrappers around |this|. There are three possible +// outcomes from calling this method: +// +// 1. An error was hit, and this method returned false. In this case, the +// caller should propagate it. +// 2. The |this| passed in was directly acceptable for XBL field access. This +// method returned true and set *thisObj to a |this| that can be used for +// field definition (i.e. that passes ObjectHasISupportsPrivate). In this +// case, the caller should install the field on *thisObj. +// 3. The |this| passed in was a proxy/wrapper around an object usable for +// XBL field access. The method recursively (and successfully) invoked the +// native on the unwrapped |this|, then it returned true and set *thisObj +// to null. In this case, the caller should itself return true. +// +// Thus this method implements the JS_CallNonGenericMethodOnProxy idiom in +// jsapi.h. +// +// Note that a |this| valid for field access is *not* necessarily one on which +// the field value will be installed. It's just one where calling the specified +// method on it won't unconditionally throw a TypeError. Confusing? Perhaps, +// but it's compatible with what we did before we implemented XBL fields using +// this technique. +inline bool +FieldAccessorGuard(JSContext *cx, unsigned argc, JS::Value *vp, JSNative native, JSObject **thisObj) +{ + JS::Rooted obj(cx, JS_THIS_OBJECT(cx, vp)); + if (!obj) { + xpc::Throw(cx, NS_ERROR_UNEXPECTED); + return false; } - if (didInstall) { - *objp = origObj; + if (ObjectHasISupportsPrivate(obj)) { + *thisObj = obj; + return true; } - // else we didn't resolve this field after all - return JS_TRUE; + // |this| wasn't an unwrapped object passing the has-private-nsISupports test. + // So try to unwrap |this| and recursively call the native on it. + // + // This |protoClass| gunk is needed for the JS engine to report an error if an + // object of the wrong class was passed as |this|, so that it can complain + // that it expected an object of type |protoClass|. It would be better if the + // error didn't try to specify the expected class of objects -- particularly + // because there's no one class of objects -- but it's what the API wants, so + // pass a class that's marginally correct as an answer. + JSClass* protoClass; + { + JS::Rooted callee(cx, &JS_CALLEE(cx, vp).toObject()); + JS::Rooted xblProto(cx); + xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject(); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, xblProto)) { + return false; + } + + protoClass = ::JS_GetClass(xblProto); + } + + *thisObj = NULL; + return JS_CallNonGenericMethodOnProxy(cx, argc, vp, native, protoClass); +} + +static JSBool +FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp) +{ + JS::Rooted thisObj(cx); + if (!FieldAccessorGuard(cx, argc, vp, FieldGetter, thisObj.address())) { + return false; + } + if (!thisObj) { + return true; // FieldGetter was recursively invoked on an unwrapped |this| + } + + bool installed = false; + JS::Rooted callee(cx, &JS_CALLEE(cx, vp).toObject()); + JS::Rooted id(cx); + if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) { + return false; + } + + if (!installed) { + JS_SET_RVAL(cx, vp, JS::UndefinedValue()); + return true; + } + + JS::Rooted v(cx); + if (!JS_GetPropertyById(cx, thisObj, id, v.address())) { + return false; + } + JS_SET_RVAL(cx, vp, v); + return true; +} + +static JSBool +FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp) +{ + JS::Rooted thisObj(cx); + if (!FieldAccessorGuard(cx, argc, vp, FieldSetter, thisObj.address())) { + return false; + } + if (!thisObj) { + return true; // FieldSetter was recursively invoked on an unwrapped |this| + } + + bool installed = false; + JS::Rooted callee(cx, &JS_CALLEE(cx, vp).toObject()); + JS::Rooted id(cx); + if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) { + return false; + } + + JS::Rooted v(cx, + argc > 0 ? JS_ARGV(cx, vp)[0] : JS::UndefinedValue()); + return JS_SetPropertyById(cx, thisObj, id, v.address()); +} + +static JSBool +XBLResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags, + JSObject **objp) +{ + *objp = NULL; + + if (!JSID_IS_STRING(id)) { + return true; + } + + nsXBLPrototypeBinding* protoBinding = + static_cast(::JS_GetReservedSlot(obj, 0).toPrivate()); + MOZ_ASSERT(protoBinding); + + // If the field's not present, don't resolve it. Also don't resolve it if the + // field is empty; see also nsXBLProtoImplField::InstallField which also must + // implement the not-empty requirement. + nsDependentJSString fieldName(id); + nsXBLProtoImplField* field = protoBinding->FindField(fieldName); + if (!field || field->IsEmpty()) { + return true; + } + + // We have a field: now install a getter/setter pair which will resolve the + // field onto the actual object, when invoked. + JS::Rooted global(cx, JS_GetGlobalForObject(cx, obj)); + + JS::Rooted get(cx); + get = ::JS_GetFunctionObject(js::NewFunctionByIdWithReserved(cx, FieldGetter, + 0, 0, global, + id)); + if (!get) { + return false; + } + js::SetFunctionNativeReserved(get, XBLPROTO_SLOT, JS::ObjectValue(*obj)); + js::SetFunctionNativeReserved(get, FIELD_SLOT, + JS::StringValue(JSID_TO_STRING(id))); + + JS::Rooted set(cx); + set = ::JS_GetFunctionObject(js::NewFunctionByIdWithReserved(cx, FieldSetter, + 1, 0, global, + id)); + if (!set) { + return false; + } + js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, JS::ObjectValue(*obj)); + js::SetFunctionNativeReserved(set, FIELD_SLOT, + JS::StringValue(JSID_TO_STRING(id))); + + if (!::JS_DefinePropertyById(cx, obj, id, JS::UndefinedValue(), + JS_DATA_TO_FUNC_PTR(JSPropertyOp, + get.reference()), + JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, + set.reference()), + field->AccessorAttributes())) { + return false; + } + + *objp = obj; + return true; +} + +static JSBool +XBLEnumerate(JSContext *cx, JS::Handle obj) +{ + nsXBLPrototypeBinding* protoBinding = + static_cast(::JS_GetReservedSlot(obj, 0).toPrivate()); + MOZ_ASSERT(protoBinding); + + return protoBinding->ResolveAllFields(cx, obj); } nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName) @@ -179,12 +408,12 @@ nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName) name = ToNewCString(aClassName); flags = JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | - JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE_GETS_START | + JSCLASS_NEW_RESOLVE | // Our one reserved slot holds the relevant nsXBLPrototypeBinding JSCLASS_HAS_RESERVED_SLOTS(1); addProperty = delProperty = getProperty = ::JS_PropertyStub; setProperty = ::JS_StrictPropertyStub; - enumerate = ::JS_EnumerateStub; + enumerate = XBLEnumerate; resolve = (JSResolveOp)XBLResolve; convert = ::JS_ConvertStub; finalize = XBLFinalize; diff --git a/content/xbl/src/nsXBLProtoImplField.cpp b/content/xbl/src/nsXBLProtoImplField.cpp index 474343e0f48..014f8338d5e 100644 --- a/content/xbl/src/nsXBLProtoImplField.cpp +++ b/content/xbl/src/nsXBLProtoImplField.cpp @@ -88,7 +88,8 @@ nsXBLProtoImplField::InstallField(nsIScriptContext* aContext, *aDidInstall = false; - if (mFieldTextLength == 0) { + // Empty fields are treated as not actually present. + if (IsEmpty()) { return NS_OK; } diff --git a/content/xbl/src/nsXBLProtoImplField.h b/content/xbl/src/nsXBLProtoImplField.h index b8ffbc09db8..75f3e73178c 100644 --- a/content/xbl/src/nsXBLProtoImplField.h +++ b/content/xbl/src/nsXBLProtoImplField.h @@ -41,6 +41,13 @@ public: const PRUnichar* GetName() const { return mName; } + unsigned AccessorAttributes() const { + return JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER | + (mJSAttributes & (JSPROP_ENUMERATE | JSPROP_PERMANENT)); + } + + bool IsEmpty() const { return mFieldTextLength == 0; } + protected: nsXBLProtoImplField* mNext; PRUnichar* mName; diff --git a/content/xbl/test/test_bug372769.xhtml b/content/xbl/test/test_bug372769.xhtml index 53e4e1111bb..dc754cd9bcc 100644 --- a/content/xbl/test/test_bug372769.xhtml +++ b/content/xbl/test/test_bug372769.xhtml @@ -116,8 +116,9 @@ addLoadEvent(function() { is(found, true, "Enumeration is broken"); is(d.four, 9, "Shouldn't have rerun field six"); - is(d.five, 5, "Should have run field 7"); + is(d.five, 11, "Shouldn't have run field 7"); is(d.seven, 7, "Should be 7") + is(d.five, 5, "Should have run field 7"); d = $("display2"); is(typeof(d.eight), "undefined", "Recursive resolve should bail out"); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index ecfc0a5f60c..423d7013f9f 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -574,8 +574,7 @@ static const char kDOMStringBundleURL[] = #define ELEMENT_SCRIPTABLE_FLAGS \ ((NODE_SCRIPTABLE_FLAGS & ~nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY) | \ - nsIXPCScriptable::WANT_POSTCREATE | \ - nsIXPCScriptable::WANT_ENUMERATE) + nsIXPCScriptable::WANT_POSTCREATE) #define EXTERNAL_OBJ_SCRIPTABLE_FLAGS \ ((ELEMENT_SCRIPTABLE_FLAGS & \ @@ -8188,27 +8187,6 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } -NS_IMETHODIMP -nsElementSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, bool *_retval) -{ - // Make sure to not call the superclass here! - nsCOMPtr content(do_QueryWrappedNative(wrapper, obj)); - NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED); - - nsIDocument* doc = content->OwnerDoc(); - - nsRefPtr binding = doc->BindingManager()->GetBinding(content); - if (!binding) { - // Nothing else to do here - return NS_OK; - } - - *_retval = binding->ResolveAllFields(cx, obj); - - return NS_OK; -} - // Generic array scriptable helper. diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index bef070ee408..b95c003a652 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -562,8 +562,6 @@ public: JSObject *globalObj, JSObject **parentObj); NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj); - NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, bool *_retval); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index a88ac11f4e6..025c3c4a1e0 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1090,6 +1090,7 @@ nsGlobalWindow::FreeInnerObjects() } if (mScreen) { + mScreen->Reset(); mScreen = nsnull; } diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp index fb2a0d02fef..9f4c14ba61e 100644 --- a/dom/base/nsScreen.cpp +++ b/dom/base/nsScreen.cpp @@ -63,8 +63,25 @@ nsScreen::nsScreen() { } +void +nsScreen::Reset() +{ + hal::UnlockScreenOrientation(); + + if (mEventListener) { + nsCOMPtr target = do_QueryInterface(GetOwner()); + if (target) { + target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), + mEventListener, true); + } + + mEventListener = nsnull; + } +} + nsScreen::~nsScreen() { + Reset(); hal::UnregisterScreenConfigurationObserver(this); } @@ -318,6 +335,7 @@ NS_IMETHODIMP nsScreen::MozLockOrientation(const nsAString& aOrientation, bool* aReturn) { ScreenOrientation orientation; + *aReturn = false; if (aOrientation.EqualsLiteral("portrait")) { orientation = eScreenOrientation_Portrait; @@ -332,37 +350,31 @@ nsScreen::MozLockOrientation(const nsAString& aOrientation, bool* aReturn) } else if (aOrientation.EqualsLiteral("landscape-secondary")) { orientation = eScreenOrientation_LandscapeSecondary; } else { - *aReturn = false; return NS_OK; } if (!GetOwner()) { - *aReturn = false; return NS_OK; } - if (!IsChromeType(GetOwner()->GetDocShell())) { + // Chrome code and apps can always lock the screen orientation. + if (!IsChromeType(GetOwner()->GetDocShell()) && + !static_cast(GetOwner())->IsPartOfApp()) { nsCOMPtr doc; GetOwner()->GetDocument(getter_AddRefs(doc)); if (!doc) { - *aReturn = false; return NS_OK; } - // Apps and frames contained in apps can lock orientation. - // But non-apps can lock orientation only if they're fullscreen. - if (!static_cast(GetOwner())->IsPartOfApp()) { - bool fullscreen; - doc->GetMozFullScreen(&fullscreen); - if (!fullscreen) { - *aReturn = false; - return NS_OK; - } + // Non-apps content can lock orientation only if fullscreen. + bool fullscreen; + doc->GetMozFullScreen(&fullscreen); + if (!fullscreen) { + return NS_OK; } nsCOMPtr target = do_QueryInterface(GetOwner()); if (!target) { - *aReturn = false; return NS_OK; } diff --git a/dom/base/nsScreen.h b/dom/base/nsScreen.h index 6c49fa388fd..869d4fd3557 100644 --- a/dom/base/nsScreen.h +++ b/dom/base/nsScreen.h @@ -26,7 +26,7 @@ class nsScreen : public nsDOMEventTargetHelper public: static already_AddRefed Create(nsPIDOMWindow* aWindow); - void Invalidate(); + void Reset(); NS_DECL_ISUPPORTS NS_DECL_NSIDOMSCREEN diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 140d428eb3e..b38f36bbd9b 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -17,8 +17,6 @@ const DEBUG = RIL.DEBUG_RIL; const RADIOINTERFACELAYER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}"); -const DATACALLINFO_CID = - Components.ID("{ef474cd9-94f7-4c05-a31b-29b9de8a10d2}"); const nsIAudioManager = Ci.nsIAudioManager; const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer; @@ -131,20 +129,6 @@ XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() { }); -function DataCallInfo(state, cid, apn) { - this.callState = state; - this.cid = cid; - this.apn = apn; -} -DataCallInfo.protoptype = { - classID: DATACALLINFO_CID, - classInfo: XPCOMUtils.generateCI({classID: DATACALLINFO_CID, - classDescription: "DataCallInfo", - interfaces: [Ci.nsIDataCallInfo]}), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInfo]), -}; - - function RadioInterfaceLayer() { debug("Starting RIL Worker"); this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js"); @@ -897,21 +881,15 @@ RadioInterfaceLayer.prototype = { } this._deliverDataCallCallback("dataCallStateChanged", - [datacall.cid, datacall.ifname, datacall.state]); + [datacall]); }, /** * Handle data call list. */ handleDataCallList: function handleDataCallList(message) { - let datacalls = []; - for each (let datacall in message.datacalls) { - datacalls.push(new DataCallInfo(datacall.state, - datacall.cid, - datacall.apn)); - } this._deliverDataCallCallback("receiveDataCallList", - [datacalls, datacalls.length]); + [message.datacalls, message.datacalls.length]); }, /** @@ -1715,14 +1693,14 @@ let RILNetworkInterface = { // nsIRILDataCallback - dataCallStateChanged: function dataCallStateChanged(cid, interfaceName, callState) { - debug("Data call ID: " + cid + ", interface name: " + interfaceName); + dataCallStateChanged: function dataCallStateChanged(datacall) { + debug("Data call ID: " + datacall.cid + ", interface name: " + datacall.ifname); if (this.connecting && - (callState == RIL.GECKO_NETWORK_STATE_CONNECTING || - callState == RIL.GECKO_NETWORK_STATE_CONNECTED)) { + (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTING || + datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED)) { this.connecting = false; - this.cid = cid; - this.name = interfaceName; + this.cid = datacall.cid; + this.name = datacall.ifname; if (!this.registeredAsNetworkInterface) { let networkManager = Cc["@mozilla.org/network/manager;1"] .getService(Ci.nsINetworkManager); @@ -1730,14 +1708,14 @@ let RILNetworkInterface = { this.registeredAsNetworkInterface = true; } } - if (this.cid != cid) { + if (this.cid != datacall.cid) { return; } - if (this.state == callState) { + if (this.state == datacall.state) { return; } - this.state = callState; + this.state = datacall.state; Services.obs.notifyObservers(this, kNetworkInterfaceStateChangedTopic, null); diff --git a/dom/system/gonk/nsIRadioInterfaceLayer.idl b/dom/system/gonk/nsIRadioInterfaceLayer.idl index 37a7e5ca9a4..3c77645f9e9 100644 --- a/dom/system/gonk/nsIRadioInterfaceLayer.idl +++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl @@ -62,30 +62,25 @@ interface nsIRILTelephonyCallback : nsISupports in AString error); }; -[scriptable, uuid(66a55943-e63b-4731-aece-9c04bfc14019)] +[scriptable, uuid(8a711703-1ee5-4675-9d9a-0b188e944cfe)] interface nsIRILDataCallInfo : nsISupports { - readonly attribute unsigned long callState; + readonly attribute unsigned long state; readonly attribute AString cid; readonly attribute AString apn; + readonly attribute AString ifname; }; -[scriptable, uuid(cea91bcb-3cfb-42bb-8638-dae89e8870fc)] +[scriptable, uuid(5bcac053-c245-46f0-bb45-d0039bfb89f5)] interface nsIRILDataCallback : nsISupports { /** * Notified when a data call changes state. * - * @param cid - * The CID of the data call. - * @param interfaceName - * Name of the associated network interface. - * @param dataState - * One of the nsIRadioInterfaceLayer::DATACALL_STATE_* values. + * @param dataCall + * A nsIRILDataCallInfo object. */ - void dataCallStateChanged(in AString cid, - in AString interfaceName, - in unsigned short callState); + void dataCallStateChanged(in nsIRILDataCallInfo dataCall); /** * Called when nsIRadioInterfaceLayer is asked to enumerate the current diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 07fd0ad2a11..7736706b904 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1406,23 +1406,10 @@ typedef JSBool * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, - * if id was resolved. + * if id was resolved. The hook may assume *objp is null on entry. * * This hook instead of JSResolveOp is called via the JSClass.resolve member * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. - * - * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further - * extends this hook by passing in the starting object on the prototype chain - * via *objp. Thus a resolve hook implementation may define the property id - * being resolved in the object in which the id was first sought, rather than - * in a prototype object whose class led to the resolve hook being called. - * - * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore - * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no - * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. - * This is not good practice, but enough existing hook implementations count - * on it that we can't break compatibility by passing the starting object in - * *objp without a new JSClass flag. */ typedef JSBool (* JSNewResolveOp)(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags, @@ -3661,10 +3648,7 @@ struct JSClass { #define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ #define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ #define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ -#define JSCLASS_NEW_RESOLVE_GETS_START (1<<4) /* JSNewResolveOp gets starting - object in prototype chain - passed in via *objp in/out - parameter */ +/* (1<<4) is unused */ #define JSCLASS_IMPLEMENTS_BARRIERS (1<<5) /* Correctly implements GC read and write barriers */ #define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 386c5f2884e..00da6114782 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4643,9 +4643,9 @@ DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, const Value & /* * Call obj's resolve hook. * - * cx, start, id, and flags are the parameters initially passed to the ongoing - * lookup; objp and propp are its out parameters. obj is an object along - * start's prototype chain. + * cx, id, and flags are the parameters initially passed to the ongoing lookup; + * objp and propp are its out parameters. obj is an object along the prototype + * chain from where the lookup started. * * There are four possible outcomes: * @@ -4661,7 +4661,7 @@ DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, const Value & * and return true. */ static JSBool -CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, unsigned flags, +CallResolveOp(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, JSObject **objp, JSProperty **propp, bool *recursedp) { Class *clasp = obj->getClass(); @@ -4690,8 +4690,7 @@ CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, uns if (flags == RESOLVE_INFER) flags = js_InferFlags(cx, 0); - RootedObject obj2(cx); - obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL; + RootedObject obj2(cx, NULL); if (!newresolve(cx, obj, id, flags, obj2.address())) return false; @@ -4742,7 +4741,7 @@ LookupPropertyWithFlagsInline(JSContext *cx, HandleObject obj, HandleId id, unsi /* Try obj's class resolve hook if id was not found in obj's scope. */ if (current->getClass()->resolve != JS_ResolveStub) { bool recursed; - if (!CallResolveOp(cx, obj, current, id, flags, objp, propp, &recursed)) + if (!CallResolveOp(cx, current, id, flags, objp, propp, &recursed)) return false; if (recursed) break; diff --git a/mobile/android/base/GeckoScreenOrientationListener.java b/mobile/android/base/GeckoScreenOrientationListener.java index 09dc55a29e4..a257d940186 100644 --- a/mobile/android/base/GeckoScreenOrientationListener.java +++ b/mobile/android/base/GeckoScreenOrientationListener.java @@ -36,6 +36,8 @@ public class GeckoScreenOrientationListener static public final short eScreenOrientation_LandscapeSecondary = 8; static public final short eScreenOrientation_Landscape = 12; + static private final short kDefaultScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + private short mOrientation; private OrientationEventListenerImpl mListener = null; @@ -150,6 +152,7 @@ public class GeckoScreenOrientationListener break; default: Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); + return; } GeckoApp.mAppContext.setRequestedOrientation(orientation); @@ -157,7 +160,11 @@ public class GeckoScreenOrientationListener } public void unlockScreenOrientation() { - GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); + if (GeckoApp.mAppContext.getRequestedOrientation() == kDefaultScreenOrientation) { + return; + } + + GeckoApp.mAppContext.setRequestedOrientation(kDefaultScreenOrientation); updateScreenOrientation(); } } diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index 2f8b9c4b611..3d3b85eb801 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -1106,6 +1106,13 @@ nsCacheService::Init() { NS_TIME_FUNCTION; + // Thie method must be called on the main thread because mCacheIOThread must + // only be modified on the main thread. + if (!NS_IsMainThread()) { + NS_ERROR("nsCacheService::Init called off the main thread"); + return NS_ERROR_NOT_SAME_THREAD; + } + NS_ASSERTION(!mInitialized, "nsCacheService already initialized."); if (mInitialized) return NS_ERROR_ALREADY_INITIALIZED; @@ -1158,6 +1165,12 @@ nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir, void nsCacheService::Shutdown() { + // Thie method must be called on the main thread because mCacheIOThread must + // only be modified on the main thread. + if (!NS_IsMainThread()) { + NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread"); + } + nsCOMPtr cacheIOThread; Telemetry::AutoTimer totalTimer; @@ -1469,13 +1482,29 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy) NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget) { - nsCacheServiceAutoLock lock; + NS_ENSURE_ARG_POINTER(aCacheIOTarget); - if (!mCacheIOThread) - return NS_ERROR_NOT_AVAILABLE; + // Because mCacheIOThread can only be changed on the main thread, it can be + // read from the main thread without the lock. This is useful to prevent + // blocking the main thread on other cache operations. + if (!NS_IsMainThread()) { + Lock(); + } - NS_ADDREF(*aCacheIOTarget = mCacheIOThread); - return NS_OK; + nsresult rv; + if (mCacheIOThread) { + NS_ADDREF(*aCacheIOTarget = mCacheIOThread); + rv = NS_OK; + } else { + *aCacheIOTarget = nsnull; + rv = NS_ERROR_NOT_AVAILABLE; + } + + if (!NS_IsMainThread()) { + Unlock(); + } + + return rv; } /** diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 895cc0c4308..0c3e5818870 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -60,7 +60,7 @@ AccumulateCacheHitTelemetry(mozilla::Telemetry::ID deviceHistogram, PRUint32 hitOrMiss) { mozilla::Telemetry::Accumulate( - mozilla::Telemetry::HTTP_CACHE_DISPOSITION, hitOrMiss); + mozilla::Telemetry::HTTP_CACHE_DISPOSITION_2, hitOrMiss); if (deviceHistogram != UNKNOWN_DEVICE) { mozilla::Telemetry::Accumulate(deviceHistogram, hitOrMiss); } @@ -2879,13 +2879,13 @@ HttpCacheQuery::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, if (cacheDeviceID) { if (!strcmp(cacheDeviceID, kDiskDeviceID)) { mCacheEntryDeviceTelemetryID - = mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION; + = mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION_2; } else if (!strcmp(cacheDeviceID, kMemoryDeviceID)) { mCacheEntryDeviceTelemetryID - = mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION; + = mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION_2; } else if (!strcmp(cacheDeviceID, kOfflineDeviceID)) { mCacheEntryDeviceTelemetryID - = mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION; + = mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION_2; } else { MOZ_NOT_REACHED("unknown cache device ID"); } diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 718818e6dd5..c94b7b47f23 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -195,11 +195,11 @@ HISTOGRAM(SPDY_SETTINGS_IW, 1, 1000, 50, EXPONENTIAL, "SPDY: Settings IW (round #undef _HTTP_HIST #undef HTTP_HISTOGRAMS -HISTOGRAM(HTTP_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Cache Hit, Reval, Failed-Reval, Miss") HISTOGRAM(DISK_CACHE_CORRUPT, 0, 1, 2, BOOLEAN, "Was the HTTP disk cache corrupt at startup?") -HISTOGRAM(HTTP_DISK_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Disk Cache Hit, Reval, Failed-Reval, Miss") -HISTOGRAM(HTTP_MEMORY_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Memory Cache Hit, Reval, Failed-Reval, Miss") -HISTOGRAM(HTTP_OFFLINE_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Offline Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM_ENUMERATED_VALUES(HTTP_CACHE_DISPOSITION_2, 5, "HTTP Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM_ENUMERATED_VALUES(HTTP_DISK_CACHE_DISPOSITION_2, 5, "HTTP Disk Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM_ENUMERATED_VALUES(HTTP_MEMORY_CACHE_DISPOSITION_2, 5, "HTTP Memory Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM_ENUMERATED_VALUES(HTTP_OFFLINE_CACHE_DISPOSITION_2, 5, "HTTP Offline Cache Hit, Reval, Failed-Reval, Miss") HISTOGRAM(CACHE_DEVICE_SEARCH, 1, 100, 100, LINEAR, "Time to search cache (ms)") HISTOGRAM(CACHE_MEMORY_SEARCH, 1, 100, 100, LINEAR, "Time to search memory cache (ms)") HISTOGRAM(CACHE_DISK_SEARCH, 1, 100, 100, LINEAR, "Time to search disk cache (ms)") diff --git a/widget/gonk/nsWindow.cpp b/widget/gonk/nsWindow.cpp index 316d232baf0..0995779af78 100644 --- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -605,7 +605,13 @@ nsScreenGonk::GetAvailRect(PRInt32 *outLeft, PRInt32 *outTop, static uint32_t ColorDepth() { - return gNativeWindow->getDevice()->format == GGL_PIXEL_FORMAT_RGB_565 ? 16 : 24; + switch (gNativeWindow->getDevice()->format) { + case GGL_PIXEL_FORMAT_RGB_565: + return 16; + case GGL_PIXEL_FORMAT_RGBA_8888: + return 32; + } + return 24; // GGL_PIXEL_FORMAT_RGBX_8888 } NS_IMETHODIMP