author | Ed Morley <emorley@mozilla.com> |
Tue, 18 Jun 2013 12:05:32 +0100 | |
changeset 135462 | a4d01597aecbd4a295c176a65c05df1f9dc36863 |
parent 135417 | 58b1f6b4b57092c69417511f92f6f456cac1d3c9 (current diff) |
parent 135461 | 0fcf27567179423917443e46f967e3cfe8dff05d (diff) |
child 135463 | bef58eae5f12f908822b0a355abd558622d03418 |
push id | 24841 |
push user | ryanvm@gmail.com |
push date | Tue, 18 Jun 2013 23:04:53 +0000 |
treeherder | mozilla-central@d2a7cfa34154 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 24.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js +++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js @@ -1145,8 +1145,12 @@ function openBrowserWindow(callback, url // Helper for calling code at window close function closeBrowserWindow(window, callback) { window.addEventListener("unload", function unload() { window.removeEventListener("unload", unload, false); callback(); }, false); window.close(); } + +// Test disabled on Linux because of bug 882867 +if (require("sdk/system/runtime").OS == "Linux") + module.exports = {};
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js +++ b/browser/metro/base/content/helperui/SelectionHelperUI.js @@ -827,45 +827,35 @@ var SelectionHelperUI = { // shutdown but leave focus alone. the event will fall through // and the dom will update focus for us. If the user tapped on // another input, we'll get a attachToCaret call soonish on the // new input. this.closeEditSession(false); return; } - let selectionTap = this._hitTestSelection(aEvent); - - // If the tap is in the selection, just ignore it. We disallow this - // since we always get a single tap before a double, and double tap - // copies selected text. - if (selectionTap) { - if (!this._targetIsEditable) { - this.closeEditSession(false); - return; - } + if (this._hitTestSelection(aEvent) && this._targetIsEditable) { // Attach to the newly placed caret position this._sendAsyncMessage("Browser:CaretAttach", { xPos: aEvent.clientX, yPos: aEvent.clientY }); return; } // A tap within an editable but outside active selection, clear the // selection and flip back to caret mode. if (this.startMark.visible && pointInTargetElement && this._targetIsEditable) { this._transitionFromSelectionToCaret(clientCoords.x, clientCoords.y); + return; } - // If we have active selection in anything else don't let the event get - // to content. Prevents random taps from killing active selection. - aEvent.stopPropagation(); - aEvent.preventDefault(); + // Close when we get a single tap in content. + this.closeEditSession(false); }, _onKeypress: function _onKeypress() { this.closeEditSession(); }, _onResize: function _onResize() { this._sendAsyncMessage("Browser:SelectionUpdate", {});
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js +++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js @@ -301,125 +301,56 @@ gTests.push({ clearSelection(gFrame); yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); yield hideContextUI(); }, }); -/* -disable until bug 860248 is addressed. gTests.push({ - desc: "double-tap copy text in content", - setUp: setUpHelper, + desc: "tap on selection clears selection in content", + setUp: setUpAndTearDown, run: function test() { sendContextMenuClick(30, 20); yield waitForCondition(function () { return SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - sendDoubleTap(gWindow, 30, 20); + sendTap(gWindow, 30, 20); yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - - // check copy text results - let text = SpecialPowers.getClipboardData("text/unicode").trim(); - is(text, "There", "copy text test"); - - // check for active selection - is(getTrimmedSelection(gWindow).toString(), "", "selection test"); }, - tearDown: tearDownHelper, + tearDown: setUpAndTearDown, }); gTests.push({ - desc: "double-tap copy text in scrolled content", - setUp: setUpHelper, + desc: "tap off selection clears selection in content", + setUp: setUpAndTearDown, run: function test() { - let scrollPromise = waitForEvent(gWindow, "scroll"); - gWindow.scrollBy(0, 200); - yield scrollPromise; - ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error"); - sendContextMenuClick(30, 100); + sendContextMenuClick(30, 20); yield waitForCondition(function () { return SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - sendDoubleTap(gWindow, 42, 100); - - yield waitForCondition(function () { - return !SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - // check copy text results - let text = SpecialPowers.getClipboardData("text/unicode"); - is(text, "suddenly", "copy text test"); + sendTap(gWindow, 30, 100); - // check for active selection - is(getTrimmedSelection(gWindow).toString(), "", "selection test"); - }, - tearDown: function tearDown() { - emptyClipboard(); - clearSelection(gWindow); - let scrollPromise = waitForEvent(gWindow, "scroll"); - gWindow.scrollBy(0, -200); - yield scrollPromise; yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); }, + tearDown: setUpAndTearDown, }); -gTests.push({ - desc: "single clicks on selection in non-editable content", - setUp: setUpHelper, - run: function test() { - sendContextMenuClick(100, 20); - - yield waitForCondition(function () { - return SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - // active state - is(SelectionHelperUI.isActive, true, "selection active"); - - let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY; - let touchdrag = new TouchDragAndHold(); - yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos); - touchdrag.end(); - - yield waitForCondition(function () { - return !SelectionHelperUI.hasActiveDrag; - }, kCommonWaitMs, kCommonPollMs); - yield SelectionHelperUI.pingSelectionHandler(); - - // active state - is(SelectionHelperUI.isActive, true, "selection active"); - - // click on selected text - nothing should change - sendTap(gWindow, 240, 20); - - is(SelectionHelperUI.isActive, true, "selection active"); - - // click outside the text - nothing should change - sendTap(gWindow, 197, 119); - - is(SelectionHelperUI.isActive, true, "selection active"); - }, - tearDown: tearDownHelper, -}); -*/ - function test() { if (!isLandscapeMode()) { todo(false, "browser_selection_tests need landscape mode to run."); return; } requestLongerTimeout(3); runTests();
--- a/content/base/src/nsDOMFileReader.h +++ b/content/base/src/nsDOMFileReader.h @@ -140,12 +140,12 @@ protected: nsCString mCharset; uint32_t mDataLen; eDataFormat mDataFormat; nsString mResult; nsCOMPtr<nsIPrincipal> mPrincipal; - JSObject* mResultArrayBuffer; + JS::Heap<JSObject*> mResultArrayBuffer; }; #endif
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1781,17 +1781,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END struct CustomPrototypeTraceArgs { const TraceCallbacks& callbacks; void* closure; }; static PLDHashOperator -CustomPrototypeTrace(const nsAString& aName, JSObject*& aObject, void *aArg) +CustomPrototypeTrace(const nsAString& aName, JS::Heap<JSObject*>& aObject, void *aArg) { CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg); MOZ_ASSERT(aObject, "Protocol object value must not be null"); traceArgs->callbacks.Trace(&aObject, "mCustomPrototypes entry", traceArgs->closure); return PL_DHASH_NEXT; }
--- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -26,16 +26,17 @@ #include "nsIContent.h" #include "nsEventListenerManager.h" #include "nsIDOMNodeSelector.h" #include "nsIPrincipal.h" #include "nsIParser.h" #include "nsBindingManager.h" #include "nsINodeInfo.h" #include "nsInterfaceHashtable.h" +#include "nsJSThingHashtable.h" #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsIScriptObjectPrincipal.h" #include "nsIURI.h" #include "nsScriptLoader.h" #include "nsIRadioGroupContainer.h" #include "nsILayoutHistoryState.h" #include "nsIRequest.h" @@ -1198,17 +1199,17 @@ protected: nsTArray<nsWeakPtr> mFullScreenStack; // The root of the doc tree in which this document is in. This is only // non-null when this document is in fullscreen mode. nsWeakPtr mFullscreenRoot; // Hashtable for custom element prototypes in web components. // Custom prototypes are in the document's compartment. - nsDataHashtable<nsStringHashKey, JSObject*> mCustomPrototypes; + nsJSThingHashtable<nsStringHashKey, JSObject*> mCustomPrototypes; nsRefPtr<nsEventListenerManager> mListenerManager; nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets; nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList; nsRefPtr<nsScriptLoader> mScriptLoader; nsDocHeaderData* mHeaderData; /* mIdentifierMap works as follows for IDs: * 1) Attribute changes affect the table immediately (removing and adding
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -777,22 +777,24 @@ nsresult nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx) { if (!aCx) { return NS_ERROR_FAILURE; } RootJSResultObjects(); // The Unicode converter has already zapped the BOM if there was one + JS::Rooted<JS::Value> value(aCx); if (!JS_ParseJSON(aCx, static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(), - JS::MutableHandle<JS::Value>::fromMarkedLocation(&mResultJSON))) { + &value)) { return NS_ERROR_FAILURE; } + mResultJSON = value; return NS_OK; } void nsXMLHttpRequest::CreatePartialBlob() { if (mDOMFile) { if (mLoadTotal == mLoadTransferred) {
--- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -652,24 +652,24 @@ protected: * @param aType The progress event type. * @param aFlag A XML_HTTP_REQUEST_* state flag defined in * nsXMLHttpRequest.cpp. */ void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag); bool mFirstStartRequestSeen; bool mInLoadProgressEvent; - + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; nsCOMPtr<nsIChannel> mNewRedirectChannel; - - JS::Value mResultJSON; + + JS::Heap<JS::Value> mResultJSON; js::ArrayBufferBuilder mArrayBufferBuilder; - JSObject* mResultArrayBuffer; + JS::Heap<JSObject*> mResultArrayBuffer; void ResetResponse(); struct RequestHeader { nsCString header; nsCString value; };
--- a/content/canvas/src/ImageData.h +++ b/content/canvas/src/ImageData.h @@ -64,15 +64,15 @@ public: private: void HoldData(); void DropData(); ImageData() MOZ_DELETE; uint32_t mWidth, mHeight; - JSObject* mData; + JS::Heap<JSObject*> mData; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_ImageData_h
--- a/content/events/src/nsDOMMessageEvent.h +++ b/content/events/src/nsDOMMessageEvent.h @@ -60,15 +60,15 @@ public: nsIDOMWindow* aSource, mozilla::ErrorResult& aRv) { aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData, aOrigin, aLastEventId, aSource); } private: - JS::Value mData; + JS::Heap<JS::Value> mData; nsString mOrigin; nsString mLastEventId; nsCOMPtr<nsIDOMWindow> mSource; }; #endif // nsDOMMessageEvent_h__
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.h +++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.h @@ -65,13 +65,13 @@ public: uint32_t aFrameBufferLength, float aTime, bool aAllowAudioData, mozilla::ErrorResult& aRv); private: nsAutoArrayPtr<float> mFrameBuffer; uint32_t mFrameBufferLength; float mTime; - JSObject* mCachedArray; + JS::Heap<JSObject*> mCachedArray; bool mAllowAudioData; }; #endif // nsDOMNotifyAudioAvailableEvent_h_
--- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -292,17 +292,17 @@ protected: nsRefPtr<nsContentList> mApplets; nsRefPtr<nsContentList> mEmbeds; nsRefPtr<nsContentList> mLinks; nsRefPtr<nsContentList> mAnchors; nsRefPtr<nsContentList> mScripts; nsRefPtr<nsContentList> mForms; nsRefPtr<nsContentList> mFormControls; - JSObject* mAll; + JS::Heap<JSObject*> mAll; /** # of forms in the document, synchronously set */ int32_t mNumForms; static uint32_t gWyciwygSessionCnt; static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV, int32_t& aCharsetSource,
--- a/content/media/MediaDecoder.cpp +++ b/content/media/MediaDecoder.cpp @@ -1041,42 +1041,16 @@ void MediaDecoder::NotifyBytesConsumed(i NS_ENSURE_TRUE_VOID(mDecoderStateMachine); MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread()); if (!mIgnoreProgressData) { mDecoderPosition += aBytes; mPlaybackStatistics.AddBytes(aBytes); } } -void MediaDecoder::NextFrameUnavailableBuffering() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING); -} - -void MediaDecoder::NextFrameAvailable() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_AVAILABLE); -} - -void MediaDecoder::NextFrameUnavailable() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE); -} - void MediaDecoder::UpdateReadyStateForData() { MOZ_ASSERT(NS_IsMainThread()); if (!mOwner || mShuttingDown || !mDecoderStateMachine) return; MediaDecoderOwner::NextFrameStatus frameStatus = mDecoderStateMachine->GetNextFrameStatus(); mOwner->UpdateReadyStateForData(frameStatus);
--- a/content/media/MediaDecoder.h +++ b/content/media/MediaDecoder.h @@ -705,22 +705,16 @@ public: // thread. void SeekingStarted(); // Called when the backend has changed the current playback // position. It dispatches a timeupdate event and invalidates the frame. // This must be called on the main thread only. void PlaybackPositionChanged(); - // Calls mElement->UpdateReadyStateForData, telling it which state we have - // entered. Main thread only. - void NextFrameUnavailableBuffering(); - void NextFrameAvailable(); - void NextFrameUnavailable(); - // Calls mElement->UpdateReadyStateForData, telling it whether we have // data for the next frame and if we're buffering. Main thread only. void UpdateReadyStateForData(); // Find the end of the cached data starting at the current decoder // position. int64_t GetDownloadPosition();
--- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -2615,31 +2615,26 @@ void MediaDecoderStateMachine::UpdateRea mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus(); if (nextFrameStatus == mLastFrameStatus) { return; } mLastFrameStatus = nextFrameStatus; + /* This is a bit tricky. MediaDecoder::UpdateReadyStateForData will run on + * the main thread and re-evaluate GetNextFrameStatus there, passing it to + * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of + * GetNextFrameStatus we computed here, because what we're computing here + * could be stale by the time MediaDecoder::UpdateReadyStateForData runs. + * We only compute GetNextFrameStatus here to avoid posting runnables to the main + * thread unnecessarily. + */ nsCOMPtr<nsIRunnable> event; - switch (nextFrameStatus) { - case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering); - break; - case MediaDecoderOwner::NEXT_FRAME_AVAILABLE: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable); - break; - case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable); - break; - default: - PR_NOT_REACHED("unhandled frame state"); - } - + event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); } bool MediaDecoderStateMachine::JustExitedQuickBuffering() { return !mDecodeStartTime.IsNull() && mQuickBuffering && (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
--- a/content/media/webaudio/AudioBuffer.cpp +++ b/content/media/webaudio/AudioBuffer.cpp @@ -74,17 +74,17 @@ AudioBuffer::InitializeBuffers(uint32_t if (!mJSChannels.SetCapacity(aNumberOfChannels)) { return false; } for (uint32_t i = 0; i < aNumberOfChannels; ++i) { JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength)); if (!array) { return false; } - mJSChannels.AppendElement(array); + mJSChannels.AppendElement(array.get()); } return true; } JSObject* AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) {
--- a/content/media/webaudio/AudioBuffer.h +++ b/content/media/webaudio/AudioBuffer.h @@ -111,17 +111,17 @@ public: void MixToMono(JSContext* aJSContext); protected: bool RestoreJSChannelData(JSContext* aJSContext); void ClearJSChannels(); nsRefPtr<AudioContext> mContext; // Float32Arrays - AutoFallibleTArray<JSObject*,2> mJSChannels; + AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels; // mSharedChannels aggregates the data from mJSChannels. This is non-null // if and only if the mJSChannels are neutered. nsRefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels; uint32_t mLength; float mSampleRate; };
--- a/content/media/webaudio/WaveShaperNode.h +++ b/content/media/webaudio/WaveShaperNode.h @@ -32,15 +32,15 @@ public: return mCurve; } void SetCurve(const Float32Array* aData); private: void ClearCurve(); private: - JSObject* mCurve; + JS::Heap<JSObject*> mCurve; }; } } #endif
new file mode 100644 --- /dev/null +++ b/content/xbl/src/nsXBLMaybeCompiled.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXBLMaybeCompiled_h__ +#define nsXBLMaybeCompiled_h__ + +#include "js/RootingAPI.h" + +/* + * A union containing either a pointer representing uncompiled source or a + * JSObject* representing the compiled result. The class is templated on the + * source object type. + * + * The purpose of abstracting this as a separate class is to allow it to be + * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject + * pointer, when present. + */ +template <class UncompiledT> +class nsXBLMaybeCompiled +{ +public: + nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(UncompiledT* uncompiled) + : mUncompiled(reinterpret_cast<uintptr_t>(uncompiled) | BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {} + + bool IsCompiled() const + { + return !(mUncompiled & BIT_UNCOMPILED); + } + + UncompiledT* GetUncompiled() const + { + MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled"); + uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED; + return reinterpret_cast<UncompiledT*>(unmasked); + } + + JSObject* GetJSFunction() const + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + +private: + JSObject*& UnsafeGetJSFunction() + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + + enum { BIT_UNCOMPILED = 1 << 0 }; + + union + { + // An pointer that represents the function before being compiled, with + // BIT_UNCOMPILED set. + uintptr_t mUncompiled; + + // The JS object for the compiled result. + JSObject* mCompiled; + }; + + friend class js::RootMethods<nsXBLMaybeCompiled<UncompiledT> >; +}; + +/* Add support for JS::Heap<nsXBLMaybeCompiled>. */ +namespace js { + +template <class UncompiledT> +struct RootMethods<nsXBLMaybeCompiled<UncompiledT> > : public RootMethods<JSObject *> +{ + typedef struct RootMethods<JSObject *> Base; + + static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); } + + static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function) + { + return function.IsCompiled() && Base::poisoned(function.GetJSFunction()); + } + + static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function) + { + return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction()); + } + +#ifdef JSGC_GENERATIONAL + static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp) + { + Base::postBarrier(&functionp->UnsafeGetJSFunction()); + } + + static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp) + { + Base::relocate(&functionp->UnsafeGetJSFunction()); + } +#endif +}; + +template <class UncompiledT> +class HeapBase<nsXBLMaybeCompiled<UncompiledT> > +{ + const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const { + return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this); + } + + JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() { + return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this); + } + + const nsXBLMaybeCompiled<UncompiledT>* extract() const { + return wrapper().address(); + } + + nsXBLMaybeCompiled<UncompiledT>* extract() { + return wrapper().unsafeGet(); + } + +public: + bool IsCompiled() const { return extract()->IsCompiled(); } + UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); } + JSObject* GetJSFunction() const { return extract()->GetJSFunction(); } + + void SetUncompiled(UncompiledT* source) { + wrapper().set(nsXBLMaybeCompiled<UncompiledT>(source)); + } + + void SetJSFunction(JSObject* function) { + wrapper().set(nsXBLMaybeCompiled<UncompiledT>(function)); + } + + JS::Heap<JSObject*>& AsHeapObject() + { + MOZ_ASSERT(extract()->IsCompiled()); + return *reinterpret_cast<JS::Heap<JSObject*>*>(this); + } +}; + +} /* namespace js */ + +#endif // nsXBLMaybeCompiled_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -19,18 +19,18 @@ #include "nsIScriptSecurityManager.h" #include "nsIXPConnect.h" #include "xpcpublic.h" #include "nsXBLPrototypeBinding.h" using namespace mozilla; nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) : - nsXBLProtoImplMember(aName), - mUncompiledMethod(BIT_UNCOMPILED) + nsXBLProtoImplMember(aName), + mMethod() { MOZ_COUNT_CTOR(nsXBLProtoImplMethod); } nsXBLProtoImplMethod::~nsXBLProtoImplMethod() { MOZ_COUNT_DTOR(nsXBLProtoImplMethod); @@ -103,22 +103,23 @@ nsXBLProtoImplMethod::InstallMember(JSCo "Should not be installing an uncompiled method"); MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject)); NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSMethodObject) { + JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod()); + if (jsMethodObject) { nsDependentString name(mName); // First, make the function in the compartment of the scope object. JSAutoCompartment ac(aCx, scopeObject); - JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, mJSMethodObject, scopeObject)); + JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject)); if (!method) { return NS_ERROR_OUT_OF_MEMORY; } // Then, enter the content compartment, wrap the method pointer, and define // the wrapped version on the class object. JSAutoCompartment ac2(aCx, aTargetClassObject); if (!JS_WrapObject(aCx, method.address())) @@ -144,27 +145,27 @@ nsXBLProtoImplMethod::CompileMember(nsIS NS_PRECONDITION(aClassObject, "Must have class object to compile"); nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); // No parameters or body was supplied, so don't install method. if (!uncompiledMethod) { // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } // Don't install method if no name was supplied. if (!mName) { delete uncompiledMethod; // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } // We have a method. // Allocate an array for our arguments. int32_t paramCount = uncompiledMethod->GetParameterCount(); char** args = nullptr; @@ -214,73 +215,69 @@ nsXBLProtoImplMethod::CompileMember(nsIS // Destroy our uncompiled method and delete our arg list. delete uncompiledMethod; delete [] args; if (NS_FAILED(rv)) { SetUncompiledMethod(nullptr); return rv; } - mJSMethodObject = methodObject; + SetCompiledMethod(methodObject); return NS_OK; } void nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { - if (IsCompiled() && mJSMethodObject) { - aCallbacks.Trace(&mJSMethodObject, "mJSMethodObject", aClosure); + if (IsCompiled() && GetCompiledMethod()) { + aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure); } } nsresult nsXBLProtoImplMethod::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream) { JS::Rooted<JSObject*> methodObject(aContext->GetNativeContext()); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &methodObject); if (NS_FAILED(rv)) { SetUncompiledMethod(nullptr); return rv; } - mJSMethodObject = methodObject; - -#ifdef DEBUG - mIsCompiled = true; -#endif + SetCompiledMethod(methodObject); return NS_OK; } nsresult nsXBLProtoImplMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(XBLBinding_Serialize_Method); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteWStringZ(mName); NS_ENSURE_SUCCESS(rv, rv); - return XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject)); + return XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); } return NS_OK; } nsresult nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) { NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method"); - if (!mJSMethodObject) { + if (!GetCompiledMethod()) { // Nothing to do here return NS_OK; } // Get the script context the same way // nsXBLProtoImpl::InstallImplementation does. nsIDocument* document = aBoundElement->OwnerDoc(); @@ -322,17 +319,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n JSAutoCompartment ac(cx, scopeObject); if (!JS_WrapObject(cx, thisObject.address())) return NS_ERROR_OUT_OF_MEMORY; // Clone the function object, using thisObject as the parent so "this" is in // the scope chain of the resulting function (for backwards compat to the // days when this was an event handler). - JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject)); + JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject)); if (!method) return NS_ERROR_OUT_OF_MEMORY; // Now call the method // Check whether it's OK to call the method. rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method, thisObject); @@ -359,19 +356,19 @@ nsXBLProtoImplAnonymousMethod::Execute(n return NS_OK; } nsresult nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream, XBLBindingSerializeDetails aType) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(aType); NS_ENSURE_SUCCESS(rv, rv); - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject)); + rv = XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
--- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -6,16 +6,17 @@ #ifndef nsXBLProtoImplMethod_h__ #define nsXBLProtoImplMethod_h__ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" #include "jsapi.h" #include "nsString.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" #include "nsXBLSerialize.h" class nsIContent; struct nsXBLParameter { nsXBLParameter* mNext; char* mName; @@ -97,39 +98,41 @@ public: virtual void Trace(const TraceCallbacks& aCallbacks, void *aClosure) MOZ_OVERRIDE; nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream); virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE; bool IsCompiled() const { - return !(mUncompiledMethod & BIT_UNCOMPILED); + return mMethod.IsCompiled(); } + void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod) { - mUncompiledMethod = uintptr_t(aUncompiledMethod) | BIT_UNCOMPILED; + mMethod.SetUncompiled(aUncompiledMethod); } + nsXBLUncompiledMethod* GetUncompiledMethod() const { - uintptr_t unmasked = mUncompiledMethod & ~BIT_UNCOMPILED; - return reinterpret_cast<nsXBLUncompiledMethod*>(unmasked); + return mMethod.GetUncompiled(); } protected: - enum { BIT_UNCOMPILED = 1 << 0 }; + void SetCompiledMethod(JSObject* aCompiledMethod) + { + mMethod.SetJSFunction(aCompiledMethod); + } - union { - uintptr_t mUncompiledMethod; // An object that represents the method before being compiled. - JSObject* mJSMethodObject; // The JS object for the method (after compilation) - }; + JSObject* GetCompiledMethod() const + { + return mMethod.GetJSFunction(); + } -#ifdef DEBUG - bool mIsCompiled; -#endif + JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod; }; class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { public: nsXBLProtoImplAnonymousMethod() : nsXBLProtoImplMethod(EmptyString().get()) {}
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -22,18 +22,16 @@ using namespace mozilla; nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const PRUnichar* aGetter, const PRUnichar* aSetter, const PRUnichar* aReadOnly, uint32_t aLineNumber) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) #endif { MOZ_COUNT_CTOR(nsXBLProtoImplProperty); if (aReadOnly) { @@ -50,125 +48,112 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr AppendSetterText(nsDependentString(aSetter)); SetSetterLineNumber(aLineNumber); } } nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const bool aIsReadOnly) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) #endif { MOZ_COUNT_CTOR(nsXBLProtoImplProperty); if (aIsReadOnly) mJSAttributes |= JSPROP_READONLY; } nsXBLProtoImplProperty::~nsXBLProtoImplProperty() { MOZ_COUNT_DTOR(nsXBLProtoImplProperty); - if (!(mJSAttributes & JSPROP_GETTER)) { - delete mGetterText; + if (!mGetter.IsCompiled()) { + delete mGetter.GetUncompiled(); } - if (!(mJSAttributes & JSPROP_SETTER)) { - delete mSetterText; + if (!mSetter.IsCompiled()) { + delete mSetter.GetUncompiled(); + } +} + +void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp) +{ + if (!aPropertyOp.GetUncompiled()) { + nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber(); + aPropertyOp.SetUncompiled(text); } } void nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->AppendText(aText); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->AppendText(aText); } void nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->AppendText(aText); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->AppendText(aText); } void nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->SetLineNumber(aLineNumber); } void nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->SetLineNumber(aLineNumber); } const char* gPropertyArgs[] = { "val" }; nsresult nsXBLProtoImplProperty::InstallMember(JSContext *aCx, JS::Handle<JSObject*> aTargetClassObject) { NS_PRECONDITION(mIsCompiled, "Should not be installing an uncompiled property"); + MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled()); MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject)); NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSGetterObject || mJSSetterObject) { + if (mGetter.GetJSFunction() || mSetter.GetJSFunction()) { // First, enter the compartment of the scope object and clone the functions. JSAutoCompartment ac(aCx, scopeObject); JS::Rooted<JSObject*> getter(aCx, nullptr); - if (mJSGetterObject) { - if (!(getter = ::JS_CloneFunctionObject(aCx, mJSGetterObject, scopeObject))) + if (mGetter.GetJSFunction()) { + if (!(getter = ::JS_CloneFunctionObject(aCx, mGetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JSObject*> setter(aCx, nullptr); - if (mJSSetterObject) { - if (!(setter = ::JS_CloneFunctionObject(aCx, mJSSetterObject, scopeObject))) + if (mSetter.GetJSFunction()) { + if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } // Now, enter the content compartment, wrap the getter/setter, and define // them on the class object. JSAutoCompartment ac2(aCx, aTargetClassObject); nsDependentString name(mName); if (!JS_WrapObject(aCx, getter.address()) || @@ -187,161 +172,164 @@ nsXBLProtoImplProperty::InstallMember(JS nsresult nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, JS::Handle<JSObject*> aClassObject) { NS_PRECONDITION(!mIsCompiled, "Trying to compile an already-compiled property"); NS_PRECONDITION(aClassObject, "Must have class object to compile"); + MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled()); if (!mName) return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. // We have a property. nsresult rv = NS_OK; nsAutoCString functionUri; - if (mGetterText || mSetterText) { + if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) { functionUri = aClassStr; int32_t hash = functionUri.RFindChar('#'); if (hash != kNotFound) { functionUri.Truncate(hash); } } bool deletedGetter = false; - if (mGetterText && mGetterText->GetText()) { - nsDependentString getter(mGetterText->GetText()); + nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled(); + if (getterText && getterText->GetText()) { + nsDependentString getter(getterText->GetText()); if (!getter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mGetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), getterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. JS::RootedObject getterObject(cx); rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 0, nullptr, getter, getterObject.address()); - // Make sure we free mGetterText here before setting mJSGetterObject, since - // that'll overwrite mGetterText - delete mGetterText; + delete getterText; deletedGetter = true; - mJSGetterObject = getterObject; + + mGetter.SetJSFunction(getterObject); - if (mJSGetterObject && NS_SUCCEEDED(rv)) { + if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSGetterObject = nullptr; + mGetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_GETTER; /*chaining to return failure*/ } } } // if getter is not empty if (!deletedGetter) { // Empty getter - delete mGetterText; - mJSGetterObject = nullptr; + delete getterText; + mGetter.SetJSFunction(nullptr); } if (NS_FAILED(rv)) { // We failed to compile our getter. So either we've set it to null, or // it's still set to the text object. In either case, it's safe to return // the error here, since then we'll be cleaned up as uncompiled and that // will be ok. Going on and compiling the setter and _then_ returning an // error, on the other hand, will try to clean up a compiled setter as // uncompiled and crash. return rv; } bool deletedSetter = false; - if (mSetterText && mSetterText->GetText()) { - nsDependentString setter(mSetterText->GetText()); + nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled(); + if (setterText && setterText->GetText()) { + nsDependentString setter(setterText->GetText()); if (!setter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mSetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), setterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. JS::RootedObject setterObject(cx); rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 1, gPropertyArgs, setter, setterObject.address()); - // Make sure we free mSetterText here before setting mJSGetterObject, since - // that'll overwrite mSetterText - delete mSetterText; + delete setterText; deletedSetter = true; - mJSSetterObject = setterObject; + mSetter.SetJSFunction(setterObject); - if (mJSSetterObject && NS_SUCCEEDED(rv)) { + if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSSetterObject = nullptr; + mSetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_SETTER; /*chaining to return failure*/ } } } // if setter wasn't empty.... if (!deletedSetter) { // Empty setter - delete mSetterText; - mJSSetterObject = nullptr; + delete setterText; + mSetter.SetJSFunction(nullptr); } #ifdef DEBUG mIsCompiled = NS_SUCCEEDED(rv); #endif return rv; } void nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { if (mJSAttributes & JSPROP_GETTER) { - aCallbacks.Trace(&mJSGetterObject, "mJSGetterObject", aClosure); + aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure); } if (mJSAttributes & JSPROP_SETTER) { - aCallbacks.Trace(&mJSSetterObject, "mJSSetterObject", aClosure); + aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure); } } nsresult nsXBLProtoImplProperty::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream, XBLBindingSerializeDetails aType) { + MOZ_ASSERT(!mIsCompiled); + MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled()); + JSContext *cx = aContext->GetNativeContext(); + JS::Rooted<JSObject*> getterObject(cx); if (aType == XBLBinding_Serialize_GetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted<JSObject*> getterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &getterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSGetterObject = getterObject; mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } - + mGetter.SetJSFunction(getterObject); + + JS::Rooted<JSObject*> setterObject(cx); if (aType == XBLBinding_Serialize_SetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted<JSObject*> setterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &setterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSSetterObject = setterObject; mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } + mSetter.SetJSFunction(setterObject); #ifdef DEBUG mIsCompiled = true; #endif return NS_OK; } @@ -365,21 +353,19 @@ nsXBLProtoImplProperty::Write(nsIScriptC } nsresult rv = aStream->Write8(type); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteWStringZ(mName); NS_ENSURE_SUCCESS(rv, rv); if (mJSAttributes & JSPROP_GETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSGetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mGetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } if (mJSAttributes & JSPROP_SETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSSetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mSetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
--- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -7,16 +7,17 @@ #define nsXBLProtoImplProperty_h__ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" #include "jsapi.h" #include "nsString.h" #include "nsXBLSerialize.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" class nsXBLProtoImplProperty: public nsXBLProtoImplMember { public: nsXBLProtoImplProperty(const PRUnichar* aName, const PRUnichar* aGetter, const PRUnichar* aSetter, @@ -43,30 +44,26 @@ public: nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream, XBLBindingSerializeDetails aType); virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE; protected: - union { - // The raw text for the getter (prior to compilation). - nsXBLTextWithLineNumber* mGetterText; - // The JS object for the getter (after compilation) - JSObject * mJSGetterObject; - }; + typedef JS::Heap<nsXBLMaybeCompiled<nsXBLTextWithLineNumber> > PropertyOp; + + void EnsureUncompiledText(PropertyOp& aPropertyOp); - union { - // The raw text for the setter (prior to compilation). - nsXBLTextWithLineNumber* mSetterText; - // The JS object for the setter (after compilation) - JSObject * mJSSetterObject; - }; + // The raw text for the getter, or the JS object (after compilation). + PropertyOp mGetter; + + // The raw text for the setter, or the JS object (after compilation). + PropertyOp mSetter; - unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) #ifdef DEBUG bool mIsCompiled; #endif }; #endif // nsXBLProtoImplProperty_h__
--- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -2378,18 +2378,17 @@ nsXULPrototypeScript::Serialize(nsIObjec // Write basic prototype data nsresult rv; rv = aStream->Write32(mLineNo); if (NS_FAILED(rv)) return rv; rv = aStream->Write32(mLangVersion); if (NS_FAILED(rv)) return rv; // And delegate the writing to the nsIScriptContext - rv = context->Serialize(aStream, - JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject)); + rv = context->Serialize(aStream, mScriptObject); if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream, nsIScriptGlobalObject* aGlobal)
--- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -238,23 +238,23 @@ public: void Set(JSScript* aObject); // It's safe to return a handle because we trace mScriptObject, no one ever // uses the handle (or the script object) past the point at which the // nsXULPrototypeScript dies, and we can't get memmoved so the // &mScriptObject pointer can't go stale. JS::Handle<JSScript*> GetScriptObject() { - return JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject); + return JS::Handle<JSScript*>(mScriptObject); } void TraceScriptObject(JSTracer* aTrc) { if (mScriptObject) { - JS_CallScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script"); + JS_CallHeapScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script"); } } void Trace(const TraceCallbacks& aCallbacks, void* aClosure) { if (mScriptObject) { aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure); } @@ -262,17 +262,17 @@ public: nsCOMPtr<nsIURI> mSrcURI; uint32_t mLineNo; bool mSrcLoading; bool mOutOfLine; mozilla::dom::XULDocument* mSrcLoadWaiters; // [OWNER] but not COMPtr uint32_t mLangVersion; private: - JSScript* mScriptObject; + JS::Heap<JSScript*> mScriptObject; }; class nsXULPrototypeText : public nsXULPrototypeNode { public: nsXULPrototypeText() : nsXULPrototypeNode(eType_Text) {
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7885,27 +7885,16 @@ nsDocShell::RestoreFromHistory() newVM = nullptr; // Simulate the completion of the load. nsDocShell::FinishRestore(); // Restart plugins, and paint the content. if (shell) { shell->Thaw(); - - newVM = shell->GetViewManager(); - if (newVM) { - // When we insert the root view above the resulting invalidate is - // dropped because painting is suppressed in the presshell until we - // call Thaw. So we issue the invalidate here. - newRootView = newVM->GetRootView(); - if (newRootView) { - newVM->InvalidateView(newRootView); - } - } } return privWin->FireDelayedDOMEvents(); } NS_IMETHODIMP nsDocShell::CreateContentViewer(const char *aContentType, nsIRequest * request,
--- a/dom/base/DOMRequest.h +++ b/dom/base/DOMRequest.h @@ -17,17 +17,17 @@ namespace mozilla { namespace dom { class DOMRequest : public nsDOMEventTargetHelper, public nsIDOMDOMRequest { protected: - JS::Value mResult; + JS::Heap<JS::Value> mResult; nsRefPtr<DOMError> mError; bool mDone; bool mRooted; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMDOMREQUEST NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
--- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -89,17 +89,17 @@ CPP_SOURCES += [ 'nsMimeTypeArray.cpp', 'nsPerformance.cpp', 'nsPluginArray.cpp', 'nsQueryContentEventResult.cpp', 'nsScreen.cpp', 'nsScriptNameSpaceManager.cpp', 'nsStructuredCloneContainer.cpp', 'nsWindowMemoryReporter.cpp', - 'nsWindowRoot.cpp', + 'nsWindowRoot.cpp' ] EXTRA_COMPONENTS += [ 'ConsoleAPI.js', 'ConsoleAPI.manifest', 'SiteSpecificUserAgent.js', 'SiteSpecificUserAgent.manifest', ]
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1583,26 +1583,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( END_OUTER_WINDOW_ONLY NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) static PLDHashOperator -MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure) +MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) { xpc_UnmarkGrayObject(aData); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) if (tmp->IsBlackForCC()) { if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) { - tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr); + tmp->mCachedXBLPrototypeHandlers.Enumerate(MarkXBLHandlers, nullptr); } nsEventListenerManager* elm = tmp->GetListenerManager(false); if (elm) { elm->MarkForCC(); } tmp->UnmarkGrayTimers(); return true; } @@ -1744,17 +1744,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END struct TraceData { const TraceCallbacks& callbacks; void* closure; }; static PLDHashOperator -TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject*& aData, void* aClosure) +TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) { TraceData* data = static_cast<TraceData*>(aClosure); data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -10,16 +10,17 @@ #include "mozilla/XPCOM.h" // for TimeStamp/TimeDuration // Local Includes // Helper Classes #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsWeakReference.h" #include "nsDataHashtable.h" +#include "nsJSThingHashtable.h" #include "nsCycleCollectionParticipant.h" // Interfaces Needed #include "nsDOMWindowList.h" #include "nsIBaseWindow.h" #include "nsIBrowserDOMWindow.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocShellTreeItem.h" @@ -1229,17 +1230,17 @@ protected: bool mSetOpenerWindowCalled; nsCOMPtr<nsIURI> mLastOpenedURI; #endif bool mCleanedUp, mCallCleanUpAfterModalDialogCloses; nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache; - nsDataHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers; + nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers; nsCOMPtr<nsIDocument> mSuspendedDoc; nsRefPtr<mozilla::dom::indexedDB::IDBFactory> mIndexedDB; // This counts the number of windows that have been opened in rapid succession // (i.e. within dom.successive_dialog_time_limit of each other). It is reset // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
--- a/dom/base/nsIJSEventListener.h +++ b/dom/base/nsIJSEventListener.h @@ -263,17 +263,17 @@ protected: NS_ASSERTION(!mTarget, "Should have called Disconnect()!"); } // Update our mScopeObject; we have to make sure we properly handle // the hold/drop stuff, so have to do it in nsJSEventListener. virtual void UpdateScopeObject(JS::Handle<JSObject*> aScopeObject) = 0; nsCOMPtr<nsIScriptContext> mContext; - JSObject* mScopeObject; + JS::Heap<JSObject*> mScopeObject; nsISupports* mTarget; nsCOMPtr<nsIAtom> mEventName; nsEventHandler mHandler; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID) /* factory function. aHandler must already be bound to aTarget.
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3518,30 +3518,31 @@ public: // nsIJSArgArray nsresult GetArgs(uint32_t *argc, void **argv); void ReleaseJSObjects(); protected: JSContext *mContext; - JS::Value *mArgv; + JS::Heap<JS::Value> *mArgv; uint32_t mArgc; }; nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv, nsresult *prv) : mContext(aContext), mArgv(nullptr), mArgc(argc) { // copy the array - we don't know its lifetime, and ours is tied to xpcom - // refcounting. Alloc zero'd array so cleanup etc is safe. + // refcounting. if (argc) { - mArgv = (JS::Value *) PR_CALLOC(argc * sizeof(JS::Value)); + static const fallible_t fallible = fallible_t(); + mArgv = new (fallible) JS::Heap<JS::Value>[argc]; if (!mArgv) { *prv = NS_ERROR_OUT_OF_MEMORY; return; } } // Callers are allowed to pass in a null argv even for argc > 0. They can // then use GetArgs to initialize the values. @@ -3561,17 +3562,17 @@ nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); } void nsJSArgArray::ReleaseJSObjects() { if (mArgv) { - PR_DELETE(mArgv); + delete [] mArgv; } if (mArgc > 0) { mArgc = 0; NS_DROP_JS_OBJECTS(this, nsJSArgArray); } } // QueryInterface implementation for nsJSArgArray
--- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -62,20 +62,21 @@ public: void ReleaseJSObjects(); private: // filename, line number and JS language version string of the // caller of setTimeout() nsCString mFileName; uint32_t mLineNo; - nsTArray<JS::Value> mArgs; + nsTArray<JS::Heap<JS::Value> > mArgs; // The JS expression to evaluate or function to call, if !mExpr - JSFlatString *mExpr; + // Note this is always a flat string. + JS::Heap<JSString*> mExpr; nsRefPtr<Function> mFunction; }; // nsJSScriptTimeoutHandler // QueryInterface implementation for nsJSScriptTimeoutHandler NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler) tmp->ReleaseJSObjects(); @@ -276,17 +277,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK. return NS_ERROR_DOM_TYPE_ERR; } } } // if there's no document, we don't have to do anything. NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); - mExpr = expr; + mExpr = JS_FORGET_STRING_FLATNESS(expr); // Get the calling location. const char *filename; if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) { mFileName.Assign(filename); } } else if (funobj) { NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); @@ -294,17 +295,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW mFunction = new Function(funobj); // Create our arg array. argc is the number of arguments passed // to setTimeout or setInterval; the first two are our callback // and the delay, so only arguments after that need to go in our // array. // std::max(argc - 2, 0) wouldn't work right because argc is unsigned. uint32_t argCount = std::max(argc, 2u) - 2; - FallibleTArray<JS::Value> args; + + FallibleTArray<JS::Heap<JS::Value> > args; if (!args.SetCapacity(argCount)) { // No need to drop here, since we already have a non-null mFunction return NS_ERROR_OUT_OF_MEMORY; } for (uint32_t idx = 0; idx < argCount; ++idx) { *args.AppendElement() = argv[idx + 2]; } args.SwapElements(mArgs); @@ -314,17 +316,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW *aInterval = interval; return NS_OK; } const PRUnichar * nsJSScriptTimeoutHandler::GetHandlerText() { NS_ASSERTION(mExpr, "No expression, so no handler text!"); - return ::JS_GetFlatStringChars(mExpr); + return ::JS_GetFlatStringChars(JS_ASSERT_STRING_IS_FLAT(mExpr)); } nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow, bool *aIsInterval, int32_t *aInterval, nsIScriptTimeoutHandler **aRet) { *aRet = nullptr;
--- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -3,17 +3,17 @@ * 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/. */ #ifndef nsWrapperCache_h___ #define nsWrapperCache_h___ #include "nsCycleCollectionParticipant.h" #include "mozilla/Assertions.h" -#include "js/RootingAPI.h" +#include "js/Value.h" struct JSTracer; class JSObject; struct JSContext; class XPCWrappedNativeScope; namespace mozilla { namespace dom { @@ -177,17 +177,17 @@ public: else { UnsetWrapperFlags(WRAPPER_BIT_PRESERVED); } } void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure) { if (PreservingWrapper() && mWrapper) { - aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure); + aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure); } } /* * The following methods for getting and manipulating flags allow the unused * bits of mFlags to be used by derived classes. */ @@ -275,18 +275,18 @@ private: * (regular JS object or proxy) that has a system only wrapper for same-origin * access. */ enum { WRAPPER_HAS_SOW = 1 << 2 }; enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING | WRAPPER_HAS_SOW) }; - JSObject* mWrapper; - uint32_t mFlags; + JS::Heap<JSObject*> mWrapper; + uint32_t mFlags; }; enum { WRAPPER_CACHE_FLAGS_BITS_USED = 3 }; NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID) #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \ if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) { \
--- a/dom/base/nsWrapperCacheInlines.h +++ b/dom/base/nsWrapperCacheInlines.h @@ -45,12 +45,12 @@ nsWrapperCache::IsBlackAndDoesNotNeedTra return !hasGrayObjects; } return false; } inline void nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName) { - JS_CallObjectTracer(aTrc, &mWrapper, aName); + JS_CallHeapObjectTracer(aTrc, &mWrapper, aName); } #endif /* nsWrapperCache_h___ */
--- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -71,17 +71,17 @@ public: * This should only be called if you are certain that the return value won't * be passed into a JS API function and that it won't be stored without being * rooted (or otherwise signaling the stored value to the CC). * * This can return a handle because we trace our mCallback. */ JS::Handle<JSObject*> CallbackPreserveColor() const { - return JS::Handle<JSObject*>::fromMarkedLocation(&mCallback); + return mCallback; } enum ExceptionHandling { eReportExceptions, eRethrowExceptions }; protected: @@ -106,17 +106,17 @@ protected: { if (mCallback) { mCallback = nullptr; NS_DROP_JS_OBJECTS(this, CallbackObject); nsLayoutStatics::Release(); } } - JSObject* mCallback; + JS::Heap<JSObject*> mCallback; class MOZ_STACK_CLASS CallSetup { /** * A class that performs whatever setup we need to safely make a * call while this class is on the stack, After the constructor * returns, the call is safe to make if GetContext() returns * non-null.
--- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -257,39 +257,43 @@ BluetoothAdapter::SetPropertyByValue(con mClass = value.get_uint32_t(); } else if (name.EqualsLiteral("UUIDs")) { mUuids = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) { + JS::Rooted<JSObject*> uuids(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { NS_WARNING("Cannot set JS UUIDs object!"); return; } + mJsUuids = uuids; Root(); } else if (name.EqualsLiteral("Devices")) { mDeviceAddresses = value.get_ArrayOfnsString(); uint32_t length = mDeviceAddresses.Length(); for (int i = 0; i < length; i++) { mDeviceAddresses[i] = GetAddressFromObjectPath(mDeviceAddresses[i]); } nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); + JS::Rooted<JSObject*> deviceAddresses(cx); if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses, - &mJsDeviceAddresses))) { + deviceAddresses.address()))) { NS_WARNING("Cannot set JS Devices object!"); return; } + mJsDeviceAddresses = deviceAddresses; Root(); } else { #ifdef DEBUG nsCString warningMsg; warningMsg.AssignLiteral("Not handling adapter property: "); warningMsg.Append(NS_ConvertUTF16toUTF8(name)); NS_WARNING(warningMsg.get()); #endif
--- a/dom/bluetooth/BluetoothAdapter.h +++ b/dom/bluetooth/BluetoothAdapter.h @@ -67,16 +67,16 @@ private: bool mDiscovering; bool mPairable; bool mPowered; uint32_t mPairableTimeout; uint32_t mDiscoverableTimeout; uint32_t mClass; nsTArray<nsString> mDeviceAddresses; nsTArray<nsString> mUuids; - JSObject* mJsUuids; - JSObject* mJsDeviceAddresses; + JS::Heap<JSObject*> mJsUuids; + JS::Heap<JSObject*> mJsDeviceAddresses; bool mIsRooted; }; END_BLUETOOTH_NAMESPACE #endif
--- a/dom/bluetooth/BluetoothDevice.cpp +++ b/dom/bluetooth/BluetoothDevice.cpp @@ -119,33 +119,37 @@ BluetoothDevice::SetPropertyByValue(cons } else if (name.EqualsLiteral("UUIDs")) { mUuids = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) { + JS::Rooted<JSObject*> uuids(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { NS_WARNING("Cannot set JS UUIDs object!"); return; } + mJsUuids = uuids; Root(); } else if (name.EqualsLiteral("Services")) { mServices = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &mJsServices))) { + JS::Rooted<JSObject*> services(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) { NS_WARNING("Cannot set JS Services object!"); return; } + mJsServices = services; Root(); } else { nsCString warningMsg; warningMsg.AssignLiteral("Not handling device property: "); warningMsg.Append(NS_ConvertUTF16toUTF8(name)); NS_WARNING(warningMsg.get()); } }
--- a/dom/bluetooth/BluetoothDevice.h +++ b/dom/bluetooth/BluetoothDevice.h @@ -53,18 +53,18 @@ public: void Unroot(); private: BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath, const BluetoothValue& aValue); ~BluetoothDevice(); void Root(); - JSObject* mJsUuids; - JSObject* mJsServices; + JS::Heap<JSObject*> mJsUuids; + JS::Heap<JSObject*> mJsServices; nsString mAdapterPath; nsString mAddress; nsString mName; nsString mIcon; uint32_t mClass; bool mConnected; bool mPaired;
--- a/dom/future/Future.h +++ b/dom/future/Future.h @@ -102,17 +102,17 @@ private: nsRefPtr<nsPIDOMWindow> mWindow; nsRefPtr<FutureResolver> mResolver; nsTArray<nsRefPtr<FutureCallback> > mResolveCallbacks; nsTArray<nsRefPtr<FutureCallback> > mRejectCallbacks; - JS::Value mResult; + JS::Heap<JS::Value> mResult; FutureState mState; bool mTaskPending; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_Future_h
--- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -539,17 +539,17 @@ IDBCursor::GetKey(JSContext* aCx, } if (!mHaveCachedKey) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBCursor); mRooted = true; } - nsresult rv = mKey.ToJSVal(aCx, &mCachedKey); + nsresult rv = mKey.ToJSVal(aCx, mCachedKey); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedKey = true; } *aKey = mCachedKey; return NS_OK; } @@ -573,17 +573,17 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, JSAutoRequest ar(aCx); NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() : !mObjectKey.IsUnset(), "Bad key!"); const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey; - nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey); + nsresult rv = key.ToJSVal(aCx, mCachedPrimaryKey); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedPrimaryKey = true; } *aValue = mCachedPrimaryKey; return NS_OK; } @@ -734,17 +734,17 @@ IDBCursor::Update(const jsval& aValue, rv = mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; } } else { JS::Rooted<JS::Value> keyVal(aCx); - rv = objectKey.ToJSVal(aCx, keyVal.address()); + rv = objectKey.ToJSVal(aCx, &keyVal); NS_ENSURE_SUCCESS(rv, rv); rv = mObjectStore->Put(aValue, keyVal, aCx, 1, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; } } @@ -805,17 +805,17 @@ IDBCursor::Delete(JSContext* aCx, } NS_ASSERTION(mObjectStore, "This cannot be null!"); NS_ASSERTION(!mKey.IsUnset() , "Bad key!"); Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; JS::Rooted<JS::Value> key(aCx); - nsresult rv = objectKey.ToJSVal(aCx, key.address()); + nsresult rv = objectKey.ToJSVal(aCx, &key); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIIDBRequest> request; rv = mObjectStore->Delete(key, aCx, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; }
--- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -163,27 +163,27 @@ protected: const nsACString& aContinueQuery, const nsACString& aContinueToQuery); nsRefPtr<IDBRequest> mRequest; nsRefPtr<IDBTransaction> mTransaction; nsRefPtr<IDBObjectStore> mObjectStore; nsRefPtr<IDBIndex> mIndex; - JSObject* mScriptOwner; + JS::Heap<JSObject*> mScriptOwner; Type mType; Direction mDirection; nsCString mContinueQuery; nsCString mContinueToQuery; // These are cycle-collected! - jsval mCachedKey; - jsval mCachedPrimaryKey; - jsval mCachedValue; + JS::Heap<JS::Value> mCachedKey; + JS::Heap<JS::Value> mCachedPrimaryKey; + JS::Heap<JS::Value> mCachedValue; Key mRangeKey; Key mKey; Key mObjectKey; StructuredCloneReadInfo mCloneReadInfo; Key mContinueToKey;
--- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -178,17 +178,17 @@ private: Open(JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv); nsCString mASCIIOrigin; // If this factory lives on a window then mWindow must be non-null. Otherwise // mOwningObject must be non-null. nsCOMPtr<nsPIDOMWindow> mWindow; - JSObject* mOwningObject; + JS::Heap<JSObject*> mOwningObject; IndexedDBChild* mActorChild; IndexedDBParent* mActorParent; mozilla::dom::ContentParent* mContentParent; bool mRootedOwningObject; };
--- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -805,17 +805,17 @@ IDBIndex::GetKeyPath(JSContext* aCx, { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!JSVAL_IS_VOID(mCachedKeyPath)) { *aVal = mCachedKeyPath; return NS_OK; } - nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath); + nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); NS_ENSURE_SUCCESS(rv, rv); if (JSVAL_IS_GCTHING(mCachedKeyPath)) { NS_HOLD_JS_OBJECTS(this, IDBIndex); mRooted = true; } *aVal = mCachedKeyPath; @@ -1175,17 +1175,22 @@ GetKeyHelper::DoDatabaseWork(mozIStorage return NS_OK; } nsresult GetKeyHelper::GetSuccessResult(JSContext* aCx, jsval* aVal) { - return mKey.ToJSVal(aCx, aVal); + JS::Rooted<JS::Value> value(aCx); + nsresult rv = mKey.ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + *aVal = value; + } + return rv; } void GetKeyHelper::ReleaseMainThreadObjects() { mKeyRange = nullptr; IndexHelper::ReleaseMainThreadObjects(); } @@ -1503,17 +1508,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } for (uint32_t index = 0, count = keys.Length(); index < count; index++) { const Key& key = keys[index]; NS_ASSERTION(!key.IsUnset(), "Bad key!"); JS::Rooted<JS::Value> value(aCx); - nsresult rv = key.ToJSVal(aCx, value.address()); + nsresult rv = key.ToJSVal(aCx, &value); if (NS_FAILED(rv)) { NS_WARNING("Failed to get jsval for key!"); return rv; } if (!JS_SetElement(aCx, array, index, value.address())) { NS_WARNING("Failed to set array element!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
--- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -152,17 +152,17 @@ private: IDBIndex(); ~IDBIndex(); nsRefPtr<IDBObjectStore> mObjectStore; int64_t mId; nsString mName; KeyPath mKeyPath; - JS::Value mCachedKeyPath; + JS::Heap<JS::Value> mCachedKeyPath; IndexedDBIndexChild* mActorChild; IndexedDBIndexParent* mActorParent; bool mUnique; bool mMultiEntry; bool mRooted; };
--- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -358,17 +358,17 @@ IDBKeyRange::GetLower(JSContext* aCx, NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedLowerVal) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBKeyRange); mRooted = true; } - nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal); + nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedLowerVal = true; } *aLower = mCachedLowerVal; return NS_OK; } @@ -380,17 +380,17 @@ IDBKeyRange::GetUpper(JSContext* aCx, NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedUpperVal) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBKeyRange); mRooted = true; } - nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal); + nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedUpperVal = true; } *aUpper = mCachedUpperVal; return NS_OK; }
--- a/dom/indexedDB/IDBKeyRange.h +++ b/dom/indexedDB/IDBKeyRange.h @@ -152,18 +152,18 @@ public: void DropJSObjects(); private: ~IDBKeyRange(); Key mLower; Key mUpper; - jsval mCachedLowerVal; - jsval mCachedUpperVal; + JS::Heap<JS::Value> mCachedLowerVal; + JS::Heap<JS::Value> mCachedUpperVal; bool mLowerOpen; bool mUpperOpen; bool mIsOnly; bool mHaveCachedLowerVal; bool mHaveCachedUpperVal; bool mRooted; };
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -2382,17 +2382,17 @@ IDBObjectStore::GetKeyPath(JSContext* aC { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!JSVAL_IS_VOID(mCachedKeyPath)) { *aVal = mCachedKeyPath; return NS_OK; } - nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath); + nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); NS_ENSURE_SUCCESS(rv, rv); if (JSVAL_IS_GCTHING(mCachedKeyPath)) { NS_HOLD_JS_OBJECTS(this, IDBObjectStore); mRooted = true; } *aVal = mCachedKeyPath; @@ -3129,17 +3129,22 @@ AddHelper::DoDatabaseWork(mozIStorageCon nsresult AddHelper::GetSuccessResult(JSContext* aCx, jsval* aVal) { NS_ASSERTION(!mKey.IsUnset(), "Badness!"); mCloneWriteInfo.mCloneBuffer.clear(); - return mKey.ToJSVal(aCx, aVal); + JS::Rooted<JS::Value> value(aCx); + nsresult rv = mKey.ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + *aVal = value; + } + return rv; } void AddHelper::ReleaseMainThreadObjects() { IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); ObjectStoreHelper::ReleaseMainThreadObjects(); }
--- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -282,17 +282,17 @@ protected: uint32_t aTag, BlobOrFileData* aRetval); private: nsRefPtr<IDBTransaction> mTransaction; int64_t mId; nsString mName; KeyPath mKeyPath; - JS::Value mCachedKeyPath; + JS::Heap<JS::Value> mCachedKeyPath; bool mRooted; bool mAutoIncrement; nsCOMPtr<nsIAtom> mDatabaseId; nsRefPtr<ObjectStoreInfo> mInfo; nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes; IndexedDBObjectStoreChild* mActorChild;
--- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -115,23 +115,25 @@ IDBRequest::NotifyHelperCompleted(Helper } JS::Rooted<JSObject*> global(cx, GetParentObject()); NS_ASSERTION(global, "This should never be null!"); JSAutoCompartment ac(cx, global); AssertIsRooted(); - rv = aHelper->GetSuccessResult(cx, &mResultVal); + JS::Rooted<JS::Value> value(cx); + rv = aHelper->GetSuccessResult(cx, value.address()); if (NS_FAILED(rv)) { NS_WARNING("GetSuccessResult failed!"); } if (NS_SUCCEEDED(rv)) { mError = nullptr; + mResultVal = value; } else { SetError(rv); mResultVal = JSVAL_VOID; } return rv; }
--- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -104,17 +104,17 @@ public: protected: IDBRequest(); ~IDBRequest(); nsCOMPtr<nsISupports> mSource; nsRefPtr<IDBTransaction> mTransaction; - jsval mResultVal; + JS::Heap<JS::Value> mResultVal; nsRefPtr<mozilla::dom::DOMError> mError; IndexedDBRequestParentBase* mActorParent; nsString mFilename; #ifdef MOZ_ENABLE_PROFILER_SPS uint64_t mSerialNumber; #endif nsresult mErrorCode; uint32_t mLineNo;
--- a/dom/indexedDB/IDBWrapperCache.h +++ b/dom/indexedDB/IDBWrapperCache.h @@ -57,14 +57,14 @@ public: protected: IDBWrapperCache() : mScriptOwner(nullptr) { } virtual ~IDBWrapperCache(); private: - JSObject* mScriptOwner; + JS::Heap<JSObject*> mScriptOwner; }; END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbwrappercache_h__
--- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -180,17 +180,17 @@ Key::EncodeJSValInternal(JSContext* aCx, } return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } // static nsresult Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, - JSContext* aCx, uint8_t aTypeOffset, jsval* aVal, + JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth) { NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR); if (*aPos - aTypeOffset >= eArray) { JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0, nullptr)); if (!array) { NS_WARNING("Failed to make array!"); @@ -203,52 +203,52 @@ Key::DecodeJSValInternal(const unsigned ++aPos; aTypeOffset = 0; } uint32_t index = 0; while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) { JS::Rooted<JS::Value> val(aCx); nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, - val.address(), aRecursionDepth + 1); + &val, aRecursionDepth + 1); NS_ENSURE_SUCCESS(rv, rv); aTypeOffset = 0; if (!JS_SetElement(aCx, array, index++, val.address())) { NS_WARNING("Failed to set array element!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator, "Should have found end-of-array marker"); ++aPos; - *aVal = OBJECT_TO_JSVAL(array); + aVal.setObject(*array); } else if (*aPos - aTypeOffset == eString) { nsString key; DecodeString(aPos, aEnd, key); - if (!xpc::StringToJsval(aCx, key, aVal)) { + if (!xpc::StringToJsval(aCx, key, aVal.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } else if (*aPos - aTypeOffset == eDate) { double msec = static_cast<double>(DecodeNumber(aPos, aEnd)); JSObject* date = JS_NewDateObjectMsec(aCx, msec); if (!date) { NS_WARNING("Failed to make date!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - *aVal = OBJECT_TO_JSVAL(date); + aVal.setObject(*date); } else if (*aPos - aTypeOffset == eFloat) { - *aVal = DOUBLE_TO_JSVAL(DecodeNumber(aPos, aEnd)); + aVal.setDouble(DecodeNumber(aPos, aEnd)); } else { NS_NOTREACHED("Unknown key type!"); } return NS_OK; }
--- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -174,33 +174,44 @@ public: return rv; } TrimBuffer(); return NS_OK; } nsresult ToJSVal(JSContext* aCx, - jsval* aVal) const + JS::MutableHandle<JS::Value> aVal) const { if (IsUnset()) { - *aVal = JSVAL_VOID; + aVal.set(JSVAL_VOID); return NS_OK; } const unsigned char* pos = BufferStart(); nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(pos >= BufferEnd(), "Didn't consume whole buffer"); return NS_OK; } + nsresult ToJSVal(JSContext* aCx, + JS::Heap<JS::Value>& aVal) const + { + JS::Rooted<JS::Value> value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aVal = value; + } + return rv; + } + nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, const jsval aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { Unset(); return rv; @@ -299,17 +310,17 @@ private: } void EncodeString(const nsAString& aString, uint8_t aTypeOffset); void EncodeNumber(double aFloat, uint8_t aType); // Decoding functions. aPos points into mBuffer and is adjusted to point // past the consumed value. static inline nsresult DecodeJSVal(const unsigned char*& aPos, const unsigned char* aEnd, JSContext* aCx, - uint8_t aTypeOffset, jsval* aVal) + uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal) { return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); } static void DecodeString(const unsigned char*& aPos, const unsigned char* aEnd, nsString& aString); static double DecodeNumber(const unsigned char*& aPos, @@ -319,14 +330,14 @@ private: private: nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, JSContext* aCx, uint8_t aTypeOffset, - jsval* aVal, uint16_t aRecursionDepth); + JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth); }; END_INDEXEDDB_NAMESPACE #endif /* mozilla_dom_indexeddb_key_h__ */
--- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -452,17 +452,17 @@ KeyPath::DeserializeFromString(const nsA keyPath.SetType(STRING); keyPath.mStrings.AppendElement(aString); return keyPath; } nsresult -KeyPath::ToJSVal(JSContext* aCx, JS::Value* aValue) const +KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const { if (IsArray()) { uint32_t len = mStrings.Length(); JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len, nullptr)); if (!array) { NS_WARNING("Failed to make array!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -474,32 +474,43 @@ KeyPath::ToJSVal(JSContext* aCx, JS::Val return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!JS_SetElement(aCx, array, i, val.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } - *aValue = OBJECT_TO_JSVAL(array); + aValue.setObject(*array); return NS_OK; } if (IsString()) { nsString tmp(mStrings[0]); - if (!xpc::StringToJsval(aCx, tmp, aValue)) { + if (!xpc::StringToJsval(aCx, tmp, aValue.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } return NS_OK; } - *aValue = JSVAL_NULL; + aValue.setNull(); return NS_OK; } +nsresult +KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const +{ + JS::Rooted<JS::Value> value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aValue = value; + } + return rv; +} + bool KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const { // Any keypath that passed validation is allowed for non-autoIncrement // objectStores. if (!aAutoIncrement) { return true; }
--- a/dom/indexedDB/KeyPath.h +++ b/dom/indexedDB/KeyPath.h @@ -82,17 +82,18 @@ public: bool operator==(const KeyPath& aOther) const { return mType == aOther.mType && mStrings == aOther.mStrings; } void SerializeToString(nsAString& aString) const; static KeyPath DeserializeFromString(const nsAString& aString); - nsresult ToJSVal(JSContext* aCx, JS::Value* aValue) const; + nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const; + nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const; bool IsAllowedForObjectStore(bool aAutoIncrement) const; KeyPathType mType; nsTArray<nsString> mStrings; };
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1159,22 +1159,17 @@ public: NS_METHOD GetDescription(nsAString& aDescription) { CopyUTF8toUTF16(mPluginTag.mDescription, aDescription); return NS_OK; } NS_METHOD GetFilename(nsAString& aFilename) { - if (Preferences::GetBool("plugin.expose_full_path", false)) { - CopyUTF8toUTF16(mPluginTag.mFullPath, aFilename); - } else { - CopyUTF8toUTF16(mPluginTag.mFileName, aFilename); - } - + CopyUTF8toUTF16(mPluginTag.mFileName, aFilename); return NS_OK; } NS_METHOD GetVersion(nsAString& aVersion) { CopyUTF8toUTF16(mPluginTag.mVersion, aVersion); return NS_OK; }
--- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -36,17 +36,17 @@ class Telephony : public nsDOMEventTarge nsCOMPtr<nsITelephonyProvider> mProvider; nsRefPtr<Listener> mListener; TelephonyCall* mActiveCall; nsTArray<nsRefPtr<TelephonyCall> > mCalls; // Cached calls array object. Cleared whenever mCalls changes and then rebuilt // once a page looks for the liveCalls attribute. - JSObject* mCallsArray; + JS::Heap<JSObject*> mCallsArray; bool mRooted; bool mEnumerated; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMTELEPHONY NS_DECL_NSITELEPHONYLISTENER
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -461,16 +461,31 @@ public: */ virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} /** * Flag the next paint as the first for a document. */ virtual void SetIsFirstPaint() {} + /** + * Make sure that the previous transaction has been entirely + * completed. + * + * Note: This may sychronously wait on a remote compositor + * to complete rendering. + */ + virtual void FlushRendering() { } + + /** + * Checks if we need to invalidate the OS widget to trigger + * painting when updating this layer manager. + */ + virtual bool NeedsWidgetInvalidation() { return true; } + // We always declare the following logging symbols, because it's // extremely tricky to conditionally declare them. However, for // ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial // definitions in Layers.cpp. virtual const char* Name() const { return "???"; } /** * Dump information about this layer manager and its managed tree to
--- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -256,16 +256,26 @@ ClientLayerManager::MakeSnapshotIfRequir ShadowLayerForwarder::DestroySharedSurface(&snapshot); } } } mShadowTarget = nullptr; } void +ClientLayerManager::FlushRendering() +{ + if (mWidget) { + if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) { + remoteRenderer->SendFlushRendering(); + } + } +} + +void ClientLayerManager::ForwardTransaction() { mPhase = PHASE_FORWARD; // forward this transaction's changeset to our LayerManagerComposite AutoInfallibleTArray<EditReply, 10> replies; if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) { for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
--- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -48,16 +48,20 @@ public: virtual already_AddRefed<ThebesLayer> CreateThebesLayer(); virtual already_AddRefed<ContainerLayer> CreateContainerLayer(); virtual already_AddRefed<ImageLayer> CreateImageLayer(); virtual already_AddRefed<CanvasLayer> CreateCanvasLayer(); virtual already_AddRefed<ColorLayer> CreateColorLayer(); virtual already_AddRefed<RefLayer> CreateRefLayer(); + virtual void FlushRendering() MOZ_OVERRIDE; + + virtual bool NeedsWidgetInvalidation() MOZ_OVERRIDE { return Compositor::GetBackend() == LAYERS_BASIC; } + ShadowableLayer* Hold(Layer* aLayer); bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); } virtual bool IsCompositingCheap(); virtual bool HasShadowManagerInternal() const { return HasShadowManager(); } virtual void SetIsFirstPaint() MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -244,16 +244,28 @@ CompositorParent::RecvMakeSnapshot(const { AutoOpenSurface opener(OPEN_READ_WRITE, aInSnapshot); nsRefPtr<gfxContext> target = new gfxContext(opener.Get()); ComposeToTarget(target); *aOutSnapshot = aInSnapshot; return true; } +bool +CompositorParent::RecvFlushRendering() +{ + // If we're waiting to do a composite, then cancel it + // and do it immediately instead. + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + ComposeToTarget(nullptr); + } + return true; +} + void CompositorParent::ActorDestroy(ActorDestroyReason why) { mPaused = true; RemoveCompositor(mCompositorID); if (mLayerManager) { mLayerManager->Destroy(); @@ -738,16 +750,17 @@ public: // FIXME/bug 774388: work out what shutdown protocol we need. virtual bool RecvWillStop() MOZ_OVERRIDE { return true; } virtual bool RecvStop() MOZ_OVERRIDE { return true; } virtual bool RecvPause() MOZ_OVERRIDE { return true; } virtual bool RecvResume() MOZ_OVERRIDE { return true; } virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, SurfaceDescriptor* aOutSnapshot) { return true; } + virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; } virtual PLayerTransactionParent* AllocPLayerTransaction(const LayersBackend& aBackendType, const uint64_t& aId, TextureFactoryIdentifier* aTextureFactoryIdentifier) MOZ_OVERRIDE; virtual bool DeallocPLayerTransaction(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -50,16 +50,17 @@ public: virtual ~CompositorParent(); virtual bool RecvWillStop() MOZ_OVERRIDE; virtual bool RecvStop() MOZ_OVERRIDE; virtual bool RecvPause() MOZ_OVERRIDE; virtual bool RecvResume() MOZ_OVERRIDE; virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, SurfaceDescriptor* aOutSnapshot); + virtual bool RecvFlushRendering() MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, bool isFirstPaint) MOZ_OVERRIDE; /** * This forces the is-first-paint flag to true. This is intended to
--- a/gfx/layers/ipc/PCompositor.ipdl +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -48,14 +48,18 @@ parent: // or format of |inSnapshot| doesn't match our render target, // results are undefined. // // NB: this message will result in animations, transforms, effects, // and so forth being interpolated. That's what we want to happen. sync MakeSnapshot(SurfaceDescriptor inSnapshot) returns (SurfaceDescriptor outSnapshot); + // Make sure any pending composites are started immediately and + // block until they are completed. + sync FlushRendering(); + sync PLayerTransaction(LayersBackend layersBackendHint, uint64_t id) returns (TextureFactoryIdentifier textureFactoryIdentifier); }; } // layers } // mozilla
--- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -176,28 +176,28 @@ struct JS_PUBLIC_API(NullPtr) */ template <typename T> class Heap : public js::HeapBase<T> { public: Heap() { MOZ_STATIC_ASSERT(sizeof(T) == sizeof(Heap<T>), "Heap<T> must be binary compatible with T."); - set(js::RootMethods<T>::initial()); + init(js::RootMethods<T>::initial()); } - explicit Heap(T p) { set(p); } - explicit Heap(const Heap<T> &p) { set(p.ptr); } + explicit Heap(T p) { init(p); } + explicit Heap(const Heap<T> &p) { init(p.ptr); } ~Heap() { if (js::RootMethods<T>::needsPostBarrier(ptr)) relocate(); } - bool operator!=(const T &other) { return *ptr != other; } - bool operator==(const T &other) { return *ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } operator T() const { return ptr; } T operator->() const { return ptr; } const T *address() const { return &ptr; } const T &get() const { return ptr; } T *unsafeGet() { return &ptr; } @@ -215,16 +215,23 @@ class Heap : public js::HeapBase<T> relocate(); /* Called before overwriting ptr. */ ptr = newPtr; } else { ptr = newPtr; } } private: + void init(T newPtr) { + JS_ASSERT(!js::RootMethods<T>::poisoned(newPtr)); + ptr = newPtr; + if (js::RootMethods<T>::needsPostBarrier(ptr)) + post(); + } + void post() { #ifdef JSGC_GENERATIONAL JS_ASSERT(js::RootMethods<T>::needsPostBarrier(ptr)); js::RootMethods<T>::postBarrier(&ptr); #endif } void relocate() { @@ -315,18 +322,18 @@ class MOZ_NONHEAP_CLASS Handle : public /* * Return a reference so passing a Handle<T> to something that * takes a |const T&| is not a GC hazard. */ operator const T&() const { return get(); } T operator->() const { return get(); } - bool operator!=(const T &other) { return *ptr != other; } - bool operator==(const T &other) { return *ptr == other; } + bool operator!=(const T &other) const { return *ptr != other; } + bool operator==(const T &other) const { return *ptr == other; } private: Handle() {} const T *ptr; template <typename S> void operator=(S v) MOZ_DELETE; @@ -600,18 +607,18 @@ class MOZ_STACK_CLASS Rooted : public js return ptr; } void set(T value) { JS_ASSERT(!js::RootMethods<T>::poisoned(value)); ptr = value; } - bool operator!=(const T &other) { return ptr != other; } - bool operator==(const T &other) { return ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } private: void commonInit(Rooted<void*> **thingGCRooters) { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) js::ThingRootKind kind = js::RootMethods<T>::kind(); this->stack = &thingGCRooters[kind]; this->prev = *stack; *stack = reinterpret_cast<Rooted<void*>*>(this); @@ -761,18 +768,18 @@ class FakeRooted : public RootedBase<T> const T &get() const { return ptr; } T &operator=(T value) { JS_ASSERT(!RootMethods<T>::poisoned(value)); ptr = value; return ptr; } - bool operator!=(const T &other) { return ptr != other; } - bool operator==(const T &other) { return ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } private: T ptr; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER FakeRooted(const FakeRooted &) MOZ_DELETE; };
--- a/js/public/Value.h +++ b/js/public/Value.h @@ -1415,28 +1415,31 @@ template <> struct RootMethods<JS::Value static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); } static bool needsPostBarrier(const JS::Value &v) { return v.isMarkable(); } #ifdef JSGC_GENERATIONAL static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); } static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); } #endif }; +template <class Outer> class UnbarrieredMutableValueOperations; template <class Outer> class MutableValueOperations; /* * A class designed for CRTP use in implementing the non-mutating parts of the * Value interface in Value-like classes. Outer must be a class inheriting * ValueOperations<Outer> with a visible extract() method returning the * const Value* abstracted by Outer. */ template <class Outer> class ValueOperations { + friend class UnbarrieredMutableValueOperations<Outer>; friend class MutableValueOperations<Outer>; + const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); } public: bool isUndefined() const { return value()->isUndefined(); } bool isNull() const { return value()->isNull(); } bool isBoolean() const { return value()->isBoolean(); } bool isTrue() const { return value()->isTrue(); } bool isFalse() const { return value()->isFalse(); } @@ -1464,51 +1467,92 @@ class ValueOperations void *toGCThing() const { return value()->toGCThing(); } JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); } JSWhyMagic whyMagic() const { return value()->whyMagic(); } }; /* - * A class designed for CRTP use in implementing the mutating parts of the - * Value interface in Value-like classes. Outer must be a class inheriting - * MutableValueOperations<Outer> with visible extractMutable() and extract() - * methods returning the const Value* and Value* abstracted by Outer. + * A class designed for CRTP use in implementing the mutating parts of the Value + * interface in Value-like classes that don't need post barriers. Outer must be + * a class inheriting UnbarrieredMutableValueOperations<Outer> with visible + * extractMutable() and extract() methods returning the const Value* and Value* + * abstracted by Outer. */ template <class Outer> -class MutableValueOperations : public ValueOperations<Outer> +class UnbarrieredMutableValueOperations : public ValueOperations<Outer> { + friend class MutableValueOperations<Outer>; JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); } public: void setNull() { value()->setNull(); } void setUndefined() { value()->setUndefined(); } void setInt32(int32_t i) { value()->setInt32(i); } void setDouble(double d) { value()->setDouble(d); } - void setString(JSString *str) { value()->setString(str); } - void setString(const JS::Anchor<JSString *> &str) { value()->setString(str); } - void setObject(JSObject &obj) { value()->setObject(obj); } void setBoolean(bool b) { value()->setBoolean(b); } void setMagic(JSWhyMagic why) { value()->setMagic(why); } bool setNumber(uint32_t ui) { return value()->setNumber(ui); } bool setNumber(double d) { return value()->setNumber(d); } - void setObjectOrNull(JSObject *arg) { value()->setObjectOrNull(arg); } +}; + +/* + * A class designed for CRTP use in implementing all the mutating parts of the + * Value interface in Value-like classes. Outer must be a class inheriting + * MutableValueOperations<Outer> with visible extractMutable() and extract() + * methods returning the const Value* and Value* abstracted by Outer. + */ +template <class Outer> +class MutableValueOperations : public UnbarrieredMutableValueOperations<Outer> +{ + public: + void setString(JSString *str) { this->value()->setString(str); } + void setString(const JS::Anchor<JSString *> &str) { this->value()->setString(str); } + void setObject(JSObject &obj) { this->value()->setObject(obj); } + void setObjectOrNull(JSObject *arg) { this->value()->setObjectOrNull(arg); } }; /* - * Augment the generic Heap<T> interface when T = Value with type-querying - * and value-extracting operations. + * Augment the generic Heap<T> interface when T = Value with + * type-querying, value-extracting, and mutating operations. */ template <> -class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> > +class HeapBase<JS::Value> : public UnbarrieredMutableValueOperations<JS::Heap<JS::Value> > { - friend class ValueOperations<JS::Heap<JS::Value> >; - const JS::Value * extract() const { - return static_cast<const JS::Heap<JS::Value>*>(this)->address(); + typedef JS::Heap<JS::Value> Outer; + + friend class ValueOperations<Outer>; + friend class UnbarrieredMutableValueOperations<Outer>; + + const JS::Value * extract() const { return static_cast<const Outer*>(this)->address(); } + JS::Value * extractMutable() { return static_cast<Outer*>(this)->unsafeGet(); } + + /* + * Setters that potentially change the value to a GC thing from a non-GC + * thing must call JS::Heap::set() to trigger the post barrier. + * + * Changing from a GC thing to a non-GC thing value will leave the heap + * value in the store buffer, but it will be ingored so this is not a + * problem. + */ + void setBarriered(const JS::Value &v) { + static_cast<JS::Heap<JS::Value> *>(this)->set(v); + } + + public: + void setString(JSString *str) { setBarriered(JS::StringValue(str)); } + void setString(const JS::Anchor<JSString *> &str) { setBarriered(JS::StringValue(str.get())); } + void setObject(JSObject &obj) { setBarriered(JS::ObjectValue(obj)); } + + void setObjectOrNull(JSObject *arg) { + if (arg) + setObject(*arg); + else + setNull(); } }; /* * Augment the generic Handle<T> interface when T = Value with type-querying * and value-extracting operations. */ template <> @@ -1527,16 +1571,17 @@ class HandleBase<JS::Value> : public Val template <> class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> > { friend class ValueOperations<JS::MutableHandle<JS::Value> >; const JS::Value * extract() const { return static_cast<const JS::MutableHandle<JS::Value>*>(this)->address(); } + friend class UnbarrieredMutableValueOperations<JS::MutableHandle<JS::Value> >; friend class MutableValueOperations<JS::MutableHandle<JS::Value> >; JS::Value * extractMutable() { return static_cast<JS::MutableHandle<JS::Value>*>(this)->address(); } }; /* * Augment the generic Rooted<T> interface when T = Value with type-querying, @@ -1545,16 +1590,17 @@ class MutableHandleBase<JS::Value> : pub template <> class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> > { friend class ValueOperations<JS::Rooted<JS::Value> >; const JS::Value * extract() const { return static_cast<const JS::Rooted<JS::Value>*>(this)->address(); } + friend class UnbarrieredMutableValueOperations<JS::Rooted<JS::Value> >; friend class MutableValueOperations<JS::Rooted<JS::Value> >; JS::Value * extractMutable() { return static_cast<JS::Rooted<JS::Value>*>(this)->address(); } }; } // namespace js
--- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -1134,32 +1134,32 @@ CodeGenerator::visitParDump(LParDump *li masm.freeStack(sizeof(Value)); return true; } bool CodeGenerator::visitTypeBarrier(LTypeBarrier *lir) { ValueOperand operand = ToValue(lir, LTypeBarrier::Input); - Register scratch = ToRegister(lir->temp()); + Register scratch = ToTempUnboxRegister(lir->temp()); Label matched, miss; masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss); masm.jump(&miss); if (!bailoutFrom(&miss, lir->snapshot())) return false; masm.bind(&matched); return true; } bool CodeGenerator::visitMonitorTypes(LMonitorTypes *lir) { ValueOperand operand = ToValue(lir, LMonitorTypes::Input); - Register scratch = ToRegister(lir->temp()); + Register scratch = ToTempUnboxRegister(lir->temp()); Label matched, miss; masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss); masm.jump(&miss); if (!bailoutFrom(&miss, lir->snapshot())) return false; masm.bind(&matched); return true; @@ -1283,17 +1283,17 @@ CodeGenerator::visitPostWriteBarrierV(LP } else { Label tenured; Register objreg = ToRegister(lir->object()); masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured); masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin()); masm.bind(&tenured); } - Register valuereg = masm.extractObject(value, ToRegister(lir->temp())); + Register valuereg = masm.extractObject(value, ToTempUnboxRegister(lir->temp())); masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin()); masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry()); masm.bind(ool->rejoin()); #endif return true; } @@ -3539,26 +3539,27 @@ bool CodeGenerator::visitCompareStrictS(LCompareStrictS *lir) { JSOp op = lir->mir()->jsop(); JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs); Register right = ToRegister(lir->right()); Register output = ToRegister(lir->output()); - Register temp = ToRegister(lir->temp0()); + Register temp = ToRegister(lir->temp()); + Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox()); Label string, done; masm.branchTestString(Assembler::Equal, leftV, &string); masm.move32(Imm32(op == JSOP_STRICTNE), output); masm.jump(&done); masm.bind(&string); - Register left = masm.extractString(leftV, ToRegister(lir->temp1())); + Register left = masm.extractString(leftV, tempToUnbox); if (!emitCompareS(lir, op, left, right, output, temp)) return false; masm.bind(&done); return true; } @@ -3705,19 +3706,19 @@ CodeGenerator::visitIsNullOrLikeUndefine masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); if (ool) { // Check whether it's a truthy object or a falsy object that emulates // undefined. masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); - Register objreg = masm.extractObject(value, ToRegister(lir->temp0())); + Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); testObjectTruthy(objreg, notNullOrLikeUndefined, nullOrLikeUndefined, - ToRegister(lir->temp1()), ool); + ToRegister(lir->temp()), ool); } Label done; // It's not null or undefined, and if it's an object it doesn't // emulate undefined, so it's not like undefined. masm.bind(notNullOrLikeUndefined); masm.move32(Imm32(op == JSOP_NE), output); @@ -3784,18 +3785,18 @@ CodeGenerator::visitIsNullOrLikeUndefine masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); if (ool) { masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); // Objects that emulate undefined are loosely equal to null/undefined. - Register objreg = masm.extractObject(value, ToRegister(lir->temp0())); - testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp1()), ool); + Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); + testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp()), ool); } else { masm.jump(ifFalseLabel); } return true; } JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
--- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -78,16 +78,17 @@ MacroAssembler::guardTypeSet(const Sourc if (types->hasType(types::Type::NullType())) branchTestNull(Equal, tag, matched); if (types->hasType(types::Type::MagicArgType())) branchTestMagic(Equal, tag, matched); if (types->hasType(types::Type::AnyObjectType())) { branchTestObject(Equal, tag, matched); } else if (types->getObjectCount()) { + JS_ASSERT(scratch != InvalidReg); branchTestObject(NotEqual, tag, miss); Register obj = extractObject(address, scratch); unsigned count = types->getObjectCount(); for (unsigned i = 0; i < count; i++) { if (JSObject *object = types->getSingleObject(i)) branchPtr(Equal, obj, ImmGCPtr(object), matched); }
--- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -1556,20 +1556,20 @@ class LCompareStrictS : public LInstruct setTemp(1, temp1); } static const size_t Lhs = 0; const LAllocation *right() { return getOperand(BOX_PIECES); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } MCompare *mir() { return mir_->toCompare(); } }; // Used for strict-equality comparisons where one side is a boolean @@ -1675,65 +1675,65 @@ class LCompareVM : public LCallInstructi } }; class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2> { public: LIR_HEADER(IsNullOrLikeUndefined) - LIsNullOrLikeUndefined(const LDefinition &temp0, const LDefinition &temp1) + LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox) { - setTemp(0, temp0); - setTemp(1, temp1); + setTemp(0, temp); + setTemp(1, tempToUnbox); } static const size_t Value = 0; MCompare *mir() { return mir_->toCompare(); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } }; class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2> { public: LIR_HEADER(IsNullOrLikeUndefinedAndBranch) - LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp0, const LDefinition &temp1) + LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp, const LDefinition &tempToUnbox) { setSuccessor(0, ifTrue); setSuccessor(1, ifFalse); - setTemp(0, temp0); - setTemp(1, temp1); + setTemp(0, temp); + setTemp(1, tempToUnbox); } static const size_t Value = 0; MBasicBlock *ifTrue() const { return getSuccessor(0); } MBasicBlock *ifFalse() const { return getSuccessor(1); } MCompare *mir() { return mir_->toCompare(); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } }; // Takes an object and tests whether it emulates |undefined|, as determined by // the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects. See also // js::EmulatesUndefined. class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
--- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -601,27 +601,27 @@ LIRGenerator::visitTest(MTest *test) MOZ_ASSERT(comp->operandMightEmulateUndefined(), "MCompare::tryFold should handle the never-emulates-undefined case"); LEmulatesUndefinedAndBranch *lir = new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp()); return add(lir, comp); } - LDefinition temp0, temp1; + LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { - temp0 = temp(); - temp1 = temp(); + tmp = temp(); + tmpToUnbox = tempToUnbox(); } else { - temp0 = LDefinition::BogusTemp(); - temp1 = LDefinition::BogusTemp(); + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); } LIsNullOrLikeUndefinedAndBranch *lir = - new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, temp0, temp1); + new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, tmp, tmpToUnbox); if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left)) return false; return add(lir, comp); } // Compare and branch booleans. if (comp->compareType() == MCompare::Compare_Boolean) { JS_ASSERT(left->type() == MIRType_Value); @@ -744,17 +744,17 @@ LIRGenerator::visitCompare(MCompare *com return assignSafepoint(lir, comp); } // Strict compare between value and string if (comp->compareType() == MCompare::Compare_StrictString) { JS_ASSERT(left->type() == MIRType_Value); JS_ASSERT(right->type() == MIRType_String); - LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp()); + LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), tempToUnbox()); if (!useBox(lir, LCompareStrictS::Lhs, left)) return false; if (!define(lir, comp)) return false; return assignSafepoint(lir, comp); } // Unknown/unspecialized compare use a VM call. @@ -780,26 +780,26 @@ LIRGenerator::visitCompare(MCompare *com { if (left->type() == MIRType_Object) { MOZ_ASSERT(comp->operandMightEmulateUndefined(), "MCompare::tryFold should have folded this away"); return define(new LEmulatesUndefined(useRegister(left)), comp); } - LDefinition temp0, temp1; + LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { - temp0 = temp(); - temp1 = temp(); + tmp = temp(); + tmpToUnbox = tempToUnbox(); } else { - temp0 = LDefinition::BogusTemp(); - temp1 = LDefinition::BogusTemp(); + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); } - LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(temp0, temp1); + LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(tmp, tmpToUnbox); if (!useBox(lir, LIsNullOrLikeUndefined::Value, left)) return false; return define(lir, comp); } // Compare booleans. if (comp->compareType() == MCompare::Compare_Boolean) { JS_ASSERT(left->type() == MIRType_Value); @@ -1727,30 +1727,40 @@ LIRGenerator::visitStoreSlot(MStoreSlot return true; } bool LIRGenerator::visitTypeBarrier(MTypeBarrier *ins) { // Requesting a non-GC pointer is safe here since we never re-enter C++ // from inside a type barrier test. - LTypeBarrier *barrier = new LTypeBarrier(temp()); + + const types::StackTypeSet *types = ins->resultTypeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + + LTypeBarrier *barrier = new LTypeBarrier(tmp); if (!useBox(barrier, LTypeBarrier::Input, ins->input())) return false; if (!assignSnapshot(barrier, ins->bailoutKind())) return false; return redefine(ins, ins->input()) && add(barrier, ins); } bool LIRGenerator::visitMonitorTypes(MMonitorTypes *ins) { // Requesting a non-GC pointer is safe here since we never re-enter C++ // from inside a type check. - LMonitorTypes *lir = new LMonitorTypes(temp()); + + const types::StackTypeSet *types = ins->typeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + + LMonitorTypes *lir = new LMonitorTypes(tmp); if (!useBox(lir, LMonitorTypes::Input, ins->input())) return false; return assignSnapshot(lir, Bailout_Normal) && add(lir, ins); } bool LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins) { @@ -1758,17 +1768,17 @@ LIRGenerator::visitPostWriteBarrier(MPos switch (ins->value()->type()) { case MIRType_Object: { LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()), useRegister(ins->value())); return add(lir, ins) && assignSafepoint(lir, ins); } case MIRType_Value: { LPostWriteBarrierV *lir = - new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp()); + new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox()); if (!useBox(lir, LPostWriteBarrierV::Input, ins->value())) return false; return add(lir, ins) && assignSafepoint(lir, ins); } default: // Currently, only objects can be in the nursery. Other instruction // types cannot hold nursery pointers. return true;
--- a/js/src/ion/arm/Lowering-arm.h +++ b/js/src/ion/arm/Lowering-arm.h @@ -21,16 +21,20 @@ class LIRGeneratorARM : public LIRGenera protected: // Adds a box input to an instruction, setting operand |n| to the type and // |n+1| to the payload. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2); + inline LDefinition tempToUnbox() { + return LDefinition::BogusTemp(); + } + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs); bool lowerUrshD(MUrsh *mir); bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h +++ b/js/src/ion/shared/CodeGenerator-shared-inl.h @@ -41,16 +41,24 @@ ToRegister(const LAllocation *a) static inline Register ToRegister(const LDefinition *def) { return ToRegister(*def->output()); } static inline Register +ToTempUnboxRegister(const LDefinition *def) +{ + if (def->isBogusTemp()) + return InvalidReg; + return ToRegister(def); +} + +static inline Register ToRegisterOrInvalid(const LAllocation *a) { return a ? ToRegister(*a) : InvalidReg; } static inline FloatRegister ToFloatRegister(const LAllocation &a) {
--- a/js/src/ion/x64/Lowering-x64.h +++ b/js/src/ion/x64/Lowering-x64.h @@ -23,16 +23,20 @@ class LIRGeneratorX64 : public LIRGenera void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); // Adds a use at operand |n| of a value-typed insturction. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register); + inline LDefinition tempToUnbox() { + return temp(); + } + LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins); bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
--- a/js/src/ion/x86/Lowering-x86.h +++ b/js/src/ion/x86/Lowering-x86.h @@ -21,16 +21,20 @@ class LIRGeneratorX86 : public LIRGenera protected: // Adds a box input to an instruction, setting operand |n| to the type and // |n+1| to the payload. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2); + inline LDefinition tempToUnbox() { + return LDefinition::BogusTemp(); + } + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox);
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -26,16 +26,21 @@ ((uintptr_t)(sp) < (limit)+(tolerance)) #else # define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ ((uintptr_t)(sp) > (limit)-(tolerance)) #endif #define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0) +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + extern JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); extern JS_FRIEND_API(JSString *) JS_GetAnonymousString(JSRuntime *rt); extern JS_FRIEND_API(JSObject *) JS_FindCompilationScope(JSContext *cx, JSObject *obj); @@ -885,17 +890,17 @@ NukeCrossCompartmentWrappers(JSContext* */ struct ExpandoAndGeneration { ExpandoAndGeneration() : expando(UndefinedValue()), generation(0) {} - Value expando; + JS::Heap<JS::Value> expando; uint32_t generation; }; typedef enum DOMProxyShadowsResult { ShadowCheckFailed, Shadows, DoesntShadow, DoesntShadowUnique
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -498,30 +498,30 @@ void XPCJSRuntime::TraceGrayJS(JSTracer* XPCJSRuntime* self = (XPCJSRuntime*)data; // Mark these roots as gray so the CC can walk them later. self->TraceXPConnectRoots(trc); } struct JsGcTracer : public TraceCallbacks { - virtual void Trace(JS::Value *p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallValueTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapValueTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(jsid *p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallIdTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<jsid> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapIdTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSObject **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallObjectTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapObjectTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSString **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallStringTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSString *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapStringTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSScript **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallScriptTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSScript *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapScriptTracer(static_cast<JSTracer*>(closure), p, name); } }; static PLDHashOperator TraceJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg) { tracer->Trace(holder, JsGcTracer(), arg);
--- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -322,17 +322,18 @@ NS_NewResizerFrame (nsIPresShell* aPresS #endif nsIFrame* NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); nsIFrame* -NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); +NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants); nsIFrame* NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* @@ -4111,18 +4112,21 @@ nsCSSFrameConstructor::BeginBuildingScro nsRefPtr<nsStyleContext> contentStyle = aContentStyle; if (!gfxScrollFrame) { // Build a XULScrollFrame when the child is a box, otherwise an // HTMLScrollFrame // XXXbz this is the lone remaining consumer of IsXULDisplayType. // I wonder whether we can eliminate that somehow. - if (IsXULDisplayType(aContentStyle->StyleDisplay())) { - gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot); + const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay(); + if (IsXULDisplayType(displayStyle)) { + gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot, + displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK || + displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK); } else { gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot); } InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame); } // if there are any anonymous children for the scroll frame, create
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -593,18 +593,20 @@ void nsDisplayListBuilder::MarkOutOfFlow } if (mHasDisplayPort && IsFixedFrame(aFrame)) { dirty = overflowRect; } if (!dirty.IntersectRect(dirty, overflowRect)) return; - aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), - new OutOfFlowDisplayData(mClipState.GetClipForContainingBlockDescendants(), dirty)); + const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants(); + OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty) + : new OutOfFlowDisplayData(dirty); + aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); MarkFrameForDisplay(aFrame, aDirtyFrame); } static void UnmarkFrameForDisplay(nsIFrame* aFrame) { nsPresContext* presContext = aFrame->PresContext(); presContext->PropertyTable()-> Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty()); @@ -803,24 +805,30 @@ void nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect) { if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) { // Must have not allocated a state for this presshell, presumably due // to OOM. return; } + ResetMarkedFramesForDisplayList(); + mPresShellStates.SetLength(mPresShellStates.Length() - 1); +} + +void +nsDisplayListBuilder::ResetMarkedFramesForDisplayList() +{ // Unmark and pop off the frames marked for display in this pres shell. uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay; for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length(); ++i) { UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]); } mFramesMarkedForDisplay.SetLength(firstFrameForShell); - mPresShellStates.SetLength(mPresShellStates.Length() - 1); } void nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, const nsFrameList& aFrames, const nsRect& aDirtyRect) { for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) { mFramesMarkedForDisplay.AppendElement(e.get()); @@ -1194,28 +1202,31 @@ void nsDisplayList::PaintForFrame(nsDisp nsIntRegion invalid; if (props) { invalid = props->ComputeDifferences(root, computeInvalidFunc); } else if (widgetTransaction) { LayerProperties::ClearInvalidations(root); } + bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); if (view) { if (props) { if (!invalid.IsEmpty()) { nsIntRect bounds = invalid.GetBounds(); nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), presContext->DevPixelsToAppUnits(bounds.y), presContext->DevPixelsToAppUnits(bounds.width), presContext->DevPixelsToAppUnits(bounds.height)); - view->GetViewManager()->InvalidateViewNoSuppression(view, rect); + if (shouldInvalidate) { + view->GetViewManager()->InvalidateViewNoSuppression(view, rect); + } presContext->NotifyInvalidation(bounds, 0); } - } else { + } else if (shouldInvalidate) { view->GetViewManager()->InvalidateView(view); } } if (aFlags & PAINT_FLUSH_LAYERS) { FrameLayerBuilder::InvalidateAllLayers(layerManager); }
--- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -330,16 +330,24 @@ public: nsCaret* GetCaret(); /** * Notify the display list builder that we're entering a presshell. * aReferenceFrame should be a frame in the new presshell and aDirtyRect * should be the current dirty rect in aReferenceFrame's coordinate space. */ void EnterPresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect); /** + * For print-preview documents, we sometimes need to build display items for + * the same frames multiple times in the same presentation, with different + * clipping. Between each such batch of items, call + * ResetMarkedFramesForDisplayList to make sure that the results of + * MarkFramesForDisplayList do not carry over between batches. + */ + void ResetMarkedFramesForDisplayList(); + /** * Notify the display list builder that we're leaving a presshell. */ void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect); /** * Returns true if we're currently building a display list that's * directly or indirectly under an nsDisplayTransform. */ @@ -561,22 +569,25 @@ public: bool mOldValue; }; // Helpers for tables nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; } void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; } struct OutOfFlowDisplayData { - OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip, + OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip, const nsRect &aDirtyRect) : mContainingBlockClip(aContainingBlockClip) , mDirtyRect(aDirtyRect) {} - const DisplayItemClip* mContainingBlockClip; + OutOfFlowDisplayData(const nsRect &aDirtyRect) + : mDirtyRect(aDirtyRect) + {} + DisplayItemClip mContainingBlockClip; nsRect mDirtyRect; }; static void DestroyOutOfFlowDisplayData(void* aPropertyValue) { delete static_cast<OutOfFlowDisplayData*>(aPropertyValue); } NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty, DestroyOutOfFlowDisplayData)
--- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1536,22 +1536,16 @@ nsDocumentViewer::Destroy() // Remove our root view from the view hierarchy. if (mPresShell) { nsViewManager *vm = mPresShell->GetViewManager(); if (vm) { nsView *rootView = vm->GetRootView(); if (rootView) { - // The invalidate that removing this view causes is dropped because - // the Freeze call above sets painting to be suppressed for our - // document. So we do it ourselves and make it happen. - vm->InvalidateViewNoSuppression(rootView, - rootView->GetBounds() - rootView->GetPosition()); - nsView *rootViewParent = rootView->GetParent(); if (rootViewParent) { nsViewManager *parentVM = rootViewParent->GetViewManager(); if (parentVM) { parentVM->RemoveChild(rootView); } } }
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5484,27 +5484,32 @@ PresShell::Paint(nsView* aViewToP NS_ASSERTION(aViewToPaint, "null view"); MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared"); if (!mIsActive || mIsZombie) { return; } - nsAutoNotifyDidPaint notifyDidPaint(this, aFlags); - nsPresContext* presContext = GetPresContext(); AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); nsIFrame* frame = aViewToPaint->GetFrame(); bool isRetainingManager; LayerManager* layerManager = aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager); NS_ASSERTION(layerManager, "Must be in paint event"); + bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); + + uint32_t didPaintFlags = aFlags; + if (!shouldInvalidate) { + didPaintFlags |= PAINT_COMPOSITE; + } + nsAutoNotifyDidPaint notifyDidPaint(this, didPaintFlags); // Whether or not we should set first paint when painting is // suppressed is debatable. For now we'll do it because // B2G relies on first paint to configure the viewport and // we only want to do that when we have real content to paint. // See Bug 798245 if (mIsFirstPaint && !mPaintingSuppressed) { layerManager->SetIsFirstPaint(); @@ -5514,19 +5519,16 @@ PresShell::Paint(nsView* aViewToP if (frame && isRetainingManager) { // Try to do an empty transaction, if the frame tree does not // need to be updated. Do not try to do an empty transaction on // a non-retained layer manager (like the BasicLayerManager that // draws the window title bar on Mac), because a) it won't work // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE, // that will cause us to forget to update the real layer manager! if (!(aFlags & PAINT_LAYERS)) { - if (layerManager->HasShadowManager() && Compositor::GetBackend() != LAYERS_BASIC) { - return; - } layerManager->BeginTransaction(); if (layerManager->EndEmptyTransaction()) { return; } NS_WARNING("Must complete empty transaction when compositing!"); } else { layerManager->BeginTransaction(); } @@ -5551,20 +5553,22 @@ PresShell::Paint(nsView* aViewToP } if (props) { if (!invalid.IsEmpty()) { nsIntRect bounds = invalid.GetBounds(); nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), presContext->DevPixelsToAppUnits(bounds.y), presContext->DevPixelsToAppUnits(bounds.width), presContext->DevPixelsToAppUnits(bounds.height)); - aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); + if (shouldInvalidate) { + aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); + } presContext->NotifyInvalidation(bounds, 0); } - } else { + } else if (shouldInvalidate) { aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint); } frame->UpdatePaintCountForPaintedPresShells(); return; } } frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2102,17 +2102,17 @@ nsIFrame::BuildDisplayListForChild(nsDis IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG; nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem); DisplayListClipState::AutoClipMultiple clipState(aBuilder); if (savedOutOfFlowData) { clipState.SetClipForContainingBlockDescendants( - savedOutOfFlowData->mContainingBlockClip); + &savedOutOfFlowData->mContainingBlockClip); } // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, // or overflow:hidden on elements that don't support scrolling (and therefore // don't create nsHTML/XULScrollFrame). This clipping needs to not clip // anything directly rendered by the parent, only the rendering of its // children. // Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -885,28 +885,32 @@ NS_QUERYFRAME_HEAD(nsHTMLScrollFrame) NS_QUERYFRAME_ENTRY(nsIScrollbarOwner) NS_QUERYFRAME_ENTRY(nsIScrollableFrame) NS_QUERYFRAME_ENTRY(nsIStatefulFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) //----------nsXULScrollFrame------------------------------------------- nsIFrame* -NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot) +NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants) { - return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot); + return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot, + aClipAllDescendants); } NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame) -nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot) +nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants) : nsBoxFrame(aShell, aContext, aIsRoot), mInner(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot) { SetLayoutManager(nullptr); + mInner.mClipAllDescendants = aClipAllDescendants; } nsMargin nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) { NS_ASSERTION(aState && aState->GetRenderingContext(), "Must have rendering context in layout state for size " "computations"); @@ -1498,16 +1502,17 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI , mLastUpdateImagesPos(-1, -1) , mNeverHasVerticalScrollbar(false) , mNeverHasHorizontalScrollbar(false) , mHasVerticalScrollbar(false) , mHasHorizontalScrollbar(false) , mFrameIsUpdatingScrollbar(false) , mDidHistoryRestore(false) , mIsRoot(aIsRoot) + , mClipAllDescendants(aIsRoot) , mSupppressScrollbarUpdate(false) , mSkippedScrollbarLayout(false) , mHadNonInitialReflow(false) , mHorizontalOverflow(false) , mVerticalOverflow(false) , mPostedReflowCallback(false) , mMayHaveDirtyFixedChildren(false) , mUpdateScrollbarAttributes(false) @@ -2193,26 +2198,26 @@ nsGfxScrollFrameInner::BuildDisplayList( } nsDisplayListCollection set; { DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (usingDisplayport) { nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter); - if (mIsRoot) { + if (mClipAllDescendants) { clipState.ClipContentDescendants(clip); } else { clipState.ClipContainingBlockDescendants(clip, nullptr); } } else { nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter); // Our override of GetBorderRadii ensures we never have a radius at // the corners where we have a scrollbar. - if (mIsRoot) { + if (mClipAllDescendants) { #ifdef DEBUG nscoord radii[8]; #endif NS_ASSERTION(!mOuter->GetPaddingBoxBorderRadii(radii), "Roots with radii not supported"); clipState.ClipContentDescendants(clip); } else { nscoord radii[8];
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -315,16 +315,19 @@ public: bool mNeverHasVerticalScrollbar:1; bool mNeverHasHorizontalScrollbar:1; bool mHasVerticalScrollbar:1; bool mHasHorizontalScrollbar:1; bool mFrameIsUpdatingScrollbar:1; bool mDidHistoryRestore:1; // Is this the scrollframe for the document's viewport? bool mIsRoot:1; + // True if we should clip all descendants, false if we should only clip + // descendants for which we are the containing block. + bool mClipAllDescendants:1; // If true, don't try to layout the scrollbars in Reflow(). This can be // useful if multiple passes are involved, because we don't want to place the // scrollbars at the wrong size. bool mSupppressScrollbarUpdate:1; // If true, we skipped a scrollbar layout due to mSupppressScrollbarUpdate // being set at some point. That means we should lay out scrollbars even if // it might not strictly be needed next time mSupppressScrollbarUpdate is // false. @@ -625,17 +628,18 @@ private: class nsXULScrollFrame : public nsBoxFrame, public nsIScrollableFrame, public nsIAnonymousContentCreator, public nsIStatefulFrame { public: NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS - friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); + friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants); // Called to set the child frames. We typically have three: the scroll area, // the vertical scrollbar, and the horizontal scrollbar. NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) MOZ_OVERRIDE; virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -848,17 +852,18 @@ public: return nsBoxFrame::IsFrameOfType(aFlags); } #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; #endif protected: - nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot); + nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot, + bool aClipAllDescendants); void ClampAndSetBounds(nsBoxLayoutState& aState, nsRect& aRect, nsPoint aScrollPosition, bool aRemoveOverflowAreas = false) { /* * For RTL frames, restore the original scrolled position of the right * edge, then subtract the current width to find the physical position.
--- a/layout/generic/nsSimplePageSequence.cpp +++ b/layout/generic/nsSimplePageSequence.cpp @@ -813,16 +813,17 @@ nsSimplePageSequenceFrame::BuildDisplayL // nsDisplayTransform, since they'll be in a different coordinate system. DisplayListClipState::AutoSaveRestore clipState(aBuilder); clipState.Clear(); nsIFrame* child = GetFirstPrincipalChild(); while (child) { child->BuildDisplayListForStackingContext(aBuilder, child->GetVisualOverflowRectRelativeToSelf(), &content); + aBuilder->ResetMarkedFramesForDisplayList(); child = child->GetNextSibling(); } } content.AppendNewToTop(new (aBuilder) nsDisplayTransform(aBuilder, this, &content, ::ComputePageSequenceTransform)); aLists.Content()->AppendToTop(&content);
--- a/layout/reftests/bugs/598726-1.html +++ b/layout/reftests/bugs/598726-1.html @@ -1,10 +1,10 @@ <!DOCTYPE html> -<html class="reftest-wait"> +<html class="reftest-wait reftest-snapshot-all"> <head> <style type="text/css"> input { -webkit-transition: -webkit-transform 200ms ease-in-out; transition: transform 200ms ease-in-out; } input:focus { -webkit-transform: scale(1.05); @@ -26,17 +26,17 @@ i.getClientRects(); i.addEventListener("transitionend", function(aEvent) { if (aEvent.propertyName != 'transform' && aEvent.propertyName != '-webkit-transform') { return; } i.removeEventListener("transitionend", arguments.callee, true); i.getClientRects(); - document.documentElement.removeAttribute("class"); + document.documentElement.setAttribute("class", "reftest-snapshot-all"); }, true); i.blur(); i.getClientRects(); }, true); i.focus(); i.getClientRects(); } </script>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/875060-1-ref.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> +<div style="display:-moz-inline-stack; width:100px; height:100px; background:yellow"> +</div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/875060-1.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> +<body> +<div style="display:-moz-inline-stack; overflow:hidden; position:relative; width:100px; height:100px;"> + <div style="width:200px; height:200px; background:yellow; position:absolute"></div> +</div> +</body> +</html>
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1755,8 +1755,9 @@ fails-if(Android) == 836844-1.html 83684 == 846144-1.html 846144-1-ref.html == 847850-1.html 847850-1-ref.html == 848421-1.html 848421-1-ref.html test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html == 849996-1.html 849996-1-ref.html == 858803-1.html 858803-1-ref.html != 860370.html 860370-notref.html == 871338-1.html 871338-1-ref.html +== 875060-1.html 875060-1-ref.html
--- a/layout/reftests/forms/input/range/reftest.list +++ b/layout/reftests/forms/input/range/reftest.list @@ -16,18 +16,18 @@ default-preferences pref(dom.experimenta # dynamic value changes: == input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html == input-value-prop.html input-75pct-common-ref.html == input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html == input-valueAsNumber-prop.html input-75pct-common-ref.html fails-if(B2G) == input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html fails-if(B2G) == input-stepDown.html input-75pct-common-ref.html -fails-if(B2G) == input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html -fails-if(B2G) == input-stepUp.html input-75pct-common-ref.html +== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html +== input-stepUp.html input-75pct-common-ref.html # 'direction' property: == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html # ::-moz-range-progress pseudo-element: fails-if(B2G) == input-range-moz-range-progress-1.html input-range-moz-range-progress-1-ref.html == input-range-moz-range-progress-2.html input-range-moz-range-progress-2-ref.html == input-range-moz-range-progress-3.html input-range-moz-range-progress-3-ref.html
--- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -299,17 +299,20 @@ CommonElementAnimationData::CanAnimatePr } } bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled(); if (!enabled && shouldLog) { nsCString message; message.AppendLiteral("Performance warning: Async animations are disabled"); LogAsyncAnimationFailure(message); } - return enabled && (aFlags & CanAnimate_AllowPartial); + bool propertyAllowed = (aProperty == eCSSProperty_transform) || + (aProperty == eCSSProperty_opacity) || + (aFlags & CanAnimate_AllowPartial); + return enabled && propertyAllowed; } /* static */ void CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage, const nsIContent* aContent) { if (aContent) { aMessage.AppendLiteral(" [");
--- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -236,16 +236,24 @@ function shouldWaitForPendingPaints() { function shouldWaitForReftestWaitRemoval(contentRootElement) { // use getAttribute because className works differently in HTML and SVG return contentRootElement && contentRootElement.hasAttribute('class') && contentRootElement.getAttribute('class').split(/\s+/) .indexOf("reftest-wait") != -1; } +function shouldSnapshotWholePage(contentRootElement) { + // use getAttribute because className works differently in HTML and SVG + return contentRootElement && + contentRootElement.hasAttribute('class') && + contentRootElement.getAttribute('class').split(/\s+/) + .indexOf("reftest-snapshot-all") != -1; +} + function getNoPaintElements(contentRootElement) { return contentRootElement.getElementsByClassName('reftest-no-paint'); } // Initial state. When the document has loaded and all MozAfterPaint events and // all explicit paint waits are flushed, we can fire the MozReftestInvalidate // event and move to the next state. const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0; @@ -301,17 +309,17 @@ function WaitForTestEnd(contentRootEleme function AfterPaintListener(event) { LogInfo("AfterPaintListener in " + event.target.document.location.href); if (event.target.document != currentDoc) { // ignore paint events for subframes or old documents in the window. // Invalidation in subframes will cause invalidation in the toplevel document anyway. return; } - SendUpdateCanvasForEvent(event); + SendUpdateCanvasForEvent(event, contentRootElement); // These events are fired immediately after a paint. Don't // confuse ourselves by firing synchronously if we triggered the // paint ourselves. setTimeout(MakeProgress, 0); } function AttrModifiedListener() { LogInfo("AttrModifiedListener fired"); @@ -835,22 +843,34 @@ function SendTestDone(runtimeMs) sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs }); } function roundTo(x, fraction) { return Math.round(x/fraction)*fraction; } -function SendUpdateCanvasForEvent(event) +function SendUpdateCanvasForEvent(event, contentRootElement) { var win = content; var scale = markupDocumentViewer().fullZoom; var rects = [ ]; + if (shouldSnapshotWholePage) { + // See comments in SendInitCanvasWithSnapshot() re: the split + // logic here. + if (!gBrowserIsRemote) { + sendSyncMessage("reftest:UpdateWholeCanvasForInvalidation"); + } else { + SynchronizeForSnapshot(SYNC_ALLOW_DISABLE); + sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation"); + } + return; + } + var rectList = event.clientRects; LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects"); for (var i = 0; i < rectList.length; ++i) { var r = rectList[i]; // Set left/top/right/bottom to "device pixel" boundaries var left = Math.floor(roundTo(r.left*scale, 0.001)); var top = Math.floor(roundTo(r.top*scale, 0.001)); var right = Math.ceil(roundTo(r.right*scale, 0.001));
--- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -1387,16 +1387,28 @@ function UpdateCurrentCanvasForInvalidat ctx.save(); ctx.translate(left, top); DoDrawWindow(ctx, left, top, right - left, bottom - top); ctx.restore(); } } +function UpdateWholeCurrentCanvasForInvalidation() +{ + LogInfo("Updating entire canvas for invalidation"); + + if (!gCurrentCanvas) { + return; + } + + var ctx = gCurrentCanvas.getContext("2d"); + DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height); +} + function RecordResult(testRunTime, errorMsg, scriptResults) { LogInfo("RecordResult fired"); // Keep track of which test was slowest, and how long it took. if (testRunTime > gSlowestTestTime) { gSlowestTestTime = testRunTime; gSlowestTestURL = gCurrentURL; @@ -1786,16 +1798,20 @@ function RegisterMessageListenersAndLoad "reftest:TestDone", function (m) { RecvTestDone(m.json.runtimeMs); } ); gBrowserMessageManager.addMessageListener( "reftest:UpdateCanvasForInvalidation", function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); } ); gBrowserMessageManager.addMessageListener( + "reftest:UpdateWholeCanvasForInvalidation", + function (m) { RecvUpdateWholeCanvasForInvalidation(); } + ); + gBrowserMessageManager.addMessageListener( "reftest:ExpectProcessCrash", function (m) { RecvExpectProcessCrash(); } ); gBrowserMessageManager.addMessageListener( "reftest:EnableAsyncScroll", function (m) { SetAsyncScroll(true); } ); @@ -1865,16 +1881,21 @@ function RecvTestDone(runtimeMs) RecordResult(runtimeMs, '', [ ]); } function RecvUpdateCanvasForInvalidation(rects) { UpdateCurrentCanvasForInvalidation(rects); } +function RecvUpdateWholeCanvasForInvalidation() +{ + UpdateWholeCurrentCanvasForInvalidation(); +} + function OnProcessCrashed(subject, topic, data) { var id; subject = subject.QueryInterface(CI.nsIPropertyBag2); if (topic == "plugin-crashed") { id = subject.getPropertyAsAString("pluginDumpID"); } else if (topic == "ipc:content-shutdown") { id = subject.getPropertyAsAString("dumpID");
--- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1799,17 +1799,16 @@ pref("layout.frame_rate", -1); // heavily loaded. pref("layout.frame_rate.precise", false); // pref to permit users to make verified SOAP calls by default pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader", "allAccess"); // if true, allow plug-ins to override internal imglib decoder mime types in full-page mode pref("plugin.override_internal_types", false); -pref("plugin.expose_full_path", false); // if true navigator.plugins reveals full path // See bug 136985. Gives embedders a pref to hook into to show // a popup blocker if they choose. pref("browser.popups.showPopupBlocker", true); // Pref to control whether the viewmanager code does double-buffering or not // See http://bugzilla.mozilla.org/show_bug.cgi?id=169483 for further details... pref("viewmanager.do_doublebuffering", true);
--- a/view/public/nsViewManager.h +++ b/view/public/nsViewManager.h @@ -339,20 +339,16 @@ private: void ReparentWidgets(nsView* aView, nsView *aParent); void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion); void InvalidateViews(nsView *aView); // aView is the view for aWidget and aRegion is relative to aWidget. void Refresh(nsView *aView, const nsIntRegion& aRegion); - void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut); - void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, - nscoord aY1, nscoord aY2, bool aInCutOut); - // Utilities bool IsViewInserted(nsView *aView); /** * Intersects aRect with aView's bounds and then transforms it from aView's * coordinate system to the coordinate system of the widget attached to * aView.
--- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -20,16 +20,17 @@ #include "nsPresContext.h" #include "nsEventStateManager.h" #include "mozilla/StartupTimeline.h" #include "GeckoProfiler.h" #include "nsRefreshDriver.h" #include "mozilla/Preferences.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" +#include "mozilla/layers/Compositor.h" /** XXX TODO XXX DeCOMify newly private methods Optimize view storage */ @@ -37,16 +38,18 @@ A note about platform assumptions: We assume that a widget is z-ordered on top of its parent. We do NOT assume anything about the relative z-ordering of sibling widgets. Even though we ask for a specific z-order, we don't assume that widget z-ordering actually works. */ +using namespace mozilla::layers; + #define NSCOORD_NONE INT32_MIN #undef DEBUG_MOUSE_LOCATION int32_t nsViewManager::mVMCount = 0; // Weakly held references to all of the view managers nsVoidArray* nsViewManager::gViewManagers = nullptr; @@ -325,18 +328,24 @@ void nsViewManager::Refresh(nsView *aVie "Widgets that we paint must all be display roots"); if (mPresShell) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("--COMPOSITE-- %p\n", mPresShell); } #endif - mPresShell->Paint(aView, damageRegion, - nsIPresShell::PAINT_COMPOSITE); + uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE; + LayerManager *manager = widget->GetLayerManager(); + if (!manager->NeedsWidgetInvalidation()) { + manager->FlushRendering(); + } else { + mPresShell->Paint(aView, damageRegion, + paintFlags); + } #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("--ENDCOMPOSITE--\n"); } #endif mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); } @@ -379,17 +388,16 @@ void nsViewManager::ProcessPendingUpdate for (nsViewManager *vm = this; vm; vm = vm->mRootView->GetParent() ? vm->mRootView->GetParent()->GetViewManager() : nullptr) { if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) && vm->mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) { vm->FlushDelayedResize(true); - vm->InvalidateView(vm->mRootView); } } NS_ASSERTION(aView->HasWidget(), "Must have a widget!"); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget); @@ -837,21 +845,16 @@ nsViewManager::InsertChild(nsView *aPare aParent->InsertChild(aChild, kid); ReparentWidgets(aChild, aParent); } } // if the parent view is marked as "floating", make the newly added view float as well. if (aParent->GetFloating()) aChild->SetFloating(true); - - //and mark this area as dirty if the view is visible... - - if (nsViewVisibility_kHide != aChild->GetVisibility()) - aChild->GetViewManager()->InvalidateView(aChild); } } void nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex) { // no-one really calls this with anything other than aZIndex == 0 on a fresh view // XXX this method should simply be eliminated and its callers redirected to the real method @@ -864,103 +867,35 @@ nsViewManager::RemoveChild(nsView *aChil { NS_ASSERTION(aChild, "aChild must not be null"); nsView* parent = aChild->GetParent(); if (nullptr != parent) { NS_ASSERTION(aChild->GetViewManager() == this || parent->GetViewManager() == this, "wrong view manager"); - aChild->GetViewManager()->InvalidateView(aChild); parent->RemoveChild(aChild); } } void nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); - nsPoint oldPt = aView->GetPosition(); - nsRect oldBounds = aView->GetBoundsInParentUnits(); aView->SetPosition(aX, aY); - - // only do damage control if the view is visible - - if ((aX != oldPt.x) || (aY != oldPt.y)) { - if (aView->GetVisibility() != nsViewVisibility_kHide) { - nsView* parentView = aView->GetParent(); - if (parentView) { - nsViewManager* parentVM = parentView->GetViewManager(); - parentVM->InvalidateView(parentView, oldBounds); - parentVM->InvalidateView(parentView, aView->GetBoundsInParentUnits()); - } - } - } -} - -void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, - nscoord aY1, nscoord aY2, bool aInCutOut) { - nscoord height = aY2 - aY1; - if (aRect.x < aCutOut.x) { - nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height); - InvalidateView(aView, r); - } - if (!aInCutOut && aCutOut.x < aCutOut.XMost()) { - nsRect r(aCutOut.x, aY1, aCutOut.width, height); - InvalidateView(aView, r); - } - if (aCutOut.XMost() < aRect.XMost()) { - nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height); - InvalidateView(aView, r); - } -} - -void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) { - NS_ASSERTION(aView->GetViewManager() == this, - "InvalidateRectDifference called on view we don't own"); - if (aRect.y < aCutOut.y) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false); - } - if (aCutOut.y < aCutOut.YMost()) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true); - } - if (aCutOut.YMost() < aRect.YMost()) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false); - } } void nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); nsRect oldDimensions = aView->GetDimensions(); if (!oldDimensions.IsEqualEdges(aRect)) { - // resize the view. - // Prevent Invalidation of hidden views - if (aView->GetVisibility() == nsViewVisibility_kHide) { - aView->SetDimensions(aRect, false); - } else { - nsView* parentView = aView->GetParent(); - if (!parentView) { - parentView = aView; - } - nsRect oldBounds = aView->GetBoundsInParentUnits(); - aView->SetDimensions(aRect, true); - nsViewManager* parentVM = parentView->GetViewManager(); - if (!aRepaintExposedAreaOnly) { - // Invalidate the union of the old and new size - InvalidateView(aView, aRect); - parentVM->InvalidateView(parentView, oldBounds); - } else { - InvalidateRectDifference(aView, aRect, oldDimensions); - nsRect newBounds = aView->GetBoundsInParentUnits(); - parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds); - } - } + aView->SetDimensions(aRect, true); } // Note that if layout resizes the view and the view has a custom clip // region set, then we expect layout to update the clip region too. Thus // in the case where mClipRect has been optimized away to just be a null // pointer, and this resize is implicitly changing the clip rect, it's OK // because layout will change it back again if necessary. } @@ -975,31 +910,16 @@ nsViewManager::SetViewFloating(nsView *a void nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); if (aVisible != aView->GetVisibility()) { aView->SetVisibility(aVisible); - - if (IsViewInserted(aView)) { - if (!aView->HasWidget()) { - if (nsViewVisibility_kHide == aVisible) { - nsView* parentView = aView->GetParent(); - if (parentView) { - parentView->GetViewManager()-> - InvalidateView(parentView, aView->GetBoundsInParentUnits()); - } - } - else { - InvalidateView(aView); - } - } - } } } bool nsViewManager::IsViewInserted(nsView *aView) { if (mRootView == aView) { return true; } else if (aView->GetParent() == nullptr) { @@ -1022,30 +942,21 @@ nsViewManager::SetViewZIndex(nsView *aVi NS_ASSERTION((aView != nullptr), "no view"); // don't allow the root view's z-index to be changed. It should always be zero. // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences if (aView == mRootView) { return; } - bool oldTopMost = aView->IsTopMost(); - bool oldIsAuto = aView->GetZIndexIsAuto(); - if (aAutoZIndex) { aZIndex = 0; } - int32_t oldidx = aView->GetZIndex(); aView->SetZIndex(aAutoZIndex, aZIndex, aTopMost); - - if (oldidx != aZIndex || oldTopMost != aTopMost || - oldIsAuto != aAutoZIndex) { - InvalidateView(aView); - } } nsViewManager* nsViewManager::IncrementDisableRefreshCount() { if (!IsRootVM()) { return RootViewManager()->IncrementDisableRefreshCount(); }
--- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1381,16 +1381,20 @@ static void blinkRgn(RgnHandle rgn) // Invalidate this component's visible area NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; if (!mView || !mVisible) return NS_OK; + NS_ASSERTION(GetLayerManager()->GetBackendType() != LAYERS_CLIENT || + Compositor::GetBackend() == LAYERS_BASIC, + "Shouldn't need to invalidate with accelerated OMTC layers!"); + if ([NSView focusView]) { // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we // don't lose it. [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)]; } else { [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)]; }
--- a/xpcom/glue/Makefile.in +++ b/xpcom/glue/Makefile.in @@ -41,16 +41,17 @@ SDK_HEADERS = \ nsIClassInfoImpl.h \ nsID.h \ nsIInterfaceRequestorUtils.h \ nsINIParser.h \ nsISupportsImpl.h \ nsISupportsUtils.h \ nsIWeakReferenceUtils.h \ nsInterfaceHashtable.h \ + nsJSThingHashtable.h \ nsMemory.h \ nsQuickSort.h \ nsRefPtrHashtable.h \ nsServiceManagerUtils.h \ nsStringAPI.h \ nsStringGlue.h \ nsTArray.h \ nsTArray-inl.h \
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp +++ b/xpcom/glue/nsCycleCollectionParticipant.cpp @@ -64,39 +64,39 @@ CycleCollectionNoteEdgeNameImpl(nsCycleC nsAutoCString arrayEdgeName(aName); if (aFlags & CycleCollectionEdgeNameArrayFlag) { arrayEdgeName.AppendLiteral("[i]"); } aCallback.NoteNextEdgeName(arrayEdgeName.get()); } void -TraceCallbackFunc::Trace(JS::Value* p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const { - mCallback(JSVAL_TO_TRACEABLE(*p), name, closure); + mCallback(JSVAL_TO_TRACEABLE(p->get()), name, closure); } void -TraceCallbackFunc::Trace(jsid* p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<jsid>* p, const char* name, void* closure) const { void *thing = JSID_TO_GCTHING(*p); if (thing) { mCallback(thing, name, closure); } } void -TraceCallbackFunc::Trace(JSObject** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const { mCallback(*p, name, closure); } void -TraceCallbackFunc::Trace(JSString** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const { mCallback(*p, name, closure); } void -TraceCallbackFunc::Trace(JSScript** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const { - mCallback(*p, name, closure); + mCallback(p->get(), name, closure); }
--- a/xpcom/glue/nsCycleCollectionParticipant.h +++ b/xpcom/glue/nsCycleCollectionParticipant.h @@ -46,49 +46,49 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCol /** * Forward declarations */ class nsCycleCollectionParticipant; class nsScriptObjectTracer; class nsXPCOMCycleCollectionParticipant; +namespace JS { +template <class T> class Heap; +} /* namespace JS */ + /* * A struct defining pure virtual methods which are called when tracing cycle * collection paticipants. The appropriate method is called depending on the * type of JS GC thing. */ struct TraceCallbacks { - virtual void Trace(JS::Value* p, const char* name, void* closure) const = 0; - virtual void Trace(jsid* p, const char* name, void* closure) const = 0; - virtual void Trace(JSObject** p, const char* name, void* closure) const = 0; - virtual void Trace(JSString** p, const char* name, void* closure) const = 0; - virtual void Trace(JSScript** p, const char* name, void* closure) const = 0; - - void Trace(JSFlatString** p, const char* name, void* closure) const { - Trace(reinterpret_cast<JSString**>(p), name, closure); - } + virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const = 0; }; /* * An implementation of TraceCallbacks that calls a single function for all JS * GC thing types encountered. */ struct TraceCallbackFunc : public TraceCallbacks { typedef void (* Func)(void* p, const char* name, void* closure); explicit TraceCallbackFunc(Func cb) : mCallback(cb) {} - virtual void Trace(JS::Value* p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(jsid* p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSObject** p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSString** p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSScript** p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const MOZ_OVERRIDE; private: Func mCallback; }; /** * VTables *
new file mode 100644 --- /dev/null +++ b/xpcom/glue/nsJSThingHashtable.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsJSThingHashtable_h__ +#define nsJSThingHashtable_h__ + +#include "nsHashKeys.h" +#include "nsBaseHashtable.h" + +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + +/** + * A wrapper for hash keys that sets ALLOW_MEMMOVE to false. + * + * This is used in the implementation of nsJSThingHashtable and is not intended + * to be used directly. + * + * It is necessary for hash tables containing JS::Heap<T> values as these must + * be copied rather than memmoved. + */ +template<class T> +class nsHashKeyDisallowMemmove : public T +{ + public: + nsHashKeyDisallowMemmove(const T& key) : T(key) {} + enum { ALLOW_MEMMOVE = false }; +}; + + +/** + * Templated hashtable class for use on the heap where the values are JS GC things. + * + * Storing JS GC thing pointers on the heap requires wrapping them in a + * JS::Heap<T>, and this class takes care of that while presenting an interface + * in terms of the wrapped type T. + * + * For example, to store a hashtable mapping strings to JSObject pointers, you + * can declare a data member like this: + * + * nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap; + * + * See nsBaseHashtable for complete declaration + * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h + * for a complete specification. + * @param DataType the datatype being wrapped, must be a JS GC thing. + * @see nsInterfaceHashtable, nsClassHashtable + */ +template<class KeyClass,class DataType> +class nsJSThingHashtable : + public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>, JS::Heap<DataType>, DataType> +{ }; + +#endif // nsJSThingHashtable_h__
--- a/xpcom/glue/nsTArray-inl.h +++ b/xpcom/glue/nsTArray-inl.h @@ -3,32 +3,32 @@ /* 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/. */ #ifndef nsTArray_h__ # error "Don't include this file directly" #endif -template<class Alloc> -nsTArray_base<Alloc>::nsTArray_base() +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::nsTArray_base() : mHdr(EmptyHdr()) { MOZ_COUNT_CTOR(nsTArray_base); } -template<class Alloc> -nsTArray_base<Alloc>::~nsTArray_base() { +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::~nsTArray_base() { if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) { Alloc::Free(mHdr); } MOZ_COUNT_DTOR(nsTArray_base); } -template<class Alloc> -const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const { +template<class Alloc, class Copy> +const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const { // Assuming |this| points to an nsAutoArray, we want to get a pointer to // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf! const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf; // If we're on a 32-bit system and elemAlign is 8, we need to adjust our // pointer to take into account the extra alignment in the auto array. @@ -43,18 +43,18 @@ const nsTArrayHeader* nsTArray_base<Allo NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment."); if (sizeof(void*) == 4 && elemAlign == 8) { autoBuf = reinterpret_cast<const char*>(autoBuf) + 4; } return reinterpret_cast<const Header*>(autoBuf); } -template<class Alloc> -bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const { +template<class Alloc, class Copy> +bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const { if (!mHdr->mIsAutoArray) { return false; } // This is nuts. If we were sane, we'd pass elemAlign as a parameter to // this function. Unfortunately this function is called in nsTArray_base's // destructor, at which point we don't know elem_type's alignment. // @@ -91,19 +91,19 @@ bool nsTArray_base<Alloc>::UsesAutoArray reinterpret_cast<const char*>(GetAutoArrayBuffer(4)); NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect."); #endif return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8); } -template<class Alloc> +template<class Alloc, class Copy> typename Alloc::ResultTypeProxy -nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) { +nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) { // This should be the most common case so test this first if (capacity <= mHdr->mCapacity) return Alloc::SuccessResult(); // If the requested memory allocation exceeds size_type(-1)/2, then // our doubling algorithm may not be able to allocate it. // Additionally we couldn't fit in the Header::mCapacity // member. Just bail out in cases like that. We don't want to be @@ -150,23 +150,26 @@ nsTArray_base<Alloc>::EnsureCapacity(siz bytesToAlloc |= bytesToAlloc >> 16; bytesToAlloc++; MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0, "nsTArray's allocation size should be a power of two!"); } Header *header; - if (UsesAutoArrayBuffer()) { + if (UsesAutoArrayBuffer() || !Copy::allowRealloc) { // Malloc() and copy header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc)); if (!header) return Alloc::FailureResult(); - memcpy(header, mHdr, sizeof(Header) + Length() * elemSize); + Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); + + if (!UsesAutoArrayBuffer()) + Alloc::Free(mHdr); } else { // Realloc() existing data header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc)); if (!header) return Alloc::FailureResult(); } // How many elements can we fit in bytesToAlloc? @@ -174,33 +177,33 @@ nsTArray_base<Alloc>::EnsureCapacity(siz MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!"); header->mCapacity = newCapacity; mHdr = header; return Alloc::SuccessResult(); } -template<class Alloc> +template<class Alloc, class Copy> void -nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) { +nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) { if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) return; if (mHdr->mLength >= mHdr->mCapacity) // should never be greater than... return; size_type length = Length(); if (IsAutoArray() && GetAutoArrayBuffer(elemAlign)->mCapacity >= length) { Header* header = GetAutoArrayBuffer(elemAlign); // Copy data, but don't copy the header to avoid overwriting mCapacity header->mLength = length; - memcpy(header + 1, mHdr + 1, length * elemSize); + Copy::CopyElements(header + 1, mHdr + 1, length, elemSize); Alloc::Free(mHdr); mHdr = header; return; } if (length == 0) { MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements"); @@ -212,19 +215,19 @@ nsTArray_base<Alloc>::ShrinkCapacity(siz size_type size = sizeof(Header) + length * elemSize; void *ptr = Alloc::Realloc(mHdr, size); if (!ptr) return; mHdr = static_cast<Header*>(ptr); mHdr->mCapacity = length; } -template<class Alloc> +template<class Alloc, class Copy> void -nsTArray_base<Alloc>::ShiftData(index_type start, +nsTArray_base<Alloc, Copy>::ShiftData(index_type start, size_type oldLen, size_type newLen, size_type elemSize, size_t elemAlign) { if (oldLen == newLen) return; // Determine how many elements need to be shifted size_type num = mHdr->mLength - (start + oldLen); @@ -235,88 +238,86 @@ nsTArray_base<Alloc>::ShiftData(index_ty } else { // Maybe nothing needs to be shifted if (num == 0) return; // Perform shift (change units to bytes first) start *= elemSize; newLen *= elemSize; oldLen *= elemSize; - num *= elemSize; char *base = reinterpret_cast<char*>(mHdr + 1) + start; - memmove(base + newLen, base + oldLen, num); + Copy::MoveElements(base + newLen, base + oldLen, num, elemSize); } } -template<class Alloc> +template<class Alloc, class Copy> bool -nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count, +nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count, size_type elementSize, size_t elemAlign) { MOZ_ASSERT(index <= Length(), "Bogus insertion index"); size_type newLen = Length() + count; EnsureCapacity(newLen, elementSize); // Check for out of memory conditions if (Capacity() < newLen) return false; // Move the existing elements as needed. Note that this will // change our mLength, so no need to call IncrementLength. ShiftData(index, 0, count, elementSize, elemAlign); - + return true; } // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes // |nsTArray_base &array| in its constructor. When it's destructed, it ensures // that // // * array.mIsAutoArray has the same value as it did when we started, and // * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr, // array.mHdr points to array's auto buffer. -template<class Alloc> -nsTArray_base<Alloc>::IsAutoArrayRestorer::IsAutoArrayRestorer( - nsTArray_base<Alloc> &array, - size_t elemAlign) +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer( + nsTArray_base<Alloc, Copy> &array, + size_t elemAlign) : mArray(array), mElemAlign(elemAlign), mIsAuto(array.IsAutoArray()) { } -template<class Alloc> -nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() { +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() { // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr. if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) { // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts // that mHdr->mIsAutoArray is true, which surely isn't the case here. mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign); mArray.mHdr->mLength = 0; } else if (mArray.mHdr != mArray.EmptyHdr()) { mArray.mHdr->mIsAutoArray = mIsAuto; } } -template<class Alloc> +template<class Alloc, class Copy> template<class Allocator> typename Alloc::ResultTypeProxy -nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other, - size_type elemSize, - size_t elemAlign) { +nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other, + size_type elemSize, size_t elemAlign) { // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an // auto buffer. We need to point mHdr back to our auto buffer before we // return, otherwise we'll forget that we have an auto buffer at all! // IsAutoArrayRestorer takes care of this for us. IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign); - typename nsTArray_base<Allocator>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign); + typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign); // If neither array uses an auto buffer which is big enough to store the // other array's elements, then ensure that both arrays use malloc'ed storage // and swap their mHdr pointers. if ((!UsesAutoArrayBuffer() || Capacity() < other.Length()) && (!other.UsesAutoArrayBuffer() || other.Capacity() < Length())) { if (!EnsureNotUsingAutoArrayBuffer(elemSize) || @@ -326,17 +327,17 @@ nsTArray_base<Alloc>::SwapArrayElements( Header *temp = mHdr; mHdr = other.mHdr; other.mHdr = temp; return Alloc::SuccessResult(); } - // Swap the two arrays using memcpy, since at least one is using an auto + // Swap the two arrays by copying, since at least one is using an auto // buffer which is large enough to hold all of the other's elements. We'll // copy the shorter array into temporary storage. // // (We could do better than this in some circumstances. Suppose we're // swapping arrays X and Y. X has space for 2 elements in its auto buffer, // but currently has length 4, so it's using malloc'ed storage. Y has length // 2. When we swap X and Y, we don't need to use a temporary buffer; we can // write Y straight into X's auto buffer, write X's malloc'ed buffer on top @@ -370,34 +371,34 @@ nsTArray_base<Alloc>::SwapArrayElements( // job for AutoTArray! (One of the two arrays we're swapping is using an // auto buffer, so we're likely not allocating a lot of space here. But one // could, in theory, allocate a huge AutoTArray on the heap.) nsAutoArrayBase<nsTArray_Impl<uint8_t, Alloc>, 64> temp; if (!Alloc::Successful(temp.EnsureCapacity(smallerLength, elemSize))) { return Alloc::FailureResult(); } - memcpy(temp.Elements(), smallerElements, smallerLength * elemSize); - memcpy(smallerElements, largerElements, largerLength * elemSize); - memcpy(largerElements, temp.Elements(), smallerLength * elemSize); + Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize); + Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize); + Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize); // Swap the arrays' lengths. NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) && (Length() == 0 || other.mHdr != EmptyHdr()), "Don't set sEmptyHdr's length."); size_type tempLength = Length(); mHdr->mLength = other.Length(); other.mHdr->mLength = tempLength; return Alloc::SuccessResult(); } -template<class Alloc> +template<class Alloc, class Copy> bool -nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) { +nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) { if (UsesAutoArrayBuffer()) { // If you call this on a 0-length array, we'll set that array's mHdr to // sEmptyHdr, in flagrant violation of the nsAutoTArray invariants. It's // up to you to set it back! (If you don't, the nsAutoTArray will forget // that it has an auto buffer.) if (Length() == 0) { mHdr = EmptyHdr(); @@ -405,15 +406,15 @@ nsTArray_base<Alloc>::EnsureNotUsingAuto } size_type size = sizeof(Header) + Length() * elemSize; Header* header = static_cast<Header*>(Alloc::Malloc(size)); if (!header) return false; - memcpy(header, mHdr, size); + Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); header->mCapacity = Length(); mHdr = header; } - + return true; }
--- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -17,16 +17,21 @@ #include "nsCycleCollectionNoteChild.h" #include "nsAlgorithm.h" #include "nscore.h" #include "nsQuickSort.h" #include "nsDebug.h" #include "nsTraceRefcnt.h" #include NEW_H +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + // // nsTArray is a resizable array class, like std::vector. // // Unlike std::vector, which follows C++'s construction/destruction rules, // nsTArray assumes that your "T" can be memmoved()'ed safely. // // The public classes defined in this header are // @@ -51,16 +56,19 @@ // The template parameter (i.e., T in nsTArray<T>) specifies the type of the // elements and has the following requirements: // // T MUST be safely memmove()'able. // T MUST define a copy-constructor. // T MAY define operator< for sorting. // T MAY define operator== for searching. // +// (Note that the memmove requirement may be relaxed for certain types - see +// nsTArray_CopyElements below.) +// // For methods taking a Comparator instance, the Comparator must be a class // defining the following methods: // // class Comparator { // public: // /** @return True if the elements are equals; false otherwise. */ // bool Equals(const elem_type& a, const Item& b) const; // @@ -337,23 +345,23 @@ struct nsTArray_SafeElementAtHelper<nsRe { }; // // This class serves as a base class for nsTArray. It shouldn't be used // directly. It holds common implementation code that does not depend on the // element type of the nsTArray. // -template<class Alloc> +template<class Alloc, class Copy> class nsTArray_base { // Allow swapping elements with |nsTArray_base|s created using a // different allocator. This is kosher because all allocators use // the same free(). - template<class Allocator> + template<class Allocator, class Copier> friend class nsTArray_base; protected: typedef nsTArrayHeader Header; public: typedef uint32_t size_type; typedef uint32_t index_type; @@ -391,17 +399,17 @@ protected: // @param elemSize The size of an array element. // @return False if insufficient memory is available; true otherwise. typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize); // Resize the storage to the minimum required amount. // @param elemSize The size of an array element. // @param elemAlign The alignment in bytes of an array element. void ShrinkCapacity(size_type elemSize, size_t elemAlign); - + // This method may be called to resize a "gap" in the array by shifting // elements around. It updates mLength appropriately. If the resulting // array has zero elements, then the array's memory is free'd. // @param start The starting index of the gap. // @param oldLen The current length of the gap. // @param newLen The desired length of the gap. // @param elemSize The size of an array element. // @param elemAlign The alignment in bytes of an array element. @@ -430,28 +438,28 @@ protected: // @param elementSize the size of an array element. // @param elemAlign the alignment in bytes of an array element. bool InsertSlotsAt(index_type index, size_type count, size_type elementSize, size_t elemAlign); protected: template<class Allocator> typename Alloc::ResultTypeProxy - SwapArrayElements(nsTArray_base<Allocator>& other, + SwapArrayElements(nsTArray_base<Allocator, Copy>& other, size_type elemSize, size_t elemAlign); // This is an RAII class used in SwapArrayElements. class IsAutoArrayRestorer { public: - IsAutoArrayRestorer(nsTArray_base<Alloc> &array, size_t elemAlign); + IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign); ~IsAutoArrayRestorer(); private: - nsTArray_base<Alloc> &mArray; + nsTArray_base<Alloc, Copy> &mArray; size_t mElemAlign; bool mIsAuto; }; // Helper function for SwapArrayElements. Ensures that if the array // is an nsAutoTArray that it doesn't use the built-in buffer. bool EnsureNotUsingAutoArrayBuffer(size_type elemSize); @@ -468,30 +476,30 @@ protected: const Header* GetAutoArrayBuffer(size_t elemAlign) const { MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this"); return GetAutoArrayBufferUnsafe(elemAlign); } // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't // assert that we are an nsAutoTArray. Header* GetAutoArrayBufferUnsafe(size_t elemAlign) { - return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)-> + return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)-> GetAutoArrayBufferUnsafe(elemAlign)); } const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const; // Returns true if this is an nsAutoTArray and it currently uses the // built-in buffer to store its elements. bool UsesAutoArrayBuffer() const; // The array's elements (prefixed with a Header). This pointer is never // null. If the array is empty, then this will point to sEmptyHdr. Header *mHdr; - Header* Hdr() const { + Header* Hdr() const { return mHdr; } Header** PtrToHdr() { return &mHdr; } static Header* EmptyHdr() { @@ -559,38 +567,172 @@ struct AssignRangeAlgorithm<true, true> template<class Item, class ElemType, class IndexType, class SizeType> static void implementation(ElemType* elements, IndexType start, SizeType count, const Item *values) { memcpy(elements + start, values, count * sizeof(ElemType)); } }; // +// Normally elements are copied with memcpy and memmove, but for some element +// types that is problematic. The nsTArray_CopyElements template class can be +// specialized to ensure that copying calls constructors and destructors +// instead, as is done below for JS::Heap<E> elements. +// + +// +// A class that defines how to copy elements using memcpy/memmove. +// +struct nsTArray_CopyWithMemutils +{ + const static bool allowRealloc = true; + + static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) { + memcpy(dest, src, count * elemSize); + } + + static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) { + memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize); + } + + static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) { + memmove(dest, src, count * elemSize); + } +}; + +// +// A template class that defines how to copy elements calling their constructors +// and destructors appropriately. +// +template <class ElemType> +struct nsTArray_CopyWithConstructors +{ + typedef nsTArrayElementTraits<ElemType> traits; + + const static bool allowRealloc = false; + + static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) { + ElemType* destElem = static_cast<ElemType*>(dest); + ElemType* srcElem = static_cast<ElemType*>(src); + ElemType* destElemEnd = destElem + count; +#ifdef DEBUG + ElemType* srcElemEnd = srcElem + count; + MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd); +#endif + while (destElem != destElemEnd) { + traits::Construct(destElem, *srcElem); + traits::Destruct(srcElem); + ++destElem; + ++srcElem; + } + } + + static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) { + nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest); + nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src); + *destHeader = *srcHeader; + CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader), + static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader), + count, elemSize); + } + + static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) { + ElemType* destElem = static_cast<ElemType*>(dest); + ElemType* srcElem = static_cast<ElemType*>(src); + ElemType* destElemEnd = destElem + count; + ElemType* srcElemEnd = srcElem + count; + if (destElem == srcElem) { + return; // In practice, we don't do this. + } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) { + while (destElemEnd != destElem) { + --destElemEnd; + --srcElemEnd; + traits::Construct(destElemEnd, *srcElemEnd); + traits::Destruct(srcElem); + } + } else { + CopyElements(dest, src, count, elemSize); + } + } +}; + +// +// The default behaviour is to use memcpy/memmove for everything. +// +template <class E> +struct nsTArray_CopyElements : public nsTArray_CopyWithMemutils {}; + +// +// JS::Heap<E> elements require constructors/destructors to be called and so is +// specialized here. +// +template <class E> +struct nsTArray_CopyElements<JS::Heap<E> > : public nsTArray_CopyWithConstructors<E> {}; + +// +// Base class for nsTArray_Impl that is templated on element type and derived +// nsTArray_Impl class, to allow extra conversions to be added for specific +// types. +// +template <class E, class Derived> +struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {}; + +// +// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E> +// elements. +// +// These conversions are safe because JS::Heap<E> and E share the same +// representation, and since the result of the conversions are const references +// we won't miss any barriers. +// +// The static_cast is necessary to obtain the correct address for the derived +// class since we are a base class used in multiple inheritance. +// +template <class E, class Derived> +struct nsTArray_TypedBase<JS::Heap<E>, Derived> + : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived> +{ + operator const nsTArray<E>& () { + MOZ_STATIC_ASSERT(sizeof(E) == sizeof(JS::Heap<E>), + "JS::Heap<E> must be binary compatible with E."); + Derived* self = static_cast<Derived*>(this); + return *reinterpret_cast<nsTArray<E> *>(self); + } + + operator const FallibleTArray<E>& () { + Derived* self = static_cast<Derived*>(this); + return *reinterpret_cast<FallibleTArray<E> *>(self); + } +}; + + +// // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray, // nsAutoTArray, and AutoFallibleTArray. // // The only situation in which you might need to use nsTArray_Impl in your code // is if you're writing code which mutates a TArray which may or may not be // infallible. // // Code which merely reads from a TArray which may or may not be infallible can // simply cast the TArray to |const nsTArray&|; both fallible and infallible // TArrays can be cast to |const nsTArray&|. // template<class E, class Alloc> -class nsTArray_Impl : public nsTArray_base<Alloc>, - public nsTArray_SafeElementAtHelper<E, nsTArray_Impl<E, Alloc> > +class nsTArray_Impl : public nsTArray_base<Alloc, nsTArray_CopyElements<E> >, + public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> > { public: - typedef nsTArray_base<Alloc> base_type; - typedef typename base_type::size_type size_type; - typedef typename base_type::index_type index_type; - typedef E elem_type; - typedef nsTArray_Impl<E, Alloc> self_type; - typedef nsTArrayElementTraits<E> elem_traits; + typedef nsTArray_CopyElements<E> copy_type; + typedef nsTArray_base<Alloc, copy_type> base_type; + typedef typename base_type::size_type size_type; + typedef typename base_type::index_type index_type; + typedef E elem_type; + typedef nsTArray_Impl<E, Alloc> self_type; + typedef nsTArrayElementTraits<E> elem_traits; typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type; using safeelementat_helper_type::SafeElementAt; using base_type::EmptyHdr; // A special value that is used to indicate an invalid or unknown index // into the array. enum { @@ -711,17 +853,17 @@ public: } // This method provides direct, readonly access to the array elements. // @return A pointer to the first element of the array. If the array is // empty, then this pointer must not be dereferenced. const elem_type* Elements() const { return reinterpret_cast<const elem_type *>(Hdr() + 1); } - + // This method provides direct access to the i'th element of the array. // The given index must be within the array bounds. // @param i The index of an element in the array. // @return A reference to the i'th element of the array. elem_type& ElementAt(index_type i) { MOZ_ASSERT(i < Length(), "invalid array index"); return Elements()[i]; } @@ -1078,28 +1220,28 @@ public: // Append a new element without copy-constructing. This is useful to avoid // temporaries. // @return A pointer to the newly appended element, or null on OOM. elem_type *AppendElement() { return AppendElements(1); } - // Move all elements from another array to the end of this array without + // Move all elements from another array to the end of this array without // calling copy constructors or destructors. // @return A pointer to the newly appended elements, or null on OOM. template<class Item, class Allocator> elem_type *MoveElementsFrom(nsTArray_Impl<Item, Allocator>& array) { MOZ_ASSERT(&array != this, "argument must be different array"); index_type len = Length(); index_type otherLen = array.Length(); if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type)))) return nullptr; - memcpy(Elements() + len, array.Elements(), otherLen * sizeof(elem_type)); - this->IncrementLength(otherLen); + copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type)); + this->IncrementLength(otherLen); array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); return Elements() + len; } // This method removes a range of elements from this array. // @param start The starting index of the elements to remove. // @param count The number of elements to remove. void RemoveElementsAt(index_type start, size_type count) { @@ -1196,17 +1338,17 @@ public: // @return True if the operation succeeded; false otherwise. // See also TruncateLength if the new length is guaranteed to be // smaller than the old. bool SetLength(size_type newLen) { size_type oldLen = Length(); if (newLen > oldLen) { return InsertElementsAt(oldLen, newLen - oldLen) != nullptr; } - + TruncateLength(newLen); return true; } // This method modifies the length of the array, but may only be // called when the new length is shorter than the old. It can // therefore be called when elem_type has no default constructor, // unlike SetLength. It removes elements from the array (see also @@ -1278,17 +1420,17 @@ typename Alloc::ResultType EnsureLengthA // This method may be called to minimize the memory used by this array. void Compact() { ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type)); } // // Sorting // - + // This function is meant to be used with the NS_QuickSort function. It // maps the callback API expected by NS_QuickSort to the Comparator API // used by nsTArray_Impl. See nsTArray_Impl::Sort. template<class Comparator> static int Compare(const void* e1, const void* e2, void *data) { const Comparator* c = reinterpret_cast<const Comparator*>(data); const elem_type* a = static_cast<const elem_type*>(e1); const elem_type* b = static_cast<const elem_type*>(e2); @@ -1391,18 +1533,18 @@ protected: elem_type *iter = Elements() + start, *end = iter + count; for (; iter != end; ++iter) { elem_traits::Destruct(iter); } } // This method invokes elem_type's copy-constructor on a range of elements. // @param start The index of the first element to construct. - // @param count The number of elements to construct. - // @param values The array of elements to copy. + // @param count The number of elements to construct. + // @param values The array of elements to copy. template<class Item> void AssignRange(index_type start, size_type count, const Item *values) { AssignRangeAlgorithm<mozilla::IsPod<Item>::value, mozilla::IsSame<Item, elem_type>::value> ::implementation(Elements(), start, count, values); } @@ -1528,17 +1670,17 @@ protected: nsAutoArrayBase(const TArrayBase &aOther) { Init(); AppendElements(aOther); } private: // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer // to mAutoBuf. - template<class Allocator> + template<class Allocator, class Copier> friend class nsTArray_base; void Init() { MOZ_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8, "can't handle alignments greater than 8, " "see nsTArray_base::UsesAutoArrayBuffer()"); // Temporary work around for VS2012 RC compiler crash Header** phdr = base_type::PtrToHdr();