diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h index 65f312d236..393ef9733b 100644 --- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -26,7 +26,7 @@ namespace dom { class SpeakerManagerService; #endif -#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::Publicnotification + 1 +#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_ class AudioChannelService final : public nsIAudioChannelService , public nsIObserver diff --git a/dom/base/StructuredCloneHelper.cpp b/dom/base/StructuredCloneHelper.cpp index 30ec81d04c..20583cbee5 100644 --- a/dom/base/StructuredCloneHelper.cpp +++ b/dom/base/StructuredCloneHelper.cpp @@ -125,7 +125,6 @@ void StructuredCloneHelperInternal::Shutdown() { #ifdef DEBUG - MOZ_ASSERT(!mShutdownCalled, "Shutdown already called!"); mShutdownCalled = true; #endif @@ -163,7 +162,6 @@ StructuredCloneHelperInternal::Read(JSContext* aCx, MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown."); bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this); - mBuffer = nullptr; return ok; } @@ -247,7 +245,11 @@ StructuredCloneHelper::Read(nsISupports* aParent, aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); } - mBlobImplArray.Clear(); + // If we are tranferring something, we cannot call 'Read()' more than once. + if (mSupportsTransferring) { + mBlobImplArray.Clear(); + Shutdown(); + } } void @@ -257,6 +259,19 @@ StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent, size_t aBufferLength, JS::MutableHandle aValue, ErrorResult& aRv) +{ + ReadFromBuffer(aParent, aCx, aBuffer, aBufferLength, + JS_STRUCTURED_CLONE_VERSION, aValue, aRv); +} + +void +StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + uint64_t* aBuffer, + size_t aBufferLength, + uint32_t aAlgorithmVersion, + JS::MutableHandle aValue, + ErrorResult& aRv) { MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write()."); MOZ_ASSERT(aBuffer); @@ -264,9 +279,8 @@ StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent, mozilla::AutoRestore guard(mParent); mParent = aParent; - if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, - JS_STRUCTURED_CLONE_VERSION, aValue, - &gCallbacks, this)) { + if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion, + aValue, &gCallbacks, this)) { JS_ClearPendingException(aCx); aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); } diff --git a/dom/base/StructuredCloneHelper.h b/dom/base/StructuredCloneHelper.h index efd3a5f884..b8e97e99df 100644 --- a/dom/base/StructuredCloneHelper.h +++ b/dom/base/StructuredCloneHelper.h @@ -159,10 +159,24 @@ public: JS::MutableHandle aValue, ErrorResult &aRv); + void ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + uint64_t* aBuffer, + size_t aBufferLength, + uint32_t aAlgorithmVersion, + JS::MutableHandle aValue, + ErrorResult &aRv); + // Use this method to free a buffer generated by MoveToBuffer(). void FreeBuffer(uint64_t* aBuffer, size_t aBufferLength); + bool HasClonedDOMObjects() const + { + return !mBlobImplArray.IsEmpty() || + !mClonedImages.IsEmpty(); + } + nsTArray>& BlobImpls() { MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported."); diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 334ef1927d..62bc8ecd13 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -13,14 +13,14 @@ #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" -#include "jsfriendapi.h" -#include "js/StructuredClone.h" #include "xpcpublic.h" #include "mozilla/Base64.h" +#include "mozilla/dom/StructuredCloneHelper.h" #include "mozilla/dom/ScriptSettings.h" using namespace mozilla; +using namespace mozilla::dom; NS_IMPL_ADDREF(nsStructuredCloneContainer) NS_IMPL_RELEASE(nsStructuredCloneContainer) @@ -31,60 +31,44 @@ NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer) NS_INTERFACE_MAP_END nsStructuredCloneContainer::nsStructuredCloneContainer() - : mData(nullptr), mSize(0), mVersion(0) + : StructuredCloneHelper(CloningSupported, TransferringNotSupported) + , mState(eNotInitialized) , mData(nullptr), mSize(0), mVersion(0) { } nsStructuredCloneContainer::~nsStructuredCloneContainer() { - free(mData); + if (mData) { + free(mData); + } } -nsresult +NS_IMETHODIMP nsStructuredCloneContainer::InitFromJSVal(JS::Handle aData, JSContext* aCx) { - NS_ENSURE_STATE(!mData); - - uint64_t* jsBytes = nullptr; - bool success = false; - if (aData.isPrimitive()) { - success = JS_WriteStructuredClone(aCx, aData, &jsBytes, &mSize, - nullptr, nullptr, - JS::UndefinedHandleValue); - } else { - success = JS_WriteStructuredClone(aCx, aData, &jsBytes, &mSize, - nullptr, nullptr, - JS::UndefinedHandleValue); - } - NS_ENSURE_STATE(success); - NS_ENSURE_STATE(jsBytes); - - // Copy jsBytes into our own buffer. - mData = (uint64_t*) malloc(mSize); - if (!mData) { - mSize = 0; - mVersion = 0; - - JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); + if (mState != eNotInitialized) { return NS_ERROR_FAILURE; } - else { - mVersion = JS_STRUCTURED_CLONE_VERSION; + + ErrorResult rv; + Write(aCx, aData, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); } - memcpy(mData, jsBytes, mSize); - - JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); + mState = eInitializedFromJSVal; return NS_OK; } -nsresult +NS_IMETHODIMP nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, uint32_t aFormatVersion, - JSContext *aCx) + JSContext* aCx) { - NS_ENSURE_STATE(!mData); + if (mState != eNotInitialized) { + return NS_ERROR_FAILURE; + } NS_ConvertUTF16toUTF8 data(aData); @@ -99,28 +83,56 @@ nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, mSize = binaryData.Length(); mVersion = aFormatVersion; + + mState = eInitializedFromBase64; return NS_OK; } - nsresult -nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, - nsIVariant **aData) +nsStructuredCloneContainer::DeserializeToJsval(JSContext* aCx, + JS::MutableHandle aValue) +{ + aValue.setNull(); + JS::Rooted jsStateObj(aCx); + + if (mState == eInitializedFromJSVal) { + ErrorResult rv; + Read(nullptr, aCx, &jsStateObj, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + } else { + MOZ_ASSERT(mState == eInitializedFromBase64); + MOZ_ASSERT(mData); + + ErrorResult rv; + ReadFromBuffer(nullptr, aCx, mData, mSize, mVersion, &jsStateObj, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + } + + aValue.set(jsStateObj); + return NS_OK; +} + +NS_IMETHODIMP +nsStructuredCloneContainer::DeserializeToVariant(JSContext* aCx, + nsIVariant** aData) { - NS_ENSURE_STATE(mData); NS_ENSURE_ARG_POINTER(aData); *aData = nullptr; + if (mState == eNotInitialized) { + return NS_ERROR_FAILURE; + } + // Deserialize to a JS::Value. JS::Rooted jsStateObj(aCx); - bool hasTransferable = false; - bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion, - &jsStateObj, nullptr, nullptr) && - JS_StructuredCloneHasTransferables(mData, mSize, - &hasTransferable); - // We want to be sure that mData doesn't contain transferable objects - MOZ_ASSERT(!hasTransferable); - NS_ENSURE_STATE(success && !hasTransferable); + nsresult rv = DeserializeToJsval(aCx, &jsStateObj); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } // Now wrap the JS::Value as an nsIVariant. nsCOMPtr varStateObj; @@ -129,31 +141,64 @@ nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, xpconnect->JSValToVariant(aCx, jsStateObj, getter_AddRefs(varStateObj)); NS_ENSURE_STATE(varStateObj); - NS_ADDREF(*aData = varStateObj); + varStateObj.forget(aData); return NS_OK; } -nsresult +NS_IMETHODIMP nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) { - NS_ENSURE_STATE(mData); aOut.Truncate(); - nsAutoCString binaryData(reinterpret_cast(mData), mSize); + if (mState == eNotInitialized) { + return NS_ERROR_FAILURE; + } + + uint64_t* data; + size_t size; + + if (mState == eInitializedFromJSVal) { + if (HasClonedDOMObjects()) { + return NS_ERROR_FAILURE; + } + + data = BufferData(); + size = BufferSize(); + } else { + MOZ_ASSERT(mState == eInitializedFromBase64); + MOZ_ASSERT(mData); + + data = mData; + size = mSize; + } + + nsAutoCString binaryData(reinterpret_cast(data), size); nsAutoCString base64Data; nsresult rv = Base64Encode(binaryData, base64Data); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - aOut.Assign(NS_ConvertASCIItoUTF16(base64Data)); + CopyASCIItoUTF16(base64Data, aOut); return NS_OK; } -nsresult -nsStructuredCloneContainer::GetSerializedNBytes(uint64_t *aSize) +NS_IMETHODIMP +nsStructuredCloneContainer::GetSerializedNBytes(uint64_t* aSize) { - NS_ENSURE_STATE(mData); NS_ENSURE_ARG_POINTER(aSize); + if (mState == eNotInitialized) { + return NS_ERROR_FAILURE; + } + + if (mState == eInitializedFromJSVal) { + *aSize = BufferSize(); + return NS_OK; + } + + MOZ_ASSERT(mState == eInitializedFromBase64); + // mSize is a size_t, while aSize is a uint64_t. We rely on an implicit cast // here so that we'll get a compile error if a size_t-to-uint64_t cast is // narrowing. @@ -162,11 +207,21 @@ nsStructuredCloneContainer::GetSerializedNBytes(uint64_t *aSize) return NS_OK; } -nsresult -nsStructuredCloneContainer::GetFormatVersion(uint32_t *aFormatVersion) +NS_IMETHODIMP +nsStructuredCloneContainer::GetFormatVersion(uint32_t* aFormatVersion) { - NS_ENSURE_STATE(mData); NS_ENSURE_ARG_POINTER(aFormatVersion); + + if (mState == eNotInitialized) { + return NS_ERROR_FAILURE; + } + + if (mState == eInitializedFromJSVal) { + *aFormatVersion = JS_STRUCTURED_CLONE_VERSION; + return NS_OK; + } + + MOZ_ASSERT(mState == eInitializedFromBase64); *aFormatVersion = mVersion; return NS_OK; } diff --git a/dom/base/nsStructuredCloneContainer.h b/dom/base/nsStructuredCloneContainer.h index fb04b1dd9d..ddad5af9c6 100644 --- a/dom/base/nsStructuredCloneContainer.h +++ b/dom/base/nsStructuredCloneContainer.h @@ -9,6 +9,7 @@ #include "nsIStructuredCloneContainer.h" #include "mozilla/Attributes.h" +#include "mozilla/dom/StructuredCloneHelper.h" #define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \ "@mozilla.org/docshell/structured-clone-container;1" @@ -20,7 +21,9 @@ {0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \ } -class nsStructuredCloneContainer final : public nsIStructuredCloneContainer +class nsStructuredCloneContainer final + : public nsIStructuredCloneContainer + , public mozilla::dom::StructuredCloneHelper { public: nsStructuredCloneContainer(); @@ -31,6 +34,12 @@ class nsStructuredCloneContainer final : public nsIStructuredCloneContainer private: ~nsStructuredCloneContainer(); + enum { + eNotInitialized = 0, + eInitializedFromJSVal, + eInitializedFromBase64, + } mState; + uint64_t* mData; // This needs to be size_t rather than a PR-type so it matches the JS API. diff --git a/dom/interfaces/base/nsIStructuredCloneContainer.idl b/dom/interfaces/base/nsIStructuredCloneContainer.idl index 68813be5d7..84a12ae0e4 100644 --- a/dom/interfaces/base/nsIStructuredCloneContainer.idl +++ b/dom/interfaces/base/nsIStructuredCloneContainer.idl @@ -27,7 +27,7 @@ interface nsIDocument; * string containing a copy of the container's serialized data, using * getDataAsBase64. */ -[scriptable, uuid(63eeafec-63f5-42c3-aea9-5c04678784e7)] +[scriptable, uuid(c664aae7-0d67-4155-a2dd-a3861778626f)] interface nsIStructuredCloneContainer : nsISupports { /** @@ -45,9 +45,17 @@ interface nsIStructuredCloneContainer : nsISupports [implicit_jscontext] void initFromBase64(in AString aData,in unsigned long aFormatVersion); + /** + * Deserializes this structured clone container returning it as a jsval. + * Can be called on main and worker threads. + */ + [implicit_jscontext] + jsval deserializeToJsval(); + /** * Deserialize the object this container holds, returning it wrapped as * an nsIVariant. + * Main thread only! */ [implicit_jscontext] nsIVariant deserializeToVariant(); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index f885adcd4f..e6e99169a0 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -621,6 +621,14 @@ child: * they are 'compressed' by dumping the oldest one. */ RealMouseMoveEvent(WidgetMouseEvent event) compress; + /** + * Mouse move events with |reason == eSynthesized| are sent via a separate + * message because they do not generate DOM 'mousemove' events, and the + * 'compress' attribute on RealMouseMoveEvent() could result in a + * |reason == eReal| event being dropped in favour of an |eSynthesized| + * event, and thus a DOM 'mousemove' event to be lost. + */ + SynthMouseMoveEvent(WidgetMouseEvent event); RealMouseButtonEvent(WidgetMouseEvent event); RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding); MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); @@ -713,8 +721,13 @@ child: /** * Tell the child that the UI resolution changed for the containing * window. + * To avoid some sync messages from child to parent, we also send the dpi + * and default scale with the notification. + * If we don't know the dpi and default scale, we just pass in a negative + * value (-1) but in the majority of the cases this saves us from two + * sync requests from the child to the parent. */ - UIResolutionChanged(); + UIResolutionChanged(float dpi, double scale); /** * Tell the child that the system theme has changed, and that a repaint diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 9ce5dc085c..7024c69103 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -8,7 +8,6 @@ #include "TabChild.h" -#include "AudioChannelService.h" #include "gfxPrefs.h" #ifdef ACCESSIBILITY #include "mozilla/a11y/DocAccessibleChild.h" @@ -601,7 +600,6 @@ TabChild::TabChild(nsIContentChild* aManager, , mDefaultScale(0) , mIPCOpen(true) , mParentIsActive(false) - , mAudioChannelActive(false) { // In the general case having the TabParent tell us if APZ is enabled or not // doesn't really work because the TabParent itself may not have a reference @@ -631,6 +629,10 @@ TabChild::TabChild(nsIContentChild* aManager, observerService->AddObserver(this, topic.get(), false); } } + + for (uint32_t idx = 0; idx < NUMBER_OF_AUDIO_CHANNELS; idx++) { + mAudioChannelsActive.AppendElement(false); + } } NS_IMETHODIMP @@ -714,8 +716,8 @@ TabChild::Observe(nsISupports *aSubject, nsAutoString activeStr(aData); bool active = activeStr.EqualsLiteral("active"); - if (active != mAudioChannelActive) { - mAudioChannelActive = active; + if (active != mAudioChannelsActive[audioChannel]) { + mAudioChannelsActive[audioChannel] = active; unused << SendAudioChannelActivityNotification(audioChannel, active); } } @@ -755,7 +757,7 @@ TabChild::Init() nsCOMPtr docShellItem(do_QueryInterface(WebNavigation())); docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper); - + nsCOMPtr baseWindow = do_QueryInterface(WebNavigation()); if (!baseWindow) { NS_ERROR("mWebNav doesn't QI to nsIBaseWindow"); @@ -1875,6 +1877,12 @@ TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& event) return RecvRealMouseButtonEvent(event); } +bool +TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& event) +{ + return RecvRealMouseButtonEvent(event); +} + bool TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& event) { @@ -2939,12 +2947,12 @@ TabChild::RecvRequestNotifyAfterRemotePaint() } bool -TabChild::RecvUIResolutionChanged() +TabChild::RecvUIResolutionChanged(const float& aDpi, const double& aScale) { ScreenIntSize oldScreenSize = GetInnerSize(); mDPI = 0; mDefaultScale = 0; - static_cast(mPuppetWidget.get())->ClearBackingScaleCache(); + static_cast(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aScale); nsCOMPtr document(GetDocument()); nsCOMPtr presShell = document->GetShell(); if (presShell) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index f9f1dd526e..eeeb33f59c 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -34,6 +34,7 @@ #include "mozilla/layers/CompositorTypes.h" #include "nsIWebBrowserChrome3.h" #include "mozilla/dom/ipc/IdType.h" +#include "AudioChannelService.h" #include "PuppetWidget.h" class nsICachedFileDescriptorListener; @@ -42,22 +43,22 @@ class nsIDOMWindowUtils; namespace mozilla { namespace layout { class RenderFrameChild; -} +} // namespace layout namespace layers { class APZEventState; class ImageCompositeNotification; struct SetTargetAPZCCallback; struct SetAllowedTouchBehaviorCallback; -} +} // namespace layers namespace widget { struct AutoCacheNativeKeyCommands; -} +} // namespace widget namespace plugins { class PluginWidgetChild; -} +} // namespace plugins namespace dom { @@ -241,7 +242,7 @@ public: static already_AddRefed FindTabChild(const TabId& aTabId); public: - /** + /** * This is expected to be called off the critical path to content * startup. This is an opportunity to load things that are slow * on the critical path. @@ -333,6 +334,7 @@ public: const int32_t& aModifiers, const bool& aIgnoreRootScrollFrame) override; virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& event) override; + virtual bool RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& event) override; virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& event) override; virtual bool RecvRealDragEvent(const WidgetDragEvent& aEvent, const uint32_t& aDragAction, @@ -471,7 +473,7 @@ public: return GetFrom(docShell); } - virtual bool RecvUIResolutionChanged() override; + virtual bool RecvUIResolutionChanged(const float& aDpi, const double& aScale) override; virtual bool RecvThemeChanged(nsTArray&& aLookAndFeelIntCache) override; @@ -637,10 +639,11 @@ private: double mDefaultScale; bool mIPCOpen; bool mParentIsActive; - bool mAudioChannelActive; bool mAsyncPanZoomEnabled; CSSSize mUnscaledInnerSize; + nsAutoTArray mAudioChannelsActive; + DISALLOW_EVIL_CONSTRUCTORS(TabChild); }; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 480904bfa5..0ef3bbe47b 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1092,7 +1092,11 @@ TabParent::UIResolutionChanged() // mDPI being greater than 0, so this invalidates it. mDPI = -1; TryCacheDPIAndScale(); - unused << SendUIResolutionChanged(); + // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale + // fails to cache the values, then mDefaultScale.scale might be invalid. + // We don't want to send that value to content. Just send -1 for it too in + // that case. + unused << SendUIResolutionChanged(mDPI, mDPI < 0 ? -1.0 : mDefaultScale.scale); } } @@ -1391,7 +1395,11 @@ bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event) } if (NS_MOUSE_MOVE == event.mMessage) { - return SendRealMouseMoveEvent(event); + if (event.reason == WidgetMouseEvent::eSynthesized) { + return SendSynthMouseMoveEvent(event); + } else { + return SendRealMouseMoveEvent(event); + } } return SendRealMouseButtonEvent(event); } diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index fdc5a82ecd..6b29d6feec 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -701,8 +701,8 @@ Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID, : DOMEventTargetHelper(), mWorkerPrivate(nullptr), mObserver(nullptr), mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), - mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false), - mIsStored(false), mTaskCount(0) + mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mData(JS::NullValue()), + mIsClosed(false), mIsStored(false), mTaskCount(0) { if (NS_IsMainThread()) { // We can only call this on the main thread because @@ -832,13 +832,6 @@ Notification::PersistNotification() nsString alertName; GetAlertName(alertName); - nsString dataString; - nsCOMPtr scContainer; - scContainer = GetDataCloneContainer(); - if (scContainer) { - scContainer->GetDataAsBase64(dataString); - } - nsAutoString behavior; if (!mBehavior.ToJSON(behavior)) { return NS_ERROR_FAILURE; @@ -853,7 +846,7 @@ Notification::PersistNotification() mTag, mIconUrl, alertName, - dataString, + mDataAsBase64, behavior, mScope); @@ -918,6 +911,8 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal, Notification::~Notification() { + mData.setUndefined(); + mozilla::DropJSObjects(this); AssertIsOnTargetThread(); MOZ_ASSERT(!mFeature); MOZ_ASSERT(!mTempRef); @@ -925,15 +920,16 @@ Notification::~Notification() NS_IMPL_CYCLE_COLLECTION_CLASS(Notification) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mData) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataObjectContainer) + tmp->mData.setUndefined(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataObjectContainer) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper) @@ -1389,13 +1385,6 @@ Notification::ShowInternal() } MOZ_ASSERT(observer); - // mDataObjectContainer might be uninitialized here because the notification - // was constructed with an undefined data property. - nsString dataStr; - if (mDataObjectContainer) { - mDataObjectContainer->GetDataAsBase64(dataStr); - } - #ifdef MOZ_B2G nsCOMPtr appNotifier = do_GetService("@mozilla.org/system-alerts-service;1"); @@ -1423,7 +1412,7 @@ Notification::ShowInternal() ops.mDir = DirectionToString(mDir); ops.mLang = mLang; ops.mTag = mTag; - ops.mData = dataStr; + ops.mData = mDataAsBase64; ops.mMozbehavior = mBehavior; ops.mMozbehavior.mSoundFile = soundUrl; @@ -1466,7 +1455,7 @@ Notification::ShowInternal() alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true, uniqueCookie, observer, alertName, DirectionToString(mDir), mLang, - dataStr, GetPrincipal(), + mDataAsBase64, GetPrincipal(), inPrivateBrowsing); } @@ -1972,52 +1961,73 @@ Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) return NS_OK; } -nsIStructuredCloneContainer* Notification::GetDataCloneContainer() -{ - return mDataObjectContainer; -} - void Notification::GetData(JSContext* aCx, JS::MutableHandle aRetval) { - if (!mData && mDataObjectContainer) { + if (mData.isNull() && !mDataAsBase64.IsEmpty()) { nsresult rv; - rv = mDataObjectContainer->DeserializeToVariant(aCx, getter_AddRefs(mData)); + auto container = new nsStructuredCloneContainer(); + rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION, + aCx); if (NS_WARN_IF(NS_FAILED(rv))) { aRetval.setNull(); return; } + + JS::Rooted data(aCx); + rv = container->DeserializeToJsval(aCx, &data); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRetval.setNull(); + return; + } + + if (data.isGCThing()) { + mozilla::HoldJSObjects(this); + } + mData = data; } - if (!mData) { + if (mData.isNull()) { aRetval.setNull(); return; } - VariantToJsval(aCx, mData, aRetval); + + JS::ExposeValueToActiveJS(mData); + aRetval.set(mData); } void Notification::InitFromJSVal(JSContext* aCx, JS::Handle aData, ErrorResult& aRv) { - if (mDataObjectContainer || aData.isNull()) { + if (!mDataAsBase64.IsEmpty() || aData.isNull()) { return; } - mDataObjectContainer = new nsStructuredCloneContainer(); - aRv = mDataObjectContainer->InitFromJSVal(aData, aCx); + auto dataObjectContainer = new nsStructuredCloneContainer(); + aRv = dataObjectContainer->InitFromJSVal(aData, aCx); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + dataObjectContainer->GetDataAsBase64(mDataAsBase64); } void Notification::InitFromBase64(JSContext* aCx, const nsAString& aData, ErrorResult& aRv) { - if (mDataObjectContainer || aData.IsEmpty()) { + if (!mDataAsBase64.IsEmpty() || aData.IsEmpty()) { return; } + // To and fro to ensure it is valid base64. auto container = new nsStructuredCloneContainer(); aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION, aCx); - mDataObjectContainer = container; + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + container->GetDataAsBase64(mDataAsBase64); } bool diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index e7a685b71c..dd2825d349 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -17,7 +17,6 @@ #include "nsCycleCollectionParticipant.h" class nsIPrincipal; -class nsIStructuredCloneContainer; class nsIVariant; namespace mozilla { @@ -126,7 +125,7 @@ public: IMPL_EVENT_HANDLER(close) NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Notification, DOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper) static bool PrefEnabled(JSContext* aCx, JSObject* aObj); // Returns if Notification.get() is allowed for the current global. @@ -205,8 +204,6 @@ public: return mIsStored; } - nsIStructuredCloneContainer* GetDataCloneContainer(); - static bool RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */); static void RequestPermission(const GlobalObject& aGlobal, @@ -359,11 +356,11 @@ protected: const nsString mLang; const nsString mTag; const nsString mIconUrl; - nsCOMPtr mDataObjectContainer; + nsString mDataAsBase64; const NotificationBehavior mBehavior; // It's null until GetData is first called - nsCOMPtr mData; + JS::Heap mData; nsString mAlertName; nsString mScope; diff --git a/dom/workers/test/notification_worker.js b/dom/workers/test/notification_worker.js index b054a0a6db..cb55f83585 100644 --- a/dom/workers/test/notification_worker.js +++ b/dom/workers/test/notification_worker.js @@ -20,7 +20,9 @@ if (self.Notification) { lang: "", body: "This is a notification body", tag: "sometag", - icon: "icon.png" + icon: "icon.png", + data: ["a complex object that should be", { "structured": "cloned" }], + mozbehavior: { vibrationPattern: [30, 200, 30] }, }; var notification = new Notification("This is a title", options); @@ -36,6 +38,8 @@ if (self.Notification) { is(notification.body, options.body, "body should get set"); is(notification.tag, options.tag, "tag should get set"); is(notification.icon, options.icon, "icon should get set"); + is(notification.data[0], "a complex object that should be", "data item 0 should be a matching string"); + is(notification.data[1]["structured"], "cloned", "data item 1 should be a matching object literal"); // store notification in test context this.notification = notification; diff --git a/dom/workers/test/serviceworkers/notificationclick.html b/dom/workers/test/serviceworkers/notificationclick.html index 80531b85bb..448764a1cb 100644 --- a/dom/workers/test/serviceworkers/notificationclick.html +++ b/dom/workers/test/serviceworkers/notificationclick.html @@ -13,11 +13,11 @@ } navigator.serviceWorker.ready.then(function(swr) { - swr.showNotification("Hi there. The ServiceWorker should receive a click event for this."); + swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }}); }); navigator.serviceWorker.onmessage = function(msg) { - testWindow.callback(); + testWindow.callback(msg.data.result); }; diff --git a/dom/workers/test/serviceworkers/notificationclick.js b/dom/workers/test/serviceworkers/notificationclick.js index 63ab71c35b..39e7adb815 100644 --- a/dom/workers/test/serviceworkers/notificationclick.js +++ b/dom/workers/test/serviceworkers/notificationclick.js @@ -9,7 +9,11 @@ onnotificationclick = function(e) { } clients.forEach(function(client) { - client.postMessage("done"); + client.postMessage({ result: e.notification.data && + e.notification.data['complex'] && + e.notification.data['complex'][0] == "jsval" && + e.notification.data['complex'][1] == 5 }); + }); }); } diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html index 258736137f..04d63184b0 100644 --- a/dom/workers/test/serviceworkers/test_notificationclick.html +++ b/dom/workers/test/serviceworkers/test_notificationclick.html @@ -23,11 +23,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=916893 function testFrame(src) { var iframe = document.createElement("iframe"); iframe.src = src; - window.callback = function() { + window.callback = function(result) { window.callback = null; document.body.removeChild(iframe); iframe = null; - ok(true, "Got notificationclick event."); + ok(result, "Got notificationclick event with correct data."); MockServices.unregister(); SimpleTest.finish(); }; diff --git a/js/src/builtin/Date.js b/js/src/builtin/Date.js index 5bd8575c21..8e938b30da 100644 --- a/js/src/builtin/Date.js +++ b/js/src/builtin/Date.js @@ -5,9 +5,73 @@ /*global intl_DateTimeFormat: false, */ +// This cache, once primed, has these properties: +// +// runtimeDefaultLocale: +// Locale information provided by the embedding, guiding SpiderMonkey's +// selection of a default locale. See RuntimeDefaultLocale(), whose +// value controls the value returned by DefaultLocale() that's what's +// *actually* used. +// localTZA: +// The local time zone's adjustment from UTC. See LocalTZA(). +// formatters: +// A Record storing formatters consistent with the above +// runtimeDefaultLocale/localTZA values, for use with the appropriate +// ES6 toLocale*String Date method when called with its first two +// arguments having the value |undefined|. +// +// The "formatters" Record has (some subset of) these properties, as determined +// by all values of the first argument passed to |GetCachedFormat|: +// +// dateTimeFormat: for Date's toLocaleString operation +// dateFormat: for Date's toLocaleDateString operation +// timeFormat: for Date's toLocaleTimeString operation +// +// Using this cache, then, requires 1) verifying the current +// runtimeDefaultLocale/localTZA are consistent with cached values, then +// 2) seeing if the desired formatter is cached and returning it if so, or else +// 3) create the desired formatter and store and return it. var dateTimeFormatCache = new Record(); +/** + * Get a cached DateTimeFormat formatter object, created like so: + * + * var opts = ToDateTimeOptions(undefined, required, defaults); + * return new Intl.DateTimeFormat(undefined, opts); + * + * |format| must be a key from the "formatters" Record described above. + */ +function GetCachedFormat(format, required, defaults) { + assert(format === "dateTimeFormat" || + format === "dateFormat" || + format === "timeFormat", + "unexpected format key: please update the comment by " + + "dateTimeFormatCache"); + + var runtimeDefaultLocale = RuntimeDefaultLocale(); + var localTZA = LocalTZA(); + + var formatters; + if (dateTimeFormatCache.runtimeDefaultLocale !== runtimeDefaultLocale || + dateTimeFormatCache.localTZA !== localTZA) + { + formatters = dateTimeFormatCache.formatters = new Record(); + dateTimeFormatCache.runtimeDefaultLocale = runtimeDefaultLocale; + dateTimeFormatCache.localTZA = localTZA; + } else { + formatters = dateTimeFormatCache.formatters; + } + + var fmt = formatters[format]; + if (fmt === undefined) { + var options = ToDateTimeOptions(undefined, required, defaults); + fmt = formatters[format] = intl_DateTimeFormat(undefined, options); + } + + return fmt; +} + /** * Format this Date object into a date and time string, using the locale and * formatting options provided. @@ -30,11 +94,7 @@ function Date_toLocaleString() { if (locales === undefined && options === undefined) { // This cache only optimizes for the old ES5 toLocaleString without // locales and options. - if (dateTimeFormatCache.dateTimeFormat === undefined) { - options = ToDateTimeOptions(options, "any", "all"); - dateTimeFormatCache.dateTimeFormat = intl_DateTimeFormat(locales, options); - } - dateTimeFormat = dateTimeFormatCache.dateTimeFormat; + dateTimeFormat = GetCachedFormat("dateTimeFormat", "any", "all"); } else { options = ToDateTimeOptions(options, "any", "all"); dateTimeFormat = intl_DateTimeFormat(locales, options); @@ -67,11 +127,7 @@ function Date_toLocaleDateString() { if (locales === undefined && options === undefined) { // This cache only optimizes for the old ES5 toLocaleDateString without // locales and options. - if (dateTimeFormatCache.dateFormat === undefined) { - options = ToDateTimeOptions(options, "date", "date"); - dateTimeFormatCache.dateFormat = intl_DateTimeFormat(locales, options); - } - dateTimeFormat = dateTimeFormatCache.dateFormat; + dateTimeFormat = GetCachedFormat("dateFormat", "date", "date"); } else { options = ToDateTimeOptions(options, "date", "date"); dateTimeFormat = intl_DateTimeFormat(locales, options); @@ -104,11 +160,7 @@ function Date_toLocaleTimeString() { if (locales === undefined && options === undefined) { // This cache only optimizes for the old ES5 toLocaleTimeString without // locales and options. - if (dateTimeFormatCache.timeFormat === undefined) { - options = ToDateTimeOptions(options, "time", "time"); - dateTimeFormatCache.timeFormat = intl_DateTimeFormat(locales, options); - } - dateTimeFormat = dateTimeFormatCache.timeFormat; + dateTimeFormat = GetCachedFormat("timeFormat", "time", "time"); } else { options = ToDateTimeOptions(options, "time", "time"); dateTimeFormat = intl_DateTimeFormat(locales, options); diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp index ad9fc9e98d..b96c89defe 100644 --- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -62,7 +62,7 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj) return nullptr; RootedFunction ctor(cx, global->createConstructor(cx, construct, - ClassName(JSProto_Symbol, cx), 1)); + ClassName(JSProto_Symbol, cx), 0)); if (!ctor) return nullptr; diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 5a0d8217d5..d88adb7889 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -18,6 +18,7 @@ UNIFIED_SOURCES += [ 'testClassGetter.cpp', 'testCloneScript.cpp', 'testContexts.cpp', + 'testDateToLocaleString.cpp', 'testDebugger.cpp', 'testDeepFreeze.cpp', 'testDefineGetterSetterNonEnumerable.cpp', diff --git a/js/src/jsapi-tests/testDateToLocaleString.cpp b/js/src/jsapi-tests/testDateToLocaleString.cpp new file mode 100644 index 0000000000..495f7cffd2 --- /dev/null +++ b/js/src/jsapi-tests/testDateToLocaleString.cpp @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + */ +/* 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/. */ + +#include "jsapi-tests/tests.h" + +BEGIN_TEST(testDateToLocaleString) +{ + // This test should only attempt to run if we have Intl support: necessary + // to properly assume that changes to the default locale will predictably + // affect the behavior of the locale-sensitive Date methods tested here. + JS::Rooted haveIntl(cx); + EVAL("typeof Intl !== 'undefined'", &haveIntl); + if (!haveIntl.toBoolean()) + return true; + + // Pervasive assumption: our Intl support includes "de" (German) and + // "en" (English) and treats them differently for purposes of + // Date.prototype.toLocale{,Date,Time}String behavior. + + // Start with German. + CHECK(JS_SetDefaultLocale(rt, "de")); + + // The (constrained) Date object we'll use to test behavior. + EXEC("var d = new Date(Date.UTC(2015, 9 - 1, 17));"); + + // Test that toLocaleString behavior changes with default locale changes. + EXEC("var deAll = d.toLocaleString();"); + + CHECK(JS_SetDefaultLocale(rt, "en")); + EXEC("if (d.toLocaleString() === deAll) \n" + " throw 'toLocaleString results should have changed with system locale change';"); + + // Test that toLocaleDateString behavior changes with default locale changes. + EXEC("var enDate = d.toLocaleDateString();"); + + CHECK(JS_SetDefaultLocale(rt, "de")); + EXEC("if (d.toLocaleDateString() === enDate) \n" + " throw 'toLocaleDateString results should have changed with system locale change';"); + + // Test that toLocaleTimeString behavior changes with default locale changes. + EXEC("var deTime = d.toLocaleTimeString();"); + + CHECK(JS_SetDefaultLocale(rt, "en")); + EXEC("if (d.toLocaleTimeString() === deTime) \n" + " throw 'toLocaleTimeString results should have changed with system locale change';"); + + JS_ResetDefaultLocale(rt); + return true; +} +END_TEST(testDateToLocaleString) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d7048849fb..e2f1c71772 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -556,10 +556,10 @@ XDRRelazificationInfo(XDRState* xdr, HandleFunction fun, HandleScript scri } static inline uint32_t -FindScopeObjectIndex(JSScript *script, NestedScopeObject& scope) +FindScopeObjectIndex(JSScript* script, NestedScopeObject& scope) { - ObjectArray *objects = script->objects(); - HeapPtrObject *vector = objects->vector; + ObjectArray* objects = script->objects(); + HeapPtrObject* vector = objects->vector; unsigned length = objects->length; for (unsigned i = 0; i < length; ++i) { if (vector[i] == &scope) @@ -965,11 +965,11 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript * after the enclosing block has been XDR'd. */ for (i = 0; i != nobjects; ++i) { - HeapPtrObject *objp = &script->objects()->vector[i]; + HeapPtrObject* objp = &script->objects()->vector[i]; XDRClassKind classk; if (mode == XDR_ENCODE) { - JSObject *obj = *objp; + JSObject* obj = *objp; if (obj->is()) classk = CK_BlockObject; else if (obj->is()) @@ -2655,19 +2655,19 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t ncon if (nobjects != 0) { script->objects()->length = nobjects; - script->objects()->vector = (HeapPtrObject *)cursor; + script->objects()->vector = (HeapPtrObject*)cursor; cursor += nobjects * sizeof(script->objects()->vector[0]); } if (nregexps != 0) { script->regexps()->length = nregexps; - script->regexps()->vector = (HeapPtrObject *)cursor; + script->regexps()->vector = (HeapPtrObject*)cursor; cursor += nregexps * sizeof(script->regexps()->vector[0]); } if (ntrynotes != 0) { script->trynotes()->length = ntrynotes; - script->trynotes()->vector = reinterpret_cast(cursor); + script->trynotes()->vector = reinterpret_cast(cursor); size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); #ifdef DEBUG memset(cursor, 0, vectorSize); @@ -3250,7 +3250,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri AutoObjectVector objects(cx); if (nobjects != 0) { - HeapPtrObject *vector = src->objects()->vector; + HeapPtrObject* vector = src->objects()->vector; for (unsigned i = 0; i < nobjects; i++) { RootedObject obj(cx, vector[i]); RootedObject clone(cx); @@ -3360,13 +3360,13 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom()); } if (nobjects != 0) { - HeapPtrObject *vector = Rebase(dst, src, src->objects()->vector); + HeapPtrObject* vector = Rebase(dst, src, src->objects()->vector); dst->objects()->vector = vector; for (unsigned i = 0; i < nobjects; ++i) vector[i].init(&objects[i]->as()); } if (nregexps != 0) { - HeapPtrObject *vector = Rebase(dst, src, src->regexps()->vector); + HeapPtrObject* vector = Rebase(dst, src, src->regexps()->vector); dst->regexps()->vector = vector; for (unsigned i = 0; i < nregexps; ++i) vector[i].init(®exps[i]->as()); @@ -4064,8 +4064,8 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); } -/* static */ LazyScript * -LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, +/* static */ LazyScript* +LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) { diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4678438c9b..dc9dee2715 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1525,7 +1525,6 @@ RopeMatch(JSContext* cx, JSRope* text, JSLinearString* pat, int* match) return true; } - /* ES6 draft rc4 21.1.3.7. */ static bool str_includes(JSContext* cx, unsigned argc, Value* vp) @@ -2274,8 +2273,8 @@ DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLine /* ES5 15.5.4.10 step 8. */ static bool -DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, - HandleLinearString input, StringRegExpGuard& g) +DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input, + StringRegExpGuard& g) { // Step 8a. // diff --git a/js/src/tests/ecma_6/Symbol/surfaces.js b/js/src/tests/ecma_6/Symbol/surfaces.js index 0c0c2021f8..7b3ef9f9b2 100644 --- a/js/src/tests/ecma_6/Symbol/surfaces.js +++ b/js/src/tests/ecma_6/Symbol/surfaces.js @@ -8,7 +8,7 @@ assertEq(desc.configurable, true); assertEq(desc.enumerable, false); assertEq(desc.writable, true); assertEq(typeof Symbol, "function"); -assertEq(Symbol.length, 1); +assertEq(Symbol.length, 0); desc = Object.getOwnPropertyDescriptor(Symbol, "prototype"); assertEq(desc.configurable, false); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 7c04e629cc..3afb3697e9 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1261,6 +1261,16 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_LocalTZA(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0, "the LocalTZA intrinsic takes no arguments"); + + args.rval().setDouble(cx->runtime()->dateTimeInfo.localTZA()); + return true; +} + static bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp) { @@ -1382,6 +1392,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), + JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0), JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, IntrinsicIsConstructing), diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm index 7907ee5742..b067dfcdb0 100644 --- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -105,18 +105,20 @@ this.BrowserTestUtils = { * @resolves The tab switched to. */ switchTab(tabbrowser, tab) { + let promise = new Promise(resolve => { + tabbrowser.addEventListener("TabSwitchDone", function onSwitch() { + tabbrowser.removeEventListener("TabSwitchDone", onSwitch); + TestUtils.executeSoon(() => resolve(tabbrowser.selectedTab)); + }); + }); + if (typeof tab == "function") { tab(); } else { tabbrowser.selectedTab = tab; } - - //XXX this is esr38, we do not have e10s, this is never async, we do not pass go - // and do not collect, err, I mean, we continue without waiting for an event: - return new Promise(resolve => { - TestUtils.executeSoon(() => resolve(tabbrowser.selectedTab)); - }); + return promise; }, /** diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index 38690eadcc..6991f48698 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -196,10 +196,11 @@ public: virtual bool NeedsPaint() override; virtual TabChild* GetOwningTabChild() override { return mTabChild; } - virtual void ClearBackingScaleCache() + + void UpdateBackingScaleCache(float aDpi, double aScale) { - mDPI = -1; - mDefaultScale = -1; + mDPI = aDpi; + mDefaultScale = aScale; } nsIntSize GetScreenDimensions();