From e89acd7c708ba34a108cef65f45d88742f5c3c8f Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 10 Feb 2016 22:35:50 -0800 Subject: [PATCH 001/187] Bug 1150737: Make executeSoon work properly on workers, enable tests. r=bent --- dom/indexedDB/test/helpers.js | 4 +++- dom/indexedDB/test/unit/test_transaction_abort.js | 10 ---------- dom/indexedDB/test/unit/test_transaction_lifetimes.js | 10 ---------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js index aea4a26daf0..3a3af6bf214 100644 --- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -379,7 +379,9 @@ function workerScript() { }; self.executeSoon = function(_fun_) { - setTimeout(_fun_, 0); + var channel = new MessageChannel(); + channel.port1.postMessage(""); + channel.port2.onmessage = function(event) { _fun_(); }; }; self.finishTest = function() { diff --git a/dom/indexedDB/test/unit/test_transaction_abort.js b/dom/indexedDB/test/unit/test_transaction_abort.js index 4c961d848ad..0f051f968e8 100644 --- a/dom/indexedDB/test/unit/test_transaction_abort.js +++ b/dom/indexedDB/test/unit/test_transaction_abort.js @@ -3,14 +3,6 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -var disableWorkerTest = - "This test requires a precise 'executeSoon()' to complete reliably. On a " + - "worker 'executeSoon()' currently uses 'setTimeout()', and that switches " + - "to the timer thread and back before completing. That gives the IndexedDB " + - "transaction thread time to fully complete transactions and to place " + - "'complete' events in the worker thread's queue before the timer event, " + - "causing ordering problems in the spot marked 'Worker Fails Here' below."; - var testGenerator = testSteps(); var abortFired = false; @@ -338,8 +330,6 @@ function testSteps() // During COMMITTING transaction = db.transaction("foo", "readwrite"); transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) { - // Worker Fails Here! Due to the thread switching of 'executeSoon()' the - // transaction can commit and fire a 'complete' event before we continue. continueToNextStep(); }; yield undefined; diff --git a/dom/indexedDB/test/unit/test_transaction_lifetimes.js b/dom/indexedDB/test/unit/test_transaction_lifetimes.js index b8bd0020952..e42f7a2188c 100644 --- a/dom/indexedDB/test/unit/test_transaction_lifetimes.js +++ b/dom/indexedDB/test/unit/test_transaction_lifetimes.js @@ -3,14 +3,6 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -var disableWorkerTest = - "This test requires a precise 'executeSoon()' to complete reliably. On a " + - "worker 'executeSoon()' currently uses 'setTimeout()', and that switches " + - "to the timer thread and back before completing. That gives the IndexedDB " + - "transaction thread time to fully complete transactions and to place " + - "'complete' events in the worker thread's queue before the timer event, " + - "causing ordering problems in the spot marked 'Worker Fails Here' below."; - var testGenerator = testSteps(); function testSteps() @@ -49,8 +41,6 @@ function testSteps() let wasAbleToGrabObjectStoreOutsideOfCallback = false; let wasAbleToGrabIndexOutsideOfCallback = false; executeSoon(function() { - // Worker Fails Here! Due to the thread switching of 'executeSoon()' the - // transaction can commit and fire a 'complete' event before we continue. ok(!requestComplete, "Ordering is correct."); wasAbleToGrabObjectStoreOutsideOfCallback = !!transaction.objectStore("foo"); wasAbleToGrabIndexOutsideOfCallback = From 6b26e7ff2bf561abe4dbce32860a465d2d65cc41 Mon Sep 17 00:00:00 2001 From: Aidin Gharibnavaz Date: Wed, 10 Feb 2016 08:23:00 +0100 Subject: [PATCH 002/187] Bug 1164581 - Adding an overload for NS_ProxyRelease that accepts already_AddRefed, and removing all the others. r=bobbyholley --- dom/archivereader/ArchiveEvent.cpp | 12 +- dom/base/Console.cpp | 4 +- dom/base/WebSocket.cpp | 4 +- dom/base/nsScriptLoader.cpp | 13 +- dom/cache/ManagerId.cpp | 5 +- dom/devicestorage/DeviceStorageStatics.cpp | 2 +- dom/devicestorage/nsDeviceStorage.cpp | 3 +- .../recognition/SpeechStreamListener.cpp | 5 +- dom/workers/WorkerPrivate.cpp | 17 +-- extensions/gio/nsGIOProtocolHandler.cpp | 11 +- .../spellcheck/src/mozPersonalDictionary.cpp | 21 +-- image/Decoder.cpp | 11 +- image/decoders/icon/mac/nsIconChannelCocoa.mm | 7 +- image/decoders/icon/win/nsIconChannel.cpp | 7 +- .../peerconnection/PeerConnectionMedia.cpp | 5 +- modules/libjar/nsJARChannel.cpp | 2 +- netwerk/base/TLSServerSocket.cpp | 8 +- netwerk/base/nsBaseChannel.cpp | 2 +- netwerk/base/nsProtocolProxyService.cpp | 19 +-- netwerk/base/nsServerSocket.cpp | 10 +- netwerk/base/nsStreamListenerTee.cpp | 7 +- netwerk/base/nsTransportUtils.cpp | 2 +- netwerk/base/nsUDPSocket.cpp | 6 +- netwerk/cache/nsCacheService.cpp | 2 +- netwerk/cache2/CacheIndex.h | 11 +- netwerk/cache2/CacheStorageService.h | 5 +- netwerk/ipc/RemoteOpenFileChild.cpp | 33 +---- netwerk/protocol/file/nsFileChannel.cpp | 4 +- netwerk/protocol/http/HttpBaseChannel.cpp | 2 +- .../rtsp/controller/RtspControllerParent.cpp | 7 +- .../websocket/BaseWebSocketChannel.cpp | 7 +- .../protocol/websocket/WebSocketChannel.cpp | 10 +- .../protocol/wyciwyg/WyciwygChannelChild.cpp | 7 +- netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp | 15 +-- netwerk/sctp/datachannel/DataChannel.cpp | 4 +- security/manager/ssl/nsNSSCallbacks.cpp | 3 +- storage/StorageBaseStatementInternal.cpp | 15 +-- storage/mozStorageAsyncStatement.cpp | 6 +- storage/mozStorageConnection.cpp | 26 +--- storage/mozStorageService.cpp | 19 +-- storage/mozStorageStatementData.h | 3 +- .../osfile/NativeOSFileInternals.cpp | 12 +- .../components/places/AsyncFaviconHelpers.cpp | 4 +- toolkit/components/places/Database.cpp | 8 +- toolkit/components/places/Helpers.h | 2 +- .../nsUrlClassifierDBService.cpp | 5 +- widget/gonk/nsScreenManagerGonk.cpp | 2 +- xpcom/base/nsConsoleService.cpp | 2 +- xpcom/base/nsInterfaceRequestorAgg.cpp | 12 +- xpcom/glue/nsProxyRelease.cpp | 65 --------- xpcom/glue/nsProxyRelease.h | 127 ++++++++---------- xpcom/glue/objs.mozbuild | 1 - xpcom/libxpcomrt/moz.build | 1 - 53 files changed, 152 insertions(+), 451 deletions(-) delete mode 100644 xpcom/glue/nsProxyRelease.cpp diff --git a/dom/archivereader/ArchiveEvent.cpp b/dom/archivereader/ArchiveEvent.cpp index afcc665a0da..164a03eb2d4 100644 --- a/dom/archivereader/ArchiveEvent.cpp +++ b/dom/archivereader/ArchiveEvent.cpp @@ -49,17 +49,7 @@ ArchiveReaderEvent::ArchiveReaderEvent(ArchiveReader* aArchiveReader) ArchiveReaderEvent::~ArchiveReaderEvent() { if (!NS_IsMainThread()) { - nsIMIMEService* mimeService; - mMimeService.forget(&mimeService); - - if (mimeService) { - nsCOMPtr mainThread = do_GetMainThread(); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread! Leaking!"); - - if (mainThread) { - NS_ProxyRelease(mainThread, mimeService); - } - } + NS_ReleaseOnMainThread(mMimeService.forget()); } MOZ_COUNT_DTOR(ArchiveReaderEvent); diff --git a/dom/base/Console.cpp b/dom/base/Console.cpp index 6bca69f2214..d0a25ea89c0 100644 --- a/dom/base/Console.cpp +++ b/dom/base/Console.cpp @@ -778,11 +778,11 @@ Console::~Console() if (!NS_IsMainThread()) { if (mStorage) { - NS_ReleaseOnMainThread(mStorage); + NS_ReleaseOnMainThread(mStorage.forget()); } if (mSandbox) { - NS_ReleaseOnMainThread(mSandbox); + NS_ReleaseOnMainThread(mSandbox.forget()); } } diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 862653bb743..1ae6ebf2f1b 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -629,8 +629,8 @@ WebSocketImpl::Disconnect() // until the end of the method. RefPtr kungfuDeathGrip = this; - NS_ReleaseOnMainThread(mChannel); - NS_ReleaseOnMainThread(static_cast(mService.forget().take())); + NS_ReleaseOnMainThread(mChannel.forget()); + NS_ReleaseOnMainThread(mService.forget()); mWebSocket->DontKeepAliveAnyMore(); mWebSocket->mImpl = nullptr; diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 29d7b7c9ce4..7f2cf1df56f 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -782,17 +782,8 @@ nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest) NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable() { if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (mainThread) { - NS_ProxyRelease(mainThread, mRequest); - NS_ProxyRelease(mainThread, mLoader); - } else { - MOZ_ASSERT(false, "We really shouldn't leak!"); - // Better to leak than crash. - Unused << mRequest.forget(); - Unused << mLoader.forget(); - } + NS_ReleaseOnMainThread(mRequest.forget()); + NS_ReleaseOnMainThread(mLoader.forget()); } } diff --git a/dom/cache/ManagerId.cpp b/dom/cache/ManagerId.cpp index 085c5f591fd..9887f28f004 100644 --- a/dom/cache/ManagerId.cpp +++ b/dom/cache/ManagerId.cpp @@ -62,10 +62,7 @@ ManagerId::~ManagerId() // The PBackground worker thread shouldn't be running after the main thread // is stopped. So main thread is guaranteed to exist here. - nsCOMPtr mainThread = do_GetMainThread(); - MOZ_ASSERT(mainThread); - - NS_ProxyRelease(mainThread, mPrincipal.forget().take()); + NS_ReleaseOnMainThread(mPrincipal.forget()); } } // namespace cache diff --git a/dom/devicestorage/DeviceStorageStatics.cpp b/dom/devicestorage/DeviceStorageStatics.cpp index 6db92455366..dbb9e8e65bd 100644 --- a/dom/devicestorage/DeviceStorageStatics.cpp +++ b/dom/devicestorage/DeviceStorageStatics.cpp @@ -860,7 +860,7 @@ DeviceStorageStatics::ListenerWrapper::ListenerWrapper(nsDOMDeviceStorage* aList DeviceStorageStatics::ListenerWrapper::~ListenerWrapper() { // Even weak pointers are not thread safe - NS_ProxyRelease(mOwningThread, mListener); + NS_ProxyRelease(mOwningThread, mListener.forget()); } bool diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index 95a4ed556f1..ec3a6a0e71e 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -3716,8 +3716,7 @@ DeviceStorageRequestManager::~DeviceStorageRequestManager() while (i > 0) { --i; DS_LOG_ERROR("terminate %u", mPending[i].mId); - NS_ProxyRelease(mOwningThread, - NS_ISUPPORTS_CAST(EventTarget*, mPending[i].mRequest.forget().take())); + NS_ProxyRelease(mOwningThread, mPending[i].mRequest.forget()); } } } diff --git a/dom/media/webspeech/recognition/SpeechStreamListener.cpp b/dom/media/webspeech/recognition/SpeechStreamListener.cpp index 6e7b76b1f69..773ec27a2c4 100644 --- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp +++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp @@ -22,10 +22,7 @@ SpeechStreamListener::~SpeechStreamListener() nsCOMPtr mainThread; NS_GetMainThread(getter_AddRefs(mainThread)); - SpeechRecognition* forgottenRecognition = nullptr; - mRecognition.swap(forgottenRecognition); - NS_ProxyRelease(mainThread, - static_cast(forgottenRecognition)); + NS_ProxyRelease(mainThread, mRecognition.forget()); } void diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index e2c0b96a651..4e4d4d8e802 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3626,17 +3626,8 @@ WorkerDebugger::~WorkerDebugger() MOZ_ASSERT(!mWorkerPrivate); if (!NS_IsMainThread()) { - nsCOMPtr mainThread; - if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) { - NS_WARNING("Failed to proxy release of listeners, leaking instead!"); - } - for (size_t index = 0; index < mListeners.Length(); ++index) { - nsIWorkerDebuggerListener* listener = nullptr; - mListeners[index].forget(&listener); - if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) { - NS_WARNING("Failed to proxy release of listener, leaking instead!"); - } + NS_ReleaseOnMainThread(mListeners[index].forget()); } } } @@ -4158,11 +4149,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow, } if (parentStatus > Running) { - nsCOMPtr mainThread; - if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) || - NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) { - NS_WARNING("Failed to proxy release of channel, leaking instead!"); - } + NS_ReleaseOnMainThread(loadInfo.mChannel.forget()); return NS_ERROR_FAILURE; } diff --git a/extensions/gio/nsGIOProtocolHandler.cpp b/extensions/gio/nsGIOProtocolHandler.cpp index 71db2d5da3a..584fc2ef785 100644 --- a/extensions/gio/nsGIOProtocolHandler.cpp +++ b/extensions/gio/nsGIOProtocolHandler.cpp @@ -615,17 +615,10 @@ nsGIOInputStream::Close() mDirListPtr = nullptr; } - if (mChannel) - { - nsresult rv = NS_OK; + if (mChannel) { + NS_ReleaseOnMainThread(dont_AddRef(mChannel)); - nsCOMPtr thread = do_GetMainThread(); - if (thread) - rv = NS_ProxyRelease(thread, mChannel); - - NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference"); mChannel = nullptr; - (void) rv; } mSpec.Truncate(); // free memory diff --git a/extensions/spellcheck/src/mozPersonalDictionary.cpp b/extensions/spellcheck/src/mozPersonalDictionary.cpp index 0eab1c1235e..6cd870e31d1 100644 --- a/extensions/spellcheck/src/mozPersonalDictionary.cpp +++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp @@ -65,16 +65,7 @@ public: mDict->SyncLoad(); // Release the dictionary on the main thread - mozPersonalDictionary *dict; - mDict.forget(&dict); - - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, static_cast(dict)); - } else { - // It's better to leak the dictionary than to release it on a wrong thread - NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary."); - } + NS_ReleaseOnMainThread(mDict.forget()); return NS_OK; } @@ -145,16 +136,8 @@ public: } // Release the dictionary on the main thread. - mozPersonalDictionary *dict; - mDict.forget(&dict); + NS_ReleaseOnMainThread(mDict.forget()); - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, static_cast(dict)); - } else { - // It's better to leak the dictionary than to release it on a wrong thread. - NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary."); - } return NS_OK; } diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 9f177998b46..7ea89f4990c 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -55,16 +55,7 @@ Decoder::~Decoder() if (mImage && !NS_IsMainThread()) { // Dispatch mImage to main thread to prevent it from being destructed by the // decode thread. - nsCOMPtr mainThread = do_GetMainThread(); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); - if (mainThread) { - // Handle ambiguous nsISupports inheritance. - RasterImage* rawImg = nullptr; - mImage.swap(rawImg); - DebugOnly rv = - NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg)); - MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread"); - } + NS_ReleaseOnMainThread(mImage.forget()); } } diff --git a/image/decoders/icon/mac/nsIconChannelCocoa.mm b/image/decoders/icon/mac/nsIconChannelCocoa.mm index e6208d7be75..69968a895f8 100644 --- a/image/decoders/icon/mac/nsIconChannelCocoa.mm +++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm @@ -38,12 +38,7 @@ nsIconChannel::nsIconChannel() nsIconChannel::~nsIconChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo* forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp index 9fa2a7750d8..9ddcbbc4880 100644 --- a/image/decoders/icon/win/nsIconChannel.cpp +++ b/image/decoders/icon/win/nsIconChannel.cpp @@ -76,12 +76,7 @@ nsIconChannel::nsIconChannel() nsIconChannel::~nsIconChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo* forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index f9643ad7713..fd003c4538d 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -1129,10 +1129,7 @@ void PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp) { int index_inner = GetTransportFlowIndex(aIndex, aRtcp); - TransportFlow* flow = mTransportFlows[index_inner].forget().take(); - if (flow) { - NS_ProxyRelease(GetSTSThread(), flow); - } + NS_ProxyRelease(GetSTSThread(), mTransportFlows[index_inner].forget()); } void diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index f49be0f6449..9a563f16b89 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -214,7 +214,7 @@ nsJARChannel::nsJARChannel() nsJARChannel::~nsJARChannel() { - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); // release owning reference to the jar handler nsJARProtocolHandler *handler = gJarHandler; diff --git a/netwerk/base/TLSServerSocket.cpp b/netwerk/base/TLSServerSocket.cpp index 5d9046f901a..85f757b29af 100644 --- a/netwerk/base/TLSServerSocket.cpp +++ b/netwerk/base/TLSServerSocket.cpp @@ -329,16 +329,14 @@ TLSServerConnectionInfo::~TLSServerConnectionInfo() return; } - nsITLSServerSecurityObserver* observer; + RefPtr observer; { MutexAutoLock lock(mLock); - mSecurityObserver.forget(&observer); + observer = mSecurityObserver.forget(); } if (observer) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - NS_ProxyRelease(mainThread, observer); + NS_ReleaseOnMainThread(observer.forget()); } } diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index c07328b37c0..be7154299fe 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -66,7 +66,7 @@ nsBaseChannel::nsBaseChannel() nsBaseChannel::~nsBaseChannel() { - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } nsresult diff --git a/netwerk/base/nsProtocolProxyService.cpp b/netwerk/base/nsProtocolProxyService.cpp index 2a1d9a0fd92..ee21b947109 100644 --- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -128,31 +128,20 @@ private: // main thread to delete safely, but if this request had its // callbacks called normally they will all be null and this is a nop - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (mChannel) { - nsIChannel *forgettable; - mChannel.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mChannel.forget()); } if (mCallback) { - nsIProtocolProxyCallback *forgettable; - mCallback.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mCallback.forget()); } if (mProxyInfo) { - nsIProxyInfo *forgettable; - mProxyInfo.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mProxyInfo.forget()); } if (mXPComPPS) { - nsIProtocolProxyService *forgettable; - mXPComPPS.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mXPComPPS.forget()); } } } diff --git a/netwerk/base/nsServerSocket.cpp b/netwerk/base/nsServerSocket.cpp index d9ca570bf49..6d50e264682 100644 --- a/netwerk/base/nsServerSocket.cpp +++ b/netwerk/base/nsServerSocket.cpp @@ -233,15 +233,17 @@ nsServerSocket::OnSocketDetached(PRFileDesc *fd) mListener->OnStopListening(this, mCondition); // need to atomically clear mListener. see our Close() method. - nsIServerSocketListener *listener = nullptr; + RefPtr listener = nullptr; { MutexAutoLock lock(mLock); - mListener.swap(listener); + listener = mListener.forget(); } + // XXX we need to proxy the release to the listener's target thread to work // around bug 337492. - if (listener) - NS_ProxyRelease(mListenerTarget, listener); + if (listener) { + NS_ProxyRelease(mListenerTarget, listener.forget()); + } } } diff --git a/netwerk/base/nsStreamListenerTee.cpp b/netwerk/base/nsStreamListenerTee.cpp index 455974d7586..b13e9401851 100644 --- a/netwerk/base/nsStreamListenerTee.cpp +++ b/netwerk/base/nsStreamListenerTee.cpp @@ -39,12 +39,7 @@ nsStreamListenerTee::OnStopRequest(nsIRequest *request, // release sink on the same thread where the data was written (bug 716293) if (mEventTarget) { - nsIOutputStream *sink = nullptr; - mSink.swap(sink); - if (NS_FAILED(NS_ProxyRelease(mEventTarget, sink))) { - NS_WARNING("Releasing sink on the current thread!"); - NS_RELEASE(sink); - } + NS_ProxyRelease(mEventTarget, mSink.forget()); } else { mSink = 0; diff --git a/netwerk/base/nsTransportUtils.cpp b/netwerk/base/nsTransportUtils.cpp index 835c7c0bbbe..7040914497b 100644 --- a/netwerk/base/nsTransportUtils.cpp +++ b/netwerk/base/nsTransportUtils.cpp @@ -37,7 +37,7 @@ private: { // our reference to mSink could be the last, so be sure to release // it on the target thread. otherwise, we could get into trouble. - NS_ProxyRelease(mTarget, mSink); + NS_ProxyRelease(mTarget, dont_AddRef(mSink)); } public: diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp index 108273410cd..72c062bde37 100644 --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -523,15 +523,15 @@ nsUDPSocket::OnSocketDetached(PRFileDesc *fd) if (mListener) { // need to atomically clear mListener. see our Close() method. - nsCOMPtr listener; + RefPtr listener = nullptr; { MutexAutoLock lock(mLock); - mListener.swap(listener); + listener = mListener.forget(); } if (listener) { listener->OnStopListening(this, mCondition); - NS_ProxyRelease(mListenerTarget, listener); + NS_ProxyRelease(mListenerTarget, listener.forget()); } } } diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index 7cbb351461f..419497c6999 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -2691,7 +2691,7 @@ nsCacheService::ReleaseObject_Locked(nsISupports * obj, if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) { gService->mDoomedObjects.AppendElement(obj); } else { - NS_ProxyRelease(target, obj); + NS_ProxyRelease(target, dont_AddRef(obj)); } } diff --git a/netwerk/cache2/CacheIndex.h b/netwerk/cache2/CacheIndex.h index 2a6ef9ae7ac..015ccef2eb9 100644 --- a/netwerk/cache2/CacheIndex.h +++ b/netwerk/cache2/CacheIndex.h @@ -1046,16 +1046,7 @@ private: : mObserver(aWeakObserver) { } virtual ~DiskConsumptionObserver() { if (mObserver && !NS_IsMainThread()) { - nsIWeakReference *obs; - mObserver.forget(&obs); - - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, obs); - } else { - NS_WARNING("Cannot get main thread, leaking weak reference to " - "CacheStorageConsumptionObserver."); - } + NS_ReleaseOnMainThread(mObserver.forget()); } } diff --git a/netwerk/cache2/CacheStorageService.h b/netwerk/cache2/CacheStorageService.h index 054dab9b67c..d8bbdd3c84e 100644 --- a/netwerk/cache2/CacheStorageService.h +++ b/netwerk/cache2/CacheStorageService.h @@ -383,10 +383,7 @@ private: template void ProxyRelease(nsCOMPtr &object, nsIThread* thread) { - T* release; - object.forget(&release); - - NS_ProxyRelease(thread, release); + NS_ProxyRelease(thread, object.forget()); } template diff --git a/netwerk/ipc/RemoteOpenFileChild.cpp b/netwerk/ipc/RemoteOpenFileChild.cpp index b66ba40dae6..86780366522 100644 --- a/netwerk/ipc/RemoteOpenFileChild.cpp +++ b/netwerk/ipc/RemoteOpenFileChild.cpp @@ -107,35 +107,10 @@ RemoteOpenFileChild::~RemoteOpenFileChild() NotifyListener(NS_ERROR_UNEXPECTED); } } else { - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mURI, true))); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mAppURI, true))); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mListener, - true))); - - TabChild* tabChild; - mTabChild.forget(&tabChild); - - if (tabChild) { - nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(tabChild, &TabChild::Release); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(runnable, - NS_DISPATCH_NORMAL))); - } - } else { - using mozilla::Unused; - - NS_WARNING("RemoteOpenFileChild released after thread shutdown, leaking " - "its members!"); - - Unused << mURI.forget(); - Unused << mAppURI.forget(); - Unused << mListener.forget(); - Unused << mTabChild.forget(); - } + NS_ReleaseOnMainThread(mURI.forget(), true); + NS_ReleaseOnMainThread(mAppURI.forget(), true); + NS_ReleaseOnMainThread(mListener.forget(), true); + NS_ReleaseOnMainThread(mTabChild.forget(), true); } if (mNSPRFileDesc) { diff --git a/netwerk/protocol/file/nsFileChannel.cpp b/netwerk/protocol/file/nsFileChannel.cpp index e11e878fc65..3f0a1246036 100644 --- a/netwerk/protocol/file/nsFileChannel.cpp +++ b/netwerk/protocol/file/nsFileChannel.cpp @@ -129,9 +129,7 @@ nsFileCopyEvent::DoCopy() // Release the callback on the target thread to avoid destroying stuff on // the wrong thread. - nsIRunnable *doomed = nullptr; - mCallback.swap(doomed); - NS_ProxyRelease(mCallbackTarget, doomed); + NS_ProxyRelease(mCallbackTarget, mCallback.forget()); } } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index ba071894a3c..4d76a31e345 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -127,7 +127,7 @@ HttpBaseChannel::~HttpBaseChannel() { LOG(("Destroying HttpBaseChannel @%x\n", this)); - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); // Make sure we don't leak CleanRedirectCacheChainIfNecessary(); diff --git a/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp index 8a7e9856ea7..fc9091a0d4e 100644 --- a/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp +++ b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp @@ -40,13 +40,8 @@ RtspControllerParent::Destroy() // RtspControllerParent is deleted. This ensures we only delete the // RtspControllerParent on the main thread. if (!NS_IsMainThread()) { - nsCOMPtr mainThread = do_GetMainThread(); - NS_ENSURE_TRUE_VOID(mainThread); RefPtr doomed(this); - if (NS_FAILED(NS_ProxyRelease(mainThread, - static_cast(doomed), true))) { - NS_WARNING("Failed to proxy release to main thread!"); - } + NS_ReleaseOnMainThread(doomed.forget(), true); } else { delete this; } diff --git a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp index 786e4ca2594..3d357461225 100644 --- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp +++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp @@ -360,11 +360,8 @@ BaseWebSocketChannel::ListenerAndContextContainer::~ListenerAndContextContainer( { MOZ_ASSERT(mListener); - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - NS_ProxyRelease(mainThread, mListener, false); - NS_ProxyRelease(mainThread, mContext, false); + NS_ReleaseOnMainThread(mListener.forget()); + NS_ReleaseOnMainThread(mContext.forget()); } } // namespace net diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 58eb68b5aad..841db157ab0 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -1216,14 +1216,14 @@ WebSocketChannel::~WebSocketChannel() while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront())) delete mCurrentOut; - NS_ReleaseOnMainThread(mURI); - NS_ReleaseOnMainThread(mOriginalURI); + NS_ReleaseOnMainThread(mURI.forget()); + NS_ReleaseOnMainThread(mOriginalURI.forget()); mListenerMT = nullptr; - NS_ReleaseOnMainThread(mLoadGroup); - NS_ReleaseOnMainThread(mLoadInfo); - NS_ReleaseOnMainThread(static_cast(mService.forget().take())); + NS_ReleaseOnMainThread(mLoadGroup.forget()); + NS_ReleaseOnMainThread(mLoadInfo.forget()); + NS_ReleaseOnMainThread(mService.forget()); } NS_IMETHODIMP diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp index f4f7c1a266e..5cb57358272 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp @@ -56,12 +56,7 @@ WyciwygChannelChild::~WyciwygChannelChild() { LOG(("Destroying WyciwygChannelChild @%x\n", this)); if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp index 9c9c571785d..4e15668756e 100644 --- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp +++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp @@ -40,13 +40,7 @@ public: ~nsWyciwygAsyncEvent() { - nsCOMPtr thread = do_GetMainThread(); - NS_WARN_IF_FALSE(thread, "Couldn't get the main thread!"); - if (thread) { - nsIWyciwygChannel *chan = static_cast(mChannel); - mozilla::Unused << mChannel.forget(); - NS_ProxyRelease(thread, chan); - } + NS_ReleaseOnMainThread(mChannel.forget()); } protected: RefPtr mChannel; @@ -109,12 +103,7 @@ nsWyciwygChannel::nsWyciwygChannel() nsWyciwygChannel::~nsWyciwygChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget(), false); } } diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index e6ac11e1035..5385c9e8321 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -196,7 +196,7 @@ DataChannelConnection::~DataChannelConnection() ASSERT_WEBRTC(NS_IsMainThread()); if (mTransportFlow) { ASSERT_WEBRTC(mSTS); - NS_ProxyRelease(mSTS, mTransportFlow); + NS_ProxyRelease(mSTS, mTransportFlow.forget()); } if (mInternalIOThread) { @@ -2417,7 +2417,7 @@ DataChannelConnection::ReadBlob(already_AddRefed aThis, // Bug 966602: Doesn't return an error to the caller via onerror. // We must release DataChannelConnection on MainThread to avoid issues (bug 876167) // aThis is now owned by the runnable; release it there - NS_ProxyRelease(mainThread, runnable); + NS_ProxyRelease(mainThread, runnable.forget()); return; } aBlob->Close(); diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 6fb8e3080aa..c71d58cec6c 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -610,8 +610,7 @@ nsHTTPListener::~nsHTTPListener() } if (mLoader) { - nsCOMPtr mainThread(do_GetMainThread()); - NS_ProxyRelease(mainThread, mLoader); + NS_ReleaseOnMainThread(mLoader.forget()); } } diff --git a/storage/StorageBaseStatementInternal.cpp b/storage/StorageBaseStatementInternal.cpp index 72ea84fb03e..f2c2bb52229 100644 --- a/storage/StorageBaseStatementInternal.cpp +++ b/storage/StorageBaseStatementInternal.cpp @@ -45,10 +45,12 @@ public: NS_IMETHOD Run() { if (mStatement->mAsyncStatement) { - (void)::sqlite3_finalize(mStatement->mAsyncStatement); + sqlite3_finalize(mStatement->mAsyncStatement); mStatement->mAsyncStatement = nullptr; } - (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement); + + nsCOMPtr targetThread(mConnection->threadOpenedOn); + NS_ProxyRelease(targetThread, mStatement.forget()); return NS_OK; } private: @@ -91,13 +93,8 @@ public: (void)::sqlite3_finalize(mAsyncStatement); mAsyncStatement = nullptr; - // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease - // template helpers. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)::NS_ProxyRelease( - rawConnection->threadOpenedOn, - NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection)); + nsCOMPtr target(mConnection->threadOpenedOn); + (void)::NS_ProxyRelease(target, mConnection.forget()); return NS_OK; } private: diff --git a/storage/mozStorageAsyncStatement.cpp b/storage/mozStorageAsyncStatement.cpp index 7b4029b55ca..2df196fb8ec 100644 --- a/storage/mozStorageAsyncStatement.cpp +++ b/storage/mozStorageAsyncStatement.cpp @@ -220,10 +220,8 @@ AsyncStatement::~AsyncStatement() if (!onCallingThread) { // NS_ProxyRelase only magic forgets for us if mDBConnection is an // nsCOMPtr. Which it is not; it's an nsRefPtr. - Connection *forgottenConn = nullptr; - mDBConnection.swap(forgottenConn); - (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn, - static_cast(forgottenConn)); + nsCOMPtr targetThread(mDBConnection->threadOpenedOn); + NS_ProxyRelease(targetThread, mDBConnection.forget()); } } diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index 5ac5b73f9f1..0506fb572bd 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -382,15 +382,8 @@ public: } ~AsyncCloseConnection() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); - // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, - NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); - (void)NS_ProxyRelease(thread, mCallbackEvent); + NS_ReleaseOnMainThread(mConnection.forget()); + NS_ReleaseOnMainThread(mCallbackEvent.forget()); } private: RefPtr mConnection; @@ -452,22 +445,13 @@ private: MOZ_ASSERT(NS_SUCCEEDED(rv)); // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); - - Connection *rawClone = nullptr; - mClone.swap(rawClone); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawClone)); + NS_ProxyRelease(thread, mConnection.forget()); + NS_ProxyRelease(thread, mClone.forget()); // Generally, the callback will be released by CallbackComplete. // However, if for some reason Run() is not executed, we still // need to ensure that it is released here. - mozIStorageCompletionCallback *rawCallback = nullptr; - mCallback.swap(rawCallback); - (void)NS_ProxyRelease(thread, rawCallback); + NS_ProxyRelease(thread, mCallback.forget()); } RefPtr mConnection; diff --git a/storage/mozStorageService.cpp b/storage/mozStorageService.cpp index 5370cf43072..b9dafd9ad6f 100644 --- a/storage/mozStorageService.cpp +++ b/storage/mozStorageService.cpp @@ -332,8 +332,7 @@ Service::unregisterConnection(Connection *aConnection) // Ensure the connection is released on its opening thread. Note, we // must use .forget().take() so that we can manually cast to an // unambiguous nsISupports type. - NS_ProxyRelease(thread, - static_cast(mConnections[i].forget().take())); + NS_ProxyRelease(thread, mConnections[i].forget()); mConnections.RemoveElementAt(i); return; @@ -733,23 +732,13 @@ private: ~AsyncInitDatabase() { - nsCOMPtr thread; - DebugOnly rv = NS_GetMainThread(getter_AddRefs(thread)); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - (void)NS_ProxyRelease(thread, mStorageFile); - - // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); + NS_ReleaseOnMainThread(mStorageFile.forget()); + NS_ReleaseOnMainThread(mConnection.forget()); // Generally, the callback will be released by CallbackComplete. // However, if for some reason Run() is not executed, we still // need to ensure that it is released here. - mozIStorageCompletionCallback *rawCallback = nullptr; - mCallback.swap(rawCallback); - (void)NS_ProxyRelease(thread, rawCallback); + NS_ReleaseOnMainThread(mCallback.forget()); } RefPtr mConnection; diff --git a/storage/mozStorageStatementData.h b/storage/mozStorageStatementData.h index b9d6b45c97a..8baaf2fa73a 100644 --- a/storage/mozStorageStatementData.h +++ b/storage/mozStorageStatementData.h @@ -52,8 +52,7 @@ public: // We need to ensure that mParamsArray is released on the main thread, // as the binding arguments may be XPConnect values, which are safe // to release only on the main thread. - nsCOMPtr mainThread = do_GetMainThread(); - (void)NS_ProxyRelease(mainThread, mParamsArray); + NS_ReleaseOnMainThread(mParamsArray.forget()); } /** diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index 571c24c6be5..4738f68fe83 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -529,8 +529,7 @@ public: // Last ditch attempt to release on the main thread - some of // the members of event are not thread-safe, so letting the // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); + NS_ReleaseOnMainThread(event.forget()); } } @@ -547,8 +546,7 @@ public: // Last ditch attempt to release on the main thread - some of // the members of event are not thread-safe, so letting the // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); + NS_ReleaseOnMainThread(event.forget()); } } @@ -749,8 +747,7 @@ public: if (!mResult) { return; } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); + NS_ReleaseOnMainThread(mResult.forget()); } protected: @@ -787,8 +784,7 @@ public: if (!mResult) { return; } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); + NS_ReleaseOnMainThread(mResult.forget()); } protected: diff --git a/toolkit/components/places/AsyncFaviconHelpers.cpp b/toolkit/components/places/AsyncFaviconHelpers.cpp index c40ec39f676..3118f3a0f42 100644 --- a/toolkit/components/places/AsyncFaviconHelpers.cpp +++ b/toolkit/components/places/AsyncFaviconHelpers.cpp @@ -357,10 +357,8 @@ AsyncFaviconHelperBase::AsyncFaviconHelperBase( AsyncFaviconHelperBase::~AsyncFaviconHelperBase() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); if (mCallback) { - (void)NS_ProxyRelease(thread, mCallback, true); + NS_ReleaseOnMainThread(mCallback.forget(), true); } } diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index 3354ba25e45..90d00fb69d6 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -543,13 +543,9 @@ DatabaseShutdown::Complete(nsresult, nsISupports*) if (NS_WARN_IF(!mBarrier)) { return NS_ERROR_NOT_AVAILABLE; } - nsCOMPtr barrier = mBarrier.forget(); - nsCOMPtr parentClient = mParentClient.forget(); - nsCOMPtr mainThread = do_GetMainThread(); - MOZ_ASSERT(mainThread); - NS_ProxyRelease(mainThread, barrier); - NS_ProxyRelease(mainThread, parentClient); + NS_ReleaseOnMainThread(mBarrier.forget()); + NS_ReleaseOnMainThread(mParentClient.forget()); return NS_OK; } diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index 3d7ff6c40ae..8bd3161ae6c 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -196,7 +196,7 @@ public: { mStatementCache.FinalizeStatements(); // Release the owner back on the calling thread. - (void)NS_ProxyRelease(mCallingThread, mOwner); + NS_ProxyRelease(mCallingThread, mOwner.forget()); return NS_OK; } diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index 47e9d4faf78..0220005e071 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -798,11 +798,8 @@ NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback, nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); - if (mCallback) { - (void)NS_ProxyRelease(thread, mCallback, false); + NS_ReleaseOnMainThread(mCallback.forget()); } } diff --git a/widget/gonk/nsScreenManagerGonk.cpp b/widget/gonk/nsScreenManagerGonk.cpp index e608db5c085..976c7fb26d7 100644 --- a/widget/gonk/nsScreenManagerGonk.cpp +++ b/widget/gonk/nsScreenManagerGonk.cpp @@ -736,7 +736,7 @@ nsScreenGonk::UpdateMirroringWidget(already_AddRefed& aWindow) if (mMirroringWidget) { nsCOMPtr widget = mMirroringWidget.forget(); - NS_ReleaseOnMainThread(widget); + NS_ReleaseOnMainThread(widget.forget()); } mMirroringWidget = aWindow; } diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index 722f71a1ac6..17ed1fa6ef3 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -314,7 +314,7 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage, // Release |retiredMessage| on the main thread in case it is an instance of // a mainthread-only class like nsScriptErrorWithStack and we're off the // main thread. - NS_ReleaseOnMainThread(retiredMessage); + NS_ReleaseOnMainThread(retiredMessage.forget()); } if (r) { diff --git a/xpcom/base/nsInterfaceRequestorAgg.cpp b/xpcom/base/nsInterfaceRequestorAgg.cpp index 0f87a74c66d..7e5cd83da88 100644 --- a/xpcom/base/nsInterfaceRequestorAgg.cpp +++ b/xpcom/base/nsInterfaceRequestorAgg.cpp @@ -54,16 +54,8 @@ nsInterfaceRequestorAgg::GetInterface(const nsIID& aIID, void** aResult) nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg() { - nsIInterfaceRequestor* iir = nullptr; - mFirst.swap(iir); - if (iir) { - NS_ProxyRelease(mConsumerTarget, iir); - } - iir = nullptr; - mSecond.swap(iir); - if (iir) { - NS_ProxyRelease(mConsumerTarget, iir); - } + NS_ProxyRelease(mConsumerTarget, mFirst.forget()); + NS_ProxyRelease(mConsumerTarget, mSecond.forget()); } nsresult diff --git a/xpcom/glue/nsProxyRelease.cpp b/xpcom/glue/nsProxyRelease.cpp deleted file mode 100644 index c61bd2009f9..00000000000 --- a/xpcom/glue/nsProxyRelease.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsProxyRelease.h" -#include "nsThreadUtils.h" -#include "nsAutoPtr.h" - -class nsProxyReleaseEvent : public nsRunnable -{ -public: - explicit nsProxyReleaseEvent(nsISupports* aDoomed) : mDoomed(aDoomed) {} - - NS_IMETHOD Run() - { - mDoomed->Release(); - return NS_OK; - } - -private: - nsISupports* MOZ_OWNING_REF mDoomed; -}; - -nsresult -NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, - bool aAlwaysProxy) -{ - nsresult rv; - - if (!aDoomed) { - // nothing to do - return NS_OK; - } - - if (!aTarget) { - NS_RELEASE(aDoomed); - return NS_OK; - } - - if (!aAlwaysProxy) { - bool onCurrentThread = false; - rv = aTarget->IsOnCurrentThread(&onCurrentThread); - if (NS_SUCCEEDED(rv) && onCurrentThread) { - NS_RELEASE(aDoomed); - return NS_OK; - } - } - - nsCOMPtr ev = new nsProxyReleaseEvent(aDoomed); - if (!ev) { - // we do not release aDoomed here since it may cause a delete on the - // wrong thread. better to leak than crash. - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - NS_WARNING("failed to post proxy release event"); - // again, it is better to leak the aDoomed object than risk crashing as - // a result of deleting it on the wrong thread. - } - return rv; -} diff --git a/xpcom/glue/nsProxyRelease.h b/xpcom/glue/nsProxyRelease.h index a247abf9250..f920c316c09 100644 --- a/xpcom/glue/nsProxyRelease.h +++ b/xpcom/glue/nsProxyRelease.h @@ -12,44 +12,34 @@ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "MainThreadUtils.h" +#include "nsThreadUtils.h" #include "mozilla/Likely.h" +#include "mozilla/Move.h" #ifdef XPCOM_GLUE_AVOID_NSPR #error NS_ProxyRelease implementation depends on NSPR. #endif -/** - * Ensure that a nsCOMPtr is released on the target thread. - * - * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) - */ + template -inline NS_HIDDEN_(nsresult) -NS_ProxyRelease(nsIEventTarget* aTarget, nsCOMPtr& aDoomed, - bool aAlwaysProxy = false) +class nsProxyReleaseEvent : public nsRunnable { - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ProxyRelease(aTarget, raw, aAlwaysProxy); -} +public: + explicit nsProxyReleaseEvent(already_AddRefed aDoomed) + : mDoomed(aDoomed.take()) {} + + NS_IMETHOD Run() + { + NS_IF_RELEASE(mDoomed); + return NS_OK; + } + +private: + T* MOZ_OWNING_REF mDoomed; +}; /** - * Ensure that a nsRefPtr is released on the target thread. - * - * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) - */ -template -inline NS_HIDDEN_(nsresult) -NS_ProxyRelease(nsIEventTarget* aTarget, RefPtr& aDoomed, - bool aAlwaysProxy = false) -{ - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ProxyRelease(aTarget, raw, aAlwaysProxy); -} - -/** - * Ensures that the delete of a nsISupports object occurs on the target thread. + * Ensures that the delete of a smart pointer occurs on the target thread. * * @param aTarget * the target thread where the doomed object should be released. @@ -61,42 +51,39 @@ NS_ProxyRelease(nsIEventTarget* aTarget, RefPtr& aDoomed, * true, then an event will always be posted to the target thread for * asynchronous release. */ -nsresult -NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, - bool aAlwaysProxy = false); - -/** - * Ensure that a nsCOMPtr is released on the main thread. - * - * @see NS_ReleaseOnMainThread( nsISupports*, bool) - */ template -inline NS_HIDDEN_(nsresult) -NS_ReleaseOnMainThread(nsCOMPtr& aDoomed, - bool aAlwaysProxy = false) +inline NS_HIDDEN_(void) +NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed aDoomed, + bool aAlwaysProxy = false) { - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ReleaseOnMainThread(raw, aAlwaysProxy); + // Auto-managing release of the pointer. + RefPtr doomed = aDoomed; + nsresult rv; + + if (!doomed || !aTarget) { + return; + } + + if (!aAlwaysProxy) { + bool onCurrentThread = false; + rv = aTarget->IsOnCurrentThread(&onCurrentThread); + if (NS_SUCCEEDED(rv) && onCurrentThread) { + return; + } + } + + nsCOMPtr ev = new nsProxyReleaseEvent(doomed.forget()); + + rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + NS_WARNING("failed to post proxy release event, leaking!"); + // It is better to leak the aDoomed object than risk crashing as + // a result of deleting it on the wrong thread. + } } /** - * Ensure that a nsRefPtr is released on the main thread. - * - * @see NS_ReleaseOnMainThread(nsISupports*, bool) - */ -template -inline NS_HIDDEN_(nsresult) -NS_ReleaseOnMainThread(RefPtr& aDoomed, - bool aAlwaysProxy = false) -{ - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ReleaseOnMainThread(raw, aAlwaysProxy); -} - -/** - * Ensures that the delete of a nsISupports object occurs on the main thread. + * Ensures that the delete of a smart pointer occurs on the main thread. * * @param aDoomed * the doomed object; the object to be released on the main thread. @@ -106,8 +93,9 @@ NS_ReleaseOnMainThread(RefPtr& aDoomed, * parameter is true, then an event will always be posted to the main * thread for asynchronous release. */ -inline nsresult -NS_ReleaseOnMainThread(nsISupports* aDoomed, +template +inline NS_HIDDEN_(void) +NS_ReleaseOnMainThread(already_AddRefed aDoomed, bool aAlwaysProxy = false) { // NS_ProxyRelease treats a null event target as "the current thread". So a @@ -115,10 +103,15 @@ NS_ReleaseOnMainThread(nsISupports* aDoomed, // main thread or the release must happen asynchronously. nsCOMPtr mainThread; if (!NS_IsMainThread() || aAlwaysProxy) { - NS_GetMainThread(getter_AddRefs(mainThread)); + nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); + + if (NS_FAILED(rv)) { + NS_WARNING("Could not get main thread! Leaking."); + return; + } } - return NS_ProxyRelease(mainThread, aDoomed, aAlwaysProxy); + NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy); } /** @@ -185,13 +178,7 @@ private: if (NS_IsMainThread()) { NS_IF_RELEASE(mRawPtr); } else if (mRawPtr) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (!mainThread) { - NS_WARNING("Couldn't get main thread! Leaking pointer."); - return; - } - NS_ProxyRelease(mainThread, mRawPtr); + NS_ReleaseOnMainThread(dont_AddRef(mRawPtr)); } } diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild index 9c211cc7892..641ae8aa456 100644 --- a/xpcom/glue/objs.mozbuild +++ b/xpcom/glue/objs.mozbuild @@ -39,7 +39,6 @@ xpcom_glue_src_cppsrcs = [ xpcom_gluens_src_lcppsrcs = [ 'BlockingResourceBase.cpp', 'GenericFactory.cpp', - 'nsProxyRelease.cpp', 'nsTextFormatter.cpp', ] diff --git a/xpcom/libxpcomrt/moz.build b/xpcom/libxpcomrt/moz.build index cb58cc5a727..29b4322e5df 100644 --- a/xpcom/libxpcomrt/moz.build +++ b/xpcom/libxpcomrt/moz.build @@ -61,7 +61,6 @@ xpcom_glue_src = [ 'nsID.cpp', 'nsISupportsImpl.cpp', 'nsMemory.cpp', - 'nsProxyRelease.cpp', 'nsQuickSort.cpp', 'nsTArray.cpp', 'nsTObserverArray.cpp', From a6ae41ff4765088c3fd572679e6740678aeed5a9 Mon Sep 17 00:00:00 2001 From: Bogdan Postelnicu Date: Thu, 17 Dec 2015 00:18:00 +0100 Subject: [PATCH 003/187] Bug 1232635 - since presContext is always a valid pointer remove the useless null check that also confused Coverity. r=bz --- layout/base/FrameLayerBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 1c2d8914ab8..d3f331548d0 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -5850,7 +5850,7 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer, FlashPaint(aContext); } - if (presContext && presContext->GetDocShell() && isActiveLayerManager) { + if (presContext->GetDocShell() && isActiveLayerManager) { nsDocShell* docShell = static_cast(presContext->GetDocShell()); RefPtr timelines = TimelineConsumers::Get(); From a53e09dd706473502cae5e651cadddfe9e3fcb5f Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Wed, 23 Dec 2015 11:45:26 -0800 Subject: [PATCH 004/187] Bug 1234813 - Tests for: sendBeacon should not throw if blocked by Content Policy. r=rbarnes --- dom/security/test/csp/file_main.js | 5 +--- dom/security/test/csp/file_sendbeacon.html | 21 +++++++++++++ dom/security/test/csp/mochitest.ini | 2 ++ dom/security/test/csp/test_sendbeacon.html | 34 ++++++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 dom/security/test/csp/file_sendbeacon.html create mode 100644 dom/security/test/csp/test_sendbeacon.html diff --git a/dom/security/test/csp/file_main.js b/dom/security/test/csp/file_main.js index 0bc15b6827a..e584c2645c3 100644 --- a/dom/security/test/csp/file_main.js +++ b/dom/security/test/csp/file_main.js @@ -11,10 +11,7 @@ doXHR("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_ba fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_good"); fetch("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_bad"); navigator.sendBeacon("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_good"); -try { - navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad"); -} catch(ex) {} - +navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad"); new Worker("file_main_worker.js").postMessage({inherited : false}); diff --git a/dom/security/test/csp/file_sendbeacon.html b/dom/security/test/csp/file_sendbeacon.html new file mode 100644 index 00000000000..13202c65ffa --- /dev/null +++ b/dom/security/test/csp/file_sendbeacon.html @@ -0,0 +1,21 @@ + + + + + + Bug 1234813 - sendBeacon should not throw if blocked by Content Policy + + + + + + + diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index f86bd6a9499..d9ae9d0a1b6 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -153,6 +153,7 @@ support-files = file_docwrite_meta.css file_docwrite_meta.js file_multipart_testserver.sjs + file_sendbeacon.html [test_base-uri.html] [test_blob_data_schemes.html] @@ -229,3 +230,4 @@ skip-if = buildapp == 'b2g' #investigate in bug 1222904 [test_meta_header_dual.html] [test_docwrite_meta.html] [test_multipartchannel.html] +[test_sendbeacon.html] diff --git a/dom/security/test/csp/test_sendbeacon.html b/dom/security/test/csp/test_sendbeacon.html new file mode 100644 index 00000000000..1b4cfbc867a --- /dev/null +++ b/dom/security/test/csp/test_sendbeacon.html @@ -0,0 +1,34 @@ + + + + + Bug 1234813 - sendBeacon should not throw if blocked by Content Policy + + + + + +

+ + + + + From a86e06a13c40c6c7a283948432ffb2bfb90c12f9 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Wed, 23 Dec 2015 11:50:05 -0800 Subject: [PATCH 005/187] Bug 1234813 - sendBeacon should not throw if blocked by Content Policy. r=rbarnes --- dom/base/Navigator.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 265c3c26a17..a4c178b74fe 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1342,10 +1342,9 @@ Navigator::SendBeacon(const nsAString& aUrl, RefPtr beaconListener = new BeaconStreamListener(); rv = channel->AsyncOpen2(beaconListener); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return false; - } + // do not throw if security checks fail within asyncOpen2 + NS_ENSURE_SUCCESS(rv, false); + // make the beaconListener hold a strong reference to the loadgroup // which is released in ::OnStartRequest beaconListener->SetLoadGroup(loadGroup); From 88519e1241ac179d47b78c958e1167b7e6440a18 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 19 Jan 2016 14:07:00 +0100 Subject: [PATCH 006/187] Bug 1240906 - Shut down geolocation service at xpcom-shutdown instead of quit-application. r=dougt Also, remove trailing spaces from network_geolocation.sjs. --- dom/geolocation/nsGeolocation.cpp | 6 +++--- dom/tests/mochitest/geolocation/network_geolocation.sjs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp index 8cb4ed0a6a5..c17632aa46d 100644 --- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -872,7 +872,7 @@ nsresult nsGeolocationService::Init() return NS_ERROR_FAILURE; } - obs->AddObserver(this, "quit-application", false); + obs->AddObserver(this, "xpcom-shutdown", false); obs->AddObserver(this, "mozsettings-changed", false); #ifdef MOZ_ENABLE_QT5GEOPOSITION @@ -980,10 +980,10 @@ nsGeolocationService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { - if (!strcmp("quit-application", aTopic)) { + if (!strcmp("xpcom-shutdown", aTopic)) { nsCOMPtr obs = services::GetObserverService(); if (obs) { - obs->RemoveObserver(this, "quit-application"); + obs->RemoveObserver(this, "xpcom-shutdown"); obs->RemoveObserver(this, "mozsettings-changed"); } diff --git a/dom/tests/mochitest/geolocation/network_geolocation.sjs b/dom/tests/mochitest/geolocation/network_geolocation.sjs index 5938a53ade6..6c7ca47beda 100644 --- a/dom/tests/mochitest/geolocation/network_geolocation.sjs +++ b/dom/tests/mochitest/geolocation/network_geolocation.sjs @@ -18,7 +18,7 @@ function parseQueryString(str) } function getPosition(action) -{ +{ var response = { status: "OK", location: { @@ -27,7 +27,7 @@ function getPosition(action) }, accuracy: (action == "worse-accuracy") ? 100 : 42, }; - + return JSON.stringify(response); } From 6539d69a616f970d8e449b37dd10f8142cc80f62 Mon Sep 17 00:00:00 2001 From: Cykesiopka Date: Wed, 10 Feb 2016 21:40:00 +0100 Subject: [PATCH 007/187] Bug 1243193 - Use Assert.throws() more in PSM tests. r=keeler --- .../manager/ssl/tests/unit/test_cert_dbKey.js | 10 ++------- .../unit/test_constructX509FromBase64.js | 22 ++++++++----------- .../ssl/tests/unit/test_logoutAndTeardown.js | 14 ++++-------- .../ssl/tests/unit/test_pinning_dynamic.js | 13 +++++------ .../tests/unit/test_pinning_header_parsing.js | 7 ++---- .../ssl/tests/unit/test_pkcs11_safe_mode.js | 10 ++------- .../manager/ssl/tests/unit/test_sts_fqdn.js | 7 ++---- 7 files changed, 26 insertions(+), 57 deletions(-) diff --git a/security/manager/ssl/tests/unit/test_cert_dbKey.js b/security/manager/ssl/tests/unit/test_cert_dbKey.js index f04e56a1d0b..ca47a499797 100644 --- a/security/manager/ssl/tests/unit/test_cert_dbKey.js +++ b/security/manager/ssl/tests/unit/test_cert_dbKey.js @@ -53,14 +53,8 @@ function encodeCommonNameAsBytes(commonName) { } function testInvalidDBKey(certDB, dbKey) { - let exceptionCaught = false; - try { - let cert = certDB.findCertByDBKey(dbKey); - } catch(e) { - do_print(e); - exceptionCaught = true; - } - ok(exceptionCaught, "should have thrown and caught an exception"); + throws(() => certDB.findCertByDBKey(dbKey), /NS_ERROR_ILLEGAL_INPUT/, + `findCertByDBKey(${dbKey}) should raise NS_ERROR_ILLEGAL_INPUT`); } function testDBKeyForNonexistentCert(certDB, dbKey) { diff --git a/security/manager/ssl/tests/unit/test_constructX509FromBase64.js b/security/manager/ssl/tests/unit/test_constructX509FromBase64.js index e565c6cb99f..a7b722a4118 100644 --- a/security/manager/ssl/tests/unit/test_constructX509FromBase64.js +++ b/security/manager/ssl/tests/unit/test_constructX509FromBase64.js @@ -35,26 +35,22 @@ function testGood(data) { } function testBad(data) { - try { - let cert = certDB.constructX509FromBase64(data.input); - ok(false, `Should have gotten an exception for "${data.input}"`); - } catch (e) { - equal(e.result, data.result, - "Actual and expected exception result should match"); - } + throws(() => certDB.constructX509FromBase64(data.input), data.result, + `Should get "${data.result}" for "${data.input}"`); } function run_test() { const badCases = [ // Wrong type or too short - { input: null, result: Cr.NS_ERROR_ILLEGAL_VALUE }, - { input: "", result: Cr.NS_ERROR_ILLEGAL_VALUE }, - { input: "=", result: Cr.NS_ERROR_ILLEGAL_VALUE }, - { input: "==", result: Cr.NS_ERROR_ILLEGAL_VALUE }, + { input: null, result: /NS_ERROR_ILLEGAL_VALUE/ }, + { input: "", result: /NS_ERROR_ILLEGAL_VALUE/ }, + { input: "=", result: /NS_ERROR_ILLEGAL_VALUE/ }, + { input: "==", result: /NS_ERROR_ILLEGAL_VALUE/ }, // Not base64 - { input: "forty-four dead stone lions", result: Cr.NS_ERROR_ILLEGAL_VALUE }, + { input: "forty-four dead stone lions", result: /NS_ERROR_ILLEGAL_VALUE/ }, // Not a cert - { input: "Zm9ydHktZm91ciBkZWFkIHN0b25lIGxpb25z", result: Cr.NS_ERROR_FAILURE } + { input: "Zm9ydHktZm91ciBkZWFkIHN0b25lIGxpb25z", + result: /NS_ERROR_FAILURE/ }, ]; // Real certs with all three padding levels diff --git a/security/manager/ssl/tests/unit/test_logoutAndTeardown.js b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js index 853d3e2854b..2bf8855fe12 100644 --- a/security/manager/ssl/tests/unit/test_logoutAndTeardown.js +++ b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js @@ -16,16 +16,10 @@ function connect_and_teardown() { let reader = { onInputStreamReady: function(stream) { - try { - stream.available(); - Assert.ok(false, "stream.available() should have thrown"); - } - catch (e) { - Assert.equal(e.result, Components.results.NS_ERROR_FAILURE, - "stream should be in an error state"); - Assert.ok(tearDown, "this should be as a result of logoutAndTeardown"); - run_next_test(); - } + throws(() => stream.available(), /NS_ERROR_FAILURE/, + "stream should be in an error state"); + ok(tearDown, "A tear down attempt should have occurred"); + run_next_test(); } }; diff --git a/security/manager/ssl/tests/unit/test_pinning_dynamic.js b/security/manager/ssl/tests/unit/test_pinning_dynamic.js index 8adcd005755..4b8e819265b 100644 --- a/security/manager/ssl/tests/unit/test_pinning_dynamic.js +++ b/security/manager/ssl/tests/unit/test_pinning_dynamic.js @@ -149,12 +149,10 @@ function checkStateRead(aSubject, aTopic, aData) { checkDefaultSiteHPKPStatus(); // failure to insert new pin entry leaves previous pin behavior - try { + throws(() => { gSSService.setKeyPins("a.pinning2.example.com", true, 1000, 1, ["not a hash"]); - ok(false, "Attempting to set an invalid pin should have failed"); - } catch(e) { - } + }, /NS_ERROR_ILLEGAL_VALUE/, "Attempting to set an invalid pin should fail"); checkFail(certFromFile('cn-a.pinning2.example.com-badca'), "a.pinning2.example.com"); checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot'), "a.pinning2.example.com"); checkOK(certFromFile('cn-x.a.pinning2.example.com-badca'), "x.a.pinning2.example.com"); @@ -170,12 +168,11 @@ function checkStateRead(aSubject, aTopic, aData) { checkDefaultSiteHPKPStatus(); // Incorrect size results in failure - try { + throws(() => { gSSService.setKeyPins("a.pinning2.example.com", true, 1000, 2, ["not a hash"]); - ok(false, "Attempting to set a pin with an incorrect size should have failed"); - } catch(e) { - } + }, /NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY/, + "Attempting to set a pin with an incorrect size should fail"); // Ensure built-in pins work as expected ok(!gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP, diff --git a/security/manager/ssl/tests/unit/test_pinning_header_parsing.js b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js index 9a594120305..9ab3b636add 100644 --- a/security/manager/ssl/tests/unit/test_pinning_header_parsing.js +++ b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js @@ -25,13 +25,10 @@ function checkFailParseInvalidPin(pinValue) { let sslStatus = new FakeSSLStatus( certFromFile('cn-a.pinning2.example.com-pinningroot')); let uri = Services.io.newURI("https://a.pinning2.example.com", null, null); - try { + throws(() => { gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, pinValue, sslStatus, 0); - ok(false, "Invalid pin should have been rejected"); - } catch (e) { - ok(true, "Invalid pin should be rejected"); - } + }, /NS_ERROR_FAILURE/, `Invalid pin "${pinValue}" should be rejected`); } function checkPassValidPin(pinValue, settingPin) { diff --git a/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js index e3ac01509f3..8264c4214c8 100644 --- a/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js +++ b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js @@ -46,12 +46,6 @@ function run_test() { libraryFile.append("pkcs11testmodule"); libraryFile.append(libraryName); ok(libraryFile.exists(), "The pkcs11testmodule file should exist"); - let exceptionCaught = false; - try { - pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0); - ok(false, "addModule should have thrown an exception"); - } catch (e) { - exceptionCaught = true; - } - ok(exceptionCaught, "addModule should have thrown an exception"); + throws(() => pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0), + /NS_ERROR_FAILURE/, "addModule should throw when in safe mode"); } diff --git a/security/manager/ssl/tests/unit/test_sts_fqdn.js b/security/manager/ssl/tests/unit/test_sts_fqdn.js index be49318f2e3..c2c3eb2bd16 100644 --- a/security/manager/ssl/tests/unit/test_sts_fqdn.js +++ b/security/manager/ssl/tests/unit/test_sts_fqdn.js @@ -44,10 +44,7 @@ function run_test() { // gracefully. uri = Services.io.newURI("https://../foo", null, null); equal(uri.host, ".."); - try { + throws(() => { SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0); - ok(false); // this shouldn't run - } catch (e) { - equal(e.result, Cr.NS_ERROR_UNEXPECTED); - } + }, /NS_ERROR_UNEXPECTED/, "Malformed URI should be rejected"); } From f53e450151fb4c2393e1d8a19cbf89fb635a95ac Mon Sep 17 00:00:00 2001 From: Louis Christie Date: Fri, 5 Feb 2016 16:32:41 +1300 Subject: [PATCH 008/187] Bug 1245982 - Added telemetry for the number of uses of HTMLMediaElement.fastSeek(). r=cpearce --- dom/html/HTMLMediaElement.cpp | 2 ++ toolkit/components/telemetry/Histograms.json | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 3716e3ca016..e2d23fa4ac9 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1431,6 +1431,8 @@ NS_IMETHODIMP HTMLMediaElement::GetCurrentTime(double* aCurrentTime) void HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv) { + LOG(LogLevel::Debug, ("Reporting telemetry VIDEO_FASTSEEK_USED")); + Telemetry::Accumulate(Telemetry::VIDEO_FASTSEEK_USED, 1); Seek(aTime, SeekTarget::PrevSyncPoint, aRv); } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 4637680e4eb..a4549d000b0 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -10171,5 +10171,12 @@ "bug_numbers": [1241278], "kind": "boolean", "description": "Usage of the deprecated Notification.requestPermission() callback argument" + }, + "VIDEO_FASTSEEK_USED": { + "alert_emails": ["lchristie@mozilla.com", "cpearce@mozilla.com"], + "expires_in_version": "55", + "bug_numbers": [1245982], + "kind": "count", + "description": "Uses of HTMLMediaElement.fastSeek" } } From 630c588705cd61be9bf45ef249fe3e8534b4a555 Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Wed, 10 Feb 2016 01:48:00 +0100 Subject: [PATCH 009/187] Bug 1207038 - Backout workaround of bug 1156475 for Marionette shutdown hang. r=maja_zf MozReview-Commit-ID: 8ZZGDA09ojo --- .../update/direct/test_direct_update.py | 14 ++------------ .../update/fallback/test_fallback_update.py | 17 +++-------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/testing/firefox-ui/tests/firefox_ui_tests/update/direct/test_direct_update.py b/testing/firefox-ui/tests/firefox_ui_tests/update/direct/test_direct_update.py index f786e4095ab..580d1b63586 100644 --- a/testing/firefox-ui/tests/firefox_ui_tests/update/direct/test_direct_update.py +++ b/testing/firefox-ui/tests/firefox_ui_tests/update/direct/test_direct_update.py @@ -16,16 +16,6 @@ class TestDirectUpdate(UpdateTestCase): finally: UpdateTestCase.tearDown(self) - def _test_update(self): - self.download_and_apply_available_update(force_fallback=False) - - self.check_update_applied() - def test_update(self): - try: - self._test_update() - except: - # Switch context to the main browser window before embarking - # down the failure code path to work around bug 1141519. - self.browser.switch_to() - raise + self.download_and_apply_available_update(force_fallback=False) + self.check_update_applied() diff --git a/testing/firefox-ui/tests/firefox_ui_tests/update/fallback/test_fallback_update.py b/testing/firefox-ui/tests/firefox_ui_tests/update/fallback/test_fallback_update.py index afe02352c76..e0a810d7201 100644 --- a/testing/firefox-ui/tests/firefox_ui_tests/update/fallback/test_fallback_update.py +++ b/testing/firefox-ui/tests/firefox_ui_tests/update/fallback/test_fallback_update.py @@ -16,18 +16,7 @@ class TestFallbackUpdate(UpdateTestCase): finally: UpdateTestCase.tearDown(self) - def _test_update(self): - self.download_and_apply_available_update(force_fallback=True) - - self.download_and_apply_forced_update() - - self.check_update_applied() - def test_update(self): - try: - self._test_update() - except: - # Switch context to the main browser window before embarking - # down the failure code path to work around bug 1141519. - self.browser.switch_to() - raise + self.download_and_apply_available_update(force_fallback=True) + self.download_and_apply_forced_update() + self.check_update_applied() From 613eb48f16bd55f2b553c565849484e4de9370f5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 11 Feb 2016 09:33:07 +0100 Subject: [PATCH 010/187] Bug 1246593 - Ensure that the element index is greater than zero for the PostWriteElementBarrier; r=jandem --- js/src/devtools/automation/cgc-jittest-timeouts.txt | 1 + js/src/jit-test/tests/gc/bug-1246593.js | 4 ++++ js/src/jit/VMFunctions.cpp | 3 ++- js/src/jit/VMFunctions.h | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/gc/bug-1246593.js diff --git a/js/src/devtools/automation/cgc-jittest-timeouts.txt b/js/src/devtools/automation/cgc-jittest-timeouts.txt index 892630a4bf4..28cc9b36fa7 100644 --- a/js/src/devtools/automation/cgc-jittest-timeouts.txt +++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt @@ -13,6 +13,7 @@ basic/testBug686274.js basic/testManyVars.js basic/testTypedArrayInit.js gc/bug-1014972.js +gc/bug-1246593.js gc/bug-906236.js gc/bug-906241.js ion/bug787921.js diff --git a/js/src/jit-test/tests/gc/bug-1246593.js b/js/src/jit-test/tests/gc/bug-1246593.js new file mode 100644 index 00000000000..4d90bbb24b9 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1246593.js @@ -0,0 +1,4 @@ +length = 10000; +array = Array(); +for (i = length;i > -100000; i--) + array[i] = {}; diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 774d9d8b0a3..2abbb2837bf 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -619,10 +619,11 @@ PostWriteBarrier(JSRuntime* rt, JSObject* obj) static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096; void -PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, size_t index) +PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index) { MOZ_ASSERT(!IsInsideNursery(obj)); if (obj->is() && + uint32_t(index) < obj->as().getDenseInitializedLength() && (obj->as().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE #ifdef JS_GC_ZEAL || rt->hasZealMode(gc::ZealMode::ElementsBarrier) diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index dbc6d5267f7..12907132984 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -640,7 +640,7 @@ bool CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, Muta void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp); void PostWriteBarrier(JSRuntime* rt, JSObject* obj); -void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, size_t index); +void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index); void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj); uint32_t GetIndexFromString(JSString* str); From bf84e4e6346c9c823eadc0bd46249580109be9f8 Mon Sep 17 00:00:00 2001 From: nhirata Date: Wed, 3 Feb 2016 16:13:00 +0100 Subject: [PATCH 011/187] Bug 1244893 - Disable F/OTA updates to balrog. r=wcosta --- testing/mozharness/configs/b2g/releng-fota-updates.py | 2 +- .../mozharness/configs/b2g/taskcluster-spark-ota-balrog.py | 2 +- testing/mozharness/configs/b2g/taskcluster-spark-ota.py | 1 - testing/taskcluster/scripts/phone-builder/build-phone-ota.sh | 4 ---- .../tasks/builds/b2g_aries_spark_ota_balrog_debug.yml | 2 -- .../tasks/builds/b2g_aries_spark_ota_balrog_opt.yml | 2 -- testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml | 2 +- testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml | 2 +- testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml | 4 +--- testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml | 4 +--- 10 files changed, 6 insertions(+), 19 deletions(-) diff --git a/testing/mozharness/configs/b2g/releng-fota-updates.py b/testing/mozharness/configs/b2g/releng-fota-updates.py index f135db39ffc..9162a13f52b 100644 --- a/testing/mozharness/configs/b2g/releng-fota-updates.py +++ b/testing/mozharness/configs/b2g/releng-fota-updates.py @@ -14,7 +14,7 @@ config = { # bug 1222227 - temporarily disable for S3 migration # 'make-socorro-json', # 'upload-source-manifest', - 'submit-to-balrog', + # 'submit-to-balrog', ], "upload": { "default": { diff --git a/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py b/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py index 797e96f39fa..7a089b7738d 100644 --- a/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py +++ b/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py @@ -8,7 +8,7 @@ config = { 'build-symbols', 'make-updates', 'prep-upload', - 'submit-to-balrog' + #'submit-to-balrog' ], "balrog_credentials_file": "balrog_credentials", "nightly_build": True, diff --git a/testing/mozharness/configs/b2g/taskcluster-spark-ota.py b/testing/mozharness/configs/b2g/taskcluster-spark-ota.py index d4e92790483..9319d26ac9e 100644 --- a/testing/mozharness/configs/b2g/taskcluster-spark-ota.py +++ b/testing/mozharness/configs/b2g/taskcluster-spark-ota.py @@ -9,7 +9,6 @@ config = { 'make-updates', 'prep-upload' ], - "balrog_credentials_file": "balrog_credentials", "nightly_build": True, "env": { "GAIA_OPTIMIZE": "1", diff --git a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh index 1981513cf20..7c1f44f5dc2 100755 --- a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh +++ b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh @@ -10,8 +10,6 @@ fi PLATFORM=${TARGET%%-*} -aws s3 cp s3://b2g-nightly-credentials/balrog_credentials . - # We need different platform names for each variant (user, userdebug and # eng). We do not append variant suffix for "user" to keep compability with # verions already installed in the phones. @@ -20,14 +18,12 @@ if [ 0$DOGFOOD -ne 1 -a $VARIANT != "user" ]; then fi MOZHARNESS_CONFIG=${MOZHARNESS_CONFIG:=b2g/taskcluster-phone-ota.py} -BALROG_SERVER_CONFIG=${BALROG_SERVER_CONFIG:=balrog/docker-worker.py} rm -rf $WORKSPACE/B2G/upload-public/ rm -rf $WORKSPACE/B2G/upload/ $WORKSPACE/gecko/testing/mozharness/scripts/b2g_build.py \ --config $MOZHARNESS_CONFIG \ - --config $BALROG_SERVER_CONFIG \ "$debug_flag" \ --disable-mock \ --variant=$VARIANT \ diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml index 78e5658b02d..e516aca68be 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml @@ -2,10 +2,8 @@ $inherits: from: 'tasks/builds/b2g_aries_spark_ota_debug.yml' task: scopes: - - 'docker-worker:feature:balrogVPNProxy' payload: features: - balrogVPNProxy: true env: MOZHARNESS_CONFIG: b2g/taskcluster-spark-ota-balrog.py diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml index 0405d1030f9..5caeb748009 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml @@ -2,9 +2,7 @@ $inherits: from: 'tasks/builds/b2g_aries_spark_ota_opt.yml' task: scopes: - - 'docker-worker:feature:balrogVPNProxy' payload: features: - balrogVPNProxy: true env: MOZHARNESS_CONFIG: b2g/taskcluster-spark-ota-balrog.py diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml index fd7fb60cdb9..96a1ecda8ce 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_phone_base.yml' task: - workerType: balrog + workerType: flame-kk payload: env: diff --git a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml index 6cd485f87c9..78df46b33af 100644 --- a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_phone_base.yml' task: - workerType: balrog + workerType: flame-kk payload: env: TARGET: 'flame-kk' diff --git a/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml b/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml index dde8124659b..863d2aebe4f 100644 --- a/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml @@ -4,17 +4,15 @@ $inherits: build_name: 'nexus-4-kk-ota' build_type: 'debug' task: - workerType: balrog + workerType: flame-kk metadata: name: '[TC] B2G Nexus 4 KK OTA (userdebug)' scopes: - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug' - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug-objdir-gecko' - - 'docker-worker:feature:balrogVPNProxy' payload: features: - balrogVPNProxy: true cache: level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug: /home/worker/workspace level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug-objdir-gecko: /home/worker/objdir-gecko diff --git a/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml b/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml index b419c27ef48..f5c6c22aaa6 100644 --- a/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml @@ -4,17 +4,15 @@ $inherits: build_name: 'nexus-5-l-ota' build_type: 'debug' task: - workerType: balrog + workerType: flame-kk metadata: name: '[TC] B2G Nexus 5L OTA (userdebug)' scopes: - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-5l-ota-debug' - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-5l-ota-debug-objdir-gecko' - - 'docker-worker:feature:balrogVPNProxy' payload: features: - balrogVPNProxy: true cache: level-{{level}}-{{project}}-build-nexus-5l-ota-debug: /home/worker/workspace level-{{level}}-{{project}}-build-nexus-5l-ota-debug-objdir-gecko: /home/worker/objdir-gecko From 511bc685e93824d7ab516acce385d468c210fd8e Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Wed, 10 Feb 2016 17:15:28 +0100 Subject: [PATCH 012/187] Bug 1230768. r=jesup --- dom/media/systemservices/CamerasChild.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h index c7fa8985ca0..1e6d8d10023 100644 --- a/dom/media/systemservices/CamerasChild.h +++ b/dom/media/systemservices/CamerasChild.h @@ -13,6 +13,7 @@ #include "mozilla/camera/PCamerasChild.h" #include "mozilla/camera/PCamerasParent.h" #include "mozilla/Mutex.h" +#include "base/singleton.h" #include "nsCOMPtr.h" // conflicts with #include of scoped_ptr.h @@ -79,24 +80,21 @@ public: ~CamerasSingleton(); static OffTheBooksMutex& Mutex() { - return GetInstance().mCamerasMutex; + return gTheInstance.get()->mCamerasMutex; } static CamerasChild*& Child() { Mutex().AssertCurrentThreadOwns(); - return GetInstance().mCameras; + return gTheInstance.get()->mCameras; } static nsCOMPtr& Thread() { Mutex().AssertCurrentThreadOwns(); - return GetInstance().mCamerasChildThread; + return gTheInstance.get()->mCamerasChildThread; } private: - static CamerasSingleton& GetInstance() { - static CamerasSingleton instance; - return instance; - } + static Singleton gTheInstance; // Reinitializing CamerasChild will change the pointers below. // We don't want this to happen in the middle of preparing IPC. From 15b18cf5178d343cce1a0fba0bb8b3e68ad0e798 Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Wed, 10 Feb 2016 18:12:02 +0800 Subject: [PATCH 013/187] Bug 1245398 - Use in-tree script for all mulet tests; r=ahal MozReview-Commit-ID: AntB37HzGXa --- .../taskcluster/scripts/tester/test-mulet.sh | 21 ++++++++++++- .../tasks/tests/b2g_unittest_base.yml | 2 ++ .../tasks/tests/mulet_build_test.yml | 22 +++++++------- .../tasks/tests/mulet_build_unit.yml | 20 ++++++------- .../tests/mulet_gaia_js_integration_tests.yml | 24 +++++++-------- .../tasks/tests/mulet_gaia_unit.yml | 26 ++++++++-------- .../tasks/tests/mulet_gaia_unit_oop.yml | 28 ++++++++--------- .../taskcluster/tasks/tests/mulet_linter.yml | 20 ++++++------- .../tasks/tests/mulet_mochitests.yml | 19 ++---------- .../tasks/tests/mulet_reftests.yml | 30 +++++++++---------- 10 files changed, 109 insertions(+), 103 deletions(-) diff --git a/testing/taskcluster/scripts/tester/test-mulet.sh b/testing/taskcluster/scripts/tester/test-mulet.sh index 9a6e1b77adc..43a54f93aec 100644 --- a/testing/taskcluster/scripts/tester/test-mulet.sh +++ b/testing/taskcluster/scripts/tester/test-mulet.sh @@ -15,6 +15,7 @@ echo "running as" $(id) : MOZHARNESS_CONFIG ${MOZHARNESS_CONFIG} : NEED_XVFB ${NEED_XVFB:=true} : NEED_PULSEAUDIO ${NEED_PULSEAUDIO:=false} +: NEED_PULL_GAIA ${NEED_PULL_GAIA:=false} : SKIP_MOZHARNESS_RUN ${SKIP_MOZHARNESS_RUN:=false} : WORKSPACE ${WORKSPACE:=/home/worker/workspace} : mozharness args "${@}" @@ -79,6 +80,24 @@ if $NEED_XVFB; then if [ $xvfb_test == 255 ]; then exit 255; fi fi +gaia_cmds="" +if $NEED_PULL_GAIA; then + # test required parameters are supplied + if [[ -z ${GAIA_BASE_REPOSITORY} ]]; then exit 1; fi + if [[ -z ${GAIA_HEAD_REPOSITORY} ]]; then exit 1; fi + if [[ -z ${GAIA_REV} ]]; then exit 1; fi + if [[ -z ${GAIA_REF} ]]; then exit 1; fi + + tc-vcs checkout \ + ${WORKSPACE}/gaia \ + ${GAIA_BASE_REPOSITORY} \ + ${GAIA_HEAD_REPOSITORY} \ + ${GAIA_REV} \ + ${GAIA_REF} + + gaia_cmds="--gaia-dir=${WORKSPACE}" +fi + # support multiple, space delimited, config files config_cmds="" for cfg in $MOZHARNESS_CONFIG; do @@ -95,5 +114,5 @@ if [ ${SKIP_MOZHARNESS_RUN} == true ]; then else # run the given mozharness script and configs, but pass the rest of the # arguments in from our own invocation - python2.7 $WORKSPACE/${MOZHARNESS_SCRIPT} ${config_cmds} "${@}" + python2.7 $WORKSPACE/${MOZHARNESS_SCRIPT} ${config_cmds} ${gaia_cmds} "${@}" fi diff --git a/testing/taskcluster/tasks/tests/b2g_unittest_base.yml b/testing/taskcluster/tasks/tests/b2g_unittest_base.yml index d33aba03566..14096481477 100644 --- a/testing/taskcluster/tasks/tests/b2g_unittest_base.yml +++ b/testing/taskcluster/tasks/tests/b2g_unittest_base.yml @@ -17,6 +17,8 @@ task: loopbackVideo: true loopbackAudio: true env: + GECKO_HEAD_REPOSITORY: '{{{head_repository}}}' + GECKO_HEAD_REV: '{{{head_rev}}}' GAIA_HEAD_REPOSITORY: '{{{gaia_head_repository}}}' GAIA_BASE_REPOSITORY: '{{{gaia_base_repository}}}' GAIA_REF: '{{{gaia_ref}}}' diff --git a/testing/taskcluster/tasks/tests/mulet_build_test.yml b/testing/taskcluster/tasks/tests/mulet_build_test.yml index 1fe8757221a..9194d576841 100644 --- a/testing/taskcluster/tasks/tests/mulet_build_test.yml +++ b/testing/taskcluster/tasks/tests/mulet_build_test.yml @@ -7,18 +7,18 @@ task: description: Gaia Build Test test run payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_build_integration.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_integration.py' command: - - entrypoint # entrypoint ensures we are running in xvfb - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_build_integration.py - --application firefox - --config-file ./mozharness/configs/b2g/taskcluster_gaia_integration.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --gaia-repo https://hg.mozilla.org/integration/gaia-central - --gaia-dir /home/worker - --xre-url https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --xre-url=https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_build_unit.yml b/testing/taskcluster/tasks/tests/mulet_build_unit.yml index 34de098214c..d9ed7ff751d 100644 --- a/testing/taskcluster/tasks/tests/mulet_build_unit.yml +++ b/testing/taskcluster/tasks/tests/mulet_build_unit.yml @@ -7,17 +7,17 @@ task: description: Gaia Build Unit Test payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_build_unit.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_integration.py' command: - - entrypoint - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_build_unit.py - --application firefox - --config-file ./mozharness/configs/b2g/taskcluster_gaia_integration.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --gaia-repo https://hg.mozilla.org/integration/gaia-central - --gaia-dir /home/worker + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml index eddf9e786b4..03c29b4bebe 100644 --- a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml +++ b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml @@ -8,19 +8,19 @@ task: description: Mulet Gaia JS Integration Test run {{chunk}} payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_integration.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_integration.py' command: - - entrypoint # entrypoint ensures we are running in xvfb - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_integration.py - --application firefox - --config-file b2g/taskcluster_gaia_integration.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --total-chunk {{total_chunks}} - --this-chunk {{chunk}} - --gaia-repo {{gaia_head_repository}} - --gaia-dir /home/worker + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --total-chunk={{total_chunks}} + - --this-chunk={{chunk}} artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_gaia_unit.yml b/testing/taskcluster/tasks/tests/mulet_gaia_unit.yml index 6920ecf1c62..7602079f4a2 100644 --- a/testing/taskcluster/tasks/tests/mulet_gaia_unit.yml +++ b/testing/taskcluster/tasks/tests/mulet_gaia_unit.yml @@ -7,20 +7,20 @@ task: description: Mulet Gaia Unit Test payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_unit.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_unit_production.py' command: - - entrypoint - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_unit.py - --application firefox - --config-file b2g/taskcluster_gaia_unit_production.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --gaia-repo {{gaia_head_repository}} - --gaia-dir /home/worker - --xre-url https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 - --total-chunk={{total_chunks}} - --this-chunk={{chunk}} + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --xre-url=https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 + - --total-chunk={{total_chunks}} + - --this-chunk={{chunk}} artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_gaia_unit_oop.yml b/testing/taskcluster/tasks/tests/mulet_gaia_unit_oop.yml index 3833b9b242b..e021c6fa86b 100644 --- a/testing/taskcluster/tasks/tests/mulet_gaia_unit_oop.yml +++ b/testing/taskcluster/tasks/tests/mulet_gaia_unit_oop.yml @@ -7,21 +7,21 @@ task: description: Mulet Gaia Unit Test OOP payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_unit.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_unit_production.py' command: - - entrypoint - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_unit.py - --application firefox - --config-file b2g/taskcluster_gaia_unit_production.py - --browser-arg -oop - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --gaia-repo {{gaia_head_repository}} - --gaia-dir /home/worker - --xre-url https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 - --total-chunk={{total_chunks}} - --this-chunk={{chunk}} + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --browser-arg=-oop + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --xre-url=https://api.pub.build.mozilla.org/tooltool/sha512/cefa8c00db04969d3a50e2a5509bd4ea1dc17d256a651a9518cb28dad72e87a1dbbcd3c88ef770be0edf0ab73d2d73925140df93618ffb7fab81b789d312f547 + - --total-chunk={{total_chunks}} + - --this-chunk={{chunk}} artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_linter.yml b/testing/taskcluster/tasks/tests/mulet_linter.yml index e575156558e..177761bc1d3 100644 --- a/testing/taskcluster/tasks/tests/mulet_linter.yml +++ b/testing/taskcluster/tasks/tests/mulet_linter.yml @@ -7,17 +7,17 @@ task: description: Linter Test payload: + env: + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/gaia_linter.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_gaia_integration.py' command: - - entrypoint - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/gaia_linter.py - --application firefox - --config-file ./mozharness/configs/b2g/taskcluster_gaia_integration.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --gaia-repo https://hg.mozilla.org/integration/gaia-central - --gaia-dir /home/worker + - bash + - /home/worker/bin/test.sh + - --application=firefox + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} artifacts: 'public/build': type: directory diff --git a/testing/taskcluster/tasks/tests/mulet_mochitests.yml b/testing/taskcluster/tasks/tests/mulet_mochitests.yml index 284b6c60bf0..253eebe6480 100644 --- a/testing/taskcluster/tasks/tests/mulet_mochitests.yml +++ b/testing/taskcluster/tasks/tests/mulet_mochitests.yml @@ -1,28 +1,13 @@ --- $inherits: - from: 'tasks/test.yml' + from: 'tasks/tests/b2g_unittest_base.yml' task: metadata: name: '[TC] Mulet Mochitests ( {{chunk}} )' description: Mulet Mochitest run {{chunk}} - scopes: - - 'docker-worker:cache:level-{{level}}-{{project}}-tc-vcs' - - 'docker-worker:cache:level-{{level}}-{{project}}-dotcache' - - 'docker-worker:capability:device:loopbackVideo' - - 'docker-worker:capability:device:loopbackAudio' + payload: - image: '{{#docker_image}}tester{{/docker_image}}' - cache: - # So pip installs are cached... - level-{{level}}-{{project}}-dotcache: /home/worker/.cache - level-{{level}}-{{project}}-tc-vcs: '/home/worker/.tc-vcs' - capabilities: - devices: - loopbackVideo: true - loopbackAudio: true env: - GECKO_HEAD_REPOSITORY: '{{{head_repository}}}' - GECKO_HEAD_REV: '{{{head_rev}}}' NEED_XVFB: true NEED_PULSEAUDIO: true MOZHARNESS_SCRIPT: 'mozharness/scripts/desktop_unittest.py' diff --git a/testing/taskcluster/tasks/tests/mulet_reftests.yml b/testing/taskcluster/tasks/tests/mulet_reftests.yml index d515b198f56..e4984eb8018 100644 --- a/testing/taskcluster/tasks/tests/mulet_reftests.yml +++ b/testing/taskcluster/tasks/tests/mulet_reftests.yml @@ -3,26 +3,25 @@ $inherits: from: 'tasks/tests/b2g_unittest_base.yml' task: metadata: - name: '[TC] Reftest' - description: Reftest test run {{chunk}} + name: '[TC] Mulet Reftest ( {{chunk}} )' + description: Mulet Reftest run {{chunk}} payload: env: MOZ_DISABLE_NONLOCAL_CONNECTIONS: 0 + NEED_XVFB: true + NEED_PULL_GAIA: true + MOZHARNESS_SCRIPT: 'mozharness/scripts/mulet_unittest.py' + MOZHARNESS_CONFIG: 'mozharness/configs/b2g/taskcluster_mulet_automation.py' command: - - entrypoint - - ./bin/pull_gaia.sh && - - > - python ./mozharness/scripts/mulet_unittest.py - --config-file ./mozharness/configs/b2g/taskcluster_mulet_automation.py - --installer-url {{build_url}} - --test-packages-url {{test_packages_url}} - --test-suite reftest - --test-manifest tests/layout/reftests/reftest.list - --this-chunk {{chunk}} - --total-chunk {{total_chunks}} - --gaia-repo https://hg.mozilla.org/integration/gaia-central - --gaia-dir /home/worker/gaia + - bash + - /home/worker/bin/test.sh + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --test-suite=reftest + - --test-manifest=tests/layout/reftests/reftest.list + - --this-chunk={{chunk}} + - --total-chunk={{total_chunks}} artifacts: 'public/build': type: directory @@ -40,3 +39,4 @@ task: groupName: Mulet Reftest symbol: 'R{{chunk}}' productName: b2g + tier: 3 From dfef659a8c344df5a5265a4a9a018b10e199845b Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 11 Feb 2016 11:32:32 +0100 Subject: [PATCH 014/187] Backed out changeset 84b8eef80b22 (bug 1234813) --- dom/base/Navigator.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index a4c178b74fe..265c3c26a17 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1342,9 +1342,10 @@ Navigator::SendBeacon(const nsAString& aUrl, RefPtr beaconListener = new BeaconStreamListener(); rv = channel->AsyncOpen2(beaconListener); - // do not throw if security checks fail within asyncOpen2 - NS_ENSURE_SUCCESS(rv, false); - + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return false; + } // make the beaconListener hold a strong reference to the loadgroup // which is released in ::OnStartRequest beaconListener->SetLoadGroup(loadGroup); From 5aaf7f18ae6a0ad05ef7032b23fc41ec3e210e82 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 11 Feb 2016 11:33:06 +0100 Subject: [PATCH 015/187] Backed out changeset b9aecc2e6334 (bug 1234813) for test failures in connect-src-beacon-blocked.sub.html --- dom/security/test/csp/file_main.js | 5 +++- dom/security/test/csp/file_sendbeacon.html | 21 ------------- dom/security/test/csp/mochitest.ini | 2 -- dom/security/test/csp/test_sendbeacon.html | 34 ---------------------- 4 files changed, 4 insertions(+), 58 deletions(-) delete mode 100644 dom/security/test/csp/file_sendbeacon.html delete mode 100644 dom/security/test/csp/test_sendbeacon.html diff --git a/dom/security/test/csp/file_main.js b/dom/security/test/csp/file_main.js index e584c2645c3..0bc15b6827a 100644 --- a/dom/security/test/csp/file_main.js +++ b/dom/security/test/csp/file_main.js @@ -11,7 +11,10 @@ doXHR("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_ba fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_good"); fetch("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_bad"); navigator.sendBeacon("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_good"); -navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad"); +try { + navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad"); +} catch(ex) {} + new Worker("file_main_worker.js").postMessage({inherited : false}); diff --git a/dom/security/test/csp/file_sendbeacon.html b/dom/security/test/csp/file_sendbeacon.html deleted file mode 100644 index 13202c65ffa..00000000000 --- a/dom/security/test/csp/file_sendbeacon.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - Bug 1234813 - sendBeacon should not throw if blocked by Content Policy - - - - - - - diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index d9ae9d0a1b6..f86bd6a9499 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -153,7 +153,6 @@ support-files = file_docwrite_meta.css file_docwrite_meta.js file_multipart_testserver.sjs - file_sendbeacon.html [test_base-uri.html] [test_blob_data_schemes.html] @@ -230,4 +229,3 @@ skip-if = buildapp == 'b2g' #investigate in bug 1222904 [test_meta_header_dual.html] [test_docwrite_meta.html] [test_multipartchannel.html] -[test_sendbeacon.html] diff --git a/dom/security/test/csp/test_sendbeacon.html b/dom/security/test/csp/test_sendbeacon.html deleted file mode 100644 index 1b4cfbc867a..00000000000 --- a/dom/security/test/csp/test_sendbeacon.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Bug 1234813 - sendBeacon should not throw if blocked by Content Policy - - - - - -

- - - - - From de19c2597842d4ec375bcc26014b6238a76e32e1 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Wed, 10 Feb 2016 18:32:16 -0800 Subject: [PATCH 016/187] Bug 1246174 - fix tp5o responsiveness calculation for reporting to perfherder. r=parkouss MozReview-Commit-ID: 3MP9ugObja4 --- testing/talos/talos/output.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/testing/talos/talos/output.py b/testing/talos/talos/output.py index 57a15801231..098a8bada9f 100755 --- a/testing/talos/talos/output.py +++ b/testing/talos/talos/output.py @@ -516,6 +516,16 @@ class PerfherderOutput(Output): if 'mainthreadio' in name: continue + # responsiveness has it's own metric, not the mean + # TODO: consider doing this for all counters + if 'responsiveness' is name: + subtest = { + 'name': name, + 'value': self.responsiveness_Metric(vals) + } + counter_subtests.append(subtest) + continue + subtest = { 'name': name, 'value': 0.0, From 73e1084a34f1f2d87adfc5c4a4324342d0e2cc80 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 12 Feb 2016 00:18:55 +1300 Subject: [PATCH 017/187] Bug 1216832 - Handle preserve-3d visible regions during display list building by always transforming from the preserve-3d root each time. r=roc --- layout/base/nsDisplayList.cpp | 9 +-------- layout/base/nsDisplayList.h | 4 +--- layout/generic/nsFrame.cpp | 23 +++++++++-------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 50e41a036a4..922e98ae065 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -966,12 +966,9 @@ nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, * dirty rect for preserve-3d children. * * @param aDirtyFrame is the frame to mark children extending context. - * @param aDirtyRect is the same as the dirty rect of the root of the - * current 3D context, but be translated relative to - * the aDirtyFrame. */ void -nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) +nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame) { AutoTArray childListArray; aDirtyFrame->GetChildLists(&childListArray); @@ -982,10 +979,6 @@ nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, nsIFrame *child = childFrames.get(); if (child->Combines3DTransformWithAncestors()) { mFramesMarkedForDisplay.AppendElement(child); - nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame); - child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(), - new nsRect(dirty)); - MarkFrameForDisplay(child, aDirtyFrame); } } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 819b75f13f7..d62b74ca14c 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -575,7 +575,7 @@ public: * Because these frames include transforms set on their parent, dirty rects * for intermediate frames may be empty, yet child frames could still be visible. */ - void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect); + void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame); const nsTArray& GetThemeGeometries() { return mThemeGeometries; } @@ -968,8 +968,6 @@ public: aFrame->Properties().Get(OutOfFlowDisplayDataProperty())); } - NS_DECLARE_FRAME_PROPERTY_DELETABLE(Preserve3DDirtyRectProperty, nsRect) - nsPresContext* CurrentPresContext() { return CurrentPresShellState()->mPresShell->GetPresContext(); } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d1372fc471f..de3a688755b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2051,8 +2051,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsRect dirtyRectOutsideTransform = dirtyRect; if (isTransformed) { const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); - if (Extend3DContext() || Combines3DTransformWithAncestors() || - nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, + if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { dirtyRect = overflow; } else { @@ -2060,9 +2059,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, return; } + // If we're in preserve-3d then grab the dirty rect that was given to the root + // and transform using the combined transform. + if (Combines3DTransformWithAncestors()) { + dirtyRect = aBuilder->GetPreserves3DDirtyRect(this); + } + nsRect untransformedDirtyRect; if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, - nsPoint(0,0), &untransformedDirtyRect, false)) { + nsPoint(0,0), &untransformedDirtyRect, true)) { dirtyRect = untransformedDirtyRect; } else { NS_WARNING("Unable to untransform dirty rect!"); @@ -2134,8 +2139,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // Extend3DContext() also guarantees that applyAbsPosClipping and usingSVGEffects are false // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy if (Extend3DContext()) { - nsRect dirty = aBuilder->GetPreserves3DDirtyRect(this); - aBuilder->MarkPreserve3DFramesForDisplayList(this, dirty); + aBuilder->MarkPreserve3DFramesForDisplayList(this); } if (aBuilder->IsBuildingLayerEventRegions()) { @@ -2462,15 +2466,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } pseudoStackingContext = true; } - if (child->Combines3DTransformWithAncestors()) { - nsRect* savedDirty = static_cast - (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty())); - if (savedDirty) { - dirty = *savedDirty; - } else { - dirty.SetEmpty(); - } - } NS_ASSERTION(childType != nsGkAtoms::placeholderFrame, "Should have dealt with placeholders already"); From 3d68f0bf292c239bc2a9e0aba88b9062e62c032d Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 12 Feb 2016 00:19:19 +1300 Subject: [PATCH 018/187] Bug 1216832 - Add crashtest. r=roc --- gfx/tests/crashtests/1216832-1.html | 13 +++++++++++++ gfx/tests/crashtests/crashtests.list | 1 + 2 files changed, 14 insertions(+) create mode 100644 gfx/tests/crashtests/1216832-1.html diff --git a/gfx/tests/crashtests/1216832-1.html b/gfx/tests/crashtests/1216832-1.html new file mode 100644 index 00000000000..0f02a9280be --- /dev/null +++ b/gfx/tests/crashtests/1216832-1.html @@ -0,0 +1,13 @@ + + + + + + + +
+
+
+
+ + diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list index ce12aa560e9..d4790a107c6 100644 --- a/gfx/tests/crashtests/crashtests.list +++ b/gfx/tests/crashtests/crashtests.list @@ -124,4 +124,5 @@ load 944579.html pref(security.fileuri.strict_origin_policy,false) load 950000.html load 1034403-1.html load balinese-letter-spacing.html +load 1216832-1.html load 1225125-1.html From 2309c92888c96aecdaeaa1e47f1a42c8c36e8ecb Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Tue, 9 Feb 2016 19:39:55 +0000 Subject: [PATCH 019/187] Bug 1239744 - no longer automatically show an on-screen keyboard for programmatic focus changes, tidy up osk logic in IMEHandler, r=masayuki --- widget/IMEData.h | 12 +++++++ widget/windows/WinIMEHandler.cpp | 62 +++++++++++++++++++++----------- widget/windows/WinIMEHandler.h | 1 + 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/widget/IMEData.h b/widget/IMEData.h index 8c022331942..369615e2767 100644 --- a/widget/IMEData.h +++ b/widget/IMEData.h @@ -393,6 +393,18 @@ struct InputContextAction final (mCause == CAUSE_MOUSE || mCause == CAUSE_TOUCH)); } + static bool IsUserAction(Cause aCause) + { + switch (aCause) { + case CAUSE_KEY: + case CAUSE_MOUSE: + case CAUSE_TOUCH: + return true; + default: + return false; + } + } + InputContextAction() : mCause(CAUSE_UNKNOWN) , mFocusChange(FOCUS_NOT_CHANGED) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 261988cb24b..b0581dbe347 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -606,7 +606,7 @@ IMEHandler::MaybeShowOnScreenKeyboard() !IsWin8OrLater() || !Preferences::GetBool(kOskEnabled, true) || GetOnScreenKeyboardWindow() || - IMEHandler::IsKeyboardPresentOnSlate()) { + !IMEHandler::NeedOnScreenKeyboard()) { return; } @@ -653,24 +653,31 @@ IMEHandler::WStringStartsWithCaseInsensitive(const std::wstring& aHaystack, lowerCaseNeedle.c_str()) == lowerCaseHaystack.c_str(); } -// Returns true if a physical keyboard is detected on Windows 8 and up. -// Uses the Setup APIs to enumerate the attached keyboards and returns true -// if the keyboard count is 1 or more. While this will work in most cases -// it won't work if there are devices which expose keyboard interfaces which -// are attached to the machine. -// Based on IsKeyboardPresentOnSlate() in Chromium's base/win/win_util.cc. +// Returns false if a physical keyboard is detected on Windows 8 and up, +// or there is some other reason why an onscreen keyboard is not necessary. +// Returns true if no keyboard is found and this device looks like it needs +// an on-screen keyboard for text input. // static bool -IMEHandler::IsKeyboardPresentOnSlate() +IMEHandler::NeedOnScreenKeyboard() { // This function is only supported for Windows 8 and up. if (!IsWin8OrLater()) { Preferences::SetString(kOskDebugReason, L"IKPOS: Requires Win8+."); - return true; + return false; } if (!Preferences::GetBool(kOskDetectPhysicalKeyboard, true)) { Preferences::SetString(kOskDebugReason, L"IKPOS: Detection disabled."); + return true; + } + + // If the last focus cause was not user-initiated (ie a result of code + // setting focus to an element) then don't auto-show a keyboard. This + // avoids cases where the keyboard would pop up "just" because e.g. a + // web page chooses to focus a search field on the page, even when that + // really isn't what the user is trying to do at that moment. + if (!InputContextAction::IsUserAction(sLastContextActionCause)) { return false; } @@ -679,13 +686,13 @@ IMEHandler::IsKeyboardPresentOnSlate() != NID_INTEGRATED_TOUCH) { Preferences::SetString(kOskDebugReason, L"IKPOS: Touch screen not found."); - return true; + return false; } // If the device is docked, the user is treating the device as a PC. if (::GetSystemMetrics(SM_SYSTEMDOCKED) != 0) { Preferences::SetString(kOskDebugReason, L"IKPOS: System docked."); - return true; + return false; } // To determine whether a keyboard is present on the device, we do the @@ -697,11 +704,15 @@ IMEHandler::IsKeyboardPresentOnSlate() // 2. If the device supports auto rotation, then we get its platform role // and check the system metric SM_CONVERTIBLESLATEMODE to see if it is - // being used in slate mode. If yes then we return false here to ensure - // that the OSK is displayed. + // being used in slate mode. If not then we return false here to ensure + // that the OSK is not displayed. - // 3. If step 1 and 2 fail then we check attached keyboards and return true - // if we find ACPI\*, HID\VID* or bluetooth keyboards. + // 3. If step 1 and 2 both confirm this *could* be a device with no usable + // keyboard, we check whether the last input was touch-based. If so, + // we return true immediately to get an on-screen keyboard. + + // 4. If this was a mouse or (on-screen) keyboard event, we check if + // this device has keyboards attached to it. typedef BOOL (WINAPI* GetAutoRotationState)(PAR_STATE state); GetAutoRotationState get_rotation_state = @@ -717,11 +728,11 @@ IMEHandler::IsKeyboardPresentOnSlate() if (auto_rotation_state & AR_NOSENSOR) { Preferences::SetString(kOskDebugReason, L"IKPOS: Rotation sensor not found."); - return true; + return false; } else if (auto_rotation_state & AR_NOT_SUPPORTED) { Preferences::SetString(kOskDebugReason, L"IKPOS: Auto-rotation not supported."); - return true; + return false; } } @@ -747,13 +758,13 @@ IMEHandler::IsKeyboardPresentOnSlate() if (sPowerPlatformRole != PlatformRoleMobile && sPowerPlatformRole != PlatformRoleSlate) { Preferences::SetString(kOskDebugReason, L"IKPOS: PlatformRole is neither Mobile nor Slate."); - return true; + return false; } // Likewise, if the tablet/mobile isn't in "slate" mode, we should bail: if (::GetSystemMetrics(SM_CONVERTIBLESLATEMODE) != 0) { Preferences::SetString(kOskDebugReason, L"IKPOS: ConvertibleSlateMode is non-zero"); - return true; + return false; } // Before we check for a keyboard, we should check if the last input was touch, @@ -761,9 +772,20 @@ IMEHandler::IsKeyboardPresentOnSlate() if (sLastContextActionCause == InputContextAction::CAUSE_TOUCH) { Preferences::SetString(kOskDebugReason, L"IKPOS: Used touch to focus control, ignoring keyboard presence"); - return false; + return true; } + return !IMEHandler::IsKeyboardPresentOnSlate(); +} +// Uses the Setup APIs to enumerate the attached keyboards and returns true +// if the keyboard count is 1 or more. While this will work in most cases +// it won't work if there are devices which expose keyboard interfaces which +// are attached to the machine. +// Based on IsKeyboardPresentOnSlate() in Chromium's base/win/win_util.cc. +// static +bool +IMEHandler::IsKeyboardPresentOnSlate() +{ const GUID KEYBOARD_CLASS_GUID = { 0x4D36E96B, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 9df8c87a8dd..fd037e77d59 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -145,6 +145,7 @@ private: static void MaybeDismissOnScreenKeyboard(nsWindow* aWindow); static bool WStringStartsWithCaseInsensitive(const std::wstring& aHaystack, const std::wstring& aNeedle); + static bool NeedOnScreenKeyboard(); static bool IsKeyboardPresentOnSlate(); static bool IsInTabletMode(); static bool AutoInvokeOnScreenKeyboardInDesktopMode(); From 9ade408f907328e0fcf4dd45a3ba9885ce7061f0 Mon Sep 17 00:00:00 2001 From: Dan Minor Date: Mon, 1 Feb 2016 14:41:13 -0500 Subject: [PATCH 020/187] Bug 1244143 - Record whether or not an artifact build was used in build telemetry data r=gps This adds a substs field and cherrypicks the MOZ_ARTIFACT_BUILDS field so we can determine whether or not an artifact build occurred. MozReview-Commit-ID: 8aio8mP8pmR --- python/mozbuild/mozbuild/mach_commands.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 8916aa97267..718a0a42e98 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -495,8 +495,21 @@ class Build(MachCommandBase): telemetry_handler = getattr(self._mach_context, 'telemetry_handler', None) - usage = monitor.record_resource_usage() - telemetry_handler(self._mach_context, usage) + telemetry_data = monitor.record_resource_usage() + + # Record build configuration data. For now, we cherry pick + # items we need rather than grabbing everything, in order + # to avoid accidentally disclosing PII. + try: + moz_artifact_builds = self.substs.get('MOZ_ARTIFACT_BUILDS', + False) + telemetry_data['substs'] = { + 'MOZ_ARTIFACT_BUILDS': moz_artifact_builds, + } + except BuildEnvironmentNotFoundException: + pass + + telemetry_handler(self._mach_context, telemetry_data) # Only for full builds because incremental builders likely don't # need to be burdened with this. From e7ddbfc7cbd1884d2260a4fc3a2752f651897fad Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 11 Feb 2016 08:14:17 -0500 Subject: [PATCH 021/187] Bug 1245925 - Revert cset 2ae6e19e2caf now that a proper fix has landed. r=me --- b2g/app/b2g.js | 1 - 1 file changed, 1 deletion(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index efee29c72c2..b76bcd5dc20 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1015,7 +1015,6 @@ pref("security.exthelperapp.disable_background_handling", true); // Inactivity time in milliseconds after which we shut down the OS.File worker. pref("osfile.reset_worker_delay", 5000); -pref("apz.displayport_expiry_ms", 0); // APZ physics settings, tuned by UX designers pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking pref("apz.fling_curve_function_x1", "0.41"); From 64ca196ec524c9baf1a9dab17e3a4d61009320fb Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 11 Feb 2016 08:22:39 -0500 Subject: [PATCH 022/187] Bug 1247364 - add AllChildrenIterator::Seek, r=bz --- dom/base/ChildIterator.cpp | 37 ++++++++++++++++++++++++++++++++++--- dom/base/ChildIterator.h | 4 +++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp index 13dd9fc66f4..9557d1ed1af 100644 --- a/dom/base/ChildIterator.cpp +++ b/dom/base/ChildIterator.cpp @@ -190,7 +190,7 @@ FlattenedChildIterator::Init(bool aIgnoreXBL) } } -void +bool ExplicitChildIterator::Seek(nsIContent* aChildToFind) { if (aChildToFind->GetParent() == mParent && @@ -204,14 +204,14 @@ ExplicitChildIterator::Seek(nsIContent* aChildToFind) mShadowIterator = nullptr; mDefaultChild = nullptr; mIsFirst = false; - return; + return true; } // Can we add more fast paths here based on whether the parent of aChildToFind // is a shadow insertion point or content insertion point? // Slow path: just walk all our kids. - Seek(aChildToFind, nullptr); + return Seek(aChildToFind, nullptr); } nsIContent* @@ -311,6 +311,37 @@ ExplicitChildIterator::GetPreviousChild() return mChild; } +bool +AllChildrenIterator::Seek(nsIContent* aChildToFind) +{ + if (mPhase == eNeedBeforeKid) { + mPhase = eNeedExplicitKids; + nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); + if (frame) { + nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); + if (beforeFrame) { + if (beforeFrame->GetContent() == aChildToFind) { + return true; + } + } + } + } + + if (mPhase == eNeedExplicitKids) { + if (ExplicitChildIterator::Seek(aChildToFind)) { + return true; + } + mPhase = eNeedAnonKids; + } + + nsIContent* child = nullptr; + do { + child = GetNextChild(); + } while (child && child != aChildToFind); + + return child == aChildToFind; +} + nsIContent* AllChildrenIterator::GetNextChild() { diff --git a/dom/base/ChildIterator.h b/dom/base/ChildIterator.h index dbe6def3cf6..d7c0c4c8962 100644 --- a/dom/base/ChildIterator.h +++ b/dom/base/ChildIterator.h @@ -64,7 +64,7 @@ public: // found. This version can take shortcuts that the two-argument version // can't, so can be faster (and in fact can be O(1) instead of O(N) in many // cases). - void Seek(nsIContent* aChildToFind); + bool Seek(nsIContent* aChildToFind); // Looks for aChildToFind respecting insertion points until aChildToFind is found. // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns @@ -199,6 +199,8 @@ public: ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); } #endif + bool Seek(nsIContent* aChildToFind); + nsIContent* GetNextChild(); nsIContent* Parent() const { return mOriginalContent; } From e9f6da0b5c5861e0a13f1c620a6d620d648f1a47 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 11 Feb 2016 08:24:38 -0500 Subject: [PATCH 023/187] Bug 1247364 - use AllChildrenIterator::Seek by a11y tree walker, r=davidb --- accessible/base/TreeWalker.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/accessible/base/TreeWalker.cpp b/accessible/base/TreeWalker.cpp index 365477afa33..e48b1e85e13 100644 --- a/accessible/base/TreeWalker.cpp +++ b/accessible/base/TreeWalker.cpp @@ -80,11 +80,9 @@ TreeWalker::NextChild() nsIContent* parent = parentNode->AsElement(); top = PushState(parent); - while (nsIContent* childNode = Next(top)) { - if (childNode == mAnchorNode) { - mAnchorNode = parent; - return NextChild(); - } + if (top->mDOMIter.Seek(mAnchorNode)) { + mAnchorNode = parent; + return NextChild(); } // XXX We really should never get here, it means we're trying to find an From 8825708b0c0679e81c58d6b84399a637721fbf34 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:41:37 +0000 Subject: [PATCH 024/187] Bug 1245153 - error.isError must recognise built-in Error prototypes; r=automatedtester Due to a previous programming error, error.isError only recognised the base Error prototype. It must also test for the other built-in prototypes, such as TypeError et al. MozReview-Commit-ID: HLkiOAg0Jl1 --- testing/marionette/error.js | 21 ++++++++++++++++----- testing/marionette/test_error.js | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/testing/marionette/error.js b/testing/marionette/error.js index fb00c40b2b1..66f9f612881 100644 --- a/testing/marionette/error.js +++ b/testing/marionette/error.js @@ -6,7 +6,7 @@ const {interfaces: Ci, utils: Cu} = Components; -const errors = [ +const ERRORS = [ "ElementNotAccessibleError", "ElementNotVisibleError", "InvalidArgumentError", @@ -29,10 +29,21 @@ const errors = [ "WebDriverError", ]; -this.EXPORTED_SYMBOLS = ["error"].concat(errors); +this.EXPORTED_SYMBOLS = ["error"].concat(ERRORS); this.error = {}; +error.BuiltinErrors = { + Error: 0, + EvalError: 1, + InternalError: 2, + RangeError: 3, + ReferenceError: 4, + SyntaxError: 5, + TypeError: 6, + URIError: 7, +}; + /** * Checks if obj is an instance of the Error prototype in a safe manner. * Prefer using this over using instanceof since the Error prototype @@ -50,7 +61,7 @@ error.isError = function(val) { } else if (val instanceof Ci.nsIException) { return true; } else { - return Object.getPrototypeOf(val) == "Error"; + return Object.getPrototypeOf(val) in error.BuiltinErrors; } }; @@ -59,7 +70,7 @@ error.isError = function(val) { */ error.isWebDriverError = function(obj) { return error.isError(obj) && - ("name" in obj && errors.indexOf(obj.name) >= 0); + ("name" in obj && ERRORS.indexOf(obj.name) >= 0); }; /** @@ -328,7 +339,7 @@ UnsupportedOperationError.prototype = Object.create(WebDriverError.prototype); const nameLookup = new Map(); const statusLookup = new Map(); -for (let s of errors) { +for (let s of ERRORS) { let cls = this[s]; let inst = new cls(); nameLookup.set(inst.name, cls); diff --git a/testing/marionette/test_error.js b/testing/marionette/test_error.js index 2ee7c18996b..9b65d6242bf 100644 --- a/testing/marionette/test_error.js +++ b/testing/marionette/test_error.js @@ -14,6 +14,19 @@ function notok(condition) { ok(!(condition)); } +add_test(function test_BuiltinErrors() { + ok("Error" in error.BuiltinErrors); + ok("EvalError" in error.BuiltinErrors); + ok("InternalError" in error.BuiltinErrors); + ok("RangeError" in error.BuiltinErrors); + ok("ReferenceError" in error.BuiltinErrors); + ok("SyntaxError" in error.BuiltinErrors); + ok("TypeError" in error.BuiltinErrors); + ok("URIError" in error.BuiltinErrors); + + run_next_test(); +}); + add_test(function test_isError() { notok(error.isError(null)); notok(error.isError([])); @@ -21,6 +34,13 @@ add_test(function test_isError() { ok(error.isError(new Components.Exception())); ok(error.isError(new Error())); + ok(error.isError(new EvalError())); + ok(error.isError(new InternalError())); + ok(error.isError(new RangeError())); + ok(error.isError(new ReferenceError())); + ok(error.isError(new SyntaxError())); + ok(error.isError(new TypeError())); + ok(error.isError(new URIError())); ok(error.isError(new WebDriverError())); ok(error.isError(new InvalidArgumentError())); @@ -30,6 +50,13 @@ add_test(function test_isError() { add_test(function test_isWebDriverError() { notok(error.isWebDriverError(new Components.Exception())); notok(error.isWebDriverError(new Error())); + notok(error.isWebDriverError(new EvalError())); + notok(error.isWebDriverError(new InternalError())); + notok(error.isWebDriverError(new RangeError())); + notok(error.isWebDriverError(new ReferenceError())); + notok(error.isWebDriverError(new SyntaxError())); + notok(error.isWebDriverError(new TypeError())); + notok(error.isWebDriverError(new URIError())); ok(error.isWebDriverError(new WebDriverError())); ok(error.isWebDriverError(new InvalidArgumentError())); From 76045c0fafe8b03318b28adba468ca15b4547681 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:43:37 +0000 Subject: [PATCH 025/187] Bug 1245153 - Add error.wrap to wrap Error prototypes; r=automatedtester Generally, Error prototypes that are not based on WebDriverError must be wrapped so that they can be serialised across the AsyncMessageChannel. MozReview-Commit-ID: EtkpEOBhrST --- testing/marionette/error.js | 14 +++++++++++++- testing/marionette/test_error.js | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/testing/marionette/error.js b/testing/marionette/error.js index 66f9f612881..01a31d49813 100644 --- a/testing/marionette/error.js +++ b/testing/marionette/error.js @@ -73,6 +73,17 @@ error.isWebDriverError = function(obj) { ("name" in obj && ERRORS.indexOf(obj.name) >= 0); }; +/** + * Wraps an Error prototype in a WebDriverError. If the given error is + * already a WebDriverError, this is effectively a no-op. + */ +error.wrap = function(err) { + if (error.isWebDriverError(err)) { + return err; + } + return new WebDriverError(`${err.name}: ${err.message}`, err.stack); +}; + /** * Unhandled error reporter. Dumps the error and its stacktrace to console, * and reports error to the Browser Console. @@ -151,11 +162,12 @@ error.fromJson = function(json) { * It should not be used directly, as it does not correspond to a real * error in the specification. */ -this.WebDriverError = function(msg) { +this.WebDriverError = function(msg, stack = undefined) { Error.call(this, msg); this.name = "WebDriverError"; this.message = msg; this.status = "webdriver error"; + this.stack = stack; }; WebDriverError.prototype = Object.create(Error.prototype); diff --git a/testing/marionette/test_error.js b/testing/marionette/test_error.js index 9b65d6242bf..c20b93aec51 100644 --- a/testing/marionette/test_error.js +++ b/testing/marionette/test_error.js @@ -64,6 +64,21 @@ add_test(function test_isWebDriverError() { run_next_test(); }); +add_test(function test_wrap() { + equal(error.wrap(new WebDriverError()).name, "WebDriverError"); + equal(error.wrap(new InvalidArgumentError()).name, "InvalidArgumentError"); + equal(error.wrap(new Error()).name, "WebDriverError"); + equal(error.wrap(new EvalError()).name, "WebDriverError"); + equal(error.wrap(new InternalError()).name, "WebDriverError"); + equal(error.wrap(new RangeError()).name, "WebDriverError"); + equal(error.wrap(new ReferenceError()).name, "WebDriverError"); + equal(error.wrap(new SyntaxError()).name, "WebDriverError"); + equal(error.wrap(new TypeError()).name, "WebDriverError"); + equal(error.wrap(new URIError()).name, "WebDriverError"); + + run_next_test(); +}); + add_test(function test_stringify() { equal("", error.stringify()); equal("", error.stringify("foo")); From 8db2daad1038ec4d86ced4a54379de5c970bf066 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:44:55 +0000 Subject: [PATCH 026/187] Bug 1245153 - Wrap errors before they are passed through the IPC channel; r=automatedtester error.wrap acts as a no-op if it is passed a prototype which is already of the WebDriverError prototypal chain. MozReview-Commit-ID: Gd9kUEvsgNv --- testing/marionette/proxy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/marionette/proxy.js b/testing/marionette/proxy.js index 1a294394f1f..0be8b35e9d7 100644 --- a/testing/marionette/proxy.js +++ b/testing/marionette/proxy.js @@ -171,7 +171,8 @@ proxy.AsyncMessageChannel = class { if (typeof obj == "undefined") { this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Ok); } else if (error.isError(obj)) { - let serr = error.toJson(obj); + let err = error.wrap(obj); + let serr = error.toJson(err); this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Error, serr); } else { this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Value, obj); From ff63613475011367d93e145bf5c827c2f25f01f5 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:47:08 +0000 Subject: [PATCH 027/187] Bug 1245153 - Convert EventUtils.js to a module; r=automatedtester testing/marionette/sendkeys.js has been merged into the new testing/marionette/event.js module, together with testing/marionette/EventUtils.js. There is a lot of functionality still left in this module that we can probably remove, as it is not in use by Marionette. MozReview-Commit-ID: GrjNuK9VPjp --- testing/marionette/EventUtils.js | 673 ---------------------- testing/marionette/event.js | 954 +++++++++++++++++++++++++++++++ testing/marionette/sendkeys.js | 165 ------ 3 files changed, 954 insertions(+), 838 deletions(-) delete mode 100644 testing/marionette/EventUtils.js create mode 100644 testing/marionette/event.js delete mode 100644 testing/marionette/sendkeys.js diff --git a/testing/marionette/EventUtils.js b/testing/marionette/EventUtils.js deleted file mode 100644 index 0a0e5f052cb..00000000000 --- a/testing/marionette/EventUtils.js +++ /dev/null @@ -1,673 +0,0 @@ -/* 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/. */ - -/** - * EventUtils provides some utility methods for creating and sending DOM events. - * Current methods: - * sendMouseEvent - * sendChar - * sendString - * sendKey - * synthesizeMouse - * synthesizeMouseAtCenter - * synthesizeMouseScroll - * synthesizeKey - * synthesizeMouseExpectEvent - * synthesizeKeyExpectEvent - * - * When adding methods to this file, please add a performance test for it. - */ - -/** - * Send a mouse event to the node aTarget (aTarget can be an id, or an - * actual node) . The "event" passed in to aEvent is just a JavaScript - * object with the properties set that the real mouse event object should - * have. This includes the type of the mouse event. - * E.g. to send an click event to the node with id 'node' you might do this: - * - * sendMouseEvent({type:'click'}, 'node'); - */ -function getElement(id) { - return ((typeof(id) == "string") ? - document.getElementById(id) : id); -}; - -this.$ = this.getElement; -const KeyEvent = Components.interfaces.nsIDOMKeyEvent; - -function sendMouseEvent(aEvent, aTarget, aWindow) { - if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) { - throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'"); - } - - if (!aWindow) { - aWindow = window; - } - - if (!(aTarget instanceof Element)) { - aTarget = aWindow.document.getElementById(aTarget); - } - - var event = aWindow.document.createEvent('MouseEvent'); - - var typeArg = aEvent.type; - var canBubbleArg = true; - var cancelableArg = true; - var viewArg = aWindow; - var detailArg = aEvent.detail || (aEvent.type == 'click' || - aEvent.type == 'mousedown' || - aEvent.type == 'mouseup' ? 1 : - aEvent.type == 'dblclick'? 2 : 0); - var screenXArg = aEvent.screenX || 0; - var screenYArg = aEvent.screenY || 0; - var clientXArg = aEvent.clientX || 0; - var clientYArg = aEvent.clientY || 0; - var ctrlKeyArg = aEvent.ctrlKey || false; - var altKeyArg = aEvent.altKey || false; - var shiftKeyArg = aEvent.shiftKey || false; - var metaKeyArg = aEvent.metaKey || false; - var buttonArg = aEvent.button || 0; - var relatedTargetArg = aEvent.relatedTarget || null; - - event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, - screenXArg, screenYArg, clientXArg, clientYArg, - ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, - buttonArg, relatedTargetArg); - - //removed: SpecialPowers.dispatchEvent(aWindow, aTarget, event); -} - -/** - * Send the char aChar to the focused element. This method handles casing of - * chars (sends the right charcode, and sends a shift key for uppercase chars). - * No other modifiers are handled at this point. - * - * For now this method only works for English letters (lower and upper case) - * and the digits 0-9. - */ -function sendChar(aChar, aWindow) { - // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9. - var hasShift = (aChar == aChar.toUpperCase()); - synthesizeKey(aChar, { shiftKey: hasShift }, aWindow); -} - -/** - * Send the string aStr to the focused element. - * - * For now this method only works for English letters (lower and upper case) - * and the digits 0-9. - */ -function sendString(aStr, aWindow) { - for (var i = 0; i < aStr.length; ++i) { - sendChar(aStr.charAt(i), aWindow); - } -} - -/** - * Send the non-character key aKey to the focused node. - * The name of the key should be the part that comes after "DOM_VK_" in the - * KeyEvent constant name for this key. - * No modifiers are handled at this point. - */ -function sendKey(aKey, aWindow) { - var keyName = "VK_" + aKey.toUpperCase(); - synthesizeKey(keyName, { shiftKey: false }, aWindow); -} - -/** - * Parse the key modifier flags from aEvent. Used to share code between - * synthesizeMouse and synthesizeKey. - */ -function _parseModifiers(aEvent) -{ - const masks = Components.interfaces.nsIDOMNSEvent; - var mval = 0; - if (aEvent.shiftKey) - mval |= masks.SHIFT_MASK; - if (aEvent.ctrlKey) - mval |= masks.CONTROL_MASK; - if (aEvent.altKey) - mval |= masks.ALT_MASK; - if (aEvent.metaKey) - mval |= masks.META_MASK; - if (aEvent.accelKey) - mval |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK : - masks.CONTROL_MASK; - - return mval; -} - -/** - * Synthesize a mouse event on a target. The actual client point is determined - * by taking the aTarget's client box and offseting it by aOffsetX and - * aOffsetY. This allows mouse clicks to be simulated by calling this method. - * - * aEvent is an object which may contain the properties: - * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type - * - * If the type is specified, an mouse event of that type is fired. Otherwise, - * a mousedown followed by a mouse up is performed. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) -{ - var rect = aTarget.getBoundingClientRect(); - synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, - aEvent, aWindow); -} - -/* - * Synthesize a mouse event at a particular point in aWindow. - * - * aEvent is an object which may contain the properties: - * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type - * - * If the type is specified, an mouse event of that type is fired. Otherwise, - * a mousedown followed by a mouse up is performed. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeMouseAtPoint(left, top, aEvent, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - - if (utils) { - var button = aEvent.button || 0; - var clickCount = aEvent.clickCount || 1; - var modifiers = _parseModifiers(aEvent); - - if (("type" in aEvent) && aEvent.type) { - utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers); - } - else { - utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers); - utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers); - } - } -} - -// Call synthesizeMouse with coordinates at the center of aTarget. -function synthesizeMouseAtCenter(aTarget, aEvent, aWindow) -{ - var rect = aTarget.getBoundingClientRect(); - synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent, - aWindow); -} - -/** - * Synthesize a mouse scroll event on a target. The actual client point is determined - * by taking the aTarget's client box and offseting it by aOffsetX and - * aOffsetY. - * - * aEvent is an object which may contain the properties: - * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels - * - * If the type is specified, a mouse scroll event of that type is fired. Otherwise, - * "DOMMouseScroll" is used. - * - * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified, - * "vertical" is used. - * - * 'delta' is the amount to scroll by (can be positive or negative). It must - * be specified. - * - * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags. - * - * 'isMomentum' specifies whether kIsMomentum should be set in the scrollFlags. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - - if (utils) { - // See nsMouseScrollFlags in nsGUIEvent.h - const kIsVertical = 0x02; - const kIsHorizontal = 0x04; - const kHasPixels = 0x08; - const kIsMomentum = 0x40; - - var button = aEvent.button || 0; - var modifiers = _parseModifiers(aEvent); - - var rect = aTarget.getBoundingClientRect(); - - var left = rect.left; - var top = rect.top; - - var type = (("type" in aEvent) && aEvent.type) || "DOMMouseScroll"; - var axis = aEvent.axis || "vertical"; - var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; - if (aEvent.hasPixels) { - scrollFlags |= kHasPixels; - } - if (aEvent.isMomentum) { - scrollFlags |= kIsMomentum; - } - utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button, - scrollFlags, aEvent.delta, modifiers); - } -} - -function _computeKeyCodeFromChar(aChar) -{ - if (aChar.length != 1) { - return 0; - } - const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent; - if (aChar >= 'a' && aChar <= 'z') { - return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); - } - if (aChar >= 'A' && aChar <= 'Z') { - return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); - } - if (aChar >= '0' && aChar <= '9') { - return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); - } - // returns US keyboard layout's keycode - switch (aChar) { - case '~': - case '`': - return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; - case '!': - return nsIDOMKeyEvent.DOM_VK_1; - case '@': - return nsIDOMKeyEvent.DOM_VK_2; - case '#': - return nsIDOMKeyEvent.DOM_VK_3; - case '$': - return nsIDOMKeyEvent.DOM_VK_4; - case '%': - return nsIDOMKeyEvent.DOM_VK_5; - case '^': - return nsIDOMKeyEvent.DOM_VK_6; - case '&': - return nsIDOMKeyEvent.DOM_VK_7; - case '*': - return nsIDOMKeyEvent.DOM_VK_8; - case '(': - return nsIDOMKeyEvent.DOM_VK_9; - case ')': - return nsIDOMKeyEvent.DOM_VK_0; - case '-': - case '_': - return nsIDOMKeyEvent.DOM_VK_SUBTRACT; - case '+': - case '=': - return nsIDOMKeyEvent.DOM_VK_EQUALS; - case '{': - case '[': - return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; - case '}': - case ']': - return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; - case '|': - case '\\': - return nsIDOMKeyEvent.DOM_VK_BACK_SLASH; - case ':': - case ';': - return nsIDOMKeyEvent.DOM_VK_SEMICOLON; - case '\'': - case '"': - return nsIDOMKeyEvent.DOM_VK_QUOTE; - case '<': - case ',': - return nsIDOMKeyEvent.DOM_VK_COMMA; - case '>': - case '.': - return nsIDOMKeyEvent.DOM_VK_PERIOD; - case '?': - case '/': - return nsIDOMKeyEvent.DOM_VK_SLASH; - case '\n': - return nsIDOMKeyEvent.DOM_VK_RETURN; - default: - return 0; - } -} - -/** - * isKeypressFiredKey() returns TRUE if the given key should cause keypress - * event when widget handles the native key event. Otherwise, FALSE. - * - * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key - * name begins with "VK_", or a character. - */ -function isKeypressFiredKey(aDOMKeyCode) -{ - const KeyEvent = Components.interfaces.nsIDOMKeyEvent; - if (typeof(aDOMKeyCode) == "string") { - if (aDOMKeyCode.indexOf("VK_") == 0) { - aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode]; - if (!aDOMKeyCode) { - throw "Unknown key: " + aDOMKeyCode; - } - } else { - // If the key generates a character, it must cause a keypress event. - return true; - } - } - switch (aDOMKeyCode) { - case KeyEvent.DOM_VK_SHIFT: - case KeyEvent.DOM_VK_CONTROL: - case KeyEvent.DOM_VK_ALT: - case KeyEvent.DOM_VK_CAPS_LOCK: - case KeyEvent.DOM_VK_NUM_LOCK: - case KeyEvent.DOM_VK_SCROLL_LOCK: - case KeyEvent.DOM_VK_META: - return false; - default: - return true; - } -} - -/** - * Synthesize a key event. It is targeted at whatever would be targeted by an - * actual keypress by the user, typically the focused element. - * - * aKey should be either a character or a keycode starting with VK_ such as - * VK_RETURN. - * - * aEvent is an object which may contain the properties: - * shiftKey, ctrlKey, altKey, metaKey, accessKey, type - * - * If the type is specified, a key event of that type is fired. Otherwise, - * a keydown, a keypress and then a keyup event are fired in sequence. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeKey(aKey, aEvent, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (utils) { - var keyCode = 0, charCode = 0; - if (aKey.indexOf("VK_") == 0) { - keyCode = KeyEvent["DOM_" + aKey]; - if (!keyCode) { - throw "Unknown key: " + aKey; - } - } else { - charCode = aKey.charCodeAt(0); - keyCode = _computeKeyCodeFromChar(aKey.charAt(0)); - } - - var modifiers = _parseModifiers(aEvent); - - if (!("type" in aEvent) || !aEvent.type) { - // Send keydown + (optional) keypress + keyup events. - var keyDownDefaultHappened = - utils.sendKeyEvent("keydown", keyCode, 0, modifiers); - if (isKeypressFiredKey(keyCode)) { - utils.sendKeyEvent("keypress", charCode ? 0 : keyCode, charCode, - modifiers, !keyDownDefaultHappened); - } - utils.sendKeyEvent("keyup", keyCode, 0, modifiers); - } else if (aEvent.type == "keypress") { - // Send standalone keypress event. - utils.sendKeyEvent(aEvent.type, charCode ? 0 : keyCode, - charCode, modifiers); - } else { - // Send other standalone event than keypress. - utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers); - } - } -} - -var _gSeenEvent = false; - -/** - * Indicate that an event with an original target of aExpectedTarget and - * a type of aExpectedEvent is expected to be fired, or not expected to - * be fired. - */ -function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName) -{ - if (!aExpectedTarget || !aExpectedEvent) - return null; - - _gSeenEvent = false; - - var type = (aExpectedEvent.charAt(0) == "!") ? - aExpectedEvent.substring(1) : aExpectedEvent; - var eventHandler = function(event) { - var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget && - event.type == type); - is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : "")); - _gSeenEvent = true; - }; - - aExpectedTarget.addEventListener(type, eventHandler, false); - return eventHandler; -} - -/** - * Check if the event was fired or not. The event handler aEventHandler - * will be removed. - */ -function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName) -{ - if (aEventHandler) { - var expectEvent = (aExpectedEvent.charAt(0) != "!"); - var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1); - aExpectedTarget.removeEventListener(type, aEventHandler, false); - var desc = type + " event"; - if (!expectEvent) - desc += " not"; - is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired"); - } - - _gSeenEvent = false; -} - -/** - * Similar to synthesizeMouse except that a test is performed to see if an - * event is fired at the right target as a result. - * - * aExpectedTarget - the expected originalTarget of the event. - * aExpectedEvent - the expected type of the event, such as 'select'. - * aTestName - the test name when outputing results - * - * To test that an event is not fired, use an expected type preceded by an - * exclamation mark, such as '!select'. This might be used to test that a - * click on a disabled element doesn't fire certain events for instance. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent, - aExpectedTarget, aExpectedEvent, aTestName, - aWindow) -{ - var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); - synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow); - _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); -} - -/** - * Similar to synthesizeKey except that a test is performed to see if an - * event is fired at the right target as a result. - * - * aExpectedTarget - the expected originalTarget of the event. - * aExpectedEvent - the expected type of the event, such as 'select'. - * aTestName - the test name when outputing results - * - * To test that an event is not fired, use an expected type preceded by an - * exclamation mark, such as '!select'. - * - * aWindow is optional, and defaults to the current window object. - */ -function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent, - aTestName, aWindow) -{ - var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); - synthesizeKey(key, aEvent, aWindow); - _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); -} - -function disableNonTestMouseEvents(aDisable) -{ - var domutils = _getDOMWindowUtils(); - domutils.disableNonTestMouseEvents(aDisable); -} - -function _getDOMWindowUtils(aWindow) -{ - if (!aWindow) { - aWindow = window; - } - - //TODO: this is assuming we are in chrome space - return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). - getInterface(Components.interfaces.nsIDOMWindowUtils); -} - -// Must be synchronized with nsIDOMWindowUtils. -const COMPOSITION_ATTR_RAWINPUT = 0x02; -const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; -const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; -const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; - -/** - * Synthesize a composition event. - * - * @param aEvent The composition event information. This must - * have |type| member. The value must be - * "compositionstart", "compositionend" or - * "compositionupdate". - * And also this may have |data| and |locale| which - * would be used for the value of each property of - * the composition event. Note that the data would - * be ignored if the event type were - * "compositionstart". - * @param aWindow Optional (If null, current |window| will be used) - */ -function synthesizeComposition(aEvent, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return; - } - - utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "", - aEvent.locale ? aEvent.locale : ""); -} -/** - * Synthesize a text event. - * - * @param aEvent The text event's information, this has |composition| - * and |caret| members. |composition| has |string| and - * |clauses| members. |clauses| must be array object. Each - * object has |length| and |attr|. And |caret| has |start| and - * |length|. See the following tree image. - * - * aEvent - * +-- composition - * | +-- string - * | +-- clauses[] - * | +-- length - * | +-- attr - * +-- caret - * +-- start - * +-- length - * - * Set the composition string to |composition.string|. Set its - * clauses information to the |clauses| array. - * - * When it's composing, set the each clauses' length to the - * |composition.clauses[n].length|. The sum of the all length - * values must be same as the length of |composition.string|. - * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the - * |composition.clauses[n].attr|. - * - * When it's not composing, set 0 to the - * |composition.clauses[0].length| and - * |composition.clauses[0].attr|. - * - * Set caret position to the |caret.start|. It's offset from - * the start of the composition string. Set caret length to - * |caret.length|. If it's larger than 0, it should be wide - * caret. However, current nsEditor doesn't support wide - * caret, therefore, you should always set 0 now. - * - * @param aWindow Optional (If null, current |window| will be used) - */ -function synthesizeText(aEvent, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return; - } - - if (!aEvent.composition || !aEvent.composition.clauses || - !aEvent.composition.clauses[0]) { - return; - } - - var firstClauseLength = aEvent.composition.clauses[0].length; - var firstClauseAttr = aEvent.composition.clauses[0].attr; - var secondClauseLength = 0; - var secondClauseAttr = 0; - var thirdClauseLength = 0; - var thirdClauseAttr = 0; - if (aEvent.composition.clauses[1]) { - secondClauseLength = aEvent.composition.clauses[1].length; - secondClauseAttr = aEvent.composition.clauses[1].attr; - if (aEvent.composition.clauses[2]) { - thirdClauseLength = aEvent.composition.clauses[2].length; - thirdClauseAttr = aEvent.composition.clauses[2].attr; - } - } - - var caretStart = -1; - var caretLength = 0; - if (aEvent.caret) { - caretStart = aEvent.caret.start; - caretLength = aEvent.caret.length; - } - - utils.sendTextEvent(aEvent.composition.string, - firstClauseLength, firstClauseAttr, - secondClauseLength, secondClauseAttr, - thirdClauseLength, thirdClauseAttr, - caretStart, caretLength); -} - -/** - * Synthesize a query selected text event. - * - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeQuerySelectedText(aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return null; - } - - return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0); -} - -/** - * Synthesize a selection set event. - * - * @param aOffset The character offset. 0 means the first character in the - * selection root. - * @param aLength The length of the text. If the length is too long, - * the extra length is ignored. - * @param aReverse If true, the selection is from |aOffset + aLength| to - * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|. - * @param aWindow Optional (If null, current |window| will be used) - * @return True, if succeeded. Otherwise false. - */ -function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return false; - } - return utils.sendSelectionSetEvent(aOffset, aLength, aReverse); -} diff --git a/testing/marionette/event.js b/testing/marionette/event.js new file mode 100644 index 00000000000..054c7e6881f --- /dev/null +++ b/testing/marionette/event.js @@ -0,0 +1,954 @@ +/* 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/. */ + +// Provides functionality for creating and sending DOM events. + +"use strict"; + +const {interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Log.jsm"); +const logger = Log.repository.getLogger("Marionette"); + +Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/error.js"); + +this.EXPORTED_SYMBOLS = ["event"]; + +// must be synchronised with nsIDOMWindowUtils +const COMPOSITION_ATTR_RAWINPUT = 0x02; +const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; +const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; +const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; + +// TODO(ato): Document! +let seenEvent = false; + +function getDOMWindowUtils(win) { + if (!win) { + win = window; + } + + // this assumes we are operating in chrome space + return win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); +} + +this.event = {}; + +event.MouseEvents = { + click: 0, + dblclick: 1, + mousedown: 2, + mouseup: 3, + mouseover: 4, + mouseout: 5, +}; + +event.Modifiers = { + shiftKey: 0, + ctrlKey: 1, + altKey: 2, + metaKey: 3, +}; + +/** + * Sends a mouse event to given target. + * + * @param {nsIDOMMouseEvent} mouseEvent + * Event to send. + * @param {(Element|string)} target + * Target of event. Can either be an Element or the ID of an element. + * @param {Window=} window + * Window object. Defaults to the current window. + * + * @throws {TypeError} + * If the event is unsupported. + */ +event.sendMouseEvent = function(mouseEvent, target, window = undefined) { + if (event.MouseEvents.hasOwnProperty(mouseEvent.type)) { + throw new TypeError("Unsupported event type: " + mouseEvent.type); + } + + if (!(target instanceof Element)) { + target = window.document.getElementById(target); + } + + let ev = window.document.createEvent("MouseEvent"); + + let type = mouseEvent.type; + let view = window; + + let detail = mouseEvent.detail; + if (!detail) { + if (mouseEvent.type in ["click", "mousedown", "mouseup"]) { + detail = 1; + } else if (mouseEvent.type == "dblclick") { + detail = 2; + } else { + detail = 0; + } + } + + let screenX = mouseEvent.screenX || 0; + let screenY = mouseEvent.screenY || 0; + let clientX = mouseEvent.clientX || 0; + let clientY = mouseEvent.clientY || 0; + let ctrlKey = mouseEvent.ctrlKey || false; + let altKey = mouseEvent.altKey || false; + let shiftKey = mouseEvent.shiftKey || false; + let metaKey = mouseEvent.metaKey || false; + let button = mouseEvent.button || 0; + let relatedTarget = mouseEvent.relatedTarget || null; + + ev.initMouseEvent( + mouseEvent.type, + /* canBubble */ true, + /* cancelable */ true, + view, + detail, + screenX, + screenY, + clientX, + clientY, + ctrlKey, + altKey, + shiftKey, + metaKey, + button, + relatedTarget); +}; + +/** + * Send character to the currently focused element. + * + * This function handles casing of characters (sends the right charcode, + * and sends a shift key for uppercase chars). No other modifiers are + * handled at this point. + * + * For now this method only works for English letters (lower and upper + * case) and the digits 0-9. + */ +event.sendChar = function(char, window = undefined) { + // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9 + let hasShift = (char == char.toUpperCase()); + event.synthesizeKey(char, {shiftKey: hasShift}, window); +}; + +/** + * Send string to the focused element. + * + * For now this method only works for English letters (lower and upper + * case) and the digits 0-9. + */ +event.sendString = function(string, window = undefined) { + for (let i = 0; i < string.length; ++i) { + event.sendChar(string.charAt(i), window); + } +}; + +/** + * Send the non-character key to the focused element. + * + * The name of the key should be the part that comes after "DOM_VK_" + * in the nsIDOMKeyEvent constant name for this key. No modifiers are + * handled at this point. + */ +event.sendKey = function(key, window = undefined) { + let keyName = "VK_" + key.toUpperCase(); + event.synthesizeKey(keyName, {shiftKey: false}, window); +}; + +// TODO(ato): Unexpose this when actions.Chain#emitMouseEvent +// no longer emits its own events +event.parseModifiers_ = function(event) { + let mval = 0; + if (event.shiftKey) { + mval |= Ci.nsIDOMNSEvent.SHIFT_MASK; + } + if (event.ctrlKey) { + mval |= Ci.nsIDOMNSEvent.CONTROL_MASK; + } + if (event.altKey) { + mval |= Ci.nsIDOMNSEvent.ALT_MASK; + } + if (event.metaKey) { + mval |= Ci.nsIDOMNSEvent.META_MASK; + } + if (event.accelKey) { + if (navigator.platform.indexOf("Mac") >= 0) { + mval |= Ci.nsIDOMNSEvent.META_MASK; + } else { + mval |= Ci.nsIDOMNSEvent.CONTROL_MASK; + } + } + return mval; +}; + +/** + * Synthesise a mouse event on a target. + * + * The actual client point is determined by taking the aTarget's client + * box and offseting it by offsetX and offsetY. This allows mouse clicks + * to be simulated by calling this method. + * + * If the type is specified, an mouse event of that type is + * fired. Otherwise, a mousedown followed by a mouse up is performed. + * + * @param {Element} element + * Element to click. + * @param {number} offsetX + * Horizontal offset to click from the target's bounding box. + * @param {number} offsetY + * Vertical offset to click from the target's bounding box. + * @param {Object.} opts + * Object which may contain the properties "shiftKey", "ctrlKey", + * "altKey", "metaKey", "accessKey", "clickCount", "button", and + * "type". + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeMouse = function( + element, offsetX, offsetY, opts, window = undefined) { + let rect = element.getBoundingClientRect(); + event.synthesizeMouseAtPoint( + rect.left + offsetX, rect.top + offsetY, opts, window); +}; + +/* + * Synthesize a mouse event at a particular point in a window. + * + * If the type of the event is specified, a mouse event of that type is + * fired. Otherwise, a mousedown followed by a mouse up is performed. + * + * @param {number} left + * CSS pixels from the left document margin. + * @param {number} top + * CSS pixels from the top document margin. + * @param {Object.} event + * Object which may contain the properties "shiftKey", "ctrlKey", + * "altKey", "metaKey", "accessKey", "clickCount", "button", and + * "type". + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeMouseAtPoint = function( + left, top, opts, window = undefined) { + + let domutils = getDOMWindowUtils(window); + + let button = event.button || 0; + let clickCount = event.clickCount || 1; + let modifiers = event.parseModifiers_(event); + + if (("type" in event) && event.type) { + domutils.sendMouseEvent( + event.type, left, top, button, clickCount, modifiers); + } else { + domutils.sendMouseEvent( + "mousedown", left, top, button, clickCount, modifiers); + domutils.sendMouseEvent( + "mouseup", left, top, button, clickCount, modifiers); + } +}; + +/** + * Call event.synthesizeMouse with coordinates at the centre of the + * target. + */ +event.synthesizeMouseAtCenter = function(element, event, window) { + let rect = element.getBoundingClientRect(); + event.synthesizeMouse( + element, + rect.width / 2, + rect.height / 2, + event, + window); +}; + +/** + * Synthesise a mouse scroll event on a target. + * + * The actual client point is determined by taking the target's client + * box and offseting it by |offsetX| and |offsetY|. + * + * If the |type| property is specified for the |event| argument, a mouse + * scroll event of that type is fired. Otherwise, DOMMouseScroll is used. + * + * If the |axis| is specified, it must be one of "horizontal" or + * "vertical". If not specified, "vertical" is used. + * + * |delta| is the amount to scroll by (can be positive or negative). + * It must be specified. + * + * |hasPixels| specifies whether kHasPixels should be set in the + * |scrollFlags|. + * + * |isMomentum| specifies whether kIsMomentum should be set in the + * |scrollFlags|. + * + * @param {Element} target + * @param {number} offsetY + * @param {number} offsetY + * @param {Object.} event + * Object which may contain the properties shiftKey, ctrlKey, altKey, + * metaKey, accessKey, button, type, axis, delta, and hasPixels. + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeMouseScroll = function( + target, offsetX, offsetY, ev, window = undefined) { + + let domutils = getDOMWindowUtils(window); + + // see nsMouseScrollFlags in nsGUIEvent.h + const kIsVertical = 0x02; + const kIsHorizontal = 0x04; + const kHasPixels = 0x08; + const kIsMomentum = 0x40; + + let button = ev.button || 0; + let modifiers = event.parseModifiers_(ev); + + let rect = target.getBoundingClientRect(); + let left = rect.left; + let top = rect.top; + + let type = (("type" in ev) && ev.type) || "DOMMouseScroll"; + let axis = ev.axis || "vertical"; + let scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; + if (ev.hasPixels) { + scrollFlags |= kHasPixels; + } + if (ev.isMomentum) { + scrollFlags |= kIsMomentum; + } + + domutils.sendMouseScrollEvent( + type, + left + offsetX, + top + offsetY, + button, + scrollFlags, + ev.delta, + modifiers); +}; + +function computeKeyCodeFromChar_(char) { + if (char.length != 1) { + return 0; + } + + if (char >= "a" && char <= "z") { + return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "a".charCodeAt(0); + } + if (char >= "A" && char <= "Z") { + return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "A".charCodeAt(0); + } + if (char >= "0" && char <= "9") { + return Ci.nsIDOMKeyEvent.DOM_VK_0 + char.charCodeAt(0) - "0".charCodeAt(0); + } + + // returns US keyboard layout's keycode + switch (char) { + case "~": + case "`": + return Ci.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; + + case "!": + return Ci.nsIDOMKeyEvent.DOM_VK_1; + + case "@": + return Ci.nsIDOMKeyEvent.DOM_VK_2; + + case "#": + return Ci.nsIDOMKeyEvent.DOM_VK_3; + + case "$": + return Ci.nsIDOMKeyEvent.DOM_VK_4; + + case "%": + return Ci.nsIDOMKeyEvent.DOM_VK_5; + + case "^": + return Ci.nsIDOMKeyEvent.DOM_VK_6; + + case "&": + return Ci.nsIDOMKeyEvent.DOM_VK_7; + + case "*": + return Ci.nsIDOMKeyEvent.DOM_VK_8; + + case "(": + return Ci.nsIDOMKeyEvent.DOM_VK_9; + + case ")": + return Ci.nsIDOMKeyEvent.DOM_VK_0; + + case "-": + case "_": + return Ci.nsIDOMKeyEvent.DOM_VK_SUBTRACT; + + case "+": + case "=": + return Ci.nsIDOMKeyEvent.DOM_VK_EQUALS; + + case "{": + case "[": + return Ci.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; + + case "}": + case "]": + return Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; + + case "|": + case "\\": + return Ci.nsIDOMKeyEvent.DOM_VK_BACK_SLASH; + + case ":": + case ";": + return Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON; + + case "'": + case "\"": + return Ci.nsIDOMKeyEvent.DOM_VK_QUOTE; + + case "<": + case ",": + return Ci.nsIDOMKeyEvent.DOM_VK_COMMA; + + case ">": + case ".": + return Ci.nsIDOMKeyEvent.DOM_VK_PERIOD; + + case "?": + case "/": + return Ci.nsIDOMKeyEvent.DOM_VK_SLASH; + + case "\n": + return Ci.nsIDOMKeyEvent.DOM_VK_RETURN; + + default: + return 0; + } +} + +/** + * Returns true if the given key should cause keypress event when widget + * handles the native key event. Otherwise, false. + * + * The key code should be one of consts of nsIDOMKeyEvent.DOM_VK_*, + * or a key name begins with "VK_", or a character. + */ +event.isKeypressFiredKey = function(key) { + if (typeof key == "string") { + if (key.indexOf("VK_") === 0) { + key = Ci.nsIDOMKeyEvent["DOM_" + key]; + if (!key) { + throw new TypeError("Unknown key: " + key); + } + + // if key generates a character, it must cause a keypress event + } else { + return true; + } + } + + switch (key) { + case Ci.nsIDOMKeyEvent.DOM_VK_SHIFT: + case Ci.nsIDOMKeyEvent.DOM_VK_CONTROL: + case Ci.nsIDOMKeyEvent.DOM_VK_ALT: + case Ci.nsIDOMKeyEvent.DOM_VK_CAPS_LOCK: + case Ci.nsIDOMKeyEvent.DOM_VK_NUM_LOCK: + case Ci.nsIDOMKeyEvent.DOM_VK_SCROLL_LOCK: + case Ci.nsIDOMKeyEvent.DOM_VK_META: + return false; + + default: + return true; + } +}; + +/** + * Synthesise a key event. + * + * It is targeted at whatever would be targeted by an actual keypress + * by the user, typically the focused element. + * + * @param {string} key + * Key to synthesise. Should either be a character or a key code + * starting with "VK_" such as VK_RETURN. + * @param {Object.} event + * Object which may contain the properties shiftKey, ctrlKey, altKey, + * metaKey, accessKey, type. If the type is specified, a key event + * of that type is fired. Otherwise, a keydown, a keypress, and then a + * keyup event are fired in sequence. + * @param {Window=} window + * Window object. Defaults to the current window. + * + * @throws {TypeError} + * If unknown key. + */ +event.synthesizeKey = function(key, ev, window = undefined) { + let domutils = getDOMWindowUtils(window); + + let keyCode = 0; + let charCode = 0; + if (key.indexOf("VK_") === 0) { + keyCode = Ci.nsIDOMKeyEvent["DOM_" + key]; + if (!keyCode) { + throw new TypeError("Unknown key: " + key); + } + } else { + charCode = key.charCodeAt(0); + keyCode = computeKeyCodeFromChar_(key.charAt(0)); + } + + let modifiers = event.parseModifiers_(ev); + + // send keydown + (optional) keypress + keyup events + if (!("type" in ev) || !ev.type) { + let keyDownDefaultHappened = domutils.sendKeyEvent( + "keydown", keyCode, 0, modifiers); + if (event.isKeypressFiredKey(keyCode)) { + domutils.sendKeyEvent( + "keypress", + charCode ? 0 : keyCode, + charCode, + modifiers, + !keyDownDefaultHappened); + } + domutils.sendKeyEvent("keyup", keyCode, 0, modifiers); + + // send standalone keypress event + } else if (ev.type == "keypress") { + domutils.sendKeyEvent( + ev.type, + charCode ? 0 : keyCode, + charCode, + modifiers); + + // send other standalone event than keypress + } else { + domutils.sendKeyEvent(ev.type, keyCode, 0, modifiers); + } +}; + +/** + * Indicate that an event with an original target and type is expected + * to be fired, or not expected to be fired. + */ +function expectEvent_(expectedTarget, expectedEvent, testName) { + if (!expectedTarget || !expectedEvent) { + return null; + } + + seenEvent = false; + + let type; + if (expectedEvent.charAt(0) == "!") { + type = expectedEvent.substring(1); + } else { + type = expectedEvent; + } + + let handler = ev => { + let pass = (!seenEvent && ev.originalTarget == expectedTarget && ev.type == type); + is(pass, true, `${testName} ${type} event target ${seenEvent ? "twice" : ""}`); + seenEvent = true; + }; + + expectedTarget.addEventListener(type, handler, false); + return handler; +} + +/** + * Check if the event was fired or not. The provided event handler will + * be removed. + */ +function checkExpectedEvent_( + expectedTarget, expectedEvent, eventHandler, testName) { + + if (eventHandler) { + let expectEvent = (expectedEvent.charAt(0) != "!"); + let type = expectEvent; + if (!type) { + type = expectedEvent.substring(1); + } + expectedTarget.removeEventListener(type, eventHandler, false); + + let desc = `${type} event`; + if (!expectEvent) { + desc += " not"; + } + is(seenEvent, expectEvent, `${testName} ${desc} fired`); + } + + seenEvent = false; +} + +/** + * Similar to event.synthesizeMouse except that a test is performed to + * see if an event is fired at the right target as a result. + * + * To test that an event is not fired, use an expected type preceded by + * an exclamation mark, such as "!select". This might be used to test that + * a click on a disabled element doesn't fire certain events for instance. + * + * @param {Element} target + * Synthesise the mouse event on this target. + * @param {number} offsetX + * Horizontal offset from the target's bounding box. + * @param {number} offsetY + * Vertical offset from the target's bounding box. + * @param {Object.} ev + * Object which may contain the properties shiftKey, ctrlKey, altKey, + * metaKey, accessKey, type. + * @param {Element} expectedTarget + * Expected originalTarget of the event. + * @param {DOMEvent} expectedEvent + * Expected type of the event, such as "select". + * @param {string} testName + * Test name when outputing results. + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeMouseExpectEvent = function( + target, offsetX, offsetY, ev, expectedTarget, expectedEvent, + testName, window = undefined) { + + let eventHandler = expectEvent_( + expectedTarget, + expectedEvent, + testName); + event.synthesizeMouse(target, offsetX, offsetY, ev, window); + checkExpectedEvent_( + expectedTarget, + expectedEvent, + eventHandler, + testName); +}; + +/** + * Similar to synthesizeKey except that a test is performed to see if + * an event is fired at the right target as a result. + * + * @param {string} key + * Key to synthesise. + * @param {Object.} ev + * Object which may contain the properties shiftKey, ctrlKey, altKey, + * metaKey, accessKey, type. + * @param {Element} expectedTarget + * Expected originalTarget of the event. + * @param {DOMEvent} expectedEvent + * Expected type of the event, such as "select". + * @param {string} testName + * Test name when outputing results + * @param {Window=} window + * Window object. Defaults to the current window. + * + * To test that an event is not fired, use an expected type preceded by an + * exclamation mark, such as "!select". + * + * aWindow is optional, and defaults to the current window object. + */ +event.synthesizeKeyExpectEvent = function( + key, ev, expectedTarget, expectedEvent, testName, + window = undefined) { + + let eventHandler = expectEvent_( + expectedTarget, + expectedEvent, + testName); + event.synthesizeKey(key, ev, window); + checkExpectedEvent_( + expectedTarget, + expectedEvent, + eventHandler, + testName); +}; + +/** + * Synthesize a composition event. + * + * @param {DOMEvent} ev + * The composition event information. This must have |type| + * member. The value must be "compositionstart", "compositionend" or + * "compositionupdate". And also this may have |data| and |locale| + * which would be used for the value of each property of the + * composition event. Note that the data would be ignored if the + * event type were "compositionstart". + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeComposition = function(ev, window = undefined) { + let domutils = getDOMWindowUtils(window); + domutils.sendCompositionEvent(ev.type, ev.data || "", ev.locale || ""); +}; + +/** + * Synthesize a text event. + * + * The text event's information, this has |composition| and |caret| + * members. |composition| has |string| and |clauses| members. |clauses| + * must be array object. Each object has |length| and |attr|. + * And |caret| has |start| and |length|. See the following tree image. + * + * ev + * +-- composition + * | +-- string + * | +-- clauses[] + * | +-- length + * | +-- attr + * +-- caret + * +-- start + * +-- length + * + * Set the composition string to |composition.string|. Set its clauses + * information to the |clauses| array. + * + * When it's composing, set the each clauses' length + * to the |composition.clauses[n].length|. The sum + * of the all length values must be same as the length of + * |composition.string|. Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the + * |composition.clauses[n].attr|. + * + * When it's not composing, set 0 to the |composition.clauses[0].length| + * and |composition.clauses[0].attr|. + * + * Set caret position to the |caret.start|. Its offset from the start of + * the composition string. Set caret length to |caret.length|. If it's + * larger than 0, it should be wide caret. However, current nsEditor + * doesn't support wide caret, therefore, you should always set 0 now. + * + * @param {Object.} ev + * The text event's information, + * @param {Window=} window + * Window object. Defaults to the current window. + */ +event.synthesizeText = function(ev, window = undefined) { + let domutils = getDOMWindowUtils(window); + + if (!ev.composition || + !ev.composition.clauses || + !ev.composition.clauses[0]) { + return; + } + + let firstClauseLength = ev.composition.clauses[0].length; + let firstClauseAttr = ev.composition.clauses[0].attr; + let secondClauseLength = 0; + let secondClauseAttr = 0; + let thirdClauseLength = 0; + let thirdClauseAttr = 0; + if (ev.composition.clauses[1]) { + secondClauseLength = ev.composition.clauses[1].length; + secondClauseAttr = ev.composition.clauses[1].attr; + if (event.composition.clauses[2]) { + thirdClauseLength = ev.composition.clauses[2].length; + thirdClauseAttr = ev.composition.clauses[2].attr; + } + } + + let caretStart = -1; + let caretLength = 0; + if (event.caret) { + caretStart = ev.caret.start; + caretLength = ev.caret.length; + } + + domutils.sendTextEvent( + ev.composition.string, + firstClauseLength, + firstClauseAttr, + secondClauseLength, + secondClauseAttr, + thirdClauseLength, + thirdClauseAttr, + caretStart, + caretLength); +}; + +/** + * Synthesize a query selected text event. + * + * @param {Window=} + * Window object. Defaults to the current window. + * + * @return {(nsIQueryContentEventResult|null)} + * Event's result, or null if it failed. + */ +event.synthesizeQuerySelectedText = function(window = undefined) { + let domutils = getDOMWindowUtils(window); + return domutils.sendQueryContentEvent( + domutils.QUERY_SELECTED_TEXT, 0, 0, 0, 0); +}; + +/** + * Synthesize a selection set event. + * + * @param {number} offset + * Character offset. 0 means the first character in the selection + * root. + * @param {number} length + * Length of the text. If the length is too long, the extra length + * is ignored. + * @param {boolean} reverse + * If true, the selection is from |aOffset + aLength| to |aOffset|. + * Otherwise, from |aOffset| to |aOffset + aLength|. + * @param {Window=} window + * Window object. Defaults to the current window. + * + * @return True, if succeeded. Otherwise false. + */ +event.synthesizeSelectionSet = function( + offset, length, reverse, window = undefined) { + let domutils = getDOMWindowUtils(window); + return domutils.sendSelectionSetEvent(offset, length, reverse); +}; + +const KEYCODES_LOOKUP = { + "VK_SHIFT": "shiftKey", + "VK_CONTROL": "ctrlKey", + "VK_ALT": "altKey", + "VK_META": "metaKey", +}; + +const VIRTUAL_KEYCODE_LOOKUP = { + "\uE001": "VK_CANCEL", + "\uE002": "VK_HELP", + "\uE003": "VK_BACK_SPACE", + "\uE004": "VK_TAB", + "\uE005": "VK_CLEAR", + "\uE006": "VK_RETURN", + "\uE007": "VK_RETURN", + "\uE008": "VK_SHIFT", + "\uE009": "VK_CONTROL", + "\uE00A": "VK_ALT", + "\uE03D": "VK_META", + "\uE00B": "VK_PAUSE", + "\uE00C": "VK_ESCAPE", + "\uE00D": "VK_SPACE", // printable + "\uE00E": "VK_PAGE_UP", + "\uE00F": "VK_PAGE_DOWN", + "\uE010": "VK_END", + "\uE011": "VK_HOME", + "\uE012": "VK_LEFT", + "\uE013": "VK_UP", + "\uE014": "VK_RIGHT", + "\uE015": "VK_DOWN", + "\uE016": "VK_INSERT", + "\uE017": "VK_DELETE", + "\uE018": "VK_SEMICOLON", + "\uE019": "VK_EQUALS", + "\uE01A": "VK_NUMPAD0", + "\uE01B": "VK_NUMPAD1", + "\uE01C": "VK_NUMPAD2", + "\uE01D": "VK_NUMPAD3", + "\uE01E": "VK_NUMPAD4", + "\uE01F": "VK_NUMPAD5", + "\uE020": "VK_NUMPAD6", + "\uE021": "VK_NUMPAD7", + "\uE022": "VK_NUMPAD8", + "\uE023": "VK_NUMPAD9", + "\uE024": "VK_MULTIPLY", + "\uE025": "VK_ADD", + "\uE026": "VK_SEPARATOR", + "\uE027": "VK_SUBTRACT", + "\uE028": "VK_DECIMAL", + "\uE029": "VK_DIVIDE", + "\uE031": "VK_F1", + "\uE032": "VK_F2", + "\uE033": "VK_F3", + "\uE034": "VK_F4", + "\uE035": "VK_F5", + "\uE036": "VK_F6", + "\uE037": "VK_F7", + "\uE038": "VK_F8", + "\uE039": "VK_F9", + "\uE03A": "VK_F10", + "\uE03B": "VK_F11", + "\uE03C": "VK_F12", +}; + +function getKeyCode(c) { + if (c in VIRTUAL_KEYCODE_LOOKUP) { + return VIRTUAL_KEYCODE_LOOKUP[c]; + } + return c; +} + +event.sendKeyDown = function(keyToSend, modifiers, document) { + modifiers.type = "keydown"; + event.sendSingleKey(keyToSend, modifiers, document); + if (["VK_SHIFT", "VK_CONTROL", "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) < 0) { + modifiers.type = "keypress"; + event.sendSingleKey(keyToSend, modifiers, document); + } + delete modifiers.type; +}; + +event.sendKeyUp = function(keyToSend, modifiers, window = undefined) { + modifiers.type = "keyup"; + event.sendSingleKey(keyToSend, modifiers, window); + delete modifiers.type; +}; + +event.sendSingleKey = function(keyToSend, modifiers, window = undefined) { + let keyCode = getKeyCode(keyToSend); + if (keyCode in KEYCODES_LOOKUP) { + let modName = KEYCODES_LOOKUP[keyCode]; + modifiers[modName] = !modifiers[modName]; + } else if (modifiers.shiftKey) { + keyCode = keyCode.toUpperCase(); + } + event.synthesizeKey(keyCode, modifiers, window); +}; + +/** + * Focus element and, if a textual input field and no previous selection + * state exists, move the caret to the end of the input field. + * + * @param {Element} element + * Element to focus. + */ +function focusElement(element) { + let t = element.type; + if (t && (t == "text" || t == "textarea")) { + if (element.selectionEnd == 0) { + let len = element.value.length; + element.setSelectionRange(len, len); + } + } + element.focus(); +} + +/** + * @param {Array.} keySequence + * @param {Element} element + * @param {Object.=} opts + * @param {Window=} window + */ +event.sendKeysToElement = function( + keySequence, element, opts = {}, window = undefined) { + + if (opts.ignoreVisibility || elements.checkVisible(element, window)) { + focusElement(element); + + // make Object. map + let modifiers = Object.create(event.Modifiers); + for (let modifier in event.Modifiers) { + modifiers[modifier] = false; + } + + let value = keySequence.join(""); + for (let i = 0; i < value.length; i++) { + let c = value.charAt(i); + event.sendSingleKey(c, modifiers, window); + } + + } else { + throw new ElementNotVisibleError("Element is not visible"); + } +}; diff --git a/testing/marionette/sendkeys.js b/testing/marionette/sendkeys.js deleted file mode 100644 index 2475fd7839a..00000000000 --- a/testing/marionette/sendkeys.js +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2007-2009 WebDriver committers - * Copyright 2007-2009 Google Inc. - * Portions copyright 2012 Software Freedom Conservancy - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import("chrome://marionette/content/error.js"); - -var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); - -var utils = {}; -loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils); -loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils); - -var keyModifierNames = { - "VK_SHIFT": 'shiftKey', - "VK_CONTROL": 'ctrlKey', - "VK_ALT": 'altKey', - "VK_META": 'metaKey' -}; - -var keyCodes = { - '\uE001': "VK_CANCEL", - '\uE002': "VK_HELP", - '\uE003': "VK_BACK_SPACE", - '\uE004': "VK_TAB", - '\uE005': "VK_CLEAR", - '\uE006': "VK_RETURN", - '\uE007': "VK_RETURN", - '\uE008': "VK_SHIFT", - '\uE009': "VK_CONTROL", - '\uE00A': "VK_ALT", - '\uE03D': "VK_META", - '\uE00B': "VK_PAUSE", - '\uE00C': "VK_ESCAPE", - '\uE00D': "VK_SPACE", // printable - '\uE00E': "VK_PAGE_UP", - '\uE00F': "VK_PAGE_DOWN", - '\uE010': "VK_END", - '\uE011': "VK_HOME", - '\uE012': "VK_LEFT", - '\uE013': "VK_UP", - '\uE014': "VK_RIGHT", - '\uE015': "VK_DOWN", - '\uE016': "VK_INSERT", - '\uE017': "VK_DELETE", - '\uE018': "VK_SEMICOLON", - '\uE019': "VK_EQUALS", - '\uE01A': "VK_NUMPAD0", - '\uE01B': "VK_NUMPAD1", - '\uE01C': "VK_NUMPAD2", - '\uE01D': "VK_NUMPAD3", - '\uE01E': "VK_NUMPAD4", - '\uE01F': "VK_NUMPAD5", - '\uE020': "VK_NUMPAD6", - '\uE021': "VK_NUMPAD7", - '\uE022': "VK_NUMPAD8", - '\uE023': "VK_NUMPAD9", - '\uE024': "VK_MULTIPLY", - '\uE025': "VK_ADD", - '\uE026': "VK_SEPARATOR", - '\uE027': "VK_SUBTRACT", - '\uE028': "VK_DECIMAL", - '\uE029': "VK_DIVIDE", - '\uE031': "VK_F1", - '\uE032': "VK_F2", - '\uE033': "VK_F3", - '\uE034': "VK_F4", - '\uE035': "VK_F5", - '\uE036': "VK_F6", - '\uE037': "VK_F7", - '\uE038': "VK_F8", - '\uE039': "VK_F9", - '\uE03A': "VK_F10", - '\uE03B': "VK_F11", - '\uE03C': "VK_F12" -}; - -function getKeyCode (c) { - if (c in keyCodes) { - return keyCodes[c]; - } - return c; -}; - -function sendKeyDown (keyToSend, modifiers, document) { - modifiers.type = "keydown"; - sendSingleKey(keyToSend, modifiers, document); - if (["VK_SHIFT", "VK_CONTROL", - "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) == -1) { - modifiers.type = "keypress"; - sendSingleKey(keyToSend, modifiers, document); - } - delete modifiers.type; -} - -function sendKeyUp (keyToSend, modifiers, document) { - modifiers.type = "keyup"; - sendSingleKey(keyToSend, modifiers, document); - delete modifiers.type; -} - -function sendSingleKey (keyToSend, modifiers, document) { - let keyCode = getKeyCode(keyToSend); - if (keyCode in keyModifierNames) { - let modName = keyModifierNames[keyCode]; - modifiers[modName] = !modifiers[modName]; - } else if (modifiers.shiftKey) { - keyCode = keyCode.toUpperCase(); - } - utils.synthesizeKey(keyCode, modifiers, document); -} - -/** - * Focus element and, if a textual input field and no previous selection - * state exists, move the caret to the end of the input field. - * - * @param {Element} el - * Element to focus. - */ -function focusElement(el) { - let t = el.type; - if (t && (t == "text" || t == "textarea")) { - if (el.selectionEnd == 0) { - let len = el.value.length; - el.setSelectionRange(len, len); - } - } - el.focus(); -} - -function sendKeysToElement(document, element, keysToSend, ignoreVisibility) { - if (ignoreVisibility || checkVisible(element)) { - focusElement(element); - - let modifiers = { - shiftKey: false, - ctrlKey: false, - altKey: false, - metaKey: false - }; - let value = keysToSend.join(""); - for (var i = 0; i < value.length; i++) { - var c = value.charAt(i); - sendSingleKey(c, modifiers, document); - } - } else { - throw new ElementNotVisibleError("Element is not visible"); - } -}; From b200390cdff45dd741782fe420a2cca1abdc60cc Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:50:17 +0000 Subject: [PATCH 028/187] Bug 1245153 - Convert atoms.js to a module; r=automatedtester Through some very clever hacking of the arguments to each of the atoms, we are able to contain this in a JS module: Atoms normally extract their arguments directly from the function scoped `arguments' variable, but by explicitly naming `window' as the last argument in the functions' prototype we are able to set the `window' variable used inside. This is obviously a big hack, but it encapsulates the atoms and we are moving away from atoms in the long term. MozReview-Commit-ID: KGO77fjRN2d --- testing/marionette/atoms/atoms.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/testing/marionette/atoms/atoms.js b/testing/marionette/atoms/atoms.js index 35ad299a62e..570bb5a0c11 100644 --- a/testing/marionette/atoms/atoms.js +++ b/testing/marionette/atoms/atoms.js @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -//clearElement -var clearElement = function(){return function(){function g(a){throw a;}var h=void 0,i=!0,k=null,l=!1;function n(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,q=this; +this.EXPORTED_SYMBOLS = ["atom"]; + +this.atom = {}; + +atom.clearElement = function(element, window){return function(){function g(a){throw a;}var h=void 0,i=!0,k=null,l=!1;function n(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,q=this; function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function r(a){return a!==h}function ba(a){var b=aa(a);return"array"==b||"object"==b&&"number"==typeof a.length}function t(a){return"string"==typeof a}function w(a){return"function"==aa(a)}function ca(a){a=aa(a);return"object"==a||"array"==a||"function"==a}var da="closure_uid_"+Math.floor(2147483648*Math.random()).toString(36),ea=0,fa=Date.now||function(){return+new Date}; function x(a,b){function c(){}c.prototype=b.prototype;a.$=b.prototype;a.prototype=new c};function ga(a,b){for(var c=1;c")&&(a=a.replace(ma,">"));-1!=a.indexOf('"')&&(a=a.replace(na,"""));return a}var ka=/&/g,la=//g,na=/\"/g,ja=/[&<>\"]/; @@ -86,8 +89,8 @@ p.H=k;p.V=0;p.b=function(){return this.H[0].b()};p.g=function(){return z(this.H) function Lc(a,b,c,d){if(a==c)return d(0==B[1].length? 0:parseInt(B[1],10))?1:0)||((0==A[2].length)<(0==B[2].length)?-1:(0==A[2].length)>(0==B[2].length)?1:0)||(A[2]B[2]?1:0)}while(0==q)}k["1.9.1"]=0<=q};(function(){var a=e.Components;if(!a)return!1;try{if(!a.classes)return!1}catch(c){return!1}var b=a.classes,a=a.interfaces;b["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);b["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function C(a,c,b,d,y){this.b=!!c;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=y?y:this.c||0;this.b&&(this.depth*=-1)}f(C,function(){});C.prototype.a=null;C.prototype.c=0;f(function(a,c,b,d){C.call(this,a,c,0,null,d)},C);var D={"class":"className",readonly:"readOnly"},E=["checked","disabled","draggable","hidden"],F="BUTTON,INPUT,OPTGROUP,OPTION,SELECT,TEXTAREA".split(",");function G(a){var c=a.tagName.toUpperCase();if(p(F,c)){var b;b=D.disabled||"disabled";var d=a[b];b=void 0===d&&p(E,b)?!1:d;a=b?!1:a.parentNode&&1==a.parentNode.nodeType&&"OPTGROUP"==c||"OPTION"==c?G(a.parentNode):!0}else a=!0;return a};var H=G,I=["_"],J=e;!(I[0]in J)&&J.execScript&&J.execScript("var "+I[0]);for(var K;I.length&&(K=I.shift());)!I.length&&void 0!==H?J[K]=H:J=J[K]?J[K]:J[K]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} -//isElementSelected -var isElementSelected = function(){return function(){var f=!1,g=this;function h(a,b){function c(){}c.prototype=b.prototype;a.d=b.prototype;a.prototype=new c};function i(a,b){for(var c=1;c(0==C[1].length? 0:parseInt(C[1],10))?1:0)||((0==B[2].length)<(0==C[2].length)?-1:(0==B[2].length)>(0==C[2].length)?1:0)||(B[2]C[2]?1:0)}while(0==s)}n["1.9.1"]=0<=s};var D={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},E={IMG:" ",BR:"\n"};function F(a,b,c){if(!(a.nodeName in D))if(3==a.nodeType)c?b.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in E)b.push(E[a.nodeName]);else for(a=a.firstChild;a;)F(a,b,c),a=a.nextSibling};(function(){var a=g.Components;if(!a)return f;try{if(!a.classes)return f}catch(b){return f}var c=a.classes,a=a.interfaces;c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function G(a,b,c,d,e){this.b=!!b;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=e?e:this.c||0;this.b&&(this.depth*=-1)}h(G,function(){});G.prototype.a=null;G.prototype.c=0;h(function(a,b,c,d){G.call(this,a,b,0,null,d)},G);function H(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}function I(a){return H(a,"OPTION")?!0:H(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):f}var J={"class":"className",readonly:"readOnly"},K=["checked","disabled","draggable","hidden"];function L(a){if(I(a)){if(!I(a))throw new o(15,"Element is not selectable");var b="selected",c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";var c=b,d=J[c]||c,b=a[d],e;if(e=void 0===b){b:if("string"==typeof K)d="string"!=typeof d||1!=d.length?-1:K.indexOf(d,0);else{for(e=0;e(0==v[1].length? 0:parseInt(v[1],10))?1:0)||((0==l[2].length)<(0==v[2].length)?-1:(0==l[2].length)>(0==v[2].length)?1:0)||(l[2]v[2]?1:0)}while(0==b)}return b}function aa(a){return String(a).replace(/\-([a-z])/g,function(a,c){return c.toUpperCase()})};var s=Array.prototype;function t(a,b){for(var c=a.length,d=n(a)?a.split(""):a,e=0;e Date: Wed, 3 Feb 2016 18:52:37 +0000 Subject: [PATCH 029/187] Bug 1245153 - Convert frame-manager.js to a module; r=automatedtester MozReview-Commit-ID: HNCvHitE3Fh --- testing/marionette/frame-manager.js | 85 +++++++++++++++++------------ 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/testing/marionette/frame-manager.js b/testing/marionette/frame-manager.js index ae71f8ea835..9d8126ddf25 100644 --- a/testing/marionette/frame-manager.js +++ b/testing/marionette/frame-manager.js @@ -2,54 +2,67 @@ * 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/. */ -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; +"use strict"; -this.EXPORTED_SYMBOLS = ["FrameManager"]; - -var FRAME_SCRIPT = "chrome://marionette/content/listener.js"; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); +this.EXPORTED_SYMBOLS = ["frame"]; -//list of OOP frames that has the frame script loaded +this.frame = {}; + +const FRAME_SCRIPT = "chrome://marionette/content/listener.js"; + +// list of OOP frames that has the frame script loaded var remoteFrames = []; /** * An object representing a frame that Marionette has loaded a * frame script in. */ -function MarionetteRemoteFrame(windowId, frameId) { - this.windowId = windowId; //outerWindowId relative to main process - this.frameId = frameId; //actual frame relative to windowId's frames list - this.targetFrameId = this.frameId; //assigned FrameId, used for messaging +frame.RemoteFrame = function(windowId, frameId) { + // outerWindowId relative to main process + this.windowId = windowId; + // actual frame relative to the windowId's frames list + this.frameId = frameId; + // assigned frame ID, used for messaging + this.targetFrameId = this.frameId; + // list of OOP frames that has the frame script loaded + this.remoteFrames = []; }; /** - * The FrameManager will maintain the list of Out Of Process (OOP) frames and will handle - * frame switching between them. - * It handles explicit frame switching (switchToFrame), and implicit frame switching, which - * occurs when a modal dialog is triggered in B2G. + * The FrameManager will maintain the list of Out Of Process (OOP) + * frames and will handle frame switching between them. * + * It handles explicit frame switching (switchToFrame), and implicit + * frame switching, which occurs when a modal dialog is triggered in B2G. */ -this.FrameManager = function FrameManager(server) { - //messageManager maintains the messageManager for the current process' chrome frame or the global message manager - this.currentRemoteFrame = null; //holds a member of remoteFrames (for an OOP frame) or null (for the main process) - this.previousRemoteFrame = null; //frame we'll need to restore once interrupt is gone - this.handledModal = false; //set to true when we have been interrupted by a modal - this.server = server; // a reference to the marionette server +frame.Manager = function(server) { + // messageManager maintains the messageManager + // for the current process' chrome frame or the global message manager + + // holds a member of the remoteFrames (for an OOP frame) + // or null (for the main process) + this.currentRemoteFrame = null; + // frame we'll need to restore once interrupt is gone + this.previousRemoteFrame = null; + // set to true when we have been interrupted by a modal + this.handledModal = false; + // a reference to the Marionette server + this.server = server; }; -FrameManager.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener, - Ci.nsISupportsWeakReference]), +frame.Manager.prototype = { + QueryInterface: XPCOMUtils.generateQI( + [Ci.nsIMessageListener, Ci.nsISupportsWeakReference]), /** - * Receives all messages from content messageManager + * Receives all messages from content messageManager. */ - receiveMessage: function FM_receiveMessage(message) { + receiveMessage: function(message) { switch (message.name) { case "MarionetteFrame:getInterruptedState": // This will return true if the calling frame was interrupted by a modal dialog @@ -68,6 +81,7 @@ FrameManager.prototype = { return {value: this.handledModal}; } return {value: false}; + case "MarionetteFrame:handleModal": /* * handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt. @@ -89,6 +103,7 @@ FrameManager.prototype = { this.handledModal = true; this.server.sendOk(this.server.command_id); return {value: isLocal}; + case "MarionetteFrame:getCurrentFrameId": if (this.currentRemoteFrame != null) { return this.currentRemoteFrame.frameId; @@ -96,7 +111,7 @@ FrameManager.prototype = { } }, - getOopFrame: function FM_getOopFrame(winId, frameId) { + getOopFrame: function(winId, frameId) { // get original frame window let outerWin = Services.wm.getOuterWindowWithId(winId); // find the OOP frame @@ -104,7 +119,7 @@ FrameManager.prototype = { return f; }, - getFrameMM: function FM_getFrameMM(winId, frameId) { + getFrameMM: function(winId, frameId) { let oopFrame = this.getOopFrame(winId, frameId); let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner) .frameLoader.messageManager; @@ -112,10 +127,10 @@ FrameManager.prototype = { }, /** - * Switch to OOP frame. We're handling this here - * so we can maintain a list of remote frames. + * Switch to OOP frame. We're handling this here so we can maintain + * a list of remote frames. */ - switchToFrame: function FM_switchToFrame(winId, frameId) { + switchToFrame: function(winId, frameId) { let oopFrame = this.getOopFrame(winId, frameId); let mm = this.getFrameMM(winId, frameId); @@ -145,7 +160,7 @@ FrameManager.prototype = { // and set the frame's ChromeMessageSender as the active message manager // the server will listen to. this.addMessageManagerListeners(mm); - let aFrame = new MarionetteRemoteFrame(winId, frameId); + let aFrame = new frame.RemoteFrame(winId, frameId); aFrame.messageManager = Cu.getWeakReference(mm); remoteFrames.push(aFrame); this.currentRemoteFrame = aFrame; @@ -159,7 +174,7 @@ FrameManager.prototype = { * This function handles switching back to the frame that was interrupted by the modal dialog. * This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process */ - switchToModalOrigin: function FM_switchToModalOrigin() { + switchToModalOrigin: function() { //only handle this if we indeed switched out of the modal's originating frame if (this.previousRemoteFrame != null) { this.currentRemoteFrame = this.previousRemoteFrame; @@ -179,7 +194,7 @@ FrameManager.prototype = { * The message manager object, typically * ChromeMessageBroadcaster or ChromeMessageSender. */ - addMessageManagerListeners: function FM_addMessageManagerListeners(mm) { + addMessageManagerListeners: function(mm) { mm.addWeakMessageListener("Marionette:ok", this.server); mm.addWeakMessageListener("Marionette:done", this.server); mm.addWeakMessageListener("Marionette:error", this.server); @@ -211,7 +226,7 @@ FrameManager.prototype = { * The message manager object, typically * ChromeMessageBroadcaster or ChromeMessageSender. */ - removeMessageManagerListeners: function FM_removeMessageManagerListeners(mm) { + removeMessageManagerListeners: function(mm) { mm.removeWeakMessageListener("Marionette:ok", this.server); mm.removeWeakMessageListener("Marionette:done", this.server); mm.removeWeakMessageListener("Marionette:error", this.server); From 892f383cb0ca784707319b12986abbae1b8fc49c Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:54:23 +0000 Subject: [PATCH 030/187] Bug 1245153 - Convert interactions.js and elements.js to modules; r=automatedtester To simplify the dependency chain and reduce the number of duplicate functions in Marionette, a number of functions have been removed from interactions.js and added to elements.js. This makes them more easily re-usable and works around a circular dependency issue. MozReview-Commit-ID: TZc3VZzHqM --- testing/marionette/elements.js | 112 ++++++++++++++++++++++++- testing/marionette/interactions.js | 128 ++++++----------------------- 2 files changed, 135 insertions(+), 105 deletions(-) diff --git a/testing/marionette/elements.js b/testing/marionette/elements.js index 3f9eaf91ed7..297178d75f9 100644 --- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -2,8 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -let {classes: Cc, interfaces: Ci, utils: Cu} = Components; +"use strict"; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("chrome://marionette/content/atoms.js"); Cu.import("chrome://marionette/content/error.js"); /** @@ -299,7 +302,7 @@ ElementManager.prototype = { * as its members. */ applyNamedArgs: function EM_applyNamedArgs(args) { - namedArgs = {}; + let namedArgs = {}; args.forEach(function(arg) { if (arg && typeof(arg['__marionetteArgs']) === 'object') { for (let prop in arg['__marionetteArgs']) { @@ -592,10 +595,113 @@ ElementManager.prototype = { } return elements; }, -} +}; this.elements = {}; + elements.generateUUID = function() { let uuid = uuidGen.generateUUID().toString(); return uuid.substring(1, uuid.length - 1); }; + +/** + * This function generates a pair of coordinates relative to the viewport + * given a target element and coordinates relative to that element's + * top-left corner. + * + * @param {Node} node + * Target node. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +// TODO(ato): Replicated from listener.js for the time being +elements.coordinates = function(node, x = undefined, y = undefined) { + let box = node.getBoundingClientRect(); + if (!x) { + x = box.width / 2.0; + } + if (!y) { + y = box.height / 2.0; + } + return { + x: box.left + x, + y: box.top + y, + }; +} + +/** + * This function returns true if the node is in the viewport. + * + * @param {Element} element + * Target element. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +elements.inViewport = function(el, x = undefined, y = undefined) { + let win = el.ownerDocument.defaultView; + let c = elements.coordinates(el, x, y); + let vp = { + top: win.pageYOffset, + left: win.pageXOffset, + bottom: (win.pageYOffset + win.innerHeight), + right: (win.pageXOffset + win.innerWidth) + }; + + return (vp.left <= c.x + win.pageXOffset && + c.x + win.pageXOffset <= vp.right && + vp.top <= c.y + win.pageYOffset && + c.y + win.pageYOffset <= vp.bottom); +}; + +/** + * This function throws the visibility of the element error if the element is + * not displayed or the given coordinates are not within the viewport. + * + * @param {Element} element + * Element to check if visible. + * @param {Window} window + * Window object. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +elements.checkVisible = function(el, win, x = undefined, y = undefined) { + // Bug 1094246: Webdriver's isShown doesn't work with content xul + let ns = atom.getElementAttribute(el, "namespaceURI", win); + if (ns.indexOf("there.is.only.xul") < 0 && + !atom.isElementDisplayed(el, win)) { + return false; + } + + if (el.tagName.toLowerCase() == "body") { + return true; + } + + if (!elements.inViewport(el, x, y)) { + if (el.scrollIntoView) { + el.scrollIntoView(false); + if (!elements.inViewport(el)) { + return false; + } + } else { + return false; + } + } + return true; +}; + +elements.isXULElement = function(el) { + let ns = atom.getElementAttribute(el, "namespaceURI"); + return ns.indexOf("there.is.only.xul") >= 0; +}; diff --git a/testing/marionette/interactions.js b/testing/marionette/interactions.js index 1679fb9e2f2..5cea585322b 100644 --- a/testing/marionette/interactions.js +++ b/testing/marionette/interactions.js @@ -2,15 +2,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/. */ -/* global Components, Accessibility, ElementNotVisibleError, - InvalidElementStateError, Interactions */ +"use strict"; -var {utils: Cu} = Components; +const {utils: Cu} = Components; -this.EXPORTED_SYMBOLS = ['Interactions']; +Cu.import("chrome://marionette/content/accessibility.js"); +Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/error.js"); +Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/event.js"); -Cu.import('chrome://marionette/content/accessibility.js'); -Cu.import('chrome://marionette/content/error.js'); +this.EXPORTED_SYMBOLS = ["Interactions"]; /** * XUL elements that support disabled attribtue. @@ -72,32 +74,11 @@ const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([ 'TAB' ]); -/** - * This function generates a pair of coordinates relative to the viewport given - * a target element and coordinates relative to that element's top-left corner. - * @param 'x', and 'y' are the relative to the target. - * If they are not specified, then the center of the target is used. - */ -function coordinates(target, x, y) { - let box = target.getBoundingClientRect(); - if (typeof x === 'undefined') { - x = box.width / 2; - } - if (typeof y === 'undefined') { - y = box.height / 2; - } - return { - x: box.left + x, - y: box.top + y - }; -} - /** * A collection of interactions available in marionette. * @type {Object} */ -this.Interactions = function(utils, getCapabilies) { - this.utils = utils; +this.Interactions = function(getCapabilies) { this.accessibility = new Accessibility(getCapabilies); }; @@ -115,22 +96,25 @@ Interactions.prototype = { */ clickElement(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); - let visible = this.checkVisible(container, el); + let visible = elements.checkVisible(el, container.frame); if (!visible) { throw new ElementNotVisibleError('Element is not visible'); } return this.accessibility.getAccessibleObject(el, true).then(acc => { this.accessibility.checkVisible(acc, el, visible); - if (this.utils.isElementEnabled(el)) { + if (atom.isElementEnabled(el)) { this.accessibility.checkEnabled(acc, el, true, container); this.accessibility.checkActionable(acc, el); - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { el.click(); } else { let rects = el.getClientRects(); - this.utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2, - rects[0].top + rects[0].height/2, - {}, el.ownerDocument.defaultView); + let win = el.ownerDocument.defaultView; + event.synthesizeMouseAtPoint( + rects[0].left + rects[0].width / 2, + rects[0].top + rects[0].height / 2, + {} /* opts */, + win); } } else { throw new InvalidElementStateError('Element is not enabled'); @@ -159,8 +143,8 @@ Interactions.prototype = { let el = elementManager.getKnownElement(id, container); return this.accessibility.getAccessibleObject(el, true).then(acc => { this.accessibility.checkActionable(acc, el); - this.utils.sendKeysToElement( - container.frame, el, value, ignoreVisibility); + event.sendKeysToElement( + value, el, {ignoreVisibility: false}, container.frame); }); }, @@ -180,7 +164,7 @@ Interactions.prototype = { */ isElementDisplayed(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); - let displayed = this.utils.isElementDisplayed(el); + let displayed = atom.isElementDisplayed(el, container.frame); return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkVisible(acc, el, displayed); return displayed; @@ -204,16 +188,16 @@ Interactions.prototype = { isElementEnabled(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); let enabled = true; - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { // Check if XUL element supports disabled attribute if (DISABLED_ATTRIBUTE_SUPPORTED_XUL.has(el.tagName.toUpperCase())) { - let disabled = this.utils.getElementAttribute(el, 'disabled'); + let disabled = atom.getElementAttribute(el, 'disabled', container.frame); if (disabled && disabled === 'true') { enabled = false; } } } else { - enabled = this.utils.isElementEnabled(el); + enabled = atom.isElementEnabled(el, container.frame); } return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkEnabled(acc, el, enabled, container); @@ -238,7 +222,7 @@ Interactions.prototype = { isElementSelected(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); let selected = true; - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { let tagName = el.tagName.toUpperCase(); if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) { selected = el.checked; @@ -247,71 +231,11 @@ Interactions.prototype = { selected = el.selected; } } else { - selected = this.utils.isElementSelected(el); + selected = atom.isElementSelected(el, container.frame); } return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkSelected(acc, el, selected); return selected; }); }, - - /** - * This function throws the visibility of the element error if the element is - * not displayed or the given coordinates are not within the viewport. - * - * @param 'x', and 'y' are the coordinates relative to the target. - * If they are not specified, then the center of the target is used. - */ - checkVisible(container, el, x, y) { - // Bug 1094246 - Webdriver's isShown doesn't work with content xul - if (!this.isXULElement(el)) { - //check if the element is visible - let visible = this.utils.isElementDisplayed(el); - if (!visible) { - return false; - } - } - - if (el.tagName.toLowerCase() === 'body') { - return true; - } - if (!this.elementInViewport(container, el, x, y)) { - //check if scroll function exist. If so, call it. - if (el.scrollIntoView) { - el.scrollIntoView(false); - if (!this.elementInViewport(container, el)) { - return false; - } - } - else { - return false; - } - } - return true; - }, - - isXULElement(el) { - return this.utils.getElementAttribute(el, 'namespaceURI').indexOf( - 'there.is.only.xul') >= 0; - }, - - /** - * This function returns true if the given coordinates are in the viewport. - * @param 'x', and 'y' are the coordinates relative to the target. - * If they are not specified, then the center of the target is used. - */ - elementInViewport(container, el, x, y) { - let c = coordinates(el, x, y); - let win = container.frame; - let viewPort = { - top: win.pageYOffset, - left: win.pageXOffset, - bottom: win.pageYOffset + win.innerHeight, - right: win.pageXOffset + win.innerWidth - }; - return (viewPort.left <= c.x + win.pageXOffset && - c.x + win.pageXOffset <= viewPort.right && - viewPort.top <= c.y + win.pageYOffset && - c.y + win.pageYOffset <= viewPort.bottom); - } }; From ee96a9bc809060ecab1fff67d895e5b2453a793a Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:56:02 +0000 Subject: [PATCH 031/187] Bug 1245153 - Employ new modules throughout Marionette; r=automatedtester This change removes almost all the remaining uses of loadSubScript and global scope pollution. The only remaining use is for common.js, which is resolved by a later bug for evaluating scripts. MozReview-Commit-ID: 96h0yLElauq --- testing/marionette/actions.js | 13 +++-- testing/marionette/driver.js | 33 +++++-------- testing/marionette/listener.js | 86 +++++----------------------------- testing/marionette/server.js | 2 - 4 files changed, 31 insertions(+), 103 deletions(-) diff --git a/testing/marionette/actions.js b/testing/marionette/actions.js index 0c2dd76bf74..c5db838dd09 100644 --- a/testing/marionette/actions.js +++ b/testing/marionette/actions.js @@ -7,6 +7,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); +Cu.import("chrome://marionette/content/event.js"); + const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay"; const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms @@ -19,7 +21,7 @@ this.actions = {}; /** * Functionality for (single finger) action chains. */ -actions.Chain = function(utils, checkForInterrupted) { +actions.Chain = function(checkForInterrupted) { // for assigning unique ids to all touches this.nextTouchId = 1000; // keep track of active Touches @@ -43,9 +45,6 @@ actions.Chain = function(utils, checkForInterrupted) { // determines if we create touch events this.inputSource = null; - - // test utilities providing some event synthesis code - this.utils = utils; }; actions.Chain.prototype.dispatchActions = function( @@ -127,7 +126,7 @@ actions.Chain.prototype.emitMouseEvent = function( let mods; if (typeof modifiers != "undefined") { - mods = this.utils._parseModifiers(modifiers); + mods = event.parseModifiers_(modifiers); } else { mods = 0; } @@ -187,12 +186,12 @@ actions.Chain.prototype.actions = function(chain, touchId, i, keyModifiers) { switch(command) { case "keyDown": - this.utils.sendKeyDown(pack[1], keyModifiers, this.container.frame); + event.sendKeyDown(pack[1], keyModifiers, this.container.frame); this.actions(chain, touchId, i, keyModifiers); break; case "keyUp": - this.utils.sendKeyUp(pack[1], keyModifiers, this.container.frame); + event.sendKeyUp(pack[1], keyModifiers, this.container.frame); this.actions(chain, touchId, i, keyModifiers); break; diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index febe3a07970..f699e17eace 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -24,8 +24,11 @@ XPCOMUtils.defineLazyServiceGetter( this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2"); Cu.import("chrome://marionette/content/actions.js"); +Cu.import("chrome://marionette/content/atoms.js"); Cu.import("chrome://marionette/content/interactions.js"); Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/event.js"); +Cu.import("chrome://marionette/content/frame-manager.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/modal.js"); Cu.import("chrome://marionette/content/proxy.js"); @@ -33,14 +36,6 @@ Cu.import("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); -// preserve this import order: -var utils = {}; -loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils); -loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils); -loader.loadSubScript("chrome://marionette/content/atoms.js", utils); -loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils); -loader.loadSubScript("chrome://marionette/content/frame-manager.js"); - this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"]; var FRAME_SCRIPT = "chrome://marionette/content/listener.js"; @@ -138,7 +133,7 @@ this.GeckoDriver = function(appName, device, stopSignal, emulator) { this.oopFrameId = null; this.observing = null; this._browserIds = new WeakMap(); - this.actions = new actions.Chain(utils); + this.actions = new actions.Chain(); this.sessionCapabilities = { // mandated capabilities @@ -166,7 +161,7 @@ this.GeckoDriver = function(appName, device, stopSignal, emulator) { "version": Services.appinfo.version, }; - this.interactions = new Interactions(utils, () => this.sessionCapabilities); + this.interactions = new Interactions(() => this.sessionCapabilities); this.mm = globalMessageManager; this.listener = proxy.toListener(() => this.mm, this.sendAsync.bind(this)); @@ -354,8 +349,6 @@ GeckoDriver.prototype.startBrowser = function(win, isNewSession=false) { * True if this is the first time we're talking to this browser. */ GeckoDriver.prototype.whenBrowserStarted = function(win, isNewSession) { - utils.window = win; - try { let mm = win.window.messageManager; if (!isNewSession) { @@ -390,7 +383,7 @@ GeckoDriver.prototype.whenBrowserStarted = function(win, isNewSession) { */ GeckoDriver.prototype.getVisibleText = function(el, lines) { try { - if (utils.isElementDisplayed(el)) { + if (atom.isElementDisplayed(el, this.getCurrentWindow())) { if (el.value) { lines.push(el.value); } @@ -786,7 +779,6 @@ GeckoDriver.prototype.createExecuteSandbox = function(win, mn, sandboxName) { let sb = new Cu.Sandbox(principal, {sandboxPrototype: win, wantXrays: false, sandboxName: ""}); sb.global = sb; - sb.testUtils = utils; sb.proto = win; mn.exports.forEach(function(fn) { @@ -1590,7 +1582,6 @@ GeckoDriver.prototype.switchToWindow = function*(cmd, resp) { yield browserListening; } } else { - utils.window = found.win; this.curBrowser = this.browsers[found.outerId]; if ("tabIndex" in found) { @@ -2017,7 +2008,7 @@ GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) { case Context.CHROME: let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win}); - resp.body.value = utils.getElementAttribute(el, name); + resp.body.value = atom.getElementAttribute(el, name, this.getCurrentWindow()); break; case Context.CONTENT: @@ -2792,11 +2783,11 @@ GeckoDriver.prototype.sendKeysToDialog = function(cmd, resp) { } let win = this.dialog.window ? this.dialog.window : this.getCurrentWindow(); - utils.sendKeysToElement( - win, - loginTextbox, + event.sendKeysToElement( cmd.parameters.value, - true /* ignore visibility check */); + loginTextbox, + {ignoreVisibility: true}, + win); }; /** @@ -3068,7 +3059,7 @@ var BrowserObj = function(win, driver) { this.pendingCommands = []; // we should have one FM per BO so that we can handle modals in each Browser - this.frameManager = new FrameManager(driver); + this.frameManager = new frame.Manager(driver); this.frameRegsPending = 0; // register all message listeners diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index 0cec32be334..7b61fb76f18 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -5,19 +5,21 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; var uuidGen = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); + .getService(Ci.nsIUUIDGenerator); var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); + .getService(Ci.mozIJSSubScriptLoader); loader.loadSubScript("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); Cu.import("chrome://marionette/content/actions.js"); +Cu.import("chrome://marionette/content/atoms.js"); Cu.import("chrome://marionette/content/capture.js"); Cu.import("chrome://marionette/content/cookies.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/error.js"); +Cu.import("chrome://marionette/content/event.js"); Cu.import("chrome://marionette/content/proxy.js"); Cu.import("chrome://marionette/content/interactions.js"); @@ -25,13 +27,6 @@ Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -var utils = {}; -utils.window = content; -// Load Event/ChromeUtils for use with JS scripts: -loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils); -loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils); -loader.loadSubScript("chrome://marionette/content/atoms.js", utils); -loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils); var marionetteLogObj = new MarionetteLogObj(); @@ -48,9 +43,9 @@ var elementManager = new ElementManager([]); // Holds session capabilities. var capabilities = {}; -var interactions = new Interactions(utils, () => capabilities); +var interactions = new Interactions(() => capabilities); -var actions = new actions.Chain(utils, checkForInterrupted); +var actions = new actions.Chain(checkForInterrupted); var importedScripts = null; // Contains the last file input element that was the target of @@ -529,7 +524,6 @@ function createExecuteContentSandbox(win, timeout) { sandbox.window = win; sandbox.document = sandbox.window.document; sandbox.navigator = sandbox.window.navigator; - sandbox.testUtils = utils; sandbox.asyncTestCommandId = asyncTestCommandId; sandbox.marionette = mn; @@ -893,67 +887,13 @@ function coordinates(target, x, y) { return coords; } - -/** - * This function returns true if the given coordinates are in the viewport. - * @param 'x', and 'y' are the coordinates relative to the target. - * If they are not specified, then the center of the target is used. - */ -function elementInViewport(el, x, y) { - let c = coordinates(el, x, y); - let curFrame = curContainer.frame; - let viewPort = {top: curFrame.pageYOffset, - left: curFrame.pageXOffset, - bottom: (curFrame.pageYOffset + curFrame.innerHeight), - right:(curFrame.pageXOffset + curFrame.innerWidth)}; - return (viewPort.left <= c.x + curFrame.pageXOffset && - c.x + curFrame.pageXOffset <= viewPort.right && - viewPort.top <= c.y + curFrame.pageYOffset && - c.y + curFrame.pageYOffset <= viewPort.bottom); -} - -/** - * This function throws the visibility of the element error if the element is - * not displayed or the given coordinates are not within the viewport. - * @param 'x', and 'y' are the coordinates relative to the target. - * If they are not specified, then the center of the target is used. - */ -function checkVisible(el, x, y) { - // Bug 1094246 - Webdriver's isShown doesn't work with content xul - if (utils.getElementAttribute(el, "namespaceURI").indexOf("there.is.only.xul") == -1) { - //check if the element is visible - let visible = utils.isElementDisplayed(el); - if (!visible) { - return false; - } - } - - if (el.tagName.toLowerCase() === 'body') { - return true; - } - if (!elementInViewport(el, x, y)) { - //check if scroll function exist. If so, call it. - if (el.scrollIntoView) { - el.scrollIntoView(false); - if (!elementInViewport(el)) { - return false; - } - } - else { - return false; - } - } - return true; -} - - /** * Function that perform a single tap */ function singleTap(id, corx, cory) { let el = elementManager.getKnownElement(id, curContainer); // after this block, the element will be scrolled into view - let visible = checkVisible(el, corx, cory); + let visible = elements.checkVisible(el, curContainer.frame, corx, cory); if (!visible) { throw new ElementNotVisibleError("Element is not currently visible and may not be manipulated"); } @@ -1393,7 +1333,7 @@ function clickElement(id) { */ function getElementAttribute(id, name) { let el = elementManager.getKnownElement(id, curContainer); - return utils.getElementAttribute(el, name); + return atom.getElementAttribute(el, name, curContainer.frame); } /** @@ -1407,7 +1347,7 @@ function getElementAttribute(id, name) { */ function getElementText(id) { let el = elementManager.getKnownElement(id, curContainer); - return utils.getElementText(el); + return atom.getElementText(el, curContainer.frame); } /** @@ -1511,11 +1451,11 @@ function sendKeysToElement(msg) { // so pass the filename to driver.js, which in turn passes them back // to this frame script in receiveFiles. sendSyncMessage("Marionette:getFiles", - {value: p, command_id: command_id}); + {value: p, command_id: command_id}); } else { interactions.sendKeysToElement(curContainer, elementManager, id, val) - .then(() => sendOk(command_id)) - .catch(e => sendError(e, command_id)); + .then(() => sendOk(command_id)) + .catch(e => sendError(e, command_id)); } } @@ -1528,7 +1468,7 @@ function clearElement(id) { if (el.type == "file") { el.value = null; } else { - utils.clearElement(el); + atom.clearElement(el, curContainer.frame); } } catch (e) { // Bug 964738: Newer atoms contain status codes which makes wrapping diff --git a/testing/marionette/server.js b/testing/marionette/server.js index 492291d2bb3..b709cd582f4 100644 --- a/testing/marionette/server.js +++ b/testing/marionette/server.js @@ -23,9 +23,7 @@ loader.loadSubScript("resource://devtools/shared/transport/transport.js"); // Preserve this import order: var events = {}; -loader.loadSubScript("chrome://marionette/content/EventUtils.js", events); loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", events); -loader.loadSubScript("chrome://marionette/content/frame-manager.js"); const logger = Log.repository.getLogger("Marionette"); From 65f61ab2a34c250076906c0412ebea9f0d3d7215 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 18:56:45 +0000 Subject: [PATCH 032/187] Bug 1245153 - Add event.js module and remove EventUtils.js and sendkeys.js from manifest; r=automatedtester MozReview-Commit-ID: 9KqUpfp6KHG --- testing/marionette/jar.mn | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 4cbb9585f14..cae32f2cb62 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -11,11 +11,10 @@ marionette.jar: content/accessibility.js (accessibility.js) content/listener.js (listener.js) content/elements.js (elements.js) - content/sendkeys.js (sendkeys.js) content/common.js (common.js) content/simpletest.js (simpletest.js) content/frame-manager.js (frame-manager.js) - content/EventUtils.js (EventUtils.js) + content/event.js (event.js) content/ChromeUtils.js (ChromeUtils.js) content/error.js (error.js) content/message.js (message.js) From 02dea22eb2f4ed1a0c9f2a3a3055ef98587a1677 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:00:46 +0000 Subject: [PATCH 033/187] Bug 1245153 - Remove testing/marionette/atoms; r=automatedtester MozReview-Commit-ID: 1aeRNnvqmcS --- .../marionette/{atoms/atoms.js => atom.js} | 0 testing/marionette/atoms/HOWTO | 29 ------------------- testing/marionette/atoms/jar.mn | 7 ----- testing/marionette/atoms/moz.build | 7 ----- testing/marionette/driver.js | 2 +- testing/marionette/elements.js | 2 +- testing/marionette/interactions.js | 2 +- testing/marionette/jar.mn | 1 + testing/marionette/listener.js | 2 +- testing/marionette/moz.build | 2 +- 10 files changed, 6 insertions(+), 48 deletions(-) rename testing/marionette/{atoms/atoms.js => atom.js} (100%) delete mode 100644 testing/marionette/atoms/HOWTO delete mode 100644 testing/marionette/atoms/jar.mn delete mode 100644 testing/marionette/atoms/moz.build diff --git a/testing/marionette/atoms/atoms.js b/testing/marionette/atom.js similarity index 100% rename from testing/marionette/atoms/atoms.js rename to testing/marionette/atom.js diff --git a/testing/marionette/atoms/HOWTO b/testing/marionette/atoms/HOWTO deleted file mode 100644 index 72829e665e3..00000000000 --- a/testing/marionette/atoms/HOWTO +++ /dev/null @@ -1,29 +0,0 @@ -These atoms are generated from the selenium trunk. They are minified versions of what's in the trunk, -optimized to run on Firefox. To generate them, clone the repo: - - svn checkout http://selenium.googlecode.com/svn/trunk/ selenium-read-only - -then run the Google closure compiler and specify which atom you'd like to get. -For example, this will generate the "get_text" atom: - - cd selenium-read-only - ./go //javascript/webdriver/atoms:get_text:firefox - -This generates the atom, which is a function. You'll need to assign that function to a variable of your choice -which you can then import, i.e.: you'll need to modify the atom with a variable assignment: - - var myVar = - -You can now import this atom and call it with myVar(). Please note the name of the function as a comment above this line to help readability in the atoms file. - -For more information on atoms, refer to http://code.google.com/p/selenium/wiki/AutomationAtoms#Atoms_Summary - -Currently bundled atoms (please update as you add more): -- clearElement -- click -- getAttributeValue -- getElementText -- isElementDisplayed -- isElementEnabled -- isElementSelected -- sendKeysToElement/type diff --git a/testing/marionette/atoms/jar.mn b/testing/marionette/atoms/jar.mn deleted file mode 100644 index 99d47021c84..00000000000 --- a/testing/marionette/atoms/jar.mn +++ /dev/null @@ -1,7 +0,0 @@ -# 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/. - -marionette.jar: -% content marionette %content/ - content/atoms.js (atoms.js) diff --git a/testing/marionette/atoms/moz.build b/testing/marionette/atoms/moz.build deleted file mode 100644 index c97072bba2d..00000000000 --- a/testing/marionette/atoms/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index f699e17eace..3695def67e8 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -24,7 +24,7 @@ XPCOMUtils.defineLazyServiceGetter( this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2"); Cu.import("chrome://marionette/content/actions.js"); -Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/interactions.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/event.js"); diff --git a/testing/marionette/elements.js b/testing/marionette/elements.js index 297178d75f9..21e55d70a97 100644 --- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -6,7 +6,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; -Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/error.js"); /** diff --git a/testing/marionette/interactions.js b/testing/marionette/interactions.js index 5cea585322b..25c2b0fa5ce 100644 --- a/testing/marionette/interactions.js +++ b/testing/marionette/interactions.js @@ -7,7 +7,7 @@ const {utils: Cu} = Components; Cu.import("chrome://marionette/content/accessibility.js"); -Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/event.js"); diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index cae32f2cb62..7bd349d361b 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -24,6 +24,7 @@ marionette.jar: content/proxy.js (proxy.js) content/capture.js (capture.js) content/cookies.js (cookies.js) + content/atom.js (atom.js) #ifdef ENABLE_TESTS content/test.xul (client/marionette/chrome/test.xul) content/test2.xul (client/marionette/chrome/test2.xul) diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index 7b61fb76f18..d64646908de 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -14,7 +14,7 @@ loader.loadSubScript("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); Cu.import("chrome://marionette/content/actions.js"); -Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/capture.js"); Cu.import("chrome://marionette/content/cookies.js"); Cu.import("chrome://marionette/content/elements.js"); diff --git a/testing/marionette/moz.build b/testing/marionette/moz.build index 9c0b3a39f60..2df34a120fa 100644 --- a/testing/marionette/moz.build +++ b/testing/marionette/moz.build @@ -2,7 +2,7 @@ # 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/. -DIRS += ["components", "atoms"] +DIRS += ["components"] JAR_MANIFESTS += ["jar.mn"] MARIONETTE_UNIT_MANIFESTS += ['client/marionette/tests/unit/unit-tests.ini'] From ef3803b9b2b74c4316b1d248e0fbabf71fec07dd Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:14:10 +0000 Subject: [PATCH 034/187] Bug 1245153 - Rename elements.js to element.js; r=automatedtester MozReview-Commit-ID: KAGGXOHMvyM --- testing/marionette/driver.js | 2 +- testing/marionette/{elements.js => element.js} | 0 testing/marionette/event.js | 2 +- testing/marionette/interactions.js | 2 +- testing/marionette/jar.mn | 2 +- testing/marionette/listener.js | 2 +- testing/marionette/server.js | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename testing/marionette/{elements.js => element.js} (100%) diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 3695def67e8..77f46bdde61 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -26,7 +26,7 @@ XPCOMUtils.defineLazyServiceGetter( Cu.import("chrome://marionette/content/actions.js"); Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/interactions.js"); -Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/event.js"); Cu.import("chrome://marionette/content/frame-manager.js"); Cu.import("chrome://marionette/content/error.js"); diff --git a/testing/marionette/elements.js b/testing/marionette/element.js similarity index 100% rename from testing/marionette/elements.js rename to testing/marionette/element.js diff --git a/testing/marionette/event.js b/testing/marionette/event.js index 054c7e6881f..f54e1be45d0 100644 --- a/testing/marionette/event.js +++ b/testing/marionette/event.js @@ -11,7 +11,7 @@ const {interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Log.jsm"); const logger = Log.repository.getLogger("Marionette"); -Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/error.js"); this.EXPORTED_SYMBOLS = ["event"]; diff --git a/testing/marionette/interactions.js b/testing/marionette/interactions.js index 25c2b0fa5ce..d2921f0840f 100644 --- a/testing/marionette/interactions.js +++ b/testing/marionette/interactions.js @@ -9,7 +9,7 @@ const {utils: Cu} = Components; Cu.import("chrome://marionette/content/accessibility.js"); Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/event.js"); this.EXPORTED_SYMBOLS = ["Interactions"]; diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 7bd349d361b..6f4ea0bfd44 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -10,7 +10,7 @@ marionette.jar: content/interactions.js (interactions.js) content/accessibility.js (accessibility.js) content/listener.js (listener.js) - content/elements.js (elements.js) + content/element.js (element.js) content/common.js (common.js) content/simpletest.js (simpletest.js) content/frame-manager.js (frame-manager.js) diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index d64646908de..8d7c9823fe0 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -17,7 +17,7 @@ Cu.import("chrome://marionette/content/actions.js"); Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/capture.js"); Cu.import("chrome://marionette/content/cookies.js"); -Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/event.js"); Cu.import("chrome://marionette/content/proxy.js"); diff --git a/testing/marionette/server.js b/testing/marionette/server.js index b709cd582f4..aa1fb273e3b 100644 --- a/testing/marionette/server.js +++ b/testing/marionette/server.js @@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("chrome://marionette/content/dispatcher.js"); Cu.import("chrome://marionette/content/driver.js"); -Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/simpletest.js"); // Bug 1083711: Load transport.js as an SDK module instead of subscript From 68f2b5133cf9b873d4c3366a32633e852335d753 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:15:54 +0000 Subject: [PATCH 035/187] Bug 1245153 - Rename interactions.js to interaction.js; r=automatedtester MozReview-Commit-ID: F51ENbkcoR2 --- testing/marionette/driver.js | 2 +- testing/marionette/{interactions.js => interaction.js} | 0 testing/marionette/jar.mn | 2 +- testing/marionette/listener.js | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename testing/marionette/{interactions.js => interaction.js} (100%) diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 77f46bdde61..a308b553e3e 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -25,7 +25,7 @@ XPCOMUtils.defineLazyServiceGetter( Cu.import("chrome://marionette/content/actions.js"); Cu.import("chrome://marionette/content/atom.js"); -Cu.import("chrome://marionette/content/interactions.js"); +Cu.import("chrome://marionette/content/interaction.js"); Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/event.js"); Cu.import("chrome://marionette/content/frame-manager.js"); diff --git a/testing/marionette/interactions.js b/testing/marionette/interaction.js similarity index 100% rename from testing/marionette/interactions.js rename to testing/marionette/interaction.js diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 6f4ea0bfd44..033ff458daa 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -7,7 +7,7 @@ marionette.jar: content/server.js (server.js) content/driver.js (driver.js) content/actions.js (actions.js) - content/interactions.js (interactions.js) + content/interaction.js (interaction.js) content/accessibility.js (accessibility.js) content/listener.js (listener.js) content/element.js (element.js) diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index 8d7c9823fe0..e52a7b8d5af 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -21,7 +21,7 @@ Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/event.js"); Cu.import("chrome://marionette/content/proxy.js"); -Cu.import("chrome://marionette/content/interactions.js"); +Cu.import("chrome://marionette/content/interaction.js"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); From cfed1321e62de166be912c1a43f16cf0a6964600 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:18:30 +0000 Subject: [PATCH 036/187] Bug 1245153 - Remove testing/marionette/ChromeUtils.js; r=automatedtester This does not appear to be in use. MozReview-Commit-ID: B4M335Lb1UM --- testing/marionette/ChromeUtils.js | 291 ------------------------------ testing/marionette/jar.mn | 1 - testing/marionette/server.js | 4 - 3 files changed, 296 deletions(-) delete mode 100644 testing/marionette/ChromeUtils.js diff --git a/testing/marionette/ChromeUtils.js b/testing/marionette/ChromeUtils.js deleted file mode 100644 index d51f96bc39b..00000000000 --- a/testing/marionette/ChromeUtils.js +++ /dev/null @@ -1,291 +0,0 @@ -/* 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/. */ - -/** - * ChromeUtils.js is a set of mochitest utilities that are used to - * synthesize events in the browser. These are only used by - * mochitest-chrome and browser-chrome tests. Originally these functions were in - * EventUtils.js, but when porting to specialPowers, we didn't want - * to move unnecessary functions. - * - * ChromeUtils.js depends on EventUtils.js being loaded. - * - */ - -/** - * Synthesize a query text content event. - * - * @param aOffset The character offset. 0 means the first character in the - * selection root. - * @param aLength The length of getting text. If the length is too long, - * the extra length is ignored. - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeQueryTextContent(aOffset, aLength, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return nullptr; - } - return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT, - aOffset, aLength, 0, 0); -} - -/** - * Synthesize a query caret rect event. - * - * @param aOffset The caret offset. 0 means left side of the first character - * in the selection root. - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeQueryCaretRect(aOffset, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return nullptr; - } - return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, - aOffset, 0, 0, 0); -} - -/** - * Synthesize a query text rect event. - * - * @param aOffset The character offset. 0 means the first character in the - * selection root. - * @param aLength The length of the text. If the length is too long, - * the extra length is ignored. - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeQueryTextRect(aOffset, aLength, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return nullptr; - } - return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT, - aOffset, aLength, 0, 0); -} - -/** - * Synthesize a query editor rect event. - * - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeQueryEditorRect(aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return nullptr; - } - return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0); -} - -/** - * Synthesize a character at point event. - * - * @param aX, aY The offset in the client area of the DOM window. - * @param aWindow Optional (If null, current |window| will be used) - * @return An nsIQueryContentEventResult object. If this failed, - * the result might be null. - */ -function synthesizeCharAtPoint(aX, aY, aWindow) -{ - var utils = _getDOMWindowUtils(aWindow); - if (!utils) { - return nullptr; - } - return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT, - 0, 0, aX, aY); -} - -/** - * Emulate a dragstart event. - * element - element to fire the dragstart event on - * expectedDragData - the data you expect the data transfer to contain afterwards - * This data is in the format: - * [ [ {type: value, data: value, test: function}, ... ], ... ] - * can be null - * aWindow - optional; defaults to the current window object. - * x - optional; initial x coordinate - * y - optional; initial y coordinate - * Returns null if data matches. - * Returns the event.dataTransfer if data does not match - * - * eqTest is an optional function if comparison can't be done with x == y; - * function (actualData, expectedData) {return boolean} - * @param actualData from dataTransfer - * @param expectedData from expectedDragData - * see bug 462172 for example of use - * - */ -function synthesizeDragStart(element, expectedDragData, aWindow, x, y) -{ - if (!aWindow) - aWindow = window; - x = x || 2; - y = y || 2; - const step = 9; - - var result = "trapDrag was not called"; - var trapDrag = function(event) { - try { - var dataTransfer = event.dataTransfer; - result = null; - if (!dataTransfer) - throw "no dataTransfer"; - if (expectedDragData == null || - dataTransfer.mozItemCount != expectedDragData.length) - throw dataTransfer; - for (var i = 0; i < dataTransfer.mozItemCount; i++) { - var dtTypes = dataTransfer.mozTypesAt(i); - if (dtTypes.length != expectedDragData[i].length) - throw dataTransfer; - for (var j = 0; j < dtTypes.length; j++) { - if (dtTypes[j] != expectedDragData[i][j].type) - throw dataTransfer; - var dtData = dataTransfer.mozGetDataAt(dtTypes[j],i); - if (expectedDragData[i][j].eqTest) { - if (!expectedDragData[i][j].eqTest(dtData, expectedDragData[i][j].data)) - throw dataTransfer; - } - else if (expectedDragData[i][j].data != dtData) - throw dataTransfer; - } - } - } catch(ex) { - result = ex; - } - event.preventDefault(); - event.stopPropagation(); - } - aWindow.addEventListener("dragstart", trapDrag, false); - synthesizeMouse(element, x, y, { type: "mousedown" }, aWindow); - x += step; y += step; - synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); - x += step; y += step; - synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); - aWindow.removeEventListener("dragstart", trapDrag, false); - synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow); - return result; -} - -/** - * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop. - * srcElement - the element to use to start the drag, usually the same as destElement - * but if destElement isn't suitable to start a drag on pass a suitable - * element for srcElement - * destElement - the element to fire the dragover, dragleave and drop events - * dragData - the data to supply for the data transfer - * This data is in the format: - * [ [ {type: value, data: value}, ...], ... ] - * dropEffect - the drop effect to set during the dragstart event, or 'move' if null - * aWindow - optional; defaults to the current window object. - * eventUtils - optional; allows you to pass in a reference to EventUtils.js. - * If the eventUtils parameter is not passed in, we assume EventUtils.js is - * in the scope. Used by browser-chrome tests. - * - * Returns the drop effect that was desired. - */ -function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, eventUtils) -{ - if (!aWindow) - aWindow = window; - - var synthesizeMouseAtCenter = (eventUtils || window).synthesizeMouseAtCenter; - var synthesizeMouse = (eventUtils || window).synthesizeMouse; - - var gWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor). - getInterface(Components.interfaces.nsIDOMWindowUtils); - var ds = Components.classes["@mozilla.org/widget/dragservice;1"]. - getService(Components.interfaces.nsIDragService); - - var dataTransfer; - var trapDrag = function(event) { - dataTransfer = event.dataTransfer; - for (var i = 0; i < dragData.length; i++) { - var item = dragData[i]; - for (var j = 0; j < item.length; j++) { - dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); - } - } - dataTransfer.dropEffect = dropEffect || "move"; - event.preventDefault(); - event.stopPropagation(); - } - - ds.startDragSession(); - - try { - // need to use real mouse action - aWindow.addEventListener("dragstart", trapDrag, true); - synthesizeMouseAtCenter(srcElement, { type: "mousedown" }, aWindow); - - var rect = srcElement.getBoundingClientRect(); - var x = rect.width / 2; - var y = rect.height / 2; - synthesizeMouse(srcElement, x, y, { type: "mousemove" }, aWindow); - synthesizeMouse(srcElement, x+10, y+10, { type: "mousemove" }, aWindow); - aWindow.removeEventListener("dragstart", trapDrag, true); - - event = aWindow.document.createEvent("DragEvents"); - event.initDragEvent("dragenter", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); - gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); - - var event = aWindow.document.createEvent("DragEvents"); - event.initDragEvent("dragover", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); - if (gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true)) { - synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); - return "none"; - } - - if (dataTransfer.dropEffect != "none") { - event = aWindow.document.createEvent("DragEvents"); - event.initDragEvent("drop", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); - gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); - } - - synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); - - return dataTransfer.dropEffect; - } finally { - ds.endDragSession(true); - } -}; - -var PluginUtils = -{ - withTestPlugin : function(callback) - { - if (typeof Components == "undefined") - { - todo(false, "Not a Mozilla-based browser"); - return false; - } - - var ph = Components.classes["@mozilla.org/plugin/host;1"] - .getService(Components.interfaces.nsIPluginHost); - var tags = ph.getPluginTags(); - - // Find the test plugin - for (var i = 0; i < tags.length; i++) - { - if (tags[i].name == "Test Plug-in") - { - callback(tags[i]); - return true; - } - } - todo(false, "Need a test plugin on this platform"); - return false; - } -}; diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 033ff458daa..a54c0e7862b 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -15,7 +15,6 @@ marionette.jar: content/simpletest.js (simpletest.js) content/frame-manager.js (frame-manager.js) content/event.js (event.js) - content/ChromeUtils.js (ChromeUtils.js) content/error.js (error.js) content/message.js (message.js) content/dispatcher.js (dispatcher.js) diff --git a/testing/marionette/server.js b/testing/marionette/server.js index aa1fb273e3b..e4de2049862 100644 --- a/testing/marionette/server.js +++ b/testing/marionette/server.js @@ -21,10 +21,6 @@ Cu.import("chrome://marionette/content/simpletest.js"); // Bug 1083711: Load transport.js as an SDK module instead of subscript loader.loadSubScript("resource://devtools/shared/transport/transport.js"); -// Preserve this import order: -var events = {}; -loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", events); - const logger = Log.repository.getLogger("Marionette"); this.EXPORTED_SYMBOLS = ["MarionetteServer"]; From dbfee71802ae7075325cf9639701c7bcf33390b4 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:25:28 +0000 Subject: [PATCH 037/187] Bug 1245153 - Rename actions.js to action.js; r=automatedtester MozReview-Commit-ID: 58v7aMH2deS --- testing/marionette/{actions.js => action.js} | 22 ++++++++++---------- testing/marionette/driver.js | 4 ++-- testing/marionette/event.js | 2 +- testing/marionette/jar.mn | 2 +- testing/marionette/listener.js | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) rename testing/marionette/{actions.js => action.js} (95%) diff --git a/testing/marionette/actions.js b/testing/marionette/action.js similarity index 95% rename from testing/marionette/actions.js rename to testing/marionette/action.js index c5db838dd09..c74d667bde1 100644 --- a/testing/marionette/actions.js +++ b/testing/marionette/action.js @@ -12,16 +12,16 @@ Cu.import("chrome://marionette/content/event.js"); const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay"; const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms -this.EXPORTED_SYMBOLS = ["actions"]; +this.EXPORTED_SYMBOLS = ["action"]; const logger = Log.repository.getLogger("Marionette"); -this.actions = {}; +this.action = {}; /** * Functionality for (single finger) action chains. */ -actions.Chain = function(checkForInterrupted) { +action.Chain = function(checkForInterrupted) { // for assigning unique ids to all touches this.nextTouchId = 1000; // keep track of active Touches @@ -47,7 +47,7 @@ actions.Chain = function(checkForInterrupted) { this.inputSource = null; }; -actions.Chain.prototype.dispatchActions = function( +action.Chain.prototype.dispatchActions = function( args, touchId, container, @@ -105,7 +105,7 @@ actions.Chain.prototype.dispatchActions = function( * @param {Object} modifiers * An object of modifier keys present. */ -actions.Chain.prototype.emitMouseEvent = function( +action.Chain.prototype.emitMouseEvent = function( doc, type, elClientX, @@ -147,7 +147,7 @@ actions.Chain.prototype.emitMouseEvent = function( /** * Reset any persisted values after a command completes. */ -actions.Chain.prototype.resetValues = function() { +action.Chain.prototype.resetValues = function() { this.onSuccess = null; this.onError = null; this.container = null; @@ -163,7 +163,7 @@ actions.Chain.prototype.resetValues = function() { * keyModifiers is an object keeping track keyDown/keyUp pairs through * an action chain. */ -actions.Chain.prototype.actions = function(chain, touchId, i, keyModifiers) { +action.Chain.prototype.actions = function(chain, touchId, i, keyModifiers) { if (i == chain.length) { this.onSuccess(touchId || null); this.resetValues(); @@ -323,7 +323,7 @@ actions.Chain.prototype.actions = function(chain, touchId, i, keyModifiers) { * Y coordinate relative to target. If unspecified, the centre of * the target is used. */ -actions.Chain.prototype.coordinates = function(target, x, y) { +action.Chain.prototype.coordinates = function(target, x, y) { let box = target.getBoundingClientRect(); if (x == null) { x = box.width / 2; @@ -341,7 +341,7 @@ actions.Chain.prototype.coordinates = function(target, x, y) { * Given an element and a pair of coordinates, returns an array of the * form [clientX, clientY, pageX, pageY, screenX, screenY]. */ -actions.Chain.prototype.getCoordinateInfo = function(el, corx, cory) { +action.Chain.prototype.getCoordinateInfo = function(el, corx, cory) { let win = el.ownerDocument.defaultView; return [ corx, // clientX @@ -361,7 +361,7 @@ actions.Chain.prototype.getCoordinateInfo = function(el, corx, cory) { * Y coordinate of the location to generate the event that is relative * to the viewport. */ -actions.Chain.prototype.generateEvents = function( +action.Chain.prototype.generateEvents = function( type, x, y, touchId, target, keyModifiers) { this.lastCoordinates = [x, y]; let doc = this.container.frame.document; @@ -494,7 +494,7 @@ actions.Chain.prototype.generateEvents = function( this.checkForInterrupted(); }; -actions.Chain.prototype.mouseTap = function(doc, x, y, button, count, mod) { +action.Chain.prototype.mouseTap = function(doc, x, y, button, count, mod) { this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod); this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod); this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod); diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index a308b553e3e..c56416ed50a 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -23,7 +23,7 @@ this.DevToolsUtils = devtools.require("devtools/shared/DevToolsUtils"); XPCOMUtils.defineLazyServiceGetter( this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2"); -Cu.import("chrome://marionette/content/actions.js"); +Cu.import("chrome://marionette/content/action.js"); Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/interaction.js"); Cu.import("chrome://marionette/content/element.js"); @@ -133,7 +133,7 @@ this.GeckoDriver = function(appName, device, stopSignal, emulator) { this.oopFrameId = null; this.observing = null; this._browserIds = new WeakMap(); - this.actions = new actions.Chain(); + this.actions = new action.Chain(); this.sessionCapabilities = { // mandated capabilities diff --git a/testing/marionette/event.js b/testing/marionette/event.js index f54e1be45d0..4abbeb32ff7 100644 --- a/testing/marionette/event.js +++ b/testing/marionette/event.js @@ -160,7 +160,7 @@ event.sendKey = function(key, window = undefined) { event.synthesizeKey(keyName, {shiftKey: false}, window); }; -// TODO(ato): Unexpose this when actions.Chain#emitMouseEvent +// TODO(ato): Unexpose this when action.Chain#emitMouseEvent // no longer emits its own events event.parseModifiers_ = function(event) { let mval = 0; diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index a54c0e7862b..2864cc4a838 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -6,7 +6,7 @@ marionette.jar: % content marionette %content/ content/server.js (server.js) content/driver.js (driver.js) - content/actions.js (actions.js) + content/action.js (action.js) content/interaction.js (interaction.js) content/accessibility.js (accessibility.js) content/listener.js (listener.js) diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index e52a7b8d5af..cc11b3c8029 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -13,7 +13,7 @@ var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] loader.loadSubScript("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); -Cu.import("chrome://marionette/content/actions.js"); +Cu.import("chrome://marionette/content/action.js"); Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/capture.js"); Cu.import("chrome://marionette/content/cookies.js"); @@ -45,7 +45,7 @@ var elementManager = new ElementManager([]); var capabilities = {}; var interactions = new Interactions(() => capabilities); -var actions = new actions.Chain(checkForInterrupted); +var actions = new action.Chain(checkForInterrupted); var importedScripts = null; // Contains the last file input element that was the target of From 73ee3f8d9d6a850e5fcc77406a2e5430071b4435 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:28:02 +0000 Subject: [PATCH 038/187] Bug 1245153 - Rename frame-manager.js to frame.js; r=automatedtester MozReview-Commit-ID: 3s6s2LccFac --- testing/marionette/driver.js | 2 +- testing/marionette/{frame-manager.js => frame.js} | 0 testing/marionette/jar.mn | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename testing/marionette/{frame-manager.js => frame.js} (100%) diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index c56416ed50a..94de6262352 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -28,7 +28,7 @@ Cu.import("chrome://marionette/content/atom.js"); Cu.import("chrome://marionette/content/interaction.js"); Cu.import("chrome://marionette/content/element.js"); Cu.import("chrome://marionette/content/event.js"); -Cu.import("chrome://marionette/content/frame-manager.js"); +Cu.import("chrome://marionette/content/frame.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/modal.js"); Cu.import("chrome://marionette/content/proxy.js"); diff --git a/testing/marionette/frame-manager.js b/testing/marionette/frame.js similarity index 100% rename from testing/marionette/frame-manager.js rename to testing/marionette/frame.js diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 2864cc4a838..24d60837484 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -13,7 +13,7 @@ marionette.jar: content/element.js (element.js) content/common.js (common.js) content/simpletest.js (simpletest.js) - content/frame-manager.js (frame-manager.js) + content/frame.js (frame.js) content/event.js (event.js) content/error.js (error.js) content/message.js (message.js) From 308098741fd8d1af6be072885e4eeb48b4a61b2f Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Wed, 3 Feb 2016 19:41:03 +0000 Subject: [PATCH 039/187] Bug 1245153 - Lint testing/marionette/frame.js; r=automatedtester There should be no functional changes here. MozReview-Commit-ID: 360y6vHYuC3 --- testing/marionette/frame.js | 235 +++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 108 deletions(-) diff --git a/testing/marionette/frame.js b/testing/marionette/frame.js index 9d8126ddf25..de1614934df 100644 --- a/testing/marionette/frame.js +++ b/testing/marionette/frame.js @@ -4,7 +4,7 @@ "use strict"; -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -39,116 +39,130 @@ frame.RemoteFrame = function(windowId, frameId) { * * It handles explicit frame switching (switchToFrame), and implicit * frame switching, which occurs when a modal dialog is triggered in B2G. + * + * @param {GeckoDriver} driver + * Reference to the driver instance. */ -frame.Manager = function(server) { - // messageManager maintains the messageManager - // for the current process' chrome frame or the global message manager +frame.Manager = class { + constructor(driver) { + // messageManager maintains the messageManager + // for the current process' chrome frame or the global message manager - // holds a member of the remoteFrames (for an OOP frame) - // or null (for the main process) - this.currentRemoteFrame = null; - // frame we'll need to restore once interrupt is gone - this.previousRemoteFrame = null; - // set to true when we have been interrupted by a modal - this.handledModal = false; - // a reference to the Marionette server - this.server = server; -}; - -frame.Manager.prototype = { - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsIMessageListener, Ci.nsISupportsWeakReference]), + // holds a member of the remoteFrames (for an OOP frame) + // or null (for the main process) + this.currentRemoteFrame = null; + // frame we'll need to restore once interrupt is gone + this.previousRemoteFrame = null; + // set to true when we have been interrupted by a modal + this.handledModal = false; + this.driver = driver; + } /** * Receives all messages from content messageManager. */ - receiveMessage: function(message) { + receiveMessage(message) { switch (message.name) { case "MarionetteFrame:getInterruptedState": - // This will return true if the calling frame was interrupted by a modal dialog + // this will return true if the calling frame was interrupted by a modal dialog if (this.previousRemoteFrame) { - let interruptedFrame = Services.wm.getOuterWindowWithId(this.previousRemoteFrame.windowId);//get the frame window of the interrupted frame - if (this.previousRemoteFrame.frameId != null) { - interruptedFrame = interruptedFrame.document.getElementsByTagName("iframe")[this.previousRemoteFrame.frameId]; //find the OOP frame + // get the frame window of the interrupted frame + let interruptedFrame = Services.wm.getOuterWindowWithId( + this.previousRemoteFrame.windowId); + + if (this.previousRemoteFrame.frameId !== null) { + // find OOP frame + let iframes = interruptedFrame.document.getElementsByTagName("iframe"); + interruptedFrame = iframes[this.previousRemoteFrame.frameId]; } - //check if the interrupted frame is the same as the calling frame + + // check if the interrupted frame is the same as the calling frame if (interruptedFrame.src == message.target.src) { return {value: this.handledModal}; } - } - else if (this.currentRemoteFrame == null) { - // we get here if previousRemoteFrame and currentRemoteFrame are null, ie: if we're in a non-OOP process, or we haven't switched into an OOP frame, in which case, handledModal can't be set to true. + + // we get here if previousRemoteFrame and currentRemoteFrame are null, + // i.e. if we're in a non-OOP process, or we haven't switched into an OOP frame, + // in which case, handledModal can't be set to true + } else if (this.currentRemoteFrame === null) { return {value: this.handledModal}; } return {value: false}; + // handleModal is called when we need to switch frames to the main + // process due to a modal dialog interrupt case "MarionetteFrame:handleModal": - /* - * handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt. - */ - // If previousRemoteFrame was set, that means we switched into a remote frame. - // If this is the case, then we want to switch back into the system frame. - // If it isn't the case, then we're in a non-OOP environment, so we don't need to handle remote frames + // If previousRemoteFrame was set, that means we switched into a + // remote frame. If this is the case, then we want to switch back + // into the system frame. If it isn't the case, then we're in a + // non-OOP environment, so we don't need to handle remote frames. let isLocal = true; - if (this.currentRemoteFrame != null) { + if (this.currentRemoteFrame !== null) { isLocal = false; - this.removeMessageManagerListeners(this.currentRemoteFrame.messageManager.get()); - //store the previous frame so we can switch back to it when the modal is dismissed + this.removeMessageManagerListeners( + this.currentRemoteFrame.messageManager.get()); + + // store the previous frame so we can switch back to it when + // the modal is dismissed this.previousRemoteFrame = this.currentRemoteFrame; - //by setting currentRemoteFrame to null, it signifies we're in the main process + + // by setting currentRemoteFrame to null, + // it signifies we're in the main process this.currentRemoteFrame = null; - this.server.messageManager = Cc["@mozilla.org/globalmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster); + this.driver.messageManager = Cc["@mozilla.org/globalmessagemanager;1"] + .getService(Ci.nsIMessageBroadcaster); } + this.handledModal = true; - this.server.sendOk(this.server.command_id); + this.driver.sendOk(this.driver.command_id); return {value: isLocal}; case "MarionetteFrame:getCurrentFrameId": - if (this.currentRemoteFrame != null) { + if (this.currentRemoteFrame !== null) { return this.currentRemoteFrame.frameId; } } - }, + } - getOopFrame: function(winId, frameId) { + getOopFrame(winId, frameId) { // get original frame window let outerWin = Services.wm.getOuterWindowWithId(winId); // find the OOP frame let f = outerWin.document.getElementsByTagName("iframe")[frameId]; return f; - }, + } - getFrameMM: function(winId, frameId) { + getFrameMM(winId, frameId) { let oopFrame = this.getOopFrame(winId, frameId); let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner) .frameLoader.messageManager; return mm; - }, + } /** * Switch to OOP frame. We're handling this here so we can maintain * a list of remote frames. */ - switchToFrame: function(winId, frameId) { + switchToFrame(winId, frameId) { let oopFrame = this.getOopFrame(winId, frameId); let mm = this.getFrameMM(winId, frameId); - // See if this frame already has our frame script loaded in it; - // if so, just wake it up. + // see if this frame already has our frame script loaded in it; + // if so, just wake it up for (let i = 0; i < remoteFrames.length; i++) { - let frame = remoteFrames[i]; - let frameMessageManager = frame.messageManager.get(); + let f = remoteFrames[i]; + let fmm = f.messageManager.get(); try { - frameMessageManager.sendAsyncMessage("aliveCheck", {}); + fmm.sendAsyncMessage("aliveCheck", {}); } catch (e) { - if (e.result == Components.results.NS_ERROR_NOT_INITIALIZED) { + if (e.result == Cr.NS_ERROR_NOT_INITIALIZED) { remoteFrames.splice(i--, 1); continue; } } - if (frameMessageManager == mm) { - this.currentRemoteFrame = frame; + + if (fmm == mm) { + this.currentRemoteFrame = f; this.addMessageManagerListeners(mm); mm.sendAsyncMessage("Marionette:restart"); @@ -156,90 +170,95 @@ frame.Manager.prototype = { } } - // If we get here, then we need to load the frame script in this frame, + // if we get here, then we need to load the frame script in this frame, // and set the frame's ChromeMessageSender as the active message manager - // the server will listen to. + // the driver will listen to. this.addMessageManagerListeners(mm); - let aFrame = new frame.RemoteFrame(winId, frameId); - aFrame.messageManager = Cu.getWeakReference(mm); - remoteFrames.push(aFrame); - this.currentRemoteFrame = aFrame; + let f = new frame.RemoteFrame(winId, frameId); + f.messageManager = Cu.getWeakReference(mm); + remoteFrames.push(f); + this.currentRemoteFrame = f; mm.loadFrameScript(FRAME_SCRIPT, true, true); return oopFrame.id; - }, + } /* - * This function handles switching back to the frame that was interrupted by the modal dialog. - * This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process + * This function handles switching back to the frame that was + * interrupted by the modal dialog. It gets called by the interrupted + * frame once the dialog is dismissed and the frame resumes its process. */ - switchToModalOrigin: function() { - //only handle this if we indeed switched out of the modal's originating frame - if (this.previousRemoteFrame != null) { + switchToModalOrigin() { + // only handle this if we indeed switched out of the modal's + // originating frame + if (this.previousRemoteFrame !== null) { this.currentRemoteFrame = this.previousRemoteFrame; - this.addMessageManagerListeners(this.currentRemoteFrame.messageManager.get()); + let mm = this.currentRemoteFrame.messageManager.get(); + this.addMessageManagerListeners(mm); } this.handledModal = false; - }, + } /** - * Adds message listeners to the server, - * listening for messages from content frame scripts. - * It also adds a MarionetteFrame:getInterruptedState - * message listener to the FrameManager, - * so the frame manager's state can be checked by the frame. + * Adds message listeners to the driver, listening for + * messages from content frame scripts. It also adds a + * MarionetteFrame:getInterruptedState message listener to the + * FrameManager, so the frame manager's state can be checked by the frame. * * @param {nsIMessageListenerManager} mm * The message manager object, typically * ChromeMessageBroadcaster or ChromeMessageSender. */ - addMessageManagerListeners: function(mm) { - mm.addWeakMessageListener("Marionette:ok", this.server); - mm.addWeakMessageListener("Marionette:done", this.server); - mm.addWeakMessageListener("Marionette:error", this.server); - mm.addWeakMessageListener("Marionette:emitTouchEvent", this.server); - mm.addWeakMessageListener("Marionette:log", this.server); - mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.server.emulator); - mm.addWeakMessageListener("Marionette:runEmulatorShell", this.server.emulator); - mm.addWeakMessageListener("Marionette:shareData", this.server); - mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.server); - mm.addWeakMessageListener("Marionette:switchedToFrame", this.server); - mm.addWeakMessageListener("Marionette:getVisibleCookies", this.server); - mm.addWeakMessageListener("Marionette:register", this.server); - mm.addWeakMessageListener("Marionette:listenersAttached", this.server); - mm.addWeakMessageListener("Marionette:getFiles", this.server); + addMessageManagerListeners(mm) { + mm.addWeakMessageListener("Marionette:ok", this.driver); + mm.addWeakMessageListener("Marionette:done", this.driver); + mm.addWeakMessageListener("Marionette:error", this.driver); + mm.addWeakMessageListener("Marionette:emitTouchEvent", this.driver); + mm.addWeakMessageListener("Marionette:log", this.driver); + mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.driver.emulator); + mm.addWeakMessageListener("Marionette:runEmulatorShell", this.driver.emulator); + mm.addWeakMessageListener("Marionette:shareData", this.driver); + mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.driver); + mm.addWeakMessageListener("Marionette:switchedToFrame", this.driver); + mm.addWeakMessageListener("Marionette:getVisibleCookies", this.driver); + mm.addWeakMessageListener("Marionette:register", this.driver); + mm.addWeakMessageListener("Marionette:listenersAttached", this.driver); + mm.addWeakMessageListener("Marionette:getFiles", this.driver); mm.addWeakMessageListener("MarionetteFrame:handleModal", this); mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this); - }, + } /** * Removes listeners for messages from content frame scripts. - * We do not remove the MarionetteFrame:getInterruptedState - * or the Marionette:switchToModalOrigin message listener, - * because we want to allow all known frames to contact the frame manager - * so that it can check if it was interrupted, and if so, - * it will call switchToModalOrigin when its process gets resumed. + * We do not remove the MarionetteFrame:getInterruptedState or + * the Marionette:switchToModalOrigin message listener, because we + * want to allow all known frames to contact the frame manager so + * that it can check if it was interrupted, and if so, it will call + * switchToModalOrigin when its process gets resumed. * * @param {nsIMessageListenerManager} mm * The message manager object, typically * ChromeMessageBroadcaster or ChromeMessageSender. */ - removeMessageManagerListeners: function(mm) { - mm.removeWeakMessageListener("Marionette:ok", this.server); - mm.removeWeakMessageListener("Marionette:done", this.server); - mm.removeWeakMessageListener("Marionette:error", this.server); - mm.removeWeakMessageListener("Marionette:log", this.server); - mm.removeWeakMessageListener("Marionette:shareData", this.server); - mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server.emulator); - mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.server.emulator); - mm.removeWeakMessageListener("Marionette:switchedToFrame", this.server); - mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.server); - mm.removeWeakMessageListener("Marionette:listenersAttached", this.server); - mm.removeWeakMessageListener("Marionette:register", this.server); - mm.removeWeakMessageListener("Marionette:getFiles", this.server); + removeMessageManagerListeners(mm) { + mm.removeWeakMessageListener("Marionette:ok", this.driver); + mm.removeWeakMessageListener("Marionette:done", this.driver); + mm.removeWeakMessageListener("Marionette:error", this.driver); + mm.removeWeakMessageListener("Marionette:log", this.driver); + mm.removeWeakMessageListener("Marionette:shareData", this.driver); + mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.driver.emulator); + mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.driver.emulator); + mm.removeWeakMessageListener("Marionette:switchedToFrame", this.driver); + mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.driver); + mm.removeWeakMessageListener("Marionette:listenersAttached", this.driver); + mm.removeWeakMessageListener("Marionette:register", this.driver); + mm.removeWeakMessageListener("Marionette:getFiles", this.driver); mm.removeWeakMessageListener("MarionetteFrame:handleModal", this); mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); } }; + +frame.Manager.prototype.QueryInterface = XPCOMUtils.generateQI( + [Ci.nsIMessageListener, Ci.nsISupportsWeakReference]); From a71035026a0b674367aaa8f9f4b4cfd9b5abd2a2 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Mon, 8 Feb 2016 13:59:40 +0000 Subject: [PATCH 040/187] Bug 1245153 - Make EventUtils.js use aWindow argument for sub-calls consistently; r=jmaher EventUtils.js previously allowed you to override the Window object reference through passing it as an optional argument to its functions. This change fixes certain uses of implicit globals that reside on Window. MozReview-Commit-ID: EJT8iIs85ej --- .../mochitest/tests/SimpleTest/EventUtils.js | 117 +++++++++--------- 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index 06f115c6f76..c57a8082237 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -600,75 +600,75 @@ function _computeKeyCodeFromChar(aChar) if (aChar.length != 1) { return 0; } - const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent; + const KeyEvent = _EU_Ci.nsIDOMKeyEvent; if (aChar >= 'a' && aChar <= 'z') { - return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); + return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); } if (aChar >= 'A' && aChar <= 'Z') { - return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); + return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); } if (aChar >= '0' && aChar <= '9') { - return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); + return KeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); } // returns US keyboard layout's keycode switch (aChar) { case '~': case '`': - return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; + return KeyEvent.DOM_VK_BACK_QUOTE; case '!': - return nsIDOMKeyEvent.DOM_VK_1; + return KeyEvent.DOM_VK_1; case '@': - return nsIDOMKeyEvent.DOM_VK_2; + return KeyEvent.DOM_VK_2; case '#': - return nsIDOMKeyEvent.DOM_VK_3; + return KeyEvent.DOM_VK_3; case '$': - return nsIDOMKeyEvent.DOM_VK_4; + return KeyEvent.DOM_VK_4; case '%': - return nsIDOMKeyEvent.DOM_VK_5; + return KeyEvent.DOM_VK_5; case '^': - return nsIDOMKeyEvent.DOM_VK_6; + return KeyEvent.DOM_VK_6; case '&': - return nsIDOMKeyEvent.DOM_VK_7; + return KeyEvent.DOM_VK_7; case '*': - return nsIDOMKeyEvent.DOM_VK_8; + return KeyEvent.DOM_VK_8; case '(': - return nsIDOMKeyEvent.DOM_VK_9; + return KeyEvent.DOM_VK_9; case ')': - return nsIDOMKeyEvent.DOM_VK_0; + return KeyEvent.DOM_VK_0; case '-': case '_': - return nsIDOMKeyEvent.DOM_VK_SUBTRACT; + return KeyEvent.DOM_VK_SUBTRACT; case '+': case '=': - return nsIDOMKeyEvent.DOM_VK_EQUALS; + return KeyEvent.DOM_VK_EQUALS; case '{': case '[': - return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; + return KeyEvent.DOM_VK_OPEN_BRACKET; case '}': case ']': - return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; + return KeyEvent.DOM_VK_CLOSE_BRACKET; case '|': case '\\': - return nsIDOMKeyEvent.DOM_VK_BACK_SLASH; + return KeyEvent.DOM_VK_BACK_SLASH; case ':': case ';': - return nsIDOMKeyEvent.DOM_VK_SEMICOLON; + return KeyEvent.DOM_VK_SEMICOLON; case '\'': case '"': - return nsIDOMKeyEvent.DOM_VK_QUOTE; + return KeyEvent.DOM_VK_QUOTE; case '<': case ',': - return nsIDOMKeyEvent.DOM_VK_COMMA; + return KeyEvent.DOM_VK_COMMA; case '>': case '.': - return nsIDOMKeyEvent.DOM_VK_PERIOD; + return KeyEvent.DOM_VK_PERIOD; case '?': case '/': - return nsIDOMKeyEvent.DOM_VK_SLASH; + return KeyEvent.DOM_VK_SLASH; case '\n': - return nsIDOMKeyEvent.DOM_VK_RETURN; + return KeyEvent.DOM_VK_RETURN; case ' ': - return nsIDOMKeyEvent.DOM_VK_SPACE; + return KeyEvent.DOM_VK_SPACE; default: return 0; } @@ -711,15 +711,15 @@ function _computeKeyCodeFromChar(aChar) * * aWindow is optional, and defaults to the current window object. */ -function synthesizeKey(aKey, aEvent, aWindow) +function synthesizeKey(aKey, aEvent, aWindow = window) { var TIP = _getTIP(aWindow); if (!TIP) { return; } - var modifiers = _emulateToActivateModifiers(TIP, aEvent); - var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent); - var keyEvent = new KeyboardEvent("", keyEventDict.dictionary); + var modifiers = _emulateToActivateModifiers(TIP, aEvent, aWindow); + var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent, aWindow); + var keyEvent = new aWindow.KeyboardEvent("", keyEventDict.dictionary); var dispatchKeydown = !("type" in aEvent) || aEvent.type === "keydown" || !aEvent.type; var dispatchKeyup = @@ -730,7 +730,7 @@ function synthesizeKey(aKey, aEvent, aWindow) TIP.keydown(keyEvent, keyEventDict.flags); if ("repeat" in aEvent && aEvent.repeat > 1) { keyEventDict.dictionary.repeat = true; - var repeatedKeyEvent = new KeyboardEvent("", keyEventDict.dictionary); + var repeatedKeyEvent = new aWindow.KeyboardEvent("", keyEventDict.dictionary); for (var i = 1; i < aEvent.repeat; i++) { TIP.keydown(repeatedKeyEvent, keyEventDict.flags); } @@ -740,7 +740,7 @@ function synthesizeKey(aKey, aEvent, aWindow) TIP.keyup(keyEvent, keyEventDict.flags); } } finally { - _emulateToInactivateModifiers(TIP, modifiers); + _emulateToInactivateModifiers(TIP, modifiers, aWindow); } } @@ -864,9 +864,9 @@ const KEYBOARD_LAYOUT_THAI = */ function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers, - aChars, aUnmodifiedChars, aCallback) + aChars, aUnmodifiedChars, aCallback, aWindow = window) { - var utils = _getDOMWindowUtils(window); + var utils = _getDOMWindowUtils(aWindow); if (!utils) { return false; } @@ -989,12 +989,8 @@ function disableNonTestMouseEvents(aDisable) domutils.disableNonTestMouseEvents(aDisable); } -function _getDOMWindowUtils(aWindow) +function _getDOMWindowUtils(aWindow = window) { - if (!aWindow) { - aWindow = window; - } - // we need parent.SpecialPowers for: // layout/base/tests/test_reftests_with_caret.html // chrome: toolkit/content/tests/chrome/test_findbar.xul @@ -1055,8 +1051,9 @@ function _getTIP(aWindow, aCallback) return tip; } -function _guessKeyNameFromKeyCode(aKeyCode) +function _guessKeyNameFromKeyCode(aKeyCode, aWindow = window) { + const KeyboardEvent = aWindow.KeyboardEvent; switch (aKeyCode) { case KeyboardEvent.DOM_VK_CANCEL: return "Cancel"; @@ -1201,10 +1198,8 @@ function _guessKeyNameFromKeyCode(aKeyCode) } } -function _createKeyboardEventDictionary(aKey, aKeyEvent) -{ +function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) { var result = { dictionary: null, flags: 0 }; - var keyCodeIsDefined = "keyCode" in aKeyEvent; var keyCode = (keyCodeIsDefined && aKeyEvent.keyCode >= 0 && aKeyEvent.keyCode <= 255) ? @@ -1214,11 +1209,11 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent) keyName = aKey.substr("KEY_".length); result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY; } else if (aKey.indexOf("VK_") == 0) { - keyCode = KeyEvent["DOM_" + aKey]; + keyCode = _EU_Ci.nsIDOMKeyEvent["DOM_" + aKey]; if (!keyCode) { throw "Unknown key: " + aKey; } - keyName = _guessKeyNameFromKeyCode(keyCode); + keyName = _guessKeyNameFromKeyCode(keyCode, aWindow); result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY; } else if (aKey != "") { keyName = aKey; @@ -1244,7 +1239,7 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent) return result; } -function _emulateToActivateModifiers(aTIP, aKeyEvent) +function _emulateToActivateModifiers(aTIP, aKeyEvent, aWindow = window) { if (!aKeyEvent) { return null; @@ -1259,7 +1254,7 @@ function _emulateToActivateModifiers(aTIP, aKeyEvent) { key: "OS", attr: "osKey" }, { key: "Shift", attr: "shiftKey" }, { key: "Symbol", attr: "symbolKey" }, - { key: (navigator.platform.indexOf("Mac") >= 0) ? "Meta" : "Control", + { key: (aWindow.navigator.platform.indexOf("Mac") >= 0) ? "Meta" : "Control", attr: "accelKey" }, ], lockable: [ @@ -1278,7 +1273,7 @@ function _emulateToActivateModifiers(aTIP, aKeyEvent) if (aTIP.getModifierState(modifiers.normal[i].key)) { continue; // already activated. } - var event = new KeyboardEvent("", { key: modifiers.normal[i].key }); + var event = new aWindow.KeyboardEvent("", { key: modifiers.normal[i].key }); aTIP.keydown(event, aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); modifiers.normal[i].activated = true; @@ -1290,7 +1285,7 @@ function _emulateToActivateModifiers(aTIP, aKeyEvent) if (aTIP.getModifierState(modifiers.lockable[i].key)) { continue; // already activated. } - var event = new KeyboardEvent("", { key: modifiers.lockable[i].key }); + var event = new aWindow.KeyboardEvent("", { key: modifiers.lockable[i].key }); aTIP.keydown(event, aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); aTIP.keyup(event, @@ -1300,7 +1295,7 @@ function _emulateToActivateModifiers(aTIP, aKeyEvent) return modifiers; } -function _emulateToInactivateModifiers(aTIP, aModifiers) +function _emulateToInactivateModifiers(aTIP, aModifiers, aWindow = window) { if (!aModifiers) { return; @@ -1309,7 +1304,7 @@ function _emulateToInactivateModifiers(aTIP, aModifiers) if (!aModifiers.normal[i].activated) { continue; } - var event = new KeyboardEvent("", { key: aModifiers.normal[i].key }); + var event = new aWindow.KeyboardEvent("", { key: aModifiers.normal[i].key }); aTIP.keyup(event, aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); } @@ -1320,7 +1315,7 @@ function _emulateToInactivateModifiers(aTIP, aModifiers) if (!aTIP.getModifierState(aModifiers.lockable[i].key)) { continue; // who already inactivated this? } - var event = new KeyboardEvent("", { key: aModifiers.lockable[i].key }); + var event = new aWindow.KeyboardEvent("", { key: aModifiers.lockable[i].key }); aTIP.keydown(event, aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); aTIP.keyup(event, @@ -1355,15 +1350,15 @@ function synthesizeComposition(aEvent, aWindow, aCallback) if (!TIP) { return false; } - var modifiers = _emulateToActivateModifiers(TIP, aEvent.key); + var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow); var ret = false; var keyEventDict = "key" in aEvent ? - _createKeyboardEventDictionary(aEvent.key.key, aEvent.key) : + _createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) : { dictionary: null, flags: 0 }; var keyEvent = "key" in aEvent ? - new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "", + new aWindow.KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "", keyEventDict.dictionary) : null; try { @@ -1380,7 +1375,7 @@ function synthesizeComposition(aEvent, aWindow, aCallback) break; } } finally { - _emulateToInactivateModifiers(TIP, modifiers); + _emulateToInactivateModifiers(TIP, modifiers, aWindow); } } /** @@ -1470,20 +1465,20 @@ function synthesizeCompositionChange(aEvent, aWindow, aCallback) TIP.setCaretInPendingComposition(aEvent.caret.start); } - var modifiers = _emulateToActivateModifiers(TIP, aEvent.key); + var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow); try { var keyEventDict = "key" in aEvent ? - _createKeyboardEventDictionary(aEvent.key.key, aEvent.key) : + _createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) : { dictionary: null, flags: 0 }; var keyEvent = "key" in aEvent ? - new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "", + new aWindow.KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "", keyEventDict.dictionary) : null; TIP.flushPendingComposition(keyEvent, keyEventDict.flags); } finally { - _emulateToInactivateModifiers(TIP, modifiers); + _emulateToInactivateModifiers(TIP, modifiers, aWindow); } } From ab4418f4381f5ef012b43f04d9547c150fb41d27 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Fri, 5 Feb 2016 14:14:15 +0000 Subject: [PATCH 041/187] Bug 1245153 - Use EventUtils.js from mochikit; r=pbrosset testing/marionette/EventUtils.js has been converted to a JS module in testing/marionette/event.js and its API has changed. It was originally a copy of testing/mochitest/tests/SimpleTest/EventUtils.js, and it should be fine to use the original instead. MozReview-Commit-ID: Exi9d5rEeOz --- .../test/mochitest/code_frame-script.js | 13 ++++++++++-- devtools/client/shared/frame-script-utils.js | 21 ++----------------- devtools/client/shared/test/test-actor.js | 12 +++++++++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/devtools/client/debugger/test/mochitest/code_frame-script.js b/devtools/client/debugger/test/mochitest/code_frame-script.js index 479ab3d9cbc..075136006f9 100644 --- a/devtools/client/debugger/test/mochitest/code_frame-script.js +++ b/devtools/client/debugger/test/mochitest/code_frame-script.js @@ -4,8 +4,17 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1']. getService(Ci.mozIJSSubScriptLoader); -const EventUtils = {}; -loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils); +// Set up a dummy environment so that EventUtils works. We need to be careful to +// pass a window object into each EventUtils method we call rather than having +// it rely on the |window| global. +let EventUtils = {}; +EventUtils.window = content; +EventUtils.parent = EventUtils.window; +EventUtils._EU_Ci = Components.interfaces; +EventUtils._EU_Cc = Components.classes; +EventUtils.navigator = content.navigator; +EventUtils.KeyboardEvent = content.KeyboardEvent; +loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils); dump("Frame script loaded.\n"); diff --git a/devtools/client/shared/frame-script-utils.js b/devtools/client/shared/frame-script-utils.js index 472c1233ad2..f6abce96277 100644 --- a/devtools/client/shared/frame-script-utils.js +++ b/devtools/client/shared/frame-script-utils.js @@ -3,14 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu} = Components; const {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {}); const promise = require("promise"); loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm", "Task"); -const subScriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); -var EventUtils = {}; -subScriptLoader.loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils); + loader.lazyGetter(this, "nsIProfilerModule", () => { return Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler); }); @@ -180,20 +177,6 @@ addMessageListener("devtools:test:setAttribute", function(msg) { sendAsyncMessage("devtools:test:setAttribute"); }); -/** - * Synthesize a key event for an element. This handler doesn't send a message - * back. Consumers should listen to specific events on the inspector/highlighter - * to know when the event got synthesized. - * @param {Object} msg The msg.data part expects the following properties: - * - {String} key - * - {Object} options - */ -addMessageListener("Test:SynthesizeKey", function(msg) { - let {key, options} = msg.data; - - EventUtils.synthesizeKey(key, options, content); -}); - /** * Like document.querySelector but can go into iframes too. * ".container iframe || .sub-container div" will first try to find the node diff --git a/devtools/client/shared/test/test-actor.js b/devtools/client/shared/test/test-actor.js index 73bfdbf044a..06036a603e1 100644 --- a/devtools/client/shared/test/test-actor.js +++ b/devtools/client/shared/test/test-actor.js @@ -13,8 +13,16 @@ const {Task} = Cu.import("resource://gre/modules/Task.jsm", {}); var DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] .getService(Ci.mozIJSSubScriptLoader); -var EventUtils = {}; -loader.loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils); + +// Set up a dummy environment so that EventUtils works. We need to be careful to +// pass a window object into each EventUtils method we call rather than having +// it rely on the |window| global. +let EventUtils = {}; +EventUtils.window = {}; +EventUtils.parent = {}; +EventUtils._EU_Ci = Components.interfaces; +EventUtils._EU_Cc = Components.classes; +loader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils); const protocol = require("devtools/server/protocol"); const {Arg, Option, method, RetVal, types} = protocol; From bfabeca6975ce6a6ac83146b116f7e36911c27d9 Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Wed, 10 Feb 2016 15:19:39 -0500 Subject: [PATCH 042/187] Bug 1247382 - Improve naming of TaskCluster Linux64 debug jobs. DONTBUILD. r=jmaher MozReview-Commit-ID: 3QLSUofHvi5 --- testing/taskcluster/tasks/tests/fx_linux64_jittests.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml | 2 +- .../taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml | 2 +- testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml b/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml index fddb25a1113..be0514f984d 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml @@ -3,7 +3,7 @@ $inherits: from: 'tasks/tests/fx_desktop_unittest.yml' task: metadata: - name: '[TC] Linux64 jittests {{chunk}}' + name: '[TC] Linux64 jittest' description: Jittests run {{chunk}} extra: chunks: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml index 07003366502..854a9f837ba 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml @@ -3,7 +3,7 @@ $inherits: from: 'tasks/tests/fx_desktop_unittest.yml' task: metadata: - name: '[TC] Linux64 jsreftest {{chunk}}' + name: '[TC] Linux64 jsreftest' description: Jsreftest run {{chunk}} extra: chunks: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml index 5a74d1851cf..987f9de2bd6 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml @@ -6,7 +6,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-browser-chrome M(bc{{chunk}})' + name: '[TC] Linux64 mochitest-browser-chrome' description: Mochitest browser-chrome run {{chunk}} payload: capabilities: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml index 29e39071a50..ab6cffdbc89 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml @@ -13,7 +13,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-browser-chrome e10s M(bc{{chunk}})' + name: '[TC] Linux64 mochitest-e10s-browser-chrome' description: Mochitest browser-chrome e10s run {{chunk}} extra: chunks: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml index d962ff3b84c..ef197de2c64 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml @@ -6,7 +6,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-devtools-chrome M(dt{{chunk}})' + name: '[TC] Linux64 mochitest-devtools-chrome' description: Mochitest devtools-chrome run {{chunk}} payload: # these tests take longer than most to complete diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml index e3ba5175ec4..f1c072aa751 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml @@ -5,7 +5,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-gl M(gl)' + name: '[TC] Linux64 mochitest-gl' description: Mochitest webgl run payload: capabilities: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml index 859e72f2418..ce4d751cdac 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml @@ -6,7 +6,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-plain {{chunk}}' + name: '[TC] Linux64 mochitest {{chunk}}' description: Mochitest plain run {{chunk}} payload: maxRunTime: 5400 diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml index 2f6bef04317..f556a7ab2ca 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml @@ -5,7 +5,7 @@ task: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: - name: '[TC] Linux64 mochitest-push M(p)' + name: '[TC] Linux64 mochitest-push' description: Mochitest push run payload: capabilities: From 295f1ca3e702a4b7c47c61e490766418d5194ab0 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Thu, 11 Feb 2016 06:23:11 -0800 Subject: [PATCH 043/187] Bug 1247436 Ensure service worker registration is persisted if its resurrected from a pending uninstall. r=baku --- dom/workers/ServiceWorkerManager.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index f34ed7d6989..09f141778a2 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -1311,6 +1311,13 @@ public: mRegistration = swm->GetRegistration(mPrincipal, mScope); if (mRegistration) { + // If we are resurrecting an uninstalling registration, then persist + // it to disk again. We preemptively removed it earlier during + // unregister so that closing the window by shutting down the browser + // results in the registration being gone on restart. + if (mRegistration->mPendingUninstall) { + swm->StoreRegistration(mPrincipal, mRegistration); + } mRegistration->mPendingUninstall = false; RefPtr newest = mRegistration->Newest(); if (newest && mScriptSpec.Equals(newest->ScriptSpec())) { @@ -2467,7 +2474,12 @@ private: RefPtr swm = ServiceWorkerManager::GetInstance(); - // Could it be that we are shutting down. + // Note, we send the message to remove the registration from disk now even + // though we may only set the mPendingUninstall flag below. This is + // necessary to ensure the registration is removed if the controlled + // clients are closed by shutting down the browser. If the registration + // is resurrected by clearing mPendingUninstall then it should be saved + // to disk again. if (swm->mActor) { swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope)); } From 8fa1fe045fc885858de859b24e0d74d3fc2dbcdf Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Mon, 25 Jan 2016 09:55:57 -0500 Subject: [PATCH 044/187] Bug 1231784 - Install specialpowers and mochikit extensions at runtime via AddonManager.loadTemporaryAddon(), r=jgriffin MozReview-Commit-ID: 9I56x6Vnbf7 --- build/mobile/remoteautomation.py | 3 +- .../driver/marionette_driver/marionette.py | 5 +- testing/mochitest/bootstrap.js | 84 +++++++++++ testing/mochitest/browser-harness.xul | 2 +- testing/mochitest/browser-test-overlay.xul | 2 +- testing/mochitest/browser-test.js | 89 +++++------- testing/mochitest/install.rdf | 3 + testing/mochitest/jetpack-addon-harness.js | 10 +- testing/mochitest/jetpack-package-harness.js | 9 +- testing/mochitest/mochitest_options.py | 16 ++- testing/mochitest/moz.build | 7 +- testing/mochitest/runtests.py | 136 ++++++++++-------- testing/mochitest/runtestsb2g.py | 22 +-- testing/mochitest/runtestsremote.py | 19 ++- .../{b2g_start_script.js => start_b2g.js} | 0 testing/mochitest/start_desktop.js | 15 ++ testing/profiles/prefs_general.js | 3 + 17 files changed, 274 insertions(+), 151 deletions(-) create mode 100644 testing/mochitest/bootstrap.js rename testing/mochitest/{b2g_start_script.js => start_b2g.js} (100%) create mode 100644 testing/mochitest/start_desktop.js diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index 343f2e7bc39..3475b24feba 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -7,6 +7,7 @@ import glob import time import re import os +import posixpath import tempfile import shutil import subprocess @@ -214,7 +215,7 @@ class RemoteAutomation(Automation): try: dumpDir = tempfile.mkdtemp() - remoteCrashDir = self._remoteProfile + '/minidumps/' + remoteCrashDir = posixpath.join(self._remoteProfile, 'minidumps') if not self._devicemanager.dirExists(remoteCrashDir): # If crash reporting is enabled (MOZ_CRASHREPORTER=1), the # minidumps directory is automatically created when Fennec diff --git a/testing/marionette/driver/marionette_driver/marionette.py b/testing/marionette/driver/marionette_driver/marionette.py index 3b60feb380f..0c7f66e94a6 100644 --- a/testing/marionette/driver/marionette_driver/marionette.py +++ b/testing/marionette/driver/marionette_driver/marionette.py @@ -1149,8 +1149,11 @@ class Marionette(object): self.host, self.port, self.socket_timeout) - self.protocol, _ = self.client.connect() + + # Call wait_for_port() before attempting to connect in + # the event gecko hasn't started yet. self.wait_for_port(timeout=timeout) + self.protocol, _ = self.client.connect() body = {"capabilities": desired_capabilities, "sessionId": session_id} resp = self._send_message("newSession", body) diff --git a/testing/mochitest/bootstrap.js b/testing/mochitest/bootstrap.js new file mode 100644 index 00000000000..b6111232cf2 --- /dev/null +++ b/testing/mochitest/bootstrap.js @@ -0,0 +1,84 @@ +/* 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/. */ + +const { utils: Cu, interfaces: Ci, classes: Cc } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var WindowListener = { + // browser-test.js is only loaded into the first window. Setup that + // needs to happen in all navigator:browser windows should go here. + setupWindow: function(win) { + win.nativeConsole = win.console; + XPCOMUtils.defineLazyModuleGetter(win, "console", + "resource://gre/modules/Console.jsm"); + }, + + tearDownWindow: function(win) { + if (win.nativeConsole) { + win.console = win.nativeConsole; + win.nativeConsole = undefined; + } + }, + + onOpenWindow: function (win) { + win = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); + + win.addEventListener("load", function listener() { + win.removeEventListener("load", listener, false); + if (win.document.documentElement.getAttribute("windowtype") == "navigator:browser") { + WindowListener.setupWindow(win); + } + }, false); + } +} + +function loadMochitest(e) { + let flavor = e.detail[0]; + let url = e.detail[1]; + + let win = Services.wm.getMostRecentWindow("navigator:browser"); + win.removeEventListener('mochitest-load', loadMochitest); + + // for mochitest-plain, navigating to the url is all we need + win.loadURI(url); + if (flavor == "mochitest") { + return; + } + + WindowListener.setupWindow(win); + Services.wm.addListener(WindowListener); + + let overlay; + if (flavor == "jetpack-addon") { + overlay = "chrome://mochikit/content/jetpack-addon-overlay.xul"; + } else if (flavor == "jetpack-package") { + overlay = "chrome://mochikit/content/jetpack-package-overlay.xul"; + } else { + overlay = "chrome://mochikit/content/browser-test-overlay.xul"; + } + + win.document.loadOverlay(overlay, null); +} + +function startup(data, reason) { + let win = Services.wm.getMostRecentWindow("navigator:browser"); + // wait for event fired from start_desktop.js containing the + // suite and url to load + win.addEventListener('mochitest-load', loadMochitest); +} + +function shutdown(data, reason) { + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let win = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + WindowListener.tearDownWindow(win); + } + + Services.wm.removeListener(WindowListener); +} + +function install(data, reason) {} +function uninstall(data, reason) {} diff --git a/testing/mochitest/browser-harness.xul b/testing/mochitest/browser-harness.xul index a513b66cf09..53edaa552e6 100644 --- a/testing/mochitest/browser-harness.xul +++ b/testing/mochitest/browser-harness.xul @@ -251,7 +251,7 @@ waitForFocus(() => { // Focus the test window and start tests. waitForFocus(() => { - var Tester = new testWin.Tester(links, gDumper, testsFinished); + var Tester = new testWin.Tester(links, gDumper.structuredLogger, testsFinished); Tester.start(); }, testWin); }, window); diff --git a/testing/mochitest/browser-test-overlay.xul b/testing/mochitest/browser-test-overlay.xul index 4c5b71bdd40..2aaaf247f1b 100644 --- a/testing/mochitest/browser-test-overlay.xul +++ b/testing/mochitest/browser-test-overlay.xul @@ -7,7 +7,7 @@ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/invalidation/negative-w-component.html b/layout/reftests/invalidation/negative-w-component.html new file mode 100644 index 00000000000..b7cfcfb6c1b --- /dev/null +++ b/layout/reftests/invalidation/negative-w-component.html @@ -0,0 +1,86 @@ + + + +Change a layer's transform making negative w component. + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/invalidation/reftest.list b/layout/reftests/invalidation/reftest.list index 68e4e3e872a..3daba5e5b55 100644 --- a/layout/reftests/invalidation/reftest.list +++ b/layout/reftests/invalidation/reftest.list @@ -74,3 +74,4 @@ pref(layers.single-tile.enabled,false) != fast-scrolling.html about:blank == background-position-1.html background-position-1-ref.html == zero-opacity-animation.html about:blank == zero-opacity-text.html about:blank +== negative-w-component.html negative-w-component-ref.html From f2d70652b6a43e5e2fd9fcab7e1824f6b4b31dd8 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 12 Feb 2016 11:35:17 +1300 Subject: [PATCH 120/187] Bug 1224433 - Part 2: Clamp the invalidation rect to values that fit within nscoord. r=roc --- gfx/2d/BaseRect.h | 16 ++++++++++++++++ gfx/2d/Rect.h | 16 ---------------- layout/base/nsPresContext.cpp | 19 +++++++++++++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h index b802df5e370..9836565b57b 100644 --- a/gfx/2d/BaseRect.h +++ b/gfx/2d/BaseRect.h @@ -545,6 +545,22 @@ struct BaseRect { return rect; } + // Returns the largest rectangle that can be represented with 32-bit + // signed integers, centered around a point at 0,0. As BaseRect's represent + // the dimensions as a top-left point with a width and height, the width + // and height will be the largest positive 32-bit value. The top-left + // position coordinate is divided by two to center the rectangle around a + // point at 0,0. + static Sub MaxIntRect() + { + return Sub( + -std::numeric_limits::max() * 0.5, + -std::numeric_limits::max() * 0.5, + std::numeric_limits::max(), + std::numeric_limits::max() + ); + }; + friend std::ostream& operator<<(std::ostream& stream, const BaseRect& aRect) { return stream << '(' << aRect.x << ',' << aRect.y << ',' diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index 35e5078cac8..606aec1fa2a 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -156,22 +156,6 @@ struct RectTyped : Super(F(rect.x), F(rect.y), F(rect.width), F(rect.height)) {} - // Returns the largest rectangle that can be represented with 32-bit - // signed integers, centered around a point at 0,0. As BaseRect's represent - // the dimensions as a top-left point with a width and height, the width - // and height will be the largest positive 32-bit value. The top-left - // position coordinate is divided by two to center the rectangle around a - // point at 0,0. - static RectTyped MaxIntRect() - { - return RectTyped( - -std::numeric_limits::max() * 0.5, - -std::numeric_limits::max() * 0.5, - std::numeric_limits::max(), - std::numeric_limits::max() - ); - }; - void NudgeToIntegers() { NudgeToInteger(&(this->x)); diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 8bedc64f72f..b796ab74abc 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2452,10 +2452,21 @@ nsPresContext::NotifyInvalidation(uint32_t aFlags) void nsPresContext::NotifyInvalidation(const nsIntRect& aRect, uint32_t aFlags) { - nsRect rect(DevPixelsToAppUnits(aRect.x), - DevPixelsToAppUnits(aRect.y), - DevPixelsToAppUnits(aRect.width), - DevPixelsToAppUnits(aRect.height)); + // Prevent values from overflow after DevPixelsToAppUnits(). + // + // DevPixelsTopAppUnits() will multiple a factor (60) to the value, + // it may make the result value over the edge (overflow) of max or + // min value of int32_t. Compute the max sized dev pixel rect that + // we can support and intersect with it. + nsIntRect clampedRect = nsIntRect::MaxIntRect(); + clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel()); + + clampedRect = clampedRect.Intersect(aRect); + + nsRect rect(DevPixelsToAppUnits(clampedRect.x), + DevPixelsToAppUnits(clampedRect.y), + DevPixelsToAppUnits(clampedRect.width), + DevPixelsToAppUnits(clampedRect.height)); NotifyInvalidation(rect, aFlags); } From a68b21e0872c6cadc5ed2295811538fcd3cdd5a6 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 12 Feb 2016 11:35:35 +1300 Subject: [PATCH 121/187] Bug 1224433 - Part 3: Compute the invalidation area for preserve-3d layers by accumulating the leaves. r=roc --- gfx/layers/LayerTreeInvalidation.cpp | 33 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index f5c0693904e..4017a2ff9fc 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -231,13 +231,13 @@ struct LayerPropertiesBase : public LayerProperties return result; } - IntRect NewTransformedBounds() + virtual IntRect NewTransformedBounds() { return TransformRect(mLayer->GetVisibleRegion().ToUnknownRegion().GetBounds(), GetTransformForInvalidation(mLayer)); } - IntRect OldTransformedBounds() + virtual IntRect OldTransformedBounds() { return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform); } @@ -273,8 +273,8 @@ struct ContainerLayerProperties : public LayerPropertiesBase } } - virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, - bool& aGeometryChanged) + nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, + bool& aGeometryChanged) override { ContainerLayer* container = mLayer->AsContainerLayer(); nsIntRegion invalidOfLayer; // Invalid regions of this layer. @@ -382,6 +382,31 @@ struct ContainerLayerProperties : public LayerPropertiesBase return result; } + IntRect NewTransformedBounds() override + { + if (mLayer->Extend3DContext()) { + IntRect result; + for (UniquePtr& child : mChildren) { + result = result.Union(child->NewTransformedBounds()); + } + return result; + } + + return LayerPropertiesBase::NewTransformedBounds(); + } + + IntRect OldTransformedBounds() override + { + if (mLayer->Extend3DContext()) { + IntRect result; + for (UniquePtr& child : mChildren) { + result = result.Union(child->OldTransformedBounds()); + } + return result; + } + return LayerPropertiesBase::OldTransformedBounds(); + } + // The old list of children: AutoTArray,1> mChildren; float mPreXScale; From b4e0d304ac45cc36e0e8f8bf0a08976074c01a22 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 11 Feb 2016 14:49:54 -0800 Subject: [PATCH 122/187] Bug 1247104 - BaldrMonkey: Outline a method to reduce indenetation. r=luke --- js/src/asmjs/WasmText.cpp | 1163 +++++++++++++++++++------------------ 1 file changed, 583 insertions(+), 580 deletions(-) diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 99e7f608f4e..dc01449d4c4 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -838,586 +838,7 @@ class WasmTokenStream WasmToken fail(const char16_t* begin) const { return WasmToken(begin); } - WasmToken next() { - while (cur_ != end_ && IsWasmSpace(*cur_)) { - if (IsWasmNewLine(*cur_++)) { - lineStart_ = cur_; - line_++; - } - } - - if (cur_ == end_) - return WasmToken(WasmToken::EndOfFile, cur_, cur_); - - const char16_t* begin = cur_; - switch (*begin) { - case '"': - cur_++; - while (true) { - if (cur_ == end_) - return fail(begin); - if (*cur_ == '"') - break; - if (!ConsumeTextByte(&cur_, end_)) - return fail(begin); - } - cur_++; - return WasmToken(WasmToken::Text, begin, cur_); - - case '$': - cur_++; - while (cur_ != end_ && IsNameAfterDollar(*cur_)) - cur_++; - return WasmToken(WasmToken::Name, begin, cur_); - - case '(': - cur_++; - return WasmToken(WasmToken::OpenParen, begin, cur_); - - case ')': - cur_++; - return WasmToken(WasmToken::CloseParen, begin, cur_); - - case '+': case '-': - cur_++; - if (consume(MOZ_UTF16("infinity"))) - goto infinity; - if (consume(MOZ_UTF16("nan"))) - goto nan; - if (!IsWasmDigit(*cur_)) - break; - MOZ_FALLTHROUGH; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - CheckedInt u = 0; - if (consume(MOZ_UTF16("0x"))) { - if (cur_ == end_) - return fail(begin); - do { - if (*cur_ == '.' || *cur_ == 'p') - return LexHexFloatLiteral(begin, end_, &cur_); - uint8_t digit; - if (!IsHexDigit(*cur_, &digit)) - break; - u *= 16; - u += digit; - if (!u.isValid()) - return fail(begin); - cur_++; - } while (cur_ != end_); - } else { - while (cur_ != end_) { - if (*cur_ == '.' || *cur_ == 'e') - return LexDecFloatLiteral(begin, end_, &cur_); - if (!IsWasmDigit(*cur_)) - break; - u *= 10; - u += *cur_ - '0'; - if (!u.isValid()) - return fail(begin); - cur_++; - } - } - - uint64_t value = u.value(); - if (*begin == '-') { - if (value > uint64_t(INT64_MIN)) - return fail(begin); - value = -value; - return WasmToken(int64_t(value), begin, cur_); - } - - CheckedInt index = u.value(); - if (index.isValid()) - return WasmToken(index.value(), begin, cur_); - - return WasmToken(value, begin, cur_); - } - - case 'b': - if (consume(MOZ_UTF16("block"))) - return WasmToken(WasmToken::Block, begin, cur_); - break; - - case 'c': - if (consume(MOZ_UTF16("call"))) { - if (consume(MOZ_UTF16("_import"))) - return WasmToken(WasmToken::CallImport, begin, cur_); - return WasmToken(WasmToken::Call, begin, cur_); - } - break; - - case 'e': - if (consume(MOZ_UTF16("export"))) - return WasmToken(WasmToken::Export, begin, cur_); - break; - - case 'f': - if (consume(MOZ_UTF16("func"))) - return WasmToken(WasmToken::Func, begin, cur_); - - if (consume(MOZ_UTF16("f32"))) { - if (!consume(MOZ_UTF16("."))) - return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_); - - switch (*cur_) { - case 'a': - if (consume(MOZ_UTF16("abs"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Abs, begin, cur_); - if (consume(MOZ_UTF16("add"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_); - break; - case 'c': - if (consume(MOZ_UTF16("ceil"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Ceil, begin, cur_); - if (consume(MOZ_UTF16("const"))) - return WasmToken(WasmToken::Const, ValType::F32, begin, cur_); - if (consume(MOZ_UTF16("convert_s/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertSI32, - begin, cur_); - if (consume(MOZ_UTF16("convert_u/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertUI32, - begin, cur_); - if (consume(MOZ_UTF16("copysign"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_); - break; - case 'd': - if (consume(MOZ_UTF16("demote/f64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F32DemoteF64, - begin, cur_); - if (consume(MOZ_UTF16("div"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_); - break; - case 'e': - if (consume(MOZ_UTF16("eq"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Eq, begin, cur_); - break; - case 'f': - if (consume(MOZ_UTF16("floor"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Floor, begin, cur_); - break; - case 'g': - if (consume(MOZ_UTF16("ge"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ge, begin, cur_); - if (consume(MOZ_UTF16("gt"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_); - break; - case 'l': - if (consume(MOZ_UTF16("le"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_); - if (consume(MOZ_UTF16("lt"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_); - break; - case 'm': - if (consume(MOZ_UTF16("max"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_); - if (consume(MOZ_UTF16("min"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_); - if (consume(MOZ_UTF16("mul"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_); - break; - case 'n': - if (consume(MOZ_UTF16("nearest"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Nearest, begin, cur_); - if (consume(MOZ_UTF16("neg"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Neg, begin, cur_); - if (consume(MOZ_UTF16("ne"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ne, begin, cur_); - break; - case 'r': - if (consume(MOZ_UTF16("reinterpret/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32, - begin, cur_); - break; - case 's': - if (consume(MOZ_UTF16("sqrt"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_); - if (consume(MOZ_UTF16("sub"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_); - break; - case 't': - if (consume(MOZ_UTF16("trunc"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_); - break; - } - break; - } - if (consume(MOZ_UTF16("f64"))) { - if (!consume(MOZ_UTF16("."))) - return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_); - - switch (*cur_) { - case 'a': - if (consume(MOZ_UTF16("abs"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Abs, begin, cur_); - if (consume(MOZ_UTF16("add"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_); - break; - case 'c': - if (consume(MOZ_UTF16("ceil"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Ceil, begin, cur_); - if (consume(MOZ_UTF16("const"))) - return WasmToken(WasmToken::Const, ValType::F64, begin, cur_); - if (consume(MOZ_UTF16("convert_s/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertSI32, - begin, cur_); - if (consume(MOZ_UTF16("convert_u/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertUI32, - begin, cur_); - if (consume(MOZ_UTF16("copysign"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_); - break; - case 'd': - if (consume(MOZ_UTF16("div"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_); - break; - case 'e': - if (consume(MOZ_UTF16("eq"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Eq, begin, cur_); - break; - case 'f': - if (consume(MOZ_UTF16("floor"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Floor, begin, cur_); - break; - case 'g': - if (consume(MOZ_UTF16("ge"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ge, begin, cur_); - if (consume(MOZ_UTF16("gt"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_); - break; - case 'l': - if (consume(MOZ_UTF16("le"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_); - if (consume(MOZ_UTF16("lt"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_); - break; - case 'm': - if (consume(MOZ_UTF16("max"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_); - if (consume(MOZ_UTF16("min"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_); - if (consume(MOZ_UTF16("mul"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_); - break; - case 'n': - if (consume(MOZ_UTF16("nearest"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Nearest, begin, cur_); - if (consume(MOZ_UTF16("neg"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Neg, begin, cur_); - if (consume(MOZ_UTF16("ne"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_); - break; - case 'p': - if (consume(MOZ_UTF16("promote/f32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32, - begin, cur_); - break; - case 's': - if (consume(MOZ_UTF16("sqrt"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_); - if (consume(MOZ_UTF16("sub"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_); - break; - case 't': - if (consume(MOZ_UTF16("trunc"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_); - break; - } - break; - } - break; - - case 'g': - if (consume(MOZ_UTF16("get_local"))) - return WasmToken(WasmToken::GetLocal, begin, cur_); - break; - - case 'i': - if (consume(MOZ_UTF16("i32"))) { - if (!consume(MOZ_UTF16("."))) - return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_); - - switch (*cur_) { - case 'a': - if (consume(MOZ_UTF16("add"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_); - if (consume(MOZ_UTF16("and"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_); - break; - case 'c': - if (consume(MOZ_UTF16("const"))) - return WasmToken(WasmToken::Const, ValType::I32, begin, cur_); - if (consume(MOZ_UTF16("clz"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I32Clz, begin, cur_); - if (consume(MOZ_UTF16("ctz"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I32Ctz, begin, cur_); - break; - case 'd': - if (consume(MOZ_UTF16("div_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_); - if (consume(MOZ_UTF16("div_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_); - break; - case 'e': - if (consume(MOZ_UTF16("eq"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Eq, begin, cur_); - break; - case 'g': - if (consume(MOZ_UTF16("ge_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeS, begin, cur_); - if (consume(MOZ_UTF16("ge_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeU, begin, cur_); - if (consume(MOZ_UTF16("gt_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtS, begin, cur_); - if (consume(MOZ_UTF16("gt_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtU, begin, cur_); - break; - case 'l': - if (consume(MOZ_UTF16("le_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_); - if (consume(MOZ_UTF16("le_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_); - if (consume(MOZ_UTF16("lt_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_); - if (consume(MOZ_UTF16("lt_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_); - break; - case 'm': - if (consume(MOZ_UTF16("mul"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_); - break; - case 'n': - if (consume(MOZ_UTF16("ne"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_); - break; - case 'o': - if (consume(MOZ_UTF16("or"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_); - break; - case 'p': - if (consume(MOZ_UTF16("popcnt"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I32Popcnt, begin, cur_); - break; - case 'r': - if (consume(MOZ_UTF16("reinterpret/f32"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I32ReinterpretF32, - begin, cur_); - if (consume(MOZ_UTF16("rem_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_); - if (consume(MOZ_UTF16("rem_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_); - break; - case 's': - if (consume(MOZ_UTF16("sub"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_); - if (consume(MOZ_UTF16("shl"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_); - if (consume(MOZ_UTF16("shr_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_); - if (consume(MOZ_UTF16("shr_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_); - break; - case 't': - if (consume(MOZ_UTF16("trunc_s/f32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32, - begin, cur_); - if (consume(MOZ_UTF16("trunc_s/f64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64, - begin, cur_); - if (consume(MOZ_UTF16("trunc_u/f32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF32, - begin, cur_); - if (consume(MOZ_UTF16("trunc_u/f64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF64, - begin, cur_); - break; - case 'w': - if (consume(MOZ_UTF16("wrap/i64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I32WrapI64, - begin, cur_); - break; - case 'x': - if (consume(MOZ_UTF16("xor"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_); - break; - } - break; - } - if (consume(MOZ_UTF16("i64"))) { - if (!consume(MOZ_UTF16("."))) - return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_); - - switch (*cur_) { - case 'a': - if (consume(MOZ_UTF16("add"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Add, begin, cur_); - if (consume(MOZ_UTF16("and"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64And, begin, cur_); - break; - case 'c': - if (consume(MOZ_UTF16("const"))) - return WasmToken(WasmToken::Const, ValType::I64, begin, cur_); - if (consume(MOZ_UTF16("clz"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I64Clz, begin, cur_); - if (consume(MOZ_UTF16("ctz"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I64Ctz, begin, cur_); - break; - case 'd': - if (consume(MOZ_UTF16("div_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivS, begin, cur_); - if (consume(MOZ_UTF16("div_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivU, begin, cur_); - break; - case 'e': - if (consume(MOZ_UTF16("eq"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Eq, begin, cur_); - if (consume(MOZ_UTF16("extend_s/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendSI32, - begin, cur_); - if (consume(MOZ_UTF16("extend_u/i32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendUI32, - begin, cur_); - break; - case 'g': - if (consume(MOZ_UTF16("ge_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeS, begin, cur_); - if (consume(MOZ_UTF16("ge_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeU, begin, cur_); - if (consume(MOZ_UTF16("gt_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtS, begin, cur_); - if (consume(MOZ_UTF16("gt_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtU, begin, cur_); - break; - case 'l': - if (consume(MOZ_UTF16("le_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_); - if (consume(MOZ_UTF16("le_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_); - if (consume(MOZ_UTF16("lt_s"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_); - if (consume(MOZ_UTF16("lt_u"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_); - break; - case 'm': - if (consume(MOZ_UTF16("mul"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_); - break; - case 'n': - if (consume(MOZ_UTF16("ne"))) - return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_); - break; - case 'o': - if (consume(MOZ_UTF16("or"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Or, begin, cur_); - break; - case 'p': - if (consume(MOZ_UTF16("popcnt"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I64Popcnt, begin, cur_); - break; - case 'r': - if (consume(MOZ_UTF16("reinterpret/f64"))) - return WasmToken(WasmToken::UnaryOpcode, Expr::I64ReinterpretF64, - begin, cur_); - if (consume(MOZ_UTF16("rem_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemS, begin, cur_); - if (consume(MOZ_UTF16("rem_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemU, begin, cur_); - break; - case 's': - if (consume(MOZ_UTF16("sub"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_); - if (consume(MOZ_UTF16("shl"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_); - if (consume(MOZ_UTF16("shr_s"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_); - if (consume(MOZ_UTF16("shr_u"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_); - break; - case 't': - if (consume(MOZ_UTF16("trunc_s/f32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32, - begin, cur_); - if (consume(MOZ_UTF16("trunc_s/f64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64, - begin, cur_); - if (consume(MOZ_UTF16("trunc_u/f32"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF32, - begin, cur_); - if (consume(MOZ_UTF16("trunc_u/f64"))) - return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF64, - begin, cur_); - break; - case 'x': - if (consume(MOZ_UTF16("xor"))) - return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_); - break; - } - break; - } - if (consume(MOZ_UTF16("import"))) - return WasmToken(WasmToken::Import, begin, cur_); - if (consume(MOZ_UTF16("infinity"))) { - infinity: - return WasmToken(WasmToken::Infinity, begin, cur_); - } - if (consume(MOZ_UTF16("if"))) { - if (consume(MOZ_UTF16("_else"))) - return WasmToken(WasmToken::IfElse, begin, cur_); - return WasmToken(WasmToken::If, begin, cur_); - } - break; - - case 'l': - if (consume(MOZ_UTF16("local"))) - return WasmToken(WasmToken::Local, begin, cur_); - break; - - case 'm': - if (consume(MOZ_UTF16("module"))) - return WasmToken(WasmToken::Module, begin, cur_); - if (consume(MOZ_UTF16("memory"))) - return WasmToken(WasmToken::Memory, begin, cur_); - break; - - case 'n': - if (consume(MOZ_UTF16("nan"))) { - nan: - if (consume(MOZ_UTF16(":"))) { - if (!consume(MOZ_UTF16("0x"))) - break; - uint8_t digit; - while (cur_ != end_ && IsHexDigit(*cur_, &digit)) - cur_++; - } - return WasmToken(WasmToken::NaN, begin, cur_); - } - if (consume(MOZ_UTF16("nop"))) - return WasmToken(WasmToken::Nop, begin, cur_); - break; - - case 'p': - if (consume(MOZ_UTF16("param"))) - return WasmToken(WasmToken::Param, begin, cur_); - break; - - case 'r': - if (consume(MOZ_UTF16("result"))) - return WasmToken(WasmToken::Result, begin, cur_); - break; - - case 's': - if (consume(MOZ_UTF16("set_local"))) - return WasmToken(WasmToken::SetLocal, begin, cur_); - if (consume(MOZ_UTF16("segment"))) - return WasmToken(WasmToken::Segment, begin, cur_); - break; - - default: - break; - } - - return fail(begin); - } + WasmToken next(); public: WasmTokenStream(const char16_t* text, UniqueChars* error) @@ -1478,6 +899,588 @@ class WasmTokenStream } }; +WasmToken WasmTokenStream::next() +{ + while (cur_ != end_ && IsWasmSpace(*cur_)) { + if (IsWasmNewLine(*cur_++)) { + lineStart_ = cur_; + line_++; + } + } + + if (cur_ == end_) + return WasmToken(WasmToken::EndOfFile, cur_, cur_); + + const char16_t* begin = cur_; + switch (*begin) { + case '"': + cur_++; + while (true) { + if (cur_ == end_) + return fail(begin); + if (*cur_ == '"') + break; + if (!ConsumeTextByte(&cur_, end_)) + return fail(begin); + } + cur_++; + return WasmToken(WasmToken::Text, begin, cur_); + + case '$': + cur_++; + while (cur_ != end_ && IsNameAfterDollar(*cur_)) + cur_++; + return WasmToken(WasmToken::Name, begin, cur_); + + case '(': + cur_++; + return WasmToken(WasmToken::OpenParen, begin, cur_); + + case ')': + cur_++; + return WasmToken(WasmToken::CloseParen, begin, cur_); + + case '+': case '-': + cur_++; + if (consume(MOZ_UTF16("infinity"))) + goto infinity; + if (consume(MOZ_UTF16("nan"))) + goto nan; + if (!IsWasmDigit(*cur_)) + break; + MOZ_FALLTHROUGH; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + CheckedInt u = 0; + if (consume(MOZ_UTF16("0x"))) { + if (cur_ == end_) + return fail(begin); + do { + if (*cur_ == '.' || *cur_ == 'p') + return LexHexFloatLiteral(begin, end_, &cur_); + uint8_t digit; + if (!IsHexDigit(*cur_, &digit)) + break; + u *= 16; + u += digit; + if (!u.isValid()) + return fail(begin); + cur_++; + } while (cur_ != end_); + } else { + while (cur_ != end_) { + if (*cur_ == '.' || *cur_ == 'e') + return LexDecFloatLiteral(begin, end_, &cur_); + if (!IsWasmDigit(*cur_)) + break; + u *= 10; + u += *cur_ - '0'; + if (!u.isValid()) + return fail(begin); + cur_++; + } + } + + uint64_t value = u.value(); + if (*begin == '-') { + if (value > uint64_t(INT64_MIN)) + return fail(begin); + value = -value; + return WasmToken(int64_t(value), begin, cur_); + } + + CheckedInt index = u.value(); + if (index.isValid()) + return WasmToken(index.value(), begin, cur_); + + return WasmToken(value, begin, cur_); + } + + case 'b': + if (consume(MOZ_UTF16("block"))) + return WasmToken(WasmToken::Block, begin, cur_); + break; + + case 'c': + if (consume(MOZ_UTF16("call"))) { + if (consume(MOZ_UTF16("_import"))) + return WasmToken(WasmToken::CallImport, begin, cur_); + return WasmToken(WasmToken::Call, begin, cur_); + } + break; + + case 'e': + if (consume(MOZ_UTF16("export"))) + return WasmToken(WasmToken::Export, begin, cur_); + break; + + case 'f': + if (consume(MOZ_UTF16("func"))) + return WasmToken(WasmToken::Func, begin, cur_); + + if (consume(MOZ_UTF16("f32"))) { + if (!consume(MOZ_UTF16("."))) + return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_); + + switch (*cur_) { + case 'a': + if (consume(MOZ_UTF16("abs"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Abs, begin, cur_); + if (consume(MOZ_UTF16("add"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_); + break; + case 'c': + if (consume(MOZ_UTF16("ceil"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Ceil, begin, cur_); + if (consume(MOZ_UTF16("const"))) + return WasmToken(WasmToken::Const, ValType::F32, begin, cur_); + if (consume(MOZ_UTF16("convert_s/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertSI32, + begin, cur_); + if (consume(MOZ_UTF16("convert_u/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertUI32, + begin, cur_); + if (consume(MOZ_UTF16("copysign"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_); + break; + case 'd': + if (consume(MOZ_UTF16("demote/f64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F32DemoteF64, + begin, cur_); + if (consume(MOZ_UTF16("div"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_); + break; + case 'e': + if (consume(MOZ_UTF16("eq"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Eq, begin, cur_); + break; + case 'f': + if (consume(MOZ_UTF16("floor"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Floor, begin, cur_); + break; + case 'g': + if (consume(MOZ_UTF16("ge"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ge, begin, cur_); + if (consume(MOZ_UTF16("gt"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_); + break; + case 'l': + if (consume(MOZ_UTF16("le"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_); + if (consume(MOZ_UTF16("lt"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_); + break; + case 'm': + if (consume(MOZ_UTF16("max"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_); + if (consume(MOZ_UTF16("min"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_); + if (consume(MOZ_UTF16("mul"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_); + break; + case 'n': + if (consume(MOZ_UTF16("nearest"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Nearest, begin, cur_); + if (consume(MOZ_UTF16("neg"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Neg, begin, cur_); + if (consume(MOZ_UTF16("ne"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ne, begin, cur_); + break; + case 'r': + if (consume(MOZ_UTF16("reinterpret/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32, + begin, cur_); + break; + case 's': + if (consume(MOZ_UTF16("sqrt"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_); + if (consume(MOZ_UTF16("sub"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_); + break; + case 't': + if (consume(MOZ_UTF16("trunc"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_); + break; + } + break; + } + if (consume(MOZ_UTF16("f64"))) { + if (!consume(MOZ_UTF16("."))) + return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_); + + switch (*cur_) { + case 'a': + if (consume(MOZ_UTF16("abs"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Abs, begin, cur_); + if (consume(MOZ_UTF16("add"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_); + break; + case 'c': + if (consume(MOZ_UTF16("ceil"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Ceil, begin, cur_); + if (consume(MOZ_UTF16("const"))) + return WasmToken(WasmToken::Const, ValType::F64, begin, cur_); + if (consume(MOZ_UTF16("convert_s/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertSI32, + begin, cur_); + if (consume(MOZ_UTF16("convert_u/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertUI32, + begin, cur_); + if (consume(MOZ_UTF16("copysign"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_); + break; + case 'd': + if (consume(MOZ_UTF16("div"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_); + break; + case 'e': + if (consume(MOZ_UTF16("eq"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Eq, begin, cur_); + break; + case 'f': + if (consume(MOZ_UTF16("floor"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Floor, begin, cur_); + break; + case 'g': + if (consume(MOZ_UTF16("ge"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ge, begin, cur_); + if (consume(MOZ_UTF16("gt"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_); + break; + case 'l': + if (consume(MOZ_UTF16("le"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_); + if (consume(MOZ_UTF16("lt"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_); + break; + case 'm': + if (consume(MOZ_UTF16("max"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_); + if (consume(MOZ_UTF16("min"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_); + if (consume(MOZ_UTF16("mul"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_); + break; + case 'n': + if (consume(MOZ_UTF16("nearest"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Nearest, begin, cur_); + if (consume(MOZ_UTF16("neg"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Neg, begin, cur_); + if (consume(MOZ_UTF16("ne"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_); + break; + case 'p': + if (consume(MOZ_UTF16("promote/f32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32, + begin, cur_); + break; + case 's': + if (consume(MOZ_UTF16("sqrt"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_); + if (consume(MOZ_UTF16("sub"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_); + break; + case 't': + if (consume(MOZ_UTF16("trunc"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_); + break; + } + break; + } + break; + + case 'g': + if (consume(MOZ_UTF16("get_local"))) + return WasmToken(WasmToken::GetLocal, begin, cur_); + break; + + case 'i': + if (consume(MOZ_UTF16("i32"))) { + if (!consume(MOZ_UTF16("."))) + return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_); + + switch (*cur_) { + case 'a': + if (consume(MOZ_UTF16("add"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_); + if (consume(MOZ_UTF16("and"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_); + break; + case 'c': + if (consume(MOZ_UTF16("const"))) + return WasmToken(WasmToken::Const, ValType::I32, begin, cur_); + if (consume(MOZ_UTF16("clz"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I32Clz, begin, cur_); + if (consume(MOZ_UTF16("ctz"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I32Ctz, begin, cur_); + break; + case 'd': + if (consume(MOZ_UTF16("div_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_); + if (consume(MOZ_UTF16("div_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_); + break; + case 'e': + if (consume(MOZ_UTF16("eq"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Eq, begin, cur_); + break; + case 'g': + if (consume(MOZ_UTF16("ge_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeS, begin, cur_); + if (consume(MOZ_UTF16("ge_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeU, begin, cur_); + if (consume(MOZ_UTF16("gt_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtS, begin, cur_); + if (consume(MOZ_UTF16("gt_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtU, begin, cur_); + break; + case 'l': + if (consume(MOZ_UTF16("le_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_); + if (consume(MOZ_UTF16("le_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_); + if (consume(MOZ_UTF16("lt_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_); + if (consume(MOZ_UTF16("lt_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_); + break; + case 'm': + if (consume(MOZ_UTF16("mul"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_); + break; + case 'n': + if (consume(MOZ_UTF16("ne"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_); + break; + case 'o': + if (consume(MOZ_UTF16("or"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_); + break; + case 'p': + if (consume(MOZ_UTF16("popcnt"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I32Popcnt, begin, cur_); + break; + case 'r': + if (consume(MOZ_UTF16("reinterpret/f32"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I32ReinterpretF32, + begin, cur_); + if (consume(MOZ_UTF16("rem_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_); + if (consume(MOZ_UTF16("rem_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_); + break; + case 's': + if (consume(MOZ_UTF16("sub"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_); + if (consume(MOZ_UTF16("shl"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_); + if (consume(MOZ_UTF16("shr_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_); + if (consume(MOZ_UTF16("shr_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_); + break; + case 't': + if (consume(MOZ_UTF16("trunc_s/f32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32, + begin, cur_); + if (consume(MOZ_UTF16("trunc_s/f64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64, + begin, cur_); + if (consume(MOZ_UTF16("trunc_u/f32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF32, + begin, cur_); + if (consume(MOZ_UTF16("trunc_u/f64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF64, + begin, cur_); + break; + case 'w': + if (consume(MOZ_UTF16("wrap/i64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I32WrapI64, + begin, cur_); + break; + case 'x': + if (consume(MOZ_UTF16("xor"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_); + break; + } + break; + } + if (consume(MOZ_UTF16("i64"))) { + if (!consume(MOZ_UTF16("."))) + return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_); + + switch (*cur_) { + case 'a': + if (consume(MOZ_UTF16("add"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Add, begin, cur_); + if (consume(MOZ_UTF16("and"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64And, begin, cur_); + break; + case 'c': + if (consume(MOZ_UTF16("const"))) + return WasmToken(WasmToken::Const, ValType::I64, begin, cur_); + if (consume(MOZ_UTF16("clz"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I64Clz, begin, cur_); + if (consume(MOZ_UTF16("ctz"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I64Ctz, begin, cur_); + break; + case 'd': + if (consume(MOZ_UTF16("div_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivS, begin, cur_); + if (consume(MOZ_UTF16("div_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivU, begin, cur_); + break; + case 'e': + if (consume(MOZ_UTF16("eq"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Eq, begin, cur_); + if (consume(MOZ_UTF16("extend_s/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendSI32, + begin, cur_); + if (consume(MOZ_UTF16("extend_u/i32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendUI32, + begin, cur_); + break; + case 'g': + if (consume(MOZ_UTF16("ge_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeS, begin, cur_); + if (consume(MOZ_UTF16("ge_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeU, begin, cur_); + if (consume(MOZ_UTF16("gt_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtS, begin, cur_); + if (consume(MOZ_UTF16("gt_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtU, begin, cur_); + break; + case 'l': + if (consume(MOZ_UTF16("le_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_); + if (consume(MOZ_UTF16("le_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_); + if (consume(MOZ_UTF16("lt_s"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_); + if (consume(MOZ_UTF16("lt_u"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_); + break; + case 'm': + if (consume(MOZ_UTF16("mul"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_); + break; + case 'n': + if (consume(MOZ_UTF16("ne"))) + return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_); + break; + case 'o': + if (consume(MOZ_UTF16("or"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Or, begin, cur_); + break; + case 'p': + if (consume(MOZ_UTF16("popcnt"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I64Popcnt, begin, cur_); + break; + case 'r': + if (consume(MOZ_UTF16("reinterpret/f64"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::I64ReinterpretF64, + begin, cur_); + if (consume(MOZ_UTF16("rem_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemS, begin, cur_); + if (consume(MOZ_UTF16("rem_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemU, begin, cur_); + break; + case 's': + if (consume(MOZ_UTF16("sub"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_); + if (consume(MOZ_UTF16("shl"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_); + if (consume(MOZ_UTF16("shr_s"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_); + if (consume(MOZ_UTF16("shr_u"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_); + break; + case 't': + if (consume(MOZ_UTF16("trunc_s/f32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32, + begin, cur_); + if (consume(MOZ_UTF16("trunc_s/f64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64, + begin, cur_); + if (consume(MOZ_UTF16("trunc_u/f32"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF32, + begin, cur_); + if (consume(MOZ_UTF16("trunc_u/f64"))) + return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF64, + begin, cur_); + break; + case 'x': + if (consume(MOZ_UTF16("xor"))) + return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_); + break; + } + break; + } + if (consume(MOZ_UTF16("import"))) + return WasmToken(WasmToken::Import, begin, cur_); + if (consume(MOZ_UTF16("infinity"))) { + infinity: + return WasmToken(WasmToken::Infinity, begin, cur_); + } + if (consume(MOZ_UTF16("if"))) { + if (consume(MOZ_UTF16("_else"))) + return WasmToken(WasmToken::IfElse, begin, cur_); + return WasmToken(WasmToken::If, begin, cur_); + } + break; + + case 'l': + if (consume(MOZ_UTF16("local"))) + return WasmToken(WasmToken::Local, begin, cur_); + break; + + case 'm': + if (consume(MOZ_UTF16("module"))) + return WasmToken(WasmToken::Module, begin, cur_); + if (consume(MOZ_UTF16("memory"))) + return WasmToken(WasmToken::Memory, begin, cur_); + break; + + case 'n': + if (consume(MOZ_UTF16("nan"))) { + nan: + if (consume(MOZ_UTF16(":"))) { + if (!consume(MOZ_UTF16("0x"))) + break; + uint8_t digit; + while (cur_ != end_ && IsHexDigit(*cur_, &digit)) + cur_++; + } + return WasmToken(WasmToken::NaN, begin, cur_); + } + if (consume(MOZ_UTF16("nop"))) + return WasmToken(WasmToken::Nop, begin, cur_); + break; + + case 'p': + if (consume(MOZ_UTF16("param"))) + return WasmToken(WasmToken::Param, begin, cur_); + break; + + case 'r': + if (consume(MOZ_UTF16("result"))) + return WasmToken(WasmToken::Result, begin, cur_); + break; + + case 's': + if (consume(MOZ_UTF16("set_local"))) + return WasmToken(WasmToken::SetLocal, begin, cur_); + if (consume(MOZ_UTF16("segment"))) + return WasmToken(WasmToken::Segment, begin, cur_); + break; + + default: + break; + } + + return fail(begin); +} + /*****************************************************************************/ // wasm text format parser From 1af4d5f6838e121fe503a33276fa10581833c710 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 11 Feb 2016 14:49:55 -0800 Subject: [PATCH 123/187] Bug 1247104 - BaldrMonkey: Enclose most of WasmText.cpp in anonymous namespaces. r=luke --- js/src/asmjs/WasmText.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index dc01449d4c4..a07947cafae 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -47,6 +47,8 @@ static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096; /*****************************************************************************/ // wasm AST +namespace { + class WasmAstExpr; template @@ -467,9 +469,13 @@ class WasmAstConversionOperator final : public WasmAstExpr WasmAstExpr* op() const { return op_; } }; +} // end anonymous namespace + /*****************************************************************************/ // wasm text token stream +namespace { + class WasmToken { public: @@ -1481,10 +1487,13 @@ WasmToken WasmTokenStream::next() return fail(begin); } +} // end anonymous namespace /*****************************************************************************/ // wasm text format parser +namespace { + struct WasmParseContext { WasmTokenStream ts; @@ -2213,6 +2222,8 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error) return module; } +} // end anonymous namespace + /*****************************************************************************/ // wasm function body serialization From 66178c5df7c175f111c9600425a46bb37f7b383c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 11 Feb 2016 14:49:57 -0800 Subject: [PATCH 124/187] Bug 1247104 - BaldrMonkey: Parsing, encoding, and decoding for load+store. r=luke --- js/src/asmjs/Wasm.cpp | 59 ++++ js/src/asmjs/WasmText.cpp | 340 +++++++++++++++++++-- js/src/jit-test/tests/wasm/basic-memory.js | 111 +++++++ 3 files changed, 489 insertions(+), 21 deletions(-) create mode 100644 js/src/jit-test/tests/wasm/basic-memory.js diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 4e04d180570..8b4ce98875c 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -346,6 +346,33 @@ DecodeIfElse(FunctionDecoder& f, bool hasElse, ExprType expected) : CheckType(f, ExprType::Void, expected)); } +static bool +DecodeLoadStoreAddress(FunctionDecoder &f) +{ + uint32_t offset, align; + return DecodeExpr(f, ExprType::I32) && + f.d().readVarU32(&offset) && + f.d().readVarU32(&align) && + mozilla::IsPowerOfTwo(align) && + (offset == 0 || f.fail("NYI: address offsets")) && + f.fail("NYI: wasm loads and stores"); +} + +static bool +DecodeLoad(FunctionDecoder& f, ExprType expected, ExprType type) +{ + return DecodeLoadStoreAddress(f) && + CheckType(f, type, expected); +} + +static bool +DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type) +{ + return DecodeLoadStoreAddress(f) && + DecodeExpr(f, expected) && + CheckType(f, type, expected); +} + static bool DecodeExpr(FunctionDecoder& f, ExprType expected) { @@ -539,6 +566,38 @@ DecodeExpr(FunctionDecoder& f, ExprType expected) DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I64); case Expr::F64PromoteF32: return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::F32); + case Expr::I32LoadMem: + case Expr::I32LoadMem8S: + case Expr::I32LoadMem8U: + case Expr::I32LoadMem16S: + case Expr::I32LoadMem16U: + return DecodeLoad(f, expected, ExprType::I32); + case Expr::I64LoadMem: + case Expr::I64LoadMem8S: + case Expr::I64LoadMem8U: + case Expr::I64LoadMem16S: + case Expr::I64LoadMem16U: + case Expr::I64LoadMem32S: + case Expr::I64LoadMem32U: + return DecodeLoad(f, expected, ExprType::I64); + case Expr::F32LoadMem: + return DecodeLoad(f, expected, ExprType::F32); + case Expr::F64LoadMem: + return DecodeLoad(f, expected, ExprType::F64); + case Expr::I32StoreMem: + case Expr::I32StoreMem8: + case Expr::I32StoreMem16: + return DecodeStore(f, expected, ExprType::I32); + case Expr::I64StoreMem: + case Expr::I64StoreMem8: + case Expr::I64StoreMem16: + case Expr::I64StoreMem32: + return f.fail("NYI: i64") && + DecodeStore(f, expected, ExprType::I64); + case Expr::F32StoreMem: + return DecodeStore(f, expected, ExprType::F32); + case Expr::F64StoreMem: + return DecodeStore(f, expected, ExprType::F64); default: break; } diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index a07947cafae..38359dab672 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -110,8 +110,10 @@ enum class WasmAstExprKind ConversionOperator, GetLocal, IfElse, + Load, Nop, SetLocal, + Store, UnaryOperator, }; @@ -245,6 +247,63 @@ class WasmAstIfElse : public WasmAstExpr WasmAstExpr& elseBody() const { return *elseBody_; } }; +class WasmAstLoadStoreAddress +{ + WasmAstExpr* base_; + int32_t offset_; + int32_t align_; + + public: + explicit WasmAstLoadStoreAddress(WasmAstExpr* base, int32_t offset, + int32_t align) + : base_(base), + offset_(offset), + align_(align) + {} + + WasmAstExpr& base() const { return *base_; } + int32_t offset() const { return offset_; } + int32_t align() const { return align_; } +}; + +class WasmAstLoad : public WasmAstExpr +{ + Expr expr_; + WasmAstLoadStoreAddress address_; + + public: + static const WasmAstExprKind Kind = WasmAstExprKind::Load; + explicit WasmAstLoad(Expr expr, const WasmAstLoadStoreAddress &address) + : WasmAstExpr(Kind), + expr_(expr), + address_(address) + {} + + Expr expr() const { return expr_; } + const WasmAstLoadStoreAddress& address() const { return address_; } +}; + +class WasmAstStore : public WasmAstExpr +{ + Expr expr_; + WasmAstLoadStoreAddress address_; + WasmAstExpr* value_; + + public: + static const WasmAstExprKind Kind = WasmAstExprKind::Store; + explicit WasmAstStore(Expr expr, const WasmAstLoadStoreAddress &address, + WasmAstExpr* value) + : WasmAstExpr(Kind), + expr_(expr), + address_(address), + value_(value) + {} + + Expr expr() const { return expr_; } + const WasmAstLoadStoreAddress& address() const { return address_; } + WasmAstExpr& value() const { return *value_; } +}; + class WasmAstFunc : public WasmAstNode { const uint32_t sigIndex_; @@ -489,6 +548,7 @@ class WasmToken enum Kind { + Align, BinaryOpcode, Block, Call, @@ -498,6 +558,7 @@ class WasmToken Const, ConversionOpcode, EndOfFile, + Equal, Error, Export, Float, @@ -510,15 +571,18 @@ class WasmToken UnsignedInteger, SignedInteger, Memory, + Load, Local, Module, Name, Nop, + Offset, OpenParen, Param, Result, Segment, SetLocal, + Store, Text, UnaryOpcode, ValueType @@ -594,7 +658,7 @@ class WasmToken { MOZ_ASSERT(begin != end); MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode || - kind_ == ConversionOpcode); + kind_ == ConversionOpcode || kind_ == Load || kind_ == Store); u.expr_ = expr; } explicit WasmToken(const char16_t* begin) @@ -640,7 +704,7 @@ class WasmToken } Expr expr() const { MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode || - kind_ == ConversionOpcode); + kind_ == ConversionOpcode || kind_ == Load || kind_ == Store); return u.expr_; } }; @@ -946,6 +1010,10 @@ WasmToken WasmTokenStream::next() cur_++; return WasmToken(WasmToken::CloseParen, begin, cur_); + case '=': + cur_++; + return WasmToken(WasmToken::Equal, begin, cur_); + case '+': case '-': cur_++; if (consume(MOZ_UTF16("infinity"))) @@ -1002,6 +1070,11 @@ WasmToken WasmTokenStream::next() return WasmToken(value, begin, cur_); } + case 'a': + if (consume(MOZ_UTF16("align"))) + return WasmToken(WasmToken::Align, begin, cur_); + break; + case 'b': if (consume(MOZ_UTF16("block"))) return WasmToken(WasmToken::Block, begin, cur_); @@ -1075,6 +1148,8 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_); if (consume(MOZ_UTF16("lt"))) return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_); + if (consume(MOZ_UTF16("load"))) + return WasmToken(WasmToken::Load, Expr::F32LoadMem, begin, cur_); break; case 'm': if (consume(MOZ_UTF16("max"))) @@ -1102,6 +1177,8 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_); if (consume(MOZ_UTF16("sub"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_); + if (consume(MOZ_UTF16("store"))) + return WasmToken(WasmToken::Store, Expr::F32StoreMem, begin, cur_); break; case 't': if (consume(MOZ_UTF16("trunc"))) @@ -1158,6 +1235,8 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_); if (consume(MOZ_UTF16("lt"))) return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_); + if (consume(MOZ_UTF16("load"))) + return WasmToken(WasmToken::Load, Expr::F64LoadMem, begin, cur_); break; case 'm': if (consume(MOZ_UTF16("max"))) @@ -1185,6 +1264,8 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_); if (consume(MOZ_UTF16("sub"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_); + if (consume(MOZ_UTF16("store"))) + return WasmToken(WasmToken::Store, Expr::F64StoreMem, begin, cur_); break; case 't': if (consume(MOZ_UTF16("trunc"))) @@ -1249,6 +1330,19 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_); if (consume(MOZ_UTF16("lt_u"))) return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_); + if (consume(MOZ_UTF16("load"))) { + if (IsWasmSpace(*cur_)) + return WasmToken(WasmToken::Load, Expr::I32LoadMem, begin, cur_); + if (consume(MOZ_UTF16("8_s"))) + return WasmToken(WasmToken::Load, Expr::I32LoadMem8S, begin, cur_); + if (consume(MOZ_UTF16("8_u"))) + return WasmToken(WasmToken::Load, Expr::I32LoadMem8U, begin, cur_); + if (consume(MOZ_UTF16("16_s"))) + return WasmToken(WasmToken::Load, Expr::I32LoadMem16S, begin, cur_); + if (consume(MOZ_UTF16("16_u"))) + return WasmToken(WasmToken::Load, Expr::I32LoadMem16U, begin, cur_); + break; + } break; case 'm': if (consume(MOZ_UTF16("mul"))) @@ -1284,6 +1378,15 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_); if (consume(MOZ_UTF16("shr_u"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_); + if (consume(MOZ_UTF16("store"))) { + if (IsWasmSpace(*cur_)) + return WasmToken(WasmToken::Store, Expr::I32StoreMem, begin, cur_); + if (consume(MOZ_UTF16("8"))) + return WasmToken(WasmToken::Store, Expr::I32StoreMem8, begin, cur_); + if (consume(MOZ_UTF16("16"))) + return WasmToken(WasmToken::Store, Expr::I32StoreMem16, begin, cur_); + break; + } break; case 't': if (consume(MOZ_UTF16("trunc_s/f32"))) @@ -1365,6 +1468,23 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_); if (consume(MOZ_UTF16("lt_u"))) return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_); + if (consume(MOZ_UTF16("load"))) { + if (IsWasmSpace(*cur_)) + return WasmToken(WasmToken::Load, Expr::I64LoadMem, begin, cur_); + if (consume(MOZ_UTF16("8_s"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem8S, begin, cur_); + if (consume(MOZ_UTF16("8_u"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem8U, begin, cur_); + if (consume(MOZ_UTF16("16_s"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem16S, begin, cur_); + if (consume(MOZ_UTF16("16_u"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem16U, begin, cur_); + if (consume(MOZ_UTF16("32_s"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem32S, begin, cur_); + if (consume(MOZ_UTF16("32_u"))) + return WasmToken(WasmToken::Load, Expr::I64LoadMem32U, begin, cur_); + break; + } break; case 'm': if (consume(MOZ_UTF16("mul"))) @@ -1400,6 +1520,17 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_); if (consume(MOZ_UTF16("shr_u"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_); + if (consume(MOZ_UTF16("store"))) { + if (IsWasmSpace(*cur_)) + return WasmToken(WasmToken::Store, Expr::I64StoreMem, begin, cur_); + if (consume(MOZ_UTF16("8"))) + return WasmToken(WasmToken::Store, Expr::I64StoreMem8, begin, cur_); + if (consume(MOZ_UTF16("16"))) + return WasmToken(WasmToken::Store, Expr::I64StoreMem16, begin, cur_); + if (consume(MOZ_UTF16("32"))) + return WasmToken(WasmToken::Store, Expr::I64StoreMem32, begin, cur_); + break; + } break; case 't': if (consume(MOZ_UTF16("trunc_s/f32"))) @@ -1463,6 +1594,11 @@ WasmToken WasmTokenStream::next() return WasmToken(WasmToken::Nop, begin, cur_); break; + case 'o': + if (consume(MOZ_UTF16("offset"))) + return WasmToken(WasmToken::Offset, begin, cur_); + break; + case 'p': if (consume(MOZ_UTF16("param"))) return WasmToken(WasmToken::Param, begin, cur_); @@ -1771,12 +1907,16 @@ ParseFloatLiteral(WasmParseContext& c, WasmToken token, Float* result) *result = PositiveInfinity(); break; case WasmToken::NaN: - if (!ParseNaNLiteral(cur, end, result)) + if (!ParseNaNLiteral(cur, end, result)) { + c.ts.generateError(token, c.error); return false; + } break; case WasmToken::HexNumber: - if (!ParseHexFloatLiteral(cur, end, result)) + if (!ParseHexFloatLiteral(cur, end, result)) { + c.ts.generateError(token, c.error); return false; + } break; case WasmToken::DecNumber: { // Call into JS' strtod. Tokenization has already required that the @@ -1792,6 +1932,7 @@ ParseFloatLiteral(WasmParseContext& c, WasmToken token, Float* result) Float d = (Float)js_strtod_harder(c.dtoaState, buffer, &strtod_end, &err); if (err != 0 || strtod_end == buffer) { c.lifo.release(mark); + c.ts.generateError(token, c.error); return false; } c.lifo.release(mark); @@ -1815,21 +1956,16 @@ ParseConst(WasmParseContext& c, WasmToken constToken) switch (val.kind()) { case WasmToken::Index: return new(c.lifo) WasmAstConst(Val(val.index())); - case WasmToken::UnsignedInteger: { - CheckedInt uint = val.uint(); - if (uint.isValid()) - return new(c.lifo) WasmAstConst(Val(uint.value())); - return nullptr; - } case WasmToken::SignedInteger: { CheckedInt sint = val.sint(); - if (sint.isValid()) - return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value()))); - return nullptr; + if (!sint.isValid()) + break; + return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value()))); } default: - return nullptr; + break; } + break; } case ValType::I64: { switch (val.kind()) { @@ -1840,29 +1976,31 @@ ParseConst(WasmParseContext& c, WasmToken constToken) case WasmToken::SignedInteger: return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint()))); default: - return nullptr; + break; } + break; } case ValType::F32: { if (val.kind() != WasmToken::Float) - return nullptr; + break; float result; if (!ParseFloatLiteral(c, val, &result)) - return nullptr; + break; return new(c.lifo) WasmAstConst(Val(result)); } case ValType::F64: { if (val.kind() != WasmToken::Float) - return nullptr; + break; double result; if (!ParseFloatLiteral(c, val, &result)) - return nullptr; + break; return new(c.lifo) WasmAstConst(Val(result)); } default: - c.ts.generateError(constToken, c.error); - return nullptr; + break; } + c.ts.generateError(constToken, c.error); + return nullptr; } static WasmAstGetLocal* @@ -1958,6 +2096,135 @@ ParseIfElse(WasmParseContext& c, Expr expr) return new(c.lifo) WasmAstIfElse(expr, cond, ifBody, elseBody); } +static bool +ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base, + int32_t* offset, int32_t* align) +{ + *base = ParseExpr(c); + if (!*base) + return false; + + WasmToken token = c.ts.get(); + + *offset = 0; + if (token.kind() == WasmToken::Offset) { + if (!c.ts.match(WasmToken::Equal, c.error)) + return false; + WasmToken val = c.ts.get(); + switch (val.kind()) { + case WasmToken::Index: + *offset = val.index(); + break; + default: + c.ts.generateError(val, c.error); + return false; + } + + token = c.ts.get(); + } + + *align = 0; + if (token.kind() == WasmToken::Align) { + if (!c.ts.match(WasmToken::Equal, c.error)) + return false; + WasmToken val = c.ts.get(); + switch (val.kind()) { + case WasmToken::Index: + *align = val.index(); + break; + default: + c.ts.generateError(val, c.error); + return false; + } + + token = c.ts.get(); + } + + c.ts.unget(token); + return true; +} + +static WasmAstLoad* +ParseLoad(WasmParseContext& c, Expr expr) +{ + WasmAstExpr* base; + int32_t offset; + int32_t align; + if (!ParseLoadStoreAddress(c, &base, &offset, &align)) + return nullptr; + + if (align == 0) { + switch (expr) { + case Expr::I32LoadMem8S: + case Expr::I32LoadMem8U: + case Expr::I64LoadMem8S: + case Expr::I64LoadMem8U: + align = 1; + break; + case Expr::I32LoadMem16S: + case Expr::I32LoadMem16U: + case Expr::I64LoadMem16S: + case Expr::I64LoadMem16U: + align = 2; + break; + case Expr::I32LoadMem: + case Expr::F32LoadMem: + case Expr::I64LoadMem32S: + case Expr::I64LoadMem32U: + align = 4; + break; + case Expr::I64LoadMem: + case Expr::F64LoadMem: + align = 8; + break; + default: + MOZ_CRASH("Bad load expr"); + } + } + + return new(c.lifo) WasmAstLoad(expr, WasmAstLoadStoreAddress(base, offset, align)); +} + +static WasmAstStore* +ParseStore(WasmParseContext& c, Expr expr) +{ + WasmAstExpr* base; + int32_t offset; + int32_t align; + if (!ParseLoadStoreAddress(c, &base, &offset, &align)) + return nullptr; + + if (align == 0) { + switch (expr) { + case Expr::I32StoreMem8: + case Expr::I64StoreMem8: + align = 1; + break; + case Expr::I32StoreMem16: + case Expr::I64StoreMem16: + align = 2; + break; + case Expr::I32StoreMem: + case Expr::F32StoreMem: + case Expr::I64StoreMem32: + align = 4; + break; + case Expr::I64StoreMem: + case Expr::F64StoreMem: + align = 8; + break; + default: + MOZ_CRASH("Bad load expr"); + } + } + + WasmAstExpr* value = ParseExpr(c); + if (!value) + return nullptr; + + return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value); +} + static WasmAstExpr* ParseExprInsideParens(WasmParseContext& c) { @@ -1986,8 +2253,12 @@ ParseExprInsideParens(WasmParseContext& c) return ParseIfElse(c, Expr::IfElse); case WasmToken::GetLocal: return ParseGetLocal(c); + case WasmToken::Load: + return ParseLoad(c, token.expr()); case WasmToken::SetLocal: return ParseSetLocal(c); + case WasmToken::Store: + return ParseStore(c, token.expr()); case WasmToken::UnaryOpcode: return ParseUnaryOperator(c, token.expr()); default: @@ -2341,6 +2612,29 @@ EncodeIfElse(Encoder& e, WasmAstIfElse& ie) (!ie.hasElse() || EncodeExpr(e, ie.elseBody())); } +static bool +EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address) +{ + return EncodeExpr(e, address.base()) && + e.writeVarU32(address.offset()) && + e.writeVarU32(address.align()); +} + +static bool +EncodeLoad(Encoder& e, WasmAstLoad& l) +{ + return e.writeExpr(l.expr()) && + EncodeLoadStoreAddress(e, l.address()); +} + +static bool +EncodeStore(Encoder& e, WasmAstStore& s) +{ + return e.writeExpr(s.expr()) && + EncodeLoadStoreAddress(e, s.address()) && + EncodeExpr(e, s.value()); +} + static bool EncodeExpr(Encoder& e, WasmAstExpr& expr) { @@ -2363,8 +2657,12 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr) return EncodeGetLocal(e, expr.as()); case WasmAstExprKind::IfElse: return EncodeIfElse(e, expr.as()); + case WasmAstExprKind::Load: + return EncodeLoad(e, expr.as()); case WasmAstExprKind::SetLocal: return EncodeSetLocal(e, expr.as()); + case WasmAstExprKind::Store: + return EncodeStore(e, expr.as()); case WasmAstExprKind::UnaryOperator: return EncodeUnaryOperator(e, expr.as()); default:; diff --git a/js/src/jit-test/tests/wasm/basic-memory.js b/js/src/jit-test/tests/wasm/basic-memory.js new file mode 100644 index 00000000000..7316826ef55 --- /dev/null +++ b/js/src/jit-test/tests/wasm/basic-memory.js @@ -0,0 +1,111 @@ +load(libdir + "wasm.js"); + +quit(); // TODO: Loads and stores are NYI. + +if (!wasmIsSupported()) + quit(); + +function mismatchError(actual, expect) { + var str = "type mismatch: expression has type " + actual + " but expected " + expect; + return RegExp(str); +} + +function testLoad(type, ext, base, offset, align, expect) { + print('(module' + + ' (memory 0x10000' + + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + + ' )' + + ' (func (param i32) (result ' + type + ')' + + ' (' + type + '.load' + ext + ' (get_local 0)' + + ' offset=' + offset + + ' ' + (align != 0 ? 'align=' + align : '') + + ' )' + + ' ) (export "" 0))'); + assertEq(wasmEvalText( + '(module' + + ' (memory 0x10000' + + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + + ' )' + + ' (func (param i32) (result ' + type + ')' + + ' (' + type + '.load' + ext + ' (get_local 0)' + + ' offset=' + offset + + ' ' + (align != 0 ? 'align=' + align : '') + + ' )' + + ' ) (export "" 0))' + )(base), expect); +} + +function testStore(type, ext, base, offset, align, value) { + assertEq(wasmEvalText( + '(module' + + ' (memory 0x10000)' + + ' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' + + ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + + ' )' + + ' (func (param i32) (param ' + type + ') (result ' + type + ')' + + ' (' + type + '.store' + ext + ' (get_local 0)' + + ' offset=' + offset + + ' ' + (align != 0 ? 'align=' + align : '') + + ' (get_local 1)' + + ' )' + + ' ) (export "" 0))' + )(base, value), value); +} + +function testConstError(type, str) { + // For now at least, we don't distinguish between parse errors and OOMs. + assertErrorMessage(() => wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), Error, /out of memory/); +} + +testLoad('i32', '', 0, 0, 0, 0x00010203); +testLoad('i32', '', 1, 0, 0, 0x01020304); +//testLoad('i32', '', 0, 1, 0, 0x01020304); // TODO: NYI +//testLoad('i32', '', 1, 1, 4, 0x02030405); // TODO: NYI +//testLoad('i64', '', 0, 0, 0, 0x0001020304050607); // TODO: NYI +//testLoad('i64', '', 1, 0, 0, 0x0102030405060708); // TODO: NYI +//testLoad('i64', '', 0, 1, 0, 0x0102030405060708); // TODO: NYI +//testLoad('i64', '', 1, 1, 4, 0x0203040506070809); // TODO: NYI +testLoad('f32', '', 0, 0, 0, 0x00010203); +testLoad('f32', '', 1, 0, 0, 0x01020304); +//testLoad('f32', '', 0, 1, 0, 0x01020304); // TODO: NYI +//testLoad('f32', '', 1, 1, 4, 0x02030405); // TODO: NYI +testLoad('f64', '', 0, 0, 0, 0x00010203); +testLoad('f64', '', 1, 0, 0, 0x01020304); +//testLoad('f64', '', 0, 1, 0, 0x01020304); // TODO: NYI +//testLoad('f64', '', 1, 1, 4, 0x02030405); // TODO: NYI + +testLoad('i32', '8_s', 16, 0, 0, -0x8); +testLoad('i32', '8_u', 16, 0, 0, 0x8); +testLoad('i32', '16_s', 16, 0, 0, -0x707); +testLoad('i32', '16_u', 16, 0, 0, 0x8f9); +testLoad('i64', '8_s', 16, 0, 0, -0x8); +testLoad('i64', '8_u', 16, 0, 0, 0x8); +//testLoad('i64', '16_s', 16, 0, 0, -0x707); // TODO: NYI +//testLoad('i64', '16_u', 16, 0, 0, 0x8f9); // TODO: NYI +//testLoad('i64', '32_s', 16, 0, 0, -0x7060505); // TODO: NYI +//testLoad('i64', '32_u', 16, 0, 0, 0x8f9fafb); // TODO: NYI + +testStore('i32', '', 0, 0, 0, 0xc0c1d3d4); +testStore('i32', '', 1, 0, 0, 0xc0c1d3d4); +//testStore('i32', '', 0, 1, 0, 0xc0c1d3d4); // TODO: NYI +//testStore('i32', '', 1, 1, 4, 0xc0c1d3d4); // TODO: NYI +//testStore('i64', '', 0, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI +//testStore('i64', '', 1, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI +//testStore('i64', '', 0, 1, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI +//testStore('i64', '', 1, 1, 4, 0xc0c1d3d4e6e7090a); // TODO: NYI +testStore('f32', '', 0, 0, 0, 0.01234567); +testStore('f32', '', 1, 0, 0, 0.01234567); +//testStore('f32', '', 0, 1, 0, 0.01234567); // TODO: NYI +//testStore('f32', '', 1, 1, 4, 0.01234567); // TODO: NYI +testStore('f64', '', 0, 0, 0, 0.89012345); +testStore('f64', '', 1, 0, 0, 0.89012345); +//testStore('f64', '', 0, 1, 0, 0.89012345); // TODO: NYI +//testStore('f64', '', 1, 1, 4, 0.89012345); // TODO: NYI + +testStore('i32', '8', 0, 0, 0, 0x23); +testStore('i32', '16', 0, 0, 0, 0x2345); +//testStore('i64', '8', 0, 0, 0, 0x23); // TODO: NYI +//testStore('i64', '16', 0, 0, 0, 0x23); // TODO: NYI +//testStore('i64', '32', 0, 0, 0, 0x23); // TODO: NYI From 85f24f2b8c5755d31c413ca662865f9497908795 Mon Sep 17 00:00:00 2001 From: Deepthi Venkitaramanan Date: Thu, 11 Feb 2016 17:50:42 -0500 Subject: [PATCH 125/187] Bug 1244328 - Merge the functionality of DOMSettableTokenList into DOMTokenList and make everything that used to refer to DOMSettableTokenList refer to DOMTokenList instead. r=bzbarsky --- dom/base/Element.cpp | 21 ++++++----- dom/base/Element.h | 3 +- dom/base/moz.build | 1 - dom/base/nsDOMSettableTokenList.cpp | 29 --------------- dom/base/nsDOMSettableTokenList.h | 35 ------------------- dom/base/nsDOMTokenList.cpp | 11 +++++- dom/base/nsDOMTokenList.h | 5 ++- dom/base/test/test_bug346485.html | 2 +- dom/base/test/test_classList.html | 11 +++--- dom/bindings/Bindings.conf | 4 --- dom/html/HTMLIFrameElement.h | 4 +-- dom/html/HTMLLinkElement.h | 2 +- dom/html/HTMLOutputElement.cpp | 6 ++-- dom/html/HTMLOutputElement.h | 4 +-- dom/html/HTMLPropertiesCollection.cpp | 2 +- dom/html/HTMLTableCellElement.h | 2 +- dom/html/nsGenericHTMLElement.cpp | 2 +- dom/html/nsGenericHTMLElement.h | 8 ++--- dom/html/test/forms/test_output_element.html | 2 +- dom/html/test/test_bug845057.html | 4 +-- .../html/dom/test_interface-objects.html | 3 +- dom/imptests/html/dom/test_interfaces.html | 6 ++-- dom/interfaces/html/nsIDOMHTMLElement.idl | 2 +- .../mochitest/general/test_interfaces.html | 2 -- dom/webidl/DOMSettableTokenList.webidl | 16 --------- dom/webidl/DOMTokenList.webidl | 2 ++ dom/webidl/Element.webidl | 2 +- dom/webidl/HTMLAnchorElement.webidl | 1 + dom/webidl/HTMLAreaElement.webidl | 1 + dom/webidl/HTMLElement.webidl | 8 ++--- dom/webidl/HTMLIFrameElement.webidl | 2 +- dom/webidl/HTMLLinkElement.webidl | 3 +- dom/webidl/HTMLOutputElement.webidl | 2 +- dom/webidl/HTMLTableCellElement.webidl | 2 +- dom/webidl/moz.build | 1 - .../tests/dom/interface-objects.html | 3 +- .../web-platform/tests/dom/interfaces.html | 6 +--- .../tests/html/dom/interfaces.html | 32 ++++++++--------- .../Microsoft/sandbox/sandbox_011.htm | 20 +++++------ 39 files changed, 96 insertions(+), 176 deletions(-) delete mode 100644 dom/base/nsDOMSettableTokenList.cpp delete mode 100644 dom/base/nsDOMSettableTokenList.h delete mode 100644 dom/webidl/DOMSettableTokenList.webidl diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index b84eb2faccb..54a22aea87b 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -45,7 +45,6 @@ #include "nsNameSpaceManager.h" #include "nsContentList.h" #include "nsVariant.h" -#include "nsDOMSettableTokenList.h" #include "nsDOMTokenList.h" #include "nsXBLPrototypeBinding.h" #include "nsError.h" @@ -3066,11 +3065,11 @@ Element::GetLinkTarget(nsAString& aTarget) } static void -nsDOMSettableTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty, - void *aPropertyValue, void *aData) +nsDOMTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty, + void *aPropertyValue, void *aData) { - nsDOMSettableTokenList* list = - static_cast(aPropertyValue); + nsDOMTokenList* list = + static_cast(aPropertyValue); NS_RELEASE(list); } @@ -3092,7 +3091,7 @@ Element::HTMLSVGPropertiesToTraverseAndUnlink() return sPropertiesToTraverseAndUnlink; } -nsDOMSettableTokenList* +nsDOMTokenList* Element::GetTokenList(nsIAtom* aAtom) { #ifdef DEBUG @@ -3108,14 +3107,14 @@ Element::GetTokenList(nsIAtom* aAtom) MOZ_ASSERT(found, "Trying to use an unknown tokenlist!"); #endif - nsDOMSettableTokenList* list = nullptr; + nsDOMTokenList* list = nullptr; if (HasProperties()) { - list = static_cast(GetProperty(aAtom)); + list = static_cast(GetProperty(aAtom)); } if (!list) { - list = new nsDOMSettableTokenList(this, aAtom); + list = new nsDOMTokenList(this, aAtom); NS_ADDREF(list); - SetProperty(aAtom, list, nsDOMSettableTokenListPropertyDestructor); + SetProperty(aAtom, list, nsDOMTokenListPropertyDestructor); } return list; } @@ -3132,7 +3131,7 @@ Element::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult) nsresult Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue) { - nsDOMSettableTokenList* itemType = GetTokenList(aAtom); + nsDOMTokenList* itemType = GetTokenList(aAtom); nsAutoString string; aValue->GetAsAString(string); ErrorResult rv; diff --git a/dom/base/Element.h b/dom/base/Element.h index 1c69786997d..856f4e3ec4b 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -44,7 +44,6 @@ class nsIURI; class nsIScrollableFrame; class nsAttrValueOrString; class nsContentList; -class nsDOMSettableTokenList; class nsDOMTokenList; struct nsRect; class nsFocusManager; @@ -1298,7 +1297,7 @@ protected: */ virtual void GetLinkTarget(nsAString& aTarget); - nsDOMSettableTokenList* GetTokenList(nsIAtom* aAtom); + nsDOMTokenList* GetTokenList(nsIAtom* aAtom); void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult); nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue); diff --git a/dom/base/moz.build b/dom/base/moz.build index b828819c535..22bf80a8093 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -281,7 +281,6 @@ UNIFIED_SOURCES += [ 'nsDOMNavigationTiming.cpp', 'nsDOMScriptObjectFactory.cpp', 'nsDOMSerializer.cpp', - 'nsDOMSettableTokenList.cpp', 'nsDOMTokenList.cpp', 'nsDOMWindowList.cpp', 'nsFocusManager.cpp', diff --git a/dom/base/nsDOMSettableTokenList.cpp b/dom/base/nsDOMSettableTokenList.cpp deleted file mode 100644 index eea5a050e23..00000000000 --- a/dom/base/nsDOMSettableTokenList.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -/* - * Implementation of DOMSettableTokenList specified by HTML5. - */ - -#include "nsDOMSettableTokenList.h" -#include "mozilla/dom/DOMSettableTokenListBinding.h" -#include "mozilla/dom/Element.h" - -void -nsDOMSettableTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv) -{ - if (!mElement) { - return; - } - - rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true); -} - -JSObject* -nsDOMSettableTokenList::WrapObject(JSContext *cx, JS::Handle aGivenProto) -{ - return mozilla::dom::DOMSettableTokenListBinding::Wrap(cx, this, aGivenProto); -} diff --git a/dom/base/nsDOMSettableTokenList.h b/dom/base/nsDOMSettableTokenList.h deleted file mode 100644 index b004334ad9c..00000000000 --- a/dom/base/nsDOMSettableTokenList.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -/* - * Implementation of DOMSettableTokenList specified by HTML5. - */ - -#ifndef nsDOMSettableTokenList_h___ -#define nsDOMSettableTokenList_h___ - -#include "nsDOMTokenList.h" - -class nsIAtom; - -// nsISupports must be on the primary inheritance chain -// because nsDOMSettableTokenList is traversed by Element. -class nsDOMSettableTokenList final : public nsDOMTokenList -{ -public: - - nsDOMSettableTokenList(mozilla::dom::Element* aElement, nsIAtom* aAttrAtom) - : nsDOMTokenList(aElement, aAttrAtom) {} - - virtual JSObject* WrapObject(JSContext *cx, JS::Handle aGivenProto) override; - - // WebIDL - void GetValue(nsAString& aResult) { Stringify(aResult); } - void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv); -}; - -#endif // nsDOMSettableTokenList_h___ - diff --git a/dom/base/nsDOMTokenList.cpp b/dom/base/nsDOMTokenList.cpp index 855b8e9c0e5..63e0306135a 100644 --- a/dom/base/nsDOMTokenList.cpp +++ b/dom/base/nsDOMTokenList.cpp @@ -9,7 +9,6 @@ */ #include "nsDOMTokenList.h" - #include "nsAttrValue.h" #include "nsContentUtils.h" #include "nsError.h" @@ -74,6 +73,16 @@ nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult) } } +void +nsDOMTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv) +{ + if (!mElement) { + return; + } + + rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true); +} + nsresult nsDOMTokenList::CheckToken(const nsAString& aStr) { diff --git a/dom/base/nsDOMTokenList.h b/dom/base/nsDOMTokenList.h index 60fd5968720..9f7a7020141 100644 --- a/dom/base/nsDOMTokenList.h +++ b/dom/base/nsDOMTokenList.h @@ -26,7 +26,7 @@ class nsAttrValue; class nsIAtom; // nsISupports must be on the primary inheritance chain -// because nsDOMSettableTokenList is traversed by Element. + class nsDOMTokenList : public nsISupports, public nsWrapperCache { @@ -66,6 +66,9 @@ public: bool Toggle(const nsAString& aToken, const mozilla::dom::Optional& force, mozilla::ErrorResult& aError); + + void GetValue(nsAString& aResult) { Stringify(aResult); } + void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv); void Stringify(nsAString& aResult); protected: diff --git a/dom/base/test/test_bug346485.html b/dom/base/test/test_bug346485.html index b0543ced201..8db0fc67e20 100644 --- a/dom/base/test/test_bug346485.html +++ b/dom/base/test/test_bug346485.html @@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=346485 /** Test for Bug 346485 **/ /** - * This test is testing DOMSettableTokenList used by the output element. + * This test is testing DOMTokenList used by the output element. */ function checkHtmlFor(htmlFor, list, msg) { diff --git a/dom/base/test/test_classList.html b/dom/base/test/test_classList.html index 3b659586be2..648bde4a21a 100644 --- a/dom/base/test/test_classList.html +++ b/dom/base/test/test_classList.html @@ -98,15 +98,20 @@ function assignToClassListStrict(e) { "use strict"; try { e.classList = "foo"; - ok(false, "assigning to classList didn't throw"); - } catch (e) { } + ok(true, "assigning to classList didn't throw"); + e.removeAttribute("class"); + } catch (e) { + ok(false, "assigning to classList threw"); + } } function assignToClassList(e) { try { var expect = e.classList; e.classList = "foo"; + ok(true, "assigning to classList didn't throw"); is(e.classList, expect, "classList should be unchanged after assignment"); + e.removeAttribute("class"); } catch (e) { ok(false, "assigning to classList threw"); } @@ -160,8 +165,6 @@ function testClassList(e) { ok(DOMTokenList.prototype.hasOwnProperty("toString"), "Should have own toString on DOMTokenList") - ok(!DOMSettableTokenList.prototype.hasOwnProperty("toString"), - "Should not have own toString on DOMSettableTokenList") e.removeAttribute("class"); is(e.classList.toString(), "", "wrong classList.toString() value"); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index cef14177050..cf9dbd48907 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -437,10 +437,6 @@ DOMInterfaces = { 'implicitJSContext': [ 'then' ], }, -'DOMSettableTokenList': { - 'nativeType': 'nsDOMSettableTokenList', -}, - 'DOMStringMap': { 'nativeType': 'nsDOMStringMap' }, diff --git a/dom/html/HTMLIFrameElement.h b/dom/html/HTMLIFrameElement.h index da8c0b28003..67fcc3662ed 100644 --- a/dom/html/HTMLIFrameElement.h +++ b/dom/html/HTMLIFrameElement.h @@ -10,7 +10,7 @@ #include "mozilla/Attributes.h" #include "nsGenericHTMLFrameElement.h" #include "nsIDOMHTMLIFrameElement.h" -#include "nsDOMSettableTokenList.h" +#include "nsDOMTokenList.h" namespace mozilla { namespace dom { @@ -84,7 +84,7 @@ public: { SetHTMLAttr(nsGkAtoms::name, aName, aError); } - nsDOMSettableTokenList* Sandbox() + nsDOMTokenList* Sandbox() { return GetTokenList(nsGkAtoms::sandbox); } diff --git a/dom/html/HTMLLinkElement.h b/dom/html/HTMLLinkElement.h index 0411bcede7a..44596bf1e26 100644 --- a/dom/html/HTMLLinkElement.h +++ b/dom/html/HTMLLinkElement.h @@ -119,7 +119,7 @@ public: { SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv); } - nsDOMSettableTokenList* Sizes() + nsDOMTokenList* Sizes() { return GetTokenList(nsGkAtoms::sizes); } diff --git a/dom/html/HTMLOutputElement.cpp b/dom/html/HTMLOutputElement.cpp index 79c30cc6533..a5c5c9d2cf4 100644 --- a/dom/html/HTMLOutputElement.cpp +++ b/dom/html/HTMLOutputElement.cpp @@ -11,7 +11,7 @@ #include "mozilla/dom/HTMLFormElement.h" #include "mozilla/dom/HTMLOutputElementBinding.h" #include "nsContentUtils.h" -#include "nsDOMSettableTokenList.h" +#include "nsDOMTokenList.h" #include "nsFormSubmission.h" NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Output) @@ -166,11 +166,11 @@ HTMLOutputElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& } } -nsDOMSettableTokenList* +nsDOMTokenList* HTMLOutputElement::HtmlFor() { if (!mTokenList) { - mTokenList = new nsDOMSettableTokenList(this, nsGkAtoms::_for); + mTokenList = new nsDOMTokenList(this, nsGkAtoms::_for); } return mTokenList; } diff --git a/dom/html/HTMLOutputElement.h b/dom/html/HTMLOutputElement.h index 0f57cabfacc..b9e6ecd9c4a 100644 --- a/dom/html/HTMLOutputElement.h +++ b/dom/html/HTMLOutputElement.h @@ -64,7 +64,7 @@ public: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle aGivenProto) override; // WebIDL - nsDOMSettableTokenList* HtmlFor(); + nsDOMTokenList* HtmlFor(); // nsGenericHTMLFormElement::GetForm is fine. void GetName(nsAString& aName) { @@ -108,7 +108,7 @@ protected: ValueModeFlag mValueModeFlag; bool mIsDoneAddingChildren; nsString mDefaultValue; - RefPtr mTokenList; + RefPtr mTokenList; }; } // namespace dom diff --git a/dom/html/HTMLPropertiesCollection.cpp b/dom/html/HTMLPropertiesCollection.cpp index 252b89a22e7..3eb340a9d08 100644 --- a/dom/html/HTMLPropertiesCollection.cpp +++ b/dom/html/HTMLPropertiesCollection.cpp @@ -9,7 +9,7 @@ #include "nsContentUtils.h" #include "nsGenericHTMLElement.h" #include "nsVariant.h" -#include "nsDOMSettableTokenList.h" +#include "nsDOMTokenList.h" #include "nsAttrValue.h" #include "nsWrapperCacheInlines.h" #include "mozilla/dom/HTMLPropertiesCollectionBinding.h" diff --git a/dom/html/HTMLTableCellElement.h b/dom/html/HTMLTableCellElement.h index 80f666617eb..570747227e3 100644 --- a/dom/html/HTMLTableCellElement.h +++ b/dom/html/HTMLTableCellElement.h @@ -47,7 +47,7 @@ public: { SetHTMLIntAttr(nsGkAtoms::rowspan, aRowSpan, aError); } - //already_AddRefed Headers() const; + //already_AddRefed Headers() const; void GetHeaders(DOMString& aHeaders) { GetHTMLAttr(nsGkAtoms::headers, aHeaders); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 7b2ef9b87b8..717e2e466fb 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -96,7 +96,7 @@ #include "HTMLPropertiesCollection.h" #include "nsVariant.h" -#include "nsDOMSettableTokenList.h" +#include "nsDOMTokenList.h" #include "nsThreadUtils.h" #include "nsTextFragment.h" #include "mozilla/dom/BindingUtils.h" diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 554d71be522..2f6e3e29fab 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -20,7 +20,7 @@ #include "mozilla/dom/ValidityState.h" #include "mozilla/dom/ElementInlines.h" -class nsDOMSettableTokenList; +class nsDOMTokenList; class nsIDOMHTMLMenuElement; class nsIEditor; class nsIFormControlFrame; @@ -104,7 +104,7 @@ public: { SetHTMLBoolAttr(nsGkAtoms::itemscope, aItemScope, aError); } - nsDOMSettableTokenList* ItemType() + nsDOMTokenList* ItemType() { return GetTokenList(nsGkAtoms::itemtype); } @@ -116,11 +116,11 @@ public: { SetHTMLAttr(nsGkAtoms::itemid, aItemID, aError); } - nsDOMSettableTokenList* ItemRef() + nsDOMTokenList* ItemRef() { return GetTokenList(nsGkAtoms::itemref); } - nsDOMSettableTokenList* ItemProp() + nsDOMTokenList* ItemProp() { return GetTokenList(nsGkAtoms::itemprop); } diff --git a/dom/html/test/forms/test_output_element.html b/dom/html/test/forms/test_output_element.html index a8f0f61715e..5b4097047fb 100644 --- a/dom/html/test/forms/test_output_element.html +++ b/dom/html/test/forms/test_output_element.html @@ -109,7 +109,7 @@ function checkHtmlForIDLAttribute(element) is(String(element.htmlFor), 'a b', "htmlFor IDL attribute should reflect the for content attribute"); - // DOMSettableTokenList is tested in another bug so we just test assignation + // DOMTokenList is tested in another bug so we just test assignation element.htmlFor.value = 'a b c'; is(String(element.htmlFor), 'a b c', "htmlFor should have changed"); } diff --git a/dom/html/test/test_bug845057.html b/dom/html/test/test_bug845057.html index 39c176748fe..631cd2b5cce 100644 --- a/dom/html/test/test_bug845057.html +++ b/dom/html/test/test_bug845057.html @@ -26,8 +26,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=845057 return ((a+'').split(" ").sort()+'') == ((b+'').split(" ").sort()+''); } - ok(attr instanceof DOMSettableTokenList, - "Iframe sandbox attribute is instace of DOMSettableTokenList"); + ok(attr instanceof DOMTokenList, + "Iframe sandbox attribute is instace of DOMTokenList"); ok(eq(attr, "allow-scripts") && eq(iframe.getAttribute("sandbox"), "allow-scripts"), "Stringyfied sandbox attribute is same as that of the DOM element"); diff --git a/dom/imptests/html/dom/test_interface-objects.html b/dom/imptests/html/dom/test_interface-objects.html index c0697b566c1..0e2bf1acdda 100644 --- a/dom/imptests/html/dom/test_interface-objects.html +++ b/dom/imptests/html/dom/test_interface-objects.html @@ -33,8 +33,7 @@ var interfaces = [ "HTMLCollection", "DOMStringList", "DOMTokenList", - "DOMSettableTokenList" -]; + ]; test(function() { for (var p in window) { interfaces.forEach(function(i) { diff --git a/dom/imptests/html/dom/test_interfaces.html b/dom/imptests/html/dom/test_interfaces.html index 6403448e97e..9299ec8b7b7 100644 --- a/dom/imptests/html/dom/test_interfaces.html +++ b/dom/imptests/html/dom/test_interfaces.html @@ -249,6 +249,7 @@ interface Element : Node { attribute DOMString id; attribute DOMString className; + [PutForwards=value] readonly attribute DOMTokenList classList; readonly attribute Attr[] attributes; @@ -414,10 +415,7 @@ interface DOMTokenList { void remove(DOMString... tokens); boolean toggle(DOMString token, optional boolean force); stringifier; -}; - -interface DOMSettableTokenList : DOMTokenList { - attribute DOMString value; + attribute DOMString value; }; @@ -17,29 +17,29 @@ test(function() { var iframeEle = document.getElementById("iframe1"); assert_equals(iframeEle.sandbox.length, 3) - }, "DOMSettableTokenList length") + }, "DOMTokenList length") test(function() { var iframeEle = document.getElementById("iframe1"); assert_equals(iframeEle.sandbox.item(1), "allow-same-origin") - }, "DOMSettableTokenList item(index)") + }, "DOMTokenList item(index)") test(function() { var iframeEle = document.getElementById("iframe1"); assert_true(iframeEle.sandbox.contains("allow-forms")) - }, "DOMSettableTokenList contains(DomString)") + }, "DOMTokenList contains(DomString)") test(function() { var iframeEle = document.getElementById("iframe1"); iframeEle.sandbox.add("ALLOW-SANDBOX"); assert_true(iframeEle.sandbox.contains("ALLOW-SANDBOX")) - }, "DOMSettableTokenList add(DomString)") + }, "DOMTokenList add(DomString)") test(function() { var iframeEle = document.getElementById("iframe1"); iframeEle.sandbox.remove("ALLOW-SANDBOX"); assert_false(iframeEle.sandbox.contains("ALLOW-SANDBOX")) - }, "DOMSettableTokenList remove(DomString)") + }, "DOMTokenList remove(DomString)") test(function() { var iframeEle = document.getElementById("iframe1"); @@ -48,18 +48,18 @@ iframeEle.sandbox.toggle("allow-top-navigation") && iframeEle.sandbox.contains("allow-top-navigation") && !iframeEle.sandbox.toggle("allow-top-navigation") && !iframeEle.sandbox.contains("allow-top-navigation") ) - }, "DOMSettableTokenList toggle(DomString) - Returns true if token is now present (it was added); returns false if it is not (it was removed).") + }, "DOMTokenList toggle(DomString) - Returns true if token is now present (it was added); returns false if it is not (it was removed).") test(function() { var iframeEle = document.getElementById("iframe1"); assert_equals(iframeEle.sandbox.value, iframeEle.sandbox.toString()) - }, "DOMSettableTokenList sandbox.toString()") + }, "DOMTokenList sandbox.toString()") test(function() { var iframeEle = document.getElementById("iframe1"); iframeEle.sandbox.remove("ALLOW-SANDBOX"); assert_true(iframeEle.sandbox.contains("allow-scripts") != iframeEle.sandbox.contains("Allow-SCRIPTS")) - }, "DOMSettableTokenList case sensitivity") + }, "DOMTokenList case sensitivity") From 709303a247280efc9ac714031f3911c79e150e6d Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Wed, 10 Feb 2016 17:06:18 +1100 Subject: [PATCH 126/187] Bug 1245463: [MSE] P1. Do not attempt to retrieve the buffered range if the mediasource is in closed state. r=gerald The changes that follow may cause the active sourcebuffer list to be modified; which will trigger an assertion if the mediasource object is closed. MozReview-Commit-ID: 8A1CMKAUyTq --- dom/media/mediasource/MediaSourceDecoder.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 9e2e7fdf64a..3505801c086 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -256,6 +256,12 @@ MediaDecoderOwner::NextFrameStatus MediaSourceDecoder::NextFrameBufferedStatus() { MOZ_ASSERT(NS_IsMainThread()); + + if (!mMediaSource || + mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) { + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; + } + // Next frame hasn't been decoded yet. // Use the buffered range to consider if we have the next frame available. TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); From 7f2076506eb4ff5dd85941864932db21ce6e87ec Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 12 Feb 2016 00:54:44 +1100 Subject: [PATCH 127/187] Bug 1245463: [MSE] P2. Remove MediaSource's duration mirror. r=gerald It served no purpose other than implementing the MSE spec by the letter. The spirit is preserved. This allows to disable tail dispatching on the MediaSourceDemuxer's TaskQueue which prevents us from performing synchronous operation on the main thread. MozReview-Commit-ID: G7aqfvGsf1e --- dom/media/mediasource/MediaSourceDemuxer.cpp | 2 +- dom/media/mediasource/TrackBuffersManager.cpp | 25 ++++++------------- dom/media/mediasource/TrackBuffersManager.h | 3 --- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 46326feda0e..f7fcefa2054 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -22,7 +22,7 @@ using media::TimeIntervals; MediaSourceDemuxer::MediaSourceDemuxer() : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), - /* aSupportsTailDispatch = */ true)) + /* aSupportsTailDispatch = */ false)) , mMonitor("MediaSourceDemuxer") { MOZ_ASSERT(NS_IsMainThread()); diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index a83e5e8059b..90ab182122d 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -100,7 +100,6 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue()) , mSourceBufferAttributes(aAttributes) , mParentDecoder(new nsMainThreadPtrHolder(aParentDecoder, false /* strict */)) - , mMediaSourceDuration(mTaskQueue, Maybe(), "TrackBuffersManager::mMediaSourceDuration (Mirror)") , mAbort(false) , mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold", 100 * (1 << 20))) @@ -108,12 +107,6 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute , mMonitor("TrackBuffersManager") { MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread"); - RefPtr self = this; - nsCOMPtr task = - NS_NewRunnableFunction([self] () { - self->mMediaSourceDuration.Connect(self->mParentDecoder->CanonicalExplicitDuration()); - }); - GetTaskQueue()->Dispatch(task.forget()); } TrackBuffersManager::~TrackBuffersManager() @@ -321,7 +314,6 @@ TrackBuffersManager::Detach() TimeUnit::FromInfinity())); self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__); self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__); - self->mMediaSourceDuration.DisconnectIfConnected(); }); GetTaskQueue()->Dispatch(task.forget()); } @@ -496,15 +488,7 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) MSE_DEBUG("From %.2fs to %.2f", aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds()); - if (mMediaSourceDuration.Ref().isNothing() || - IsNaN(mMediaSourceDuration.Ref().ref())) { - MSE_DEBUG("Nothing to remove, aborting"); - return false; - } - TimeUnit duration{TimeUnit::FromSeconds(mMediaSourceDuration.Ref().ref())}; - #if DEBUG - MSE_DEBUG("duration:%.2f", duration.ToSeconds()); if (HasVideo()) { MSE_DEBUG("before video ranges=%s", DumpTimeRanges(mVideoTracks.mBufferedRanges).get()); @@ -527,7 +511,14 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) MSE_DEBUGV("Processing %s track", track->mInfo->mMimeType.get()); // 1. Let remove end timestamp be the current value of duration // See bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=28727 - TimeUnit removeEndTimestamp = std::max(duration, track->mBufferedRanges.GetEnd()); + // At worse we will remove all frames until the end, unless a key frame is + // found between the current interval's end and the trackbuffer's end. + TimeUnit removeEndTimestamp = track->mBufferedRanges.GetEnd(); + + if (start > removeEndTimestamp) { + // Nothing to remove. + continue; + } // 2. If this track buffer has a random access point timestamp that is greater than or equal to end, // then update remove end timestamp to that random access point timestamp. diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 1ba68dd97da..7fbca36743c 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -349,9 +349,6 @@ private: RefPtr mSourceBufferAttributes; nsMainThreadPtrHandle mParentDecoder; - // MediaSource duration mirrored from MediaDecoder on the main thread.. - Mirror> mMediaSourceDuration; - // Set to true if abort was called. Atomic mAbort; // Set to true if mediasource state changed to ended. From ef08bd6f5beddfb3a7c959a65cad1ffb28fe5f7c Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 12 Feb 2016 00:55:55 +1100 Subject: [PATCH 128/187] Bug 1245463: [MSE] P3. When abort() is called, wait until the current appendBuffer completes. r=gerald The W3C spec indicates that while everything in MSE is asynchronous, the abort() command is to interrupt the current segment parser loop and have the reset parser loop synchronously completes the frames present in the input buffer. This causes a fundamental issue that abort() will never result in a deterministic outcome as the segment parser loop may be in different condition. We used to really attempt to abort the current operation, however there could have been a race in the order in which tasks were queued. As such, we now simply wait for the current appendBuffer to complete. This also simplifies the code greatly, as we don't need to worry about pending concurrent appendBuffer. The actually happens to be similar to the Chromium behavior. Similar to bug 1239983, we strongly assert should a segment parser loop be running when it must have completed. MozReview-Commit-ID: 9772PLQEozf --- dom/media/mediasource/SourceBuffer.cpp | 71 +++----------- dom/media/mediasource/SourceBuffer.h | 7 +- .../mediasource/SourceBufferContentManager.h | 4 - dom/media/mediasource/TrackBuffersManager.cpp | 97 ++++++------------- dom/media/mediasource/TrackBuffersManager.h | 9 +- 5 files changed, 48 insertions(+), 140 deletions(-) diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 1c9bcd2223f..a4f88b59265 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -39,27 +39,6 @@ using media::TimeUnit; namespace dom { -class BufferAppendRunnable : public nsRunnable { -public: - BufferAppendRunnable(SourceBuffer* aSourceBuffer, - uint32_t aUpdateID) - : mSourceBuffer(aSourceBuffer) - , mUpdateID(aUpdateID) - { - } - - NS_IMETHOD Run() override final { - - mSourceBuffer->BufferAppend(mUpdateID); - - return NS_OK; - } - -private: - RefPtr mSourceBuffer; - uint32_t mUpdateID; -}; - void SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv) { @@ -226,10 +205,13 @@ void SourceBuffer::AbortBufferAppend() { if (mUpdating) { - mPendingAppend.DisconnectIfExists(); - // TODO: Abort stream append loop algorithms. - // cancel any pending buffer append. - mContentManager->AbortAppendData(); + if (mPendingAppend.Exists()) { + mPendingAppend.Disconnect(); + mContentManager->AbortAppendData(); + // Some data may have been added by the Segment Parser Loop. + // Check if we need to update the duration. + CheckEndTime(); + } AbortUpdating(); } } @@ -309,7 +291,6 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType) , mMediaSource(aMediaSource) , mUpdating(false) , mActive(false) - , mUpdateID(0) , mType(aType) { MOZ_ASSERT(NS_IsMainThread()); @@ -381,7 +362,6 @@ SourceBuffer::StartUpdating() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mUpdating); mUpdating = true; - mUpdateID++; QueueAsyncSimpleEvent("updatestart"); } @@ -390,12 +370,8 @@ SourceBuffer::StopUpdating() { MOZ_ASSERT(NS_IsMainThread()); if (!mUpdating) { - // The buffer append algorithm has been interrupted by abort(). - // - // If the sequence appendBuffer(), abort(), appendBuffer() occurs before - // the first StopUpdating() runnable runs, then a second StopUpdating() - // runnable will be scheduled, but still only one (the first) will queue - // events. + // The buffer append or range removal algorithm has been interrupted by + // abort(). return; } mUpdating = false; @@ -407,7 +383,6 @@ void SourceBuffer::AbortUpdating() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mUpdating); mUpdating = false; QueueAsyncSimpleEvent("abort"); QueueAsyncSimpleEvent("updateend"); @@ -438,23 +413,13 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR StartUpdating(); - nsCOMPtr task = new BufferAppendRunnable(this, mUpdateID); - NS_DispatchToMainThread(task); + BufferAppend(); } void -SourceBuffer::BufferAppend(uint32_t aUpdateID) +SourceBuffer::BufferAppend() { - if (!mUpdating || aUpdateID != mUpdateID) { - // The buffer append algorithm has been interrupted by abort(). - // - // If the sequence appendBuffer(), abort(), appendBuffer() occurs before - // the first StopUpdating() runnable runs, then a second StopUpdating() - // runnable will be scheduled, but still only one (the first) will queue - // events. - return; - } - + MOZ_ASSERT(mUpdating); MOZ_ASSERT(mMediaSource); MOZ_ASSERT(!mPendingAppend.Exists()); @@ -467,11 +432,8 @@ SourceBuffer::BufferAppend(uint32_t aUpdateID) void SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) { + MOZ_ASSERT(mUpdating); mPendingAppend.Complete(); - if (!mUpdating) { - // The buffer append algorithm has been interrupted by abort(). - return; - } if (aHasActiveTracks) { if (!mActive) { @@ -494,7 +456,9 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) void SourceBuffer::AppendDataErrored(nsresult aError) { + MOZ_ASSERT(mUpdating); mPendingAppend.Complete(); + switch (aError) { case NS_ERROR_ABORT: // Nothing further to do as the trackbuffer has been shutdown. @@ -510,10 +474,7 @@ void SourceBuffer::AppendError(bool aDecoderError) { MOZ_ASSERT(NS_IsMainThread()); - if (!mUpdating) { - // The buffer append algorithm has been interrupted by abort(). - return; - } + mContentManager->ResetParserState(); mUpdating = false; diff --git a/dom/media/mediasource/SourceBuffer.h b/dom/media/mediasource/SourceBuffer.h index a67537bba5c..171384fe81c 100644 --- a/dom/media/mediasource/SourceBuffer.h +++ b/dom/media/mediasource/SourceBuffer.h @@ -238,7 +238,7 @@ private: // Shared implementation of AppendBuffer overloads. void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); - void BufferAppend(uint32_t aAppendID); + void BufferAppend(); // Implement the "Append Error Algorithm". // Will call endOfStream() with "decode" error if aDecodeError is true. @@ -266,11 +266,6 @@ private: mozilla::Atomic mActive; - // Each time mUpdating is set to true, mUpdateID will be incremented. - // This allows for a queued AppendData task to identify if it was earlier - // aborted and another AppendData queued. - uint32_t mUpdateID; - MozPromiseRequestHolder mPendingAppend; const nsCString mType; diff --git a/dom/media/mediasource/SourceBufferContentManager.h b/dom/media/mediasource/SourceBufferContentManager.h index e1bbd4a73fa..c2515d7f958 100644 --- a/dom/media/mediasource/SourceBufferContentManager.h +++ b/dom/media/mediasource/SourceBufferContentManager.h @@ -108,10 +108,6 @@ public: virtual void RestartGroupStartTimestamp() {} virtual media::TimeUnit GroupEndTimestamp() = 0; -#if defined(DEBUG) - virtual void Dump(const char* aPath) { } -#endif - protected: virtual ~SourceBufferContentManager() { } }; diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 90ab182122d..d58e06a8ea6 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -96,15 +96,14 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute , mType(aType) , mParser(ContainerParser::CreateForMIMEType(aType)) , mProcessedInput(0) - , mAppendRunning(false) , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue()) , mSourceBufferAttributes(aAttributes) , mParentDecoder(new nsMainThreadPtrHolder(aParentDecoder, false /* strict */)) - , mAbort(false) , mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold", 100 * (1 << 20))) , mEvictionOccurred(false) , mMonitor("TrackBuffersManager") + , mAppendRunning(false) { MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread"); } @@ -135,7 +134,6 @@ TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData) { MOZ_ASSERT(OnTaskQueue()); mIncomingBuffers.AppendElement(aData); - mAbort = false; } RefPtr @@ -144,32 +142,33 @@ TrackBuffersManager::BufferAppend() MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); + mAppendRunning = true; return InvokeAsync(GetTaskQueue(), this, __func__, &TrackBuffersManager::InitSegmentParserLoop); } -// Abort any pending AppendData. -// We don't really care about really aborting our inner loop as by spec the -// process is happening asynchronously, as such where and when we would abort is -// non-deterministic. The SourceBuffer also makes sure BufferAppend -// isn't called should the appendBuffer be immediately aborted. -// We do however want to ensure that no new task will be dispatched on our task -// queue and only let the current one finish its job. For this we set mAbort -// to true. +// The MSE spec requires that we abort the current SegmentParserLoop +// which is then followed by a call to ResetParserState. +// However due to our asynchronous design this causes inherent difficulities. +// As the spec behaviour is non deterministic anyway, we instead wait until the +// current AppendData has completed its run. void TrackBuffersManager::AbortAppendData() { MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); - mAbort = true; + MonitorAutoLock mon(mMonitor); + while (mAppendRunning) { + mon.Wait(); + } } void TrackBuffersManager::ResetParserState() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mAppendRunning, "AbortAppendData must have been called"); + MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called"); MSE_DEBUG(""); // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed. @@ -302,20 +301,6 @@ TrackBuffersManager::Detach() { MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); - - // Abort pending operations if any. - AbortAppendData(); - - RefPtr self = this; - nsCOMPtr task = - NS_NewRunnableFunction([self] () { - // Clear our sourcebuffer - self->CodedFrameRemoval(TimeInterval(TimeUnit::FromSeconds(0), - TimeUnit::FromInfinity())); - self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__); - self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__); - }); - GetTaskQueue()->Dispatch(task.forget()); } #if defined(DEBUG) @@ -348,7 +333,7 @@ void TrackBuffersManager::CompleteResetParserState() { MOZ_ASSERT(OnTaskQueue()); - MOZ_ASSERT(!mAppendRunning); + MOZ_RELEASE_ASSERT(!mAppendRunning); MSE_DEBUG(""); for (auto& track : GetTracksList()) { @@ -586,8 +571,9 @@ RefPtr TrackBuffersManager::InitSegmentParserLoop() { MOZ_ASSERT(OnTaskQueue()); + MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty()); + MSE_DEBUG(""); - MOZ_ASSERT(mAppendPromise.IsEmpty() && !mAppendRunning); RefPtr p = mAppendPromise.Ensure(__func__); AppendIncomingBuffers(); @@ -621,6 +607,7 @@ void TrackBuffersManager::SegmentParserLoop() { MOZ_ASSERT(OnTaskQueue()); + while (true) { // 1. If the input buffer is empty, then jump to the need more data step below. if (!mInputBuffer || mInputBuffer->IsEmpty()) { @@ -724,7 +711,7 @@ TrackBuffersManager::SegmentParserLoop() ->Then(GetTaskQueue(), __func__, [self] (bool aNeedMoreData) { self->mProcessingRequest.Complete(); - if (aNeedMoreData || self->mAbort) { + if (aNeedMoreData) { self->NeedMoreData(); } else { self->ScheduleSegmentParserLoop(); @@ -743,19 +730,23 @@ void TrackBuffersManager::NeedMoreData() { MSE_DEBUG(""); - if (!mAbort) { - RestoreCachedVariables(); - } - mAppendRunning = false; + RestoreCachedVariables(); mAppendPromise.ResolveIfExists(mActiveTrack, __func__); + // Wake-up any pending Abort() + MonitorAutoLock mon(mMonitor); + mAppendRunning = false; + mon.NotifyAll(); } void TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName) { MSE_DEBUG("rv=%d", aRejectValue); - mAppendRunning = false; mAppendPromise.RejectIfExists(aRejectValue, aName); + // Wake-up any pending Abort() + MonitorAutoLock mon(mMonitor); + mAppendRunning = false; + mon.NotifyAll(); } void @@ -831,12 +822,7 @@ void TrackBuffersManager::OnDemuxerResetDone(nsresult) { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("mAbort:%d", static_cast(mAbort)); mDemuxerInitRequest.Complete(); - if (mAbort) { - RejectAppend(NS_ERROR_ABORT, __func__); - return; - } // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // request was being processed. See bug 1239983. MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); @@ -908,13 +894,8 @@ void TrackBuffersManager::OnDemuxerInitDone(nsresult) { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("mAbort:%d", static_cast(mAbort)); mDemuxerInitRequest.Complete(); - if (mAbort) { - RejectAppend(NS_ERROR_ABORT, __func__); - return; - } // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // request was being processed. See bug 1239983. MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); @@ -1174,9 +1155,8 @@ TrackBuffersManager::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure) { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("Failed to demux %s, failure:%d mAbort:%d", - aTrack == TrackType::kVideoTrack ? "video" : "audio", - aFailure, static_cast(mAbort)); + MSE_DEBUG("Failed to demux %s, failure:%d", + aTrack == TrackType::kVideoTrack ? "video" : "audio", aFailure); switch (aFailure) { case DemuxerFailureReason::END_OF_STREAM: case DemuxerFailureReason::WAITING_FOR_DATA: @@ -1203,15 +1183,10 @@ void TrackBuffersManager::DoDemuxVideo() { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("mAbort:%d", static_cast(mAbort)); if (!HasVideo()) { DoDemuxAudio(); return; } - if (mAbort) { - RejectProcessing(NS_ERROR_ABORT, __func__); - return; - } mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1) ->Then(GetTaskQueue(), __func__, this, &TrackBuffersManager::OnVideoDemuxCompleted, @@ -1232,15 +1207,10 @@ void TrackBuffersManager::DoDemuxAudio() { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("mAbort:%d", static_cast(mAbort)); if (!HasAudio()) { CompleteCodedFrameProcessing(); return; } - if (mAbort) { - RejectProcessing(NS_ERROR_ABORT, __func__); - return; - } mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1) ->Then(GetTaskQueue(), __func__, this, &TrackBuffersManager::OnAudioDemuxCompleted, @@ -1261,7 +1231,6 @@ void TrackBuffersManager::CompleteCodedFrameProcessing() { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("mAbort:%d", static_cast(mAbort)); // 1. For each coded frame in the media segment run the following steps: // Coded Frame Processing steps 1.1 to 1.21. @@ -1332,22 +1301,12 @@ TrackBuffersManager::CompleteCodedFrameProcessing() void TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName) { - if (mAbort) { - // mAppendPromise will be resolved immediately upon mProcessingPromise - // completing. - mAppendRunning = false; - } mProcessingPromise.RejectIfExists(aRejectValue, __func__); } void TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName) { - if (mAbort) { - // mAppendPromise will be resolved immediately upon mProcessingPromise - // completing. - mAppendRunning = false; - } mProcessingPromise.ResolveIfExists(aResolveValue, __func__); } diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 7fbca36743c..95a2611fd8f 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -310,10 +310,6 @@ private: MozPromiseHolder mProcessingPromise; MozPromiseHolder mAppendPromise; - // Set to true while SegmentParserLoop is running. This is used for diagnostic - // purposes only. We can't rely on mAppendPromise to be empty as it is only - // cleared in a follow up task. - bool mAppendRunning; // Trackbuffers definition. nsTArray GetTracksList(); @@ -349,8 +345,6 @@ private: RefPtr mSourceBufferAttributes; nsMainThreadPtrHandle mParentDecoder; - // Set to true if abort was called. - Atomic mAbort; // Set to true if mediasource state changed to ended. Atomic mEnded; @@ -360,7 +354,10 @@ private: Atomic mEvictionOccurred; // Monitor to protect following objects accessed across multipple threads. + // mMonitor is also notified if the value of mAppendRunning becomes false. mutable Monitor mMonitor; + // Set to true while SegmentParserLoop is running. + Atomic mAppendRunning; // Stable audio and video track time ranges. media::TimeIntervals mVideoBufferedRanges; media::TimeIntervals mAudioBufferedRanges; From 52ec724cb64dadaf558ec15cfb656646c1f7f329 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Wed, 10 Feb 2016 18:05:39 +1100 Subject: [PATCH 129/187] Bug 1245463: [MSE] P4. Remove no longer working Dump() commands. r=gerald MozReview-Commit-ID: 71hgJ63ksPU --- dom/media/mediasource/MediaSource.cpp | 14 -------------- dom/media/mediasource/MediaSource.h | 6 ------ dom/media/mediasource/SourceBuffer.cpp | 10 ---------- dom/media/mediasource/SourceBuffer.h | 4 ---- dom/media/mediasource/SourceBufferList.cpp | 10 ---------- dom/media/mediasource/SourceBufferList.h | 4 ---- dom/media/mediasource/TrackBuffersManager.cpp | 8 -------- dom/media/mediasource/TrackBuffersManager.h | 4 ---- 8 files changed, 60 deletions(-) diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index dfa3d28cd51..0faab525a17 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -487,20 +487,6 @@ MediaSource::NotifyEvicted(double aStart, double aEnd) mSourceBuffers->Evict(aStart, aEnd); } -#if defined(DEBUG) -void -MediaSource::Dump(const char* aPath) -{ - char buf[255]; - PR_snprintf(buf, sizeof(buf), "%s/mediasource-%p", aPath, this); - PR_MkDir(buf, 0700); - - if (mSourceBuffers) { - mSourceBuffers->Dump(buf); - } -} -#endif - void MediaSource::GetMozDebugReaderData(nsAString& aString) { diff --git a/dom/media/mediasource/MediaSource.h b/dom/media/mediasource/MediaSource.h index 9b318744c74..ab1b0571f72 100644 --- a/dom/media/mediasource/MediaSource.h +++ b/dom/media/mediasource/MediaSource.h @@ -100,12 +100,6 @@ public: // that were evicted are provided. void NotifyEvicted(double aStart, double aEnd); -#if defined(DEBUG) - // Dump the contents of each SourceBuffer to a series of files under aPath. - // aPath must exist. Debug only, invoke from your favourite debugger. - void Dump(const char* aPath); -#endif - // Returns a string describing the state of the MediaSource internal // buffered data. Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index a4f88b59265..27cf3b245e0 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -586,16 +586,6 @@ SourceBuffer::Evict(double aStart, double aEnd) mContentManager->EvictBefore(TimeUnit::FromSeconds(evictTime)); } -#if defined(DEBUG) -void -SourceBuffer::Dump(const char* aPath) -{ - if (mContentManager) { - mContentManager->Dump(aPath); - } -} -#endif - NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer) diff --git a/dom/media/mediasource/SourceBuffer.h b/dom/media/mediasource/SourceBuffer.h index 171384fe81c..a4c57119ab2 100644 --- a/dom/media/mediasource/SourceBuffer.h +++ b/dom/media/mediasource/SourceBuffer.h @@ -213,10 +213,6 @@ public: return mActive; } -#if defined(DEBUG) - void Dump(const char* aPath); -#endif - private: ~SourceBuffer(); diff --git a/dom/media/mediasource/SourceBufferList.cpp b/dom/media/mediasource/SourceBufferList.cpp index bea935f3d1e..58591843d51 100644 --- a/dom/media/mediasource/SourceBufferList.cpp +++ b/dom/media/mediasource/SourceBufferList.cpp @@ -176,16 +176,6 @@ SourceBufferList::QueueAsyncSimpleEvent(const char* aName) NS_DispatchToMainThread(event); } -#if defined(DEBUG) -void -SourceBufferList::Dump(const char* aPath) -{ - for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) { - mSourceBuffers[i]->Dump(aPath); - } -} -#endif - SourceBufferList::SourceBufferList(MediaSource* aMediaSource) : DOMEventTargetHelper(aMediaSource->GetParentObject()) , mMediaSource(aMediaSource) diff --git a/dom/media/mediasource/SourceBufferList.h b/dom/media/mediasource/SourceBufferList.h index c974489b869..7ec46665918 100644 --- a/dom/media/mediasource/SourceBufferList.h +++ b/dom/media/mediasource/SourceBufferList.h @@ -84,10 +84,6 @@ public: // No event is fired and no action is performed on the sourcebuffers. void ClearSimple(); -#if defined(DEBUG) - void Dump(const char* aPath); -#endif - private: ~SourceBufferList(); diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index d58e06a8ea6..9e728045cc8 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -303,14 +303,6 @@ TrackBuffersManager::Detach() MSE_DEBUG(""); } -#if defined(DEBUG) -void -TrackBuffersManager::Dump(const char* aPath) -{ - -} -#endif - void TrackBuffersManager::FinishCodedFrameProcessing() { diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 95a2611fd8f..8f66b3d79fc 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -98,10 +98,6 @@ public: bool& aError); media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack); -#if defined(DEBUG) - void Dump(const char* aPath) override; -#endif - void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes); private: From 6fe3862e9acc51d2f0204f1bea425e574636a40f Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 12 Feb 2016 00:42:48 +0100 Subject: [PATCH 130/187] Backed out 2 changesets (bug 1247250) for bustage. r=bustage on a CLOSED TREE Backed out changeset 8aded3a039f5 (bug 1247250) Backed out changeset 374e6d0abf0e (bug 1247250) --- config/external/nss/nss.symbols | 1 - security/manager/ssl/nsNSSIOLayer.cpp | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/config/external/nss/nss.symbols b/config/external/nss/nss.symbols index 191fa017554..850765ccd4b 100644 --- a/config/external/nss/nss.symbols +++ b/config/external/nss/nss.symbols @@ -673,7 +673,6 @@ SSL_PeerCertificateChain SSL_PeerStapledOCSPResponses SSL_ResetHandshake SSL_SetCanFalseStartCallback -SSL_SetDowngradeCheckVersion SSL_SetNextProtoNego SSL_SetPKCS11PinArg SSL_SetSockPeerID diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 820bfdcfc05..48d8f704233 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -1078,10 +1078,7 @@ retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo) nsIWebProgressListener::STATE_USES_SSL_3); } - // NSS will return SSL_ERROR_RX_MALFORMED_SERVER_HELLO if anti-downgrade - // detected the downgrade. - if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT || - err == SSL_ERROR_RX_MALFORMED_SERVER_HELLO) { + if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT) { // This is a clear signal that we've fallen back too many versions. Treat // this as a hard failure, but forget any intolerance so that later attempts // don't use this version (i.e., range.max) and trigger the error again. @@ -2558,10 +2555,6 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS, if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) { return NS_ERROR_FAILURE; } - // tell NSS the max enabled version to make anti-downgrade effective - if (SECSuccess != SSL_SetDowngradeCheckVersion(fd, maxEnabledVersion)) { - return NS_ERROR_FAILURE; - } } bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled(); From 8eae6e84e6a6e8e718abe9cae1db18d154de1ef0 Mon Sep 17 00:00:00 2001 From: Sambuddha Basu Date: Fri, 12 Feb 2016 03:43:18 +0530 Subject: [PATCH 131/187] Bug 1246264 - Ensure cache directory exists for artifacts installation r=chmanchester --- python/mozbuild/mozbuild/mach_commands.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 718a0a42e98..4807196cb19 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, print_function, unicode_literals import argparse +import errno import itertools import json import logging @@ -1481,6 +1482,12 @@ class PackageFrontend(MachCommandBase): state_dir = self._mach_context.state_dir cache_dir = os.path.join(state_dir, 'package-frontend') + try: + os.makedirs(cache_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + import which if self._is_windows(): hg = which.which('hg.exe') From f8fea1d1d5c3939b8ba419fe3252504122a91418 Mon Sep 17 00:00:00 2001 From: Chris Manchester Date: Thu, 29 Oct 2015 13:25:03 -0700 Subject: [PATCH 132/187] Bug 1216681 - Add a windows version of fileid to extract a guid from windows binaries. r=jimm,ted This patch introduces a small utility program to extract a guid from a shared library or executable on windows to identify the correct symbol file to read in fix_stack_using_bpsyms.py. In order for this to work correctly on windows, the library name provided by MozDescribeCodeAddress needs to be a full path, so the LoadedImageName field from the IMAGEHLP_MODULE64 structure is used here instead of the ModuleName field. --- mozglue/misc/StackWalk.cpp | 2 +- testing/tools/fileid/moz.build | 9 ++- testing/tools/fileid/win_fileid.cpp | 90 +++++++++++++++++++++++++++++ tools/rb/fix_stack_using_bpsyms.py | 20 ++++--- 4 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 testing/tools/fileid/win_fileid.cpp diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp index 7e54588c620..dbf668cbb83 100644 --- a/mozglue/misc/StackWalk.cpp +++ b/mozglue/misc/StackWalk.cpp @@ -817,7 +817,7 @@ MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails) modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo); if (modInfoRes) { - strncpy(aDetails->library, modInfo.ModuleName, + strncpy(aDetails->library, modInfo.LoadedImageName, sizeof(aDetails->library)); aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0'; aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage; diff --git a/testing/tools/fileid/moz.build b/testing/tools/fileid/moz.build index b15641bf587..701886f6650 100644 --- a/testing/tools/fileid/moz.build +++ b/testing/tools/fileid/moz.build @@ -4,6 +4,8 @@ # 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/. +GeckoProgram('fileid', linkage=None, msvcrt='static') + if CONFIG['OS_ARCH'] == 'Linux': USE_LIBS += [ 'breakpad_linux_common_s', @@ -25,4 +27,9 @@ if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'Darwin': LOCAL_INCLUDES += [ '/toolkit/crashreporter/google-breakpad/src', ] - GeckoProgram('fileid', linkage=None) + +if CONFIG['OS_ARCH'] == 'WINNT': + SOURCES += ['win_fileid.cpp'] + OS_LIBS += ['dbghelp'] + +NO_PGO = True diff --git a/testing/tools/fileid/win_fileid.cpp b/testing/tools/fileid/win_fileid.cpp new file mode 100644 index 00000000000..7151baa9238 --- /dev/null +++ b/testing/tools/fileid/win_fileid.cpp @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include + +const DWORD CV_SIGNATURE_RSDS = 0x53445352; // 'SDSR' + +struct CV_INFO_PDB70 { + DWORD CvSignature; + GUID Signature; + DWORD Age; + BYTE PdbFileName[1]; +}; + +void print_guid(const GUID& guid, DWORD age) +{ + printf("%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X", + guid.Data1, guid.Data2, guid.Data3, + guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], + guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7], + age); +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: fileid \n"); + return 1; + } + + HANDLE file = CreateFileA(argv[1], + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (file == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Couldn't open file: %s\n", argv[1]); + return 1; + } + + HANDLE mapFile = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, 0); + if (mapFile == nullptr) { + fprintf(stderr, "Couldn't create file mapping\n"); + CloseHandle(file); + return 1; + } + + uint8_t* base = reinterpret_cast(MapViewOfFile(mapFile, + FILE_MAP_READ, + 0, + 0, + 0)); + if (base == nullptr) { + fprintf(stderr, "Couldn't map file\n"); + CloseHandle(mapFile); + CloseHandle(file); + return 1; + } + + DWORD size; + PIMAGE_DEBUG_DIRECTORY debug_dir = + reinterpret_cast( + ImageDirectoryEntryToDataEx(base, + FALSE, + IMAGE_DIRECTORY_ENTRY_DEBUG, + &size, + nullptr)); + + bool found = false; + if (debug_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW) { + CV_INFO_PDB70* cv = + reinterpret_cast(base + debug_dir->PointerToRawData); + if (cv->CvSignature == CV_SIGNATURE_RSDS) { + found = true; + print_guid(cv->Signature, cv->Age); + } + } + + UnmapViewOfFile(base); + CloseHandle(mapFile); + CloseHandle(file); + + return found ? 0 : 1; +} diff --git a/tools/rb/fix_stack_using_bpsyms.py b/tools/rb/fix_stack_using_bpsyms.py index ed89345d1bd..156acb5882c 100755 --- a/tools/rb/fix_stack_using_bpsyms.py +++ b/tools/rb/fix_stack_using_bpsyms.py @@ -85,7 +85,14 @@ def findIdForPath(path): # We should always be packaged with a "fileid" executable. fileid_exe = os.path.join(here, 'fileid') if not os.path.isfile(fileid_exe): - raise Exception("Could not find fileid executable in %s" % here) + fileid_exe = fileid_exe + '.exe' + if not os.path.isfile(fileid_exe): + raise Exception("Could not find fileid executable in %s" % here) + + if not os.path.isfile(path): + for suffix in ('.exe', '.dll'): + if os.path.isfile(path + suffix): + path = path + suffix try: return subprocess.check_output([fileid_exe, path]).rstrip() except subprocess.CalledProcessError as e: @@ -96,11 +103,12 @@ def guessSymbolFile(full_path, symbolsDir): """Guess a symbol file based on an object file's basename, ignoring the path and UUID.""" fn = os.path.basename(full_path) d1 = os.path.join(symbolsDir, fn) + root, _ = os.path.splitext(fn) + if os.path.exists(os.path.join(symbolsDir, root) + '.pdb'): + d1 = os.path.join(symbolsDir, root) + '.pdb' + fn = root if not os.path.exists(d1): - fn = fn + ".pdb" - d1 = os.path.join(symbolsDir, fn) - if not os.path.exists(d1): - return None + return None uuids = os.listdir(d1) if len(uuids) == 0: raise Exception("Missing symbol file for " + fn) @@ -108,8 +116,6 @@ def guessSymbolFile(full_path, symbolsDir): uuid = findIdForPath(full_path) else: uuid = uuids[0] - if fn.endswith(".pdb"): - fn = fn[:-4] return os.path.join(d1, uuid, fn + ".sym") parsedSymbolFiles = {} From e3a907757ce03b81ecccd22eae7daa65029010e3 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 11 Feb 2016 16:34:28 -0800 Subject: [PATCH 133/187] Backed out changeset 19c631cc41c8 (bug 1243589) for test_transformed_scrolling_repaints.html failures on OS X --- gfx/layers/client/ClientTiledPaintedLayer.cpp | 17 ++++------------- gfx/layers/client/ClientTiledPaintedLayer.h | 3 --- gfx/layers/client/SingleTiledContentClient.cpp | 6 ++++++ gfx/layers/client/SingleTiledContentClient.h | 2 ++ gfx/layers/client/TiledContentClient.h | 3 +++ layout/reftests/invalidation/reftest.list | 4 ++-- modules/libpref/init/all.js | 1 - 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp index 806fb5678b7..01ac573fb3d 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.cpp +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -28,7 +28,6 @@ ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aMana ClientLayerManager::PaintedLayerCreationHint aCreationHint) : PaintedLayer(aManager, static_cast(this), aCreationHint) , mContentClient() - , mHaveSingleTiledContentClient(false) { MOZ_COUNT_CTOR(ClientTiledPaintedLayer); mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0); @@ -412,26 +411,18 @@ ClientTiledPaintedLayer::RenderLayer() void *data = ClientManager()->GetPaintedLayerCallbackData(); IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size(); - IntSize tileSize(gfxPlatform::GetPlatform()->GetTileWidth(), - gfxPlatform::GetPlatform()->GetTileHeight()); - - bool wantSingleTiledContentClient = - (mCreationHint == LayerManager::NONE || layerSize <= tileSize) && - SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) && - gfxPrefs::LayersSingleTileEnabled(); - - if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) { + if (mContentClient && !mContentClient->SupportsLayerSize(layerSize, ClientManager())) { mContentClient = nullptr; mValidRegion.SetEmpty(); } if (!mContentClient) { - if (wantSingleTiledContentClient) { + if (mCreationHint == LayerManager::NONE && + SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) && + gfxPrefs::LayersSingleTileEnabled()) { mContentClient = new SingleTiledContentClient(this, ClientManager()); - mHaveSingleTiledContentClient = true; } else { mContentClient = new MultiTiledContentClient(this, ClientManager()); - mHaveSingleTiledContentClient = false; } mContentClient->Connect(); diff --git a/gfx/layers/client/ClientTiledPaintedLayer.h b/gfx/layers/client/ClientTiledPaintedLayer.h index f4c3f2a8a9b..cda3867a3e0 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.h +++ b/gfx/layers/client/ClientTiledPaintedLayer.h @@ -133,9 +133,6 @@ private: void EndPaint(); RefPtr mContentClient; - // Flag to indicate if mContentClient is a SingleTiledContentClient. This is - // only valid when mContentClient is non-null. - bool mHaveSingleTiledContentClient; nsIntRegion mLowPrecisionValidRegion; BasicTiledLayerPaintData mPaintData; }; diff --git a/gfx/layers/client/SingleTiledContentClient.cpp b/gfx/layers/client/SingleTiledContentClient.cpp index bc6d636b0ce..2b9bd7d3132 100644 --- a/gfx/layers/client/SingleTiledContentClient.cpp +++ b/gfx/layers/client/SingleTiledContentClient.cpp @@ -46,6 +46,12 @@ SingleTiledContentClient::ClientSupportsLayerSize(const gfx::IntSize& aSize, Cli return aSize.width <= maxTextureSize && aSize.height <= maxTextureSize; } +bool +SingleTiledContentClient::SupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager) const +{ + return ClientSupportsLayerSize(aSize, aManager); +} + ClientSingleTiledLayerBuffer::ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer* aPaintedLayer, CompositableClient* aCompositableClient, ClientLayerManager* aManager) diff --git a/gfx/layers/client/SingleTiledContentClient.h b/gfx/layers/client/SingleTiledContentClient.h index 2486f83cea5..e2869976af5 100644 --- a/gfx/layers/client/SingleTiledContentClient.h +++ b/gfx/layers/client/SingleTiledContentClient.h @@ -127,6 +127,8 @@ public: virtual ClientTiledLayerBuffer* GetTiledBuffer() override { return mTiledBuffer; } virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override { return nullptr; } + virtual bool SupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager) const override; + private: RefPtr mTiledBuffer; }; diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h index e6f31e269e9..c53b396fa3c 100644 --- a/gfx/layers/client/TiledContentClient.h +++ b/gfx/layers/client/TiledContentClient.h @@ -628,6 +628,9 @@ public: }; virtual void UpdatedBuffer(TiledBufferType aType) = 0; + virtual bool SupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager) const + { return true; } + private: const char* mName; }; diff --git a/layout/reftests/invalidation/reftest.list b/layout/reftests/invalidation/reftest.list index 3daba5e5b55..5a7a573bc32 100644 --- a/layout/reftests/invalidation/reftest.list +++ b/layout/reftests/invalidation/reftest.list @@ -51,7 +51,7 @@ pref(layout.animated-image-layers.enabled,true) skip-if(Android||gtkWidget) == t != clipped-animated-transform-1.html about:blank != paintedlayer-recycling-1.html about:blank != paintedlayer-recycling-2.html about:blank -pref(layers.single-tile.enabled,false) != paintedlayer-recycling-3.html about:blank +!= paintedlayer-recycling-3.html about:blank != paintedlayer-recycling-4.html about:blank != paintedlayer-recycling-5.html about:blank != paintedlayer-recycling-6.html about:blank @@ -67,7 +67,7 @@ pref(layers.single-tile.enabled,false) != paintedlayer-recycling-3.html about:bl != layer-splitting-7.html about:blank fuzzy-if(gtkWidget,2,4) fuzzy-if(asyncPan,2,3955) fuzzy-if(OSX,179,30) == image-scrolling-zoom-1.html image-scrolling-zoom-1-ref.html != image-scrolling-zoom-1-ref.html image-scrolling-zoom-1-notref.html -pref(layers.single-tile.enabled,false) != fast-scrolling.html about:blank +!= fast-scrolling.html about:blank != fractional-transform-1.html about:blank != fractional-transform-2.html about:blank != fractional-transform-3.html about:blank diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 758d9f58010..1082106d72d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4379,7 +4379,6 @@ pref("layers.draw-tile-borders", false); pref("layers.draw-bigimage-borders", false); pref("layers.frame-counter", false); pref("layers.enable-tiles", false); -pref("layers.single-tile.enabled", true); pref("layers.tiled-drawtarget.enabled", false); pref("layers.low-precision-buffer", false); pref("layers.progressive-paint", false); From 129339406aa73dac0a531931fd77014373407a7e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 12 Feb 2016 09:42:58 +0900 Subject: [PATCH 134/187] Fix up missing dependency in bug 1247162. r=me --- config/faster/rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/faster/rules.mk b/config/faster/rules.mk index a5f0790449f..9d7b322fa20 100644 --- a/config/faster/rules.mk +++ b/config/faster/rules.mk @@ -80,7 +80,7 @@ $(TOPOBJDIR)/%: FORCE # corresponding install manifests are named correspondingly, with forward # slashes replaced with underscores, and prefixed with `install_`. That is, # the install manifest for `dist/bin` would be `install_dist_bin`. -$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/buildid.h +$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/,buildid.h source-repo.h) @# For now, force preprocessed files to be reprocessed every time. @# The overhead is not that big, and this avoids waiting for proper @# support for defines tracking in process_install_manifest. From 2fe0bff9eafce184b47daac966b315fb41e50323 Mon Sep 17 00:00:00 2001 From: Kevin Wern Date: Mon, 25 Jan 2016 04:04:13 -0800 Subject: [PATCH 135/187] Bug 1227224 - Further improvements to generic tree traversal algorithms. r=botond - Introduce a more general version of ForEachNode that takes two actions (pre-action and post-action). - Express the one-action version of ForEachNode in terms of the two-action version. - Add TraversalFlag::Abort, which stops traversal immediately. - Express DepthFirstSearch in terms of ForEachNode. - Add ForEachNodePostOrder and DepthFirstSearchPostOrder. - The new functions are all recursive, and traverse children in the order provided by the node accessors. Adjust tests to reflect this. - Refactor two APZCTreeManager functions, FindTargetNode and GetAPZCAtPoint, to use the algorithms. MozReview-Commit-ID: 83Y7psjkUWG --- gfx/layers/TreeTraversal.h | 224 ++++++++++++++++--------- gfx/layers/apz/src/APZCTreeManager.cpp | 135 +++++++-------- gfx/tests/gtest/TestTreeTraversal.cpp | 201 +++++++++++++++++----- 3 files changed, 370 insertions(+), 190 deletions(-) diff --git a/gfx/layers/TreeTraversal.h b/gfx/layers/TreeTraversal.h index 7c5f9587946..0bd8debcff1 100644 --- a/gfx/layers/TreeTraversal.h +++ b/gfx/layers/TreeTraversal.h @@ -8,19 +8,129 @@ #define mozilla_layers_TreeTraversal_h #include -#include namespace mozilla { namespace layers { /* - * Returned by |aAction| in ForEachNode. If the action returns - * TraversalFlag::Skip, the node's children are not added to the traverrsal - * stack. Otherwise, a return value of TraversalFlag::Continue indicates that - * ForEachNode should traverse each of the node's children. + * Returned by |aPostAction| and |aPreAction| in ForEachNode, indicates + * the behavior to follow either action: + * + * TraversalFlag::Skip - the node's children are not traversed. If this + * flag is returned by |aPreAction|, |aPostAction| is skipped for the + * current node, as well. + * TraversalFlag::Continue - traversal continues normally. + * TraversalFlag::Abort - traversal stops immediately. */ -enum class TraversalFlag { Skip, Continue }; +enum class TraversalFlag { Skip, Continue, Abort }; + +/* + * Do a depth-first traversal of the tree rooted at |aRoot|, performing + * |aPreAction| before traversal of children and |aPostAction| after. + * + * Returns true if traversal aborted, false if continued normally. If + * TraversalFlag::Skip is returned in |aPreAction|, then |aPostAction| + * is not performed. + */ +template +static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) -> +typename EnableIf::value && + IsSame::value, bool>::Type +{ + if (!aRoot) { + return false; + } + + TraversalFlag result = aPreAction(aRoot); + + if (result == TraversalFlag::Abort) { + return true; + } + + if (result == TraversalFlag::Continue) { + for (Node* child = aRoot->GetLastChild(); + child; + child = child->GetPrevSibling()) { + bool abort = ForEachNode(child, aPreAction, aPostAction); + if (abort) { + return true; + } + } + + result = aPostAction(aRoot); + + if (result == TraversalFlag::Abort) { + return true; + } + } + + return false; +} + +/* + * Do a depth-first traversal of the tree rooted at |aRoot|, performing + * |aPreAction| before traversal of children and |aPostAction| after. + */ +template +static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) -> +typename EnableIf::value && + IsSame::value, void>::Type +{ + if (!aRoot) { + return; + } + + aPreAction(aRoot); + + for (Node* child = aRoot->GetLastChild(); + child; + child = child->GetPrevSibling()) { + ForEachNode(child, aPreAction, aPostAction); + } + + aPostAction(aRoot); +} + +/* + * ForEachNode pre-order traversal, using TraversalFlag. + */ +template +auto ForEachNode(Node* aRoot, const PreAction& aPreAction) -> +typename EnableIf::value, bool>::Type +{ + return ForEachNode(aRoot, aPreAction, [](Node* aNode){ return TraversalFlag::Continue; }); +} + +/* + * ForEachNode pre-order, not using TraversalFlag. + */ +template +auto ForEachNode(Node* aRoot, const PreAction& aPreAction) -> +typename EnableIf::value, void>::Type +{ + ForEachNode(aRoot, aPreAction, [](Node* aNode){}); +} + +/* + * ForEachNode post-order traversal, using TraversalFlag. + */ +template +auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) -> +typename EnableIf::value, bool>::Type +{ + return ForEachNode(aRoot, [](Node* aNode){ return TraversalFlag::Continue; }, aPostAction); +} + +/* + * ForEachNode post-order, not using TraversalFlag. + */ +template +auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) -> +typename EnableIf::value, void>::Type +{ + ForEachNode(aRoot, [](Node* aNode){}, aPostAction); +} /* * Do a breadth-first search of the tree rooted at |aRoot|, and return the @@ -57,97 +167,51 @@ Node* BreadthFirstSearch(Node* aRoot, const Condition& aCondition) } /* - * Do a depth-first search of the tree rooted at |aRoot|, and return the - * first visited node that satisfies |aCondition|, or nullptr if no such node - * was found. + * Do a pre-order, depth-first search of the tree rooted at |aRoot|, and + * return the first visited node that satisfies |aCondition|, or nullptr + * if no such node was found. * * |Node| should have methods GetLastChild() and GetPrevSibling(). */ template Node* DepthFirstSearch(Node* aRoot, const Condition& aCondition) { - if (!aRoot) { - return nullptr; - } + Node* result = nullptr; - std::stack stack; - stack.push(aRoot); - while (!stack.empty()) { - Node* node = stack.top(); - stack.pop(); + ForEachNode(aRoot, + [&aCondition, &result](Node* aNode) + { + if (aCondition(aNode)) { + result = aNode; + return TraversalFlag::Abort; + } - if (aCondition(node)) { - return node; - } + return TraversalFlag::Continue; + }); - for (Node* child = node->GetLastChild(); - child; - child = child->GetPrevSibling()) { - stack.push(child); - } - } - - return nullptr; + return result; } /* - * Do a depth-first traversal of the tree rooted at |aRoot|, performing - * |aAction| for each node. |aAction| can return a TraversalFlag to determine - * whether or not to omit the children of a particular node. - * - * If |aAction| does not return a TraversalFlag, it must return nothing. There - * is no ForEachNode instance handling types other than void or TraversalFlag. + * Perform a post-order, depth-first search starting at aRoot. */ -template -auto ForEachNode(Node* aRoot, const Action& aAction) -> -typename EnableIf::value, void>::Type +template +Node* DepthFirstSearchPostOrder(Node* aRoot, const Condition& aCondition) { - if (!aRoot) { - return; - } + Node* result = nullptr; - std::stack stack; - stack.push(aRoot); + ForEachNodePostOrder(aRoot, + [&aCondition, &result](Node* aNode) + { + if (aCondition(aNode)) { + result = aNode; + return TraversalFlag::Abort; + } - while (!stack.empty()) { - Node* node = stack.top(); - stack.pop(); + return TraversalFlag::Continue; + }); - TraversalFlag result = aAction(node); - - if (result == TraversalFlag::Continue) { - for (Node* child = node->GetLastChild(); - child; - child = child->GetPrevSibling()) { - stack.push(child); - } - } - } -} - -template -auto ForEachNode(Node* aRoot, const Action& aAction) -> -typename EnableIf::value, void>::Type -{ - if (!aRoot) { - return; - } - - std::stack stack; - stack.push(aRoot); - - while (!stack.empty()) { - Node* node = stack.top(); - stack.pop(); - - aAction(node); - - for (Node* child = node->GetLastChild(); - child; - child = child->GetPrevSibling()) { - stack.push(child); - } - } + return result; } } diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index c28fc904b81..74c08e958f5 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -3,6 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include #include "APZCTreeManager.h" #include "AsyncPanZoomController.h" #include "Compositor.h" // for Compositor @@ -1512,7 +1513,20 @@ APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid, GuidComparator aComparator) { mTreeLock.AssertCurrentThreadOwns(); - RefPtr target = FindTargetNode(mRootNode, aGuid, aComparator); + RefPtr target = DepthFirstSearchPostOrder(mRootNode.get(), + [&aGuid, &aComparator](HitTestingTreeNode* node) + { + bool matches = false; + if (node->GetApzc()) { + if (aComparator) { + matches = aComparator(aGuid, node->GetApzc()->GetGuid()); + } else { + matches = node->GetApzc()->Matches(aGuid); + } + } + return matches; + } + ); return target.forget(); } @@ -1622,36 +1636,6 @@ APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled) NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled)); } -HitTestingTreeNode* -APZCTreeManager::FindTargetNode(HitTestingTreeNode* aNode, - const ScrollableLayerGuid& aGuid, - GuidComparator aComparator) -{ - mTreeLock.AssertCurrentThreadOwns(); - - // This walks the tree in depth-first, reverse order, so that it encounters - // APZCs front-to-back on the screen. - for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) { - HitTestingTreeNode* match = FindTargetNode(node->GetLastChild(), aGuid, aComparator); - if (match) { - return match; - } - - bool matches = false; - if (node->GetApzc()) { - if (aComparator) { - matches = aComparator(aGuid, node->GetApzc()->GetGuid()); - } else { - matches = node->GetApzc()->Matches(aGuid); - } - } - if (matches) { - return node; - } - } - return nullptr; -} - RefPtr APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics) { @@ -1672,51 +1656,60 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode, // This walks the tree in depth-first, reverse order, so that it encounters // APZCs front-to-back on the screen. - for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) { - if (node->IsOutsideClip(aHitTestPoint)) { - // If the point being tested is outside the clip region for this node - // then we don't need to test against this node or any of its children. - // Just skip it and move on. - APZCTM_LOG("Point %f %f outside clip for node %p\n", - aHitTestPoint.x, aHitTestPoint.y, node); - continue; - } + HitTestingTreeNode* resultNode; + HitTestingTreeNode* root = aNode; + std::stack hitTestPoints; + hitTestPoints.push(aHitTestPoint); - AsyncPanZoomController* result = nullptr; - - // First check the subtree rooted at this node, because deeper nodes - // are more "in front". - Maybe hitTestPointForChildLayers = node->Untransform(aHitTestPoint); - if (hitTestPointForChildLayers) { - ParentLayerPoint childPoint = ViewAs(hitTestPointForChildLayers.ref(), - PixelCastJustification::MovingDownToChildren); - result = GetAPZCAtPoint(node->GetLastChild(), childPoint, aOutHitResult); - } - - // If we didn't match anything in the subtree, check |node|. - if (*aOutHitResult == HitNothing) { - APZCTM_LOG("Testing ParentLayer point %s (Layer %s) against node %p\n", - Stringify(aHitTestPoint).c_str(), - hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil", - node); - HitTestResult hitResult = node->HitTest(aHitTestPoint); - if (hitResult != HitTestResult::HitNothing) { - result = node->GetNearestContainingApzcWithSameLayersId(); - if (!result) { - result = FindRootApzcForLayersId(node->GetLayersId()); - MOZ_ASSERT(result); + ForEachNode(root, + [&hitTestPoints](HitTestingTreeNode* aNode) { + if (aNode->IsOutsideClip(hitTestPoints.top())) { + // If the point being tested is outside the clip region for this node + // then we don't need to test against this node or any of its children. + // Just skip it and move on. + APZCTM_LOG("Point %f %f outside clip for node %p\n", + hitTestPoints.top().x, hitTestPoints.top().y, aNode); + return TraversalFlag::Skip; } - APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n", - result, node, hitResult); - MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion); - // If event regions are disabled, *aOutHitResult will be HitLayer - *aOutHitResult = hitResult; + // First check the subtree rooted at this node, because deeper nodes + // are more "in front". + Maybe hitTestPointForChildLayers = aNode->Untransform(hitTestPoints.top()); + APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n", + Stringify(hitTestPoints.top()).c_str(), + hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil"); + if (!hitTestPointForChildLayers) { + return TraversalFlag::Skip; + } + hitTestPoints.push(ViewAs(hitTestPointForChildLayers.ref(), + PixelCastJustification::MovingDownToChildren)); + return TraversalFlag::Continue; + }, + [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) { + hitTestPoints.pop(); + HitTestResult hitResult = aNode->HitTest(hitTestPoints.top()); + APZCTM_LOG("Testing ParentLayer point %s against node %p\n", + Stringify(hitTestPoints.top()).c_str(), aNode); + if (hitResult != HitTestResult::HitNothing) { + resultNode = aNode; + MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion); + // If event regions are disabled, *aOutHitResult will be HitLayer + *aOutHitResult = hitResult; + return TraversalFlag::Abort; + } + return TraversalFlag::Continue; } - } + ); - if (*aOutHitResult != HitNothing) { + if (*aOutHitResult != HitNothing) { + MOZ_ASSERT(resultNode); + AsyncPanZoomController* result = resultNode->GetNearestContainingApzcWithSameLayersId(); + if (!result) { + result = FindRootApzcForLayersId(resultNode->GetLayersId()); + MOZ_ASSERT(result); + } + APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n", + result, aNode, *aOutHitResult); return result; - } } return nullptr; diff --git a/gfx/tests/gtest/TestTreeTraversal.cpp b/gfx/tests/gtest/TestTreeTraversal.cpp index 731b8ba8560..d8ebec01ef5 100644 --- a/gfx/tests/gtest/TestTreeTraversal.cpp +++ b/gfx/tests/gtest/TestTreeTraversal.cpp @@ -121,15 +121,15 @@ TEST(TreeTraversal, DepthFirstSearchValueExists) } RefPtr root = nodeList[0]; - nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); - nodeList[1]->AddChild(nodeList[2]); + nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); - nodeList[4]->AddChild(nodeList[5]); + nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); + nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); - nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); + nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = DepthFirstSearch(root.get(), [&visitCount](SearchTestNode* aNode) @@ -185,33 +185,156 @@ TEST(TreeTraversal, DepthFirstSearchValueDoesNotExist) } RefPtr root = nodeList[0]; - nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); - nodeList[1]->AddChild(nodeList[2]); + nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); - nodeList[4]->AddChild(nodeList[5]); + nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); + nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); - nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); + nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = DepthFirstSearch(root.get(), [&visitCount](SearchTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (int i = 0; i < 10; i++) { - ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), + ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } - ASSERT_EQ(foundNode.get(), nullptr) + ASSERT_EQ(foundNode.get(), nullptr) + << "Search found something that should not exist."; +} + +TEST(TreeTraversal, DepthFirstSearchPostOrderNull) +{ + RefPtr nullNode; + RefPtr result = DepthFirstSearchPostOrder(nullNode.get(), + [](SearchTestNode* aNode) + { + return aNode->GetType() == SearchNodeType::Needle; + }); + ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result."; +} + +TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists) +{ + int visitCount = 0; + size_t expectedNeedleTraversalRank = 7; + RefPtr needleNode; + std::vector> nodeList; + for (size_t i = 0; i < 10; i++) + { + if (i == expectedNeedleTraversalRank) { + needleNode = new SearchTestNode(SearchNodeType::Needle, i); + nodeList.push_back(needleNode); + } else if (i < expectedNeedleTraversalRank) { + nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i)); + } else { + nodeList.push_back(new SearchTestNode(SearchNodeType::Hay)); + } + } + + RefPtr root = nodeList[9]; + nodeList[9]->AddChild(nodeList[8]); + nodeList[9]->AddChild(nodeList[2]); + nodeList[2]->AddChild(nodeList[1]); + nodeList[2]->AddChild(nodeList[0]); + nodeList[8]->AddChild(nodeList[7]); + nodeList[8]->AddChild(nodeList[6]); + nodeList[6]->AddChild(nodeList[5]); + nodeList[5]->AddChild(nodeList[4]); + nodeList[5]->AddChild(nodeList[3]); + + RefPtr foundNode = DepthFirstSearchPostOrder(root.get(), + [&visitCount](SearchTestNode* aNode) + { + aNode->SetActualTraversalRank(visitCount); + visitCount++; + return aNode->GetType() == SearchNodeType::Needle; + }); + + for (size_t i = 0; i < nodeList.size(); i++) + { + ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), + nodeList[i]->GetActualTraversalRank()) + << "Node at index " << i << " was hit out of order."; + } + + ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; + ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) + << "Returned node does not match expected value (something odd happened)."; +} + +TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle) +{ + RefPtr root = new SearchTestNode(SearchNodeType::Needle, 0); + RefPtr childNode1= new SearchTestNode(SearchNodeType::Hay); + RefPtr childNode2 = new SearchTestNode(SearchNodeType::Hay); + int visitCount = 0; + RefPtr result = DepthFirstSearchPostOrder(root.get(), + [&visitCount](SearchTestNode* aNode) + { + aNode->SetActualTraversalRank(visitCount); + visitCount++; + return aNode->GetType() == SearchNodeType::Needle; + }); + ASSERT_EQ(result, root) << "Search starting at needle did not return needle."; + ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) + << "Search starting at needle did not return needle."; + ASSERT_EQ(childNode1->GetExpectedTraversalRank(), + childNode1->GetActualTraversalRank()) + << "Search starting at needle continued past needle."; + ASSERT_EQ(childNode2->GetExpectedTraversalRank(), + childNode2->GetActualTraversalRank()) + << "Search starting at needle continued past needle."; +} + +TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist) +{ + int visitCount = 0; + std::vector> nodeList; + for (int i = 0; i < 10; i++) + { + nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i)); + } + + RefPtr root = nodeList[9]; + nodeList[9]->AddChild(nodeList[8]); + nodeList[9]->AddChild(nodeList[2]); + nodeList[2]->AddChild(nodeList[1]); + nodeList[2]->AddChild(nodeList[0]); + nodeList[8]->AddChild(nodeList[7]); + nodeList[8]->AddChild(nodeList[6]); + nodeList[6]->AddChild(nodeList[5]); + nodeList[5]->AddChild(nodeList[4]); + nodeList[5]->AddChild(nodeList[3]); + + RefPtr foundNode = DepthFirstSearchPostOrder(root.get(), + [&visitCount](SearchTestNode* aNode) + { + aNode->SetActualTraversalRank(visitCount); + visitCount++; + return aNode->GetType() == SearchNodeType::Needle; + }); + + for (int i = 0; i < 10; i++) + { + ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), + nodeList[i]->GetActualTraversalRank()) + << "Node at index " << i << " was hit out of order."; + } + + ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } @@ -243,10 +366,10 @@ TEST(TreeTraversal, BreadthFirstSearchRootIsNeedle) ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) << "Search starting at needle did not return needle."; ASSERT_EQ(childNode1->GetExpectedTraversalRank(), - childNode1->GetActualTraversalRank()) + childNode1->GetActualTraversalRank()) << "Search starting at needle continued past needle."; ASSERT_EQ(childNode2->GetExpectedTraversalRank(), - childNode2->GetActualTraversalRank()) + childNode2->GetActualTraversalRank()) << "Search starting at needle continued past needle."; } @@ -283,7 +406,7 @@ TEST(TreeTraversal, BreadthFirstSearchValueExists) [&visitCount](SearchTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); @@ -324,7 +447,7 @@ TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExist) [&visitCount](SearchTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); @@ -359,24 +482,24 @@ TEST(TreeTraversal, ForEachNodeAllEligible) } RefPtr root = nodeList[0]; - nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); - nodeList[1]->AddChild(nodeList[2]); + nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); - nodeList[4]->AddChild(nodeList[5]); + nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); + nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); - nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); + nodeList[7]->AddChild(nodeList[8]); ForEachNode(root.get(), [&visitCount](ForEachTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == ForEachNodeType::Continue - ? TraversalFlag::Continue : TraversalFlag::Skip; + ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) @@ -404,21 +527,21 @@ TEST(TreeTraversal, ForEachNodeSomeIneligibleNodes) expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip)); RefPtr root = expectedVisitedNodeList[0]; - expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]); expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]); - expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]); + expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]); expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]); + expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]); expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]); - expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]); expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]); + expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]); ForEachNode(root.get(), [&visitCount](ForEachTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == ForEachNodeType::Continue - ? TraversalFlag::Continue : TraversalFlag::Skip; + ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) @@ -448,9 +571,9 @@ TEST(TreeTraversal, ForEachNodeIneligibleRoot) [&visitCount](ForEachTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == ForEachNodeType::Continue - ? TraversalFlag::Continue : TraversalFlag::Skip; + ? TraversalFlag::Continue : TraversalFlag::Skip; }); ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) @@ -476,23 +599,23 @@ TEST(TreeTraversal, ForEachNodeLeavesIneligible) } RefPtr root = nodeList[0]; - nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[2]); - nodeList[2]->AddChild(nodeList[3]); + nodeList[0]->AddChild(nodeList[1]); nodeList[2]->AddChild(nodeList[4]); - nodeList[4]->AddChild(nodeList[5]); + nodeList[2]->AddChild(nodeList[3]); nodeList[4]->AddChild(nodeList[6]); + nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); - nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); + nodeList[7]->AddChild(nodeList[8]); ForEachNode(root.get(), [&visitCount](ForEachTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; return aNode->GetType() == ForEachNodeType::Continue - ? TraversalFlag::Continue : TraversalFlag::Skip; + ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) @@ -513,22 +636,22 @@ TEST(TreeTraversal, ForEachNodeLambdaReturnsVoid) } RefPtr root = nodeList[0]; - nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); - nodeList[1]->AddChild(nodeList[2]); + nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); - nodeList[4]->AddChild(nodeList[5]); + nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); + nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); - nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); + nodeList[7]->AddChild(nodeList[8]); ForEachNode(root.get(), [&visitCount](ForEachTestNode* aNode) { aNode->SetActualTraversalRank(visitCount); - visitCount++; + visitCount++; }); for (size_t i = 0; i < nodeList.size(); i++) From ee4677067e6caf021fc1b8781dfaa0cdaccd791c Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Wed, 3 Feb 2016 23:21:44 +0100 Subject: [PATCH 136/187] Bug 1232681 - Display script-generated animations correctly. r=pbro MozReview-Commit-ID: 2pk7sxVTHTk --- .../components/animation-time-block.js | 23 ++++++-- .../animationinspector/test/browser.ini | 1 + ...tion_playerWidgets_appear_on_panel_init.js | 38 ++++++++++-- .../test/doc_multiple_animation_types.html | 58 +++++++++++++++++++ .../en-US/animationinspector.properties | 11 ++++ devtools/client/themes/animationinspector.css | 9 +++ devtools/server/actors/animation.js | 26 ++++++--- .../server/tests/unit/test_animation_name.js | 8 +++ .../server/tests/unit/test_animation_type.js | 7 +++ 9 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 devtools/client/animationinspector/test/doc_multiple_animation_types.html diff --git a/devtools/client/animationinspector/components/animation-time-block.js b/devtools/client/animationinspector/components/animation-time-block.js index 8c12b6389e6..e05f6cd21ba 100644 --- a/devtools/client/animationinspector/components/animation-time-block.js +++ b/devtools/client/animationinspector/components/animation-time-block.js @@ -148,13 +148,24 @@ AnimationTimeBlock.prototype = { /** * Get a formatted title for this animation. This will be either: - * "some-name", "some-name : CSS Transition", or "some-name : CSS Animation", - * depending if the server provides the type, and what type it is. + * "some-name", "some-name : CSS Transition", "some-name : CSS Animation", + * "some-name : Script Animation", or "Script Animation", depending + * if the server provides the type, what type it is and if the animation + * has a name * @param {AnimationPlayerFront} animation */ function getFormattedAnimationTitle({state}) { - // Older servers don't send the type. - return state.type - ? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name) - : state.name; + // Older servers don't send a type, and only know about + // CSSAnimations and CSSTransitions, so it's safe to use + // just the name. + if (!state.type) { + return state.name; + } + + // Script-generated animations may not have a name. + if (state.type === "scriptanimation" && !state.name) { + return L10N.getStr("timeline.scriptanimation.unnamedLabel"); + } + + return L10N.getFormatStr(`timeline.${state.type}.nameLabel`, state.name); } diff --git a/devtools/client/animationinspector/test/browser.ini b/devtools/client/animationinspector/test/browser.ini index 418d9fa0e20..d4f02fc3a1f 100644 --- a/devtools/client/animationinspector/test/browser.ini +++ b/devtools/client/animationinspector/test/browser.ini @@ -8,6 +8,7 @@ support-files = doc_modify_playbackRate.html doc_negative_animation.html doc_simple_animation.html + doc_multiple_animation_types.html head.js [browser_animation_animated_properties_displayed.js] diff --git a/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js b/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js index 01c7251d828..2c66eb636ea 100644 --- a/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js +++ b/devtools/client/animationinspector/test/browser_animation_playerWidgets_appear_on_panel_init.js @@ -7,11 +7,41 @@ // Test that player widgets are displayed right when the animation panel is // initialized, if the selected node ( by default) is animated. +const { ANIMATION_TYPES } = require("devtools/server/actors/animation"); + add_task(function*() { - yield addTab(TEST_URL_ROOT + "doc_body_animation.html"); + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.animations-api.core.enabled", true] + ]}, resolve); + }); + + yield addTab(TEST_URL_ROOT + "doc_multiple_animation_types.html"); let {panel} = yield openAnimationInspector(); - is(panel.animationsTimelineComponent.animations.length, 1, - "One animation is handled by the timeline after init"); - assertAnimationsDisplayed(panel, 1, "One animation is displayed after init"); + is(panel.animationsTimelineComponent.animations.length, 3, + "Three animations are handled by the timeline after init"); + assertAnimationsDisplayed(panel, 3, + "Three animations are displayed after init"); + is( + panel.animationsTimelineComponent + .animationsEl + .querySelectorAll(`.animation.${ANIMATION_TYPES.SCRIPT_ANIMATION}`) + .length, + 1, + "One script-generated animation is displayed"); + is( + panel.animationsTimelineComponent + .animationsEl + .querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_ANIMATION}`) + .length, + 1, + "One CSS animation is displayed"); + is( + panel.animationsTimelineComponent + .animationsEl + .querySelectorAll(`.animation.${ANIMATION_TYPES.CSS_TRANSITION}`) + .length, + 1, + "One CSS transition is displayed"); }); diff --git a/devtools/client/animationinspector/test/doc_multiple_animation_types.html b/devtools/client/animationinspector/test/doc_multiple_animation_types.html new file mode 100644 index 00000000000..f3606a63bd1 --- /dev/null +++ b/devtools/client/animationinspector/test/doc_multiple_animation_types.html @@ -0,0 +1,58 @@ + + + + + + + +
+
+
+ + + + diff --git a/devtools/client/locales/en-US/animationinspector.properties b/devtools/client/locales/en-US/animationinspector.properties index bf44b7c4df6..c31b632c5ad 100644 --- a/devtools/client/locales/en-US/animationinspector.properties +++ b/devtools/client/locales/en-US/animationinspector.properties @@ -108,6 +108,17 @@ timeline.cssanimation.nameLabel=%S - CSS Animation # %S will be replaced by the name of the transition at run-time. timeline.csstransition.nameLabel=%S - CSS Transition +# LOCALIZATION NOTE (timeline.scriptanimation.nameLabel): +# This string is displayed in a tooltip of the animation panel that is shown +# when hovering over the name of a script-generated animation in the timeline UI. +# %S will be replaced by the name of the animation at run-time. +timeline.scriptanimation.nameLabel=%S - Script Animation + +# LOCALIZATION NOTE (timeline.scriptanimation.unnamedLabel): +# This string is displayed in a tooltip of the animation panel that is shown +# when hovering over an unnamed script-generated animation in the timeline UI. +timeline.scriptanimation.unnamedLabel=Script Animation + # LOCALIZATION NOTE (timeline.unknown.nameLabel): # This string is displayed in a tooltip of the animation panel that is shown # when hovering over the name of an unknown animation type in the timeline UI. diff --git a/devtools/client/themes/animationinspector.css b/devtools/client/themes/animationinspector.css index 0ae8f4b12d2..0872dd5e0ea 100644 --- a/devtools/client/themes/animationinspector.css +++ b/devtools/client/themes/animationinspector.css @@ -42,6 +42,11 @@ --timeline-background-color: var(--theme-highlight-blue); } +.animation.scriptanimation { + --timeline-border-color: var(--theme-highlight-green); + --timeline-background-color: var(--theme-graphs-green); +} + html { height: 100%; } @@ -529,6 +534,10 @@ body { background-color: var(--theme-highlight-blue); } +.keyframes.scriptanimation { + background-color: var(--theme-graphs-green); +} + .keyframes .frame { position: absolute; top: 0; diff --git a/devtools/server/actors/animation.js b/devtools/server/actors/animation.js index 47a191033fe..c95427d1c72 100644 --- a/devtools/server/actors/animation.js +++ b/devtools/server/actors/animation.js @@ -39,6 +39,7 @@ const events = require("sdk/event/core"); const ANIMATION_TYPES = { CSS_ANIMATION: "cssanimation", CSS_TRANSITION: "csstransition", + SCRIPT_ANIMATION: "scriptanimation", UNKNOWN: "unknown" }; exports.ANIMATION_TYPES = ANIMATION_TYPES; @@ -119,19 +120,28 @@ var AnimationPlayerActor = ActorClass({ return data; }, - isAnimation: function(player = this.player) { + isCssAnimation: function(player = this.player) { return player instanceof this.window.CSSAnimation; }, - isTransition: function(player = this.player) { + isCssTransition: function(player = this.player) { return player instanceof this.window.CSSTransition; }, + isScriptAnimation: function(player = this.player) { + return player instanceof this.window.Animation && !( + player instanceof this.window.CSSAnimation || + player instanceof this.window.CSSTransition + ); + }, + getType: function() { - if (this.isAnimation()) { + if (this.isCssAnimation()) { return ANIMATION_TYPES.CSS_ANIMATION; - } else if (this.isTransition()) { + } else if (this.isCssTransition()) { return ANIMATION_TYPES.CSS_TRANSITION; + } else if (this.isScriptAnimation()) { + return ANIMATION_TYPES.SCRIPT_ANIMATION; } return ANIMATION_TYPES.UNKNOWN; @@ -146,9 +156,9 @@ var AnimationPlayerActor = ActorClass({ getName: function() { if (this.player.id) { return this.player.id; - } else if (this.isAnimation()) { + } else if (this.isCssAnimation()) { return this.player.animationName; - } else if (this.isTransition()) { + } else if (this.isCssTransition()) { return this.player.transitionProperty; } @@ -626,9 +636,9 @@ var AnimationsActor = exports.AnimationsActor = ActorClass({ // a "removed" event for the one we already have. let index = this.actors.findIndex(a => { let isSameType = a.player.constructor === player.constructor; - let isSameName = (a.isAnimation() && + let isSameName = (a.isCssAnimation() && a.player.animationName === player.animationName) || - (a.isTransition() && + (a.isCssTransition() && a.player.transitionProperty === player.transitionProperty); let isSameNode = a.player.effect.target === player.effect.target; diff --git a/devtools/server/tests/unit/test_animation_name.js b/devtools/server/tests/unit/test_animation_name.js index 82cf438ee5f..2f266b6aeff 100644 --- a/devtools/server/tests/unit/test_animation_name.js +++ b/devtools/server/tests/unit/test_animation_name.js @@ -24,6 +24,9 @@ function run_test() { } }; + window.CSSAnimation.prototype = Object.create(window.Animation.prototype); + window.CSSTransition.prototype = Object.create(window.Animation.prototype); + // Helper to get a mock DOM node. function getMockNode() { return { @@ -46,6 +49,11 @@ function run_test() { animation: new window.Animation(), props: { id: "animation-id" }, expectedName: "animation-id" + }, { + desc: "Animation without an id", + animation: new window.Animation(), + props: {}, + expectedName: "" }, { desc: "CSSTransition with an id", animation: new window.CSSTransition(), diff --git a/devtools/server/tests/unit/test_animation_type.js b/devtools/server/tests/unit/test_animation_type.js index 0d31266c5ff..e8859a74831 100644 --- a/devtools/server/tests/unit/test_animation_type.js +++ b/devtools/server/tests/unit/test_animation_type.js @@ -24,6 +24,9 @@ function run_test() { } }; + window.CSSAnimation.prototype = Object.create(window.Animation.prototype); + window.CSSTransition.prototype = Object.create(window.Animation.prototype); + // Helper to get a mock DOM node. function getMockNode() { return { @@ -47,6 +50,10 @@ function run_test() { desc: "Test CSSTransition type", animation: new window.CSSTransition(), expectedType: ANIMATION_TYPES.CSS_TRANSITION + }, { + desc: "Test ScriptAnimation type", + animation: new window.Animation(), + expectedType: ANIMATION_TYPES.SCRIPT_ANIMATION }, { desc: "Test unknown type", animation: {effect: {target: getMockNode()}}, From d7d495ed868a77d51981b0b16d08a905ecefdbf2 Mon Sep 17 00:00:00 2001 From: Joe Walker Date: Fri, 5 Feb 2016 18:30:59 +0000 Subject: [PATCH 137/187] Bug 1243805 - Replace Proxy.create with new Proxy in devtools l10n code. r=jryans --- devtools/shared/gcli/source/lib/gcli/l10n.js | 2 +- devtools/shared/gcli/source/lib/gcli/util/l10n.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devtools/shared/gcli/source/lib/gcli/l10n.js b/devtools/shared/gcli/source/lib/gcli/l10n.js index afdab932fd9..d9a3cb1bc41 100644 --- a/devtools/shared/gcli/source/lib/gcli/l10n.js +++ b/devtools/shared/gcli/source/lib/gcli/l10n.js @@ -47,7 +47,7 @@ exports.lookup = function(name) { * l10n:l10n.propertyLookup in the template data and use it * like ${l10n.BLAH} */ -exports.propertyLookup = Proxy.create({ +exports.propertyLookup = new Proxy({}, { get: function(rcvr, name) { return exports.lookup(name); } diff --git a/devtools/shared/gcli/source/lib/gcli/util/l10n.js b/devtools/shared/gcli/source/lib/gcli/util/l10n.js index 56938970691..8532b6961ad 100644 --- a/devtools/shared/gcli/source/lib/gcli/util/l10n.js +++ b/devtools/shared/gcli/source/lib/gcli/util/l10n.js @@ -71,7 +71,7 @@ exports.lookup = function(key) { }; /** @see propertyLookup in lib/gcli/util/l10n.js */ -exports.propertyLookup = Proxy.create({ +exports.propertyLookup = new Proxy({}, { get: function(rcvr, name) { return exports.lookup(name); } From 868d14365e72ba824a403a6acea0d95eb63bc470 Mon Sep 17 00:00:00 2001 From: Vince Tieu Date: Tue, 9 Feb 2016 15:57:00 +0100 Subject: [PATCH 138/187] Bug 524757 - "Add architecture and operating system to about:support". r=adw --- toolkit/content/aboutSupport.js | 1 + toolkit/content/aboutSupport.xhtml | 9 +++++++++ toolkit/locales/en-US/chrome/global/aboutSupport.dtd | 1 + toolkit/modules/Troubleshoot.jsm | 6 ++++++ toolkit/modules/tests/browser/browser_Troubleshoot.js | 8 ++++++++ 5 files changed, 25 insertions(+) diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index b3594adbb86..14663c89934 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -37,6 +37,7 @@ var snapshotFormatters = { application: function application(data) { $("application-box").textContent = data.name; $("useragent-box").textContent = data.userAgent; + $("osarch-box").textContent = data.osVersion + " " + data.arch; $("supportLink").href = data.supportURL; let version = AppConstants.MOZ_APP_VERSION_DISPLAY; if (data.vendor) diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index 625f746ada2..46144461fab 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -130,6 +130,15 @@ + + + &aboutSupport.appBasicsOS; + + + + + + #ifdef XP_WIN diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd index fcda5cd9083..13dda60d910 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd @@ -54,6 +54,7 @@ Windows/Mac use the term "Folder" instead of "Directory" --> + diff --git a/toolkit/modules/Troubleshoot.jsm b/toolkit/modules/Troubleshoot.jsm index e364a0555dd..b8a28757580 100644 --- a/toolkit/modules/Troubleshoot.jsm +++ b/toolkit/modules/Troubleshoot.jsm @@ -178,8 +178,14 @@ this.Troubleshoot = { var dataProviders = { application: function application(done) { + + let sysInfo = Cc["@mozilla.org/system-info;1"]. + getService(Ci.nsIPropertyBag2); + let data = { name: Services.appinfo.name, + osVersion: sysInfo.getProperty("name") + " " + sysInfo.getProperty("version"), + arch: sysInfo.getProperty("arch"), version: AppConstants.MOZ_APP_VERSION_DISPLAY, buildID: Services.appinfo.appBuildID, userAgent: Cc["@mozilla.org/network/protocol;1?name=http"]. diff --git a/toolkit/modules/tests/browser/browser_Troubleshoot.js b/toolkit/modules/tests/browser/browser_Troubleshoot.js index 49cb92de82a..53d055ef589 100644 --- a/toolkit/modules/tests/browser/browser_Troubleshoot.js +++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js @@ -112,6 +112,14 @@ const SNAPSHOT_SCHEMA = { required: true, type: "string", }, + osVersion: { + required: true, + type: "string", + }, + arch: { + required: true, + type: "string", + }, vendor: { type: "string", }, From 050f86189e6a98e4e92c924c71345cf953c07c63 Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Wed, 10 Feb 2016 17:57:37 +0100 Subject: [PATCH 139/187] Bug 1246570 - mem.tool parentMap maps nodeId->node, remove verbose xpcshell logs;r=fitzgen --- .../client/memory/test/browser/browser.ini | 1 + .../test/browser/browser_memory_keyboard.js | 101 ++++++++++++++++++ devtools/client/memory/test/unit/head.js | 2 +- devtools/shared/heapsnapshot/CensusUtils.js | 2 +- .../shared/heapsnapshot/HeapAnalysesClient.js | 2 +- 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 devtools/client/memory/test/browser/browser_memory_keyboard.js diff --git a/devtools/client/memory/test/browser/browser.ini b/devtools/client/memory/test/browser/browser.ini index 7d8e4339bd2..29fee425dc4 100644 --- a/devtools/client/memory/test/browser/browser.ini +++ b/devtools/client/memory/test/browser/browser.ini @@ -14,6 +14,7 @@ support-files = [browser_memory_dominator_trees_01.js] [browser_memory_dominator_trees_02.js] [browser_memory_filter_01.js] +[browser_memory_keyboard.js] [browser_memory_no_allocation_stacks.js] [browser_memory_no_auto_expand.js] skip-if = debug # bug 1219554 diff --git a/devtools/client/memory/test/browser/browser_memory_keyboard.js b/devtools/client/memory/test/browser/browser_memory_keyboard.js new file mode 100644 index 00000000000..205fe198c79 --- /dev/null +++ b/devtools/client/memory/test/browser/browser_memory_keyboard.js @@ -0,0 +1,101 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Bug 1246570 - Check that when pressing on LEFT arrow, the parent tree node +// gets focused. + +"use strict"; + +const { + snapshotState +} = require("devtools/client/memory/constants"); +const { + takeSnapshotAndCensus +} = require("devtools/client/memory/actions/snapshot"); + +const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html"; + +function waitUntilFocused(store, node) { + return waitUntilState(store, state => + state.snapshots.length === 1 && + state.snapshots[0].state === snapshotState.SAVED_CENSUS && + state.snapshots[0].census && + state.snapshots[0].census.focused && + state.snapshots[0].census.focused === node + ); +} + +function waitUntilExpanded(store, node) { + return waitUntilState(store, state => + state.snapshots[0] && + state.snapshots[0].census && + state.snapshots[0].census.expanded.has(node.id)); +} + +this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) { + const heapWorker = panel.panelWin.gHeapAnalysesClient; + const front = panel.panelWin.gFront; + const store = panel.panelWin.gStore; + const { getState, dispatch } = store; + const doc = panel.panelWin.document; + + is(getState().breakdown.by, "coarseType"); + yield dispatch(takeSnapshotAndCensus(front, heapWorker)); + let census = getState().snapshots[0].census; + let root1 = census.report.children[0]; + let root2 = census.report.children[0]; + let root3 = census.report.children[0]; + let root4 = census.report.children[0]; + let child1 = root1.children[0]; + + info("Click on first node."); + let firstNode = doc.querySelector(".tree .heap-tree-item-name"); + EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin); + yield waitUntilFocused(store, root1); + ok(true, "First root is selected after click."); + + info("Press DOWN key, expect second root focused."); + EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin); + yield waitUntilFocused(store, root2); + ok(true, "Second root is selected after pressing DOWN arrow."); + + info("Press DOWN key, expect third root focused."); + EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin); + yield waitUntilFocused(store, root3); + ok(true, "Third root is selected after pressing DOWN arrow."); + + info("Press DOWN key, expect fourth root focused."); + EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin); + yield waitUntilFocused(store, root4); + ok(true, "Fourth root is selected after pressing DOWN arrow."); + + info("Press UP key, expect third root focused."); + EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin); + yield waitUntilFocused(store, root3); + ok(true, "Third root is selected after pressing UP arrow."); + + info("Press UP key, expect second root focused."); + EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin); + yield waitUntilFocused(store, root2); + ok(true, "Second root is selected after pressing UP arrow."); + + info("Press UP key, expect first root focused."); + EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin); + yield waitUntilFocused(store, root1); + ok(true, "First root is selected after pressing UP arrow."); + + info("Press RIGHT key"); + EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin); + yield waitUntilExpanded(store, root1); + ok(true, "Root node is expanded."); + + info("Press RIGHT key, expect first child focused."); + EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin); + yield waitUntilFocused(store, child1); + ok(true, "First child is selected after pressing RIGHT arrow."); + + info("Press LEFT key, expect first root focused."); + EventUtils.synthesizeKey("VK_LEFT", {}, panel.panelWin); + yield waitUntilFocused(store, root1); + ok(true, "First root is selected after pressing LEFT arrow."); +}); diff --git a/devtools/client/memory/test/unit/head.js b/devtools/client/memory/test/unit/head.js index 890cb0b063e..2a084b64bdb 100644 --- a/devtools/client/memory/test/unit/head.js +++ b/devtools/client/memory/test/unit/head.js @@ -12,7 +12,7 @@ var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); DevToolsUtils.testing = true; DevToolsUtils.dumpn.wantLogging = true; -DevToolsUtils.dumpv.wantLogging = true; +DevToolsUtils.dumpv.wantVerbose = false; var { OS } = require("resource://gre/modules/osfile.jsm"); var { FileUtils } = require("resource://gre/modules/FileUtils.jsm"); diff --git a/devtools/shared/heapsnapshot/CensusUtils.js b/devtools/shared/heapsnapshot/CensusUtils.js index 200825c44fc..63bf494e211 100644 --- a/devtools/shared/heapsnapshot/CensusUtils.js +++ b/devtools/shared/heapsnapshot/CensusUtils.js @@ -375,7 +375,7 @@ const createParentMap = exports.createParentMap = function (node, if (node.children) { for (let i = 0, length = node.children.length; i < length; i++) { const child = node.children[i]; - aggregator[getId(child)] = getId(node); + aggregator[getId(child)] = node; createParentMap(child, getId, aggregator); } } diff --git a/devtools/shared/heapsnapshot/HeapAnalysesClient.js b/devtools/shared/heapsnapshot/HeapAnalysesClient.js index 1a2ab23cc94..f8ada182043 100644 --- a/devtools/shared/heapsnapshot/HeapAnalysesClient.js +++ b/devtools/shared/heapsnapshot/HeapAnalysesClient.js @@ -21,7 +21,7 @@ var workerCounter = 0; const HeapAnalysesClient = module.exports = function () { this._worker = new DevToolsWorker(WORKER_URL, { name: `HeapAnalyses-${workerCounter++}`, - verbose: DevToolsUtils.dumpv.wantLogging + verbose: DevToolsUtils.dumpv.wantVerbose }); }; From 6588ead784ddb6600c74b2786177a61160ee3851 Mon Sep 17 00:00:00 2001 From: Andrew Swan Date: Tue, 2 Feb 2016 21:08:51 -0800 Subject: [PATCH 140/187] Bug 1236940 - Add ip property to chrome.webRequest.onCompleted callback. r=billm --- .../components/extensions/ext-webRequest.js | 4 ++ .../test/mochitest/test_ext_webrequest.html | 41 +++++++++++++++---- toolkit/modules/addons/WebRequest.jsm | 9 ++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/toolkit/components/extensions/ext-webRequest.js b/toolkit/components/extensions/ext-webRequest.js index 2e205315bef..e3bc693a31b 100644 --- a/toolkit/components/extensions/ext-webRequest.js +++ b/toolkit/components/extensions/ext-webRequest.js @@ -41,6 +41,10 @@ function WebRequestEventManager(context, eventName) { parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId), }; + if ("ip" in data) { + data2.ip = data.ip; + } + // Fills in tabId typically. let result = {}; extensions.emit("fill-browser-data", data.browser, data2, result); diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html index a4b3562ab94..2c578a5592e 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html @@ -87,6 +87,7 @@ function compareLists(list1, list2, kind) { function backgroundScript() { const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest"; + let checkCompleted = true; let savedTabId = -1; function checkType(details) { @@ -197,15 +198,36 @@ function backgroundScript() { } } + let completedUrls = { + responseStarted: new Set(), + completed: new Set(), + }; + + function checkIpAndRecord(kind, details) { + onRecord(kind, details); + + // When resources are cached, the ip property is not present, + // so only check for the ip property the first time around. + if (checkCompleted && !completedUrls[kind].has(details.url)) { + browser.test.assertEq(details.ip, "127.0.0.1", "correct ip"); + completedUrls[kind].add(details.url); + } + } + browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: [""]}, ["blocking"]); browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: [""]}, ["blocking"]); browser.webRequest.onSendHeaders.addListener(onRecord.bind(null, "sendHeaders"), {urls: [""]}); browser.webRequest.onBeforeRedirect.addListener(onBeforeRedirect, {urls: [""]}); - browser.webRequest.onResponseStarted.addListener(onRecord.bind(null, "responseStarted"), {urls: [""]}); - browser.webRequest.onCompleted.addListener(onRecord.bind(null, "completed"), {urls: [""]}); + browser.webRequest.onResponseStarted.addListener(checkIpAndRecord.bind(null, "responseStarted"), {urls: [""]}); + browser.webRequest.onCompleted.addListener(checkIpAndRecord.bind(null, "completed"), {urls: [""]}); - function onTestMessage() { - browser.test.sendMessage("results", recorded); + function onTestMessage(msg) { + if (msg == "skipCompleted") { + checkCompleted = false; + browser.test.sendMessage("ackSkipCompleted"); + } else { + browser.test.sendMessage("results", recorded); + } } browser.test.onMessage.addListener(onTestMessage); @@ -213,7 +235,7 @@ function backgroundScript() { browser.test.sendMessage("ready", browser.webRequest.ResourceType); } -function* test_once() { +function* test_once(skipCompleted) { let extensionData = { manifest: { permissions: [ @@ -228,6 +250,11 @@ function* test_once() { let [, resourceTypes] = yield Promise.all([extension.startup(), extension.awaitMessage("ready")]); info("webrequest extension loaded"); + if (skipCompleted) { + extension.sendMessage("skipCompleted"); + yield extension.awaitMessage("ackSkipCompleted"); + } + for (let key in resourceTypes) { let value = resourceTypes[key]; is(key, value.toUpperCase()); @@ -278,8 +305,8 @@ function* test_once() { } // Run the test twice to make sure it works with caching. -add_task(test_once); -add_task(test_once); +add_task(function*() { yield test_once(false); }); +add_task(function*() { yield test_once(true); }); diff --git a/toolkit/modules/addons/WebRequest.jsm b/toolkit/modules/addons/WebRequest.jsm index 99c3382cf8f..3937e53b099 100644 --- a/toolkit/modules/addons/WebRequest.jsm +++ b/toolkit/modules/addons/WebRequest.jsm @@ -333,6 +333,15 @@ HttpObserverManager = { windowId: loadInfo ? loadInfo.outerWindowID : 0, parentWindowId: loadInfo ? loadInfo.parentOuterWindowID : 0, }; + + let httpChannel = channel.QueryInterface(Ci.nsIHttpChannelInternal); + try { + data.ip = httpChannel.remoteAddress; + } catch (e) { + // The remoteAddress getter throws if the address is unavailable, + // but ip is an optional property so just ignore the exception. + } + if (extraData) { Object.assign(data, extraData); } From 677233fcc556f55638902b822151ed181ab00d1a Mon Sep 17 00:00:00 2001 From: Eric Hu Date: Thu, 4 Feb 2016 01:25:55 +0700 Subject: [PATCH 141/187] Bug 1244766 - Remove optional Health Report callback parameter from Places telemetry. r=gfritzsche --- toolkit/components/places/PlacesDBUtils.jsm | 36 ++----------------- .../places/tests/unit/test_telemetry.js | 15 -------- 2 files changed, 3 insertions(+), 48 deletions(-) diff --git a/toolkit/components/places/PlacesDBUtils.jsm b/toolkit/components/places/PlacesDBUtils.jsm index 49332a1bfec..4d2cabd42de 100644 --- a/toolkit/components/places/PlacesDBUtils.jsm +++ b/toolkit/components/places/PlacesDBUtils.jsm @@ -854,27 +854,15 @@ this.PlacesDBUtils = { }, /** - * Collects telemetry data. - * - * There are essentially two modes of collection and the mode is - * determined by the presence of aHealthReportCallback. If - * aHealthReportCallback is not defined (the default) then we are in - * "Telemetry" mode. Results will be reported to Telemetry. If we are - * in "Health Report" mode only the probes with a true healthreport - * flag will be collected and the results will be reported to the - * aHealthReportCallback. + * Collects telemetry data and reports it to Telemetry. * * @param [optional] aTasks * Tasks object to execute. - * @param [optional] aHealthReportCallback - * Function to receive data relevant for Firefox Health Report. */ - telemetry: function PDBU_telemetry(aTasks, aHealthReportCallback=null) + telemetry: function PDBU_telemetry(aTasks) { let tasks = new Tasks(aTasks); - let isTelemetry = !aHealthReportCallback; - // This will be populated with one integer property for each probe result, // using the histogram name as key. let probeValues = {}; @@ -891,19 +879,15 @@ this.PlacesDBUtils = { // histogram. If a query is also present, its result is passed // as the first argument of the function. If the function // raises an exception, no data is added to the histogram. - // healthreport: Boolean indicating whether this probe is relevant - // to Firefox Health Report. // // Since all queries are executed in order by the database backend, the // callbacks can also use the result of previous queries stored in the // probeValues object. let probes = [ { histogram: "PLACES_PAGES_COUNT", - healthreport: true, query: "SELECT count(*) FROM moz_places" }, { histogram: "PLACES_BOOKMARKS_COUNT", - healthreport: true, query: `SELECT count(*) FROM moz_bookmarks b JOIN moz_bookmarks t ON t.id = b.parent AND t.parent <> :tags_folder @@ -989,15 +973,9 @@ this.PlacesDBUtils = { places_root: PlacesUtils.placesRootId }; - let outstandingProbes = []; - for (let i = 0; i < probes.length; i++) { let probe = probes[i]; - if (!isTelemetry && !probe.healthreport) { - continue; - } - let promiseDone = new Promise((resolve, reject) => { if (!("query" in probe)) { resolve([probe]); @@ -1027,7 +1005,7 @@ this.PlacesDBUtils = { // Report the result of the probe through Telemetry. // The resulting promise cannot reject. - promiseDone = promiseDone.then( + promiseDone.then( // On success ([aProbe, aValue]) => { let value = aValue; @@ -1045,14 +1023,6 @@ this.PlacesDBUtils = { }, // On failure this._handleError); - - outstandingProbes.push(promiseDone); - } - - if (aHealthReportCallback) { - Promise.all(outstandingProbes).then(() => - aHealthReportCallback(probeValues) - ); } PlacesDBUtils._executeTasks(tasks); diff --git a/toolkit/components/places/tests/unit/test_telemetry.js b/toolkit/components/places/tests/unit/test_telemetry.js index 62d704780ce..ea8132d715d 100644 --- a/toolkit/components/places/tests/unit/test_telemetry.js +++ b/toolkit/components/places/tests/unit/test_telemetry.js @@ -124,18 +124,3 @@ add_task(function* test_execute() do_check_true(snapshot.counts.reduce((a, b) => a + b) > 0); } }); - -add_test(function test_healthreport_callback() { - Services.prefs.clearUserPref("places.database.lastMaintenance"); - PlacesDBUtils.telemetry(null, function onResult(data) { - do_check_neq(data, null); - - do_check_eq(Object.keys(data).length, 2); - do_check_eq(data.PLACES_PAGES_COUNT, 1); - do_check_eq(data.PLACES_BOOKMARKS_COUNT, 1); - - do_check_true(!Services.prefs.prefHasUserValue("places.database.lastMaintenance")); - run_next_test(); - }); -}); - From 2b89a6eb29d29c068b07103a6188a9ec94f699a7 Mon Sep 17 00:00:00 2001 From: Tracy Walker Date: Fri, 5 Feb 2016 10:18:36 -0600 Subject: [PATCH 142/187] Bug 1245225 - [e10s] Renabled working test cases. Remove relevant skip-if e10s in toolkit/components/thumbnails/test/browser.ini for browser_thumbnails_bg_crash_while_idle.js, browser_thumbnails_bg_no_cookies_sent.js, browser_thumbnails_bug727765.js and browser_thumbnails_capture.js. r=jimm --- toolkit/components/thumbnails/test/browser.ini | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index 72fb1e7a35f..dad8d145c83 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -13,14 +13,13 @@ support-files = [browser_thumbnails_bg_crash_during_capture.js] skip-if = buildapp == 'mulet' || !crashreporter || e10s # crashing the remote thumbnailer crashes the remote test tab [browser_thumbnails_bg_crash_while_idle.js] -skip-if = buildapp == 'mulet' || !crashreporter || e10s +skip-if = buildapp == 'mulet' || !crashreporter [browser_thumbnails_bg_basic.js] [browser_thumbnails_bg_queueing.js] [browser_thumbnails_bg_timeout.js] [browser_thumbnails_bg_redirect.js] [browser_thumbnails_bg_destroy_browser.js] [browser_thumbnails_bg_no_cookies_sent.js] -skip-if = e10s # e10s cookie problems [browser_thumbnails_bg_no_cookies_stored.js] [browser_thumbnails_bg_no_auth_prompt.js] [browser_thumbnails_bg_no_alert.js] @@ -29,10 +28,8 @@ skip-if = e10s # e10s cookie problems [browser_thumbnails_bug726727.js] skip-if = buildapp == 'mulet' [browser_thumbnails_bug727765.js] -skip-if = e10s # tries to open crypto/local file from the child [browser_thumbnails_bug818225.js] [browser_thumbnails_capture.js] -skip-if = e10s # tries to call drawWindow with a remote browser. [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From 43a304febe2a8c4e63e9ccbcb4af67266e2daff1 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 11 Feb 2016 04:29:47 -0800 Subject: [PATCH 143/187] Bug 1245462 - Replace usages of gDevTools.jsm by module imports. r=jryans --- devtools/bootstrap.js | 2 +- .../client/aboutdebugging/components/target.js | 4 ++-- devtools/client/animationinspector/test/head.js | 2 +- devtools/client/canvasdebugger/test/head.js | 2 +- devtools/client/debugger/debugger-commands.js | 3 +-- devtools/client/devtools-clhandler.js | 16 +++++++++++----- devtools/client/framework/connect/connect.js | 2 +- devtools/client/framework/gDevTools.jsm | 6 ------ devtools/client/framework/test/shared-head.js | 2 +- devtools/client/framework/toolbox-options.js | 2 +- devtools/client/framework/toolbox.js | 2 +- devtools/client/inspector/computed/computed.js | 2 +- devtools/client/inspector/inspector-commands.js | 2 +- devtools/client/inspector/rules/rules.js | 3 +-- devtools/client/inspector/shared/test/head.js | 2 +- devtools/client/jsonview/main.js | 2 +- devtools/client/main.js | 2 +- devtools/client/memory/test/unit/head.js | 1 - devtools/client/netmonitor/test/head.js | 2 +- devtools/client/performance/test/head.js | 2 +- devtools/client/scratchpad/scratchpad.js | 2 +- devtools/client/shadereditor/test/head.js | 2 +- devtools/client/shared/autocomplete-popup.js | 2 +- .../shared/components/test/mochitest/head.js | 2 +- .../browser_toolbar_webconsole_errors_count.js | 3 --- devtools/client/shared/theme-switching.js | 2 +- devtools/client/shared/theme.js | 2 +- devtools/client/shared/view-source.js | 2 +- devtools/client/styleeditor/StyleEditorUI.jsm | 2 +- .../client/styleeditor/styleeditor-commands.js | 2 +- devtools/client/tilt/tilt-visualizer.js | 2 +- devtools/client/webaudioeditor/includes.js | 2 +- devtools/client/webaudioeditor/test/head.js | 2 +- devtools/client/webconsole/console-commands.js | 2 +- devtools/client/webconsole/console-output.js | 2 +- devtools/client/webconsole/hudservice.js | 2 +- devtools/client/webconsole/webconsole.js | 2 +- devtools/client/webide/content/details.js | 1 - devtools/client/webide/content/logs.js | 1 - devtools/client/webide/content/monitor.js | 1 - devtools/client/webide/content/webide.js | 3 ++- devtools/client/webide/test/head.js | 2 +- devtools/client/webide/test/test_basic.html | 2 +- devtools/server/actors/csscoverage.js | 4 +--- devtools/shared/Loader.jsm | 2 +- devtools/shared/gcli/commands/calllog.js | 2 +- devtools/shared/gcli/commands/csscoverage.js | 3 +-- 47 files changed, 53 insertions(+), 64 deletions(-) diff --git a/devtools/bootstrap.js b/devtools/bootstrap.js index 46c1fe3026d..d9b1f423355 100644 --- a/devtools/bootstrap.js +++ b/devtools/bootstrap.js @@ -82,7 +82,7 @@ function reload(event) { devtools.reload(reloadToolbox); // Also tells gDevTools to reload its dependencies - const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); + const {gDevTools} = devtools.require("devtools/client/framework/devtools"); gDevTools.reload(); } diff --git a/devtools/client/aboutdebugging/components/target.js b/devtools/client/aboutdebugging/components/target.js index c88986e268f..44e3bf2cb0a 100644 --- a/devtools/client/aboutdebugging/components/target.js +++ b/devtools/client/aboutdebugging/components/target.js @@ -17,8 +17,8 @@ loader.lazyRequireGetter(this, "Services"); loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm"); -loader.lazyImporter(this, "gDevTools", - "resource://devtools/client/framework/gDevTools.jsm"); +loader.lazyRequireGetter(this, "gDevTools", + "devtools/client/framework/devtools", true); const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); diff --git a/devtools/client/animationinspector/test/head.js b/devtools/client/animationinspector/test/head.js index 2db590865c8..5e9d915ba02 100644 --- a/devtools/client/animationinspector/test/head.js +++ b/devtools/client/animationinspector/test/head.js @@ -5,8 +5,8 @@ "use strict"; var Cu = Components.utils; -const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const {gDevTools} = require("devtools/client/framework/devtools"); const promise = require("promise"); const {TargetFactory} = require("devtools/client/framework/target"); const {console} = Cu.import("resource://gre/modules/Console.jsm", {}); diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js index f0dd9bb2951..e36879ac5ad 100644 --- a/devtools/client/canvasdebugger/test/head.js +++ b/devtools/client/canvasdebugger/test/head.js @@ -13,10 +13,10 @@ Services.prefs.setBoolPref("devtools.debugger.log", false); var { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator); var { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var promise = require("promise"); +var { gDevTools } = require("devtools/client/framework/devtools"); var { DebuggerClient } = require("devtools/shared/client/main"); var { DebuggerServer } = require("devtools/server/main"); var { CallWatcherFront } = require("devtools/server/actors/call-watcher"); diff --git a/devtools/client/debugger/debugger-commands.js b/devtools/client/debugger/debugger-commands.js index cb27bbc907b..4be6976b859 100644 --- a/devtools/client/debugger/debugger-commands.js +++ b/devtools/client/debugger/debugger-commands.js @@ -8,8 +8,7 @@ const { Cc, Ci, Cu } = require("chrome"); const l10n = require("gcli/l10n"); - -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); +const { gDevTools } = require("devtools/client/framework/devtools"); /** * The commands and converters that are exported to GCLI diff --git a/devtools/client/devtools-clhandler.js b/devtools/client/devtools-clhandler.js index 593a1aec472..873c3aaf687 100644 --- a/devtools/client/devtools-clhandler.js +++ b/devtools/client/devtools-clhandler.js @@ -51,8 +51,13 @@ devtoolsCommandlineHandler.prototype = { let window = Services.wm.getMostRecentWindow("devtools:webconsole"); if (!window) { let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); - // Load the browser devtools main module as the loader's main module. - Cu.import("resource://devtools/client/framework/gDevTools.jsm"); + // Ensure loading main devtools module that hooks up into browser UI + // and initialize all devtools machinery. + // browser.xul or main top-level document used to load this module, + // but this code may be called without/before it. + // Bug 1247203 should ease handling this. + require("devtools/client/framework/devtools-browser"); + let hudservice = require("devtools/client/webconsole/hudservice"); let { console } = Cu.import("resource://gre/modules/Console.jsm", {}); hudservice.toggleBrowserConsole().then(null, console.error); @@ -71,9 +76,10 @@ devtoolsCommandlineHandler.prototype = { Services.obs.addObserver(function onStartup(window) { Services.obs.removeObserver(onStartup, "browser-delayed-startup-finished"); - const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); - const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {}); - let target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab); + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const {gDevTools} = require("devtools/client/framework/devtools"); + const {TargetFactory} = require("devtools/client/framework/target"); + let target = TargetFactory.forTab(window.gBrowser.selectedTab); gDevTools.showToolbox(target); }, "browser-delayed-startup-finished", false); }, diff --git a/devtools/client/framework/connect/connect.js b/devtools/client/framework/connect/connect.js index e7aa340cd09..5284cc7585a 100644 --- a/devtools/client/framework/connect/connect.js +++ b/devtools/client/framework/connect/connect.js @@ -10,8 +10,8 @@ var Cu = Components.utils; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); -var {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var {gDevTools} = require("devtools/client/framework/devtools"); var {TargetFactory} = require("devtools/client/framework/target"); var {Toolbox} = require("devtools/client/framework/toolbox") var promise = require("promise"); diff --git a/devtools/client/framework/gDevTools.jsm b/devtools/client/framework/gDevTools.jsm index 227fe332c5d..df1d45425f1 100644 --- a/devtools/client/framework/gDevTools.jsm +++ b/devtools/client/framework/gDevTools.jsm @@ -9,18 +9,12 @@ * Please now use the modules: * - devtools/client/framework/devtools for gDevTools * - devtools/client/framework/devtools-browser for gDevToolsBrowser - * - * We still do use gDevTools.jsm in our codebase, - * bug 1245462 is going to ensure we no longer do that. */ this.EXPORTED_SYMBOLS = [ "gDevTools", "gDevToolsBrowser" ]; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - const { loader } = Cu.import("resource://devtools/shared/Loader.jsm", {}); /** diff --git a/devtools/client/framework/test/shared-head.js b/devtools/client/framework/test/shared-head.js index a00592dbe7f..d2299dc5499 100644 --- a/devtools/client/framework/test/shared-head.js +++ b/devtools/client/framework/test/shared-head.js @@ -17,11 +17,11 @@ function scopedCuImport(path) { } const {Services} = scopedCuImport("resource://gre/modules/Services.jsm"); -const {gDevTools} = scopedCuImport("resource://devtools/client/framework/gDevTools.jsm"); const {console} = scopedCuImport("resource://gre/modules/Console.jsm"); const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm"); const {require} = scopedCuImport("resource://devtools/shared/Loader.jsm"); +const {gDevTools} = require("devtools/client/framework/devtools"); const {TargetFactory} = require("devtools/client/framework/target"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); let promise = require("promise"); diff --git a/devtools/client/framework/toolbox-options.js b/devtools/client/framework/toolbox-options.js index eea72c3fa35..9bce031c253 100644 --- a/devtools/client/framework/toolbox-options.js +++ b/devtools/client/framework/toolbox-options.js @@ -9,7 +9,7 @@ const Services = require("Services"); const promise = require("promise"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); +const {gDevTools} = require("devtools/client/framework/devtools"); exports.OptionsPanel = OptionsPanel; diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js index cc50512c649..53cbb237233 100644 --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -16,6 +16,7 @@ const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER"; var {Cc, Ci, Cu} = require("chrome"); var promise = require("promise"); +var {gDevTools} = require("devtools/client/framework/devtools"); var EventEmitter = require("devtools/shared/event-emitter"); var Telemetry = require("devtools/client/shared/telemetry"); var HUDService = require("devtools/client/webconsole/hudservice"); @@ -23,7 +24,6 @@ var viewSource = require("devtools/client/shared/view-source"); var { attachThread, detachThread } = require("./attach-thread"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm"); Cu.import("resource://devtools/client/shared/DOMHelpers.jsm"); Cu.import("resource://gre/modules/Task.jsm"); diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js index f71332450fa..c18f27144fa 100644 --- a/devtools/client/inspector/computed/computed.js +++ b/devtools/client/inspector/computed/computed.js @@ -18,7 +18,7 @@ const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", const {OutputParser} = require("devtools/client/shared/output-parser"); const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils"); const {createChild} = require("devtools/client/inspector/shared/utils"); -const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); +const {gDevTools} = require("devtools/client/framework/devtools"); loader.lazyRequireGetter(this, "overlays", "devtools/client/inspector/shared/style-inspector-overlays"); diff --git a/devtools/client/inspector/inspector-commands.js b/devtools/client/inspector/inspector-commands.js index 718c2d7fa10..bc9d8cca023 100644 --- a/devtools/client/inspector/inspector-commands.js +++ b/devtools/client/inspector/inspector-commands.js @@ -22,7 +22,7 @@ exports.items = [{ ], exec: function(args, context) { let target = context.environment.target; - let gDevTools = require("resource://devtools/client/framework/gDevTools.jsm").gDevTools; + let {gDevTools} = require("devtools/client/framework/devtools"); return gDevTools.showToolbox(target, "inspector").then(toolbox => { toolbox.getCurrentPanel().selection.setNode(args.selector, "gcli"); diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js index 0f44740568a..bdca31f05fb 100644 --- a/devtools/client/inspector/rules/rules.js +++ b/devtools/client/inspector/rules/rules.js @@ -24,11 +24,10 @@ const {RuleEditor} = require("devtools/client/inspector/rules/views/rule-editor"); const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils"); +const {gDevTools} = require("devtools/client/framework/devtools"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -loader.lazyGetter(this, "gDevTools", () => - Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}).gDevTools); loader.lazyRequireGetter(this, "overlays", "devtools/client/inspector/shared/style-inspector-overlays"); loader.lazyRequireGetter(this, "EventEmitter", diff --git a/devtools/client/inspector/shared/test/head.js b/devtools/client/inspector/shared/test/head.js index 8d1bf4f1250..de49384c728 100644 --- a/devtools/client/inspector/shared/test/head.js +++ b/devtools/client/inspector/shared/test/head.js @@ -5,8 +5,8 @@ "use strict"; var Cu = Components.utils; -var {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var {gDevTools} = require("devtools/client/framework/devtools"); var {TargetFactory} = require("devtools/client/framework/target"); var {CssRuleView, _ElementStyle} = require("devtools/client/inspector/rules/rules"); var {CssLogic, CssSelector} = require("devtools/shared/inspector/css-logic"); diff --git a/devtools/client/jsonview/main.js b/devtools/client/jsonview/main.js index 91a1408b8ad..47d6d78270a 100644 --- a/devtools/client/jsonview/main.js +++ b/devtools/client/jsonview/main.js @@ -18,7 +18,7 @@ XPCOMUtils.defineLazyGetter(this, "JsonViewService", function() { /** * Singleton object that represents the JSON View in-content tool. * It has the same lifetime as the browser. Initialization done by - * DevTools() object from gDevTools.jsm + * DevTools() object from devtools/client/framework/devtools.js */ var JsonView = { initialize: function() { diff --git a/devtools/client/main.js b/devtools/client/main.js index 7e9dc705b4f..f632ae0cca7 100644 --- a/devtools/client/main.js +++ b/devtools/client/main.js @@ -6,7 +6,7 @@ const { Cu } = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); -const { gDevTools } = require("resource://devtools/client/framework/gDevTools.jsm"); +const { gDevTools } = require("devtools/client/framework/devtools"); const { defaultTools, defaultThemes } = require("devtools/client/definitions"); diff --git a/devtools/client/memory/test/unit/head.js b/devtools/client/memory/test/unit/head.js index 2a084b64bdb..3f9d9ef3144 100644 --- a/devtools/client/memory/test/unit/head.js +++ b/devtools/client/memory/test/unit/head.js @@ -5,7 +5,6 @@ var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var { console } = Cu.import("resource://gre/modules/Console.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); diff --git a/devtools/client/netmonitor/test/head.js b/devtools/client/netmonitor/test/head.js index 41c282af49e..a27fe46d034 100644 --- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -6,8 +6,8 @@ var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); var { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var { gDevTools } = require("devtools/client/framework/devtools"); var { CurlUtils } = Cu.import("resource://devtools/client/shared/Curl.jsm", {}); var promise = require("promise"); var NetworkHelper = require("devtools/shared/webconsole/network-helper"); diff --git a/devtools/client/performance/test/head.js b/devtools/client/performance/test/head.js index 1de443d54f2..846acbddcfa 100644 --- a/devtools/client/performance/test/head.js +++ b/devtools/client/performance/test/head.js @@ -8,7 +8,7 @@ var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); var { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {}); var { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); +var { gDevTools } = require("devtools/client/framework/devtools"); var { console } = require("resource://gre/modules/Console.jsm"); var { TargetFactory } = require("devtools/client/framework/target"); var Promise = require("promise"); diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js index 4dff1db53a3..78fe239df9f 100644 --- a/devtools/client/scratchpad/scratchpad.js +++ b/devtools/client/scratchpad/scratchpad.js @@ -52,13 +52,13 @@ const EventEmitter = require("devtools/shared/event-emitter"); const {DevToolsWorker} = require("devtools/shared/worker/worker"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const promise = require("promise"); +const {gDevTools} = require("devtools/client/framework/devtools"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm"); Cu.import("resource://gre/modules/jsdebugger.jsm"); -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); Cu.import("resource://gre/modules/reflect.jsm"); diff --git a/devtools/client/shadereditor/test/head.js b/devtools/client/shadereditor/test/head.js index 0aa18bd9ec2..4429482667e 100644 --- a/devtools/client/shadereditor/test/head.js +++ b/devtools/client/shadereditor/test/head.js @@ -11,10 +11,10 @@ var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); Services.prefs.setBoolPref("devtools.debugger.log", false); var { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var promise = require("promise"); +var { gDevTools } = require("devtools/client/framework/devtools"); var { DebuggerClient } = require("devtools/shared/client/main"); var { DebuggerServer } = require("devtools/server/main"); var { WebGLFront } = require("devtools/server/actors/webgl"); diff --git a/devtools/client/shared/autocomplete-popup.js b/devtools/client/shared/autocomplete-popup.js index 11e8924b38a..355c1f1a1d0 100644 --- a/devtools/client/shared/autocomplete-popup.js +++ b/devtools/client/shared/autocomplete-popup.js @@ -9,7 +9,7 @@ const {Cc, Ci, Cu} = require("chrome"); const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); +const {gDevTools} = require("devtools/client/framework/devtools"); const events = require("devtools/shared/event-emitter"); /** diff --git a/devtools/client/shared/components/test/mochitest/head.js b/devtools/client/shared/components/test/mochitest/head.js index b4706ab29fa..218c0a1d9c0 100644 --- a/devtools/client/shared/components/test/mochitest/head.js +++ b/devtools/client/shared/components/test/mochitest/head.js @@ -10,7 +10,7 @@ var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); +var { gDevTools } = require("devtools/client/framework/devtools"); var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {}); var { DebuggerServer } = require("devtools/server/main"); var { DebuggerClient } = require("devtools/shared/client/main"); diff --git a/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js b/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js index 3bde1598e24..235af7bd0a2 100644 --- a/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js +++ b/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js @@ -6,9 +6,6 @@ function test() { const TEST_URI = TEST_URI_ROOT + "browser_toolbar_webconsole_errors_count.html"; - let gDevTools = Cu.import("resource://devtools/client/framework/gDevTools.jsm", - {}).gDevTools; - let webconsole = document.getElementById("developer-toolbar-toolbox-button"); let tab1, tab2; diff --git a/devtools/client/shared/theme-switching.js b/devtools/client/shared/theme-switching.js index c5c85ecdbc6..c076f873492 100644 --- a/devtools/client/shared/theme-switching.js +++ b/devtools/client/shared/theme-switching.js @@ -159,8 +159,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); - Cu.import("resource://devtools/client/framework/gDevTools.jsm"); const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {}); + const {gDevTools} = require("devtools/client/framework/devtools"); const StylesheetUtils = require("sdk/stylesheet/utils"); if (documentElement.hasAttribute("force-theme")) { diff --git a/devtools/client/shared/theme.js b/devtools/client/shared/theme.js index bcf5e845292..68ccee368be 100644 --- a/devtools/client/shared/theme.js +++ b/devtools/client/shared/theme.js @@ -12,7 +12,7 @@ const { Ci, Cu } = require("chrome"); const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); loader.lazyRequireGetter(this, "Services"); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); +const { gDevTools } = require("devtools/client/framework/devtools"); const VARIABLES_URI = "chrome://devtools/skin/variables.css"; const THEME_SELECTOR_STRINGS = { diff --git a/devtools/client/shared/view-source.js b/devtools/client/shared/view-source.js index 08375d609fb..09caa67d2e3 100644 --- a/devtools/client/shared/view-source.js +++ b/devtools/client/shared/view-source.js @@ -5,9 +5,9 @@ "use strict"; loader.lazyRequireGetter(this, "Services"); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm"); +var {gDevTools} = require("devtools/client/framework/devtools"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); /** diff --git a/devtools/client/styleeditor/StyleEditorUI.jsm b/devtools/client/styleeditor/StyleEditorUI.jsm index 0b375f7133d..1d17f91ca94 100644 --- a/devtools/client/styleeditor/StyleEditorUI.jsm +++ b/devtools/client/styleeditor/StyleEditorUI.jsm @@ -16,7 +16,7 @@ const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {}); const {OS} = Cu.import("resource://gre/modules/osfile.jsm", {}); const {Task} = Cu.import("resource://gre/modules/Task.jsm", {}); const EventEmitter = require("devtools/shared/event-emitter"); -const {gDevTools} = require("resource://devtools/client/framework/gDevTools.jsm"); +const {gDevTools} = require("devtools/client/framework/devtools"); /* import-globals-from StyleEditorUtil.jsm */ Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm"); const {SplitView} = Cu.import("resource://devtools/client/shared/SplitView.jsm", {}); diff --git a/devtools/client/styleeditor/styleeditor-commands.js b/devtools/client/styleeditor/styleeditor-commands.js index 9ae4c48f9ef..d4803b715b6 100644 --- a/devtools/client/styleeditor/styleeditor-commands.js +++ b/devtools/client/styleeditor/styleeditor-commands.js @@ -58,7 +58,7 @@ exports.items = [{ to: "dom", exec: function(args, context) { let target = context.environment.target; - let gDevTools = require("resource://devtools/client/framework/gDevTools.jsm").gDevTools; + let {gDevTools} = require("devtools/client/framework/devtools"); return gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) { let styleEditor = toolbox.getCurrentPanel(); styleEditor.selectStyleSheet(args.href, args.line); diff --git a/devtools/client/tilt/tilt-visualizer.js b/devtools/client/tilt/tilt-visualizer.js index 68f6f929ea7..5eb9b4ef4af 100644 --- a/devtools/client/tilt/tilt-visualizer.js +++ b/devtools/client/tilt/tilt-visualizer.js @@ -14,7 +14,7 @@ var {EPSILON, TiltMath, vec3, mat4, quat4} = require("devtools/client/tilt/tilt- var {TargetFactory} = require("devtools/client/framework/target"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); +var {gDevTools} = require("devtools/client/framework/devtools"); const ELEMENT_MIN_SIZE = 4; const INVISIBLE_ELEMENTS = { diff --git a/devtools/client/webaudioeditor/includes.js b/devtools/client/webaudioeditor/includes.js index b7911c7ec49..047b7a8a55a 100644 --- a/devtools/client/webaudioeditor/includes.js +++ b/devtools/client/webaudioeditor/includes.js @@ -8,7 +8,6 @@ var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); const { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); @@ -23,6 +22,7 @@ const L10N = new ViewHelpers.L10N(STRINGS_URI); const Telemetry = require("devtools/client/shared/telemetry"); const telemetry = new Telemetry(); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const { gDevTools } = require("devtools/client/framework/devtools"); loader.lazyRequireGetter(this, "LineGraphWidget", "devtools/client/shared/widgets/LineGraphWidget"); diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js index a9ba98652d0..c9c41245ecf 100644 --- a/devtools/client/webaudioeditor/test/head.js +++ b/devtools/client/webaudioeditor/test/head.js @@ -12,8 +12,8 @@ var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); Services.prefs.setBoolPref("devtools.debugger.log", false); var { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); -var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var { gDevTools } = require("devtools/client/framework/devtools"); var { TargetFactory } = require("devtools/client/framework/target"); var { DebuggerServer } = require("devtools/server/main"); var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); diff --git a/devtools/client/webconsole/console-commands.js b/devtools/client/webconsole/console-commands.js index 098592b46a2..7a056d6029f 100644 --- a/devtools/client/webconsole/console-commands.js +++ b/devtools/client/webconsole/console-commands.js @@ -7,7 +7,7 @@ "use strict"; const l10n = require("gcli/l10n"); -loader.lazyGetter(this, "gDevTools", () => require("resource://devtools/client/framework/gDevTools.jsm").gDevTools); +loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); exports.items = [ { diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index f903b8fb43a..f0f6847758e 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -12,11 +12,11 @@ const { Services } = require("resource://gre/modules/Services.jsm"); loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm"); loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm"); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm"); loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); loader.lazyRequireGetter(this, "promise"); +loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true); loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true); diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js index c4373f0ede1..4df5bc1c394 100644 --- a/devtools/client/webconsole/hudservice.js +++ b/devtools/client/webconsole/hudservice.js @@ -16,8 +16,8 @@ var promise = require("promise"); loader.lazyGetter(this, "Telemetry", () => require("devtools/client/shared/telemetry")); loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/client/webconsole/webconsole").WebConsoleFrame); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); +loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true); loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true); loader.lazyGetter(this, "showDoorhanger", () => require("devtools/client/shared/doorhanger").showDoorhanger); diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index 3626684ce3e..ebf0f24640d 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -29,8 +29,8 @@ loader.lazyRequireGetter(this, "system", "devtools/shared/system"); loader.lazyRequireGetter(this, "Timers", "sdk/timers"); loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm"); loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm"); +loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); const STRINGS_URI = "chrome://devtools/locale/webconsole.properties"; var l10n = new WebConsoleUtils.l10n(STRINGS_URI); diff --git a/devtools/client/webide/content/details.js b/devtools/client/webide/content/details.js index 917ed3441d1..d59e8cd05bd 100644 --- a/devtools/client/webide/content/details.js +++ b/devtools/client/webide/content/details.js @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var Cu = Components.utils; -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); const {Services} = Cu.import("resource://gre/modules/Services.jsm"); const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); const {AppManager} = require("devtools/client/webide/modules/app-manager"); diff --git a/devtools/client/webide/content/logs.js b/devtools/client/webide/content/logs.js index 64f42502836..238da963f34 100644 --- a/devtools/client/webide/content/logs.js +++ b/devtools/client/webide/content/logs.js @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var Cu = Components.utils; -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); const {AppManager} = require("devtools/client/webide/modules/app-manager"); diff --git a/devtools/client/webide/content/monitor.js b/devtools/client/webide/content/monitor.js index 289094ee377..99b90da678e 100644 --- a/devtools/client/webide/content/monitor.js +++ b/devtools/client/webide/content/monitor.js @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var Cu = Components.utils; -Cu.import('resource://devtools/client/framework/gDevTools.jsm'); const {require} = Cu.import('resource://devtools/shared/Loader.jsm', {}); const {Services} = Cu.import('resource://gre/modules/Services.jsm'); const {AppManager} = require('devtools/client/webide/modules/app-manager'); diff --git a/devtools/client/webide/content/webide.js b/devtools/client/webide/content/webide.js index 38bf96a6334..d22548b2241 100644 --- a/devtools/client/webide/content/webide.js +++ b/devtools/client/webide/content/webide.js @@ -6,10 +6,11 @@ var Cc = Components.classes; var Cu = Components.utils; var Ci = Components.interfaces; -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); Cu.import("resource://gre/modules/Task.jsm"); const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const {gDevTools} = require("devtools/client/framework/devtools"); +const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser"); const {Toolbox} = require("devtools/client/framework/toolbox"); const Services = require("Services"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); diff --git a/devtools/client/webide/test/head.js b/devtools/client/webide/test/head.js index 6e6667cc5b4..1e7ecc7902d 100644 --- a/devtools/client/webide/test/head.js +++ b/devtools/client/webide/test/head.js @@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); -const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); +const {gDevTools} = require("devtools/client/framework/devtools"); const promise = require("promise"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); diff --git a/devtools/client/webide/test/test_basic.html b/devtools/client/webide/test/test_basic.html index 544179f5b59..e619a0f0689 100644 --- a/devtools/client/webide/test/test_basic.html +++ b/devtools/client/webide/test/test_basic.html @@ -21,7 +21,7 @@ Task.spawn(function* () { let win = yield openWebIDE(); - const {gDevToolsBrowser} = Cu.import("resource://devtools/client/framework/gDevTools.jsm"); + const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser"); yield gDevToolsBrowser.isWebIDEInitialized.promise; ok(true, "WebIDE was initialized"); diff --git a/devtools/server/actors/csscoverage.js b/devtools/server/actors/csscoverage.js index 2aada9da661..68ecce0cf3f 100644 --- a/devtools/server/actors/csscoverage.js +++ b/devtools/server/actors/csscoverage.js @@ -12,10 +12,8 @@ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); const events = require("sdk/event/core"); const protocol = require("devtools/server/protocol"); const { method, custom, RetVal, Arg } = protocol; +const {gDevTools} = require("devtools/client/framework/devtools"); -loader.lazyGetter(this, "gDevTools", () => { - return require("resource://devtools/client/framework/gDevTools.jsm").gDevTools; -}); loader.lazyGetter(this, "DOMUtils", () => { return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils) }); diff --git a/devtools/shared/Loader.jsm b/devtools/shared/Loader.jsm index 536e32325c1..cc08f17d4cc 100644 --- a/devtools/shared/Loader.jsm +++ b/devtools/shared/Loader.jsm @@ -444,7 +444,7 @@ DevToolsLoader.prototype = { setTimeout(() => { let { gBrowser } = window; let target = this.TargetFactory.forTab(gBrowser.selectedTab); - const { gDevTools } = this.require("resource://devtools/client/framework/gDevTools.jsm"); + const { gDevTools } = require("devtools/client/framework/devtools"); gDevTools.showToolbox(target); }, 1000); } else if (location.includes("/webide.xul")) { diff --git a/devtools/shared/gcli/commands/calllog.js b/devtools/shared/gcli/commands/calllog.js index 58b0abafa42..54e11e8fd2b 100644 --- a/devtools/shared/gcli/commands/calllog.js +++ b/devtools/shared/gcli/commands/calllog.js @@ -7,10 +7,10 @@ const { Cc, Ci, Cu } = require("chrome"); const l10n = require("gcli/l10n"); const gcli = require("gcli/index"); +const { gDevTools } = require("devtools/client/framework/devtools"); loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true); -loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); loader.lazyGetter(this, "Debugger", () => { let global = Cu.getGlobalForObject({}); diff --git a/devtools/shared/gcli/commands/csscoverage.js b/devtools/shared/gcli/commands/csscoverage.js index d893727ed30..7d417b44dc7 100644 --- a/devtools/shared/gcli/commands/csscoverage.js +++ b/devtools/shared/gcli/commands/csscoverage.js @@ -6,8 +6,7 @@ const { Cc, Ci } = require("chrome"); -loader.lazyGetter(this, "gDevTools", () => require("resource://devtools/client/framework/gDevTools.jsm").gDevTools); - +const { gDevTools } = require("devtools/client/framework/devtools"); const domtemplate = require("gcli/util/domtemplate"); const csscoverage = require("devtools/server/actors/csscoverage"); const l10n = csscoverage.l10n; From d7300b9b6267b8f76cc680ff5f72ae62d209ab0e Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 11 Feb 2016 04:29:47 -0800 Subject: [PATCH 144/187] Bug 1245462 - Cleanup various require/import in devtools. r=jryans --- devtools/client/inspector/shared/test/head.js | 3 +-- .../responsivedesign/responsivedesign.jsm | 17 +++++++---------- .../shared/components/test/mochitest/head.js | 2 +- devtools/client/webconsole/hudservice.js | 6 +++--- devtools/shared/gcli/commands/calllog.js | 9 +-------- 5 files changed, 13 insertions(+), 24 deletions(-) diff --git a/devtools/client/inspector/shared/test/head.js b/devtools/client/inspector/shared/test/head.js index de49384c728..5857f12c7f2 100644 --- a/devtools/client/inspector/shared/test/head.js +++ b/devtools/client/inspector/shared/test/head.js @@ -14,8 +14,7 @@ var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var promise = require("promise"); var {editableField, getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor"); -var {console} = - Components.utils.import("resource://gre/modules/Console.jsm", {}); +var {console} = Cu.import("resource://gre/modules/Console.jsm", {}); // All tests are asynchronous waitForExplicitFinish(); diff --git a/devtools/client/responsivedesign/responsivedesign.jsm b/devtools/client/responsivedesign/responsivedesign.jsm index 367f3b7e139..540853b5701 100644 --- a/devtools/client/responsivedesign/responsivedesign.jsm +++ b/devtools/client/responsivedesign/responsivedesign.jsm @@ -7,21 +7,18 @@ const Ci = Components.interfaces; const Cu = Components.utils; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://devtools/client/framework/gDevTools.jsm"); -Cu.import("resource://devtools/shared/event-emitter.js"); -Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", - "resource://gre/modules/SystemAppProxy.jsm"); - -var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var Telemetry = require("devtools/client/shared/telemetry"); var { showDoorhanger } = require("devtools/client/shared/doorhanger"); var { TouchEventSimulator } = require("devtools/shared/touch/simulator"); var { Task } = require("resource://gre/modules/Task.jsm"); var promise = require("promise"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); +var Services = require("Services"); +var EventEmitter = require("devtools/shared/event-emitter"); +var { ViewHelpers } = require("devtools/client/shared/widgets/ViewHelpers.jsm"); +loader.lazyImporter(this, "SystemAppProxy", + "resource://gre/modules/SystemAppProxy.jsm"); this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"]; @@ -1092,6 +1089,6 @@ ResponsiveUI.prototype = { }, } -XPCOMUtils.defineLazyGetter(ResponsiveUI.prototype, "strings", function () { +loader.lazyGetter(ResponsiveUI.prototype, "strings", function () { return Services.strings.createBundle("chrome://devtools/locale/responsiveUI.properties"); }); diff --git a/devtools/client/shared/components/test/mochitest/head.js b/devtools/client/shared/components/test/mochitest/head.js index 218c0a1d9c0..f037dc37055 100644 --- a/devtools/client/shared/components/test/mochitest/head.js +++ b/devtools/client/shared/components/test/mochitest/head.js @@ -8,10 +8,10 @@ Cu.import("resource://testing-common/Assert.jsm"); Cu.import("resource://gre/modules/Task.jsm"); var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); -var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var { gDevTools } = require("devtools/client/framework/devtools"); var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {}); +var promise = require("promise"); var { DebuggerServer } = require("devtools/server/main"); var { DebuggerClient } = require("devtools/shared/client/main"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js index 4df5bc1c394..34828bc629a 100644 --- a/devtools/client/webconsole/hudservice.js +++ b/devtools/client/webconsole/hudservice.js @@ -14,13 +14,13 @@ var {TargetFactory} = require("devtools/client/framework/target"); var {Tools} = require("devtools/client/definitions"); var promise = require("promise"); -loader.lazyGetter(this, "Telemetry", () => require("devtools/client/shared/telemetry")); -loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/client/webconsole/webconsole").WebConsoleFrame); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); +loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry"); +loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true); loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true); loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true); -loader.lazyGetter(this, "showDoorhanger", () => require("devtools/client/shared/doorhanger").showDoorhanger); +loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true); loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source"); const STRINGS_URI = "chrome://devtools/locale/webconsole.properties"; diff --git a/devtools/shared/gcli/commands/calllog.js b/devtools/shared/gcli/commands/calllog.js index 54e11e8fd2b..f3c57aca56e 100644 --- a/devtools/shared/gcli/commands/calllog.js +++ b/devtools/shared/gcli/commands/calllog.js @@ -8,17 +8,10 @@ const { Cc, Ci, Cu } = require("chrome"); const l10n = require("gcli/l10n"); const gcli = require("gcli/index"); const { gDevTools } = require("devtools/client/framework/devtools"); +const Debugger = require("Debugger"); loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true); - -loader.lazyGetter(this, "Debugger", () => { - let global = Cu.getGlobalForObject({}); - let JsDebugger = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); - JsDebugger.addDebuggerToGlobal(global); - return global.Debugger; -}); - var debuggers = []; var chromeDebuggers = []; var sandboxes = []; From 37ff108c43a48014eef1aac68d048bb72dd5a661 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 11 Feb 2016 04:29:47 -0800 Subject: [PATCH 145/187] Bug 1245462 - DevTools is not longer exposed by gDevTools.jsm. r=jryans --- devtools/client/shims/gDevTools.jsm | 1 - 1 file changed, 1 deletion(-) diff --git a/devtools/client/shims/gDevTools.jsm b/devtools/client/shims/gDevTools.jsm index 94df038d663..0bdf45633f6 100644 --- a/devtools/client/shims/gDevTools.jsm +++ b/devtools/client/shims/gDevTools.jsm @@ -24,7 +24,6 @@ if (Services.prefs.getBoolPref(WARNING_PREF)) { this.EXPORTED_SYMBOLS = [ "gDevTools", - "DevTools", "gDevToolsBrowser" ]; From 7768e6cdb796ddbbea5f02e285af1141310822f4 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 11 Feb 2016 04:29:47 -0800 Subject: [PATCH 146/187] Bug 1245462 - Explicitely import gDevTools r=jryans toolbox.js was using Cu.import(gDevTools) without second argument and ended up injecting gDevTools into all next modules being loaded. Hopefully, fonts.js seems to be the only one relying on this! --- devtools/client/inspector/fonts/fonts.js | 1 + devtools/client/performance/performance-controller.js | 1 + 2 files changed, 2 insertions(+) diff --git a/devtools/client/inspector/fonts/fonts.js b/devtools/client/inspector/fonts/fonts.js index c5531dfab98..8284135ebb5 100644 --- a/devtools/client/inspector/fonts/fonts.js +++ b/devtools/client/inspector/fonts/fonts.js @@ -9,6 +9,7 @@ const {Cu} = require("chrome"); const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {}); +const {gDevTools} = require("devtools/client/framework/devtools"); const DEFAULT_PREVIEW_TEXT = "Abc"; const PREVIEW_UPDATE_DELAY = 150; diff --git a/devtools/client/performance/performance-controller.js b/devtools/client/performance/performance-controller.js index 63a027bd78c..e740b8ad02a 100644 --- a/devtools/client/performance/performance-controller.js +++ b/devtools/client/performance/performance-controller.js @@ -9,6 +9,7 @@ Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderMo var { loader, require } = BrowserLoaderModule.BrowserLoader("resource://devtools/client/performance/", this); var { Task } = require("resource://gre/modules/Task.jsm"); var { Heritage, ViewHelpers, WidgetMethods } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); +var {gDevTools} = require("devtools/client/framework/devtools"); // Events emitted by various objects in the panel. var EVENTS = require("devtools/client/performance/events"); From 34211fb7554b8cafb2c05388bfcd93ece25d5ecb Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Thu, 11 Feb 2016 16:09:17 +0100 Subject: [PATCH 147/187] Bug 1243549 - Add missing bits. r=post-facto MozReview-Commit-ID: BTRUMtNsSeQ --- browser/base/content/sanitize.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 9a5594631c6..4d77f87404c 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -694,20 +694,21 @@ Sanitizer.sanitize = function(aParentWindow) Sanitizer.onStartup = Task.async(function*() { // Make sure that we are triggered during shutdown, at the right time, // and only once. - let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"] .getService(Ci.nsPIPlacesDatabase) - .shutdownClient - .jsclient; + let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"] + .getService(Ci.nsPIPlacesDatabase) + .shutdownClient + .jsclient; let deferredSanitization = PromiseUtils.defer(); let sanitizationInProgress = false; let doSanitize = function() { - if (sanitizationInProgress) { - return deferredSanitization.promise; + if (!sanitizationInProgress) { + sanitizationInProgress = true; + Sanitizer.onShutdown().catch(er => {Promise.reject(er) /* Do not return rejected promise */;}).then(() => + deferredSanitization.resolve() + ); } - sanitizationInProgress = true; - Sanitizer.onShutdown().catch(er => {Promise.reject(er) /* Do not return rejected promise */;}).then(() => - deferredSanitization.resolve() - ); + return deferredSanitization.promise; } placesClient.addBlocker("sanitize.js: Sanitize on shutdown", doSanitize); From 705febdb2e61de072da192b4a70259ea751bf865 Mon Sep 17 00:00:00 2001 From: malayaleecoder Date: Wed, 3 Feb 2016 02:02:25 +0530 Subject: [PATCH 148/187] Bug 1243821 - Remove unused Old Sync strings. r=nalexander MozReview-Commit-ID: 48Nm8SxZEbG --- .../base/locales/en-US/sync_strings.dtd | 66 ---------- mobile/android/services/strings.xml.in | 115 ++++-------------- 2 files changed, 22 insertions(+), 159 deletions(-) diff --git a/mobile/android/base/locales/en-US/sync_strings.dtd b/mobile/android/base/locales/en-US/sync_strings.dtd index 079a8d833aa..f8cc009e5cb 100644 --- a/mobile/android/base/locales/en-US/sync_strings.dtd +++ b/mobile/android/base/locales/en-US/sync_strings.dtd @@ -8,72 +8,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/android/services/strings.xml.in b/mobile/android/services/strings.xml.in index bd971d67e4d..d1cd0360466 100644 --- a/mobile/android/services/strings.xml.in +++ b/mobile/android/services/strings.xml.in @@ -2,102 +2,31 @@ - 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/. --> - - &brandShortName; + +&brandShortName; - &sync.app.name.label; - &sync.title.adddevice.label; - &sync.title.pair.label; + +&sync.configure.engines.title.passwords2; +&sync.configure.engines.title.history; +&sync.configure.engines.title.tabs; - - &sync.subtitle.header2.label; - &sync.subtitle.connectlocation2.label; - &sync.pair.connectlocation.label; - &sync.pin.oneline.label; - &sync.link.show.label; - &sync.link.advancedsetup.label; + +&bookmarks.folder.menu.label; +&bookmarks.folder.places.label; +&bookmarks.folder.tags.label; +&bookmarks.folder.toolbar.label; +&bookmarks.folder.unfiled.label; +&bookmarks.folder.desktop.label; +&bookmarks.folder.mobile.label; +&bookmarks.folder.readinglist.label; +&bookmarks.folder.pinned.label; - - - &sync.jpake.subtitle.waiting.label; - - - &sync.subtitle.account.label; - &sync.input.username.label; - &sync.input.password.label; - &sync.input.key.label; - &sync.checkbox.server.label; - &sync.input.server.label; - - - &sync.title.fail.label; - &sync.subtitle.fail.label; - &sync.button.tryagain.label; - &sync.button.manual.label; - &sync.subtitle.nointernet.label; - &sync.subtitle.failaccount.label; - &sync.subtitle.failmultiple.label; - - - &sync.title.success.label; - &sync.subtitle.success.label1; - &sync.settings.label; - &sync.subtitle.manage.label1; - - - &sync.pair.tryagain.label; - - - &sync.settings.options.label; - &sync.summary.pair.label; - - - &sync.configure.engines.title.label; - &sync.configure.engines.sync.my.title.label; - &sync.configure.engines.title.bookmarks; - &sync.configure.engines.title.passwords2; - &sync.configure.engines.title.history; - &sync.configure.engines.title.tabs; - - - &sync.button.cancel.label; - &sync.button.connect.label; - &sync.button.ok.label; - - - &sync.account.label; - - - &bookmarks.folder.menu.label; - &bookmarks.folder.places.label; - &bookmarks.folder.tags.label; - &bookmarks.folder.toolbar.label; - &bookmarks.folder.unfiled.label; - &bookmarks.folder.desktop.label; - &bookmarks.folder.mobile.label; - &bookmarks.folder.readinglist.label; - &bookmarks.folder.pinned.label; - - - &sync.notification.oneaccount.label; - &sync.notification.configure.saved; - - - &sync.invalidcreds.label; - &sync.invalidserver.label; - &sync.verifying.label; - &sync.new.recoverykey.status.incorrect; - - - &new_tab; - &sync.title.send.tab.label; - &sync.button.send.label; - &sync.button.set.up.sync.label; - &sync.title.redirect.to.set.up.sync.label; - &sync.text.redirect.to.set.up.sync.label; - &sync.text.tab.sent.label; - &sync.text.tab.not.sent.label; - &sync.default.client.name; + +&sync.title.redirect.to.set.up.sync.label; +&sync.text.redirect.to.set.up.sync.label; +&sync.text.tab.sent.label; +&sync.text.tab.not.sent.label; +&sync.default.client.name; @ANDROID_PACKAGE_NAME@ @ANDROID_PACKAGE_NAME@.App From 20139b13f876190954d70e3990749bb53fa84bfe Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Thu, 11 Feb 2016 13:29:33 +0100 Subject: [PATCH 149/187] Bug 1245937: re-enable browser_chatwindow.js mochitest. r=Standard8 --- browser/base/content/test/chat/browser.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/base/content/test/chat/browser.ini b/browser/base/content/test/chat/browser.ini index 2eea02f565c..31a280f11ff 100644 --- a/browser/base/content/test/chat/browser.ini +++ b/browser/base/content/test/chat/browser.ini @@ -5,7 +5,6 @@ support-files = chat.html [browser_chatwindow.js] -skip-if = true # Bug 1245937 - Intermittent failures/timeouts. [browser_focus.js] [browser_tearoff.js] skip-if = true # Bug 1245805 - tearing off chat windows causes a browser crash. From e08a75f52326f9cc8d6cdb817f174db393eda929 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Thu, 11 Feb 2016 10:07:20 -0800 Subject: [PATCH 150/187] Bug 1247637 - Remove browser_intent_*; use ActivityUtils to open FxA links. r=sebastian This fixes a crash, since Bug 1242213 removed the .App that browser_intent_class references. I debated just updating the strings, and decided that it was best to remove a pattern that is used only once in our codebase, even though it moves more functionality to code. MozReview-Commit-ID: 4Wgw0oITgue --- .../activities/FxAccountStatusFragment.java | 13 ++++++++++ .../res/xml/fxaccount_status_prefscreen.xml | 24 ++++++++----------- mobile/android/services/strings.xml.in | 3 --- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java index 87505470d48..008fc7ccbe6 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java @@ -213,6 +213,9 @@ public class FxAccountStatusFragment syncNowPreference = ensureFindPreference("sync_now"); syncNowPreference.setEnabled(true); syncNowPreference.setOnPreferenceClickListener(this); + + ensureFindPreference("linktos").setOnPreferenceClickListener(this); + ensureFindPreference("linkprivacy").setOnPreferenceClickListener(this); } /** @@ -294,6 +297,16 @@ public class FxAccountStatusFragment return true; } + if (TextUtils.equals("linktos", preference.getKey())) { + ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), getResources().getString(R.string.fxaccount_link_tos)); + return true; + } + + if (TextUtils.equals("linkprivacy", preference.getKey())) { + ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), getResources().getString(R.string.fxaccount_link_pn)); + return true; + } + return false; } diff --git a/mobile/android/services/src/main/res/xml/fxaccount_status_prefscreen.xml b/mobile/android/services/src/main/res/xml/fxaccount_status_prefscreen.xml index 51138a4c37c..570e362cc2d 100644 --- a/mobile/android/services/src/main/res/xml/fxaccount_status_prefscreen.xml +++ b/mobile/android/services/src/main/res/xml/fxaccount_status_prefscreen.xml @@ -114,20 +114,16 @@ - - - - - - + + diff --git a/mobile/android/services/strings.xml.in b/mobile/android/services/strings.xml.in index d1cd0360466..b84e5e60193 100644 --- a/mobile/android/services/strings.xml.in +++ b/mobile/android/services/strings.xml.in @@ -28,9 +28,6 @@ &sync.text.tab.not.sent.label; &sync.default.client.name; -@ANDROID_PACKAGE_NAME@ -@ANDROID_PACKAGE_NAME@.App - &fxaccount_full_label; From 4c83718b92e58b98130ac6d5cb91456962804bfa Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Wed, 10 Feb 2016 21:06:38 -0800 Subject: [PATCH 151/187] No bug - Include distributionSha256Sum in Gradle wrapper. r=me MozReview-Commit-ID: 4YdR1qwByiz --- gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 34d35660bce..1c9e8aecfc2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,3 +4,4 @@ distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-all.zip +distributionSha256Sum=2ba0aaa11a3e96ec0af31d532d808e1f09cc6dcad0954e637902a1ab544b9e60 From 8b0bb4ed37cded975bdbb17c4f6d32612d96a573 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Wed, 3 Feb 2016 11:18:38 -0800 Subject: [PATCH 152/187] Bug 1119520 - Add opt-in Gradle build mode for mobile/android. r=gps Opt-in by adding --enable-gradle-mobile-android-builds. Gradle dependencies (including the Android-Gradle plugin) are assumed to be present. Local developers will fetch them from the jcentral repository. Android-specific Maven dependencies are shipped as "extras" with the Android SDK, and should be found automatically by the Android-Gradle plugin. MozReview-Commit-ID: 966XgddWgEu --- configure.in | 24 +++++++++++- mobile/android/base/Makefile.in | 46 ++++++++++++++++++++--- mobile/android/mach_commands.py | 10 ----- mobile/android/moz.build | 7 ++-- python/mozbuild/mozbuild/mozconfig.py | 2 +- testing/instrumentation/Makefile.in | 2 + testing/mochitest/Makefile.in | 10 +++-- toolkit/mozapps/installer/upload-files.mk | 26 +++++++++---- 8 files changed, 96 insertions(+), 31 deletions(-) diff --git a/configure.in b/configure.in index aedfd392a0c..4f0967538b7 100644 --- a/configure.in +++ b/configure.in @@ -3758,6 +3758,7 @@ MOZ_SCTP= MOZ_ANDROID_OMX= MOZ_MEDIA_NAVIGATOR= MOZ_OMX_PLUGIN= +MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE= MOZ_VPX_ERROR_CONCEALMENT= MOZ_WEBSPEECH=1 MOZ_WEBSPEECH_MODELS= @@ -5366,8 +5367,26 @@ if test -n "$MOZ_OMX_PLUGIN"; then dnl Only allow building OMX plugin on Gonk (B2G) or Android AC_DEFINE(MOZ_OMX_PLUGIN) else - dnl fail if we're not building on Gonk or Android - AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android]) + dnl fail if we're not building on Gonk or Android + AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android]) + fi +fi + +dnl ======================================================== +dnl = Enable building mobile/android with Gradle +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(gradle-mobile-android-builds, +[ --enable-gradle-mobile-android-builds Enable building mobile/android with Gradle], + MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=1, + MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=) + +if test -n "$MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE"; then + if test "$OS_TARGET" = "Android" -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then + dnl Only allow building mobile/android with Gradle. + AC_DEFINE(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE) + else + dnl fail if we're not building mobile/android. + AC_MSG_ERROR([Can only build mobile/android with Gradle]) fi fi @@ -8907,6 +8926,7 @@ AC_SUBST(MOZ_DIRECTSHOW) AC_SUBST(MOZ_ANDROID_OMX) AC_SUBST(MOZ_APPLEMEDIA) AC_SUBST(MOZ_OMX_PLUGIN) +AC_SUBST(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE) AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT) AC_SUBST(VPX_AS) AC_SUBST_LIST(VPX_ASFLAGS) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 0a455191c09..ee2843a5265 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -2,6 +2,12 @@ # 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/. +# We call mach -> Make -> gradle -> mach, which races to find and +# create .mozconfig files and to generate targets. +ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +.NOTPARALLEL: +endif + MOZ_BUILDID := $(shell cat $(DEPTH)/config/buildid) # Set the appropriate version code, based on the existance of the @@ -201,9 +207,21 @@ endif # MOZ_INSTALL_TRACKING library_jars := $(subst $(NULL) ,:,$(strip $(library_jars))) +gradle_dir := $(topobjdir)/gradle/build/mobile/android + +ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +.gradle.deps: .aapt.deps FORCE + @$(TOUCH) $@ + $(topsrcdir)/mach gradle --no-daemon --offline app:dexAutomationDebug app:assembleAutomationDebugAndroidTest -x lint + +classes.dex: .gradle.deps + $(REPORT_BUILD) + cp $(gradle_dir)/app/intermediates/dex/automation/debug/classes.dex $@ +else classes.dex: .proguard.deps $(REPORT_BUILD) $(DX) --dex --output=classes.dex jars-proguarded +endif ifdef MOZ_DISABLE_PROGUARD PROGUARD_PASSES=0 @@ -501,8 +519,13 @@ endef # .aapt.deps: $(all_resources) $(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./)) -# .aapt.nodeps: $(abspath $(CURDIR)/AndroidManifest.xml) FORCE -$(eval $(call aapt_command,.aapt.nodeps,$(abspath $(CURDIR)/AndroidManifest.xml) FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) +ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +.aapt.nodeps: FORCE + cp $(gradle_dir)/app/intermediates/res/resources-automation-debug.ap_ gecko-nodeps.ap_ +else +# .aapt.nodeps: $(CURDIR)/AndroidManifest.xml FORCE +$(eval $(call aapt_command,.aapt.nodeps,$(CURDIR)/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) +endif # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk @@ -532,7 +555,14 @@ gradle-targets: $(foreach f,$(constants_PP_JAVAFILES),$(f)) gradle-targets: $(abspath AndroidManifest.xml) gradle-targets: $(ANDROID_GENERATED_RESFILES) -gradle-omnijar: $(ABS_DIST)/fennec/$(OMNIJAR_NAME) +ifndef MOZILLA_OFFICIAL +# Local developers update omni.ja during their builds. There's a +# chicken-and-egg problem here. +gradle-omnijar: $(abspath $(DIST)/fennec/$(OMNIJAR_NAME)) +else +# In automation, omni.ja is built only during packaging. +gradle-omnijar: +endif .PHONY: gradle-targets gradle-omnijar @@ -543,8 +573,8 @@ endif # GeneratedJNIWrappers.cpp target also generates # GeneratedJNIWrappers.h and GeneratedJNINatives.h -libs:: classes.dex jni-stubs.inc GeneratedJNIWrappers.cpp $(CURDIR)/fennec_ids.txt - $(INSTALL) classes.dex $(FINAL_TARGET) +ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +libs:: jni-stubs.inc GeneratedJNIWrappers.cpp @(diff jni-stubs.inc $(topsrcdir)/mozglue/android/jni-stubs.inc >/dev/null && \ diff GeneratedJNIWrappers.cpp $(topsrcdir)/widget/android/GeneratedJNIWrappers.cpp >/dev/null && \ diff GeneratedJNIWrappers.h $(topsrcdir)/widget/android/GeneratedJNIWrappers.h >/dev/null && \ @@ -558,3 +588,9 @@ libs:: classes.dex jni-stubs.inc GeneratedJNIWrappers.cpp $(CURDIR)/fennec_ids.t echo '* Repeat the build, and check in any changes. *' && \ echo '*****************************************************' && \ exit 1) +endif + +libs:: $(CURDIR)/fennec_ids.txt + +libs:: classes.dex + $(INSTALL) classes.dex $(FINAL_TARGET) diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py index 329ca25e502..11cafbdcbbb 100644 --- a/mobile/android/mach_commands.py +++ b/mobile/android/mach_commands.py @@ -21,16 +21,6 @@ from mach.decorators import ( Command, ) -SUCCESS = ''' -You should be ready to build with Gradle and import into IntelliJ! Test with - - ./mach gradle build - -and in IntelliJ select File > Import project... and choose - - {topobjdir}/mobile/android/gradle -''' - # NOTE python/mach/mach/commands/commandinfo.py references this function # by name. If this function is renamed or removed, that file should diff --git a/mobile/android/moz.build b/mobile/android/moz.build index 049293ed682..d7d6eb6f2af 100644 --- a/mobile/android/moz.build +++ b/mobile/android/moz.build @@ -31,8 +31,9 @@ if CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']: DIRS += ['../../xulrunner/tools/redit'] -TEST_DIRS += [ - 'tests', -] +if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: + TEST_DIRS += [ + 'tests', + ] SPHINX_TREES['fennec'] = 'docs' diff --git a/python/mozbuild/mozbuild/mozconfig.py b/python/mozbuild/mozbuild/mozconfig.py index 2e829422d8d..a60b79774dd 100644 --- a/python/mozbuild/mozbuild/mozconfig.py +++ b/python/mozbuild/mozbuild/mozconfig.py @@ -107,7 +107,7 @@ class MozconfigLoader(ProcessExecutionMixin): if 'MOZ_MYCONFIG' in env: raise MozconfigFindException(MOZ_MYCONFIG_ERROR) - env_path = env.get('MOZCONFIG', None) + env_path = env.get('MOZCONFIG', None) or None if env_path is not None: if not os.path.isabs(env_path): potential_roots = [self.topsrcdir, os.getcwd()] diff --git a/testing/instrumentation/Makefile.in b/testing/instrumentation/Makefile.in index 6b1c225c87b..0ccb15a2c8f 100644 --- a/testing/instrumentation/Makefile.in +++ b/testing/instrumentation/Makefile.in @@ -14,10 +14,12 @@ include $(topsrcdir)/config/android-common.mk stage-package: $(NSINSTALL) -D $(_DEST_DIR) +ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE $(call RELEASE_SIGN_ANDROID_APK,\ $(DEPTH)/mobile/android/tests/background/junit3/background-junit3-debug-unsigned-unaligned.apk,\ $(_DEST_DIR)/background-junit3.apk) $(call RELEASE_SIGN_ANDROID_APK,\ $(DEPTH)/mobile/android/tests/browser/junit3/browser-junit3-debug-unsigned-unaligned.apk,\ $(_DEST_DIR)/browser-junit3.apk) +endif @(cd $(DEPTH)/_tests/ && tar $(TAR_CREATE_FLAGS) - instrumentation) | (cd $(PKG_STAGE) && tar -xf -) diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index e4eedf120bf..a0448dcd7bb 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -107,11 +107,15 @@ $(_DEST_DIR): ifeq ($(MOZ_BUILD_APP),mobile/android) include $(topsrcdir)/config/android-common.mk +ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +robocop_apk := $(topobjdir)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk +else +robocop_apk := $(topobjdir)/gradle/build/mobile/android/app/outputs/apk/app-automation-debug-androidTest-unaligned.apk +endif + stage-package-android: $(NSINSTALL) -D $(_DEST_DIR) - $(call RELEASE_SIGN_ANDROID_APK,\ - $(DEPTH)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk,\ - $(_DEST_DIR)/robocop.apk) + $(call RELEASE_SIGN_ANDROID_APK,$(robocop_apk),$(_DEST_DIR)/robocop.apk) stage-package: stage-package-android endif diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index 9cfe53c11c3..d4297fa3fd8 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -325,13 +325,18 @@ UPLOAD_EXTRA_FILES += geckoview_library/geckoview_assets.zip # Robocop/Robotium tests, Android Background tests, and Fennec need to # be signed with the same key, which means release signing them all. -ROBOCOP_PATH = $(topobjdir)/mobile/android/tests/browser/robocop +ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +robocop_apk := $(topobjdir)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk +else +robocop_apk := $(topobjdir)/gradle/build/mobile/android/app/outputs/apk/app-automation-debug-androidTest-unaligned.apk +endif + # Normally, $(NSINSTALL) would be used instead of cp, but INNER_ROBOCOP_PACKAGE # is used in a series of commands that run under a "cd something", while # $(NSINSTALL) is relative. INNER_ROBOCOP_PACKAGE= \ cp $(GECKO_APP_AP_PATH)/fennec_ids.txt $(ABS_DIST) && \ - $(call RELEASE_SIGN_ANDROID_APK,$(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk,$(ABS_DIST)/robocop.apk) + $(call RELEASE_SIGN_ANDROID_APK,$(robocop_apk),$(ABS_DIST)/robocop.apk) endif else INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Android Robocop for you' @@ -470,6 +475,15 @@ PKG_SUFFIX = .apk INNER_SZIP_LIBRARIES = \ $(if $(ALREADY_SZIPPED),,$(foreach lib,$(SZIP_LIBRARIES),host/bin/szip $(MOZ_SZIP_FLAGS) $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/$(lib) && )) true +ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE +INNER_CHECK_R_TXT=echo 'No R.txt checking for you!' +else +INNER_CHECK_R_TXT=\ + ((test ! -f $(GECKO_APP_AP_PATH)/R.txt && echo "*** Warning: The R.txt that is being packaged might not agree with the R.txt that was built. This is normal during l10n repacks.") || \ + diff $(GECKO_APP_AP_PATH)/R.txt $(GECKO_APP_AP_PATH)/gecko-nodeps/R.txt >/dev/null || \ + (echo "*** Error: The R.txt that was built and the R.txt that is being packaged are not the same. Rebuild mobile/android/base and re-package." && exit 1)) +endif + # Insert $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex into # $(ABS_DIST)/gecko.ap_, producing $(ABS_DIST)/gecko.apk. INNER_MAKE_APK = \ @@ -490,13 +504,11 @@ INNER_MAKE_APK = \ $(ZIPALIGN) -f -v 4 $(ABS_DIST)/gecko.apk $(PACKAGE) ifeq ($(MOZ_BUILD_APP),mobile/android) -INNER_MAKE_PACKAGE = \ +INNER_MAKE_PACKAGE = \ $(INNER_SZIP_LIBRARIES) && \ make -C $(GECKO_APP_AP_PATH) gecko-nodeps.ap_ && \ cp $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ $(ABS_DIST)/gecko.ap_ && \ - ( (test ! -f $(GECKO_APP_AP_PATH)/R.txt && echo "*** Warning: The R.txt that is being packaged might not agree with the R.txt that was built. This is normal during l10n repacks.") || \ - diff $(GECKO_APP_AP_PATH)/R.txt $(GECKO_APP_AP_PATH)/gecko-nodeps/R.txt >/dev/null || \ - (echo "*** Error: The R.txt that was built and the R.txt that is being packaged are not the same. Rebuild mobile/android/base and re-package." && exit 1)) && \ + $(INNER_CHECK_R_TXT) && \ $(INNER_MAKE_APK) && \ $(INNER_ROBOCOP_PACKAGE) && \ $(INNER_INSTALL_BOUNCER_PACKAGE) && \ @@ -505,7 +517,7 @@ INNER_MAKE_PACKAGE = \ endif ifeq ($(MOZ_BUILD_APP),mobile/android/b2gdroid) -INNER_MAKE_PACKAGE = \ +INNER_MAKE_PACKAGE = \ $(INNER_SZIP_LIBRARIES) && \ cp $(topobjdir)/mobile/android/b2gdroid/app/classes.dex $(ABS_DIST)/classes.dex && \ cp $(topobjdir)/mobile/android/b2gdroid/app/b2gdroid-unsigned-unaligned.apk $(ABS_DIST)/gecko.ap_ && \ From 24c859f6e2c2fa9b94c4efb5b2ade38a97260f56 Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Thu, 11 Feb 2016 18:47:09 -0200 Subject: [PATCH 153/187] Bug 1246245 - Webextensions and themes should also block initial e10s rollout. r=Mossop --- toolkit/mozapps/extensions/internal/XPIProvider.jsm | 10 ++++++++-- .../mozapps/extensions/internal/XPIProviderUtils.js | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 4ea3767e160..cf7cd7a9860 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -4213,8 +4213,9 @@ this.XPIProvider = { * @return true if enabling the add-on should block e10s */ isBlockingE10s: function(aAddon) { - // Only extensions change behaviour - if (aAddon.type != "extension") + if (aAddon.type != "extension" && + aAddon.type != "webextension" && + aAddon.type != "theme") return false; // The hotfix is exempt @@ -4222,6 +4223,11 @@ this.XPIProvider = { if (hotfixID && hotfixID == aAddon.id) return false; + // The default theme is exempt + if (aAddon.type == "theme" && + aAddon.internalName == XPIProvider.defaultSkin) + return false; + // System add-ons are exempt let locName = aAddon._installLocation ? aAddon._installLocation.name : undefined; diff --git a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js index e9ca852ceb7..a08ac34982d 100644 --- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js @@ -1404,8 +1404,10 @@ this.XPIDatabase = { for (let [, addon] of this.addonDB) { let active = (addon.visible && !addon.disabled && !addon.pendingUninstall); - if (active && XPIProvider.isBlockingE10s(addon)) + if (active && XPIProvider.isBlockingE10s(addon)) { blockE10s = true; + break; + } } Preferences.set(PREF_E10S_BLOCKED_BY_ADDONS, blockE10s); }, From b4b98f8444e96ef75826d658a886f631000983a8 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 10 Feb 2016 17:52:53 -0800 Subject: [PATCH 154/187] Bug 1244496: Add more detail to TabOpen/TabClose events for tabs moved between windows. r=Gijs MozReview-Commit-ID: 9YHaVlMW0T6 --- browser/base/content/browser.js | 6 ++++ browser/base/content/tabbrowser.xml | 23 +++++++------- .../content/test/general/browser_bug491431.js | 4 +-- .../browser_tab_drag_drop_perwindow.js | 30 +++++++++++++++++++ .../components/sessionstore/SessionStore.jsm | 5 ++-- .../test/mochitest/browser_dbg_listtabs-02.js | 6 ++-- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b4cafbc3422..bb6059695ed 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1102,6 +1102,12 @@ var gBrowserInit = { // the original tab in the other window. let tabToOpen = uriToLoad; + // If this tab was passed as a window argument, clear the + // reference to it from the arguments array. + if (window.arguments[0] == tabToOpen) { + window.arguments[0] = null; + } + // Stop the about:blank load gBrowser.stop(); // make sure it has a docshell diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index f6f191e87d0..117d5334d29 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1758,6 +1758,7 @@ var aForceNotRemote; var aNoReferrer; var aUserContextId; + var aEventDetail; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1775,6 +1776,7 @@ aForceNotRemote = params.forceNotRemote; aNoReferrer = params.noReferrer; aUserContextId = params.userContextId; + aEventDetail = params.eventDetail; } // if we're adding tabs, we're past interrupt mode, ditch the owner @@ -1893,8 +1895,8 @@ // Dispatch a new tab notification. We do this once we're // entirely done, so that things are in a consistent state // even if the event listener opens or closes tabs. - var evt = document.createEvent("Events"); - evt.initEvent("TabOpen", true, false); + var detail = aEventDetail || {}; + var evt = new CustomEvent("TabOpen", { bubbles: true, detail }); t.dispatchEvent(evt); // If we didn't swap docShells with a preloaded browser @@ -2128,7 +2130,7 @@ var isLastTab = (this.tabs.length - this._removingTabs.length == 1); - if (!this._beginRemoveTab(aTab, false, null, true, skipPermitUnload)) + if (!this._beginRemoveTab(aTab, null, null, true, skipPermitUnload)) return; if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse) @@ -2173,7 +2175,7 @@ - + @@ -2207,7 +2209,7 @@ newTab = true; } - if (!aTab._pendingPermitUnload && !aTabWillBeMoved && !aSkipPermitUnload) { + if (!aTab._pendingPermitUnload && !aAdoptedByTab && !aSkipPermitUnload) { // We need to block while calling permitUnload() because it // processes the event queue and may lead to another removeTab() // call before permitUnload() returns. @@ -2241,11 +2243,10 @@ // Dispatch a notification. // We dispatch it before any teardown so that event listeners can // inspect the tab that's about to close. - var evt = document.createEvent("UIEvent"); - evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0); + var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } }); aTab.dispatchEvent(evt); - if (!aTabWillBeMoved && !gMultiProcessBrowser) { + if (!aAdoptedByTab && !gMultiProcessBrowser) { // Prevent this tab from showing further dialogs, since we're closing it var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIDOMWindowUtils); @@ -2261,7 +2262,7 @@ filter.removeProgressListener(listener); listener.destroy(); - if (browser.registeredOpenURI && !aTabWillBeMoved) { + if (browser.registeredOpenURI && !aAdoptedByTab) { this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI); this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI); delete browser.registeredOpenURI; @@ -2495,7 +2496,7 @@ // First, start teardown of the other browser. Make sure to not // fire the beforeunload event in the process. Close the other // window if this was its last tab. - if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true)) + if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true)) return; let modifiedAttrs = []; @@ -2946,7 +2947,7 @@ // Swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between // windows). - let newTab = this.addTab("about:blank"); + let newTab = this.addTab("about:blank", { eventDetail: { adoptedTab: aTab } }); let newBrowser = this.getBrowserForTab(newTab); let newURL = aTab.linkedBrowser.currentURI.spec; diff --git a/browser/base/content/test/general/browser_bug491431.js b/browser/base/content/test/general/browser_bug491431.js index 9dd6312ae0e..d30e2ad125c 100644 --- a/browser/base/content/test/general/browser_bug491431.js +++ b/browser/base/content/test/general/browser_bug491431.js @@ -13,14 +13,14 @@ function test() { tabA = gBrowser.addTab(testPage); gBrowser.tabContainer.addEventListener("TabClose", function(aEvent) { gBrowser.tabContainer.removeEventListener("TabClose", arguments.callee, true); - ok(!aEvent.detail, "This was a normal tab close"); + ok(!aEvent.detail.adoptedBy, "This was a normal tab close"); // test tab close by moving tabB = gBrowser.addTab(testPage); gBrowser.tabContainer.addEventListener("TabClose", function(aEvent) { gBrowser.tabContainer.removeEventListener("TabClose", arguments.callee, true); executeSoon(function() { - ok(aEvent.detail, "This was a tab closed by moving"); + ok(aEvent.detail.adoptedBy, "This was a tab closed by moving"); // cleanup newWin.close(); diff --git a/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js b/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js index ce7f683081f..dbccc5debd8 100644 --- a/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js +++ b/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js @@ -147,3 +147,33 @@ add_task(function* test_dragging_blacklisted() { yield BrowserTestUtils.closeWindow(remoteWin1); yield BrowserTestUtils.closeWindow(remoteWin2); }); + + +/** + * Tests that tabs dragged between windows dispatch TabOpen and TabClose + * events with the appropriate adoption details. + */ +add_task(function* test_dragging_adoption_events() { + let win1 = yield BrowserTestUtils.openNewBrowserWindow(); + let win2 = yield BrowserTestUtils.openNewBrowserWindow(); + + let tab1 = yield BrowserTestUtils.openNewForegroundTab(win1.gBrowser); + let tab2 = yield BrowserTestUtils.openNewForegroundTab(win2.gBrowser); + + let awaitCloseEvent = BrowserTestUtils.waitForEvent(tab1, "TabClose"); + let awaitOpenEvent = BrowserTestUtils.waitForEvent(win2, "TabOpen"); + + let effect = ChromeUtils.synthesizeDrop(tab1, tab2, + [[{type: TAB_DROP_TYPE, data: tab1}]], + null, win1, win2); + is(effect, "move", "Tab should be moved from win1 to win2."); + + let closeEvent = yield awaitCloseEvent; + let openEvent = yield awaitOpenEvent; + + is(openEvent.detail.adoptedTab, tab1, "New tab adopted old tab"); + is(closeEvent.detail.adoptedBy, openEvent.target, "Old tab adopted by new tab"); + + yield BrowserTestUtils.closeWindow(win1); + yield BrowserTestUtils.closeWindow(win2); +}); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 82b4ebe0a05..2d619afefe8 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -891,8 +891,9 @@ var SessionStoreInternal = { this.onTabAdd(win, target); break; case "TabClose": - // aEvent.detail determines if the tab was closed by moving to a different window - if (!aEvent.detail) + // `adoptedBy` will be set if the tab was closed because it is being + // moved to a new window. + if (!aEvent.detail.adoptedBy) this.onTabClose(win, target); this.onTabRemove(win, target); break; diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js index bfe67bd2b6f..a2c7b022d08 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js @@ -101,7 +101,7 @@ function removeTabA() { let deferred = promise.defer(); once(gBrowser.tabContainer, "TabClose").then(aEvent => { - ok(!aEvent.detail, "This was a normal tab close"); + ok(!aEvent.detail.adoptedBy, "This was a normal tab close"); // Let the actor's TabClose handler finish first. executeSoon(deferred.resolve); @@ -145,7 +145,7 @@ function removeTabC() { let deferred = promise.defer(); once(gBrowser.tabContainer, "TabClose").then(aEvent => { - ok(aEvent.detail, "This was a tab closed by moving"); + ok(aEvent.detail.adoptedBy, "This was a tab closed by moving"); // Let the actor's TabClose handler finish first. executeSoon(deferred.resolve); @@ -203,7 +203,7 @@ function removeTabB() { let deferred = promise.defer(); once(gBrowser.tabContainer, "TabClose").then(aEvent => { - ok(!aEvent.detail, "This was a normal tab close"); + ok(!aEvent.detail.adoptedBy, "This was a normal tab close"); // Let the actor's TabClose handler finish first. executeSoon(deferred.resolve); From 48b2883653fc487c8e95c8121dbfa26142b9b740 Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Wed, 3 Feb 2016 15:17:38 -0800 Subject: [PATCH 155/187] Bug 1239823 - Part 1: Parse min/max date before use r=margaret MozReview-Commit-ID: 4th3CJwwcDg --- .../mozilla/gecko/widget/DateTimePicker.java | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java b/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java index 5db1f5d817c..84ad86cb76d 100644 --- a/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java +++ b/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java @@ -269,6 +269,41 @@ public class DateTimePicker extends FrameLayout { Log.d(LOGTAG, "screen width: " + mScreenWidth + " screen height: " + mScreenHeight); } + // Set the min / max attribute. + try { + if (minDateValue != null && !minDateValue.equals("")) { + mMinDate.setTime(new SimpleDateFormat(dateFormat).parse(minDateValue)); + } else { + mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); + } + } catch (Exception ex) { + Log.e(LOGTAG, "Error parsing format sting: " + ex); + mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); + } + + try { + if (maxDateValue != null && !maxDateValue.equals("")) { + mMaxDate.setTime(new SimpleDateFormat(dateFormat).parse(maxDateValue)); + } else { + mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); + } + } catch (Exception ex) { + Log.e(LOGTAG, "Error parsing format string: " + ex); + mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); + } + + // Find the initial date from the constructor arguments. + try { + if (!dateTimeValue.equals("")) { + mTempDate.setTime(new SimpleDateFormat(dateFormat).parse(dateTimeValue)); + } else { + mTempDate.setTimeInMillis(System.currentTimeMillis()); + } + } catch (Exception ex) { + Log.e(LOGTAG, "Error parsing format string: " + ex); + mTempDate.setTimeInMillis(System.currentTimeMillis()); + } + // If we're displaying a date, the screen is wide enough // (and if we're using an SDK where the calendar view exists) // then display a calendar. @@ -312,41 +347,6 @@ public class DateTimePicker extends FrameLayout { mCalendar = null; } - // Find the initial date from the constructor arguments. - try { - if (!dateTimeValue.equals("")) { - mTempDate.setTime(new SimpleDateFormat(dateFormat).parse(dateTimeValue)); - } else { - mTempDate.setTimeInMillis(System.currentTimeMillis()); - } - } catch (Exception ex) { - Log.e(LOGTAG, "Error parsing format string: " + ex); - mTempDate.setTimeInMillis(System.currentTimeMillis()); - } - - // Set the min / max attribute. - try { - if (minDateValue != null && !minDateValue.equals("")) { - mMinDate.setTime(new SimpleDateFormat(dateFormat).parse(minDateValue)); - } else { - mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); - } - } catch (Exception ex) { - Log.e(LOGTAG, "Error parsing format sting: " + ex); - mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); - } - - try { - if (maxDateValue != null && !maxDateValue.equals("")) { - mMaxDate.setTime(new SimpleDateFormat(dateFormat).parse(maxDateValue)); - } else { - mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); - } - } catch (Exception ex) { - Log.e(LOGTAG, "Error parsing format string: " + ex); - mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); - } - // Initialize all spinners. mDaySpinner = setupSpinner(R.id.day, 1, mTempDate.get(Calendar.DAY_OF_MONTH)); From 381ec4245d6f449f50761baaaa545024df91201d Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Wed, 3 Feb 2016 15:23:09 -0800 Subject: [PATCH 156/187] Bug 1239823 - Part 2: sanitise input dates for DatePicker r=margaret MozReview-Commit-ID: GiXgQwscGPT --- .../org/mozilla/gecko/widget/DateTimePicker.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java b/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java index 84ad86cb76d..2e6bd6cc23f 100644 --- a/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java +++ b/mobile/android/base/java/org/mozilla/gecko/widget/DateTimePicker.java @@ -304,6 +304,22 @@ public class DateTimePicker extends FrameLayout { mTempDate.setTimeInMillis(System.currentTimeMillis()); } + if (mMaxDate.before(mMinDate)) { + // If the input date range is illogical/garbage, we should not restrict the input range (i.e. allow the + // user to select any date). If we try to make any assumptions based on the illogical min/max date we could + // potentially prevent the user from selecting dates that are in the developers intended range, so it's best + // to allow anything. + mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); + mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); + } + + // mTempDate will either be a site-supplied value, or today's date otherwise. CalendarView implementations can + // crash if they're supplied an invalid date (i.e. a date not in the specified range), hence we need to set + // a sensible default date here. + if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) { + mTempDate.setTimeInMillis(mMinDate.getTimeInMillis()); + } + // If we're displaying a date, the screen is wide enough // (and if we're using an SDK where the calendar view exists) // then display a calendar. From 18c72db2b30f29e819bc9f5224cf6c22824e78fb Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Wed, 10 Feb 2016 14:42:46 +0000 Subject: [PATCH 157/187] Bug 408415 - fix favicon disappearance for hash changes during load, r=jaws MozReview-Commit-ID: KCdXMKX5sIH --- browser/base/content/tabbrowser.xml | 14 ++++++------ browser/base/content/test/general/browser.ini | 5 +++-- .../content/test/general/browser_bug408415.js | 21 ++++++++++++++++++ .../content/test/general/browser_bug550565.js | 4 ++-- .../test/general/file_bug550565_popup.html | 12 ---------- ...5_favicon.ico => file_generic_favicon.ico} | Bin .../test/general/file_with_favicon.html | 12 ++++++++++ 7 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 browser/base/content/test/general/browser_bug408415.js delete mode 100644 browser/base/content/test/general/file_bug550565_popup.html rename browser/base/content/test/general/{file_bug550565_favicon.ico => file_generic_favicon.ico} (100%) create mode 100644 browser/base/content/test/general/file_with_favicon.html diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 117d5334d29..35e87d46d0c 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -747,6 +747,8 @@ let topLevel = aWebProgress.isTopLevel; if (topLevel) { + let isSameDocument = + !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT); // If userTypedClear > 0, the document loaded correctly and we should be // clearing the user typed value. We also need to clear the typed value // if the document failed to load, to make sure the urlbar reflects the @@ -758,14 +760,12 @@ if (this.mBrowser.userTypedClear > 0 || ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) && aLocation.spec != "about:blank") || - aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { + isSameDocument) { this.mBrowser.userTypedValue = null; } // If the browser was playing audio, we should remove the playing state. - if (this.mTab.hasAttribute("soundplaying") && - (!this.mBrowser.lastURI || - this.mBrowser.lastURI.spec != aLocation.spec)) { + if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) { this.mTab.removeAttribute("soundplaying"); this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]); } @@ -788,9 +788,9 @@ } // Don't clear the favicon if this onLocationChange was - // triggered by a pushState or a replaceState. See bug 550565. - if (aWebProgress.isLoadingDocument && - !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) { + // triggered by a pushState or a replaceState (bug 550565) or + // a hash change (bug 408415). + if (aWebProgress.isLoadingDocument && !isSameDocument) { this.mBrowser.mIconURL = null; } diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 68529e7173b..07820104649 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -29,8 +29,8 @@ support-files = download_page.html dummy_page.html feed_tab.html - file_bug550565_favicon.ico - file_bug550565_popup.html + file_generic_favicon.ico + file_with_favicon.html file_bug822367_1.html file_bug822367_1.js file_bug822367_2.html @@ -165,6 +165,7 @@ skip-if = e10s # Bug 1236991 - Update or remove tests that use fillInPageTooltip [browser_bug380960.js] [browser_bug386835.js] [browser_bug406216.js] +[browser_bug408415.js] [browser_bug409481.js] [browser_bug409624.js] [browser_bug413915.js] diff --git a/browser/base/content/test/general/browser_bug408415.js b/browser/base/content/test/general/browser_bug408415.js new file mode 100644 index 00000000000..8a34374ac93 --- /dev/null +++ b/browser/base/content/test/general/browser_bug408415.js @@ -0,0 +1,21 @@ +function test() { + waitForExplicitFinish(); + + let testPath = getRootDirectory(gTestPath); + + let tab = gBrowser.addTab(testPath + "file_with_favicon.html"); + + tab.linkedBrowser.addEventListener("DOMContentLoaded", function() { + tab.linkedBrowser.removeEventListener("DOMContentLoaded", arguments.callee, true); + + let expectedIcon = testPath + "file_generic_favicon.ico"; + + is(gBrowser.getIcon(tab), expectedIcon, "Correct icon before hash change."); + tab.linkedBrowser.contentWindow.location.href += "#foo"; + is(gBrowser.getIcon(tab), expectedIcon, "Correct icon after hash change."); + + gBrowser.removeTab(tab); + + finish(); + }, true); +} diff --git a/browser/base/content/test/general/browser_bug550565.js b/browser/base/content/test/general/browser_bug550565.js index 0dfa4ed4a64..84f7b803b2b 100644 --- a/browser/base/content/test/general/browser_bug550565.js +++ b/browser/base/content/test/general/browser_bug550565.js @@ -3,12 +3,12 @@ function test() { let testPath = getRootDirectory(gTestPath); - let tab = gBrowser.addTab(testPath + "file_bug550565_popup.html"); + let tab = gBrowser.addTab(testPath + "file_with_favicon.html"); tab.linkedBrowser.addEventListener("DOMContentLoaded", function() { tab.linkedBrowser.removeEventListener("DOMContentLoaded", arguments.callee, true); - let expectedIcon = testPath + "file_bug550565_favicon.ico"; + let expectedIcon = testPath + "file_generic_favicon.ico"; is(gBrowser.getIcon(tab), expectedIcon, "Correct icon before pushState."); tab.linkedBrowser.contentWindow.history.pushState("page2", "page2", "page2"); diff --git a/browser/base/content/test/general/file_bug550565_popup.html b/browser/base/content/test/general/file_bug550565_popup.html deleted file mode 100644 index b4cddf971be..00000000000 --- a/browser/base/content/test/general/file_bug550565_popup.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Test file for bug 550565. - - - - - - Test file for bug 550565. - - diff --git a/browser/base/content/test/general/file_bug550565_favicon.ico b/browser/base/content/test/general/file_generic_favicon.ico similarity index 100% rename from browser/base/content/test/general/file_bug550565_favicon.ico rename to browser/base/content/test/general/file_generic_favicon.ico diff --git a/browser/base/content/test/general/file_with_favicon.html b/browser/base/content/test/general/file_with_favicon.html new file mode 100644 index 00000000000..0702b4aabaf --- /dev/null +++ b/browser/base/content/test/general/file_with_favicon.html @@ -0,0 +1,12 @@ + + + + Test file for bugs with favicons + + + + + + Test file for bugs with favicons + + From 441c99775bfe224dad7033e4fc51ceb67c77de00 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Thu, 11 Feb 2016 18:14:07 -0500 Subject: [PATCH 158/187] Bug 1241326 - Implement resize handles to change viewport size on drag r=jryans --- .../client/responsive.html/actions/index.js | 3 + .../responsive.html/actions/viewports.js | 18 ++- devtools/client/responsive.html/app.js | 4 +- .../responsive.html/components/browser.js | 11 +- .../responsive.html/components/moz.build | 1 + .../components/resizable-viewport.js | 143 ++++++++++++++++++ .../responsive.html/components/viewport.js | 17 +-- .../responsive.html/components/viewports.js | 4 + .../responsive.html/images/grippers.svg | 6 + .../client/responsive.html/images/moz.build | 1 + devtools/client/responsive.html/index.css | 52 +++++++ .../responsive.html/reducers/viewports.js | 19 ++- .../test/unit/test_resize_viewport.js | 21 +++ .../responsive.html/test/unit/xpcshell.ini | 1 + 14 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 devtools/client/responsive.html/components/resizable-viewport.js create mode 100644 devtools/client/responsive.html/images/grippers.svg create mode 100644 devtools/client/responsive.html/test/unit/test_resize_viewport.js diff --git a/devtools/client/responsive.html/actions/index.js b/devtools/client/responsive.html/actions/index.js index 9e84fa8f887..88dab35d295 100644 --- a/devtools/client/responsive.html/actions/index.js +++ b/devtools/client/responsive.html/actions/index.js @@ -17,6 +17,9 @@ createEnum([ // Add an additional viewport to display the document. "ADD_VIEWPORT", + // Resize the viewport. + "RESIZE_VIEWPORT", + // Rotate the viewport. "ROTATE_VIEWPORT", diff --git a/devtools/client/responsive.html/actions/viewports.js b/devtools/client/responsive.html/actions/viewports.js index 0ded91cb232..6723032aba1 100644 --- a/devtools/client/responsive.html/actions/viewports.js +++ b/devtools/client/responsive.html/actions/viewports.js @@ -4,7 +4,11 @@ "use strict"; -const { ADD_VIEWPORT, ROTATE_VIEWPORT } = require("./index"); +const { + ADD_VIEWPORT, + RESIZE_VIEWPORT, + ROTATE_VIEWPORT +} = require("./index"); module.exports = { @@ -17,6 +21,18 @@ module.exports = { }; }, + /** + * Resize the viewport. + */ + resizeViewport(id, width, height) { + return { + type: RESIZE_VIEWPORT, + id, + width, + height, + }; + }, + /** * Rotate the viewport. */ diff --git a/devtools/client/responsive.html/app.js b/devtools/client/responsive.html/app.js index c189224a46c..a34049901ee 100644 --- a/devtools/client/responsive.html/app.js +++ b/devtools/client/responsive.html/app.js @@ -8,7 +8,7 @@ const { createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { connect } = require("devtools/client/shared/vendor/react-redux"); -const { rotateViewport } = require("./actions/viewports"); +const { resizeViewport, rotateViewport } = require("./actions/viewports"); const Types = require("./types"); const Viewports = createFactory(require("./components/viewports")); @@ -34,6 +34,8 @@ let App = createClass({ location, viewports, onRotateViewport: id => dispatch(rotateViewport(id)), + onResizeViewport: (id, width, height) => + dispatch(resizeViewport(id, width, height)), }); }, diff --git a/devtools/client/responsive.html/components/browser.js b/devtools/client/responsive.html/components/browser.js index f20de52c01a..ff1b4bbdac9 100644 --- a/devtools/client/responsive.html/components/browser.js +++ b/devtools/client/responsive.html/components/browser.js @@ -4,7 +4,7 @@ "use strict"; -const { DOM: dom, createClass } = +const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); const Types = require("../types"); @@ -17,6 +17,7 @@ module.exports = createClass({ location: Types.location.isRequired, width: Types.viewport.width.isRequired, height: Types.viewport.height.isRequired, + isResizing: PropTypes.bool.isRequired, }, render() { @@ -24,11 +25,17 @@ module.exports = createClass({ location, width, height, + isResizing, } = this.props; + let className = "browser"; + if (isResizing) { + className += " resizing"; + } + return dom.iframe( { - className: "browser", + className, src: location, width, height, diff --git a/devtools/client/responsive.html/components/moz.build b/devtools/client/responsive.html/components/moz.build index 426a572e8d8..9a98dbe75c5 100644 --- a/devtools/client/responsive.html/components/moz.build +++ b/devtools/client/responsive.html/components/moz.build @@ -6,6 +6,7 @@ DevToolsModules( 'browser.js', + 'resizable-viewport.js', 'viewport-toolbar.js', 'viewport.js', 'viewports.js', diff --git a/devtools/client/responsive.html/components/resizable-viewport.js b/devtools/client/responsive.html/components/resizable-viewport.js new file mode 100644 index 00000000000..91a6623f174 --- /dev/null +++ b/devtools/client/responsive.html/components/resizable-viewport.js @@ -0,0 +1,143 @@ +/* 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/. */ + +/* global window */ + +"use strict"; + +const { DOM: dom, createClass, createFactory, PropTypes } = + require("devtools/client/shared/vendor/react"); + +const Types = require("../types"); +const Browser = createFactory(require("./browser")); +const ViewportToolbar = createFactory(require("./viewport-toolbar")); + +const VIEWPORT_MIN_WIDTH = 280; +const VIEWPORT_MIN_HEIGHT = 280; + +module.exports = createClass({ + + displayName: "ResizableViewport", + + propTypes: { + location: Types.location.isRequired, + viewport: PropTypes.shape(Types.viewport).isRequired, + onResizeViewport: PropTypes.func.isRequired, + onRotateViewport: PropTypes.func.isRequired, + }, + + getInitialState() { + return { + isResizing: false, + lastClientX: 0, + lastClientY: 0, + ignoreX: false, + ignoreY: false, + }; + }, + + onResizeStart({ target, clientX, clientY }) { + window.addEventListener("mousemove", this.onResizeDrag, true); + window.addEventListener("mouseup", this.onResizeStop, true); + + this.setState({ + isResizing: true, + lastClientX: clientX, + lastClientY: clientY, + ignoreX: target === this.refs.resizeBarY, + ignoreY: target === this.refs.resizeBarX, + }); + }, + + onResizeStop() { + window.removeEventListener("mousemove", this.onResizeDrag, true); + window.removeEventListener("mouseup", this.onResizeStop, true); + + this.setState({ + isResizing: false, + lastClientX: 0, + lastClientY: 0, + ignoreX: false, + ignoreY: false, + }); + }, + + onResizeDrag({ clientX, clientY }) { + if (!this.state.isResizing) { + return; + } + + let { lastClientX, lastClientY, ignoreX, ignoreY } = this.state; + let deltaX = clientX - lastClientX; + let deltaY = clientY - lastClientY; + + if (ignoreX) { + deltaX = 0; + } + if (ignoreY) { + deltaY = 0; + } + + let width = this.props.viewport.width + deltaX; + let height = this.props.viewport.height + deltaY; + + if (width < VIEWPORT_MIN_WIDTH) { + width = VIEWPORT_MIN_WIDTH; + } else { + lastClientX = clientX; + } + + if (height < VIEWPORT_MIN_HEIGHT) { + height = VIEWPORT_MIN_HEIGHT; + } else { + lastClientY = clientY; + } + + // Update the viewport store with the new width and height. + this.props.onResizeViewport(width, height); + + this.setState({ + lastClientX, + lastClientY + }); + }, + + render() { + let { + location, + viewport, + onRotateViewport, + } = this.props; + + return dom.div( + { + className: "resizable-viewport", + }, + ViewportToolbar({ + onRotateViewport, + }), + Browser({ + location, + width: viewport.width, + height: viewport.height, + isResizing: this.state.isResizing + }), + dom.div({ + className: "viewport-resize-handle", + onMouseDown: this.onResizeStart, + }), + dom.div({ + ref: "resizeBarX", + className: "viewport-horizontal-resize-handle", + onMouseDown: this.onResizeStart, + }), + dom.div({ + ref: "resizeBarY", + className: "viewport-vertical-resize-handle", + onMouseDown: this.onResizeStart, + }) + ); + }, + +}); diff --git a/devtools/client/responsive.html/components/viewport.js b/devtools/client/responsive.html/components/viewport.js index 8ed829a7f09..8f1767c40b9 100644 --- a/devtools/client/responsive.html/components/viewport.js +++ b/devtools/client/responsive.html/components/viewport.js @@ -8,8 +8,7 @@ const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const Types = require("../types"); -const Browser = createFactory(require("./browser")); -const ViewportToolbar = createFactory(require("./viewport-toolbar")); +const ResizableViewport = createFactory(require("./resizable-viewport")); module.exports = createClass({ @@ -18,6 +17,7 @@ module.exports = createClass({ propTypes: { location: Types.location.isRequired, viewport: PropTypes.shape(Types.viewport).isRequired, + onResizeViewport: PropTypes.func.isRequired, onRotateViewport: PropTypes.func.isRequired, }, @@ -25,20 +25,19 @@ module.exports = createClass({ let { location, viewport, + onResizeViewport, onRotateViewport, } = this.props; return dom.div( { - className: "viewport" + className: "viewport", }, - ViewportToolbar({ - onRotateViewport, - }), - Browser({ + ResizableViewport({ location, - width: viewport.width, - height: viewport.height, + viewport, + onResizeViewport, + onRotateViewport, }) ); }, diff --git a/devtools/client/responsive.html/components/viewports.js b/devtools/client/responsive.html/components/viewports.js index 229b401fc3b..97d216f2748 100644 --- a/devtools/client/responsive.html/components/viewports.js +++ b/devtools/client/responsive.html/components/viewports.js @@ -17,6 +17,7 @@ module.exports = createClass({ propTypes: { location: Types.location.isRequired, viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired, + onResizeViewport: PropTypes.func.isRequired, onRotateViewport: PropTypes.func.isRequired, }, @@ -24,6 +25,7 @@ module.exports = createClass({ let { location, viewports, + onResizeViewport, onRotateViewport, } = this.props; @@ -36,6 +38,8 @@ module.exports = createClass({ key: viewport.id, location, viewport, + onResizeViewport: (width, height) => + onResizeViewport(viewport.id, width, height), onRotateViewport: () => onRotateViewport(viewport.id), }); }) diff --git a/devtools/client/responsive.html/images/grippers.svg b/devtools/client/responsive.html/images/grippers.svg new file mode 100644 index 00000000000..91db83af9d2 --- /dev/null +++ b/devtools/client/responsive.html/images/grippers.svg @@ -0,0 +1,6 @@ + + + + diff --git a/devtools/client/responsive.html/images/moz.build b/devtools/client/responsive.html/images/moz.build index c97d4941f60..e7bf743777c 100644 --- a/devtools/client/responsive.html/images/moz.build +++ b/devtools/client/responsive.html/images/moz.build @@ -5,5 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'grippers.svg', 'rotate-viewport.svg', ) diff --git a/devtools/client/responsive.html/index.css b/devtools/client/responsive.html/index.css index a7df3e3b6bc..1f7aa4841e4 100644 --- a/devtools/client/responsive.html/index.css +++ b/devtools/client/responsive.html/index.css @@ -13,6 +13,10 @@ --viewport-box-shadow: 0 4px 4px 0 rgba(105, 105, 105, 0.26); } +* { + box-sizing: border-box; +} + html, body { margin: 0; height: 100%; @@ -57,6 +61,10 @@ body { box-shadow: var(--viewport-box-shadow); } +.resizable-viewport { + position: relative; +} + /** * Viewport Toolbar */ @@ -96,7 +104,51 @@ body { mask-image: url("./images/rotate-viewport.svg"); } +/** + * Viewport Browser + */ + .browser { display: block; border: 0; } + +.browser.resizing { + pointer-events: none; +} + +/** + * Viewport Resize Handles + */ + +.viewport-resize-handle { + position: absolute; + width: 16px; + height: 16px; + bottom: 0; + right: 0; + background-image: url("./images/grippers.svg"); + background-position: bottom right; + padding: 0 1px 1px 0; + background-repeat: no-repeat; + background-origin: content-box; + cursor: se-resize; +} + +.viewport-horizontal-resize-handle { + position: absolute; + width: 5px; + height: calc(100% - 16px); + right: -4px; + top: 0; + cursor: e-resize; +} + +.viewport-vertical-resize-handle { + position: absolute; + width: calc(100% - 16px); + height: 5px; + left: 0; + bottom: -4px; + cursor: s-resize; +} diff --git a/devtools/client/responsive.html/reducers/viewports.js b/devtools/client/responsive.html/reducers/viewports.js index 177e3861722..14674cd18dc 100644 --- a/devtools/client/responsive.html/reducers/viewports.js +++ b/devtools/client/responsive.html/reducers/viewports.js @@ -4,7 +4,11 @@ "use strict"; -const { ADD_VIEWPORT, ROTATE_VIEWPORT } = require("../actions/index"); +const { + ADD_VIEWPORT, + RESIZE_VIEWPORT, + ROTATE_VIEWPORT, +} = require("../actions/index"); let nextViewportId = 0; @@ -25,6 +29,19 @@ let reducers = { return [...viewports, Object.assign({}, INITIAL_VIEWPORT)]; }, + [RESIZE_VIEWPORT](viewports, { id, width, height }) { + return viewports.map(viewport => { + if (viewport.id !== id) { + return viewport; + } + + return Object.assign({}, viewport, { + width, + height, + }); + }); + }, + [ROTATE_VIEWPORT](viewports, { id }) { return viewports.map(viewport => { if (viewport.id !== id) { diff --git a/devtools/client/responsive.html/test/unit/test_resize_viewport.js b/devtools/client/responsive.html/test/unit/test_resize_viewport.js new file mode 100644 index 00000000000..0d3cf780946 --- /dev/null +++ b/devtools/client/responsive.html/test/unit/test_resize_viewport.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test resizing the viewport. + +const { addViewport, resizeViewport } = + require("devtools/client/responsive.html/actions/viewports"); + +add_task(function*() { + let store = Store(); + const { getState, dispatch } = store; + + dispatch(addViewport()); + dispatch(resizeViewport(0, 500, 500)); + + let viewport = getState().viewports[0]; + equal(viewport.width, 500, "Resized width of 500"); + equal(viewport.height, 500, "Resized height of 500"); +}); diff --git a/devtools/client/responsive.html/test/unit/xpcshell.ini b/devtools/client/responsive.html/test/unit/xpcshell.ini index 95d978f5358..3b9e704e333 100644 --- a/devtools/client/responsive.html/test/unit/xpcshell.ini +++ b/devtools/client/responsive.html/test/unit/xpcshell.ini @@ -6,4 +6,5 @@ firefox-appdir = browser [test_add_viewport.js] [test_change_location.js] +[test_resize_viewport.js] [test_rotate_viewport.js] From ca9e89cbf520ab1c5f7dadd856f3f0ca0b144fc6 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Fri, 12 Feb 2016 00:32:32 +0100 Subject: [PATCH 159/187] Bug 1224660 - Followup: use 2 space indentaion for diff.svg. r=me --- devtools/client/themes/images/diff.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devtools/client/themes/images/diff.svg b/devtools/client/themes/images/diff.svg index 887f77b1770..fabe7006237 100644 --- a/devtools/client/themes/images/diff.svg +++ b/devtools/client/themes/images/diff.svg @@ -2,6 +2,6 @@ - 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/. --> - - + + From 6dc02709665295cee6fd3f64b926476bb7dd99a8 Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Mon, 8 Feb 2016 16:30:01 +0100 Subject: [PATCH 160/187] Bug 1243131 - memory tool: select snapshot using ACCEL+{UP/DOWN};r=fitzgen Adds a keydown listener on the memory panel window. Select previous/next snapshot when user presses UP/DOWN with the accelKey modifier (metaKey on OSX, ctrlKey on windows). Keydown events with modifiers are no longer listened to by the tree node elements. Updated tree node test. Added new mochitest to test the new keyboard navigation on the census view. ) --- devtools/client/memory/app.js | 33 +++++++ .../client/memory/test/browser/browser.ini | 1 + .../browser_memory_keyboard-snapshot-list.js | 91 +++++++++++++++++++ devtools/client/memory/test/browser/head.js | 23 +++++ .../test/mochitest/test_tree_06.html | 29 ++++++ devtools/client/shared/components/tree.js | 5 + 6 files changed, 182 insertions(+) create mode 100644 devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js diff --git a/devtools/client/memory/app.js b/devtools/client/memory/app.js index 0825cb9a024..35eb21834b8 100644 --- a/devtools/client/memory/app.js +++ b/devtools/client/memory/app.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const { assert } = require("devtools/shared/DevToolsUtils"); +const { appinfo } = require("Services"); const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const { breakdowns, diffingState, viewState } = require("./constants"); @@ -53,6 +54,17 @@ const MemoryApp = createClass({ return {}; }, + componentDidMount() { + // Attach the keydown listener directly to the window. When an element that + // has the focus (such as a tree node) is removed from the DOM, the focus + // falls back to the body. + window.addEventListener("keydown", this.onKeyDown); + }, + + componentWillUnmount() { + window.removeEventListener("keydown", this.onKeyDown); + }, + childContextTypes: { front: PropTypes.any, heapWorker: PropTypes.any, @@ -67,6 +79,27 @@ const MemoryApp = createClass({ }; }, + onKeyDown(e) { + let { snapshots, dispatch, heapWorker } = this.props; + const selectedSnapshot = snapshots.find(s => s.selected); + const selectedIndex = snapshots.indexOf(selectedSnapshot); + + let isOSX = appinfo.OS == "Darwin"; + let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey); + // On ACCEL+UP, select previous snapshot. + if (isAccelKey && e.key === "ArrowUp") { + let previousIndex = Math.max(0, selectedIndex - 1); + let previousSnapshotId = snapshots[previousIndex].id; + dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId)); + } + // On ACCEL+DOWN, select next snapshot. + if (isAccelKey && e.key === "ArrowDown") { + let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1); + let nextSnapshotId = snapshots[nextIndex].id; + dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId)); + } + }, + render() { let { dispatch, diff --git a/devtools/client/memory/test/browser/browser.ini b/devtools/client/memory/test/browser/browser.ini index 29fee425dc4..769c61b3ca9 100644 --- a/devtools/client/memory/test/browser/browser.ini +++ b/devtools/client/memory/test/browser/browser.ini @@ -15,6 +15,7 @@ support-files = [browser_memory_dominator_trees_02.js] [browser_memory_filter_01.js] [browser_memory_keyboard.js] +[browser_memory_keyboard-snapshot-list.js] [browser_memory_no_allocation_stacks.js] [browser_memory_no_auto_expand.js] skip-if = debug # bug 1219554 diff --git a/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js b/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js new file mode 100644 index 00000000000..20c08b9a304 --- /dev/null +++ b/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that using ACCEL+UP/DOWN, the user can navigate between snapshots. + +"use strict"; + +const { + snapshotState +} = require("devtools/client/memory/constants"); +const { + takeSnapshotAndCensus +} = require("devtools/client/memory/actions/snapshot"); + +const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html"; + +this.test = makeMemoryTest(TEST_URL, function* ({ panel }) { + const heapWorker = panel.panelWin.gHeapAnalysesClient; + const front = panel.panelWin.gFront; + const store = panel.panelWin.gStore; + const { dispatch } = store; + const doc = panel.panelWin.document; + + info("Take 3 snapshots"); + dispatch(takeSnapshotAndCensus(front, heapWorker)); + dispatch(takeSnapshotAndCensus(front, heapWorker)); + dispatch(takeSnapshotAndCensus(front, heapWorker)); + + yield waitUntilState(store, state => + state.snapshots.length == 3 && + state.snapshots.every(s => s.state === snapshotState.SAVED_CENSUS)); + ok(true, "All snapshots are in SAVED_CENSUS state"); + + yield waitUntilSnapshotSelected(store, 2); + ok(true, "Third snapshot selected after creating all snapshots."); + + info("Press ACCEL+UP key, expect second snapshot selected."); + EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); + yield waitUntilSnapshotSelected(store, 1); + ok(true, "Second snapshot selected after alt+UP."); + + info("Press ACCEL+UP key, expect first snapshot selected."); + EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); + yield waitUntilSnapshotSelected(store, 0); + ok(true, "First snapshot is selected after ACCEL+UP"); + + info("Check ACCEL+UP is a noop when the first snapshot is selected."); + EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); + // We assume the snapshot selection should be synchronous here. + is(getSelectedSnapshotIndex(store), 0, "First snapshot is still selected"); + + info("Press ACCEL+DOWN key, expect second snapshot selected."); + EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); + yield waitUntilSnapshotSelected(store, 1); + ok(true, "Second snapshot is selected after ACCEL+DOWN"); + + info("Click on first node."); + let firstNode = doc.querySelector(".tree .heap-tree-item-name"); + EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin); + yield waitUntilState(store, state => state.snapshots[1].census.focused === + state.snapshots[1].census.report.children[0] + ); + ok(true, "First root is selected after click."); + + info("Press DOWN key, expect second root focused."); + EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin); + yield waitUntilState(store, state => state.snapshots[1].census.focused === + state.snapshots[1].census.report.children[1] + ); + ok(true, "Second root is selected after pressing DOWN."); + is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected"); + + info("Press UP key, expect second root focused."); + EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin); + yield waitUntilState(store, state => state.snapshots[1].census.focused === + state.snapshots[1].census.report.children[0] + ); + ok(true, "First root is selected after pressing UP."); + is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected"); + + info("Press ACCEL+DOWN key, expect third snapshot selected."); + EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); + yield waitUntilSnapshotSelected(store, 2); + ok(true, "Thirdˆ snapshot is selected after ACCEL+DOWN"); + + info("Check ACCEL+DOWN is a noop when the last snapshot is selected."); + EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); + // We assume the snapshot selection should be synchronous here. + is(getSelectedSnapshotIndex(store), 2, "Third snapshot is still selected"); + +}); diff --git a/devtools/client/memory/test/browser/head.js b/devtools/client/memory/test/browser/head.js index a6f51e8381b..a9d7f445e68 100644 --- a/devtools/client/memory/test/browser/head.js +++ b/devtools/client/memory/test/browser/head.js @@ -142,3 +142,26 @@ function getDisplayedSnapshotStatus(document) { const status = document.querySelector(".snapshot-status"); return status ? status.textContent.trim() : null; } + +/** + * Get the index of the currently selected snapshot. + * + * @return {Number} + */ +function getSelectedSnapshotIndex(store) { + let snapshots = store.getState().snapshots; + let selectedSnapshot = snapshots.find(s => s.selected); + return snapshots.indexOf(selectedSnapshot); +} + +/** + * Returns a promise that will resolve when the snapshot with provided index + * becomes selected. + * + * @return {Promise} + */ +function waitUntilSnapshotSelected(store, snapshotIndex) { + return waitUntilState(store, state => + state.snapshots[snapshotIndex] && + state.snapshots[snapshotIndex].selected === true); +} diff --git a/devtools/client/shared/components/test/mochitest/test_tree_06.html b/devtools/client/shared/components/test/mochitest/test_tree_06.html index 5e4259358fb..8b673e74d99 100644 --- a/devtools/client/shared/components/test/mochitest/test_tree_06.html +++ b/devtools/client/shared/components/test/mochitest/test_tree_06.html @@ -276,6 +276,35 @@ window.onload = Task.async(function* () { "-N:false", "--O:false", ], "After the RIGHT, K should be focused."); + + // Check that keys are ignored if any modifier is present. + let keysWithModifier = [ + { key: "ArrowDown", altKey: true }, + { key: "ArrowDown", ctrlKey: true }, + { key: "ArrowDown", metaKey: true }, + { key: "ArrowDown", shiftKey: true }, + ]; + for (let key of keysWithModifier) { + Simulate.keyDown(document.querySelector(".tree"), key); + yield forceRender(tree); + isRenderedTree(document.body.textContent, [ + "A:false", + "-B:false", + "--E:false", + "---K:true", + "---L:false", + "--F:false", + "--G:false", + "-C:false", + "--H:false", + "--I:false", + "-D:false", + "--J:false", + "M:false", + "-N:false", + "--O:false", + ], "After DOWN + (alt|ctrl|meta|shift), K should remain focused."); + } } catch(e) { ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); } finally { diff --git a/devtools/client/shared/components/tree.js b/devtools/client/shared/components/tree.js index bf441ad3d51..bb6cfb38912 100644 --- a/devtools/client/shared/components/tree.js +++ b/devtools/client/shared/components/tree.js @@ -405,6 +405,11 @@ const Tree = module.exports = createClass({ return; } + // Allow parent nodes to use navigation arrows with modifiers. + if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { + return; + } + // Prevent scrolling when pressing navigation keys. Guard against mocked // events received when testing. if (e.nativeEvent && e.nativeEvent.preventDefault) { From 60b21f0b72a264aad6f007bf0c865a9b737886fc Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Mon, 8 Feb 2016 10:55:44 -0800 Subject: [PATCH 161/187] Bug 1246704 - Either create URLMetadataTable, or upgrade it, don't do both r=liuche MozReview-Commit-ID: HyYPbIwqMGc --- .../android/base/java/org/mozilla/gecko/db/URLMetadataTable.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java b/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java index 664005f8a72..4ba45f677d7 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java @@ -57,6 +57,7 @@ public class URLMetadataTable extends BaseTable { // This table was added in v21 of the db. Force its creation if we're coming from an earlier version if (newVersion >= 21 && oldVersion < 21) { onCreate(db); + return; } // Removed the redundant metadata_url_idx index in version 26 From 3b4b60df63d4888e298b090a26197b0ae87baaf7 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 12 Feb 2016 02:13:57 +0100 Subject: [PATCH 162/187] Bug 1216001 part 1 - Optimize nsRange::IsNodeSelected. r=bz --- dom/base/nsRange.cpp | 69 +++++++++++++++++++++++++++++++++++++------- dom/base/nsRange.h | 16 ++++++++++ mfbt/BinarySearch.h | 2 +- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index e11fbf1a72e..c1f5ff472a4 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -151,6 +151,34 @@ GetNextRangeCommonAncestor(nsINode* aNode) return aNode; } +/** + * A Comparator suitable for mozilla::BinarySearchIf for searching a collection + * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset). + */ +struct IsItemInRangeComparator +{ + nsINode* mNode; + uint32_t mStartOffset; + uint32_t mEndOffset; + + int operator()(const nsRange* const aRange) const + { + int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset, + aRange->GetStartParent(), + aRange->StartOffset()); + if (cmp == 1) { + cmp = nsContentUtils::ComparePoints(mNode, mStartOffset, + aRange->GetEndParent(), + aRange->EndOffset()); + if (cmp == -1) { + return 0; + } + return 1; + } + return -1; + } +}; + /* static */ bool nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, uint32_t aEndOffset) @@ -160,24 +188,45 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, nsINode* n = GetNextRangeCommonAncestor(aNode); NS_ASSERTION(n || !aNode->IsSelectionDescendant(), "orphan selection descendant"); + + // Collect the potential ranges and their selection objects. + RangeHashTable ancestorSelectionRanges; + nsTHashtable> ancestorSelections; + uint32_t maxRangeCount = 0; for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { RangeHashTable* ranges = static_cast(n->GetProperty(nsGkAtoms::range)); for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) { nsRange* range = iter.Get()->GetKey(); if (range->IsInSelection() && !range->Collapsed()) { - int32_t cmp = nsContentUtils::ComparePoints(aNode, aEndOffset, - range->GetStartParent(), - range->StartOffset()); - if (cmp == 1) { - cmp = nsContentUtils::ComparePoints(aNode, aStartOffset, - range->GetEndParent(), - range->EndOffset()); - if (cmp == -1) { - return true; - } + ancestorSelectionRanges.PutEntry(range); + Selection* selection = range->mSelection; + ancestorSelections.PutEntry(selection); + maxRangeCount = std::max(maxRangeCount, selection->RangeCount()); + } + } + } + + if (!ancestorSelectionRanges.IsEmpty()) { + nsTArray sortedRanges(maxRangeCount); + for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) { + Selection* selection = iter.Get()->GetKey(); + // Sort the found ranges for |selection| in document order + // (Selection::GetRangeAt returns its ranges ordered). + for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) { + nsRange* range = selection->GetRangeAt(i); + if (ancestorSelectionRanges.Contains(range)) { + sortedRanges.AppendElement(range); } } + MOZ_ASSERT(!sortedRanges.IsEmpty()); + // Binary search the now sorted ranges. + IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset }; + size_t unused; + if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) { + return true; + } + sortedRanges.ClearAndRetainStorage(); } } return false; diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 772f4f9c462..d6c60c62d73 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -252,6 +252,14 @@ public: bool *outNodeBefore, bool *outNodeAfter); + /** + * Return true if any part of (aNode, aStartOffset) .. (aNode, aEndOffset) + * overlaps any nsRange in aNode's GetNextRangeCommonAncestor ranges (i.e. + * where aNode is a descendant of a range's common ancestor node). + * If a nsRange starts in (aNode, aEndOffset) or if it ends in + * (aNode, aStartOffset) then it is non-overlapping and the result is false + * for that nsRange. Collapsed ranges always counts as non-overlapping. + */ static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, uint32_t aEndOffset); @@ -298,6 +306,14 @@ protected: */ nsINode* GetRegisteredCommonAncestor(); + // Helper to IsNodeSelected. + static bool IsNodeInSortedRanges(nsINode* aNode, + uint32_t aStartOffset, + uint32_t aEndOffset, + const nsTArray& aRanges, + size_t aRangeStart, + size_t aRangeEnd); + struct MOZ_STACK_CLASS AutoInvalidateSelection { explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) diff --git a/mfbt/BinarySearch.h b/mfbt/BinarySearch.h index e4cebdf3a76..c06ec48a55b 100644 --- a/mfbt/BinarySearch.h +++ b/mfbt/BinarySearch.h @@ -47,7 +47,7 @@ namespace mozilla { * struct Comparator { * int operator()(int val) const { * if (mTarget < val) return -1; - * if (mValue > val) return 1; + * if (mTarget > val) return 1; * return 0; * } * Comparator(int target) : mTarget(target) {} From e3d22d458b3778eccff22b58c0865b914f16f9e3 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 12 Feb 2016 02:13:57 +0100 Subject: [PATCH 163/187] Bug 1216001 part 2 - Optimize nsRange::ExcludeNonSelectableNodes by counting ignorable whitespace text nodes next to an unselectable node as unselectable too. r=bz --- dom/base/nsRange.cpp | 27 ++++++++++++++++++----- dom/base/test/test_user_select.html | 34 ++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index c1f5ff472a4..c499ad76a85 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -3154,6 +3154,12 @@ nsRange::Constructor(const GlobalObject& aGlobal, return window->GetDoc()->CreateRange(aRv); } +static bool ExcludeIfNextToNonSelectable(nsIContent* aContent) +{ + return aContent->IsNodeOfType(nsINode::eTEXT) && + aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE); +} + void nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) { @@ -3172,6 +3178,10 @@ nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) bool added = false; bool seenSelectable = false; + // |firstNonSelectableContent| is the first node in a consecutive sequence + // of non-IsSelectable nodes. When we find a selectable node after such + // a sequence we'll end the last nsRange, create a new one and restart + // the outer loop. nsIContent* firstNonSelectableContent = nullptr; while (true) { ErrorResult err; @@ -3181,12 +3191,19 @@ nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) nsIContent* content = node && node->IsContent() ? node->AsContent() : nullptr; if (content) { - nsIFrame* frame = content->GetPrimaryFrame(); - for (nsIContent* p = content; !frame && (p = p->GetParent()); ) { - frame = p->GetPrimaryFrame(); + if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) { + // Ignorable whitespace next to a sequence of non-selectable nodes + // counts as non-selectable (bug 1216001). + selectable = false; } - if (frame) { - frame->IsSelectable(&selectable, nullptr); + if (selectable) { + nsIFrame* frame = content->GetPrimaryFrame(); + for (nsIContent* p = content; !frame && (p = p->GetParent()); ) { + frame = p->GetPrimaryFrame(); + } + if (frame) { + frame->IsSelectable(&selectable, nullptr); + } } } diff --git a/dom/base/test/test_user_select.html b/dom/base/test/test_user_select.html index 274e523a5cf..6cac439d065 100644 --- a/dom/base/test/test_user_select.html +++ b/dom/base/test/test_user_select.html @@ -12,7 +12,7 @@ src: url("Ahem.ttf"); } body { font-family: Ahem; font-size: 20px; } -s { -moz-user-select: none; } +s, .non-selectable { -moz-user-select: none; } n { display: none; } a { position:absolute; bottom: 0; right:0; } .text { -moz-user-select: text; } @@ -34,6 +34,16 @@ a { position:absolute; bottom: 0; right:0; }
aaaaaaabbbbbbbbccccccc
aaaaaaabbbbbbbbccccccc
aaaaaaabbbbddccccdddddddeeee
+
aaaa +
x
+
x
+
x
+bbbb
+
aaaa +
x
+
x
+
x
+bbbb
@@ -100,9 +110,11 @@ function test() is(NL(r.toString()), text, e.id + ": range["+index+"].toString()") } - function node(e, index) + function node(e, arg) { - return index == -1 ? e : e.childNodes[index]; + if (typeof arg == "number") + return arg == -1 ? e : e.childNodes[arg]; + return arg; } function checkRangeCount(n, e) @@ -258,6 +270,22 @@ function test() checkRanges([[0,1,-1,1]], e); doneTest(e); + clear(); + e = document.getElementById('testF'); + synthesizeMouse(e, 1, 1, {}); + synthesizeMouse(e, 400, 100, { shiftKey: true }); + checkText("aaaa bbbb", e); + checkRanges([[0,0,-1,1],[6,0,6,5]], e); + doneTest(e); + + clear(); + e = document.getElementById('testG'); + synthesizeMouse(e, 1, 1, {}); + synthesizeMouse(e, 400, 180, { shiftKey: true }); + checkText("aaaa bbbb", e); // XXX this doesn't seem right - bug 1247799 + checkRanges([[0,0,-1,1],[2,0,-1,3],[4,0,-1,5],[6,0,6,5]], e); + doneTest(e); + // ====================================================== // ==================== Script tests ==================== // ====================================================== From 9557a83bc480866c794e01e42abc92577fbbb98d Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 12 Feb 2016 02:13:57 +0100 Subject: [PATCH 164/187] Bug 1216001 part 3 - Cache the result of IsSelected() for the duration of painting. r=bz --- layout/base/nsDisplayList.h | 3 +++ layout/generic/nsTextFrame.cpp | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index d62b74ca14c..ce662e3c898 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -27,6 +27,7 @@ #include "DisplayListClipState.h" #include "LayerState.h" #include "FrameMetrics.h" +#include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "mozilla/gfx/UserData.h" @@ -4375,6 +4376,8 @@ public: // regardless of bidi directionality; top and bottom in vertical modes). nscoord mVisIStartEdge; nscoord mVisIEndEdge; + // Cached result of mFrame->IsSelected(). Only initialized when needed. + mutable mozilla::Maybe mIsFrameSelected; }; /** diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index a49fc5c4b4b..c09338014e8 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -4657,10 +4657,12 @@ nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) class nsDisplayText : public nsCharClipDisplayItem { public: - nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) : + nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame, + Maybe aIsSelected) : nsCharClipDisplayItem(aBuilder, aFrame), mOpacity(1.0f), mDisableSubpixelAA(false) { + mIsFrameSelected = aIsSelected; MOZ_COUNT_CTOR(nsDisplayText); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -4834,18 +4836,22 @@ nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame"); + Maybe isSelected; if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) || (NS_GET_A(StyleColor()->mColor) == 0 && !StyleText()->HasTextShadow())) && - aBuilder->IsForPainting() && !IsSVGText() && !IsSelected()) { - TextDecorations textDecs; - GetTextDecorations(PresContext(), eResolvedColors, textDecs); - if (!textDecs.HasDecorationLines()) { - return; + aBuilder->IsForPainting() && !IsSVGText()) { + isSelected.emplace(IsSelected()); + if (!isSelected) { + TextDecorations textDecs; + GetTextDecorations(PresContext(), eResolvedColors, textDecs); + if (!textDecs.HasDecorationLines()) { + return; + } } } aLists.Content()->AppendNewToTop( - new (aBuilder) nsDisplayText(aBuilder, this)); + new (aBuilder) nsDisplayText(aBuilder, this, isSelected)); } static nsIFrame* @@ -6490,9 +6496,12 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, return; PropertyProvider provider(this, iter, nsTextFrame::eInflated); + if (aItem.mIsFrameSelected.isNothing()) { + aItem.mIsFrameSelected.emplace(IsSelected()); + } // Trim trailing whitespace, unless we're painting a selection highlight, // which should include trailing spaces if present (bug 1146754). - provider.InitializeForDisplay(!IsSelected()); + provider.InitializeForDisplay(!aItem.mIsFrameSelected.value()); gfxContext* ctx = aRenderingContext->ThebesContext(); const bool reversed = mTextRun->IsInlineReversed(); @@ -6536,7 +6545,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); // Fork off to the (slower) paint-with-selection path if necessary. - if (IsSelected()) { + if (aItem.mIsFrameSelected.value()) { MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!"); gfxSkipCharsIterator tmp(provider.GetStart()); int32_t contentOffset = tmp.ConvertSkippedToOriginal(startOffset); From 77c295a758bbfe6562e26de9947b4d7ff2012870 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Wed, 10 Feb 2016 18:14:17 +1100 Subject: [PATCH 165/187] Bug 1245463: [MSE] P5. Remove dead code. r=gerald MozReview-Commit-ID: Elnm0WPuqHC --- dom/media/mediasource/TrackBuffersManager.cpp | 36 +++---------------- dom/media/mediasource/TrackBuffersManager.h | 4 +-- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 9e728045cc8..d389cfd50bb 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -172,20 +172,12 @@ TrackBuffersManager::ResetParserState() MSE_DEBUG(""); // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed. - if (mAppendState == AppendState::PARSING_MEDIA_SEGMENT) { - nsCOMPtr task = - NS_NewRunnableMethod(this, &TrackBuffersManager::FinishCodedFrameProcessing); - GetTaskQueue()->Dispatch(task.forget()); - } else { - nsCOMPtr task = - NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState); - GetTaskQueue()->Dispatch(task.forget()); - } + // SourceBuffer.abort() has ensured that all complete coded frames have been + // processed. As such, we don't need to check for the value of mAppendState. + nsCOMPtr task = + NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState); + GetTaskQueue()->Dispatch(task.forget()); - // Our ResetParserState is really asynchronous, the current task has been - // interrupted and will complete shortly (or has already completed). - // We must however present to the main thread a stable, reset state. - // So we run the following operation now in the main thread. // 7. Set append state to WAITING_FOR_SEGMENT. SetAppendState(AppendState::WAITING_FOR_SEGMENT); } @@ -303,24 +295,6 @@ TrackBuffersManager::Detach() MSE_DEBUG(""); } -void -TrackBuffersManager::FinishCodedFrameProcessing() -{ - MOZ_ASSERT(OnTaskQueue()); - - if (mProcessingRequest.Exists()) { - NS_WARNING("Processing request pending"); - mProcessingRequest.Disconnect(); - } - // The spec requires us to complete parsing synchronously any outstanding - // frames in the current media segment. This can't be implemented in a way - // that makes sense. - // As such we simply completely ignore the result of any pending input buffer. - // TODO: Link to W3C bug. - - CompleteResetParserState(); -} - void TrackBuffersManager::CompleteResetParserState() { diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index 8f66b3d79fc..f51937face8 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -119,9 +119,7 @@ private: // media segment have been processed. RefPtr CodedFrameProcessing(); void CompleteCodedFrameProcessing(); - // Called by ResetParserState. Complete parsing the input buffer for the - // current media segment. - void FinishCodedFrameProcessing(); + // Called by ResetParserState. void CompleteResetParserState(); RefPtr CodedFrameRemovalWithPromise(media::TimeInterval aInterval); From bf6ac0d2db8e005f09c81f609e181cc4cae4ddc5 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 12 Feb 2016 14:55:10 +1100 Subject: [PATCH 166/187] Bug 1245463: [MSE] P6. Fix incorrect assertions. r=me mAppendRunning is set prior the append task being queued; so it is possible that the segment parser loop hasn't yet been started, yet mAppendRunning is true. Separate this diagnostic with a new flag ON A CLOSED TREE. MozReview-Commit-ID: GgTyyltKOJr --- dom/media/mediasource/TrackBuffersManager.cpp | 10 ++++++++-- dom/media/mediasource/TrackBuffersManager.h | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index d389cfd50bb..59bbf116364 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -104,6 +104,7 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute , mEvictionOccurred(false) , mMonitor("TrackBuffersManager") , mAppendRunning(false) + , mSegmentParserLoopRunning(false) { MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread"); } @@ -186,6 +187,7 @@ RefPtr TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running"); MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds()); mEnded = false; @@ -299,7 +301,7 @@ void TrackBuffersManager::CompleteResetParserState() { MOZ_ASSERT(OnTaskQueue()); - MOZ_RELEASE_ASSERT(!mAppendRunning); + MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning); MSE_DEBUG(""); for (auto& track : GetTracksList()) { @@ -435,7 +437,7 @@ bool TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) { MOZ_ASSERT(OnTaskQueue()); - MOZ_ASSERT(!mAppendRunning, "Logic error: Append in progress"); + MOZ_ASSERT(!mSegmentParserLoopRunning, "Logic error: Append in progress"); MSE_DEBUG("From %.2fs to %.2f", aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds()); @@ -574,6 +576,8 @@ TrackBuffersManager::SegmentParserLoop() { MOZ_ASSERT(OnTaskQueue()); + mSegmentParserLoopRunning = true; + while (true) { // 1. If the input buffer is empty, then jump to the need more data step below. if (!mInputBuffer || mInputBuffer->IsEmpty()) { @@ -698,6 +702,7 @@ TrackBuffersManager::NeedMoreData() MSE_DEBUG(""); RestoreCachedVariables(); mAppendPromise.ResolveIfExists(mActiveTrack, __func__); + mSegmentParserLoopRunning = false; // Wake-up any pending Abort() MonitorAutoLock mon(mMonitor); mAppendRunning = false; @@ -709,6 +714,7 @@ TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName) { MSE_DEBUG("rv=%d", aRejectValue); mAppendPromise.RejectIfExists(aRejectValue, aName); + mSegmentParserLoopRunning = false; // Wake-up any pending Abort() MonitorAutoLock mon(mMonitor); mAppendRunning = false; diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index f51937face8..f150e0d4981 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -352,6 +352,9 @@ private: mutable Monitor mMonitor; // Set to true while SegmentParserLoop is running. Atomic mAppendRunning; + // Set to true while SegmentParserLoop is running. + // This is for diagnostic only. Only accessed on the task queue. + bool mSegmentParserLoopRunning; // Stable audio and video track time ranges. media::TimeIntervals mVideoBufferedRanges; media::TimeIntervals mAudioBufferedRanges; From 8f831dd76150dec516a0ce3be31d6c3698a207ee Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 11 Feb 2016 20:27:36 -0800 Subject: [PATCH 167/187] Back out ff446e81eaf5 (bug 1216681) for Linux PGO bustage CLOSED TREE --- mozglue/misc/StackWalk.cpp | 2 +- testing/tools/fileid/moz.build | 9 +-- testing/tools/fileid/win_fileid.cpp | 90 ----------------------------- tools/rb/fix_stack_using_bpsyms.py | 20 +++---- 4 files changed, 9 insertions(+), 112 deletions(-) delete mode 100644 testing/tools/fileid/win_fileid.cpp diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp index dbf668cbb83..7e54588c620 100644 --- a/mozglue/misc/StackWalk.cpp +++ b/mozglue/misc/StackWalk.cpp @@ -817,7 +817,7 @@ MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails) modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo); if (modInfoRes) { - strncpy(aDetails->library, modInfo.LoadedImageName, + strncpy(aDetails->library, modInfo.ModuleName, sizeof(aDetails->library)); aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0'; aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage; diff --git a/testing/tools/fileid/moz.build b/testing/tools/fileid/moz.build index 701886f6650..b15641bf587 100644 --- a/testing/tools/fileid/moz.build +++ b/testing/tools/fileid/moz.build @@ -4,8 +4,6 @@ # 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/. -GeckoProgram('fileid', linkage=None, msvcrt='static') - if CONFIG['OS_ARCH'] == 'Linux': USE_LIBS += [ 'breakpad_linux_common_s', @@ -27,9 +25,4 @@ if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'Darwin': LOCAL_INCLUDES += [ '/toolkit/crashreporter/google-breakpad/src', ] - -if CONFIG['OS_ARCH'] == 'WINNT': - SOURCES += ['win_fileid.cpp'] - OS_LIBS += ['dbghelp'] - -NO_PGO = True + GeckoProgram('fileid', linkage=None) diff --git a/testing/tools/fileid/win_fileid.cpp b/testing/tools/fileid/win_fileid.cpp deleted file mode 100644 index 7151baa9238..00000000000 --- a/testing/tools/fileid/win_fileid.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include -#include - -const DWORD CV_SIGNATURE_RSDS = 0x53445352; // 'SDSR' - -struct CV_INFO_PDB70 { - DWORD CvSignature; - GUID Signature; - DWORD Age; - BYTE PdbFileName[1]; -}; - -void print_guid(const GUID& guid, DWORD age) -{ - printf("%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X", - guid.Data1, guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], - guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7], - age); -} - -int main(int argc, char** argv) -{ - if (argc != 2) { - fprintf(stderr, "usage: fileid \n"); - return 1; - } - - HANDLE file = CreateFileA(argv[1], - GENERIC_READ, - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - nullptr); - if (file == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Couldn't open file: %s\n", argv[1]); - return 1; - } - - HANDLE mapFile = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, 0); - if (mapFile == nullptr) { - fprintf(stderr, "Couldn't create file mapping\n"); - CloseHandle(file); - return 1; - } - - uint8_t* base = reinterpret_cast(MapViewOfFile(mapFile, - FILE_MAP_READ, - 0, - 0, - 0)); - if (base == nullptr) { - fprintf(stderr, "Couldn't map file\n"); - CloseHandle(mapFile); - CloseHandle(file); - return 1; - } - - DWORD size; - PIMAGE_DEBUG_DIRECTORY debug_dir = - reinterpret_cast( - ImageDirectoryEntryToDataEx(base, - FALSE, - IMAGE_DIRECTORY_ENTRY_DEBUG, - &size, - nullptr)); - - bool found = false; - if (debug_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW) { - CV_INFO_PDB70* cv = - reinterpret_cast(base + debug_dir->PointerToRawData); - if (cv->CvSignature == CV_SIGNATURE_RSDS) { - found = true; - print_guid(cv->Signature, cv->Age); - } - } - - UnmapViewOfFile(base); - CloseHandle(mapFile); - CloseHandle(file); - - return found ? 0 : 1; -} diff --git a/tools/rb/fix_stack_using_bpsyms.py b/tools/rb/fix_stack_using_bpsyms.py index 156acb5882c..ed89345d1bd 100755 --- a/tools/rb/fix_stack_using_bpsyms.py +++ b/tools/rb/fix_stack_using_bpsyms.py @@ -85,14 +85,7 @@ def findIdForPath(path): # We should always be packaged with a "fileid" executable. fileid_exe = os.path.join(here, 'fileid') if not os.path.isfile(fileid_exe): - fileid_exe = fileid_exe + '.exe' - if not os.path.isfile(fileid_exe): - raise Exception("Could not find fileid executable in %s" % here) - - if not os.path.isfile(path): - for suffix in ('.exe', '.dll'): - if os.path.isfile(path + suffix): - path = path + suffix + raise Exception("Could not find fileid executable in %s" % here) try: return subprocess.check_output([fileid_exe, path]).rstrip() except subprocess.CalledProcessError as e: @@ -103,12 +96,11 @@ def guessSymbolFile(full_path, symbolsDir): """Guess a symbol file based on an object file's basename, ignoring the path and UUID.""" fn = os.path.basename(full_path) d1 = os.path.join(symbolsDir, fn) - root, _ = os.path.splitext(fn) - if os.path.exists(os.path.join(symbolsDir, root) + '.pdb'): - d1 = os.path.join(symbolsDir, root) + '.pdb' - fn = root if not os.path.exists(d1): - return None + fn = fn + ".pdb" + d1 = os.path.join(symbolsDir, fn) + if not os.path.exists(d1): + return None uuids = os.listdir(d1) if len(uuids) == 0: raise Exception("Missing symbol file for " + fn) @@ -116,6 +108,8 @@ def guessSymbolFile(full_path, symbolsDir): uuid = findIdForPath(full_path) else: uuid = uuids[0] + if fn.endswith(".pdb"): + fn = fn[:-4] return os.path.join(d1, uuid, fn + ".sym") parsedSymbolFiles = {} From a7a085691d5e3853429bc528109239f612aaa2ca Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 11 Feb 2016 20:43:41 -0800 Subject: [PATCH 168/187] Back out 3 changesets (bug 1216001) for Win8 reftest failures in 1193519-sideways-lr-3.html and 1193519-sideways-lr-4.html and intermittent OS X failures in font-display-2.html CLOSED TREE Backed out changeset dbadb8fe5803 (bug 1216001) Backed out changeset a30593ebd58e (bug 1216001) Backed out changeset c1646ffa71b4 (bug 1216001) --- dom/base/nsRange.cpp | 96 +++++------------------------ dom/base/nsRange.h | 16 ----- dom/base/test/test_user_select.html | 34 +--------- layout/base/nsDisplayList.h | 3 - layout/generic/nsTextFrame.cpp | 27 +++----- mfbt/BinarySearch.h | 2 +- 6 files changed, 28 insertions(+), 150 deletions(-) diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index c499ad76a85..e11fbf1a72e 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -151,34 +151,6 @@ GetNextRangeCommonAncestor(nsINode* aNode) return aNode; } -/** - * A Comparator suitable for mozilla::BinarySearchIf for searching a collection - * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset). - */ -struct IsItemInRangeComparator -{ - nsINode* mNode; - uint32_t mStartOffset; - uint32_t mEndOffset; - - int operator()(const nsRange* const aRange) const - { - int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset, - aRange->GetStartParent(), - aRange->StartOffset()); - if (cmp == 1) { - cmp = nsContentUtils::ComparePoints(mNode, mStartOffset, - aRange->GetEndParent(), - aRange->EndOffset()); - if (cmp == -1) { - return 0; - } - return 1; - } - return -1; - } -}; - /* static */ bool nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, uint32_t aEndOffset) @@ -188,45 +160,24 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, nsINode* n = GetNextRangeCommonAncestor(aNode); NS_ASSERTION(n || !aNode->IsSelectionDescendant(), "orphan selection descendant"); - - // Collect the potential ranges and their selection objects. - RangeHashTable ancestorSelectionRanges; - nsTHashtable> ancestorSelections; - uint32_t maxRangeCount = 0; for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { RangeHashTable* ranges = static_cast(n->GetProperty(nsGkAtoms::range)); for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) { nsRange* range = iter.Get()->GetKey(); if (range->IsInSelection() && !range->Collapsed()) { - ancestorSelectionRanges.PutEntry(range); - Selection* selection = range->mSelection; - ancestorSelections.PutEntry(selection); - maxRangeCount = std::max(maxRangeCount, selection->RangeCount()); - } - } - } - - if (!ancestorSelectionRanges.IsEmpty()) { - nsTArray sortedRanges(maxRangeCount); - for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) { - Selection* selection = iter.Get()->GetKey(); - // Sort the found ranges for |selection| in document order - // (Selection::GetRangeAt returns its ranges ordered). - for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) { - nsRange* range = selection->GetRangeAt(i); - if (ancestorSelectionRanges.Contains(range)) { - sortedRanges.AppendElement(range); + int32_t cmp = nsContentUtils::ComparePoints(aNode, aEndOffset, + range->GetStartParent(), + range->StartOffset()); + if (cmp == 1) { + cmp = nsContentUtils::ComparePoints(aNode, aStartOffset, + range->GetEndParent(), + range->EndOffset()); + if (cmp == -1) { + return true; + } } } - MOZ_ASSERT(!sortedRanges.IsEmpty()); - // Binary search the now sorted ranges. - IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset }; - size_t unused; - if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) { - return true; - } - sortedRanges.ClearAndRetainStorage(); } } return false; @@ -3154,12 +3105,6 @@ nsRange::Constructor(const GlobalObject& aGlobal, return window->GetDoc()->CreateRange(aRv); } -static bool ExcludeIfNextToNonSelectable(nsIContent* aContent) -{ - return aContent->IsNodeOfType(nsINode::eTEXT) && - aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE); -} - void nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) { @@ -3178,10 +3123,6 @@ nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) bool added = false; bool seenSelectable = false; - // |firstNonSelectableContent| is the first node in a consecutive sequence - // of non-IsSelectable nodes. When we find a selectable node after such - // a sequence we'll end the last nsRange, create a new one and restart - // the outer loop. nsIContent* firstNonSelectableContent = nullptr; while (true) { ErrorResult err; @@ -3191,19 +3132,12 @@ nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) nsIContent* content = node && node->IsContent() ? node->AsContent() : nullptr; if (content) { - if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) { - // Ignorable whitespace next to a sequence of non-selectable nodes - // counts as non-selectable (bug 1216001). - selectable = false; + nsIFrame* frame = content->GetPrimaryFrame(); + for (nsIContent* p = content; !frame && (p = p->GetParent()); ) { + frame = p->GetPrimaryFrame(); } - if (selectable) { - nsIFrame* frame = content->GetPrimaryFrame(); - for (nsIContent* p = content; !frame && (p = p->GetParent()); ) { - frame = p->GetPrimaryFrame(); - } - if (frame) { - frame->IsSelectable(&selectable, nullptr); - } + if (frame) { + frame->IsSelectable(&selectable, nullptr); } } diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index d6c60c62d73..772f4f9c462 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -252,14 +252,6 @@ public: bool *outNodeBefore, bool *outNodeAfter); - /** - * Return true if any part of (aNode, aStartOffset) .. (aNode, aEndOffset) - * overlaps any nsRange in aNode's GetNextRangeCommonAncestor ranges (i.e. - * where aNode is a descendant of a range's common ancestor node). - * If a nsRange starts in (aNode, aEndOffset) or if it ends in - * (aNode, aStartOffset) then it is non-overlapping and the result is false - * for that nsRange. Collapsed ranges always counts as non-overlapping. - */ static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, uint32_t aEndOffset); @@ -306,14 +298,6 @@ protected: */ nsINode* GetRegisteredCommonAncestor(); - // Helper to IsNodeSelected. - static bool IsNodeInSortedRanges(nsINode* aNode, - uint32_t aStartOffset, - uint32_t aEndOffset, - const nsTArray& aRanges, - size_t aRangeStart, - size_t aRangeEnd); - struct MOZ_STACK_CLASS AutoInvalidateSelection { explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) diff --git a/dom/base/test/test_user_select.html b/dom/base/test/test_user_select.html index 6cac439d065..274e523a5cf 100644 --- a/dom/base/test/test_user_select.html +++ b/dom/base/test/test_user_select.html @@ -12,7 +12,7 @@ src: url("Ahem.ttf"); } body { font-family: Ahem; font-size: 20px; } -s, .non-selectable { -moz-user-select: none; } +s { -moz-user-select: none; } n { display: none; } a { position:absolute; bottom: 0; right:0; } .text { -moz-user-select: text; } @@ -34,16 +34,6 @@ a { position:absolute; bottom: 0; right:0; }
aaaaaaabbbbbbbbccccccc
aaaaaaabbbbbbbbccccccc
aaaaaaabbbbddccccdddddddeeee
-
aaaa -
x
-
x
-
x
-bbbb
-
aaaa -
x
-
x
-
x
-bbbb
@@ -110,11 +100,9 @@ function test() is(NL(r.toString()), text, e.id + ": range["+index+"].toString()") } - function node(e, arg) + function node(e, index) { - if (typeof arg == "number") - return arg == -1 ? e : e.childNodes[arg]; - return arg; + return index == -1 ? e : e.childNodes[index]; } function checkRangeCount(n, e) @@ -270,22 +258,6 @@ function test() checkRanges([[0,1,-1,1]], e); doneTest(e); - clear(); - e = document.getElementById('testF'); - synthesizeMouse(e, 1, 1, {}); - synthesizeMouse(e, 400, 100, { shiftKey: true }); - checkText("aaaa bbbb", e); - checkRanges([[0,0,-1,1],[6,0,6,5]], e); - doneTest(e); - - clear(); - e = document.getElementById('testG'); - synthesizeMouse(e, 1, 1, {}); - synthesizeMouse(e, 400, 180, { shiftKey: true }); - checkText("aaaa bbbb", e); // XXX this doesn't seem right - bug 1247799 - checkRanges([[0,0,-1,1],[2,0,-1,3],[4,0,-1,5],[6,0,6,5]], e); - doneTest(e); - // ====================================================== // ==================== Script tests ==================== // ====================================================== diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index ce662e3c898..d62b74ca14c 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -27,7 +27,6 @@ #include "DisplayListClipState.h" #include "LayerState.h" #include "FrameMetrics.h" -#include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "mozilla/gfx/UserData.h" @@ -4376,8 +4375,6 @@ public: // regardless of bidi directionality; top and bottom in vertical modes). nscoord mVisIStartEdge; nscoord mVisIEndEdge; - // Cached result of mFrame->IsSelected(). Only initialized when needed. - mutable mozilla::Maybe mIsFrameSelected; }; /** diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index c09338014e8..a49fc5c4b4b 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -4657,12 +4657,10 @@ nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) class nsDisplayText : public nsCharClipDisplayItem { public: - nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame, - Maybe aIsSelected) : + nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) : nsCharClipDisplayItem(aBuilder, aFrame), mOpacity(1.0f), mDisableSubpixelAA(false) { - mIsFrameSelected = aIsSelected; MOZ_COUNT_CTOR(nsDisplayText); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -4836,22 +4834,18 @@ nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame"); - Maybe isSelected; if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) || (NS_GET_A(StyleColor()->mColor) == 0 && !StyleText()->HasTextShadow())) && - aBuilder->IsForPainting() && !IsSVGText()) { - isSelected.emplace(IsSelected()); - if (!isSelected) { - TextDecorations textDecs; - GetTextDecorations(PresContext(), eResolvedColors, textDecs); - if (!textDecs.HasDecorationLines()) { - return; - } + aBuilder->IsForPainting() && !IsSVGText() && !IsSelected()) { + TextDecorations textDecs; + GetTextDecorations(PresContext(), eResolvedColors, textDecs); + if (!textDecs.HasDecorationLines()) { + return; } } aLists.Content()->AppendNewToTop( - new (aBuilder) nsDisplayText(aBuilder, this, isSelected)); + new (aBuilder) nsDisplayText(aBuilder, this)); } static nsIFrame* @@ -6496,12 +6490,9 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, return; PropertyProvider provider(this, iter, nsTextFrame::eInflated); - if (aItem.mIsFrameSelected.isNothing()) { - aItem.mIsFrameSelected.emplace(IsSelected()); - } // Trim trailing whitespace, unless we're painting a selection highlight, // which should include trailing spaces if present (bug 1146754). - provider.InitializeForDisplay(!aItem.mIsFrameSelected.value()); + provider.InitializeForDisplay(!IsSelected()); gfxContext* ctx = aRenderingContext->ThebesContext(); const bool reversed = mTextRun->IsInlineReversed(); @@ -6545,7 +6536,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); // Fork off to the (slower) paint-with-selection path if necessary. - if (aItem.mIsFrameSelected.value()) { + if (IsSelected()) { MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!"); gfxSkipCharsIterator tmp(provider.GetStart()); int32_t contentOffset = tmp.ConvertSkippedToOriginal(startOffset); diff --git a/mfbt/BinarySearch.h b/mfbt/BinarySearch.h index c06ec48a55b..e4cebdf3a76 100644 --- a/mfbt/BinarySearch.h +++ b/mfbt/BinarySearch.h @@ -47,7 +47,7 @@ namespace mozilla { * struct Comparator { * int operator()(int val) const { * if (mTarget < val) return -1; - * if (mTarget > val) return 1; + * if (mValue > val) return 1; * return 0; * } * Comparator(int target) : mTarget(target) {} From 3b087cb7fa965f0409fe9398190313c825f4476f Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Fri, 12 Feb 2016 00:52:59 +0100 Subject: [PATCH 169/187] Bug 1247775 - Part 1: Remove D3D10/D2D 1.0 texture integration from layers. r=dvander MozReview-Commit-ID: CPBTl0cEG3p --- gfx/layers/client/ContentClient.cpp | 2 +- gfx/layers/d3d11/TextureD3D11.cpp | 267 +--------------------------- gfx/layers/d3d11/TextureD3D11.h | 42 ----- 3 files changed, 2 insertions(+), 309 deletions(-) diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 434f1b4b316..92649e4c691 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -73,7 +73,7 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) #ifdef XP_WIN if (backend == LayersBackend::LAYERS_D3D11) { - useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); + useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->GetRenderMode() == gfxWindowsPlatform::RENDER_DIRECT2D; } else #endif #ifdef MOZ_WIDGET_GTK diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 60074f4fe38..659f385c308 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -253,31 +253,6 @@ D3D11TextureData::~D3D11TextureData() #endif } -D3D10TextureData::D3D10TextureData(ID3D10Texture2D* aTexture, - gfx::IntSize aSize, gfx::SurfaceFormat aFormat, - bool aNeedsClear, bool aNeedsClearWhite, - bool aIsForOutOfBandContent) -: DXGITextureData(aSize, aFormat, aNeedsClear, aNeedsClearWhite, aIsForOutOfBandContent) -, mTexture(aTexture) -{ - MOZ_ASSERT(aTexture); - mHasSynchronization = HasKeyedMutex(aTexture); -} - -D3D10TextureData::~D3D10TextureData() -{ -#ifdef DEBUG - // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is - // when it calls EndDraw. This EndDraw should not execute anything so it - // shouldn't -really- need the lock but the debug layer chokes on this. - if (mDrawTarget) { - Lock(OpenMode::OPEN_NONE, nullptr); - mDrawTarget = nullptr; - Unlock(); - } -#endif -} - bool D3D11TextureData::Lock(OpenMode aMode, FenceHandle*) { @@ -295,23 +270,6 @@ D3D11TextureData::Lock(OpenMode aMode, FenceHandle*) return true; } -bool -D3D10TextureData::Lock(OpenMode aMode, FenceHandle*) -{ - if (!LockD3DTexture(mTexture.get())) { - return false; - } - - if (NS_IsMainThread() && !mIsForOutOfBandContent) { - if (!PrepareDrawTargetInLock(aMode)) { - Unlock(); - return false; - } - } - - return true; -} - bool DXGITextureData::PrepareDrawTargetInLock(OpenMode aMode) { @@ -342,12 +300,6 @@ D3D11TextureData::Unlock() UnlockD3DTexture(mTexture.get()); } -void -D3D10TextureData::Unlock() -{ - UnlockD3DTexture(mTexture.get()); -} - void D3D11TextureData::SyncWithObject(SyncObject* aSyncObject) { @@ -361,19 +313,6 @@ D3D11TextureData::SyncWithObject(SyncObject* aSyncObject) sync->RegisterTexture(mTexture); } -void -D3D10TextureData::SyncWithObject(SyncObject* aSyncObject) -{ - if (!aSyncObject || !NS_IsMainThread()) { - // When off the main thread we sync using a keyed mutex per texture. - return; - } - - MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11); - SyncObjectD3D11* sync = static_cast(aSyncObject); - sync->RegisterTexture(mTexture); -} - bool DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { @@ -393,34 +332,6 @@ DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor) return true; } -bool -D3D10TextureData::ReadBack(TextureReadbackSink* aReadbackSinc) -{ - if (NS_IsMainThread() && aReadbackSinc && mTexture) { - ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); - - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - desc.BindFlags = 0; - desc.Usage = D3D10_USAGE_STAGING; - desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; - desc.MiscFlags = 0; - - RefPtr tex; - HRESULT hr = device->CreateTexture2D(&desc, nullptr, getter_AddRefs(tex)); - - if (SUCCEEDED(hr)) { - device->CopyResource(tex, mTexture); - gfxWindowsPlatform::GetPlatform()->GetReadbackManager()->PostTask(tex, aReadbackSinc); - } else { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D3D11] CreateTexture2D failure " << mSize << " Code: " << gfx::hexa(hr); - aReadbackSinc->ProcessReadback(nullptr); - } - } - - return true; -} - DXGITextureData* DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags) { @@ -429,19 +340,7 @@ DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationF return nullptr; } - gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform(); - // When we're not on the main thread we're not going to be using Direct2D - // to access the contents of this texture client so we will always use D3D11. - bool useD3D11 = - windowsPlatform->GetContentBackendFor(LayersBackend::LAYERS_D3D11) == BackendType::DIRECT2D1_1 || - !NS_IsMainThread() || - (aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT); - - if (useD3D11) { - return D3D11TextureData::Create(aSize, aFormat, aFlags); - } else { - return D3D10TextureData::Create(aSize, aFormat, aFlags); - } + return D3D11TextureData::Create(aSize, aFormat, aFlags); } DXGITextureData* @@ -507,59 +406,12 @@ D3D11TextureData::CreateSimilar(ISurfaceAllocator* aAllocator, return D3D11TextureData::Create(mSize, mFormat, aAllocFlags); } -DXGITextureData* -D3D10TextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags) -{ - RefPtr texture10; - ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); - - CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM, - aSize.width, aSize.height, 1, 1, - D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE); - - newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED; - - HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(texture10)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) - << "[D3D10] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr); - return nullptr; - } - texture10->SetPrivateDataInterface(sD3D11TextureUsage, - new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4)); - - return new D3D10TextureData(texture10, aSize, aFormat, - aFlags & ALLOC_CLEAR_BUFFER, - aFlags & ALLOC_CLEAR_BUFFER_WHITE, - false /* aIsForOutOfBandContent */); -} - -void -D3D10TextureData::Deallocate(ISurfaceAllocator* aAllocator) -{ - mTexture = nullptr; -} - -TextureData* -D3D10TextureData::CreateSimilar(ISurfaceAllocator* aAllocator, - TextureFlags aFlags, - TextureAllocationFlags aAllocFlags) const -{ - return D3D10TextureData::Create(mSize, mFormat, aAllocFlags); -} - void D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource) { mTexture->QueryInterface(aOutResource); } -void -D3D10TextureData::GetDXGIResource(IDXGIResource** aOutResource) -{ - mTexture->QueryInterface(aOutResource); -} - DXGIYCbCrTextureData* DXGIYCbCrTextureData::Create(ISurfaceAllocator* aAllocator, TextureFlags aFlags, @@ -708,23 +560,6 @@ D3D11TextureData::BorrowDrawTarget() return result.forget(); } -already_AddRefed -D3D10TextureData::BorrowDrawTarget() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mDrawTarget && mTexture) { - // This may return a null DrawTarget - mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, mFormat); - if (!mDrawTarget) { - gfxCriticalNote << "Could not borrow DrawTarget (D3D10) " << (int)mFormat; - } - } - - RefPtr result = mDrawTarget; - return result.forget(); -} - bool D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) { @@ -758,39 +593,6 @@ D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) return true; } -bool -D3D10TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) -{ - RefPtr srcSurf = aSurface->GetDataSurface(); - - if (!srcSurf) { - gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (D3D10)."; - return false; - } - - DataSourceSurface::MappedSurface sourceMap; - if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) { - gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (D3D10)."; - return false; - } - - RefPtr device; - mTexture->GetDevice(getter_AddRefs(device)); - - D3D10_BOX box; - box.front = 0; - box.back = 1; - box.top = box.left = 0; - box.right = aSurface->GetSize().width; - box.bottom = aSurface->GetSize().height; - - device->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0); - - srcSurf->Unmap(); - - return true; -} - DXGITextureHostD3D11::DXGITextureHostD3D11(TextureFlags aFlags, const SurfaceDescriptorD3D10& aDescriptor) : TextureHost(aFlags) @@ -1227,47 +1029,11 @@ SyncObjectD3D11::RegisterTexture(ID3D11Texture2D* aTexture) mD3D11SyncedTextures.push_back(aTexture); } -void -SyncObjectD3D11::RegisterTexture(ID3D10Texture2D* aTexture) -{ - mD3D10SyncedTextures.push_back(aTexture); -} - void SyncObjectD3D11::FinalizeFrame() { HRESULT hr; - if (!mD3D10Texture && mD3D10SyncedTextures.size()) { - ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); - if (!device) { - return; - } - - hr = device->OpenSharedResource(mHandle, __uuidof(ID3D10Texture2D), (void**)(ID3D10Texture2D**)getter_AddRefs(mD3D10Texture)); - - if (FAILED(hr) || !mD3D10Texture) { - gfxCriticalError() << "Failed to D3D10 OpenSharedResource for frame finalization: " << hexa(hr); - - if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) { - return; - } - - gfxDevCrash(LogReason::D3D10FinalizeFrame) << "Without device reset: " << hexa(hr); - } - - // test QI - RefPtr mutex; - hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); - - if (FAILED(hr) || !mutex) { - // Leave both the critical error and MOZ_CRASH for now; the critical error lets - // us "save" the hr value. We will probably eventuall replace this with gfxDevCrash. - gfxCriticalError() << "Failed to get KeyedMutex (1): " << hexa(hr); - MOZ_CRASH("GFX: Cannot get D3D10 KeyedMutex"); - } - } - if (!mD3D11Texture && mD3D11SyncedTextures.size()) { ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice(); @@ -1295,37 +1061,6 @@ SyncObjectD3D11::FinalizeFrame() } } - if (mD3D10SyncedTextures.size()) { - RefPtr mutex; - hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); - hr = mutex->AcquireSync(0, 20000); - - if (hr == WAIT_TIMEOUT) { - if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) { - gfxWarning() << "AcquireSync timed out because of device reset."; - return; - } - gfxDevCrash(LogReason::D3D10SyncLock) << "Timeout on the D3D10 sync lock"; - } - - D3D10_BOX box; - box.front = box.top = box.left = 0; - box.back = box.bottom = box.right = 1; - - ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); - if (!device) { - return; - } - - for (auto iter = mD3D10SyncedTextures.begin(); iter != mD3D10SyncedTextures.end(); iter++) { - device->CopySubresourceRegion(mD3D10Texture, 0, 0, 0, 0, *iter, 0, &box); - } - - mutex->ReleaseSync(0); - - mD3D10SyncedTextures.clear(); - } - if (mD3D11SyncedTextures.size()) { RefPtr mutex; hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index f4bc9f69582..7ec59cfbf7d 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -109,45 +109,6 @@ CreateD3D11extureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat ID3D11Device* aDevice, ISurfaceAllocator* aAllocator); - -class D3D10TextureData : public DXGITextureData -{ -public: - static DXGITextureData* - Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags); - - virtual bool Lock(OpenMode aMode, FenceHandle*) override; - - virtual void Unlock() override; - - virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override; - - virtual already_AddRefed BorrowDrawTarget() override; - - virtual TextureData* - CreateSimilar(ISurfaceAllocator* aAllocator, - TextureFlags aFlags, - TextureAllocationFlags aAllocFlags) const override; - - // TODO - merge this with the FenceHandle API! - virtual void SyncWithObject(SyncObject* aSync) override; - - virtual bool ReadBack(TextureReadbackSink* aReadbackSinc) override; - - virtual void Deallocate(ISurfaceAllocator* aAllocator) override; - - ~D3D10TextureData(); -protected: - D3D10TextureData(ID3D10Texture2D* aTexture, - gfx::IntSize aSize, gfx::SurfaceFormat aFormat, - bool aNeedsClear, bool aNeedsClearWhite, - bool aIsForOutOfBandContent); - - virtual void GetDXGIResource(IDXGIResource** aOutResource) override; - - RefPtr mTexture; -}; - class DXGIYCbCrTextureData : public TextureData { public: @@ -431,12 +392,9 @@ public: virtual SyncType GetSyncType() { return SyncType::D3D11; } void RegisterTexture(ID3D11Texture2D* aTexture); - void RegisterTexture(ID3D10Texture2D* aTexture); private: RefPtr mD3D11Texture; - RefPtr mD3D10Texture; - std::vector mD3D10SyncedTextures; std::vector mD3D11SyncedTextures; SyncHandle mHandle; }; From 782d2b90b178536d6d5d9c20e20449d0e2356055 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Fri, 12 Feb 2016 02:05:35 +0100 Subject: [PATCH 170/187] Bug 1247775 - Part 2: Remove Direct2D 1.0 code from Thebes. r=dvander MozReview-Commit-ID: EC8yhkvvood --- gfx/ipc/GraphicsMessages.ipdlh | 1 - gfx/thebes/gfxBlur.cpp | 2 +- gfx/thebes/gfxPrefs.h | 2 - gfx/thebes/gfxUtils.cpp | 3 +- gfx/thebes/gfxWindowsPlatform.cpp | 263 ++++-------------------------- gfx/thebes/gfxWindowsPlatform.h | 22 +-- modules/libpref/init/all.js | 5 +- widget/windows/GfxInfo.cpp | 6 +- 8 files changed, 44 insertions(+), 260 deletions(-) diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh index f72f09abb89..09fb18416c6 100644 --- a/gfx/ipc/GraphicsMessages.ipdlh +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -19,7 +19,6 @@ struct DeviceInitData bool useD3D11WARP; bool useD3D11ImageBridge; bool d3d11TextureSharingWorks; - bool useD2D; bool useD2D1; DxgiAdapterDesc adapter; }; diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index 2f4e1fc05b7..a22aa3ab5e0 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -594,7 +594,7 @@ RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, if ((!aDT.GetTransform().IsRectilinear() && aDT.GetBackendType() != BackendType::CAIRO) || - (aDT.GetBackendType() == BackendType::DIRECT2D)) { + (aDT.GetBackendType() == BackendType::DIRECT2D1_1)) { // Use stretching if possible, since it leads to less seams when the // destination is transformed. However, don't do this if we're using cairo, // because if cairo is using pixman it won't render anything for large diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index d98ef69a415..e7a1be313bc 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -242,8 +242,6 @@ private: DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false); DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false); - DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false); - DECL_GFX_PREF(Live, "gfx.direct2d.allow1_0", Direct2DAllow1_0, bool, false); DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false); DECL_GFX_PREF(Once, "gfx.e10s.hide-plugins-for-scroll", HidePluginsForScroll, bool, true); DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.force-enabled", DirectWriteFontRenderingForceEnabled, bool, false); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index abdd342e84b..bd751e5e6a3 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -422,8 +422,7 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable, js::ProfileEntry::Category::GRAPHICS); DrawTarget* destDrawTarget = aContext->GetDrawTarget(); - if ((destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) || - (destDrawTarget->GetBackendType() == BackendType::DIRECT2D)) { + if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) { return nullptr; } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index ce39be4458e..7f455a138c6 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -43,12 +43,10 @@ #include "WinUtils.h" -#ifdef CAIRO_HAS_DWRITE_FONT #include "gfxDWriteFontList.h" #include "gfxDWriteFonts.h" #include "gfxDWriteCommon.h" #include -#endif #include "gfxTextRun.h" #include "gfxUserFontSet.h" @@ -57,13 +55,11 @@ #include -#ifdef CAIRO_HAS_D2D_SURFACE #include #include "mozilla/gfx/2D.h" #include "nsMemory.h" -#endif #include @@ -114,14 +110,11 @@ DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget) } } -#ifdef CAIRO_HAS_D2D_SURFACE - static const char *kFeatureLevelPref = "gfx.direct3d.last_used_feature_level_idx"; static const int kSupportedFeatureLevels[] = { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0 }; -#endif class GfxD2DVramReporter final : public nsIMemoryReporter { @@ -166,16 +159,16 @@ NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter) class GPUAdapterReporter final : public nsIMemoryReporter { // Callers must Release the DXGIAdapter after use or risk mem-leak - static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter) + static bool GetDXGIAdapter(IDXGIAdapter **aDXGIAdapter) { - ID3D10Device1 *D2D10Device; - IDXGIDevice *DXGIDevice; + ID3D11Device *d3d11Device; + IDXGIDevice *dxgiDevice; bool result = false; - if ((D2D10Device = mozilla::gfx::Factory::GetDirect3D10Device())) { - if (D2D10Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice) == S_OK) { - result = (DXGIDevice->GetAdapter(DXGIAdapter) == S_OK); - DXGIDevice->Release(); + if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) { + if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice) == S_OK) { + result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK); + dxgiDevice->Release(); } } @@ -376,7 +369,6 @@ gfxWindowsPlatform::gfxWindowsPlatform() , mCompositorD3D11TextureSharingWorks(false) , mAcceleration(FeatureStatus::Unused) , mD3D11Status(FeatureStatus::Unused) - , mD2DStatus(FeatureStatus::Unused) , mD2D1Status(FeatureStatus::Unused) { mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; @@ -411,7 +403,6 @@ gfxWindowsPlatform::gfxWindowsPlatform() gfxWindowsPlatform::~gfxWindowsPlatform() { mDeviceManager = nullptr; - mD3D10Device = nullptr; mD3D11Device = nullptr; mD3D11ContentDevice = nullptr; mD3D11ImageBridgeDevice = nullptr; @@ -489,9 +480,7 @@ gfxWindowsPlatform::HandleDeviceReset() // Remove devices and adapters. ResetD3D11Devices(); - mD3D10Device = nullptr; mAdapter = nullptr; - Factory::SetDirect3D10Device(nullptr); // Reset local state. Note: we leave feature status variables as-is. They // will be recomputed by InitializeDevices(). @@ -519,17 +508,11 @@ gfxWindowsPlatform::UpdateBackendPrefs() uint32_t canvasMask = BackendTypeBit(SOFTWARE_BACKEND); uint32_t contentMask = BackendTypeBit(SOFTWARE_BACKEND); BackendType defaultBackend = SOFTWARE_BACKEND; - if (GetD2DStatus() == FeatureStatus::Available) { + if (GetD2D1Status() == FeatureStatus::Available) { mRenderMode = RENDER_DIRECT2D; - canvasMask |= BackendTypeBit(BackendType::DIRECT2D); - contentMask |= BackendTypeBit(BackendType::DIRECT2D); - if (GetD2D1Status() == FeatureStatus::Available) { - contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - defaultBackend = BackendType::DIRECT2D1_1; - } else { - defaultBackend = BackendType::DIRECT2D; - } + contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + defaultBackend = BackendType::DIRECT2D1_1; } else { mRenderMode = RENDER_GDI; canvasMask |= BackendTypeBit(BackendType::SKIA); @@ -571,129 +554,11 @@ gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers) return SOFTWARE_BACKEND; } -#ifdef CAIRO_HAS_D2D_SURFACE -HRESULT -gfxWindowsPlatform::CreateDevice(RefPtr &adapter1, - int featureLevelIndex) -{ - nsModuleHandle d3d10module(LoadLibrarySystem32(L"d3d10_1.dll")); - if (!d3d10module) - return E_FAIL; - decltype(D3D10CreateDevice1)* createD3DDevice = - (decltype(D3D10CreateDevice1)*) GetProcAddress(d3d10module, "D3D10CreateDevice1"); - if (!createD3DDevice) - return E_FAIL; - - ID3D10Device1* device = nullptr; - HRESULT hr = - createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr, -#ifdef DEBUG - // This isn't set because of bug 1078411 - // D3D10_CREATE_DEVICE_DEBUG | -#endif - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - static_cast(kSupportedFeatureLevels[featureLevelIndex]), - D3D10_1_SDK_VERSION, &device); - - // If we fail here, the DirectX version or video card probably - // changed. We previously could use 10.1 but now we can't - // anymore. Revert back to doing a 10.0 check first before - // the 10.1 check. - if (device) { - mD3D10Device = device; - - // Leak the module while the D3D 10 device is being used. - d3d10module.disown(); - - // Setup a pref for future launch optimizaitons when in main process. - if (XRE_IsParentProcess()) { - Preferences::SetInt(kFeatureLevelPref, featureLevelIndex); - } - } - - return device ? S_OK : hr; -} -#endif - -void -gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce) -{ - if ((!Factory::SupportsD2D1() || !gfxPrefs::Direct2DUse1_1()) && !gfxPrefs::Direct2DAllow1_0()) { - return; - } - -#ifdef CAIRO_HAS_D2D_SURFACE - if (mD3D10Device) { - if (SUCCEEDED(mD3D10Device->GetDeviceRemovedReason())) { - return; - } - mD3D10Device = nullptr; - - // Surface cache needs to be invalidated since it may contain vector - // images rendered with our old, broken D2D device. - SurfaceCache::DiscardAll(); - } - - mozilla::ScopedGfxFeatureReporter reporter("D2D", aAttemptForce); - - int supportedFeatureLevelsCount = ArrayLength(kSupportedFeatureLevels); - - RefPtr adapter1 = GetDXGIAdapter(); - - if (!adapter1) { - // Unable to create adapter, abort acceleration. - return; - } - - // It takes a lot of time (5-10% of startup time or ~100ms) to do both - // a createD3DDevice on D3D10_FEATURE_LEVEL_10_0. We therefore store - // the last used feature level to go direct to that. - int featureLevelIndex = Preferences::GetInt(kFeatureLevelPref, 0); - if (featureLevelIndex >= supportedFeatureLevelsCount || featureLevelIndex < 0) - featureLevelIndex = 0; - - // Start with the last used feature level, and move to lower DX versions - // until we find one that works. - HRESULT hr = E_FAIL; - for (int i = featureLevelIndex; i < supportedFeatureLevelsCount; i++) { - hr = CreateDevice(adapter1, i); - // If it succeeded we found the first available feature level - if (SUCCEEDED(hr)) - break; - } - - // If we succeeded in creating a device, try for a newer device - // that we haven't tried yet. - if (SUCCEEDED(hr)) { - for (int i = featureLevelIndex - 1; i >= 0; i--) { - hr = CreateDevice(adapter1, i); - // If it failed then we don't have new hardware - if (FAILED(hr)) { - break; - } - } - } - - if (mD3D10Device) { - reporter.SetSuccessful(); - mozilla::gfx::Factory::SetDirect3D10Device(mD3D10Device); - } - - ScopedGfxFeatureReporter reporter1_1("D2D1.1V"); - - if (Factory::SupportsD2D1()) { - reporter1_1.SetSuccessful(); - } -#endif -} - gfxPlatformFontList* gfxWindowsPlatform::CreatePlatformFontList() { gfxPlatformFontList *pfl; -#ifdef CAIRO_HAS_DWRITE_FONT // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd // crashers so blacklist them altogether if (IsNotWin7PreRTM() && GetDWriteFactory()) { @@ -707,7 +572,7 @@ gfxWindowsPlatform::CreatePlatformFontList() gfxPlatformFontList::Shutdown(); DisableD2D(); } -#endif + pfl = new gfxGDIFontList(); if (NS_SUCCEEDED(pfl->InitFontList())) { @@ -727,10 +592,8 @@ gfxWindowsPlatform::CreatePlatformFontList() void gfxWindowsPlatform::DisableD2D() { - mD2DStatus = FeatureStatus::Failed; mD2D1Status = FeatureStatus::Failed; Factory::SetDirect3D11Device(nullptr); - Factory::SetDirect3D10Device(nullptr); UpdateBackendPrefs(); } @@ -1178,12 +1041,6 @@ gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason) return true; } } - if (GetD3D10Device()) { - HRESULT hr = GetD3D10Device()->GetDeviceRemovedReason(); - if (IsDeviceReset(hr, aResetReason)) { - return true; - } - } if (XRE_IsParentProcess() && gfxPrefs::DeviceResetForTesting()) { TestDeviceReset((DeviceResetReason)gfxPrefs::DeviceResetForTesting()); if (aResetReason) { @@ -1447,7 +1304,6 @@ gfxWindowsPlatform::FontsPrefsChanged(const char *aPref) void gfxWindowsPlatform::SetupClearTypeParams() { -#if CAIRO_HAS_DWRITE_FONT if (GetDWriteFactory()) { // any missing prefs will default to invalid (-1) and be ignored; // out-of-range values will also be ignored @@ -1555,7 +1411,6 @@ gfxWindowsPlatform::SetupClearTypeParams() dwriteGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, getter_AddRefs(mRenderingParams[TEXT_RENDERING_GDI_CLASSIC])); } -#endif } void @@ -2417,7 +2272,7 @@ gfxWindowsPlatform::InitializeDevices() // Usually we want D2D in order to use DWrite, but if the users have it // forced, we'll let them have it, as unsupported configuration. if (gfxPrefs::DirectWriteFontRenderingForceEnabled() && - IsFeatureStatusFailure(mD2DStatus) && + IsFeatureStatusFailure(mD2D1Status) && !mDWriteFactory) { gfxCriticalNote << "Attempting DWrite without D2D support"; InitDWriteSupport(); @@ -2583,17 +2438,11 @@ IsD2DBlacklisted() // not change after a TDR (like the OS version), we could find a driver change // that runs us into the blacklist. FeatureStatus -gfxWindowsPlatform::CheckD2DSupport() +gfxWindowsPlatform::CheckD2D1Support() { - // Don't revive D2D support after a failure. - if (IsFeatureStatusFailure(mD2DStatus)) { - return mD2DStatus; - } - - if (XRE_IsContentProcess()) { - return GetParentDevicePrefs().useD2D() - ? FeatureStatus::Available - : FeatureStatus::Blocked; + // Don't revive D2D1 support after a failure. + if (IsFeatureStatusFailure(mD2D1Status)) { + return mD2D1Status; } if (!gfxPrefs::Direct2DForceEnabled() && IsD2DBlacklisted()) { @@ -2617,70 +2466,38 @@ gfxWindowsPlatform::CheckD2DSupport() if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) { return FeatureStatus::Blocked; } + + if (!Factory::SupportsD2D1()) { + return FeatureStatus::Unavailable; + } + + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD2D1() + ? FeatureStatus::Available + : FeatureStatus::Blocked; + } + return FeatureStatus::Available; } void gfxWindowsPlatform::InitializeD2D() { - mD2DStatus = CheckD2DSupport(); - if (IsFeatureStatusFailure(mD2DStatus)) { + ScopedGfxFeatureReporter d2d1_1("D2D1.1"); + + mD2D1Status = CheckD2D1Support(); + if (IsFeatureStatusFailure(mD2D1Status)) { return; } if (!mCompositorD3D11TextureSharingWorks) { - mD2DStatus = FeatureStatus::Failed; + mD2D1Status = FeatureStatus::Failed; return; } // Using Direct2D depends on DWrite support. if (!mDWriteFactory && !InitDWriteSupport()) { - mD2DStatus = FeatureStatus::Failed; - return; - } - - // Initialize D2D 1.1. - InitializeD2D1(); - - // Initialize D2D 1.0. - VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); - if (!mD3D10Device) { - mDWriteFactory = nullptr; - mD2DStatus = FeatureStatus::Failed; - return; - } - - mD2DStatus = FeatureStatus::Available; -} - -FeatureStatus -gfxWindowsPlatform::CheckD2D1Support() -{ - // Don't revive D2D1 support after a failure. - if (IsFeatureStatusFailure(mD2D1Status)) { - return mD2D1Status; - } - if (!Factory::SupportsD2D1()) { - return FeatureStatus::Unavailable; - } - if (XRE_IsContentProcess()) { - return GetParentDevicePrefs().useD2D1() - ? FeatureStatus::Available - : FeatureStatus::Blocked; - } - if (!gfxPrefs::Direct2DUse1_1()) { - return FeatureStatus::Disabled; - } - return FeatureStatus::Available; -} - -void -gfxWindowsPlatform::InitializeD2D1() -{ - ScopedGfxFeatureReporter d2d1_1("D2D1.1"); - - mD2D1Status = CheckD2D1Support(); - if (IsFeatureStatusFailure(mD2D1Status)) { + mD2D1Status = FeatureStatus::Failed; return; } @@ -2693,6 +2510,8 @@ gfxWindowsPlatform::InitializeD2D1() Factory::SetDirect3D11Device(mD3D11ContentDevice); d2d1_1.SetSuccessful(); + + mD2D1Status = FeatureStatus::Available; } bool @@ -3051,15 +2870,6 @@ gfxWindowsPlatform::GetD3D11Status() const return mD3D11Status; } -FeatureStatus -gfxWindowsPlatform::GetD2DStatus() const -{ - if (GetD3D11Status() != FeatureStatus::Available) { - return FeatureStatus::Unavailable; - } - return mD2DStatus; -} - FeatureStatus gfxWindowsPlatform::GetD2D1Status() const { @@ -3096,7 +2906,6 @@ gfxWindowsPlatform::GetDeviceInitData(DeviceInitData* aOut) aOut->useD3D11ImageBridge() = !!mD3D11ImageBridgeDevice; aOut->d3d11TextureSharingWorks() = mCompositorD3D11TextureSharingWorks; aOut->useD3D11WARP() = mIsWARP; - aOut->useD2D() = (GetD2DStatus() == FeatureStatus::Available); aOut->useD2D1() = (GetD2D1Status() == FeatureStatus::Available); if (mD3D11Device) { diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 8716506cf09..51011799efc 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -17,9 +17,7 @@ #include "gfxFontUtils.h" #include "gfxWindowsSurface.h" #include "gfxFont.h" -#ifdef CAIRO_HAS_DWRITE_FONT #include "gfxDWriteFonts.h" -#endif #include "gfxPlatform.h" #include "gfxTelemetry.h" #include "gfxTypes.h" @@ -33,9 +31,7 @@ #include #include -#ifdef CAIRO_HAS_D2D_SURFACE #include -#endif // This header is available in the June 2010 SDK and in the Win8 SDK #include @@ -163,10 +159,6 @@ public: */ void VerifyD2DDevice(bool aAttemptForce); -#ifdef CAIRO_HAS_D2D_SURFACE - HRESULT CreateDevice(RefPtr &adapter1, int featureLevelIndex); -#endif - nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) override; @@ -230,20 +222,16 @@ public: void SetupClearTypeParams(); -#ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } inline bool DWriteEnabled() { return !!mDWriteFactory; } inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; } IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode) { return mRenderingParams[aRenderMode]; } -#else - inline bool DWriteEnabled() { return false; } -#endif + void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager); mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager(); IDirect3DDevice9* GetD3D9Device(); - ID3D10Device1 *GetD3D10Device() { return mD3D10Device; } ID3D11Device *GetD3D11Device(); ID3D11Device *GetD3D11ContentDevice(); ID3D11Device* GetD3D11DeviceForCurrentThread(); @@ -281,7 +269,6 @@ public: // initialization has not been attempted, this returns // FeatureStatus::Unused. mozilla::gfx::FeatureStatus GetD3D11Status() const; - mozilla::gfx::FeatureStatus GetD2DStatus() const; mozilla::gfx::FeatureStatus GetD2D1Status() const; unsigned GetD3D11Version(); @@ -319,14 +306,12 @@ private: void InitializeDevices(); void InitializeD3D11(); void InitializeD2D(); - void InitializeD2D1(); bool InitDWriteSupport(); void DisableD2D(); mozilla::gfx::FeatureStatus CheckAccelerationSupport(); mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware); - mozilla::gfx::FeatureStatus CheckD2DSupport(); mozilla::gfx::FeatureStatus CheckD2D1Support(); mozilla::gfx::FeatureStatus AttemptD3D11DeviceCreation(); @@ -352,14 +337,12 @@ private: IDXGIAdapter1 *GetDXGIAdapter(); bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason); -#ifdef CAIRO_HAS_DWRITE_FONT RefPtr mDWriteFactory; RefPtr mRenderingParams[TEXT_RENDERING_COUNT]; DWRITE_MEASURING_MODE mMeasuringMode; -#endif + RefPtr mAdapter; RefPtr mDeviceManager; - RefPtr mD3D10Device; RefPtr mD3D11Device; RefPtr mD3D11ContentDevice; RefPtr mD3D11ImageBridgeDevice; @@ -374,7 +357,6 @@ private: // accessors instead. mozilla::gfx::FeatureStatus mAcceleration; mozilla::gfx::FeatureStatus mD3D11Status; - mozilla::gfx::FeatureStatus mD2DStatus; mozilla::gfx::FeatureStatus mD2D1Status; nsTArray mFeatureLevels; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 1082106d72d..047c67f1391 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -713,8 +713,8 @@ pref("gfx.font_rendering.opentype_svg.enabled", true); #ifdef XP_WIN // comma separated list of backends to use in order of preference // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo"); -pref("gfx.canvas.azure.backends", "direct2d1.1,direct2d,skia,cairo"); -pref("gfx.content.azure.backends", "direct2d1.1,direct2d,cairo"); +pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo"); +pref("gfx.content.azure.backends", "direct2d1.1,cairo"); #else #ifdef XP_MACOSX pref("gfx.content.azure.backends", "cg"); @@ -4447,7 +4447,6 @@ pref("gfx.content.use-native-pushlayer", true); // Whether to disable the automatic detection and use of direct2d. pref("gfx.direct2d.disabled", false); -pref("gfx.direct2d.use1_1", true); // Whether to attempt to enable Direct2D regardless of automatic detection or // blacklisting diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 6c8e220af45..acadd920d52 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1272,14 +1272,12 @@ GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle aObj) JS_SetProperty(aCx, obj, "blacklisted", val); } - gfx::FeatureStatus d2d = platform->GetD2DStatus(); + gfx::FeatureStatus d2d = platform->GetD2D1Status(); if (!InitFeatureObject(aCx, aObj, "d2d", d2d, &obj)) { return; } { - const char* version = "1.0"; - if (platform->GetD2D1Status() == gfx::FeatureStatus::Available) - version = "1.1"; + const char* version = "1.1"; JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, version)); JS::Rooted val(aCx, JS::StringValue(str)); JS_SetProperty(aCx, obj, "version", val); From 7e3f410fb0758cd19217e3d819227075650c63f6 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Fri, 12 Feb 2016 02:11:50 +0100 Subject: [PATCH 171/187] Bug 1247775 - Part 3: Remove Moz2D code to support Direct2D 1.0. r=dvander MozReview-Commit-ID: KBZSqIdx0OC --- gfx/2d/2D.h | 11 - gfx/2d/DrawTargetD2D.cpp | 2831 --------------------------- gfx/2d/DrawTargetD2D.h | 290 --- gfx/2d/DrawTargetD2D1.cpp | 74 +- gfx/2d/Factory.cpp | 99 +- gfx/2d/FilterNodeD2D1.cpp | 5 - gfx/2d/HelpersD2D.h | 3 +- gfx/2d/NativeFontResourceDWrite.cpp | 6 +- gfx/2d/PathD2D.cpp | 4 +- gfx/2d/ScaledFontDWrite.cpp | 4 +- gfx/2d/SourceSurfaceD2D.cpp | 317 --- gfx/2d/SourceSurfaceD2D.h | 90 - gfx/2d/SourceSurfaceD2DTarget.cpp | 325 --- gfx/2d/SourceSurfaceD2DTarget.h | 90 - gfx/2d/Types.h | 2 +- gfx/2d/moz.build | 3 - 16 files changed, 78 insertions(+), 4076 deletions(-) delete mode 100644 gfx/2d/DrawTargetD2D.cpp delete mode 100644 gfx/2d/DrawTargetD2D.h delete mode 100644 gfx/2d/SourceSurfaceD2D.cpp delete mode 100644 gfx/2d/SourceSurfaceD2D.h delete mode 100644 gfx/2d/SourceSurfaceD2DTarget.cpp delete mode 100644 gfx/2d/SourceSurfaceD2DTarget.h diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 6913ce9b849..f233e4346f9 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -36,8 +36,6 @@ typedef _cairo_surface cairo_surface_t; struct _cairo_scaled_font; typedef _cairo_scaled_font cairo_scaled_font_t; -struct ID3D10Device1; -struct ID3D10Texture2D; struct ID3D11Texture2D; struct ID3D11Device; struct ID2D1Device; @@ -1364,14 +1362,6 @@ public: #endif #ifdef WIN32 - static already_AddRefed CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); - static already_AddRefed - CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, - ID3D10Texture2D *aTextureB, - SurfaceFormat aFormat); - - static void SetDirect3D10Device(ID3D10Device1 *aDevice); - static ID3D10Device1 *GetDirect3D10Device(); static already_AddRefed CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat); static void SetDirect3D11Device(ID3D11Device *aDevice); @@ -1394,7 +1384,6 @@ public: private: static ID2D1Device *mD2D1Device; - static ID3D10Device1 *mD3D10Device; static ID3D11Device *mD3D11Device; #endif diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp deleted file mode 100644 index f1e2731090a..00000000000 --- a/gfx/2d/DrawTargetD2D.cpp +++ /dev/null @@ -1,2831 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include -#include "DrawTargetD2D.h" -#include "SourceSurfaceD2D.h" -#include "SourceSurfaceD2D1.h" -#include "SourceSurfaceD2DTarget.h" -#include "ShadersD2D.h" -#include "PathD2D.h" -#include "GradientStopsD2D.h" -#include "ScaledFontDWrite.h" -#include "ImageScaling.h" -#include "Logging.h" -#include "Tools.h" -#include -#include "FilterNodeSoftware.h" - -#include "FilterNodeD2D1.h" -#include "ExtendInputEffectD2D1.h" - -#include -#include - -// decltype is not usable for overloaded functions. -typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( - D2D1_FACTORY_TYPE factoryType, - REFIID iid, - CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, - void **factory -); - -using namespace std; - -namespace mozilla { -namespace gfx { - -struct Vertex { - float x; - float y; -}; - -ID2D1Factory *DrawTargetD2D::mFactory; -IDWriteFactory *DrawTargetD2D::mDWriteFactory; -uint64_t DrawTargetD2D::mVRAMUsageDT; -uint64_t DrawTargetD2D::mVRAMUsageSS; - -// Helper class to restore surface contents that was clipped out but may have -// been altered by a drawing call. -class AutoSaveRestoreClippedOut -{ -public: - AutoSaveRestoreClippedOut(DrawTargetD2D *aDT) - : mDT(aDT) - {} - - void Save() { - if (!mDT->mPushedClips.size()) { - return; - } - - mDT->Flush(); - - RefPtr tmpTexture; - IntSize size = mDT->mSize; - SurfaceFormat format = mDT->mFormat; - - CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, - 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1 CreateTexture2D failure " << size << " Code: " << hexa(hr) << " format " << (int)format; - return; - } - mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture); - - D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format)); - - RefPtr surf; - - tmpTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surf)); - - hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf, - &props, getter_AddRefs(mOldSurfBitmap)); - - if (FAILED(hr)) { - gfxCriticalError() << "[D2D] CreateSharedBitmap failure " << size << " Code: " << hexa(hr); - return; - } - - IntRect clipBounds; - mClippedArea = mDT->GetClippedGeometry(&clipBounds); - - if (!clipBounds.IsEqualEdges(IntRect(IntPoint(0, 0), mDT->mSize))) { - // We still need to take into account clipBounds if it contains additional - // clipping information. - RefPtr rectGeom; - factory()->CreateRectangleGeometry(D2D1::Rect(Float(clipBounds.x), - Float(clipBounds.y), - Float(clipBounds.XMost()), - Float(clipBounds.YMost())), - getter_AddRefs(rectGeom)); - - mClippedArea = IntersectGeometry(mClippedArea, rectGeom); - } - } - - ID2D1Factory *factory() { return mDT->factory(); } - - ~AutoSaveRestoreClippedOut() - { - if (!mOldSurfBitmap) { - return; - } - - ID2D1RenderTarget *rt = mDT->mRT; - - // Write the area that was clipped out back to the surface. This all - // happens in device space. - rt->SetTransform(D2D1::IdentityMatrix()); - mDT->mTransformDirty = true; - - RefPtr rectGeom; - factory()->CreateRectangleGeometry( - D2D1::RectF(0, 0, float(mDT->mSize.width), float(mDT->mSize.height)), - getter_AddRefs(rectGeom)); - - RefPtr invClippedArea; - factory()->CreatePathGeometry(getter_AddRefs(invClippedArea)); - RefPtr sink; - invClippedArea->Open(getter_AddRefs(sink)); - - rectGeom->CombineWithGeometry(mClippedArea, D2D1_COMBINE_MODE_EXCLUDE, nullptr, sink); - sink->Close(); - - RefPtr brush; - HRESULT hr = rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), getter_AddRefs(brush)); - if (FAILED(hr)) { - gfxCriticalNote << "[D2D] CreateBitmapBrush failure " << hexa(hr); - return; - } - - rt->FillGeometry(invClippedArea, brush); - } - -private: - - DrawTargetD2D *mDT; - - // If we have an operator unbound by the source, this will contain a bitmap - // with the old dest surface data. - RefPtr mOldSurfBitmap; - // This contains the area drawing is clipped to. - RefPtr mClippedArea; -}; - -ID2D1Factory *D2DFactory() -{ - return DrawTargetD2D::factory(); -} - -DrawTargetD2D::DrawTargetD2D() - : mCurrentCachedLayer(0) - , mClipsArePushed(false) - , mPrivateData(nullptr) -{ -} - -DrawTargetD2D::~DrawTargetD2D() -{ - if (mRT) { - PopAllClips(); - - mRT->EndDraw(); - - mVRAMUsageDT -= GetByteSize(); - } - if (mTempRT) { - mTempRT->EndDraw(); - - mVRAMUsageDT -= GetByteSize(); - } - - if (mSnapshot) { - // We may hold the only reference. MarkIndependent will clear mSnapshot; - // keep the snapshot object alive so it doesn't get destroyed while - // MarkIndependent is running. - RefPtr deathGrip = mSnapshot; - // mSnapshot can be treated as independent of this DrawTarget since we know - // this DrawTarget won't change again. - deathGrip->MarkIndependent(); - // mSnapshot will be cleared now. - } - - for (int i = 0; i < kLayerCacheSize; i++) { - if (mCachedLayers[i]) { - mCachedLayers[i] = nullptr; - mVRAMUsageDT -= GetByteSize(); - } - } - - // Targets depending on us can break that dependency, since we're obviously not going to - // be modified in the future. - for (TargetSet::iterator iter = mDependentTargets.begin(); - iter != mDependentTargets.end(); iter++) { - (*iter)->mDependingOnTargets.erase(this); - } - // Our dependencies on other targets no longer matter. - for (TargetSet::iterator iter = mDependingOnTargets.begin(); - iter != mDependingOnTargets.end(); iter++) { - (*iter)->mDependentTargets.erase(this); - } -} - -/* - * DrawTarget Implementation - */ -already_AddRefed -DrawTargetD2D::Snapshot() -{ - if (!mSnapshot) { - mSnapshot = new SourceSurfaceD2DTarget(this, mTexture, mFormat); - Flush(); - } - - RefPtr snapshot(mSnapshot); - return snapshot.forget(); -} - -void -DrawTargetD2D::Flush() -{ - PopAllClips(); - - HRESULT hr = mRT->Flush(); - - if (FAILED(hr)) { - gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hexa(hr); - } - - // We no longer depend on any target. - for (TargetSet::iterator iter = mDependingOnTargets.begin(); - iter != mDependingOnTargets.end(); iter++) { - (*iter)->mDependentTargets.erase(this); - } - mDependingOnTargets.clear(); -} - -void -DrawTargetD2D::AddDependencyOnSource(SourceSurfaceD2DTarget* aSource) -{ - if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { - aSource->mDrawTarget->mDependentTargets.insert(this); - mDependingOnTargets.insert(aSource->mDrawTarget); - } -} - -already_AddRefed -DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, - Rect &aSource) -{ - RefPtr bitmap; - - switch (aSurface->GetType()) { - - case SurfaceType::D2D1_BITMAP: - { - SourceSurfaceD2D *srcSurf = static_cast(aSurface); - bitmap = srcSurf->GetBitmap(); - } - break; - case SurfaceType::D2D1_DRAWTARGET: - { - SourceSurfaceD2DTarget *srcSurf = static_cast(aSurface); - bitmap = srcSurf->GetBitmap(mRT); - AddDependencyOnSource(srcSurf); - } - break; - default: - { - RefPtr srcSurf = aSurface->GetDataSurface(); - - if (!srcSurf) { - gfxDebug() << "Not able to deal with non-data source surface."; - return nullptr; - } - - // We need to include any pixels that are overlapped by aSource - Rect sourceRect(aSource); - sourceRect.RoundOut(); - - if (sourceRect.IsEmpty()) { - gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail."; - return nullptr; - } - - if (sourceRect.width > mRT->GetMaximumBitmapSize() || - sourceRect.height > mRT->GetMaximumBitmapSize()) { - gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail."; - // Don't know how to deal with this yet. - return nullptr; - } - - HRESULT hr; - { - DataSourceSurface::ScopedMap srcMap(srcSurf, DataSourceSurface::READ); - if (MOZ2D_WARN_IF(!srcMap.IsMapped())) { - return nullptr; - } - - int stride = srcMap.GetStride(); - unsigned char *data = srcMap.GetData() + - (uint32_t)sourceRect.y * stride + - (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat()); - - D2D1_BITMAP_PROPERTIES props = - D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat())); - hr = mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, getter_AddRefs(bitmap)); - } - if (FAILED(hr)) { - IntSize size(sourceRect.width, sourceRect.height); - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1CreateBitmap failure " << size << " Code: " << hexa(hr) << " format " << (int)srcSurf->GetFormat(); - return nullptr; - } - - // subtract the integer part leaving the fractional part - aSource.x -= (uint32_t)aSource.x; - aSource.y -= (uint32_t)aSource.y; - } - break; - } - - return bitmap.forget(); -} - -already_AddRefed -DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface) -{ - RefPtr image; - - Rect r(Point(), Size(aSurface->GetSize())); - image = GetBitmapForSurface(aSurface, r); - - return image.forget(); -} - -void -DrawTargetD2D::DrawSurface(SourceSurface *aSurface, - const Rect &aDest, - const Rect &aSource, - const DrawSurfaceOptions &aSurfOptions, - const DrawOptions &aOptions) -{ - RefPtr bitmap; - - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - Rect srcRect = aSource; - - bitmap = GetBitmapForSurface(aSurface, srcRect); - if (!bitmap) { - return; - } - - rt->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(srcRect)); - - FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), aDest); -} - -void -DrawTargetD2D::DrawFilter(FilterNode *aNode, - const Rect &aSourceRect, - const Point &aDestPoint, - const DrawOptions &aOptions) -{ - RefPtr dc; - HRESULT hr; - - hr = mRT->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); - - if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) { - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - hr = rt->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); - - if (SUCCEEDED(hr)) { - FilterNodeD2D1* node = static_cast(aNode); - node->WillDraw(this); - - dc->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); - - Rect destRect = aSourceRect; - destRect.MoveBy(aDestPoint); - FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect); - return; - } - } - - if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) { - gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!"; - return; - } - - FilterNodeSoftware* filter = static_cast(aNode); - filter->Draw(this, aSourceRect, aDestPoint, aOptions); -} - -void -DrawTargetD2D::MaskSurface(const Pattern &aSource, - SourceSurface *aMask, - Point aOffset, - const DrawOptions &aOptions) -{ - RefPtr bitmap; - - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); - - PrepareForDrawing(rt); - - // FillOpacityMask only works if the antialias mode is MODE_ALIASED - rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - - IntSize size = aMask->GetSize(); - Rect maskRect = Rect(0.f, 0.f, size.width, size.height); - bitmap = GetBitmapForSurface(aMask, maskRect); - if (!bitmap) { - return; - } - - Rect dest = Rect(aOffset.x, aOffset.y, size.width, size.height); - RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); - rt->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); - - FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), dest); -} - -void -DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, - const Point &aDest, - const Color &aColor, - const Point &aOffset, - Float aSigma, - CompositionOp aOperator) -{ - RefPtr srView = nullptr; - if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) { - return; - } - - SetScissorToRect(nullptr); - - // XXX - This function is way too long, it should be split up soon to make - // it more graspable! - - Flush(); - - AutoSaveRestoreClippedOut restoreClippedOut(this); - - if (!IsOperatorBoundByMask(aOperator)) { - restoreClippedOut.Save(); - } - - srView = static_cast(aSurface)->GetSRView(); - if (!srView) { - return; - } - - EnsureViews(); - - if (!mTempRTView) { - // This view is only needed in this path. - HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, getter_AddRefs(mTempRTView)); - - if (FAILED(hr)) { - gfxWarning() << "Failure to create RenderTargetView. Code: " << hexa(hr); - return; - } - } - - - RefPtr destRTView = mRTView; - RefPtr destTexture; - HRESULT hr; - - RefPtr maskTexture; - RefPtr maskSRView; - IntRect clipBounds; - if (mPushedClips.size()) { - EnsureClipMaskTexture(&clipBounds); - - mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, getter_AddRefs(maskSRView)); - } - - IntSize srcSurfSize; - ID3D10RenderTargetView *rtViews; - D3D10_VIEWPORT viewport; - - UINT stride = sizeof(Vertex); - UINT offset = 0; - ID3D10Buffer *buff = mPrivateData->mVB; - - mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); - mDevice->IASetInputLayout(mPrivateData->mInputLayout); - - mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); - mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); - - // If we create a downsampled source surface we need to correct aOffset for that. - Point correctedOffset = aOffset + aDest; - - // The 'practical' scaling factors. - Float dsFactorX = 1.0f; - Float dsFactorY = 1.0f; - - if (aSigma > 1.7f) { - // In this case 9 samples of our original will not cover it. Generate the - // mip levels for the original and create a downsampled version from - // them. We generate a version downsampled so that a kernel for a sigma - // of 1.7 will produce the right results. - float blurWeights[9] = { 0.234671f, 0.197389f, 0.197389f, 0.117465f, 0.117465f, 0.049456f, 0.049456f, 0.014732f, 0.014732f }; - mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); - - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - aSurface->GetSize().width, - aSurface->GetSize().height); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; - - RefPtr mipTexture; - hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mipTexture)); - - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSurface->GetSize()))) << "[D2D] 2 CreateTexture2D failure " << aSurface->GetSize() << " Code: " << hexa(hr); - return; - } - - IntSize dsSize = IntSize(int32_t(aSurface->GetSize().width * (1.7f / aSigma)), - int32_t(aSurface->GetSize().height * (1.7f / aSigma))); - - if (dsSize.width < 1) { - dsSize.width = 1; - } - if (dsSize.height < 1) { - dsSize.height = 1; - } - - dsFactorX = dsSize.width / Float(aSurface->GetSize().width); - dsFactorY = dsSize.height / Float(aSurface->GetSize().height); - correctedOffset.x *= dsFactorX; - correctedOffset.y *= dsFactorY; - - desc = CD3D10_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, - dsSize.width, - dsSize.height, 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - RefPtr tmpDSTexture; - hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpDSTexture)); - - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(dsSize))) << "[D2D] 3 CreateTexture2D failure " << dsSize << " Code: " << hexa(hr); - return; - } - - D3D10_BOX box; - box.left = box.top = box.front = 0; - box.back = 1; - box.right = aSurface->GetSize().width; - box.bottom = aSurface->GetSize().height; - mDevice->CopySubresourceRegion(mipTexture, 0, 0, 0, 0, static_cast(aSurface)->mTexture, 0, &box); - - mDevice->CreateShaderResourceView(mipTexture, nullptr, getter_AddRefs(srView)); - mDevice->GenerateMips(srView); - - RefPtr dsRTView; - RefPtr dsSRView; - mDevice->CreateRenderTargetView(tmpDSTexture, nullptr, getter_AddRefs(dsRTView)); - mDevice->CreateShaderResourceView(tmpDSTexture, nullptr, getter_AddRefs(dsSRView)); - - // We're not guaranteed the texture we created will be empty, we've - // seen old content at least on NVidia drivers. - float color[4] = { 0, 0, 0, 0 }; - mDevice->ClearRenderTargetView(dsRTView, color); - - rtViews = dsRTView; - mDevice->OMSetRenderTargets(1, &rtViews, nullptr); - - viewport.MaxDepth = 1; - viewport.MinDepth = 0; - viewport.Height = dsSize.height; - viewport.Width = dsSize.width; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - - mDevice->RSSetViewports(1, &viewport); - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); - mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> - GetPassByIndex(0)->Apply(0); - - mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff); - - mDevice->Draw(4, 0); - - srcSurfSize = dsSize; - - srView = dsSRView; - } else { - // In this case generate a kernel to draw the blur directly to the temp - // surf in one direction and to final in the other. - float blurWeights[9]; - - float normalizeFactor = 1.0f; - if (aSigma != 0) { - normalizeFactor = 1.0f / Float(sqrt(2 * M_PI * pow(aSigma, 2))); - } - - blurWeights[0] = normalizeFactor; - - // XXX - We should actually optimize for Sigma = 0 here. We could use a - // much simpler shader and save a lot of texture lookups. - for (int i = 1; i < 9; i += 2) { - if (aSigma != 0) { - blurWeights[i] = blurWeights[i + 1] = normalizeFactor * - exp(-pow(float((i + 1) / 2), 2) / (2 * pow(aSigma, 2))); - } else { - blurWeights[i] = blurWeights[i + 1] = 0; - } - } - - mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); - - viewport.MaxDepth = 1; - viewport.MinDepth = 0; - viewport.Height = aSurface->GetSize().height; - viewport.Width = aSurface->GetSize().width; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - - mDevice->RSSetViewports(1, &viewport); - - srcSurfSize = aSurface->GetSize(); - } - - // We may need to draw to a different intermediate surface if our temp - // texture isn't big enough. - bool needBiggerTemp = srcSurfSize.width > mSize.width || - srcSurfSize.height > mSize.height; - - RefPtr tmpRTView; - RefPtr tmpSRView; - RefPtr tmpTexture; - - IntSize tmpSurfSize = mSize; - - if (!needBiggerTemp) { - tmpRTView = mTempRTView; - tmpSRView = mSRView; - - // There could still be content here! - float color[4] = { 0, 0, 0, 0 }; - mDevice->ClearRenderTargetView(tmpRTView, color); - } else { - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - srcSurfSize.width, - srcSurfSize.height, - 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); - mDevice->CreateRenderTargetView(tmpTexture, nullptr, getter_AddRefs(tmpRTView)); - mDevice->CreateShaderResourceView(tmpTexture, nullptr, getter_AddRefs(tmpSRView)); - - tmpSurfSize = srcSurfSize; - } - - rtViews = tmpRTView; - mDevice->OMSetRenderTargets(1, &rtViews, nullptr); - - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); - - // Premultiplied! - float shadowColor[4] = { aColor.r * aColor.a, aColor.g * aColor.a, - aColor.b * aColor.a, aColor.a }; - mPrivateData->mEffect->GetVariableByName("ShadowColor")->AsVector()-> - SetFloatVector(shadowColor); - - float pixelOffset = 1.0f / float(srcSurfSize.width); - float blurOffsetsH[9] = { 0, pixelOffset, -pixelOffset, - 2.0f * pixelOffset, -2.0f * pixelOffset, - 3.0f * pixelOffset, -3.0f * pixelOffset, - 4.0f * pixelOffset, - 4.0f * pixelOffset }; - - pixelOffset = 1.0f / float(tmpSurfSize.height); - float blurOffsetsV[9] = { 0, pixelOffset, -pixelOffset, - 2.0f * pixelOffset, -2.0f * pixelOffset, - 3.0f * pixelOffset, -3.0f * pixelOffset, - 4.0f * pixelOffset, - 4.0f * pixelOffset }; - - mPrivateData->mEffect->GetVariableByName("BlurOffsetsH")-> - SetRawValue(blurOffsetsH, 0, sizeof(blurOffsetsH)); - mPrivateData->mEffect->GetVariableByName("BlurOffsetsV")-> - SetRawValue(blurOffsetsV, 0, sizeof(blurOffsetsV)); - - mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> - GetPassByIndex(0)->Apply(0); - - mDevice->Draw(4, 0); - - viewport.MaxDepth = 1; - viewport.MinDepth = 0; - viewport.Height = mSize.height; - viewport.Width = mSize.width; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - - mDevice->RSSetViewports(1, &viewport); - - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(tmpSRView); - - rtViews = destRTView; - mDevice->OMSetRenderTargets(1, &rtViews, nullptr); - - Point shadowDest = aDest + aOffset; - - mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((shadowDest.x / mSize.width) * 2.0f), - 1.0f - (shadowDest.y / mSize.height * 2.0f), - (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, - (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); - mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, Float(srcSurfSize.width) / tmpSurfSize.width, - Float(srcSurfSize.height) / tmpSurfSize.height)); - - if (mPushedClips.size()) { - mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView); - mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height, - Float(aSurface->GetSize().width) / mSize.width, - Float(aSurface->GetSize().height) / mSize.height)); - mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> - GetPassByIndex(2)->Apply(0); - SetScissorToRect(&clipBounds); - } else { - mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> - GetPassByIndex(1)->Apply(0); - } - - mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); - - mDevice->Draw(4, 0); - - srView = static_cast(aSurface)->GetSRView(); - if (!srView) { - return; - } - - mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((aDest.x / mSize.width) * 2.0f), - 1.0f - (aDest.y / mSize.height * 2.0f), - (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, - (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(static_cast(aSurface)->GetSRView()); - mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); - - if (mPushedClips.size()) { - mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height, - Float(aSurface->GetSize().width) / mSize.width, - Float(aSurface->GetSize().height) / mSize.height)); - mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")-> - GetPassByIndex(0)->Apply(0); - // We've set the scissor rect here for the previous draw call. - } else { - mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> - GetPassByIndex(0)->Apply(0); - } - - mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); - - mDevice->Draw(4, 0); -} - -void -DrawTargetD2D::ClearRect(const Rect &aRect) -{ - MarkChanged(); - PushClipRect(aRect); - - PopAllClips(); - - AutoSaveRestoreClippedOut restoreClippedOut(this); - - D2D1_RECT_F clipRect; - bool isPixelAligned; - bool pushedClip = false; - if (mTransform.IsRectilinear() && - GetDeviceSpaceClipRect(clipRect, isPixelAligned)) { - if (mTransformDirty || - !mTransform.IsIdentity()) { - mRT->SetTransform(D2D1::IdentityMatrix()); - mTransformDirty = true; - } - - mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - pushedClip = true; - } else { - FlushTransformToRT(); - restoreClippedOut.Save(); - } - - mRT->Clear(D2D1::ColorF(0, 0.0f)); - - if (pushedClip) { - mRT->PopAxisAlignedClip(); - } - - PopClip(); - return; -} - -void -DrawTargetD2D::CopySurface(SourceSurface *aSurface, - const IntRect &aSourceRect, - const IntPoint &aDestination) -{ - MarkChanged(); - - Rect srcRect(Float(aSourceRect.x), Float(aSourceRect.y), - Float(aSourceRect.width), Float(aSourceRect.height)); - Rect dstRect(Float(aDestination.x), Float(aDestination.y), - Float(aSourceRect.width), Float(aSourceRect.height)); - - mRT->SetTransform(D2D1::IdentityMatrix()); - mTransformDirty = true; - mRT->PushAxisAlignedClip(D2DRect(dstRect), D2D1_ANTIALIAS_MODE_ALIASED); - mRT->Clear(D2D1::ColorF(0, 0.0f)); - mRT->PopAxisAlignedClip(); - - RefPtr bitmap = GetBitmapForSurface(aSurface, srcRect); - if (!bitmap) { - return; - } - - if (aSurface->GetFormat() == SurfaceFormat::A8) { - RefPtr brush; - mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), - D2D1::BrushProperties(), getter_AddRefs(brush)); - mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); - } else { - mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f, - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, - D2DRect(srcRect)); - } -} - -void -DrawTargetD2D::FillRect(const Rect &aRect, - const Pattern &aPattern, - const DrawOptions &aOptions) -{ - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - if (brush) { - rt->FillRectangle(D2DRect(aRect), brush); - } - - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); -} - -void -DrawTargetD2D::StrokeRect(const Rect &aRect, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, - const DrawOptions &aOptions) -{ - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); - - if (brush && strokeStyle) { - rt->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); - } - - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); -} - -void -DrawTargetD2D::StrokeLine(const Point &aStart, - const Point &aEnd, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, - const DrawOptions &aOptions) -{ - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); - - if (brush && strokeStyle) { - rt->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); - } - - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); -} - -void -DrawTargetD2D::Stroke(const Path *aPath, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions, - const DrawOptions &aOptions) -{ - if (aPath->GetBackendType() != BackendType::DIRECT2D) { - gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; - return; - } - - const PathD2D *d2dPath = static_cast(aPath); - - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); - - if (brush && strokeStyle) { - rt->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); - } - - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); -} - -void -DrawTargetD2D::Fill(const Path *aPath, - const Pattern &aPattern, - const DrawOptions &aOptions) -{ - if (aPath->GetBackendType() != BackendType::DIRECT2D) { - gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; - return; - } - - const PathD2D *d2dPath = static_cast(aPath); - - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - if (brush) { - rt->FillGeometry(d2dPath->mGeometry, brush); - } - - Rect bounds; - if (aOptions.mCompositionOp != CompositionOp::OP_OVER) { - D2D1_RECT_F d2dbounds; - d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds); - bounds = ToRect(d2dbounds); - } - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, bounds); -} - -void -DrawTargetD2D::FillGlyphs(ScaledFont *aFont, - const GlyphBuffer &aBuffer, - const Pattern &aPattern, - const DrawOptions &aOptions, - const GlyphRenderingOptions* aRenderOptions) -{ - if (aFont->GetType() != FontType::DWRITE) { - gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; - return; - } - - ScaledFontDWrite *font = static_cast(aFont); - - IDWriteRenderingParams *params = nullptr; - if (aRenderOptions) { - if (aRenderOptions->GetType() != FontType::DWRITE) { - gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; - // This should never happen. - MOZ_ASSERT(false); - } else { - params = static_cast(aRenderOptions)->mParams; - } - } - - AntialiasMode aaMode = font->GetDefaultAAMode(); - - if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { - aaMode = aOptions.mAntialiasMode; - } - - if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && - aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR && - aaMode == AntialiasMode::SUBPIXEL) { - if (FillGlyphsManual(font, aBuffer, - static_cast(&aPattern)->mColor, - params, aOptions)) { - return; - } - } - - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); - - PrepareForDrawing(rt); - - D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; - - switch (aaMode) { - case AntialiasMode::NONE: - d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; - break; - case AntialiasMode::GRAY: - d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - break; - case AntialiasMode::SUBPIXEL: - d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - break; - default: - d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; - } - - if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && - mFormat != SurfaceFormat::B8G8R8X8) { - d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - } - - rt->SetTextAntialiasMode(d2dAAMode); - - if (rt != mRT || params != mTextRenderingParams) { - rt->SetTextRenderingParams(params); - if (rt == mRT) { - mTextRenderingParams = params; - } - } - - RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); - - AutoDWriteGlyphRun autoRun; - DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); - - if (brush) { - rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); - } - - FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); -} - -void -DrawTargetD2D::Mask(const Pattern &aSource, - const Pattern &aMask, - const DrawOptions &aOptions) -{ - ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aSource); - - PrepareForDrawing(rt); - - RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); - RefPtr maskBrush = CreateBrushForPattern(aMask, 1.0f); - - RefPtr layer; - - layer = GetCachedLayer(); - - rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1::IdentityMatrix(), - 1.0f, maskBrush), - layer); - - Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); - Matrix mat = mTransform; - mat.Invert(); - - rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush); - PopCachedLayer(rt); - - FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); -} - -void -DrawTargetD2D::PushClip(const Path *aPath) -{ - if (aPath->GetBackendType() != BackendType::DIRECT2D) { - gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; - return; - } - - mCurrentClipMaskTexture = nullptr; - mCurrentClippedGeometry = nullptr; - - RefPtr pathD2D = static_cast(const_cast(aPath)); - - PushedClip clip; - clip.mTransform = D2DMatrix(mTransform); - clip.mPath = pathD2D; - - pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); - - clip.mLayer = GetCachedLayer(); - - mPushedClips.push_back(clip); - - // The transform of clips is relative to the world matrix, since we use the total - // transform for the clips, make the world matrix identity. - mRT->SetTransform(D2D1::IdentityMatrix()); - mTransformDirty = true; - - if (mClipsArePushed) { - PushD2DLayer(mRT, pathD2D->mGeometry, clip.mLayer, clip.mTransform); - } -} - -void -DrawTargetD2D::PushClipRect(const Rect &aRect) -{ - mCurrentClipMaskTexture = nullptr; - mCurrentClippedGeometry = nullptr; - if (!mTransform.IsRectilinear()) { - // Whoops, this isn't a rectangle in device space, Direct2D will not deal - // with this transform the way we want it to. - // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx - - RefPtr pathBuilder = CreatePathBuilder(); - pathBuilder->MoveTo(aRect.TopLeft()); - pathBuilder->LineTo(aRect.TopRight()); - pathBuilder->LineTo(aRect.BottomRight()); - pathBuilder->LineTo(aRect.BottomLeft()); - pathBuilder->Close(); - RefPtr path = pathBuilder->Finish(); - return PushClip(path); - } - - PushedClip clip; - Rect rect = mTransform.TransformBounds(aRect); - IntRect intRect; - clip.mIsPixelAligned = rect.ToIntRect(&intRect); - - // Do not store the transform, just store the device space rectangle directly. - clip.mBounds = D2DRect(rect); - - mPushedClips.push_back(clip); - - mRT->SetTransform(D2D1::IdentityMatrix()); - mTransformDirty = true; - - if (mClipsArePushed) { - mRT->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } -} - -void -DrawTargetD2D::PopClip() -{ - mCurrentClipMaskTexture = nullptr; - mCurrentClippedGeometry = nullptr; - if (mClipsArePushed) { - if (mPushedClips.back().mLayer) { - PopCachedLayer(mRT); - } else { - mRT->PopAxisAlignedClip(); - } - } - mPushedClips.pop_back(); -} - -already_AddRefed -DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - SurfaceFormat aFormat) const -{ - RefPtr newSurf = new SourceSurfaceD2D(); - - if (!newSurf->InitFromData(aData, aSize, aStride, aFormat, mRT)) { - return nullptr; - } - - return newSurf.forget(); -} - -already_AddRefed -DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const -{ - if (aSurface->GetType() == SurfaceType::D2D1_BITMAP || - aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) { - RefPtr surface(aSurface); - return surface.forget(); - } - - RefPtr data = aSurface->GetDataSurface(); - - DataSourceSurface::MappedSurface map; - if (!data->Map(DataSourceSurface::MapType::READ, &map)) { - return nullptr; - } - - RefPtr newSurf = new SourceSurfaceD2D(); - bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT); - - data->Unmap(); - - if (!success) { - return data.forget(); - } - return newSurf.forget(); -} - -already_AddRefed -DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const -{ - if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) { - gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface."; - return nullptr; - } - RefPtr newSurf = new SourceSurfaceD2D(); - - if (!newSurf->InitFromTexture(static_cast(aSurface.mSurface), - aSurface.mFormat, - mRT)) - { - gfxWarning() << *this << ": Failed to create SourceSurface from texture."; - return nullptr; - } - - return newSurf.forget(); -} - -already_AddRefed -DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const -{ - RefPtr newTarget = - new DrawTargetD2D(); - - if (!newTarget->Init(aSize, aFormat)) { - gfxDebug() << *this << ": Failed to create optimal draw target. Size: " << aSize; - return nullptr; - } - - return newTarget.forget(); -} - -already_AddRefed -DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const -{ - RefPtr path; - HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hexa(hr); - return nullptr; - } - - RefPtr sink; - hr = path->Open(getter_AddRefs(sink)); - if (FAILED(hr)) { - gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hexa(hr); - return nullptr; - } - - if (aFillRule == FillRule::FILL_WINDING) { - sink->SetFillMode(D2D1_FILL_MODE_WINDING); - } - - return MakeAndAddRef(sink, path, aFillRule, BackendType::DIRECT2D); -} - -already_AddRefed -DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const -{ - D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; - - for (uint32_t i = 0; i < aNumStops; i++) { - stops[i].position = rawStops[i].offset; - stops[i].color = D2DColor(rawStops[i].color); - } - - RefPtr stopCollection; - - HRESULT hr = - mRT->CreateGradientStopCollection(stops, aNumStops, - D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH), - getter_AddRefs(stopCollection)); - delete [] stops; - - if (FAILED(hr)) { - gfxWarning() << "Failed to create GradientStopCollection. Code: " << hexa(hr); - return nullptr; - } - - return MakeAndAddRef(stopCollection, Factory::GetDirect3D11Device()); -} - -already_AddRefed -DrawTargetD2D::CreateFilter(FilterType aType) -{ - RefPtr dc; - HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); - - if (SUCCEEDED(hr)) { - return FilterNodeD2D1::Create(dc, aType); - } - return FilterNodeSoftware::Create(aType); -} - -void* -DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType) -{ - if (aType != NativeSurfaceType::D3D10_TEXTURE) { - return nullptr; - } - - return mTexture; -} - -/* - * Public functions - */ -bool -DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat) -{ - HRESULT hr; - - mSize = aSize; - mFormat = aFormat; - - if (!Factory::GetDirect3D10Device()) { - gfxCriticalError() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)"; - return false; - } - mDevice = Factory::GetDirect3D10Device(); - - CD3D10_TEXTURE2D_DESC desc(DXGIFormat(aFormat), - mSize.width, - mSize.height, - 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); - - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hexa(hr); - return false; - } - - if (!InitD2DRenderTarget()) { - return false; - } - - mRT->Clear(D2D1::ColorF(0, 0)); - return true; -} - -bool -DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) -{ - HRESULT hr; - - mTexture = aTexture; - mFormat = aFormat; - - if (!mTexture) { - gfxCriticalError() << "No valid texture for Direct2D draw target initialization."; - return false; - } - - RefPtr device; - mTexture->GetDevice(getter_AddRefs(device)); - - hr = device->QueryInterface((ID3D10Device1**)getter_AddRefs(mDevice)); - - if (FAILED(hr)) { - gfxCriticalError() << "Failed to get D3D10 device from texture." << " format " << (int)aFormat; - return false; - } - - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - mSize.width = desc.Width; - mSize.height = desc.Height; - - return InitD2DRenderTarget(); -} - -// {0D398B49-AE7B-416F-B26D-EA3C137D1CF7} -static const GUID sPrivateDataD2D = -{ 0xd398b49, 0xae7b, 0x416f, { 0xb2, 0x6d, 0xea, 0x3c, 0x13, 0x7d, 0x1c, 0xf7 } }; - -bool -DrawTargetD2D::InitD3D10Data() -{ - HRESULT hr; - - UINT privateDataSize; - privateDataSize = sizeof(mPrivateData); - hr = mDevice->GetPrivateData(sPrivateDataD2D, &privateDataSize, &mPrivateData); - - if (SUCCEEDED(hr)) { - return true; - } - - mPrivateData = new PrivateD3D10DataD2D; - - decltype(D3D10CreateEffectFromMemory)* createD3DEffect; - HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll"); - createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*) - GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory"); - - hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, getter_AddRefs(mPrivateData->mEffect)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hexa(hr); - return false; - } - - privateDataSize = sizeof(mPrivateData); - mDevice->SetPrivateData(sPrivateDataD2D, privateDataSize, &mPrivateData); - - D3D10_INPUT_ELEMENT_DESC layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, - }; - D3D10_PASS_DESC passDesc; - - mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->GetDesc(&passDesc); - - hr = mDevice->CreateInputLayout(layout, - sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), - passDesc.pIAInputSignature, - passDesc.IAInputSignatureSize, - getter_AddRefs(mPrivateData->mInputLayout)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hexa(hr); - return false; - } - - D3D10_SUBRESOURCE_DATA data; - Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; - data.pSysMem = vertices; - CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); - - hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mPrivateData->mVB)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hexa(hr); - return false; - } - - return true; -} - -/* - * Private helpers - */ -uint32_t -DrawTargetD2D::GetByteSize() const -{ - return mSize.width * mSize.height * BytesPerPixel(mFormat); -} - -already_AddRefed -DrawTargetD2D::GetCachedLayer() -{ - RefPtr layer; - - if (mCurrentCachedLayer < 5) { - if (!mCachedLayers[mCurrentCachedLayer]) { - mRT->CreateLayer(getter_AddRefs(mCachedLayers[mCurrentCachedLayer])); - mVRAMUsageDT += GetByteSize(); - } - layer = mCachedLayers[mCurrentCachedLayer]; - } else { - mRT->CreateLayer(getter_AddRefs(layer)); - } - - mCurrentCachedLayer++; - return layer.forget(); -} - -void -DrawTargetD2D::PopCachedLayer(ID2D1RenderTarget *aRT) -{ - aRT->PopLayer(); - mCurrentCachedLayer--; -} - -bool -DrawTargetD2D::InitD2DRenderTarget() -{ - if (!factory()) { - gfxCriticalError() << "No valid D2D factory available."; - return false; - } - - mRT = CreateRTForTexture(mTexture, mFormat); - - if (!mRT) { - return false; - } - - mRT->BeginDraw(); - - if (mFormat == SurfaceFormat::B8G8R8X8) { - mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); - } - - mVRAMUsageDT += GetByteSize(); - - return InitD3D10Data(); -} - -void -DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT) -{ - if (!mClipsArePushed || aRT == mTempRT) { - if (mPushedClips.size()) { - // The transform of clips is relative to the world matrix, since we use the total - // transform for the clips, make the world matrix identity. - aRT->SetTransform(D2D1::IdentityMatrix()); - if (aRT == mRT) { - mTransformDirty = true; - mClipsArePushed = true; - } - PushClipsToRT(aRT); - } - } - FlushTransformToRT(); - MarkChanged(); - - if (aRT == mTempRT) { - mTempRT->SetTransform(D2DMatrix(mTransform)); - } -} - -void -DrawTargetD2D::MarkChanged() -{ - if (mSnapshot) { - if (mSnapshot->hasOneRef()) { - // Just destroy it, since no-one else knows about it. - mSnapshot = nullptr; - } else { - mSnapshot->DrawTargetWillChange(); - // The snapshot will no longer depend on this target. - MOZ_ASSERT(!mSnapshot); - } - } - if (mDependentTargets.size()) { - // Copy mDependentTargets since the Flush()es below will modify it. - TargetSet tmpTargets = mDependentTargets; - for (TargetSet::iterator iter = tmpTargets.begin(); - iter != tmpTargets.end(); iter++) { - (*iter)->Flush(); - } - // The Flush() should have broken all dependencies on this target. - MOZ_ASSERT(!mDependentTargets.size()); - } -} - -ID3D10BlendState* -DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) -{ - size_t operatorIndex = static_cast(aOperator); - if (mPrivateData->mBlendStates[operatorIndex]) { - return mPrivateData->mBlendStates[operatorIndex]; - } - - D3D10_BLEND_DESC desc; - - memset(&desc, 0, sizeof(D3D10_BLEND_DESC)); - - desc.AlphaToCoverageEnable = FALSE; - desc.BlendEnable[0] = TRUE; - desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - - switch (aOperator) { - case CompositionOp::OP_ADD: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; - break; - case CompositionOp::OP_IN: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; - break; - case CompositionOp::OP_OUT: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; - break; - case CompositionOp::OP_ATOP: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - break; - case CompositionOp::OP_DEST_IN: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; - break; - case CompositionOp::OP_DEST_OUT: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - break; - case CompositionOp::OP_DEST_ATOP: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; - break; - case CompositionOp::OP_DEST_OVER: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; - break; - case CompositionOp::OP_XOR: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - break; - case CompositionOp::OP_SOURCE: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; - break; - default: - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - } - - mDevice->CreateBlendState(&desc, getter_AddRefs(mPrivateData->mBlendStates[operatorIndex])); - - return mPrivateData->mBlendStates[operatorIndex]; -} - -/* This function prepares the temporary RT for drawing and returns it when a - * drawing operation other than OVER is required. - */ -ID2D1RenderTarget* -DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern) -{ - if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { - return mRT; - } - - PopAllClips(); - - if (aOperator > CompositionOp::OP_XOR) { - mRT->Flush(); - } - - if (mTempRT) { - mTempRT->Clear(D2D1::ColorF(0, 0)); - return mTempRT; - } - - EnsureViews(); - - if (!mRTView || !mSRView) { - gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER."; - return mRT; - } - - mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8); - - if (!mTempRT) { - return mRT; - } - - mVRAMUsageDT += GetByteSize(); - - mTempRT->BeginDraw(); - - mTempRT->Clear(D2D1::ColorF(0, 0)); - - return mTempRT; -} - -/* This function blends back the content of a drawing operation (drawn to an - * empty surface with OVER, so the surface now contains the source operation - * contents) to the rendertarget using the requested composition operation. - * In order to respect clip for operations which are unbound by their mask, - * the old content of the surface outside the clipped area may be blended back - * to the surface. - */ -void -DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds) -{ - if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { - return; - } - - if (!mTempRT) { - return; - } - - PopClipsFromRT(mTempRT); - - mRT->Flush(); - mTempRT->Flush(); - - AutoSaveRestoreClippedOut restoreClippedOut(this); - - bool needsWriteBack = - !IsOperatorBoundByMask(aOperator) && mPushedClips.size(); - - if (needsWriteBack) { - restoreClippedOut.Save(); - } - - ID3D10RenderTargetView *rtViews = mRTView; - mDevice->OMSetRenderTargets(1, &rtViews, nullptr); - - UINT stride = sizeof(Vertex); - UINT offset = 0; - ID3D10Buffer *buff = mPrivateData->mVB; - - mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); - mDevice->IASetInputLayout(mPrivateData->mInputLayout); - - D3D10_VIEWPORT viewport; - viewport.MaxDepth = 1; - viewport.MinDepth = 0; - viewport.Height = mSize.height; - viewport.Width = mSize.width; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - - RefPtr tmpTexture; - RefPtr mBckSRView; - - mDevice->RSSetViewports(1, &viewport); - mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); - - if (IsPatternSupportedByD2D(aPattern)) { - mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView); - - // Handle the case where we blend with the backdrop - if (aOperator > CompositionOp::OP_XOR) { - IntSize size = mSize; - SurfaceFormat format = mFormat; - - CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); - if (FAILED(hr)) { - gfxWarning() << "Failed to create temporary texture to hold surface data."; - return; - } - - mDevice->CopyResource(tmpTexture, mTexture); - if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); - return; - } - - DrawTargetD2D::Flush(); - - hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, getter_AddRefs(mBckSRView)); - - if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); - return; - } - - unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR; - mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView); - mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop); - - if (aOperator > CompositionOp::OP_EXCLUSION) - mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")-> - GetPassByIndex(0)->Apply(0); - else if (aOperator > CompositionOp::OP_COLOR_DODGE) - mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")-> - GetPassByIndex(0)->Apply(0); - else - mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_1")-> - GetPassByIndex(0)->Apply(0); - } - else { - mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0); - } - - } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { - const RadialGradientPattern *pat = static_cast(&aPattern); - - if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) { - // Draw nothing! - return; - } - - mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(mSRView); - - SetupEffectForRadialGradient(pat); - } - - mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); - - SetScissorToRect(nullptr); - mDevice->Draw(4, 0); -} - -static D2D1_RECT_F -IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) -{ - D2D1_RECT_F result; - result.left = max(aRect1.left, aRect2.left); - result.top = max(aRect1.top, aRect2.top); - result.right = min(aRect1.right, aRect2.right); - result.bottom = min(aRect1.bottom, aRect2.bottom); - - result.right = max(result.right, result.left); - result.bottom = max(result.bottom, result.top); - - return result; -} - -bool -DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) -{ - if (!mPushedClips.size()) { - return false; - } - - std::vector::iterator iter = mPushedClips.begin(); - if (iter->mPath) { - return false; - } - aClipRect = iter->mBounds; - aIsPixelAligned = iter->mIsPixelAligned; - - iter++; - for (;iter != mPushedClips.end(); iter++) { - if (iter->mPath) { - return false; - } - aClipRect = IntersectRect(aClipRect, iter->mBounds); - if (!iter->mIsPixelAligned) { - aIsPixelAligned = false; - } - } - return true; -} - -already_AddRefed -DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds) -{ - if (mCurrentClippedGeometry) { - *aClipBounds = mCurrentClipBounds; - RefPtr clippedGeometry(mCurrentClippedGeometry); - return clippedGeometry.forget(); - } - - mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize); - - // if pathGeom is null then pathRect represents the path. - RefPtr pathGeom; - D2D1_RECT_F pathRect; - bool pathRectIsAxisAligned = false; - std::vector::iterator iter = mPushedClips.begin(); - - if (iter->mPath) { - pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); - } else { - pathRect = iter->mBounds; - pathRectIsAxisAligned = iter->mIsPixelAligned; - } - - iter++; - for (;iter != mPushedClips.end(); iter++) { - // Do nothing but add it to the current clip bounds. - if (!iter->mPath && iter->mIsPixelAligned) { - mCurrentClipBounds.IntersectRect(mCurrentClipBounds, - IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top), - int32_t(iter->mBounds.right - iter->mBounds.left), - int32_t(iter->mBounds.bottom - iter->mBounds.top))); - continue; - } - - if (!pathGeom) { - if (pathRectIsAxisAligned) { - mCurrentClipBounds.IntersectRect(mCurrentClipBounds, - IntRect(int32_t(pathRect.left), int32_t(pathRect.top), - int32_t(pathRect.right - pathRect.left), - int32_t(pathRect.bottom - pathRect.top))); - } - if (iter->mPath) { - // See if pathRect needs to go into the path geometry. - if (!pathRectIsAxisAligned) { - pathGeom = ConvertRectToGeometry(pathRect); - } else { - pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); - } - } else { - pathRect = IntersectRect(pathRect, iter->mBounds); - pathRectIsAxisAligned = false; - continue; - } - } - - RefPtr newGeom; - factory()->CreatePathGeometry(getter_AddRefs(newGeom)); - - RefPtr currentSink; - newGeom->Open(getter_AddRefs(currentSink)); - - if (iter->mPath) { - pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT, - iter->mTransform, currentSink); - } else { - RefPtr rectGeom = ConvertRectToGeometry(iter->mBounds); - pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT, - D2D1::IdentityMatrix(), currentSink); - } - - currentSink->Close(); - - pathGeom = newGeom.forget(); - } - - // For now we need mCurrentClippedGeometry to always be non-nullptr. This - // method might seem a little strange but it is just fine, if pathGeom is - // nullptr pathRect will always still contain 1 clip unaccounted for - // regardless of mCurrentClipBounds. - if (!pathGeom) { - pathGeom = ConvertRectToGeometry(pathRect); - } - mCurrentClippedGeometry = pathGeom.forget(); - *aClipBounds = mCurrentClipBounds; - RefPtr clippedGeometry(mCurrentClippedGeometry); - return clippedGeometry.forget(); -} - -already_AddRefed -DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) -{ - HRESULT hr; - - RefPtr surface; - RefPtr rt; - - hr = aTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surface)); - - if (FAILED(hr)) { - gfxCriticalError() << "Failed to QI texture to surface. Code: " << hexa(hr); - return nullptr; - } - - D3D10_TEXTURE2D_DESC desc; - aTexture->GetDesc(&desc); - - D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; - - if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) { - alphaMode = D2D1_ALPHA_MODE_IGNORE; - } - - D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode)); - hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, getter_AddRefs(rt)); - - if (FAILED(hr)) { - gfxCriticalError() << "Failed to create D2D render target for texture. Code: " - << hexa(hr) << " " << mSize << " Format: " << uint32_t(aFormat); - return nullptr; - } - - return rt.forget(); -} - -void -DrawTargetD2D::EnsureViews() -{ - if (mTempTexture && mSRView && mRTView) { - return; - } - - HRESULT hr; - - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - mSize.width, - mSize.height, - 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTempTexture)); - - if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: " - << mSize << " Code: " << hexa(hr); - return; - } - - hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, getter_AddRefs(mSRView)); - - if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); - return; - } - - hr = mDevice->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(mRTView)); - - if (FAILED(hr)) { - gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hexa(hr); - } -} - -void -DrawTargetD2D::PopAllClips() -{ - if (mClipsArePushed) { - PopClipsFromRT(mRT); - - mClipsArePushed = false; - } -} - -void -DrawTargetD2D::PushClipsToRT(ID2D1RenderTarget *aRT) -{ - for (std::vector::iterator iter = mPushedClips.begin(); - iter != mPushedClips.end(); iter++) { - if (iter->mLayer) { - PushD2DLayer(aRT, iter->mPath->mGeometry, iter->mLayer, iter->mTransform); - } else { - aRT->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - } -} - -void -DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT) -{ - for (int i = mPushedClips.size() - 1; i >= 0; i--) { - if (mPushedClips[i].mLayer) { - aRT->PopLayer(); - } else { - aRT->PopAxisAlignedClip(); - } - } -} - -void -DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds) -{ - if (mCurrentClipMaskTexture || mPushedClips.empty()) { - *aBounds = mCurrentClipBounds; - return; - } - - RefPtr geometry = GetClippedGeometry(aBounds); - - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM, - mSize.width, - mSize.height, - 1, 1); - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mCurrentClipMaskTexture)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create texture for ClipMask!"; - return; - } - - RefPtr rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8); - - if (!rt) { - gfxWarning() << "Failed to create RT for ClipMask!"; - return; - } - - RefPtr brush; - rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush)); - - rt->BeginDraw(); - rt->Clear(D2D1::ColorF(0, 0)); - rt->FillGeometry(geometry, brush); - rt->EndDraw(); -} - -bool -DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont, - const GlyphBuffer &aBuffer, - const Color &aColor, - IDWriteRenderingParams *aParams, - const DrawOptions &aOptions) -{ - HRESULT hr; - - RefPtr params; - - if (aParams) { - params = aParams; - } else { - mRT->GetTextRenderingParams(getter_AddRefs(params)); - } - - DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; - if (params) { - hr = aFont->mFontFace->GetRecommendedRenderingMode( - (FLOAT)aFont->GetSize(), - 1.0f, - DWRITE_MEASURING_MODE_NATURAL, - params, - &renderMode); - if (FAILED(hr)) { - // this probably never happens, but let's play it safe - renderMode = DWRITE_RENDERING_MODE_DEFAULT; - } - } - - // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. - switch (renderMode) { - case DWRITE_RENDERING_MODE_ALIASED: - // ClearType texture creation will fail in this mode, so bail out - return false; - case DWRITE_RENDERING_MODE_DEFAULT: - // As per DWRITE_RENDERING_MODE documentation, pick Natural for font - // sizes under 16 ppem - if (aFont->GetSize() < 16.0f) { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; - } else { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; - } - break; - case DWRITE_RENDERING_MODE_OUTLINE: - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; - break; - default: - break; - } - - DWRITE_MEASURING_MODE measureMode = - renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : - renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : - DWRITE_MEASURING_MODE_NATURAL; - - DWRITE_MATRIX mat = DWriteMatrixFromMatrix(mTransform); - - AutoDWriteGlyphRun autoRun; - DWriteGlyphRunFromGlyphs(aBuffer, aFont, &autoRun); - - RefPtr analysis; - hr = GetDWriteFactory()->CreateGlyphRunAnalysis(&autoRun, 1.0f, &mat, - renderMode, measureMode, 0, 0, getter_AddRefs(analysis)); - - if (FAILED(hr)) { - return false; - } - - RECT bounds; - hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds); - - if (bounds.bottom <= bounds.top || bounds.right <= bounds.left) { - // DWrite seems to do this sometimes. I'm not 100% sure why. See bug 758980. - gfxDebug() << "Empty alpha texture bounds! Falling back to regular drawing."; - return false; - } - IntRect rectBounds(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top); - IntRect surfBounds(IntPoint(0, 0), mSize); - - rectBounds.IntersectRect(rectBounds, surfBounds); - - if (rectBounds.IsEmpty()) { - // Nothing to do. - return true; - } - - RefPtr tex = CreateTextureForAnalysis(analysis, rectBounds); - - if (!tex) { - return false; - } - - RefPtr srView; - hr = mDevice->CreateShaderResourceView(tex, nullptr, getter_AddRefs(srView)); - - if (FAILED(hr)) { - return false; - } - - MarkChanged(); - - // Prepare our background texture for drawing. - PopAllClips(); - mRT->Flush(); - - SetupStateForRendering(); - - ID3D10EffectTechnique *technique = mPrivateData->mEffect->GetTechniqueByName("SampleTextTexture"); - - mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((Float(rectBounds.x) / mSize.width) * 2.0f), - 1.0f - (Float(rectBounds.y) / mSize.height * 2.0f), - (Float(rectBounds.width) / mSize.width) * 2.0f, - (-Float(rectBounds.height) / mSize.height) * 2.0f)); - mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); - FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a }; - mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()-> - SetFloatVector(color); - - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); - - bool isMasking = false; - - IntRect clipBoundsStorage; - IntRect *clipBounds = nullptr; - - if (!mPushedClips.empty()) { - clipBounds = &clipBoundsStorage; - RefPtr geom = GetClippedGeometry(clipBounds); - - RefPtr rectGeom; - factory()->CreateRectangleGeometry(D2D1::RectF(Float(rectBounds.x), - Float(rectBounds.y), - Float(rectBounds.width + rectBounds.x), - Float(rectBounds.height + rectBounds.y)), - getter_AddRefs(rectGeom)); - - D2D1_GEOMETRY_RELATION relation; - if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) || - relation != D2D1_GEOMETRY_RELATION_CONTAINS ) { - isMasking = true; - } - } - - if (isMasking) { - clipBounds = &clipBoundsStorage; - EnsureClipMaskTexture(clipBounds); - - RefPtr srViewMask; - hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, getter_AddRefs(srViewMask)); - - if (FAILED(hr)) { - return false; - } - - mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(srViewMask); - - mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(Float(rectBounds.x) / mSize.width, Float(rectBounds.y) / mSize.height, - Float(rectBounds.width) / mSize.width, Float(rectBounds.height) / mSize.height)); - - technique->GetPassByIndex(1)->Apply(0); - } else { - technique->GetPassByIndex(0)->Apply(0); - } - - RefPtr rtView; - ID3D10RenderTargetView *rtViews; - mDevice->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(rtView)); - - rtViews = rtView; - mDevice->OMSetRenderTargets(1, &rtViews, nullptr); - SetScissorToRect(clipBounds); - mDevice->Draw(4, 0); - return true; -} - -already_AddRefed -DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) -{ - if (!IsPatternSupportedByD2D(aPattern)) { - RefPtr colBrush; - mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), getter_AddRefs(colBrush)); - return colBrush.forget(); - } - - if (aPattern.GetType() == PatternType::COLOR) { - RefPtr colBrush; - Color color = static_cast(&aPattern)->mColor; - mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, - color.b, color.a), - D2D1::BrushProperties(aAlpha), - getter_AddRefs(colBrush)); - return colBrush.forget(); - } - if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { - RefPtr gradBrush; - const LinearGradientPattern *pat = - static_cast(&aPattern); - - GradientStopsD2D *stops = static_cast(pat->mStops.get()); - - if (!stops) { - gfxDebug() << "No stops specified for gradient pattern."; - return nullptr; - } - - if (pat->mBegin == pat->mEnd) { - RefPtr colBrush; - uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); - vector d2dStops(stopCount); - stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); - mRT->CreateSolidColorBrush(d2dStops.back().color, - D2D1::BrushProperties(aAlpha), - getter_AddRefs(colBrush)); - return colBrush.forget(); - } - - mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), - D2DPoint(pat->mEnd)), - D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), - stops->mStopCollection, - getter_AddRefs(gradBrush)); - return gradBrush.forget(); - } - if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { - RefPtr gradBrush; - const RadialGradientPattern *pat = - static_cast(&aPattern); - - GradientStopsD2D *stops = static_cast(pat->mStops.get()); - - if (!stops) { - gfxDebug() << "No stops specified for gradient pattern."; - return nullptr; - } - - // This will not be a complex radial gradient brush. - mRT->CreateRadialGradientBrush( - D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), - D2DPoint(pat->mCenter1 - pat->mCenter2), - pat->mRadius2, pat->mRadius2), - D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), - stops->mStopCollection, - getter_AddRefs(gradBrush)); - - return gradBrush.forget(); - } - if (aPattern.GetType() == PatternType::SURFACE) { - RefPtr bmBrush; - const SurfacePattern *pat = - static_cast(&aPattern); - - if (!pat->mSurface) { - gfxDebug() << "No source surface specified for surface pattern"; - return nullptr; - } - - RefPtr bitmap; - - Matrix mat = pat->mMatrix; - - RefPtr source = pat->mSurface; - - if (!pat->mSamplingRect.IsEmpty() && - (source->GetType() == SurfaceType::D2D1_BITMAP || - source->GetType() == SurfaceType::D2D1_DRAWTARGET)) { - IntRect samplingRect = pat->mSamplingRect; - - RefPtr dt = new DrawTargetD2D(); - if (!dt->Init(samplingRect.Size(), - source->GetFormat())) { - // FIXME: Uncomment assertion, bug 1068195 - // MOZ_ASSERT(false, "Invalid sampling rect size!"); - return nullptr; - } - - dt->CopySurface(source, samplingRect, IntPoint()); - source = dt->Snapshot(); - - mat.PreTranslate(samplingRect.x, samplingRect.y); - } - - switch (source->GetType()) { - case SurfaceType::D2D1_BITMAP: - { - SourceSurfaceD2D *surf = static_cast(source.get()); - - bitmap = surf->mBitmap; - - if (!bitmap) { - return nullptr; - } - } - break; - case SurfaceType::D2D1_DRAWTARGET: - { - SourceSurfaceD2DTarget *surf = - static_cast(source.get()); - bitmap = surf->GetBitmap(mRT); - AddDependencyOnSource(surf); - } - break; - default: - { - RefPtr dataSurf = source->GetDataSurface(); - if (!dataSurf) { - gfxWarning() << "Invalid surface type."; - return nullptr; - } - - IntRect sourceRect = pat->mSamplingRect; - if (sourceRect.IsEmpty()) { - sourceRect = IntRect(0, 0, source->GetSize().width, source->GetSize().height); - } - - bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT, &sourceRect); - if (!bitmap) { - RefPtr colBrush; - mRT->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(colBrush)); - return colBrush.forget(); - } - } - break; - } - - D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS); - D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS); - HRESULT hr = mRT->CreateBitmapBrush(bitmap, - D2D1::BitmapBrushProperties(xRepeat, - yRepeat, - D2DFilter(pat->mFilter)), - D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), - getter_AddRefs(bmBrush)); - if (FAILED(hr)) { - gfxCriticalError() << "[D2D] 1CreateBitmapBrush failure: " << hexa(hr); - return nullptr; - } - return bmBrush.forget(); - } - - gfxWarning() << "Invalid pattern type detected."; - return nullptr; -} - -already_AddRefed -DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops) -{ - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 4096, 1, 1, 1); - - std::vector rawStops; - rawStops.resize(aStops->mStopCollection->GetGradientStopCount()); - aStops->mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size()); - - std::vector textureData; - textureData.resize(4096 * 4); - unsigned char *texData = &textureData.front(); - - float prevColorPos = 0; - float nextColorPos = 1.0f; - D2D1_COLOR_F prevColor = rawStops[0].color; - D2D1_COLOR_F nextColor = prevColor; - - if (rawStops.size() >= 2) { - nextColor = rawStops[1].color; - nextColorPos = rawStops[1].position; - } - - uint32_t stopPosition = 2; - - // Not the most optimized way but this will do for now. - for (int i = 0; i < 4096; i++) { - // The 4095 seems a little counter intuitive, but we want the gradient - // color at offset 0 at the first pixel, and at offset 1.0f at the last - // pixel. - float pos = float(i) / 4095; - - while (pos > nextColorPos) { - prevColor = nextColor; - prevColorPos = nextColorPos; - if (rawStops.size() > stopPosition) { - nextColor = rawStops[stopPosition].color; - nextColorPos = rawStops[stopPosition++].position; - } else { - nextColorPos = 1.0f; - } - } - - float interp; - - if (nextColorPos != prevColorPos) { - interp = (pos - prevColorPos) / (nextColorPos - prevColorPos); - } else { - interp = 0; - } - - Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp, - prevColor.g + (nextColor.g - prevColor.g) * interp, - prevColor.b + (nextColor.b - prevColor.b) * interp, - prevColor.a + (nextColor.a - prevColor.a) * interp); - - texData[i * 4] = (char)(255.0f * newColor.b); - texData[i * 4 + 1] = (char)(255.0f * newColor.g); - texData[i * 4 + 2] = (char)(255.0f * newColor.r); - texData[i * 4 + 3] = (char)(255.0f * newColor.a); - } - - D3D10_SUBRESOURCE_DATA data; - data.pSysMem = &textureData.front(); - data.SysMemPitch = 4096 * 4; - - RefPtr tex; - mDevice->CreateTexture2D(&desc, &data, getter_AddRefs(tex)); - - return tex.forget(); -} - -already_AddRefed -DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds) -{ - HRESULT hr; - - uint32_t bufferSize = aBounds.width * aBounds.height * 3; - - RECT bounds; - bounds.left = aBounds.x; - bounds.top = aBounds.y; - bounds.right = aBounds.x + aBounds.width; - bounds.bottom = aBounds.y + aBounds.height; - - // Add one byte so we can safely read a 32-bit int when copying the last - // 3 bytes. - BYTE *texture = new BYTE[bufferSize + 1]; - hr = aAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize); - - if (FAILED(hr)) { - delete [] texture; - return nullptr; - } - - int alignedBufferSize = aBounds.width * aBounds.height * 4; - - // Create a one-off immutable texture from system memory. - BYTE *alignedTextureData = new BYTE[alignedBufferSize]; - for (int y = 0; y < aBounds.height; y++) { - for (int x = 0; x < aBounds.width; x++) { - // Copy 3 Bpp source to 4 Bpp destination memory used for - // texture creation. D3D10 has no 3 Bpp texture format we can - // use. - // - // Since we don't care what ends up in the alpha pixel of the - // destination, therefor we can simply copy a normal 32 bit - // integer each time, filling the alpha pixel of the destination - // with the first subpixel of the next pixel from the source. - *((int*)(alignedTextureData + (y * aBounds.width + x) * 4)) = - *((int*)(texture + (y * aBounds.width + x) * 3)); - } - } - - D3D10_SUBRESOURCE_DATA data; - - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - aBounds.width, aBounds.height, - 1, 1); - desc.Usage = D3D10_USAGE_IMMUTABLE; - - data.SysMemPitch = aBounds.width * 4; - data.pSysMem = alignedTextureData; - - RefPtr tex; - hr = mDevice->CreateTexture2D(&desc, &data, getter_AddRefs(tex)); - - delete [] alignedTextureData; - delete [] texture; - - if (FAILED(hr)) { - return nullptr; - } - - return tex.forget(); -} - -void -DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern) -{ - mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0); - mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); - - float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 }; - mPrivateData->mEffect->GetVariableByName("dimensions")->AsVector()-> - SetFloatVector(dimensions); - - const GradientStopsD2D *stops = - static_cast(aPattern->mStops.get()); - - RefPtr tex = CreateGradientTexture(stops); - - RefPtr srView; - mDevice->CreateShaderResourceView(tex, nullptr, getter_AddRefs(srView)); - - mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); - - Point dc = aPattern->mCenter2 - aPattern->mCenter1; - float dr = aPattern->mRadius2 - aPattern->mRadius1; - - float diffv[] = { dc.x, dc.y, dr, 0 }; - mPrivateData->mEffect->GetVariableByName("diff")->AsVector()-> - SetFloatVector(diffv); - - float center1[] = { aPattern->mCenter1.x, aPattern->mCenter1.y, dr, 0 }; - mPrivateData->mEffect->GetVariableByName("center1")->AsVector()-> - SetFloatVector(center1); - - mPrivateData->mEffect->GetVariableByName("radius1")->AsScalar()-> - SetFloat(aPattern->mRadius1); - mPrivateData->mEffect->GetVariableByName("sq_radius1")->AsScalar()-> - SetFloat(pow(aPattern->mRadius1, 2)); - - Matrix invTransform = mTransform; - - if (!invTransform.Invert()) { - // Bail if the matrix is singular. - return; - } - float matrix[] = { invTransform._11, invTransform._12, 0, 0, - invTransform._21, invTransform._22, 0, 0, - invTransform._31, invTransform._32, 1.0f, 0, - 0, 0, 0, 1.0f }; - - mPrivateData->mEffect->GetVariableByName("DeviceSpaceToUserSpace")-> - AsMatrix()->SetMatrix(matrix); - - float A = dc.x * dc.x + dc.y * dc.y - dr * dr; - - uint32_t offset = 0; - switch (stops->mStopCollection->GetExtendMode()) { - case D2D1_EXTEND_MODE_WRAP: - offset = 1; - break; - case D2D1_EXTEND_MODE_MIRROR: - offset = 2; - break; - default: - gfxWarning() << "This shouldn't happen! Invalid extend mode for gradient stops."; - } - - if (A == 0) { - mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> - GetPassByIndex(offset * 2 + 1)->Apply(0); - } else { - mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A); - mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> - GetPassByIndex(offset * 2)->Apply(0); - } -} - -void -DrawTargetD2D::SetupStateForRendering() -{ - UINT stride = sizeof(Vertex); - UINT offset = 0; - ID3D10Buffer *buff = mPrivateData->mVB; - - mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); - mDevice->IASetInputLayout(mPrivateData->mInputLayout); - - D3D10_VIEWPORT viewport; - viewport.MaxDepth = 1; - viewport.MinDepth = 0; - viewport.Height = mSize.height; - viewport.Width = mSize.width; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - - mDevice->RSSetViewports(1, &viewport); -} - -ID2D1Factory* -DrawTargetD2D::factory() -{ - if (mFactory) { - return mFactory; - } - - D2D1CreateFactoryFunc createD2DFactory; - HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); - createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(d2dModule, "D2D1CreateFactory"); - - if (!createD2DFactory) { - gfxWarning() << "Failed to locate D2D1CreateFactory function."; - return nullptr; - } - - D2D1_FACTORY_OPTIONS options; -#ifdef _DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; - - HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&mFactory); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create Direct2D factory."; - } - - RefPtr factoryD2D1; - hr = mFactory->QueryInterface((ID2D1Factory1**)getter_AddRefs(factoryD2D1)); - if (SUCCEEDED(hr)) { - ExtendInputEffectD2D1::Register(factoryD2D1); - } - - return mFactory; -} - -void -DrawTargetD2D::CleanupD2D() -{ - if (mFactory) { - RefPtr factoryD2D1; - HRESULT hr = mFactory->QueryInterface((ID2D1Factory1**)getter_AddRefs(factoryD2D1)); - if (SUCCEEDED(hr)) { - ExtendInputEffectD2D1::Unregister(factoryD2D1); - } - - mFactory->Release(); - mFactory = nullptr; - } -} - -IDWriteFactory* -DrawTargetD2D::GetDWriteFactory() -{ - if (mDWriteFactory) { - return mDWriteFactory; - } - - decltype(DWriteCreateFactory)* createDWriteFactory; - HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); - createDWriteFactory = (decltype(DWriteCreateFactory)*) - GetProcAddress(dwriteModule, "DWriteCreateFactory"); - - if (!createDWriteFactory) { - gfxWarning() << "Failed to locate DWriteCreateFactory function."; - return nullptr; - } - - HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), - reinterpret_cast(&mDWriteFactory)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create DWrite Factory."; - } - - return mDWriteFactory; -} - -void -DrawTargetD2D::SetScissorToRect(IntRect *aRect) -{ - D3D10_RECT rect; - if (aRect) { - rect.left = aRect->x; - rect.right = aRect->XMost(); - rect.top = aRect->y; - rect.bottom = aRect->YMost(); - } else { - rect.left = rect.top = INT32_MIN; - rect.right = rect.bottom = INT32_MAX; - } - - mDevice->RSSetScissorRects(1, &rect); -} - -void -DrawTargetD2D::PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform) -{ - D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; - D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE; - - if (aRT->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { - options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; - options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; - } - - RefPtr dc; - HRESULT hr = aRT->QueryInterface(IID_ID2D1DeviceContext, (void**)((ID2D1DeviceContext**)getter_AddRefs(dc))); - - if (FAILED(hr)) { - aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), aGeometry, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, - 1.0, nullptr, options), - aLayer); - } else { - dc->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, - 1.0, nullptr, options1), - aLayer); - } -} - -} -} diff --git a/gfx/2d/DrawTargetD2D.h b/gfx/2d/DrawTargetD2D.h deleted file mode 100644 index 7c0a5b403e8..00000000000 --- a/gfx/2d/DrawTargetD2D.h +++ /dev/null @@ -1,290 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DRAWTARGETD2D_H_ -#define MOZILLA_GFX_DRAWTARGETD2D_H_ - -#include "2D.h" -#include "PathD2D.h" -#include -#include "HelpersD2D.h" - -#include -#include - -#include - -struct IDWriteFactory; - -namespace mozilla { -namespace gfx { - -class SourceSurfaceD2DTarget; -class SourceSurfaceD2D; -class GradientStopsD2D; -class ScaledFontDWrite; - -const int32_t kLayerCacheSize = 5; - -struct PrivateD3D10DataD2D -{ - RefPtr mEffect; - RefPtr mInputLayout; - RefPtr mVB; - RefPtr mBlendStates[size_t(CompositionOp::OP_COUNT)]; -}; - -class DrawTargetD2D : public DrawTarget -{ -public: - MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D, override) - DrawTargetD2D(); - virtual ~DrawTargetD2D(); - - virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; } - virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D; } - virtual already_AddRefed Snapshot() override; - virtual IntSize GetSize() override { return mSize; } - - virtual void Flush() override; - virtual void DrawSurface(SourceSurface *aSurface, - const Rect &aDest, - const Rect &aSource, - const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), - const DrawOptions &aOptions = DrawOptions()) override; - virtual void DrawFilter(FilterNode *aNode, - const Rect &aSourceRect, - const Point &aDestPoint, - const DrawOptions &aOptions = DrawOptions()) override; - virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, - const Point &aDest, - const Color &aColor, - const Point &aOffset, - Float aSigma, - CompositionOp aOperator) override; - virtual void ClearRect(const Rect &aRect) override; - virtual void MaskSurface(const Pattern &aSource, - SourceSurface *aMask, - Point aOffset, - const DrawOptions &aOptions = DrawOptions()) override; - - - virtual void CopySurface(SourceSurface *aSurface, - const IntRect &aSourceRect, - const IntPoint &aDestination) override; - - virtual void FillRect(const Rect &aRect, - const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()) override; - virtual void StrokeRect(const Rect &aRect, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()) override; - virtual void StrokeLine(const Point &aStart, - const Point &aEnd, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()) override; - virtual void Stroke(const Path *aPath, - const Pattern &aPattern, - const StrokeOptions &aStrokeOptions = StrokeOptions(), - const DrawOptions &aOptions = DrawOptions()) override; - virtual void Fill(const Path *aPath, - const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions()) override; - virtual void FillGlyphs(ScaledFont *aFont, - const GlyphBuffer &aBuffer, - const Pattern &aPattern, - const DrawOptions &aOptions = DrawOptions(), - const GlyphRenderingOptions *aRenderingOptions = nullptr) override; - virtual void Mask(const Pattern &aSource, - const Pattern &aMask, - const DrawOptions &aOptions = DrawOptions()) override; - virtual void PushClip(const Path *aPath) override; - virtual void PushClipRect(const Rect &aRect) override; - virtual void PopClip() override; - - virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - SurfaceFormat aFormat) const override; - virtual already_AddRefed OptimizeSourceSurface(SourceSurface *aSurface) const override; - - virtual already_AddRefed - CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override; - - virtual already_AddRefed - CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; - - virtual already_AddRefed CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; - - virtual already_AddRefed - CreateGradientStops(GradientStop *aStops, - uint32_t aNumStops, - ExtendMode aExtendMode = ExtendMode::CLAMP) const override; - - virtual already_AddRefed CreateFilter(FilterType aType) override; - - virtual bool SupportsRegionClipping() const override { return false; } - - virtual void *GetNativeSurface(NativeSurfaceType aType) override; - - bool Init(const IntSize &aSize, SurfaceFormat aFormat); - bool Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); - bool InitD3D10Data(); - uint32_t GetByteSize() const; - already_AddRefed GetCachedLayer(); - void PopCachedLayer(ID2D1RenderTarget *aRT); - - already_AddRefed GetImageForSurface(SourceSurface *aSurface); - - static ID2D1Factory *factory(); - static void CleanupD2D(); - static IDWriteFactory *GetDWriteFactory(); - ID2D1RenderTarget *GetRT() { return mRT; } - - static uint32_t GetMaxSurfaceSize() { - return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; - } - - operator std::string() const { - std::stringstream stream; - stream << "DrawTargetD2D(" << this << ")"; - return stream.str(); - } - - static uint64_t mVRAMUsageDT; - static uint64_t mVRAMUsageSS; - -private: - already_AddRefed - GetBitmapForSurface(SourceSurface *aSurface, - Rect &aSource); - friend class AutoSaveRestoreClippedOut; - friend class SourceSurfaceD2DTarget; - - typedef std::unordered_set TargetSet; - - bool InitD2DRenderTarget(); - void PrepareForDrawing(ID2D1RenderTarget *aRT); - - // This function will mark the surface as changing, and make sure any - // copy-on-write snapshots are notified. - void MarkChanged(); - void FlushTransformToRT() { - if (mTransformDirty) { - mRT->SetTransform(D2DMatrix(mTransform)); - mTransformDirty = false; - } - } - void AddDependencyOnSource(SourceSurfaceD2DTarget* aSource); - - ID3D10BlendState *GetBlendStateForOperator(CompositionOp aOperator); - ID2D1RenderTarget *GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern); - void FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds); void EnsureViews(); - void PopAllClips(); - void PushClipsToRT(ID2D1RenderTarget *aRT); - void PopClipsFromRT(ID2D1RenderTarget *aRT); - - // This function ensures mCurrentClipMaskTexture contains a texture containing - // a mask corresponding with the current DrawTarget clip. See - // GetClippedGeometry for a description of aClipBounds. - void EnsureClipMaskTexture(IntRect *aClipBounds); - - bool FillGlyphsManual(ScaledFontDWrite *aFont, - const GlyphBuffer &aBuffer, - const Color &aColor, - IDWriteRenderingParams *aParams, - const DrawOptions &aOptions = DrawOptions()); - - already_AddRefed CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); - - // This returns the clipped geometry, in addition it returns aClipBounds which - // represents the intersection of all pixel-aligned rectangular clips that - // are currently set. The returned clipped geometry must be clipped by these - // bounds to correctly reflect the total clip. This is in device space. - already_AddRefed GetClippedGeometry(IntRect *aClipBounds); - - bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned); - - already_AddRefed CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f); - - already_AddRefed CreateGradientTexture(const GradientStopsD2D *aStops); - already_AddRefed CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds); - - void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern); - void SetupStateForRendering(); - - // Set the scissor rect to a certain IntRects, resets the scissor rect to - // surface bounds when nullptr is specified. - void SetScissorToRect(IntRect *aRect); - - void PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform); - - static const uint32_t test = 4; - - IntSize mSize; - - RefPtr mDevice; - RefPtr mTexture; - RefPtr mCurrentClipMaskTexture; - RefPtr mCurrentClippedGeometry; - // This is only valid if mCurrentClippedGeometry is non-null. And will - // only be the intersection of all pixel-aligned retangular clips. This is in - // device space. - IntRect mCurrentClipBounds; - mutable RefPtr mRT; - - // We store this to prevent excessive SetTextRenderingParams calls. - RefPtr mTextRenderingParams; - - // Temporary texture and render target used for supporting alternative operators. - RefPtr mTempTexture; - RefPtr mRTView; - RefPtr mSRView; - RefPtr mTempRT; - RefPtr mTempRTView; - - // List of pushed clips. - struct PushedClip - { - RefPtr mLayer; - D2D1_RECT_F mBounds; - union { - // If mPath is non-nullptr, the mTransform member will be used, otherwise - // the mIsPixelAligned member is valid. - D2D1_MATRIX_3X2_F mTransform; - bool mIsPixelAligned; - }; - RefPtr mPath; - }; - std::vector mPushedClips; - - // We cache ID2D1Layer objects as it causes D2D to keep around textures that - // serve as the temporary surfaces for these operations. As texture creation - // is quite expensive this considerably improved performance. - // Careful here, RAII will not ensure destruction of the RefPtrs. - RefPtr mCachedLayers[kLayerCacheSize]; - uint32_t mCurrentCachedLayer; - - // The latest snapshot of this surface. This needs to be told when this - // target is modified. We keep it alive as a cache. - RefPtr mSnapshot; - // A list of targets we need to flush when we're modified. - TargetSet mDependentTargets; - // A list of targets which have this object in their mDependentTargets set - TargetSet mDependingOnTargets; - - // True of the current clip stack is pushed to the main RT. - bool mClipsArePushed; - PrivateD3D10DataD2D *mPrivateData; - static ID2D1Factory *mFactory; - static IDWriteFactory *mDWriteFactory; -}; - -} -} - -#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */ diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp index cb503ff493c..8aa23c6b80a 100644 --- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -5,24 +5,32 @@ #include #include "DrawTargetD2D1.h" -#include "DrawTargetD2D.h" #include "FilterNodeSoftware.h" #include "GradientStopsD2D.h" #include "SourceSurfaceD2D1.h" -#include "SourceSurfaceD2D.h" #include "RadialGradientEffectD2D1.h" #include "HelpersD2D.h" #include "FilterNodeD2D1.h" +#include "ExtendInputEffectD2D1.h" #include "Tools.h" using namespace std; +// decltype is not usable for overloaded functions. +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + namespace mozilla { namespace gfx { uint64_t DrawTargetD2D1::mVRAMUsageDT; uint64_t DrawTargetD2D1::mVRAMUsageSS; +IDWriteFactory *DrawTargetD2D1::mDWriteFactory; ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; ID2D1Factory1 *D2DFactory1() @@ -604,7 +612,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, DWRITE_MEASURING_MODE_NATURAL, &userRect); RefPtr path; - D2DFactory()->CreatePathGeometry(getter_AddRefs(path)); + factory()->CreatePathGeometry(getter_AddRefs(path)); RefPtr sink; path->Open(getter_AddRefs(sink)); AddRectToSink(sink, userRect); @@ -1060,27 +1068,79 @@ DrawTargetD2D1::factory() return mFactory; } - ID2D1Factory* d2dFactory = D2DFactory(); - if (!d2dFactory) { + RefPtr factory; + D2D1CreateFactoryFunc createD2DFactory; + HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(d2dModule, "D2D1CreateFactory"); + + if (!createD2DFactory) { + gfxWarning() << "Failed to locate D2D1CreateFactory function."; return nullptr; } - HRESULT hr = d2dFactory->QueryInterface((ID2D1Factory1**)&mFactory); + D2D1_FACTORY_OPTIONS options; +#ifdef _DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; - if (FAILED(hr)) { + HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, + __uuidof(ID2D1Factory), + &options, + getter_AddRefs(factory)); + + if (FAILED(hr) || !factory) { + gfxWarning() << "Failed to create Direct2D factory."; return nullptr; } + hr = factory->QueryInterface((ID2D1Factory1**)&mFactory); + if (FAILED(hr) || !mFactory) { + return nullptr; + } + + ExtendInputEffectD2D1::Register(mFactory); RadialGradientEffectD2D1::Register(mFactory); return mFactory; } +IDWriteFactory* +DrawTargetD2D1::GetDWriteFactory() +{ + if (mDWriteFactory) { + return mDWriteFactory; + } + + decltype(DWriteCreateFactory)* createDWriteFactory; + HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); + createDWriteFactory = (decltype(DWriteCreateFactory)*) + GetProcAddress(dwriteModule, "DWriteCreateFactory"); + + if (!createDWriteFactory) { + gfxWarning() << "Failed to locate DWriteCreateFactory function."; + return nullptr; + } + + HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast(&mDWriteFactory)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create DWrite Factory."; + } + + return mDWriteFactory; +} + void DrawTargetD2D1::CleanupD2D() { if (mFactory) { RadialGradientEffectD2D1::Unregister(mFactory); + ExtendInputEffectD2D1::Unregister(mFactory); mFactory->Release(); mFactory = nullptr; } diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index 3d3fb0d63e0..acbb445b590 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -35,7 +35,6 @@ #endif #ifdef WIN32 -#include "DrawTargetD2D.h" #include "DrawTargetD2D1.h" #include "ScaledFontDWrite.h" #include "NativeFontResourceDWrite.h" @@ -161,7 +160,6 @@ int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT); #ifdef WIN32 -ID3D10Device1 *Factory::mD3D10Device; ID3D11Device *Factory::mD3D11Device; ID2D1Device *Factory::mD2D1Device; #endif @@ -315,15 +313,6 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor RefPtr retVal; switch (aBackend) { #ifdef WIN32 - case BackendType::DIRECT2D: - { - RefPtr newTarget; - newTarget = new DrawTargetD2D(); - if (newTarget->Init(aSize, aFormat)) { - retVal = newTarget; - } - break; - } case BackendType::DIRECT2D1_1: { RefPtr newTarget; @@ -499,8 +488,6 @@ Factory::GetMaxSurfaceSize(BackendType aType) return DrawTargetSkia::GetMaxSurfaceSize(); #endif #ifdef WIN32 - case BackendType::DIRECT2D: - return DrawTargetD2D::GetMaxSurfaceSize(); case BackendType::DIRECT2D1_1: return DrawTargetD2D1::GetMaxSurfaceSize(); #endif @@ -611,87 +598,6 @@ Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB) #ifdef WIN32 -already_AddRefed -Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) -{ - MOZ_ASSERT(aTexture); - - RefPtr newTarget; - - newTarget = new DrawTargetD2D(); - if (newTarget->Init(aTexture, aFormat)) { - RefPtr retVal = newTarget; - - if (mRecorder) { - retVal = new DrawTargetRecording(mRecorder, retVal, true); - } - - return retVal.forget(); - } - - gfxWarning() << "Failed to create draw target for D3D10 texture."; - - // Failed - return nullptr; -} - -already_AddRefed -Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, - ID3D10Texture2D *aTextureB, - SurfaceFormat aFormat) -{ - MOZ_ASSERT(aTextureA && aTextureB); - RefPtr newTargetA; - RefPtr newTargetB; - - newTargetA = new DrawTargetD2D(); - if (!newTargetA->Init(aTextureA, aFormat)) { - gfxWarning() << "Failed to create dual draw target for D3D10 texture."; - return nullptr; - } - - newTargetB = new DrawTargetD2D(); - if (!newTargetB->Init(aTextureB, aFormat)) { - gfxWarning() << "Failed to create new draw target for D3D10 texture."; - return nullptr; - } - - RefPtr newTarget = - new DrawTargetDual(newTargetA, newTargetB); - - RefPtr retVal = newTarget; - - if (mRecorder) { - retVal = new DrawTargetRecording(mRecorder, retVal); - } - - return retVal.forget(); -} - -void -Factory::SetDirect3D10Device(ID3D10Device1 *aDevice) -{ - // do not throw on failure; return error codes and disconnect the device - // On Windows 8 error codes are the default, but on Windows 7 the - // default is to throw (or perhaps only with some drivers?) - if (aDevice) { - aDevice->SetExceptionMode(0); - } - mD3D10Device = aDevice; -} - -ID3D10Device1* -Factory::GetDirect3D10Device() -{ -#ifdef DEBUG - if (mD3D10Device) { - UINT mode = mD3D10Device->GetExceptionMode(); - MOZ_ASSERT(0 == mode); - } -#endif - return mD3D10Device; -} - already_AddRefed Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat) { @@ -767,13 +673,13 @@ Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams) uint64_t Factory::GetD2DVRAMUsageDrawTarget() { - return DrawTargetD2D::mVRAMUsageDT; + return DrawTargetD2D1::mVRAMUsageDT; } uint64_t Factory::GetD2DVRAMUsageSourceSurface() { - return DrawTargetD2D::mVRAMUsageSS; + return DrawTargetD2D1::mVRAMUsageSS; } void @@ -784,7 +690,6 @@ Factory::D2DCleanup() mD2D1Device = nullptr; } DrawTargetD2D1::CleanupD2D(); - DrawTargetD2D::CleanupD2D(); } already_AddRefed diff --git a/gfx/2d/FilterNodeD2D1.cpp b/gfx/2d/FilterNodeD2D1.cpp index c7f798e5bbc..56c95299015 100644 --- a/gfx/2d/FilterNodeD2D1.cpp +++ b/gfx/2d/FilterNodeD2D1.cpp @@ -8,9 +8,6 @@ #include "Logging.h" #include "SourceSurfaceD2D1.h" -#include "SourceSurfaceD2D.h" -#include "SourceSurfaceD2DTarget.h" -#include "DrawTargetD2D.h" #include "DrawTargetD2D1.h" #include "ExtendInputEffectD2D1.h" @@ -159,8 +156,6 @@ already_AddRefed GetImageForSourceSurface(DrawTarget *aDT, SourceSur switch (aDT->GetBackendType()) { case BackendType::DIRECT2D1_1: return static_cast(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP); - case BackendType::DIRECT2D: - return static_cast(aDT)->GetImageForSurface(aSurface); default: gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType(); return nullptr; diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index bde54fc6507..869003a17fe 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -25,9 +25,8 @@ namespace mozilla { namespace gfx { -ID2D1Factory* D2DFactory(); - ID2D1Factory1* D2DFactory1(); +static ID2D1Factory* D2DFactory() { return D2DFactory1(); } static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) { diff --git a/gfx/2d/NativeFontResourceDWrite.cpp b/gfx/2d/NativeFontResourceDWrite.cpp index 10b0486de83..92ac6e88407 100644 --- a/gfx/2d/NativeFontResourceDWrite.cpp +++ b/gfx/2d/NativeFontResourceDWrite.cpp @@ -8,7 +8,7 @@ #include -#include "DrawTargetD2D.h" +#include "DrawTargetD2D1.h" #include "Logging.h" #include "mozilla/RefPtr.h" @@ -69,7 +69,7 @@ public: { if (!mInstance) { mInstance = new DWriteFontFileLoader(); - DrawTargetD2D::GetDWriteFactory()-> + DrawTargetD2D1::GetDWriteFactory()-> RegisterFontFileLoader(mInstance); } return mInstance; @@ -221,7 +221,7 @@ already_AddRefed NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo) { - IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); + IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory(); if (!factory) { gfxWarning() << "Failed to get DWrite Factory."; return nullptr; diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp index 59e7e82c14e..eb62595a261 100644 --- a/gfx/2d/PathD2D.cpp +++ b/gfx/2d/PathD2D.cpp @@ -6,7 +6,7 @@ #include "PathD2D.h" #include "HelpersD2D.h" #include -#include "DrawTargetD2D.h" +#include "DrawTargetD2D1.h" #include "Logging.h" namespace mozilla { @@ -351,7 +351,7 @@ already_AddRefed PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const { RefPtr path; - HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(getter_AddRefs(path)); + HRESULT hr = DrawTargetD2D1::factory()->CreatePathGeometry(getter_AddRefs(path)); if (FAILED(hr)) { gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr); diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index 6da47bf4d06..a9c9e8f8051 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "DrawTargetD2D.h" +#include "DrawTargetD2D1.h" #include "ScaledFontDWrite.h" #include "PathD2D.h" @@ -120,7 +120,7 @@ ScaledFontDWrite::GetSkTypeface() { MOZ_ASSERT(mFont); if (!mTypeface) { - IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); + IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory(); mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mFont, mFontFamily); } return mTypeface; diff --git a/gfx/2d/SourceSurfaceD2D.cpp b/gfx/2d/SourceSurfaceD2D.cpp deleted file mode 100644 index 650b54473b9..00000000000 --- a/gfx/2d/SourceSurfaceD2D.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include "SourceSurfaceD2D.h" -#include "DrawTargetD2D.h" -#include "Logging.h" -#include "Tools.h" - -namespace mozilla { -namespace gfx { - -SourceSurfaceD2D::SourceSurfaceD2D() -{ -} - -SourceSurfaceD2D::~SourceSurfaceD2D() -{ - if (mBitmap) { - DrawTargetD2D::mVRAMUsageSS -= GetByteSize(); - } -} - -IntSize -SourceSurfaceD2D::GetSize() const -{ - return mSize; -} - -SurfaceFormat -SourceSurfaceD2D::GetFormat() const -{ - return mFormat; -} - -bool -SourceSurfaceD2D::IsValid() const -{ - return mDevice == Factory::GetDirect3D10Device(); -} - -already_AddRefed -SourceSurfaceD2D::GetDataSurface() -{ - RefPtr result = new DataSourceSurfaceD2D(this); - if (result->IsValid()) { - return result.forget(); - } - return nullptr; -} - -bool -SourceSurfaceD2D::InitFromData(unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - SurfaceFormat aFormat, - ID2D1RenderTarget *aRT) -{ - HRESULT hr; - - mFormat = aFormat; - mSize = aSize; - - if ((uint32_t)aSize.width > aRT->GetMaximumBitmapSize() || - (uint32_t)aSize.height > aRT->GetMaximumBitmapSize()) { - gfxDebug() << "Bitmap does not fit in texture."; - return false; - } - - D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); - hr = aRT->CreateBitmap(D2DIntSize(aSize), props, getter_AddRefs(mBitmap)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hexa(hr); - return false; - } - - hr = mBitmap->CopyFromMemory(nullptr, aData, aStride); - - if (FAILED(hr)) { - gfxWarning() << "Failed to copy data to D2D bitmap. Code: " << hexa(hr); - return false; - } - - DrawTargetD2D::mVRAMUsageSS += GetByteSize(); - mDevice = Factory::GetDirect3D10Device(); - - return true; -} - -bool -SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture, - SurfaceFormat aFormat, - ID2D1RenderTarget *aRT) -{ - HRESULT hr; - - RefPtr surf; - - hr = aTexture->QueryInterface((IDXGISurface**)&surf); - - if (FAILED(hr)) { - gfxWarning() << "Failed to QI texture to surface. Code: " << hexa(hr); - return false; - } - - D3D10_TEXTURE2D_DESC desc; - aTexture->GetDesc(&desc); - - mSize = IntSize(desc.Width, desc.Height); - mFormat = aFormat; - - D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); - hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, getter_AddRefs(mBitmap)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create SharedBitmap. Code: " << hexa(hr); - return false; - } - - aTexture->GetDevice(getter_AddRefs(mDevice)); - DrawTargetD2D::mVRAMUsageSS += GetByteSize(); - - return true; -} - -uint32_t -SourceSurfaceD2D::GetByteSize() const -{ - return mSize.width * mSize.height * BytesPerPixel(mFormat); -} - -DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface) - : mTexture(nullptr) - , mFormat(aSourceSurface->mFormat) - , mSize(aSourceSurface->mSize) - , mMapped(false) -{ - // We allocate ourselves a regular D3D surface (sourceTexture) and paint the - // D2D bitmap into it via a DXGI render target. Then we need to copy - // sourceTexture into a staging texture (mTexture), which we will lazily map - // to get the data. - - CD3D10_TEXTURE2D_DESC desc(DXGIFormat(mFormat), mSize.width, mSize.height); - desc.MipLevels = 1; - desc.Usage = D3D10_USAGE_DEFAULT; - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - RefPtr sourceTexture; - HRESULT hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, - getter_AddRefs(sourceTexture)); - if (FAILED(hr)) { - gfxWarning() << "Failed to create texture. Code: " << hexa(hr); - return; - } - - RefPtr dxgiSurface; - hr = sourceTexture->QueryInterface((IDXGISurface**)getter_AddRefs(dxgiSurface)); - if (FAILED(hr)) { - gfxWarning() << "Failed to create DXGI surface. Code: " << hexa(hr); - return; - } - - D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties( - D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); - - RefPtr renderTarget; - hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(dxgiSurface, - &rtProps, - getter_AddRefs(renderTarget)); - if (FAILED(hr)) { - gfxWarning() << "Failed to create render target. Code: " << hexa(hr); - return; - } - - renderTarget->BeginDraw(); - renderTarget->Clear(D2D1::ColorF(0, 0.0f)); - if (aSourceSurface->GetFormat() != SurfaceFormat::A8) { - renderTarget->DrawBitmap(aSourceSurface->mBitmap, - D2D1::RectF(0, 0, - Float(mSize.width), - Float(mSize.height))); - } else { - RefPtr brush; - renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush)); - renderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - renderTarget->FillOpacityMask(aSourceSurface->mBitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); - } - hr = renderTarget->EndDraw(); - if (FAILED(hr)) { - gfxWarning() << "Failed to draw bitmap. Code: " << hexa(hr); - return; - } - - desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE; - desc.Usage = D3D10_USAGE_STAGING; - desc.BindFlags = 0; - hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); - if (FAILED(hr)) { - gfxWarning() << "Failed to create staging texture. Code: " << hexa(hr); - mTexture = nullptr; - return; - } - - aSourceSurface->mDevice->CopyResource(mTexture, sourceTexture); -} - -DataSourceSurfaceD2D::~DataSourceSurfaceD2D() -{ - if (mMapped) { - mTexture->Unmap(0); - } -} - -unsigned char* -DataSourceSurfaceD2D::GetData() -{ - EnsureMappedTexture(); - if (!mMapped) { - return nullptr; - } - - return reinterpret_cast(mData.pData); -} - -int32_t -DataSourceSurfaceD2D::Stride() -{ - EnsureMappedTexture(); - if (!mMapped) { - return 0; - } - - return mData.RowPitch; -} - -IntSize -DataSourceSurfaceD2D::GetSize() const -{ - return mSize; -} - -SurfaceFormat -DataSourceSurfaceD2D::GetFormat() const -{ - return mFormat; -} - -bool -DataSourceSurfaceD2D::Map(MapType aMapType, MappedSurface *aMappedSurface) -{ - // DataSourceSurfaces used with the new Map API should not be used with GetData!! - MOZ_ASSERT(!mMapped); - MOZ_ASSERT(!mIsMapped); - - if (!mTexture) { - return false; - } - - D3D10_MAP mapType; - - if (aMapType == MapType::READ) { - mapType = D3D10_MAP_READ; - } else if (aMapType == MapType::WRITE) { - mapType = D3D10_MAP_WRITE; - } else { - mapType = D3D10_MAP_READ_WRITE; - } - - D3D10_MAPPED_TEXTURE2D map; - - HRESULT hr = mTexture->Map(0, mapType, 0, &map); - - if (FAILED(hr)) { - gfxWarning() << "Texture map failed with code: " << hexa(hr); - return false; - } - - aMappedSurface->mData = (uint8_t*)map.pData; - aMappedSurface->mStride = map.RowPitch; - mIsMapped = !!aMappedSurface->mData; - - return mIsMapped; -} - -void -DataSourceSurfaceD2D::Unmap() -{ - MOZ_ASSERT(mIsMapped); - - mIsMapped = false; - mTexture->Unmap(0); -} - -void -DataSourceSurfaceD2D::EnsureMappedTexture() -{ - // Do not use GetData() after having used Map! - MOZ_ASSERT(!mIsMapped); - - if (mMapped || - !mTexture) { - return; - } - - HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mData); - if (FAILED(hr)) { - gfxWarning() << "Failed to map texture. Code: " << hexa(hr); - mTexture = nullptr; - } else { - mMapped = true; - } -} - -} -} diff --git a/gfx/2d/SourceSurfaceD2D.h b/gfx/2d/SourceSurfaceD2D.h deleted file mode 100644 index 22ee5f125f9..00000000000 --- a/gfx/2d/SourceSurfaceD2D.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SOURCESURFACED2D_H_ -#define MOZILLA_GFX_SOURCESURFACED2D_H_ - -#include "2D.h" -#include "HelpersD2D.h" -#include - -namespace mozilla { -namespace gfx { - -class DataSourceSurfaceD2D; - -class SourceSurfaceD2D : public SourceSurface -{ -public: - MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D) - SourceSurfaceD2D(); - ~SourceSurfaceD2D(); - - virtual SurfaceType GetType() const { return SurfaceType::D2D1_BITMAP; } - virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const; - virtual bool IsValid() const; - - virtual already_AddRefed GetDataSurface(); - - ID2D1Bitmap *GetBitmap() { return mBitmap; } - - bool InitFromData(unsigned char *aData, - const IntSize &aSize, - int32_t aStride, - SurfaceFormat aFormat, - ID2D1RenderTarget *aRT); - bool InitFromTexture(ID3D10Texture2D *aTexture, - SurfaceFormat aFormat, - ID2D1RenderTarget *aRT); -private: - friend class DrawTargetD2D; - friend class DataSourceSurfaceD2D; - - uint32_t GetByteSize() const; - - RefPtr mBitmap; - // We need to keep this pointer here to check surface validity. - RefPtr mDevice; - SurfaceFormat mFormat; - IntSize mSize; -}; - - -class DataSourceSurfaceD2D : public DataSourceSurface -{ -public: - MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D) - DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface); - virtual ~DataSourceSurfaceD2D(); - - virtual unsigned char* GetData(); - virtual int32_t Stride(); - virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const; - virtual bool Map(MapType, MappedSurface *aMappedSurface); - virtual void Unmap(); - - bool IsValid() - { - return mTexture; - } - -private: - void EnsureMappedTexture(); - - RefPtr mTexture; - - D3D10_MAPPED_TEXTURE2D mData; - - SurfaceFormat mFormat; - IntSize mSize; - bool mMapped; -}; - -} -} - -#endif /* MOZILLA_GFX_SOURCESURFACED2D_H_ */ diff --git a/gfx/2d/SourceSurfaceD2DTarget.cpp b/gfx/2d/SourceSurfaceD2DTarget.cpp deleted file mode 100644 index 464a64f4230..00000000000 --- a/gfx/2d/SourceSurfaceD2DTarget.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include "SourceSurfaceD2DTarget.h" -#include "Logging.h" -#include "DrawTargetD2D.h" -#include "Tools.h" - -#include - -namespace mozilla { -namespace gfx { - -SourceSurfaceD2DTarget::SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, - ID3D10Texture2D* aTexture, - SurfaceFormat aFormat) - : mDrawTarget(aDrawTarget) - , mTexture(aTexture) - , mFormat(aFormat) - , mOwnsCopy(false) -{ -} - -SourceSurfaceD2DTarget::~SourceSurfaceD2DTarget() -{ - // We don't need to do anything special here to notify our mDrawTarget. It must - // already have cleared its mSnapshot field, otherwise this object would - // be kept alive. - if (mOwnsCopy) { - IntSize size = GetSize(); - - DrawTargetD2D::mVRAMUsageSS -= size.width * size.height * BytesPerPixel(mFormat); - } -} - -IntSize -SourceSurfaceD2DTarget::GetSize() const -{ - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - - return IntSize(desc.Width, desc.Height); -} - -SurfaceFormat -SourceSurfaceD2DTarget::GetFormat() const -{ - return mFormat; -} - -already_AddRefed -SourceSurfaceD2DTarget::GetDataSurface() -{ - RefPtr dataSurf = - new DataSourceSurfaceD2DTarget(mFormat); - - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - - desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; - desc.Usage = D3D10_USAGE_STAGING; - desc.BindFlags = 0; - desc.MiscFlags = 0; - - if (!Factory::GetDirect3D10Device()) { - gfxCriticalError() << "Invalid D3D10 device in D2D target surface (GDS)"; - return nullptr; - } - - HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(dataSurf->mTexture)); - - if (FAILED(hr)) { - gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr); - return nullptr; - } - Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture); - - return dataSurf.forget(); -} - -void* -SourceSurfaceD2DTarget::GetNativeSurface(NativeSurfaceType aType) -{ - if (aType == NativeSurfaceType::D3D10_TEXTURE) { - return static_cast(mTexture.get()); - } - return nullptr; -} - -ID3D10ShaderResourceView* -SourceSurfaceD2DTarget::GetSRView() -{ - if (mSRView) { - return mSRView; - } - - if (!Factory::GetDirect3D10Device()) { - gfxCriticalError() << "Invalid D3D10 device in D2D target surface (SRV)"; - return nullptr; - } - - HRESULT hr = Factory::GetDirect3D10Device()->CreateShaderResourceView(mTexture, nullptr, getter_AddRefs(mSRView)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create ShaderResourceView. Code: " << hexa(hr); - } - - return mSRView; -} - -void -SourceSurfaceD2DTarget::DrawTargetWillChange() -{ - RefPtr oldTexture = mTexture; - - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - - // Our original texture might implement the keyed mutex flag. We shouldn't - // need that here. We actually specifically don't want it since we don't lock - // our texture for usage! - desc.MiscFlags = 0; - - // Get a copy of the surface data so the content at snapshot time was saved. - Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); - Factory::GetDirect3D10Device()->CopyResource(mTexture, oldTexture); - - mBitmap = nullptr; - - DrawTargetD2D::mVRAMUsageSS += desc.Width * desc.Height * BytesPerPixel(mFormat); - mOwnsCopy = true; - - // We now no longer depend on the source surface content remaining the same. - MarkIndependent(); -} - -ID2D1Bitmap* -SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT) -{ - if (mBitmap) { - return mBitmap; - } - - HRESULT hr; - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - - IntSize size(desc.Width, desc.Height); - - RefPtr surf; - hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surf)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hexa(hr); - return nullptr; - } - - D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(mFormat)); - hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, getter_AddRefs(mBitmap)); - - if (FAILED(hr)) { - // This seems to happen for SurfaceFormat::A8 sometimes... - hr = aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height), - D2D1::BitmapProperties(D2DPixelFormat(mFormat)), - getter_AddRefs(mBitmap)); - - if (FAILED(hr)) { - gfxWarning() << "Failed in CreateBitmap. Code: " << hexa(hr); - return nullptr; - } - - RefPtr rt; - - if (mDrawTarget) { - rt = mDrawTarget->mRT; - } - - if (!rt) { - // Okay, we already separated from our drawtarget. And we're an A8 - // surface the only way we can get to a bitmap is by creating a - // a rendertarget and from there copying to a bitmap! Terrible! - RefPtr surface; - - hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surface)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to QI texture to surface."; - return nullptr; - } - - D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2DPixelFormat(mFormat)); - hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(surface, props, getter_AddRefs(rt)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create D2D render target for texture."; - return nullptr; - } - } - - mBitmap->CopyFromRenderTarget(nullptr, rt, nullptr); - return mBitmap; - } - - return mBitmap; -} - -void -SourceSurfaceD2DTarget::MarkIndependent() -{ - if (mDrawTarget) { - MOZ_ASSERT(mDrawTarget->mSnapshot == this); - mDrawTarget->mSnapshot = nullptr; - mDrawTarget = nullptr; - } -} - -DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget(SurfaceFormat aFormat) - : mFormat(aFormat) - , mMapped(false) -{ -} - -DataSourceSurfaceD2DTarget::~DataSourceSurfaceD2DTarget() -{ - if (mMapped) { - mTexture->Unmap(0); - } -} - -IntSize -DataSourceSurfaceD2DTarget::GetSize() const -{ - D3D10_TEXTURE2D_DESC desc; - mTexture->GetDesc(&desc); - - return IntSize(desc.Width, desc.Height); -} - -SurfaceFormat -DataSourceSurfaceD2DTarget::GetFormat() const -{ - return mFormat; -} - -uint8_t* -DataSourceSurfaceD2DTarget::GetData() -{ - EnsureMapped(); - - return (unsigned char*)mMap.pData; -} - -int32_t -DataSourceSurfaceD2DTarget::Stride() -{ - EnsureMapped(); - return mMap.RowPitch; -} - -bool -DataSourceSurfaceD2DTarget::Map(MapType aMapType, MappedSurface *aMappedSurface) -{ - // DataSourceSurfaces used with the new Map API should not be used with GetData!! - MOZ_ASSERT(!mMapped); - MOZ_ASSERT(!mIsMapped); - - if (!mTexture) { - return false; - } - - D3D10_MAP mapType; - - if (aMapType == MapType::READ) { - mapType = D3D10_MAP_READ; - } else if (aMapType == MapType::WRITE) { - mapType = D3D10_MAP_WRITE; - } else { - mapType = D3D10_MAP_READ_WRITE; - } - - D3D10_MAPPED_TEXTURE2D map; - - HRESULT hr = mTexture->Map(0, mapType, 0, &map); - - if (FAILED(hr)) { - gfxWarning() << "Texture map failed with code: " << hexa(hr); - return false; - } - - aMappedSurface->mData = (uint8_t*)map.pData; - aMappedSurface->mStride = map.RowPitch; - mIsMapped = !!aMappedSurface->mData; - - return mIsMapped; -} - -void -DataSourceSurfaceD2DTarget::Unmap() -{ - MOZ_ASSERT(mIsMapped); - - mIsMapped = false; - mTexture->Unmap(0); -} - -void -DataSourceSurfaceD2DTarget::EnsureMapped() -{ - // Do not use GetData() after having used Map! - MOZ_ASSERT(!mIsMapped); - if (!mMapped) { - HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mMap); - if (FAILED(hr)) { - gfxWarning() << "Failed to map texture to memory. Code: " << hexa(hr); - return; - } - mMapped = true; - } -} - -} -} diff --git a/gfx/2d/SourceSurfaceD2DTarget.h b/gfx/2d/SourceSurfaceD2DTarget.h deleted file mode 100644 index 82b5b20778c..00000000000 --- a/gfx/2d/SourceSurfaceD2DTarget.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ -#define MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ - -#include "2D.h" -#include "HelpersD2D.h" -#include -#include - -namespace mozilla { -namespace gfx { - -class DrawTargetD2D; - -class SourceSurfaceD2DTarget : public SourceSurface -{ -public: - MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2DTarget) - SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, ID3D10Texture2D* aTexture, - SurfaceFormat aFormat); - ~SourceSurfaceD2DTarget(); - - virtual SurfaceType GetType() const { return SurfaceType::D2D1_DRAWTARGET; } - virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const; - virtual already_AddRefed GetDataSurface(); - virtual void *GetNativeSurface(NativeSurfaceType aType); - - DrawTargetD2D* GetDT() { return mDrawTarget; } - ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT); - -private: - friend class DrawTargetD2D; - - ID3D10ShaderResourceView *GetSRView(); - - // This function is called by the draw target this texture belongs to when - // it is about to be changed. The texture will be required to make a copy - // of itself when this happens. - void DrawTargetWillChange(); - - // This will mark the surface as no longer depending on its drawtarget, - // this may happen on destruction or copying. - void MarkIndependent(); - - RefPtr mSRView; - RefPtr mBitmap; - // Non-null if this is a "lazy copy" of the given draw target. - // Null if we've made a copy. The target is not kept alive, otherwise we'd - // have leaks since it might keep us alive. If the target is destroyed, it - // will notify us. - DrawTargetD2D* mDrawTarget; - mutable RefPtr mTexture; - SurfaceFormat mFormat; - bool mOwnsCopy; -}; - -class DataSourceSurfaceD2DTarget : public DataSourceSurface -{ -public: - MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2DTarget) - DataSourceSurfaceD2DTarget(SurfaceFormat aFormat); - ~DataSourceSurfaceD2DTarget(); - - virtual SurfaceType GetType() const { return SurfaceType::DATA; } - virtual IntSize GetSize() const; - virtual SurfaceFormat GetFormat() const; - virtual uint8_t *GetData(); - virtual int32_t Stride(); - virtual bool Map(MapType, MappedSurface *aMappedSurface); - virtual void Unmap(); - -private: - friend class SourceSurfaceD2DTarget; - void EnsureMapped(); - - mutable RefPtr mTexture; - SurfaceFormat mFormat; - D3D10_MAPPED_TEXTURE2D mMap; - bool mMapped; -}; - -} -} - -#endif /* MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ */ diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 3da1523b50d..11199947da3 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -123,7 +123,7 @@ enum class DrawTargetType : int8_t { enum class BackendType : int8_t { NONE = 0, - DIRECT2D, + DIRECT2D, // Used for version independent D2D objects. COREGRAPHICS, COREGRAPHICS_ACCELERATED, CAIRO, diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index e945563ecd6..eb674d531d2 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -66,7 +66,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'): ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': SOURCES += [ - 'DrawTargetD2D.cpp', 'DrawTargetD2D1.cpp', 'ExtendInputEffectD2D1.cpp', 'FilterNodeD2D1.cpp', @@ -77,9 +76,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': 'RadialGradientEffectD2D1.cpp', 'ScaledFontDWrite.cpp', 'ScaledFontWin.cpp', - 'SourceSurfaceD2D.cpp', 'SourceSurfaceD2D1.cpp', - 'SourceSurfaceD2DTarget.cpp', ] DEFINES['WIN32'] = True From 9049fa458f5a004480f556e33d39f3ca60502427 Mon Sep 17 00:00:00 2001 From: Francois Marier Date: Thu, 11 Feb 2016 17:36:13 -0800 Subject: [PATCH 172/187] Bug 1247464 - Run CSP report URIs through the URL classifier. r=ckerschb MozReview-Commit-ID: ERoZAbw1nbf --- dom/security/nsCSPContext.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index b4a473cff8e..9d535ed0490 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -851,19 +851,26 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource, } // try to create a new channel for every report-uri + nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI; if (doc) { rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI, doc, nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_CSP_REPORT); + nsIContentPolicy::TYPE_CSP_REPORT, + nullptr, // aLoadGroup + nullptr, // aCallbacks + loadFlags); } else { rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI, mLoadingPrincipal, nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_CSP_REPORT); + nsIContentPolicy::TYPE_CSP_REPORT, + nullptr, // aLoadGroup + nullptr, // aCallbacks + loadFlags); } if (NS_FAILED(rv)) { From cd73a2d9187c4f17432a7105db126992b8e8c4d5 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 11 Feb 2016 16:02:17 -0800 Subject: [PATCH 173/187] Bug 1247789: Fix comment for js::Fifo DONTBUILD r=fitzgen --- js/src/ds/Fifo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ds/Fifo.h b/js/src/ds/Fifo.h index 80e519b00e0..bc9eb7a24d8 100644 --- a/js/src/ds/Fifo.h +++ b/js/src/ds/Fifo.h @@ -36,7 +36,7 @@ class Fifo // An element A is "younger" than an element B if B was inserted into the // |Fifo| before A was. // - // Invariant 1: Every element within |front_| is younger than every element + // Invariant 1: Every element within |front_| is older than every element // within |rear_|. // Invariant 2: Entries within |front_| are sorted from younger to older. // Invariant 3: Entries within |rear_| are sorted from older to younger. From 97e88ff837a1a5cb1941f6b5aee831ab3933421e Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 11 Feb 2016 23:28:13 -0600 Subject: [PATCH 174/187] Bug 1247755 - Baldr: disallow duplicate signature table entries (r=sunfish) MozReview-Commit-ID: 2feiq81dBKH --- js/src/asmjs/AsmJS.cpp | 8 +------- js/src/asmjs/Wasm.cpp | 13 +++++++++++++ js/src/asmjs/WasmTypes.h | 7 +++++++ js/src/jit-test/tests/wasm/binary.js | 10 +++++----- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 17623815dc7..b32fee522c2 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -1568,13 +1568,6 @@ class MOZ_STACK_CLASS ModuleValidator }; private: - struct SigHashPolicy - { - typedef const Sig& Lookup; - static HashNumber hash(Lookup sig) { return sig.hash(); } - static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; } - }; - typedef HashMap SigMap; class NamedSig { PropertyName* name_; @@ -1605,6 +1598,7 @@ class MOZ_STACK_CLASS ModuleValidator } }; typedef HashMap ImportMap; + typedef HashMap SigMap; typedef HashMap GlobalMap; typedef HashMap MathNameMap; typedef HashMap AtomicsNameMap; diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 8b4ce98875c..8cddf5c2533 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -645,6 +645,8 @@ typedef Vector ImportNameVector; /*****************************************************************************/ // wasm decoding and generation +typedef HashSet SigSet; + static bool DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) { @@ -665,6 +667,10 @@ DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) if (!init->sigs.resize(numSigs)) return false; + SigSet dupSet(cx); + if (!dupSet.init()) + return false; + for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) { uint32_t numArgs; if (!d.readVarU32(&numArgs)) @@ -687,6 +693,13 @@ DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) } init->sigs[sigIndex] = Sig(Move(args), result); + + SigSet::AddPtr p = dupSet.lookupForAdd(init->sigs[sigIndex]); + if (p) + return Fail(cx, d, "duplicate signature"); + + if (!dupSet.add(p, &init->sigs[sigIndex])) + return false; } if (!d.finishSection(sectionStart)) diff --git a/js/src/asmjs/WasmTypes.h b/js/src/asmjs/WasmTypes.h index 8dc655ee3fc..1e13e86bd54 100644 --- a/js/src/asmjs/WasmTypes.h +++ b/js/src/asmjs/WasmTypes.h @@ -245,6 +245,13 @@ class Sig } }; +struct SigHashPolicy +{ + typedef const Sig& Lookup; + static HashNumber hash(Lookup sig) { return sig.hash(); } + static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; } +}; + // A "declared" signature is a Sig object that is created and owned by the // ModuleGenerator. These signature objects are read-only and have the same // lifetime as the ModuleGenerator. This type is useful since some uses of Sig diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index 259625a25a0..8eb97546d41 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -139,11 +139,11 @@ const trivialSigSection = sigSection([{args:[], ret:VoidCode}]); const trivialDeclSection = declSection([0]); const trivialCodeSection = codeSection([{locals:[], body:[0, 0]}]); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: U32MAX_LEB, } ]))), Error, /too many signatures/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: [1, ...U32MAX_LEB], } ]))), Error, /too many arguments in signature/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: declSectionStr, body: U32MAX_LEB, }]))), Error, /too many functions/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body: U32MAX_LEB, }]))), Error, /too many imports/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: exportSectionStr, body: U32MAX_LEB, }]))), Error, /too many exports/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}, {args:[], ret:VoidCode}])]))), TypeError, /duplicate signature/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body: U32MAX_LEB, }]))), TypeError, /too many imports/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: exportSectionStr, body: U32MAX_LEB, }]))), TypeError, /too many exports/); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1]}]))), TypeError); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), TypeError); From eda84dd7cb7e00a239166705834894fbea5f526b Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 10 Feb 2016 16:36:57 -0800 Subject: [PATCH 175/187] Bug 1242013 - Record tab cache position in telemetry (r=mconley) --- browser/base/content/tabbrowser.xml | 44 ++++++++++++++++++++ toolkit/components/telemetry/Histograms.json | 8 ++++ 2 files changed, 52 insertions(+) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index f6f191e87d0..fd2beae2566 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1106,6 +1106,8 @@ } if (!this._previewMode) { + this._recordTabAccess(this.mCurrentTab); + this.mCurrentTab.lastAccessed = Infinity; this.mCurrentTab.removeAttribute("unread"); oldTab.lastAccessed = Date.now(); @@ -1298,6 +1300,45 @@ ]]>
+ + + + + + @@ -4266,6 +4307,7 @@ this.mCurrentTab._tPos = 0; this.mCurrentTab._fullyOpen = true; this.mCurrentTab.lastAccessed = Infinity; + this.mCurrentTab.cachePosition = 0; this.mCurrentTab.linkedBrowser = this.mCurrentBrowser; this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab); @@ -6162,6 +6204,8 @@ + Infinity + false Date: Thu, 11 Feb 2016 22:41:58 -0800 Subject: [PATCH 176/187] Back out changeset 561d4d620aa3 (bug 1242774) for destabilizing Linux32 crashtests --- dom/media/test/crashtests/crashtests.list | 1 - dom/media/test/crashtests/video-crash.webm | Bin 58482 -> 0 bytes .../video-replay-after-audio-end.html | 43 ------------------ 3 files changed, 44 deletions(-) delete mode 100644 dom/media/test/crashtests/video-crash.webm delete mode 100644 dom/media/test/crashtests/video-replay-after-audio-end.html diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index 0ab9c09fcf4..1c53739be48 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -92,7 +92,6 @@ HTTP load media-element-source-seek-1.html load offline-buffer-source-ended-1.html load oscillator-ended-1.html load oscillator-ended-2.html -skip-if(winWidget) load video-replay-after-audio-end.html # This needs to run at the end to avoid leaking busted state into other tests. skip-if(B2G) load 691096-1.html # bug 852821 diff --git a/dom/media/test/crashtests/video-crash.webm b/dom/media/test/crashtests/video-crash.webm deleted file mode 100644 index 9532113d87ae59497ab5e9482615c57cc51c8672..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58482 zcmdSB1ymJX|28~y!y%29Q3q(cx84lUg!CEbDu0@8@m-60_j3WzkwJD|_~ zKF@mB`u-31d#`VO->f-v_U!sy``Y_=&7PTk4zbK)9t?21QAmbBVI)t$&krcP#7j8(knM{7Nrjn8f8KFawS$8<S>5q9dzPGpw`&YtF5=3W(l5ir-@D$n^oEt+6=FQA{8O{&6com_3ks6~ z1*mH?0^J=fLV~r8wN(WIp)mKq1_r@g=4dVb$%|7J0PEiv0JEfd6VSW~{iz0nehLZ; zRs;KQ)r3hHYpbYB%4q~bVVD2ZfSnD6sSOj~aJt1~W&x!%&sE%kCD(%SaC9-Vy@d(m z|KWGP_zV0lRSyIAJCy~D*R^%BVN-FjrhIB^rhQw6QjU^~gOitolY^I%VdYiOUpwPX zaeVM&>0s_?Vd)|QQfCfQ$9F>=2MogRN~tBKv|#{o0HARtWO`WON)QAgmQ*GBN-8;M zw@W0ch#f%6rD)Jc>l#aypCcJ&zyKYN)dFq`gi3hg1%*o>Qw7nA=*9=qi-4gpK`_qt zCJrL`uY-)B4VWr{OcwN&LjXN+P2iL?NJdZ>0>*7p24P9orw5h`f|+S}WmN+a1vg29 zI#?LO13q%p69mEYGDHTfa&(UeqDh9O2g>oD5(imJcoO`!K_ej>8;B^;O&Ii00z`l) zSdSOfCs9ls#3YDJ5k$#jfe*w2Ku#`0crF7fg!nBg&hEPZcpcdpywvH+l}hA!ibhjd%c0sxS61i}k+ z!wYP}H^QQkgF?z-0e=912hH$EX$U_`lpfO5nyRy(`?uX7*^=nCILIMn)Yhs5F^mI_ z2=TTbKp6hm;T#|R|3$Jtp*&_TN`>HJ#@8G@%yju`L9z_lTCez5;SGWt8Lopp>lvJb zU??|34?P{sw~_HNM-TOPh`!ouIGyPx&UtXlBm?^s7^182icOEI1w#lGw$bSd)nG`r z!At#>!cCm(O;xvt?XE4-4BVU3;^v0rSHoRkNafSkmkh{XS#I;)k_@(291;>`I?sqa zx?w~45ZjEHYfIu}lLLh~Fhg>CWYZ=;@c}YzlC!!z{cply0dZuiU_5o%e;v%is6q%` zdKrsy0_2%WigP2m`dDiEZx0W4%WE)&&^bRsq?ju{s%zNuPU_ly7u-k>e5b8%S5d1j z&sdPCj(W=U{1JWUES~VPS zXC33>9n+A}a<`mHcGr9FYcL&bc;dSN?x*giy2+M$|FQpWIxGNCf`k2cLl{31y7mih z(j-Fu)l9HM0Vljix8qIjEseq`&A=#&${~*mu810*%ovZ>2)>~jzr7ltdTB3jn#va3lxUlq%^89Y&2(Ac7sE*rj(fHE= z0D#_8h1WMis)R#3q0XwM!8)NXGohjNKW$25JW9AAu_!kVJFM^8Uy%|mv00wd&5)ln zr7XCfKh@6=kg);bG*A7k&MAfdDOn;Y5dkC(9#8}QinE*ZBdN=v+%}EOpdb@j!6(5- zC3)j&^3{d;syR=rnnozyH`uqeZPP>>-ru~(2jGXaOO06b;f$VeQNThB-sm16+MM;i?Ik!0Yp zkO3g{z>*WGQUZ?>9<36tj0&FN6t2uf63+k+ubQTo5}wk8rj{Ool2$UU61S2Po|Xo# zj26C@MzWGtt>qAp{fws80)f^W~ioRsR!2VVW>Byr8DUZ);yu1G=s0Dm8>;0VX2kuZtSMDplvuarKLCNJ2x3@ zJn7p}T-xZ+TUlB5w*2BP+ePEIokY1Y#T!5q(rQ{tI<@v1?)C<_GE?|k6Uj0f?qDsx z#_pCgQ_>^uGFnNNHzt!T4WC)+ya$WeYkBxCx)~a#xEtzP8Jq-YZ2` zex7t+JW(90Q<|D^AJcMQoOEAHv!6Q!i@9qh2OER=rh=Ef92V|nscE?{BpMon#qK#6 z^Ut<82*0s6pA&ui=05tPmMRv*IljggrBwHYO%kbP{t%wAclKa?3vPzSDeen4JB_NE zT8287GbdU)$G#wl%`r^o?=)TjK?_>gsk$uIgY_mYDKPU{&(+oFFQ& zDojavuqq9C5S1A-69ncJ%{RtrKgG*O-58H*0Oe4*dM#bQyrFB_&c5L&-F3L6R$oK0;RaO$f}ZR5 z=D=aFqg7AA<>CRSNRFb%b`&yvOPKWnhBclj`+!*FDbl zX$?Ex_k?z!4Slex^+|JZ$^(Z9$^%^@6!d%88^4FAlIKKxEfL1Kku=E^CkMMw=MRpqldY^sY?-+OYh1)z@?uLdZ(rUcm00sxR2v`b?(L44X)4YwT z3pVD_(g9ZjcA2=+07{4%*ufc~AA-#W zbCZG>X9imJ+c-=~WLk@V#T6t*{?3O|kQo0bA2^TT+Q6$b#kkJ=h9GbQ>ttxKqoIg3jqK_Sw4Wl0VgScHh6gW+YmSo0eE-{sXF)D3W30unuC@TcVA6{3iS+ zE?})xgDWy-4Gx)`MZ%(>|=tRHVuDGFNWL=>+{e@&fb_mhZItcVSW zeZF?Q8u+!hAN6bMl%hw)V^QF`=5p+u;Fs;?*k$&y@!Xs5V%L}AFWS7(Ulg?!J`jm` z;I=hZq@4Nx?=Q!APfZp7l+##BhFnBFE{mQ7I>ofxFJVC7^+}GsC<^kz>QD5y|CXZY z1yfXuCo_O5Ahfpr9XU-sWkH!U@mODc+ zNq>J4VLc$M23Y;-Y1MZ1{+(0DltxMC2lqLdwJY>%pG=hf`O?tTXztTF2yyAKK!@rJ5#0k zKB2HA-Iik{%FbfB|AEKNwd?1?^6d6P_`-C>LbPyJo1E+a%R`_A_;uy59RSsMa$)ZR zw@Xy}l6J=&=HgK7;T(atZ1wi}0%!EI=ci?PpDlannO;IXJ0S*bIvlXOTRM@~VRSpa z=X2^Mq}`qR(an0XxylB4Gw(k=9(Q@v6`{E|mWHhJu`_=bILV>76zxf1F@G6)#aWTH zAVC)7Zq4!p1*tMCRr`zPm8JJ{p+2!+)|w3l>t6=%5k044=GG^#Wuu!Mr{{c7trh;N zR0^kEEnt7%V)}EkSzN?z(>)PJP4l$0b3Sd>AD=pQoQ8~!J8p&y6CgH@~d39o}rgHdbP<}pfWryx&!xQ2R~?nO#cxF=U3nAL@+FzrWp zrSr%I8KAzx{nFAJkl-BeS6j?})Rj*oh89}g>34r+agDe8D`P(KC0PpHb8`lRvu1Rt zoz$G)d5^-6;U8Q4Nc=3pvHTJx_LIXQ!CmC#j;E~5n`FBij%xD&q5}-Fs`g$vCbv-p z2~{0m@%efAqV340TV#b|{h*$1_71ix%3H*epq~fz%b#%h<Uw z-s_GX%z$}o?osahS+$U)l>Oq$B)W2mwyEN{xfw$GQ2MO^O8NFoKn0ECE)<1IOwTX= zaqugrq)I9N+T_HY%GZ9^EnId=1OPMo=m1+vD{^EpY=U6)Uq5 z{(McP%Ap329c5E5SAS;A%lfKr3!@8u$(JK0O>jSrCZA6YG$L%uwaP#kC4STqb z`(8&6(6@r(=rZT`DC*AZ^c1V!&1Cr*0xd+MW}Jv;AJycu(}TqW?lB>dRj9BT^G&+E z`bsmJn80bNVSTzCR5=$A-HM=*EyD%K_8@EpQtdkB?Z8By)ampLQAJ0G^tdsQYvt-) zhqKQ32nO;9WyGL$R5Mpa%`$$1yGSzgp0sZxXB8i%2NsUArozzs4;+)Z6zoLovaPBV zJE*Bs1Wi6Kb^0|_DZV7xoe?u^jr~BM5`Ic5>{|L%8jd4L4%=j)jIEcOk8458eLI%u z6yfntQ>}ch>$UR&xu`W#^rO|$0p86;aj{C6%kcElgDN zcEa-~pPf;8>39yv+ia5&zJ?fKD&HS38LF~k**(zy-hlU`H`b82mzQIvN4L+csF+mr z;lb0lCzjQZ%a|U|NxF9LJhV7Km)FNbc`Gk%=HMxXc{U>?J7Si9&fny>;q1UufFlx)=KD!YQpV92}C%uf*=rz+}%1rDYna_~XI1Gk{tZOdDy5PMk{1~Rr z9-DHFWAl+?O2~nDzp_w=lyWD$t~;F6D*6Py6KGmZ3c&C>pbQ|rvP3P@oJ#@>)7}f4 z9mXC6)eCu={~}y&5*)#rDwAKxzbb8~eg93oi-A36fkCu6BapvC5We5vf1Eh4Zn5`e zOHNtGkl#oNwODD1RK(J@CB=lIr?umoShR#um})pGcEgkv6Vi^(W;eBrkMv*cE}IBy znB$ZY%UIM^Y^z5H=}IhR&Gdx%kkkgDnD+NE21&&(1gKCd4RoDsvBM_#O&Ds3K4%9l z@BWHIb2M$63}@Ok9>a;(nICD}&mwz|p_!t@En%IIZ7;^ZIgPx%7KBe zSZgMk99WN1kPh@}Y=e{WWneciO zouWfjvY$8k1Kn(r3XY&CV@dP@ly?B<&&&HTkKzNKA#+*cnsF8ggzdDhBYE!ec z&e_k`fJtN0ud%`KSeHiY9S!jWbpGf*KFG-#mDa~aYY#x{k{cx zD5-p@)ToDyc=~akG|s?C5g^GSknyA|axyoOm|S(QStmo}5+s6_@I! zl6~06S=dt}%C1{=<7}Bse?(T+;8aY&PB*7rM_bK)s0Al#g<##7Vxck!xV;pT5h%J*=_o z5^>u^mj&`#1HfB|^z-jqQsVxdFTY-$S+y3u8^^=ibiO$LHfN4re+J%8{@MJWEYffN zdU%0(^#l9lo)P$bc@jRaA^^bhZ#gHYpa>YcE)(cI<62bEtQg0WTiU_>S3!WmgaQnc zQo!ZaKmi)S83Dk>z61yXLS6Qs0FAgmKnb5R}i@ zVlxa3V-0UvP`QRxGddYhWwOXE!P3G8F)7GWAuG(UM$aFwCR%v|N!>>l3h&cKhY@t& zR|m2L1-d>r3E^j+*06)(-sBY)=@NS7&m=!AIY?JUa?mZ)tseOi z@wdlb_e*>^9`PCyt#gXE2Xql&REw38683U4e10aBQ3RndBA;jTEsvZZDZagmq%qfj zDZ=cTM>3CsZrQ+H`JNq3zy>4LJG<(8Pd3!AJlrw5p73zP%m;pFRrW_FKh)uGXyd`b@~8%AQS@5p2Bhwj)t>FQc=Q zk=^&d?FyB@dhdT8Az)`){FVK`>um7|C}0g#BLkRV?-3$5Z+9b2vI}z{nwY=?i1CMH zHQYvU{->wk(SNkxSCR<|qa21S)s;N*MKLku0g$QS$*B4B#gjy{uER|zAY@oCI$}P5YJ;JZ9KH6ew>nNQHer~n z9@+BEpjPC|rL)BRr&jM+ijCGdZbo~|U!46{6zBhy{lBBQ4GNS32MGT)N-K^V_#l^s zo=Ea8EKawyG~R)hCMeJcjQqC-xe0ApKFfUbKTC8vl;piNHuP5u|0_aP|H}T~5z+z$ zcK>u6o(W)T(mD)KgYS`GNhGWuu)Bb2mWS@iHMPO?NybW2#Rj&u^ei@Ew+k0!#OXT& z*)78JJJ8Vxg~0@>|4TY{yKd>YVdF>DjVYMBNOW}o%w5$MM92Pj(0#vEYM8qyHQ1QD zs0oNn_3t3N;SD6>u1W-&bNM^w{t$5&-2jQWi_e3|68;XdKSbPBFFmahpzk}=#5qH%I(A>%2G53dvyJ$p30PJ1-yb)Y7^>>i{CIa^E8Ug#@@0j~d z1nk{30`_hi0c-FF+5ar%HF%~uHDm-gI{t@biav!yPHP9-TynL zeiH$ASBnD-$D0>h`VV7T<;%KcariyMBGIqKqBty=Q3ZRa67;V2Otay0!X9aRma2l zGKMBKt_@d}B=yj!@Zf__UfNG&Wu25d$Ss;aojuy1x0l7@Du3E~^}Lb)(l%KlCXHX3 zW9aJIuU*Mpcak^i_dy44->$}O0)}ic&-Jl5DnCNplK@scMi=|jIqC=6#dWUlGdyx%>v`(O^vYC08Rj0-!anX*TN{NKPG zNe#VUJ)!Uwx3d;0k(DOU`<$GgP*bt7Tw`^D)t)g1Ddc)L;vZEbX7k7pUX2dW$&(>? zm~)A{gzW(J60htk;)*<#Sn?Ah$Kucn2JlX{pit=U z?K;%-6xl_`GZ%bUUU>Er5Sv(z>1kb^$EPC)y!MjRL|NZSnfOe5?eMyh)JpIx1hYK% z)m}GMO;=dFdX&p*ZP4%7-oz+rJmiX$^7SEvE|Rp3CN>CJ!FRpkUX7!>QQ!UDyfgBX z%zNrNED0D%&(Rd=3_q4!uw8Kq80qv%IVRoTqLXql(74~3FZFruy){;g%dY2EGOVOpicP3_bJf8v`eeUI!5^ac6;In+pe24qP zp5sBE+ED0qg7jzn4Bw~NF8O;uKJl#@WhcLi)lf8he8lL#Gnm_(6&QqUr%0x!L9{@m z{&O^k&XAGuc!5HNpDp&4VZ8!s2@kH6DTkTu#fhVTuIVt$V?!8TO3(UUl+q|71@aT0 z>7PH~xz`#De0*(VUzrv(@tbb*k{W2Oc_?*@H}&ZB|G-^P#?g|4IM(RBYl(=YcHA0r zBDWZLbyT+%8;$4eANuj&S>@}+>KOe9=#f|uB4=x#PiBNda_;`)?9<~tlgIVL3L4IX zewbKe?Kn_^9;)ocQ7Hj|_k5kj7#EKi6Grurwqpq^+NBYiUNO&y4h)>Sbow1^JbEA5 zY%+(LVH)xaz4$yTh%DkFJ?*8MRtBJ4#I{?p@@8L107Z35l0jS!bLY6Cct-FQr}Vhoh5?drz^%_SIU249&_uv79P)rcUn-O8EKHi+xQ}@%Q`vz%Q@{ZcspvHF zb^RX2#w$XF&|gmz9_{gq6vLIf8o(cnDEWBkCq$r8glJMvDKRztRXR1aVH~td()m% zxi~{dgfJ95A5K2!$9||4QHYZve-#RucXL0~frrOr%8?UcPYdy(t@6`6WdETxSv=yi zcw8J>1`$=A81XOFn1DDfos6O;<2*Gj{HQZvkO#E!Uv4-VQSQE0@?gaH$?RcKCk%Nd zIB3z~#EU)(Psx1!S%;1#mzglfC$qO130=yT$0Fc3jrKV{%`Ze*WY~Fg>l%(chkj*F z@AIvkl<;EN%nY7&BJnK6?rQ}ee-#*ZQyUT3uAte+Z z9dITB?n4*=^7i-HDQZs_%%w1uR=%JuGhZG3_>z9jcR4?Hka;~MS@&!1#E?AeP4-2z zK#kwUmEXwJHPS`Du)$|JUr$+JUt|)ar*SrhUCqgO1xco&`Jaz>|}hWgy|ek zfrd-8!9+jLZYbKk|NU$WT$`y~{Us9~7C_{2rSk_c>+ef^0-9kxkv73+k{i23SulhL zk32nppm%#y^`^7kR~!cnungt~& z>SYUx;96%(Upndf#g63Pmoy*B#}z%;sv``0+(`GX1W(u&GWE)w2Eb} zmoH%TXi(?b7isw9^qa{XhYH`&;-45=nd((OU9zt^FA*;&=1gEk3=P#lhe=W*oIc7< zM0m9&*AvCT5b$=yscBZDL7>7^1=%iPNHy!h4DKOzgCoIY1tchjZ|}lwIPei2Lp}qC zRUG@v?7=yEM+5t(0x@uVANe$+h0f51gQUG)s5rIMD4B97ayI(Jh^whs%O$h<)TW0b zX29yrmHraO>$p6qXpt@N=2wixUw{u#eiIw2l#pNID)5@`2S*LF2SRDiFGZD&k1C~wJ*Db0RwAo(z z4p_?mq21LYf&2>Odzy2kHt9wmdmSl~$1viL>Oz`Fl zc5du_SsJAfA-xyrx_$k;ogG3Z-$dG!wTg|3ws~wu-kq8h=Fh-Ajd#pQ4WqxGeeIx& zjv<6t>?6vz@6npZl}yr-v~zLVZ-S;)*>br=c4AKFXh~uFL;%RAg15omx`NTCK&kM_ zNF&zVi6}5ugDu#(hs%;!k=UvFzMPGLTb}S3r)-QpA0JJw9X?{&u9-!N7zSj`;rYsvA1&O(tP6Dr|UA7uYPi+ zY0Ljui{iLZ8LBE==j!QAwpU^HuyqPHiLZ? z+PGc>wXlknAQjPz6v0D*+aH6$i=6cQ5vW|utK`2yz=_$@&CuLcF>|cY8@v-E??D>= z4(5W;ZeV#;bgSeIOe7L4M4lV#F_I2nX`vQ=S0x)!IjV8$=*AWNXT{O5A(!x?U2cnp z{UP5YNK$iLK_0~VO$=SB2=v$Fz}^q(hYt6ZX!CZ|SyBV}=1|CyN8q~aFf_gf$C%ru zWPCu}mwn*>oeK$(n?-1FtZB)wgCSB+EpUu=E7jjmXnjINj9Zltm7(eqjl`-ZGg6fq zb{1vpLuVfaX)==}w$4zLYKQQ$Kw1LQhY3EqZ-;6=@e@qr?>-I5C&wH`^LXp(vh2qR zJt`$b`cC^MVCdnn#E2U!&s(0K1uJ7-5xi{;O_kzY(}{yznew4!#?QWQ)|QO%BA)(^)BSWJGplyjz8#+(WB3-7(lBpGv@Gp8zlVsbYgeX-bw!KSy6re+oX)h101 zxNP^=K$nPjB@3u)rMZCb{>Z_|BY=#am9;K12Wn_)JfgphdmHr=vacpNl8uYh=el!p zCINKp)zJ^lJD*{eIYG8irc4%39vOW7?EH(7yOY3nBZ|=POaSv<$uu<1-{|RLqLFu%8zVP$oY3IsS~HY-@rn?SZ6&UT`19zG&xka_+(~1g8_exvGf7 zUsu(?8AVaxlp>AV<0(;LmqNL}d!BERYBd63p!5X;$_F(2RqdQ2ef#JSNTr%mnrZBq zA^eXo5px+2-(4YQPqyXLurBPzo8-o9yjxf=Y%LF7GP&e0HTt6C7^NkytX1Fr>d^RW zha>XI=tO#d#(S*+h-W0D(!H%`nxF55Kqz*QlXa*Z$KaY*Q*AP4ueQc0A9qfh>4~Yj_#iCwN1YG%XiN zO>lsj8$k%0#*zp-H^1St)&>{+47TWqF z74Ju9x^Ih(4ND|5UoWaCt_|5l7dI0K%~p9q)qcf_9dV8aqZzc%p0f?gLWB|*emed%NIXfwqI-mx<9oHO_k!C8l0&;J5#}d3M3kK`^O_1S zpsCYnZ?IsX8(g*?>CUYZTX8==oCXameu+!RslvA}aAofsQQ-nmKWB!AeBaH%Up@fl z+Q?mm(+C+?8LdcT7?gi(Sn4~bQXnE;8jcF`y(^Itz;mAF4uR&&at-ITJ0l;+wjBH9 zd2{WZhZ)6@s6h~}xP=gOXOw?jPp#^HJM$7Jt9&*%r*uAt#8i6{n4e-T&wY|ukb-OW z;;Nc*;O%U3fA&5J;bm8Z^X_nXGqG?Xa|`p{qp9b+#9~iYU>8NM%wA_Pusxa0Ar;RW ztk_&f_Juf9E64G_Ri$8Z=3_f_%jP&vzgm3lKO`XOZl&|KUvO6pLPX8i&R051Hpr5H=SW+ki7!g_Aq*Yr@xeelz|20=8*Ga-D_;Ge1Z`Y_K(JU4kF zL`2zWTjFLv%PP~4d*${wTI{mkbINRhB{wxRkUgVb?h3=Ok|n|h%!E`fxA3PsrCX|t zn}o~QFWM=ef7J#C=$*V`B7&devlgK+%zw{ z`KcD_jL4v0eE;SFd&c@k-y}#1nN!8f3>9l_;X+m!cT#!meC|4!k-$}>Zo#waSWK>S zcl=)=vxl@(_Hhz!SSISnv&u&!&Tt2)(yN5-BCzm9KC%_DaQ5|v%}JvswDnF49&f^S z3==!^t$%F4u|i*Al+$`GCpKmNG1B&zy19n$7Wj(}{s@f9PLJ0IBh0dGk_D$!j`DC< zy3DNBl&LjWWE8BFAviRgWZBXLCNR>yGPpRlxRO8LBV^%51k}d7K&XJ5s7fe&J}KV% zlkYN#E>!9%mK7O`_mT=d>$(JHMup2!2>a_^>Xr)=dB$Uj4+IJBRBTh60WS{=1sYR> zdw4j>O*i(!OBOiyEak;YSdrvuY8qcZ&h zcQ3d{>1q+v0$c@`YWYWJy06USu8jJBjx>r^@LOn%Kdk4&J!q5USnGrd@IFNRp2hHO z1}Q?c>ubd^dBSv#p&Su5haq0DsA~OoB*I%ELra~RY@>icSx2;qr3ViBs2+UED@Yen0x@QQ&yRm`d>%2 zddW@ZNZle7!%{rlM)V)&3Gi^C1qgq9RfP6@zwaRYaO{Mf1NG;I9ZE&gr&#CbqTVA9 zl-6rbowrc@-}P1nBVzuN4Re;64eTCnV!X_3!c-EsS0Ze0d}i4SyT)9iKLaPPT4frh z^9yp+H1+ztQd;AnER&OyqvyF7`Vp^LxX%xuBG^5*Qk-JjOs;Fhl=Fcjqkv%1;sO$k zpQid0s^aJr(kJJLAUGAVg`Ej*yl}iLH*|W&dhuV6*2b~>al-KkHFW6dvH#hri zwuSvLHB`-%4z;IUwqywRGGQMH>XilCh^3V_}tBFY24}`JaoqSku>FP3=-JfaC zX6JNnee>&j=1f#2VfD{}AxufmG`4B04Umz50Q4a!v3$|{ftvXDi2XZg6bOTAi=Qp! zVj~BQsc$^Xk*3BbW1Jc*d;aBp#dd7E%Qj=Avam6J;#FkE)6fRaFDSzAsxqfn$4E@3 z+*?Jk+rl#1`(9d=>nCN4`w#iQw-AYvfM>uIU0eepc2JR@<0SICe=0~QO zDJvfNQ+sAImW@Q}b_8J)kd7Gc%oQ@F1Uj3@O*^Vi$Q;YT;@3# z5-DOCk(C5VdEPQ(G&&k4_bXr7w%mI~tTy^qdA4lP2>1^EQ-XeK+%z4sVKKCNIT?lg z;~v9p|J~)S40vU~%bOMP87O=_;Ea0PcjRB75#uo>r@Vd(E=GRO({$0a(FqC>m$*@`Rp0 zD;31RcQ_iVZKe0A>~*# z{q*p7?tQh|h>_}NnDpl5voEsNTE!CkA&PFM`-%r7gjD&IFZOpTPPRMC}eqviC;+HQhk@l zsTBbTcez)dfg;=kIRAnl2EvDQgUgYT4xTbS*6wa8j#7a=G*#NKVePB}7Z`hpq_qhbOCBo#j|!s;5u)Rmb=_#fZ6YIr5c4I=mQ6cw*wY*RVR=| z^fnlA0NwTaH|iIdAnYxv>^FIBkf6W_xnibAa>O*V30@DJcHqKPrHCi#?r|6egr`|G21)rjfMTb+}S07=rVd7`-is3bJ=_C863cMT9vnT85qm zgsA?X2teNMDzzYQcTqDCbjv>s8bojxfdmmO{=*1B+U_cSAn3a&(+Ct1_=iDP1R&l; z>_7z6|1g5;0K~hB90>X@QUrpQ`iDV-2=1b8AOeej7y(Gz-Ng;@F5(7)4*7>cgS6d6 z+rT#7Rfs^)MSnowEZ=Es&CcNl|6BvOM=a-(4a9G{bPY*1VB(3(IoWW-LV~1PKOZ4T zaiUuYsh*c}S-=vB|DSRQg1(!!A$I=chR=$0HnJL(GV1g;`c%1z#y*a%e(i!Nq$eS zD8;Tfq)sxff^`tK?i2fo)}3YQ$@d82t6!hA0@~sp zn&WDREcLUmBDIb037+%QC06h?oYqH_*2^{LdEd7pm*Xi-3zsfRJ}J!wvA!}_vU;krrF%E;EFHy z`E)mWiU}C*0Y8<8zF+u0r*OOwdAsovYDB?xzMJQXA?=*hw)z3e5FaAZYs+hl;)k(j zkjdH+Rf~n`*8D9m{e8h({rRtD&W|oaC?;^!L~YOeaNu`(Mj8SZJexHLnB;!&iOEge!`X%@I4w25UU?E3pqpkzwhYwn-=21?YnL|T|Lh&3X z0?L$U0Os_(r5SF6l)iu(e>9FLk#`(CP)? zqZ+Ee?X&lsYlmjew6S1rANDJ`-8XNl@W-NKehi6oP(h(MLWfrz%9As@Ksu4x#BT7) zI^yt*E5r(TvJ&R#?2&nX8J1qs!x#~pkU2+Kg~IO>6ZM%eRzAK4{iFHuP!lTZ{uJhe zgl0VgOC0B&b;VD1hiMhnHEloYcAG?mt@p}HaDof)6=+6F5M>>A4NCpBNoSPQ!;U%2 ze7^c@QeR0D8Qa9%!$e^>rN?z1V4#3~iw=w2^4;N$XvbMY-;-7jdsXk*6_rxo=itNT z2ye7_8BW_Pk+)}uujrCL8idxgJ1%A-D?gGq68&~G>`+*9owJjh{!zH~FisC1&miIA zp650nR@RC=!c2GwJalMoiKuyL!zXb~+osWm8Y9|)J!OEW+m$fJtw*7a(+bvo*52gQ zAlqlG8pGTdf0qa8Q?Gm>L=7)q<9$Jl!=N@r42{+delK2Jho)asNHIkGQ0% zD&mt^<6i>do=rJqdOn^_V!tu&BIsTrO2r&_~q3yUL+`ogqlW9n5Lha3Qf=T?8&-9H*?_Wtd+^W4B8)E_h3A3 zO0btRVtg&FxvsIv&lIxn!&&li({+H|VYHjO=u)eq0q;Q5&d~GAL}|9{mq2>@9Js4z zM8Sk%8hVP(_jz|mx>aAQSH!<3e)PgBvkjt5xe`o;xf6680%WYe#=?1uaNZS@+ zb9^GC|M*e=*swri`hZ+6td5qKK_@=L;W5Y5{xkU)kKAHLK@$PnL`1gmSSlIEex2*( zZx}Fo3SC%G>Xk25xzgnPzWzVWryv-W^%EXdQEE^3nA6Yph#RaTztHfB1?2Gw5IH|L zQu+3h*4pCUEFSzZx@4~$O#08Wz_I~2@_L;brM{Dus+9!X&mQ+vIzqcY-5W7# z36~rHDAE^Pv??cV#LuWRlOvNm+pX7-`MR}6IZ2auB*Oc7*F!WS$5pC&j#~eq$H>fX zZ_&mtw%QI|SUr^rmm{@jg}dyc`YtY_5$^esT)AcChCMfq$a$W9Y@}$vOsrw7DCcl- z*oRtGEP+LFiLVP-M#IA7>Zqb`AiqSccMTCyIaSzQa}WJ+Ah7suZS9$aExLK zzPPYdx${s~!6va93EvZLn2 ze#p%;6{Ne)Eg#;8BK^Eso`?;h1W0nU3$4sHELA68uxW1)s>Ezuw)=%34}94ZxE7t~ zH<}%cp7%4v)~&X;?Tvdw8mHo0k^;6V9C2NU(mjCo(j)=`hIQqPc+bTq!Aq z?Dga1yf@%qAffv)Cv$!n4}gU!DpMl}X30f!5yOOpjka69x)0#gU%{fjVRNsign>aV zdB!|3m4R-iQABvV&XB3|A<;;q zoyE->r3ii;?W5r8xTyLOa1ilJ&^h9|D7H%-aH94n!Cdc>7n+r_S!}pXuP{XkO z;mpKX7^@X?`)54L6LhI5rrx+AGQwal-#f9?52Cb5#Z+UYq#I~>W2ZfesTBn6Vr>*) ztCV=L6dB6=p{@9PITq!PQ}J4@bF7U(^#{AMM^<*u16)k#PrtP`UKv~wk@f7?mWTCT z%yg~!C#Zja9B5>BDEnoiyeWA_hE{D>aJaLGasS(o+!YvE^o6t2PH&QcN%laL5K{FI z^&JFpiG!^1=*0Wll|!KtsQ}?TjR1F3v zbE5n#Ys*nZ-vA#PcRdmGbB97U(zkGIlaV*F>hzzcb9%MaSv`GZ z8qbI&_tL8S?JxByoI#X{^h^6rqK-y^O)A0XE#$09<43TJh=&=6W1$wuoE}eW;9;0~ zoS!)joaN?=?`a!-jhTq8DKkG8CMJ1Qo~xwfRC&;iCkkS& z&r`TKR%m)s7gQ68%C0_JifK7v&SQ3mJ(%Ww^KGn+bVdp{;^OuG7d+$X2}e#Q1SS1V z859QJeg)r}2Lht+af7$FxE{l>ZSsBlQ2v~b#d^BQgjw|sl$Xn-RO4qNOsZNJWfjNF zxZg*^Kg61DP3$pKJ9Zl~I`!Y7T* zS7?$sBy+D(6Jv&`N)SdI2z*LkG-!l%(Mxh>Jkw z=vH~X1yveL4%>Ga2_CZ;y<#m*jy1ClgQR2JxNNotJ^Mu2O%s=; z0LPz&jKNIvnQD}jjuWJ!C8DDD_R30pqSZs+zKt_K9re$|=w+VlNUMJC|D|a?C>mXs zE5=m0Sq*`tDnynquiQaICqH7gpo7 zZ9OIr!IBq-hmhvJGa@l4)xsS!HWMEI9B&O9*3WQ1(`7hsOor}!R;rmu!yUcnJ=WG_ zcKQFJ?k%J0TDEQBg)iI*?jAHiaCavJ4ess^!CexZV8IFQ7Th(sLvVL@{Z?|$JMG?k z_G|C5_dY*9exyNIgX-01Rj*lN_Bkq5xb|x7gGa(+o8*UaEB!Z}jCLa!#qu#6J>6xc z%~WGzx1xL8~dIBvB3tv~-CTHbZ?bS6`Ow zTHSUAt&eD2XF?Xtz>Kx90<1a5**@kzNi#TMp=w5w6Vw z1Y2lb2&2Ipyvcn+Vs}W;JZJ$dyBT28=|puaLMH%UVJBdKAmQyADJq~~`|aut0&<(K zdlV^sDR>1V5sB!vr9;;<$=%${51g}&VLV^2AjfF~C6ZRzq6CKWrII8weYD1Rh#V5` zP?4qjmJmOdzwz|F5u;*BNcyjT%-NzLul;atLK^4T8PD+yiX?Sti*BLvOrSDMSj#65vn4c0Jp9x?)4j=g zvW^a;tth{0GQ>l;N`wdr9^90oOcQdD{Gv%XFWuHXyq8_maw{Y5?TUS%ZloZhXIi{j z#_e9OQmsw~CEoW$d=4G(@4H6)Q#n7tn}d8-=)Pp2SCQI9C7|h#C6-fJOHtCTRVRvq zEh{>QPPCD7!HDxKGKK6sN_#T3dfj*_r{q5GL5ZJy+Dtpmd{1~lcvO`0ehgmMeqo4l zV8RyFovW`{3<{#IJ2sHGYM2p!>#3E#|nf6n{o#aTFaxvZd zDVygF=pFDFv!b9mH__VAc+b0xw5E@P$C<+p zGXUJ{p8Gc4JqCTRT7q<5y=^j^0UI5cn2}B|7b8NAss`6npDdb7seaNX!S8Pbc5<&4 z1#5Pea{l#soRlyYNsHW2H86cv^Fo1h^We?}%sR!5gmcsNSvQz|J1 z7B%LULVq)`S|lqB6&2X`nlL~_O>gVYy{-PF!RPIuWtQ*il6qlLkIYgTxn@7Pugpgc z)E!5;8MLa6pWCO(?8@-e0&@GG!&lSi0Lgo)j+?_wpS)FbW!cyquaAYyZaN~c+^jz{ z@?|l3e;_sZs!YyKuT_{x$X$VOb*oVA?e?9CJ9tbPY}i)8JFcuFBSbQ&=y{yn#IS`9 zH{FoqbY1!j!VY;S^5?n_b?kI~hB`W4EZtXDH z?p=X}!26?qLw$Y)zv;##`EU=o5!z`VNSS$+x8(WZaE|EWJY+yYcey!|y~o>3Zu6ws z9w~c?A6k50MZO_!c8rKfAFeIdXO<*Xg%nQruc*3J#8on$N^woaSO|Iom-~?u?;5t~I-y zs$3nVU_nLPOvi5gko<}XU9B5r+RDNG zMYYk9BC|KTs?mNq|h2Vtrw3# zS*ko`+~WI{Yw*ga`+HlIem2QlGlP$V2X6<G+o!#PJ+3uqNVI&zaXZcuDpAkYB0} zz@Q+gAGtrvBogk96=R^`2@6MkHA*%mw%xkl_yj{GILf;%C;VufP>V`E*-9|J3AGDU zf%t(n%(2GkX^j12QypC-bQ`|HuiKRgnOQ?N20S2QMq=(8k7xLks-kwQ$WH*%vfYkd zjn}0GmkooK?OSB5A$YF9tnXhNt6jy3HiZ`7ple9?4$4k#w;OAg9x*#I!1{oeN#IKo z#GsGAB%dqj;|o*+R3QGt8Z1DY1Bk=Jc{4D-bbn|A=H=a2H!w6-k9DiwFjiy=22`~y zaW4eV?g6F?paxL+`kft+<_`!p0opeJLR4>!m{77E7h=|^85scETxb*7cD8^3MPnsY zDIsQ;@ zrq*1?IycO-P?#kzDJu0iDGDe)I7A9OO1%zU#KXEI@dme*?dWTZdr*rm8M?!Va8Wyd zlZ##D-cqA|!>W?a+tSVvVT>MbV%q7I$gHdGQh4yeB9*X~PauNI{CATv@@nQ4rOI!(D>Qc8&u~5^(S7sGW8`g`6TX(fbHx54#!f z^OZ;13Z|K2U!bPm`yl&TLh}_kY_y_qxJWakLo{q|>0Rql5zpva& zGy5PGUXq9x_~Z+0`c)%P+wA;_~FAP z1#H`;CXUX{aI9ushQK2e5D0|XARftUT}uR#3v{f0Qx%FYxqLg%+Ov*)AsOK&A22b~ zAN;A^ne3P>$<#25n&K0lsT)&G(DsSR7WRT4k4xCliu+KpgJH0RAIR(lg8z~S11rA3 zrC&y`f1>}xtFkB|+kg*56^uwE4#zoBO$#Mu^~jI1MKLJlYC$;sBG9q)$=inRE~{po zWfzt>$PX_QI^oBG2A_oUcgo+F;HB9qP#<2h?-=(5J_GFj*@sZPaM?No9csWJ$d`c; zO<2bBc*|wG_Z*6DlUBNd65t927wJmwpI$*GN#P?9gY@h7v;bf(%!HM%MTbFe>xUv^ z_g^%^zGN>m?F&o%F9-na5Rj`8WH^Hcz^(Th#!7iC)ozRp@FW&%63gdB{8))=0m|gMI1E7YO-FRwEE{q5lMO z5R8|sav&H6{|Oi%0$(cKAmlHR!6{$ZfM0$y&>;83aFm>~i`qoM*D;MHX%J}{9BRq~rV*-k@(-^(2*pd(wcHQ(x6pwqqaK3_ zwl3&wd4zSavWY5yYEJ&2FAM;nvr`Dp{WCr2f3iW0fBSEYk7EDPkfnO*kOcvMiGG4$ z^!|rnyhH~<+kbpC-o5+nNU?cNa>P~1l$|JqN2^B#>gMhy~{c<+2< zIVR(eH0LF_Y%#k@Pm7)Hi00&{<|T+MC?HA%lzH#zeMFgl)Y4qK#;Bf zZpbf@H&FYLe|P&Y5jGHIzlH60CF=iO5myl8^1mDMOC+tt5AG$>1A;sLx55Qc_EIgI z@`XG9Z;P0K?jJ2;X)j*H;9jC=C4TTP5ug!Yc#OZ9IQW+<8vNV86$6Namna(O<1Y~) z5M`==E8Ge{_?IghyzAeJ0mQ*e6b}ix^!=F{E;47xa~+95Z;*HJ#hMzqimV_8PiT-5NOlf}0TESYJf{ zMlBx)91Spl7PfPZQJ|d2JdldBDOFsb6$~gd-c_24&e{Ic=iocZdF5EX(7MK8JZ|UJvKpOFaZqo2 zR@iY>sI|<_?rlGrA!)mVZCK3#2z(kC0)1TZvsDNoPizolfTeE>EqJ7>_V#f=UByRs zr$Lv^lZl;2OqIjX{VYOkj+4Nrk8J>|sT9F2$_!Ec10gj5wwx_OzluPgs4gMbHPq6w z-hPYhs9so7eS%Z1$xE22t?|L6W!H@mB>LA0x(EsHZdST{l=PT^n)UhPLV1`5i0vHW zkxV`kKFdrCaySa6XLy37QKl%5yK7Y7DiB8~7>te#+VOk`Jia>5iXDhZ)x3wapDj@b zdueS=;uZjI@5{b2eC>|TX%a?syz@{PpFicxTk@Gyf{%u?-?gVKpAYeQN9ca--1;zp z{HQ~KWpaqsGF*KssI*?jIAvZq&mM?Mb01M`8U~k_$_ww+MVM2Rl6tv7Uo?{pBMK$*Nz)J zw^{#q5ITM#l(+?-`R;pT@tJ#0LoJE9{f6#QEWZ;yCO}7R+&D|MYk)zyNDixZ{!V^! zm_)&~h5mVOMsd_6xXpgIH_Lg>WcHi#Z1XGxcYzOCs1q^qU8bM7y%wuGJBJ@w83)bW zKaNHWE!|>evl>k)O=%tr(;}&hNMURWzYjs$d43RzI%*fNxU2dGf!`OZbYq==`bgbb zusIQ%*&A=bIS=DDL4N-=`o2q4K>k>Kt_i&Flsfz70BaqK@Uv??W**cGa%JWPtK8)T zQcW+e2?M(4ifN7OP|5^27XldnI9B!dHmQ!!$1X+_iEq#!gZaU{T3g@MYCWTD3RJHa z-YsY3(Oi5_(#t9JC*N52Hsf4-Ma_j%CCvBiP$sOv8ECE&VlQu1Qh-cW$fRal^~Q=( zYPs8$5wR1(=dCQ^bwukfs#s+m?Zf)}B^sh0hIWGo!>?K!OzFG!+%@=1tJ1jv+#Anu zqd_pnu_{){N^}lcNsEzKSt&_lHaASN^U%yTu#yR{nKnBd#^NCR#V314mNn$ipL``0 z9vIGIB-nN^JE*hBQI8b35M`?d3LGTIQ*R3>2B0tN*5sVz+3~3T-l4a`G=`IS7Mi?g zw|o33G`kI1FYDZVk_kU@9Oq)t$b*BwWXgf-MubE`0%@tLUMk%nK0Z)ztWFzyA>(#m z?IhM;F8@|969>%IKb$UUPGzq}y&*=tYosx475m2=p?p_33(F4z!z{GC8wvtBk5KAJ z89T2mMlzyP0n5jHgC7dv>;md&_kl;|@%ytXWVDWDn>Jn!rRd09ktVhs_Zugj8y*eC zy-K-OXIhHb?Y2%8&U5gDTcjS*P&<{b*5{tHA8+mO7;xLN<%b|h==jg`P5YR?H(-0{ z1yRIvg{T-?yGwoFtXI{3HZyvu8Muw8vv`vpO{HD>kQw-=cuD+?n3o`n-JY0jbFO z*K#Y0qiEbX|F52Fj0irQs2HK}IHC!&g9~9S=7oeN>Un6&dNZnoF3~`{-5unX!VA3W zm_&#?rgV=!Mb0mem&@P~S+Z+-OQNg#yrcy6rw8k}2I^qTJ?E)iLim0@U5cXKMaVcAEt^Kzjiw0*=&P&GE{_| zjF*nn75f9P>G|A-f=4 zHfcoZGBRwMdXWiLOnv8$NY+;2mBPx}n%oII1*Q-ky&zLvZt!GC1QNy}RwJ|2ZGr~M zQDD>P;Gkp~^N>xc!vIi&{Bnd{OJdv4IIkOdC;GBLj8DG(tfm5 zzW8u)jrc6|rt+uPRddk_^5`w}^ZZ>p_gYH(2XZoy`=D{jj{37!tv^ zq+`%k5B9?XUH630jlftp7={l_hAZT)Eol@Wyf(A!N;yjpjX*L-Z$G8=GnVkFRbeGPjE|K?fk!Xy8$&Og z7#o2kNK4|!O;xlrQ{1IfYu@G4?iHaOzGumItMHi3^Xv1xLl&;ri!#(;i%PYK_>C9= zp;jyAuo9)h1*0nry)2DJXd09HwRQIJ)_cs7Klu|^(S*eK>n5~BGOW9`a(OJQub_*) z*blAhK?ihYgl>)FsS9P!Yuc(wps1hX!i(-h7yDM4V4NMR zr*EwBgrejD1wl{{BS>=x{p@l#HlBPYTkyvt5}7A=c7It>Fov?fe_}_kp7WXh2hRy+vKF(WYVX z=9Y940{rf=V(evf-580Fn+d%i&coA%zi`dWE2@m1GvrDLqvZwNDzSRR8j(k)^aeRH zGy8qqa)H^Fh_BNl;d~CnN9=&vygv*p@qaC~*J0ZhaS8?QGg@5|{+N|G9iknjJ3}+; zt#L5rN0oD}yHj2)?=`S@?hM#P?hp=gt~<;>(Hst4rzj~Bc^bv*kF2VYhD{+~)g{WX z<5`XP%4Vpe+#+AgBk&1`=9m(F_?BiXdC>fvL@k7wDP>^Q2%Cn@GJ`Ny0bVn|&hGvWFRG{DQ723x6L&I&?sKdj3%g+>`r^kBmt|9;68>+euLb%dk zI~l2M6so!_fwVcXu_m5IYtN8$kzRqL+8yK628Lw|J|M6`7J(htF~`kcrrszg5_vZ(GHJv9ht4MaL!tJ3sZqm-#d$gS9*xh-Ij%wqIDC(Av4e6nNt;Mjb~f|n zwWVwC+Z6j3K2FeDDf9HV?~JTrK#&&E17K1*#S7=&lpsXko9JSs#@Q~E(5sFs8OWkb zVyFN(0b7_klpvUtXf;#`64oU(a256^E~iLL zKd&Gqw=Lqpc%*n@KoC7dT>O}%()8W&WNSWl>q$(k_Q>%8<G-o<*HZ=95*dw>k_KB?&}(qBguaH}++OVNhr=^}6u|H1jT_w> z7eKS@G$09Z2-O4&V2UE{I%HuAumeX>f7|z-x;>T}m5`?mV~tu-z#mKYO_IzvC?DN7 z?nd5mZw2~r{V<`R3Dl^;BQRc9uQ7wQ3+Lat;$u)VzoArXtgsgfns2p=Z}&+*NI3Gq zJfo-QRF_Oh?J}Fd^qw}5wc^mRnEFZK;3R=A|3m-T5&Lx+=MKk;aq@zKiuLM&4f~Kb zz0p0h3M`bwcmNYxuE-5U-czg$0V1k+T}!&;4=$i$;}2$dnl|d`X_JtJt#0^2`Me|< zoZEwO6La->e&|%OK*5alFe7gxF}!(mW1986*26ISKFv>>pA1`dpD0=1v9a}WeOY?+ zRZP$9rU){oK7+pIhSE8N(aIAtWWEC2KbpI*3I?zpaBR3B5>oii%zz`o1H91yu=^;I z&dJMZZH|dwE0XIj4h|tA1o+Qa2h!keNBd<{2>VOqQwONji?>Q12=JUUNqLQz6|8at zJ_l7)i+J6)^-EAVgcNmyiKp}kB84YMl~Y zwDP35!v6k-CKicA5h_*e`>|X}q2ZZyNk9YkO+bfRyU*uDUU~)R2S1W`DVj8;kY2Bl zXw&u`4KgA{;3m$}L;QJ+6@fp?{W19%9_r2yu(-7@(M5KU4%>6aNB8rYHF{vpS`h3r zWKHu^_1HFu{F*R&AY=m_wns==?O3Y(2Lm8l=z(2BIoxvsD&p*`Liv^at2$`pVq8lJnl!)hgTvbmK3m=P@=Bj7%_1h@g;&<%1tq9S@AH1}3FAkMZet=aNm zI}rSNN9hD7YDTrbZO}%y;~dqM>l9_m{*Xy6%lzU-r7~;&QB>_CMb(_Q#t=LVvQ|w) zh1vIV#+~oLn$^}+vxrI;?Fz_Kz1PP1Y6XeSS*?Ajl9Nem2ARH)s5%!VkY=^8ehtK9 zmeM0s`;YrV319P=u9HSFY{7)9hO7&H3qpR~7$LkQ2PX*hMIZu1BmA~{5zZ||W(0fY zilw<&5V+v^v0l(`?(O_~{ABfZpd$P-MRUR-!u(hWBR~+CdDusS=|`+4f@GXo?n;WP z&q|`}?neStVhaX}FpLTct5=p=1E7&KQD&jye}`HC$~Iw9#tY3G8B9(KN(rp>ZI1h= zLSRMeW0A{lx>h^4S$&LI^b)_t;okb7H~>FM`m}LTi}=^E(wH4BJsK}e_PP2}VO2=~ zSiVjdcsxk_!B7O-Fd91T2+{EHy?>R9ctjO~eRZ-r^MeW%DV4F7UX32^iCu@I(Cp&~ z9RT0G@x!OP%}6oXxnbfvJ)&1Qs<&jXvH>!*+x^axAm%Fk5MG)+1d;ucWS_vKFM>T zjUE8imI((1P8|=F4l~w;@c&NjWhzRQilZ$clw)!z0HNrqnQQOJ)^! z&l6!DQH4Pwib&z*Mj1M}&|N6r^Ds(AI8D0Ll$p=(VaQ+ME$#7?8LX5~LnFui9&#_q zVG4NrBIpC`fB+6&wSRFW-G7m^5u_*$Mc++knrPl zNzHfmlf8y5O(*)3GsT~W7%bVbAlkSkVt?IDAiOl`3A)lR$x#Xx_#*fN?2rIZzg#6i zhxJw9)h;+ErSLdjmB+F31LnL0)m3$r{Gl0fdT%$ot;pRZji`VSu{h?TE;vw-j&}td zS`C6CI8_j+OvjS}4mP(d?0&4P+w z7lFF~RG!{YB4sC?myM?Bo+1oGS#N&UZ%a3f&pjv8%IHGTF8o5s%GBRSwtizA$*lmd zGAs0THcLM}H7AqRj}7Y3Q()@^ev50=7`f?Igfn%s^112ec>j$t(wLP|+x;Pv{Q@aj zL+?(DRq{c07zP!F>|ne>MXdU|7$d3c(&9iadn88DKnAB;jSY%?AMG9QujpER8ZHcl+rQ#98#&bVp| zJ?13xs262pUk|{6dbshOO`K_5 zJ>(ohiWH{dzybB7_3tT4;`WSTz9(FdHSqbJws}zp6Q3CZCVleUpI~>t+wO?{9=_4&?Dfl&A?5H+G&U#!h>T+DyC^Gqy5ZQmUYMx2^!RN(*z4E)Cy{UqDqimAq!echB zI2H^5hkyfABzUYuEUKsyP&G2p6nn{*9yEzxx+R$PMcDpVPr|OklIqcQsKHRcLC4f4 zU&ScbajB_RN23TdRi7SKf#>&SMs?)dnytW4cO#rl1}9WOXF095$qbBw|4eRkB%w=- z*0#rbXBolc;`0mpOEw!5zKFnIr6K-F=^xa$dIt{wL!R&_6KzBo4gzg3vK4+d2Fnjc z!ViPEp)@rU@5mA}Fd@ukZh2sZ6shy8!ZD~s-V{rWfT zm#m>64AOrc!@pDak`)X@-AmUG5Nyl84*TD!d&!~%!tnX8WB7OKUZS2L>Ru{WAlPMp z9rnLd_Yw^R&8?Sc(~K|T=r2tMBhtA4@BSDe}cZ3=v|2)(o3Xi$`=X! zpY;8zzxGFN%yNOoj98W`PhqZyJ)e#|sl#X!y_4ABDLJBAA}8(oKNQFcKctr{9un(+ zfIOs^C?1Hsmx$AZFOte%hx|WgdAT3bZ^irH+Y>=BT>d(Yf19;PFA+Qtb1%^*5ah(a z4*7pD2O4xQ7d)hfzmDVIse75=A-zQKK-B&G>yZDQx|fO_=!(BYmq4%|{yOY`V;cD- zVmIN7O!(I^{2O)1FO|9yKjfFF(zq`&|1Vq3zm~XB0LXFXgF@6@USOv|dY8o}0%WA; z@vv+wd_!%>REW=8Z6Bd9LNDQF!DLRQjsM^7qyKd)h%EoF1K+ z;8$$-Lw5J6y(0u2r1<=d)_deVd(6qk)(z3SKU1dD%7=Dwp8^Je_?1Qjefe-vWIB$m zk`(u=dLK7mS6we1yMkwfy5jfOyYfE@R-E zDL!$cGhzVQNq)#_J{V5a%S@|pV8Xedw}1Ugtx}enkx01K3GCNT z01=UGKje(;d_PcU@SUht_uHpjj)G})qgssk9XE_sZa&nQPZeIq{cQgFQxXXRkgW*r z3xMz`_X2zcqxAt_0r{zcfq@}`0U?m!5TMsI@2NIhP2jzb<=zrsoxX3VxP6g1QrboVoAaM?QXbN6sMf;V4AY}C$d7lw{#0^H z_K2MQe!6vzp42<&G4r1NvTQz*O$XeI!yNvm?6mcGd8E~dpWN$xvqiZo=hN;E{CxV! z@_efefA_~VnY>1#pO0@FDAAzV1r8S)BgYW zTbfWb3zNi>C5oL>jV9@_wK}()xyp>Uf%OpowR6E>LS!XEK)90jM~Y}XR^6S61o4}3 z!J|RlR|t*)ZJ36Fdn`W?VdKEn>o2CAgff+M{~IN;r+Lz2xw~DrW0|+=nRiDDfl_B3 zx^R`m*TzinL0m$*a0ci`cM47HM(3@&68*WW{i-;N#2oM4Gipefpwv-3s<5Hji>V2M za6Cy4gU=89HQGt= zyM60LwKj<6>#GlQ?(Wj!jm$op%;V?)NOb*r#DJ1s+U2_VkM92yiAU|n=#*c4{}<~2=5Muqo}SV@(sX;380h*O^c5eR z00PB7VU$y)|I9<hw%lQr?qe%k5sXpiaM1g#vJ=l*H%B2sD3dr7t2n8&jG(6v&6rpt#_(uJ z1o_Rr$woSi3p+np6M@ZLoz8tH?)~%jk-L{C^dPu5SDx#kA0l1P_g6xD-&?+L9!{}Y zpG$>WOHomHW|QH>9i+bDrA%yTEOyj!O)8iBTI_#bMHsX7jGf|L7QnqLwCX5MQ74s( z0n08s2v!qD?MVxtDf-$-$!s+%;i6Pbo(eiF~()`(smd|GS;(-;pAmO`~T5AE0RZTMdd&qm!PUm?Z=Ns#e1GvLj3dD#oh zd2RKZ%tLHY_sLCf^9=^iP$xIN^Z{7a^RpU1nFfsHc$tf9Svar+JDz^wDNxP)w}INJTYNxN}8<9U9~;d&lh@|$OHTX%>c+<~_q`y0X2T_JuaZ)r_e7`m? zgbik8)nlR!&5Gz1>gZ8n0?V|-xmrikB318z!IK&e6v z_JeTBVrw`4VS*9>xejbuLCxJxoU?m=!oLB@+ad#}pHEd&VcH~Rq~{9OGd^>yAYj3R zYakBNU~1~Fe0ef6wY9pg;I(p|NJd)tbZE^`SE|aF;MeVYLIko2PLF+G&>fi*?*Z~o z(6O3Qj9wv@-^>`1QfXuexrOeIvHK#00GQknZPgv8mAz>g`26D(i(-HZd7c?A9jOWX z6NJM^(xsv#>!51MR0vByzrKW}a~ns3h6Vygtu?}3yQTMvgy+s8(Ui4U*qE10!NTDd z$n={AC+R2T9!Ecdn^(?NR~c8bw$0~IO+x3CX^(%@rhe=pLqKN!2qA}Vo+8n%dX&=Rmta5?X$R8 z6~ZZ5S2nfJ>Nwq)|c;MA>0Id)#_0xt=BA1_;589w>H3*Yx!Ub#-) zyP#b##~BpR`c7eIIJ$}NMds{4FnC}eVAkmhJK8_&e_Q_TomP_o_yEnUIeSp zOL`NCBcF-Xu4iLWj?jk#(Ts+x*b_pLHGihxD!JraqLm-R)URv-G9!rX*2-TnoA*|C;! zv$S7#Ht?+zIT4F>HZ?R9NzRQ8^aX!)V_@wGpZ2!8H9IF zC)F5whA+f8JFF#(^8C~&iCmPnnCo_#{ER3Lcd|+O@q%XRf#pNp`0}@K>lg`~PDffX zsdv{(!-DYz7uLMbEaosrXxOT+8^%@eOEa);A{*$4jq5io!80$u%8nUyd{GCh`k0(` z2(R!J=!h!xW*Rz~8@<{w>509Mb<|pgr`RL%3nHFJjD`}uhw|Q*Q>Vv#0_$$5#xvxP z=McCQYpo)8Jk=o+F<0#!kGMncYAU}uu1tNy$al0oHbYc%`sQO_8s|fXkLwcIAwI7e z>!1>>XS}y;QDc1k*uY#D^ma-0Y7#y~QCE*2M+|LfjNs%^&Vqu#Lo~B1Z-N8bYxwv{{XyirtEaECJMLE$HzR!XZa@rN&1*KuF}{ zNBXC@H~Z)e7rjHScyPSWljCL#ueim`M8SdD;`mRrC-PMY-`SGOx?}B!G5mr9cbTS0 zS7#JlM~(G}BTfS>fwxZqHYk}^(hHZpjq3sINzJ1&wGeS%I;Hw1M1Z7O*B{*YZ1q*2 z(5UKP-;co2a9fA4>E2s#(v@>{!mmRvP=(e?WmoPAYYZ7PS`accWKm0~sc1(~9REDr zjJ=}?p!~SqbDB~H$9X^uN1@x&kh$5*@r89H&h)LsT7oaG=-Ae|Hyrnec(fZWA33q3 z31tTIOsSD?uGgsE*sh&4e%pjVYaWUb6mvw4C?Xz)Vd(g%-Nefo;b2J#Pa`5Mjv6q* zZC#wj4>391RdvH&>^WqgU?Ctk>5SPag@hxP99A;PCc0;xk6F&%RGO=9cGA+zF={>{ zPER#p%}vGlQVXp%==)*F*b`Mb(M|1rc@*z;y~Dgw4g6kW@}QS#$ZnF@^6p2kV6d3`BnXN}GW$QP++gx$PR8{Kx;27soUj;?$ulR& zW`0|SYR2|yj8a3JLt?*#?(;zzZP3<-u7Y2)wxTI&ay$IGN@MoZ2zFLEp@hDqbvLbGg54ZQqQRYOzRMd8`wGQS$RGC2A z$9C$kKZi0#i`9)FqwxoZS?##7HjeTJyxdXWUX&->*ZcsQ8(S)Ok&<4YsF%)6`Jyto zmvz6(wknEPMv?(LaL`^vb(Il<=e7(}wk#KE4199S=BFWUQSSa!IJ`C&98B+XR5byq z3}?xtZ2`{)IyLa(3L=>O%*4>`RDKznF>Di3$J^M_JnVWwk2^@(R4o+YHH+x-%!3l- z2f=m`YzV=PIEdU7I$-aIU*s)I&jw^Qh0+(I#bf-cReYfjzbdx6q zR}}koF;YO;yDQp()FaJUJUSxJ-$+nUqB=67P6|s_b0+EpQHvF?al?qR2R85?W#p{C3AL^%xC#vTA*rLI$0AEc zaar*nA^5{INAx}xr%+}uQzx+@s`2w5%~f%}A6K`LvALNOf@ZLoBB@ML_U{NWhR`0K zQ63Ui`Mi&|BriSuny1Xs5Gk|b=ZsyDT9f3)JM>pzWfNt_8fvEekVvHXoE>MN)C1gZRfS<+ zqe;B}P>s}AB#Z4X+kR!64j1^wLgnfBKyrpV(r|nbf(~_oFU4wr=rccZbXQFaakj{& zFMu4t{Ob3SRD?a|hnW|utzEN7Z2Tn6caHmy`MU3)Rjl}bcC;SGkS8VF9zXMy^ACAn zF4Ye`H|sUaKi^LmJ#VWapAbE-UhC*!KsHqmgiF~>)T8z&KZ~023RgtfGglJI-vJO< z5JJ)x0j~f|gIfx#QG!q?m8D5ZK}?G%u*Asv#1bJskcSVpf*a*;eK3qqP-jeH)C8O)B%4sQGG)Yqx7q>wR3nnkE!(5*I%=F*ZBs6U>TQ zuY;Aj@0as?#BQ%6KgJh18a~%HjQ`XjIUaBo?dY?q!5zCDP`!J;LU5=0SVDIy=C&Z- zpB|N-B*?$sus!>(4+CNUY`HG1#oeelfyN;TO@4!9D{1nLy%K#~^oUPIxZXrmBxHl0 zc$DF%0wpuj%3F*yJ;aPNLqb=I)eM;<7fRc6fU7^*Uco}I?s=Q!0B&e##H%m7flu>w z@5b!7u27IgY7AyK2@LbB!G`Gba7_~!K8k32ReM)+DLGllm|V5QC6?ZfOG=5pcaDjb zR~E8mg3+Wb$*{disD`ps0>kc@B%vOXC_yG3+8y!7WAxoQv7;$Y5dhn=Jn<=_YiL%5 zA~&eO}NWCDQ3A`j= z_QqEq4tlnAm)A0q&8P85bS(}KTHi?FY(I%>9MBSKqMmMkKd*OF^{YFh_~g)wA^^>L zdWAr&O!p_bR*3pPUc%b zvFeFh{4Q+;myz&X&(SHKVNH{_2&8Wzy_SM&SCjnvBRW^o9X$mRKQLU!*IUrS!Z(-D zg#oIqEQ7&0os=38(|cL%<2#u|$C?@~s2&hniZb0>)g>i|qD(DxNgmChlSAHh~X0iW%o27myo?nHW zO!SkTR6q0O)@6n~^;8l*p`UO=n2Cfm10>ztgqkVbe#$PWkZh9ozn_v zlht}3OCH1qc-Y|lw+LsowTbBL@S> zd&Py&PX6c6q=+-Fu)V?jh|Bi%ZD8jFM0vK*?N|)QI_W^jvTGx_=IZhz)Hm*yV$WfL zYAio*t)CY=>bKM@NIviLttYcgD!UvX8suom&qsMtgE}X&0=P#64lUYe5f%Nv@}FMl z&2*GepuMRP^RS?=y0Loumv&s|zqu9YSK(mvK8bKZjv{TdPVH4=OKC zVlCR1?bfqoMx4q}E0^LKU@gONr_B6ON}eq$Z|#d8__mQwCq-RwS+xXQ?_|R(#lghs z5z-)}-H#Jf9s+{Kha$*Xr^YTC)%bOv&)+x86Bn(be01=r+vDFn;_mlsfaTbOdZp

?X-4D*yauKU3LcXZ7t@Km zzV1_Z4E7s@5MY3awn|%9*ry|aRC3#_5E*!VKyNWAIQ#cRfQaRgowV=U#+wGqQ31{f z`O+Q^!tw|~0Uu3YR1`&7Xh;#Ew^U_M3Nxsm5`}bH$C7YKHquH$?%H8dR#jWiaq?#3we6!-Ec|Ay}< z`c}S7C#iHth3gCN><21hY119mYrhRBdfIHh8snli?#)MuZIHD!UEzHc_dMCWC3!!9 z=8On{_=4$NC`G=5peX{COe4hjC39hh34dydN4_cHF6*1M%BYHALxW$gt0D{g;gXd|!8{XFR`4Rqp{n8jw+?ZQEO;Lw0QI`gLsHk4)0YD!$ESJOWPVzL5qi&QGr4EHOn ze#q{zuiLMjyIV1D*a-l#1-5+wCIDu50PHV+X6CMgQJ=83oZ|M=+wJS z@gYHJ+P)WX$&l6w3=lGhkO%LKdd{RxcAl5}c+ zS*Trd*@ejA2UMN641Ha1He+w`WXMP7Nh<^%+zfV4F#qW2AG-I3 zq92#<1bsw5rTRxdixbgzM-`A>0W-S0+5|s+w|l!|w+yg_bYHQ_8p;DmI5#DBSiF|4 zG!E4Ty`~bwu@Se_t^NrA@TQHO36S0H3n&Djg34z7qn4T)sFoVg2UNi*XjJ`k_(IOi zHnl&^9zsnY3$`g;FAu^r!kd|G+8&XUfB}hp$P20=bwKuaU(kya6;S{8KSA3gYAhsQ zg`t<+V*!a1!HQ*&)h2C}<&$0K3pfBo|BeKiZuE3pM**Y&6aYA$pfMMteXLGu5e{Y& zEZ^X}APbygLlr~vd!{I{=W;-Hl`j|)!0vz3WJVRit9r?@VY=TzjDIFb;m-uw|CylJ zKND2_XM(2xOwje82m;6cGeKN`CP?F-LI01T{8j+s*Cqfpn(ojmxw5cL??K zy`e&JC%@sU;(a#awl*+rbas#8e($zxtRaVi?1tQ&2y*}%fh3C*FCDu!dym_uN-39Y*jHHx1KLi1@QzX*g5{{QM2nLPpQJy4u2o3RRFWnP(Z$ zz@hD_5swzII$rs<^Jx`aFl~|?uuDu})H3S3kLEqL9UZq0k0(5HN?$({xIim>vM1RYGx_Brm zhNvW$gD>Gb@;dOrKU*a{i&5E`TX-HlSB`LW+ju73A3F2RSC9hu2n9wK48z zXL{2N=40 zXc$@=NdXy3>5vZTMpC+m?v@m3kVd5>2auLd=@6t#QV^ce>$>kJKF|Nnc`zp%dpS7rdz$F7JKrRNd(@IDdFW7>R9!MP4BjO}SzOdpnCF@9eGs#j1jDI1M{MF&! z(I5^14HZIPYZ5MMFrM!ftB7iUb}m5o6&%fKYImB2d=ky&6i}QNwr8^f=5W#vEKdS% ztKX`&*`;!l6WLKH(UKXPCgx=XBAwEJ} z`ZA|-k5=LO5C;ftxX_ua_c!lss7l26Huu(uAR9ZX$#}VM0{2&*%ulBtAw*|3`${=~dG1PJgDs{`W@RW~L6ll7I#{&v+<%C9CrEis6w;nB| znfD~7e7s4@>|xEfYzmkkz!Vd?D-PnYN;5ZRRR@Y>Z($%DfRGwsB;W<8G1MQiez-4Qq+fX#Wavi-$(pUNoHi=UGP(gXA8jaa&^MZPm=tu6L*VDu#K7xkASrxu*#@y zftVaih*gD9p0N+-aN&%~tbNWko!fbTxacYsMT5lK!3BTfjmhE}8Cq0P?oo@M;`8Bd z?0s&QgG{PZwid{5Q66(5&tUnOq#|T(^^IWm$-dXaA1T?C1*d3xKA>U#_HUFph`eb0 zWNqgOL=7X!dj(3RuOpXMq-Abu22)rwQ6_sV&?Vx$a+}vzHR~Py5!#=});s)wWOZw@ z3U?w{`#d7uRsX^I74}(A&tB&XA4LhFD8o<)L_$$>r-A{A1V?Zx*jeG8IJlz0h-r}T zCrf|%)2L~}T)P>EO3mL%VFLY3by|s+ZlhHRL^wJJ(aK}x0{ZG(n?LMl8h%EGxj#+! zXb$9!iC{Tet-&Dh=jj?Mt~{agIew@7$)s|JVBcp3Lfw4&)L0(8SZtO)2|^lbaXx+W z>SIR?X2SYzdrs&*_^XlDVbamV20CwJp=ZSS>ki)zT$R(p*Vy)nSg#HD>Ne&G@R~H- zYMPI<+BEXOM5(7c`s;Wryv~o4F1z{yaV82H5~I2AJ^Ov(_XHS{i+e#P#C*`W8Dqot_=TWi%;PkyjCWrnGiZ{$ zFvMdpDfUP8DF_PDLHn`i#g))z+q-IsN$$)8^WtP%zFqC`E5ejw%nSGorR!M?F<|zA zeSfc(p3$s^;{9BE&06_Wour18HX81*-AcLUKw`m(I8KPV0p%0O==>wzBOV!amo|(d z6|j+EF^%J71-HjwNX&Mc4mD=J}tSCfr?Z4}& zIT5fL;;ma~$9&7nk<(2n;jff?8DJNfV))@hdTkS*Ao@ty=AlP#gxYGWKpPktvu>k6 zr$Qxb<+|f%))DfYp^9Km$j`ym%{Y}38QL9r7bgaz$`%IhsjE#w%GiaPBCY(Bp0|LHJcaKFGu7IdT&z$hN{;i>O;@%0Yg^N@+?WALL>w3U0*wXId&q~Lb@FEjrycy+NZ7sX9!-*ph`%1YjZvU)lLyp$t{4r%DU%k&$~)YH6sPo z1|1LF@MXe#Mvr8#e;>01@W}^0)T0<%;*LqY~(VSIraRog#l-m@>#p93(#Z()S#6_6L}Mu60|Dgce1Ll%!`B?F*Q=>lN@=BWnA zwZWORC~lz8Y=ALtP~izg#}=f=X?_Hcy4I<^ZE2woQ@V`9I*8y1+NMnJxre5z7NOXU z_j6dIp9H0rO^Vn%l1&<7;AI))E1F*TzRrWPms+a`E4@oK4)m7x-Wxa_U;lafGtrCO z#CaZ*t;D#5L86PlGA_M2D_(fM!P-ej|Kx&6dWhj$Gi$9A6)KYkJk>5b^+!R$VWNvd zWib8%(Rq3)iXEI|r7s;QGK5mTbQR^aeUsE(#66Frz@DZ;d!au2xd2eZ-{5MSay#d= zlTCRedyDnse#o81*s$2Uma~dVUB(94N4MuNk<;d=%~-16d{@Jix4VYFo)xgHis9Jz z$ehhYa^AMOG}vo$kGK8ae0ji|9upAz`vFq>lYgFIeN+%0Nk)Oy#S2TZL zjtj`E;Eui?zb#Sx zOI)Kd$Pz+Z)(dMccIUGS>2hAKXcxWE?kD|ykJBZ^o)lc~o~Ov{XjToCx-@%`{WN?M zOj@=}zDf10l!^(y*w|gyj`B4XT&nRZe_U%}_q|PIZLY?I*+vBzTB~n&Wrv3XOL(&uzIkqOldsCM=kwI3s1U9^PNiWQT*bG>;c_Fnhs zc1B!lYkH#evElPHW4mZ$ucNLaM;N-g9m zq4n_xi-QIR8GcA=bXfisstj2cJL0tA_Sy87n=w4L5>XiFKGs=)@I!U()T=?egRbUO zt^qEctLIttxUxCa$lf$Wh2MgepkNUYvX7U4WM*fuWo{A&c;T0T4awtqKE51(yDtZi z`WlM+)>c>d7ou{TJ+i(ihSO}^dvoq)iN;!boQw-FDs;EYY&<4GwnJG15b5F#45S8} z;sZE;{+>HBzRIk@+l?si{H^EAI>D8(;Nw`cw8E*mV5_E*>)&W`g6(d_$t4CyLc<3D z0+P-6dHVEU41`Gr=^y|Zhh81fbCG76O$sHICTX~)N74K$EA}77z)kXqw zES(AADkaZcqpX-(TG{%XHibmx#d^DJha)-hN=6N~i7; z{alQfwv>s6ZPTSHTKF`IH~~M_yV)(8jxgxR#X6)$>%xwF9)&dx(I=Bs+y`qIpYW*p zPJnzuA)JOGouB))W_RmbJt3v2cfe%$g55G1)#vg-tI!fAS`zR;;H$+xDX4AgJ8 z_WR;KxV$V-4C^y^vd_e{Jst3f^saajDrMr_9{ZxTuZ6Q#o&Nq*9Azd7-EfMqmakAn z^(o%ZT(N$o*{9SOJZxV^|v z+7Sa_$y7`EHWlb*qJ6@Bm`;SAv1`kW^_|joWI8xezR*D)Ws&5GQ%Xiv(y(YeWmMZH zh2(O{fID*^qq~J05k!|IhY8%szUQ3hFZIgI=tvD^t8S^`c%CO?S0dKwqxF{evj!v0 zQe5;h>tH-_{;alYFB@@@eidHBxRse4sp;{gyzQ7;MnYb8fPeH-yx~x=rxyf+^8-bg z2UKNEdxw4rKah^TnbE^S=%t16$-p#Pt)+lzUlb=2A**z){RLLUn%au+y5IOfHb)V0 zjGD;{jh`3K-gX^WWXy+7)DYSRcqrnJA6TSg0ZG5@xC@!htCo0QE`R>q=Fa{3)oNRu z7jnAq$xpdS-f#G}B=eIe1zrkIRi%E4>0XSr5xO$;MmB~HxFq7cf`?c@sP>*UHL-Kn ztX^5WbUb{zS#zkCEKI9-pnS0;M}+ zW)1SamQ$*jJEOt5XB8cYags~qgHt@{e10w&9G|!9E1l+;ohdAi@69D$k$qmQ@W7lX zQRA^k2ld576fNt!?c{3GszR8z^6Ty_B<*jNg_PaOp_lwMR}iyf0|h0ig|X$If%af= zt;Z|zs-b9R3}2L`0uzkCb<+&sKF> z_}8OBw70S1g-#ww6`iaZv5Zg1ETC(HGlt0PEFPZz0rTi17?M>1}OpVPIc>LkPZYll@KL=FR{z zn58z+iP;Smq*9eCYt#5y3(aI}N$Apmw1JLah_|Zo8!|x3A!ok zuTbaNi*;B~nqg}h%OVPmGFUNnD~MTyVtG!$?yZ4I_I&~@&!!BXJLR|IrseOx7EH0M zG+*OrohCDGsyZYmy=bcO>@7LGNJnP zI_E-x>pszUQ9sfHj->8szRAxUPERU{c&4)AZktu2^<5h4hUSh-8_>y4XZa#&Mte`9 z9r?4S-k^jdRkjXRJSyX8wT<0Tug@)=_cP1?5-P(RSwqK_$!*r^@E&IpAzuj?=o6rU z3*h~c@2<=89Bw(q)=zRC!Av8LSloXk?FBEJUIN!!MSUw=Fk#G_;8Y%{lp0z zkP}92Vw3S*>8l(20oU;TG@42Jz)+-DIsD*kaOm-}f056hzTvZT>CH}k-&@Q#Y|m9M zg~&Zu>#nFEwXj8fZ=(t|CN~#vD(gIRiMj|$_9y#~EfM(~*fB0A1d_#G~s z*U|O-6Y1k}sx?X(vCrSEzOwv$-;Ix!$ji-R{7$(Eir!}DTeb3Ki1t}+rnLhbPcWhu zdj|te0~*->$P&Y6!@@GYq3!WQ(CUe%-mGA}#XpysFN~!mN;>q}A0b2!@!hhGAQ=;B zf+!=qlhFMYyyqAp#wP@&W9PnD7-(G>G2`?mG59CvV=^aqlK2LC6Xis&h2G_^K6i6t z{a8>3T^hnAi)x=UtQc6;n!eofst%!*GD`ben6CMXkm6w0oR{dUf4r2Ng|AoWTw(mx z;B$GgD=d2N(2LZFdBX# zb;;r!xiYMht?x}88ht*q2*1To`8akUc~tb!oRXL8gYv&gvSt}e2!!~~v-1fn zGD~=$O*<+ zYzygbm)vF(<{mnD zB~#Bzg{y>>SYS>%R<>jJ&h>)r;{i$TRP%|ZuujvuM~GkLEBC_-S~hA!re0h-lfWxR zI~8U%?=5@0wEi)v>YC5U$iS##&~=yd#bh}Bjr@3=W?4>H`h3H&1}icqrdkC_^|)ec z^T#w^JfrP&_JKuYunz_S;z=Gl4nq|IlCl1PK~$Ek=n=K<%s<~|lKfbd?#t_RaWL`{ z>NfwS_gvg1W2Ia4Aq)VqCJ@LeCe>>_ZL;{W^xegY@rM~EZ#)VpLo!U7of1<;#Lc0~ zZs)edMYhyL%;N4zz=1X&CkB4xQ%uUzx*NN4~@CrAGoIC?_pCf?3Plla#*BG6ZJe^H*C3Z-NO?hSmbk2Hq}WG46g%U#5DWF-KVP@|^>I z-RL8_Kl!W_hUy2n{ztXoaUSO#3dx6j06CARbUqbY&zU5H%y4Og6s8V1` zXlg#=Z{}1lQ}N~aO;Hw7MSRZ#B$*Fq_;BoHWrFFX@yA79=_uwT6IU+$JC4g6XJxSx z`t)%6KRlSS97?iUex|$sie+StQiqoYH?DTGH>}-ydT<}=sp635zf7fdou_GnA{G+x zy&xfo79De{Iy?sP+yza9p}qws{(~H0Xwwb_6oDKiJU#FUHe~nMjRME(rsCSum+d(m zr5<{*X^LpaI(~=5^1?kvKVM!-i+&%^MS7y~8}B}DL=likrhmOXj<7AxPER*ff!0^o zSQPW-0Chj$8zqCtRCUfa8)bixAf6&}q(bx(NJ4r7YALcK2l|$%?Df*(U49X?0EXJ} zr$OF78y;a%-oF-EHTsNSqC{6IS&iJ(*!WV6nBknOqrGPIC#Coj=5Axs#f`k5&AZ9J z4@b_w105755%^RVEuv#W?@rj>d<)J}!9oW9Ky%EH8;eU}H{w=ix9&T}g6Df0*iyg0 zAvyRR*wmXY5TKk4YNGW?XYzC?Cyq;-X;Y>;m^BJ)(KX2U&6(Wlr_EagO@N`U0vi9d zu^B;)^e;8A3Hs~Wv8@xYCwJ{zlU?4^1TG-}55+H&4?`i38O?Trve7@FN^CV^B&NCS zqFfQsbm~gc8pq*k6cv44ck<*7{zJ|_A#=xW#iH3ejz0McH^PcKONi65`FW`ktA&Y- z0|`<8tZd+;Om?+xJrdfsF;f>r7^7f_QF;G6jLisWe+@#u(PBoEq)Y7IJiR{j8jA!D z_k+=(J4o#iQQ(=8HIxDUJ!MVy)<>U7xe3IAiWlWYH{bogP=eT^?H?Fv=^>~(F{oZDoh3+nhsSn-=Q^H_q5PAepLqwAi+W`?!3r zPurxPsX~|$2Sbzjzli*QLp1diX;Z_v44_~Jyr&5vl&34`Yc_I8<#O?oap*f>7A*C< zW@EJB(}``DCNUbSKjA@aI%3K)=nt(b1pp6A+`J&5VY7oePW5IwJIR9H_S||z*$mTy zTY=GvH58-2$oBpmQKY(sp;-bN|J`Q)GnBVap(t3BUz5V44AH#lwoy58@3K_knW!d- zs`G;8(2#ZfoVN%h)4*RG)6ZSz9J4>ox#YjSugTA1>{AN!wd#iXb72AYN_6Xp$nfj^ zT|DB?9MgYy)c>%!sW)o2%MF0T5%g$=q-8r;Y@Q3OTW3OY&I1hzr@ut*VlXJu3u+&t zX2070vpRxuqpvdFbonwN)YR5-Km2Fu3Mn4`3Dbi(QyzGDa^N_ zbgu7Vz@M*wwx6=AUvL!4q5hBir(h|@Bg*6V__-b+cytsrZ>je#pcZE-=tjP4M`g40 z#=t<@knIZzP;?JN8vqFZ4+_m-gf5`~xD3lr@7uY`D3h817a5MiX*e>DO3q3MjdXkD zNg1*{@7OgbpR_u70QC-o50B9i*Ad1iqS*$_DiiBEh{U}4O=^uUnc-2b#Erp6nZcEG z3K=_ixCKET$eUSmfj)ls^!3RYzThH6&r7sDfbaj<7{PT20{{TIE|!jr!gANKc6^cV z*ZXGjni+gc)YG>y(8yDRVx*tPuf63TvG=;#n4KpD;j(TRU+~PRV5O&RUq+j(t(cL} z|FOib`>k@Eg0H$AKv`y`zo!$=C@SA)*qWYp$>Z~6##s&w2a2{}=-9w1`ae2IY65YJ zP>8;uH$}9*QwDD5H$)1q&BL+w)>PZY5A}#Y1xSf;V%qSMkv@(}^V646AE4j?b6JZ7 zwpqv6J=5W^Zs`@yrd=$?2tPZ%$I%}B;V1EG zIZ6h5orf>~78jK^DrZk5ACd^OqZ3p*!1GL@++B&iwqDKy(JTvgC~C&Z!RJl!F$X!O)!nzW=(J z&r^Hy|K>Tu!wd%fGW@(Xo%ZkUHpg9ow%ON#G2sf-#L1C+?Q!=E1VLp`5|&GksIP&y z^#82*8q|S4c{4Z&;orS`L@>Y0q53ZE2f?mR!y`m;U4@||zDxMmK>+N+)3bvEN6aWi zrL8g;LdHVMWq%04T1)bfeT8_Fx`6)BrI&(hbvViJ)7_{1vVg*W3H=UF0}=22>)Z`m z)Zwd9HumAT4Bnqj3OvC@2Qc)HegxW7*oh*EKvJ*iJEiSUik#-)3XyA) z6pd^e{qR>>rv{PC(h2Qs|8eMLKbiGw=j?0w!!tXY;w|viLC>lKvrRCyW~;+L{X_u9 j*2GURl%|*~7}9A7-lr27b8FJ45d5Nlf5~e*fpPyYY0rVS diff --git a/dom/media/test/crashtests/video-replay-after-audio-end.html b/dom/media/test/crashtests/video-replay-after-audio-end.html deleted file mode 100644 index 9ffd6078de0..00000000000 --- a/dom/media/test/crashtests/video-replay-after-audio-end.html +++ /dev/null @@ -1,43 +0,0 @@ - - - Bug 1242774 : video crashed if pause and play again after audio track ends - - - - - \ No newline at end of file From 0a8ec492f8a7e828bd41c26af3ebe28ed0ba1a58 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 11 Feb 2016 10:38:00 +0100 Subject: [PATCH 177/187] Bug 961323 - Add a method for finding shortest retaining paths of `JS::ubi::Node` heap graphs; r=jimb This commit adds `JS::ubi::ShortestPaths` which can find the N shortest retaining paths starting from some root for any number of target nodes. --- js/public/UbiNode.h | 2 + js/public/UbiNodeDominatorTree.h | 2 - js/public/UbiNodeShortestPaths.h | 331 ++++++++++++++++ js/src/builtin/TestingFunctions.cpp | 179 +++++++++ .../tests/heap-analysis/shortestPaths.js | 44 +++ js/src/jsapi-tests/testUbiNode.cpp | 358 +++++++++++++++++- js/src/moz.build | 2 + js/src/vm/UbiNodeShortestPaths.cpp | 31 ++ 8 files changed, 946 insertions(+), 3 deletions(-) create mode 100644 js/public/UbiNodeShortestPaths.h create mode 100644 js/src/jit-test/tests/heap-analysis/shortestPaths.js create mode 100644 js/src/vm/UbiNodeShortestPaths.cpp diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 0e1a231789d..c8e2c110fb1 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -808,6 +808,8 @@ class Node { }; }; +using NodeSet = js::HashSet, js::SystemAllocPolicy>; +using NodeSetPtr = mozilla::UniquePtr>; /*** Edge and EdgeRange ***************************************************************************/ diff --git a/js/public/UbiNodeDominatorTree.h b/js/public/UbiNodeDominatorTree.h index 70548e400e7..3b583b71cbb 100644 --- a/js/public/UbiNodeDominatorTree.h +++ b/js/public/UbiNodeDominatorTree.h @@ -71,8 +71,6 @@ class JS_PUBLIC_API(DominatorTree) private: // Types. - using NodeSet = js::HashSet, js::SystemAllocPolicy>; - using NodeSetPtr = mozilla::UniquePtr>; using PredecessorSets = js::HashMap, js::SystemAllocPolicy>; using NodeToIndexMap = js::HashMap, diff --git a/js/public/UbiNodeShortestPaths.h b/js/public/UbiNodeShortestPaths.h new file mode 100644 index 00000000000..90e0856576c --- /dev/null +++ b/js/public/UbiNodeShortestPaths.h @@ -0,0 +1,331 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_UbiNodeShortestPaths_h +#define js_UbiNodeShortestPaths_h + +#include "mozilla/Maybe.h" +#include "mozilla/Move.h" + +#include "jsalloc.h" + +#include "js/UbiNodeBreadthFirst.h" +#include "js/Vector.h" + +namespace JS { +namespace ubi { + +/** + * A back edge along a path in the heap graph. + */ +struct JS_PUBLIC_API(BackEdge) +{ + private: + Node predecessor_; + EdgeName name_; + + public: + using Ptr = mozilla::UniquePtr>; + + BackEdge() : predecessor_(), name_(nullptr) { } + + bool init(const Node& predecessor, Edge& edge) { + MOZ_ASSERT(!predecessor_); + MOZ_ASSERT(!name_); + + predecessor_ = predecessor; + name_ = mozilla::Move(edge.name); + return true; + } + + BackEdge(const BackEdge&) = delete; + BackEdge& operator=(const BackEdge&) = delete; + + BackEdge(BackEdge&& rhs) + : predecessor_(rhs.predecessor_) + , name_(mozilla::Move(rhs.name_)) + { + MOZ_ASSERT(&rhs != this); + } + + BackEdge& operator=(BackEdge&& rhs) { + this->~BackEdge(); + new(this) BackEdge(Move(rhs)); + return *this; + } + + Ptr clone() const; + + const EdgeName& name() const { return name_; } + EdgeName& name() { return name_; } + + const JS::ubi::Node& predecessor() const { return predecessor_; } +}; + +/** + * A path is a series of back edges from which we discovered a target node. + */ +using Path = mozilla::Vector; + +/** + * The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest + * retaining paths for each of a target set of nodes, starting from the same + * root node. + */ +struct JS_PUBLIC_API(ShortestPaths) +{ + private: + // Types, type aliases, and data members. + + using BackEdgeVector = mozilla::Vector; + using NodeToBackEdgeVectorMap = js::HashMap, + js::SystemAllocPolicy>; + + struct Handler; + using Traversal = BreadthFirst; + + /** + * A `JS::ubi::BreadthFirst` traversal handler that records back edges for + * how we reached each node, allowing us to reconstruct the shortest + * retaining paths after the traversal. + */ + struct Handler + { + using NodeData = BackEdge; + + ShortestPaths& shortestPaths; + size_t totalMaxPathsToRecord; + size_t totalPathsRecorded; + + explicit Handler(ShortestPaths& shortestPaths) + : shortestPaths(shortestPaths) + , totalMaxPathsToRecord(shortestPaths.targets_.count() * shortestPaths.maxNumPaths_) + , totalPathsRecorded(0) + { + } + + bool + operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge, + BackEdge* back, bool first) + { + MOZ_ASSERT(back); + MOZ_ASSERT(traversal.visited.has(origin)); + MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord); + + if (first && !back->init(origin, edge)) + return false; + + if (!shortestPaths.targets_.has(edge.referent)) + return true; + + // If `first` is true, then we moved the edge's name into `back` in + // the above call to `init`. So clone that back edge to get the + // correct edge name. If `first` is not true, then our edge name is + // still in `edge`. This accounts for the asymmetry between + // `back->clone()` in the first branch, and the `init` call in the + // second branch. + + auto ptr = shortestPaths.paths_.lookupForAdd(edge.referent); + if (first) { + MOZ_ASSERT(!ptr); + BackEdgeVector paths; + if (!paths.reserve(shortestPaths.maxNumPaths_)) + return false; + auto cloned = back->clone(); + if (!cloned) + return false; + paths.infallibleAppend(mozilla::Move(cloned)); + if (!shortestPaths.paths_.add(ptr, edge.referent, mozilla::Move(paths))) + return false; + totalPathsRecorded++; + } else if (ptr->value().length() < shortestPaths.maxNumPaths_) { + MOZ_ASSERT(ptr); + BackEdge::Ptr thisBackEdge(js_new()); + if (!thisBackEdge || !thisBackEdge->init(origin, edge)) + return false; + ptr->value().infallibleAppend(mozilla::Move(thisBackEdge)); + totalPathsRecorded++; + } + + MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord); + if (totalPathsRecorded == totalMaxPathsToRecord) + traversal.stop(); + + return true; + } + + }; + + // The maximum number of paths to record for each node. + uint32_t maxNumPaths_; + + // The root node we are starting the search from. + Node root_; + + // The set of nodes we are searching for paths to. + NodeSet targets_; + + // The resulting paths. + NodeToBackEdgeVectorMap paths_; + + // Need to keep alive the traversal's back edges so we can walk them later + // when the traversal is over when recreating the shortest paths. + Traversal::NodeMap backEdges_; + + private: + // Private methods. + + ShortestPaths(uint32_t maxNumPaths, const Node& root, NodeSet&& targets) + : maxNumPaths_(maxNumPaths) + , root_(root) + , targets_(mozilla::Move(targets)) + , paths_() + , backEdges_() + { + MOZ_ASSERT(maxNumPaths_ > 0); + MOZ_ASSERT(root_); + MOZ_ASSERT(targets_.initialized()); + } + + bool initialized() const { + return targets_.initialized() && + paths_.initialized() && + backEdges_.initialized(); + } + + public: + // Public methods. + + ShortestPaths(ShortestPaths&& rhs) + : maxNumPaths_(rhs.maxNumPaths_) + , root_(rhs.root_) + , targets_(mozilla::Move(rhs.targets_)) + , paths_(mozilla::Move(rhs.paths_)) + , backEdges_(mozilla::Move(rhs.backEdges_)) + { + MOZ_ASSERT(this != &rhs, "self-move is not allowed"); + } + + ShortestPaths& operator=(ShortestPaths&& rhs) { + this->~ShortestPaths(); + new (this) ShortestPaths(mozilla::Move(rhs)); + return *this; + } + + ShortestPaths(const ShortestPaths&) = delete; + ShortestPaths& operator=(const ShortestPaths&) = delete; + + /** + * Construct a new `JS::ubi::ShortestPaths`, finding up to `maxNumPaths` + * shortest retaining paths for each target node in `targets` starting from + * `root`. + * + * The resulting `ShortestPaths` instance must not outlive the + * `JS::ubi::Node` graph it was constructed from. + * + * - For `JS::ubi::Node` graphs backed by the live heap graph, this means + * that the `ShortestPaths`'s lifetime _must_ be contained within the + * scope of the provided `AutoCheckCannotGC` reference because a GC will + * invalidate the nodes. + * + * - For `JS::ubi::Node` graphs backed by some other offline structure + * provided by the embedder, the resulting `ShortestPaths`'s lifetime is + * bounded by that offline structure's lifetime. + * + * Returns `mozilla::Nothing()` on OOM failure. It is the caller's + * responsibility to handle and report the OOM. + */ + static mozilla::Maybe + Create(JSRuntime* rt, AutoCheckCannotGC& noGC, uint32_t maxNumPaths, const Node& root, NodeSet&& targets) { + MOZ_ASSERT(targets.count() > 0); + MOZ_ASSERT(maxNumPaths > 0); + + size_t count = targets.count(); + ShortestPaths paths(maxNumPaths, root, mozilla::Move(targets)); + if (!paths.paths_.init(count)) + return mozilla::Nothing(); + + Handler handler(paths); + Traversal traversal(rt, handler, noGC); + traversal.wantNames = true; + if (!traversal.init() || !traversal.addStartVisited(root) || !traversal.traverse()) + return mozilla::Nothing(); + + // Take ownership of the back edges we created while traversing the + // graph so that we can follow them from `paths_` and don't + // use-after-free. + paths.backEdges_ = mozilla::Move(traversal.visited); + + MOZ_ASSERT(paths.initialized()); + return mozilla::Some(mozilla::Move(paths)); + } + + /** + * Get a range that iterates over each target node we searched for retaining + * paths for. The returned range must not outlive the `ShortestPaths` + * instance. + */ + NodeSet::Range eachTarget() const { + MOZ_ASSERT(initialized()); + return targets_.all(); + } + + /** + * Invoke the provided functor/lambda/callable once for each retaining path + * discovered for `target`. The `func` is passed a single `JS::ubi::Path&` + * argument, which contains each edge along the path ordered starting from + * the root and ending at the target, and must not outlive the scope of the + * call. + * + * Note that it is possible that we did not find any paths from the root to + * the given target, in which case `func` will not be invoked. + */ + template + bool forEachPath(const Node& target, Func func) { + MOZ_ASSERT(initialized()); + MOZ_ASSERT(targets_.has(target)); + + auto ptr = paths_.lookup(target); + + // We didn't find any paths to this target, so nothing to do here. + if (!ptr) + return true; + + MOZ_ASSERT(ptr->value().length() <= maxNumPaths_); + + Path path; + for (const auto& backEdge : ptr->value()) { + path.clear(); + + if (!path.append(backEdge.get())) + return false; + + Node here = backEdge->predecessor(); + MOZ_ASSERT(here); + + while (here != root_) { + auto p = backEdges_.lookup(here); + MOZ_ASSERT(p); + if (!path.append(&p->value())) + return false; + here = p->value().predecessor(); + MOZ_ASSERT(here); + } + + path.reverse(); + + if (!func(path)) + return false; + } + + return true; + } +}; + +} // namespace ubi +} // namespace JS + +#endif // js_UbiNodeShortestPaths_h diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 4b56efd1ac7..26632d20463 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -27,6 +27,7 @@ #include "js/StructuredClone.h" #include "js/UbiNode.h" #include "js/UbiNodeBreadthFirst.h" +#include "js/UbiNodeShortestPaths.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/GlobalObject.h" @@ -2541,6 +2542,177 @@ FindPath(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +ShortestPaths(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "shortestPaths", 3)) + return false; + + // We don't ToString non-objects given as 'start' or 'target', because this + // test is all about object identity, and ToString doesn't preserve that. + // Non-GCThing endpoints don't make much sense. + if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, + JSDVG_SEARCH_STACK, args[0], nullptr, + "not an object, string, or symbol", nullptr); + return false; + } + + if (!args[1].isObject() || !args[1].toObject().is()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, + JSDVG_SEARCH_STACK, args[1], nullptr, + "not an array object", nullptr); + return false; + } + + RootedArrayObject objs(cx, &args[1].toObject().as()); + size_t length = objs->getDenseInitializedLength(); + if (length == 0) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, + JSDVG_SEARCH_STACK, args[1], nullptr, + "not a dense array object with one or more elements", nullptr); + return false; + } + + for (size_t i = 0; i < length; i++) { + RootedValue el(cx, objs->getDenseElement(i)); + if (!el.isObject() && !el.isString() && !el.isSymbol()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, + JSDVG_SEARCH_STACK, el, nullptr, + "not an object, string, or symbol", nullptr); + return false; + } + } + + int32_t maxNumPaths; + if (!JS::ToInt32(cx, args[2], &maxNumPaths)) + return false; + if (maxNumPaths <= 0) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, + JSDVG_SEARCH_STACK, args[2], nullptr, + "not greater than 0", nullptr); + return false; + } + + // We accumulate the results into a GC-stable form, due to the fact that the + // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph) + // is bounded within an AutoCheckCannotGC. + Rooted>>> values(cx, GCVector>>(cx)); + Vector>> names(cx); + + { + JS::AutoCheckCannotGC noGC(cx->runtime()); + + JS::ubi::NodeSet targets; + if (!targets.init()) { + ReportOutOfMemory(cx); + return false; + } + + for (size_t i = 0; i < length; i++) { + RootedValue val(cx, objs->getDenseElement(i)); + JS::ubi::Node node(val); + if (!targets.put(node)) { + ReportOutOfMemory(cx); + return false; + } + } + + JS::ubi::Node root(args[0]); + auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(cx->runtime(), noGC, maxNumPaths, + root, mozilla::Move(targets)); + if (maybeShortestPaths.isNothing()) { + ReportOutOfMemory(cx); + return false; + } + auto& shortestPaths = *maybeShortestPaths; + + for (size_t i = 0; i < length; i++) { + if (!values.append(GCVector>(cx)) || + !names.append(Vector>(cx))) + { + return false; + } + + RootedValue val(cx, objs->getDenseElement(i)); + JS::ubi::Node target(val); + + bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) { + Rooted> pathVals(cx, GCVector(cx)); + Vector pathNames(cx); + + for (auto& part : path) { + if (!pathVals.append(part->predecessor().exposeToJS()) || + !pathNames.append(mozilla::Move(part->name()))) + { + return false; + } + } + + return values.back().append(mozilla::Move(pathVals.get())) && + names.back().append(mozilla::Move(pathNames)); + }); + + if (!ok) + return false; + } + } + + MOZ_ASSERT(values.length() == names.length()); + MOZ_ASSERT(values.length() == length); + + RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length)); + if (!results) + return false; + results->ensureDenseInitializedLength(cx, 0, length); + + for (size_t i = 0; i < length; i++) { + size_t numPaths = values[i].length(); + MOZ_ASSERT(names[i].length() == numPaths); + + RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths)); + if (!pathsArray) + return false; + pathsArray->ensureDenseInitializedLength(cx, 0, numPaths); + + for (size_t j = 0; j < numPaths; j++) { + size_t pathLength = values[i][j].length(); + MOZ_ASSERT(names[i][j].length() == pathLength); + + RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength)); + if (!path) + return false; + path->ensureDenseInitializedLength(cx, 0, pathLength); + + for (size_t k = 0; k < pathLength; k++) { + RootedPlainObject part(cx, NewBuiltinClassInstance(cx)); + if (!part) + return false; + + RootedValue predecessor(cx, values[i][j][k]); + if (!JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE)) + return false; + + if (names[i][j][k]) { + RootedString edge(cx, NewStringCopyZ(cx, names[i][j][k].get())); + if (!edge || !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) + return false; + } + + path->setDenseElement(k, ObjectValue(*part)); + } + + pathsArray->setDenseElement(j, ObjectValue(*path)); + } + + results->setDenseElement(i, ObjectValue(*pathsArray)); + } + + args.rval().setObject(*results); + return true; +} + static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) { @@ -3550,6 +3722,13 @@ gc::ZealModeHelpText), " element's edge is the node of the i+1'th array element; the destination of\n" " the last array element is implicitly |target|.\n"), + JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0, +"shortestPaths(start, targets, maxNumPaths)", +" Return an array of arrays of shortest retaining paths. There is an array of\n" +" shortest retaining paths for each object in |targets|. The maximum number of\n" +" paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n" +" path is of the form |{ predecessor, edge }|."), + #ifdef DEBUG JS_FN_HELP("dumpObject", DumpObject, 1, 0, "dumpObject()", diff --git a/js/src/jit-test/tests/heap-analysis/shortestPaths.js b/js/src/jit-test/tests/heap-analysis/shortestPaths.js new file mode 100644 index 00000000000..d0c050c297e --- /dev/null +++ b/js/src/jit-test/tests/heap-analysis/shortestPaths.js @@ -0,0 +1,44 @@ +// The shortestPaths function exists solely to let the fuzzers go to town and +// exercise the code paths it calls into, hence there is nothing to assert here. +// +// The actual behavior of JS::ubi::ShortestPaths is tested in +// js/src/jsapi-tests/testUbiNode.cpp, where we can actually control the +// structure of the heap graph to test specific shapes. + +function f(x) { + return x + x; +} + +var g = f.bind(null, 5); + +var o = { + p: g +}; + +function dumpPaths(results) { + results = results.map(paths => { + return paths.map(path => { + return path.map(part => { + return { + predecessor: Object.prototype.toString.call(part.predecessor), + edge: part.edge + }; + }); + }); + }); + print(JSON.stringify(results, null, 2)); +} + +print("shortestPaths(this, [Object, f, o.p], 5)"); +var paths = shortestPaths(this, [Object, f, o.p], 5); +dumpPaths(paths); + +print(); +print("shortestPaths(o, [f], 1)") +paths = shortestPaths(o, [f], 1); +dumpPaths(paths); + +print(); +print("shortestPaths(this, [f], 5)") +paths = shortestPaths(this, [f], 5); +dumpPaths(paths); diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index ff0d540f8a7..756eec55d77 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -6,6 +6,7 @@ #include "js/UbiNode.h" #include "js/UbiNodeDominatorTree.h" #include "js/UbiNodePostOrder.h" +#include "js/UbiNodeShortestPaths.h" #include "jsapi-tests/tests.h" #include "vm/SavedFrame.h" @@ -23,8 +24,15 @@ struct FakeNode explicit FakeNode(char name) : name(name), edges() { } - bool addEdgeTo(FakeNode& referent) { + bool addEdgeTo(FakeNode& referent, const char16_t* edgeName = nullptr) { JS::ubi::Node node(&referent); + + if (edgeName) { + auto ownedName = js::DuplicateString(edgeName); + MOZ_RELEASE_ASSERT(ownedName); + return edges.emplaceBack(ownedName.release(), node); + } + return edges.emplaceBack(nullptr, node); } }; @@ -650,3 +658,351 @@ BEGIN_TEST(test_JS_ubi_Node_scriptFilename) return true; } END_TEST(test_JS_ubi_Node_scriptFilename) + +#define LAMBDA_CHECK(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr,"%s:%d:CHECK failed: " #cond "\n", __FILE__, __LINE__); \ + return false; \ + } \ + } while (false) + +static void +dumpPath(JS::ubi::Path& path) +{ + for (size_t i = 0; i < path.length(); i++) { + fprintf(stderr, "path[%llu]->predecessor() = '%c'\n", + (long long unsigned) i, + path[i]->predecessor().as()->name); + } +} + +BEGIN_TEST(test_JS_ubi_ShortestPaths_no_path) +{ + // Create the following graph: + // + // .---. .---. .---. + // | a | <--> | c | | b | + // '---' '---' '---' + FakeNode a('a'); + FakeNode b('b'); + FakeNode c('c'); + CHECK(a.addEdgeTo(c)); + CHECK(c.addEdgeTo(a)); + + mozilla::Maybe maybeShortestPaths; + { + JS::AutoCheckCannotGC noGC(rt); + + JS::ubi::NodeSet targets; + CHECK(targets.init()); + CHECK(targets.put(&b)); + + maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, + mozilla::Move(targets)); + } + + CHECK(maybeShortestPaths); + auto& paths = *maybeShortestPaths; + + size_t numPathsFound = 0; + bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { + numPathsFound++; + dumpPath(path); + return true; + }); + CHECK(ok); + CHECK(numPathsFound == 0); + + return true; +} +END_TEST(test_JS_ubi_ShortestPaths_no_path) + +BEGIN_TEST(test_JS_ubi_ShortestPaths_one_path) +{ + // Create the following graph: + // + // .---. .---. .---. + // | a | <--> | c | --> | b | + // '---' '---' '---' + FakeNode a('a'); + FakeNode b('b'); + FakeNode c('c'); + CHECK(a.addEdgeTo(c)); + CHECK(c.addEdgeTo(a)); + CHECK(c.addEdgeTo(b)); + + mozilla::Maybe maybeShortestPaths; + { + JS::AutoCheckCannotGC noGC(rt); + + JS::ubi::NodeSet targets; + CHECK(targets.init()); + CHECK(targets.put(&b)); + + maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, + mozilla::Move(targets)); + } + + CHECK(maybeShortestPaths); + auto& paths = *maybeShortestPaths; + + size_t numPathsFound = 0; + bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { + numPathsFound++; + + dumpPath(path); + LAMBDA_CHECK(path.length() == 2); + LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); + LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&c)); + + return true; + }); + + CHECK(ok); + CHECK(numPathsFound == 1); + + return true; +} +END_TEST(test_JS_ubi_ShortestPaths_one_path) + +BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_paths) +{ + // Create the following graph: + // + // .---. + // .-----| a |-----. + // | '---' | + // V | V + // .---. | .---. + // | b | | | d | + // '---' | '---' + // | | | + // V | V + // .---. | .---. + // | c | | | e | + // '---' V '---' + // | .---. | + // '---->| f |<----' + // '---' + FakeNode a('a'); + FakeNode b('b'); + FakeNode c('c'); + FakeNode d('d'); + FakeNode e('e'); + FakeNode f('f'); + CHECK(a.addEdgeTo(b)); + CHECK(a.addEdgeTo(f)); + CHECK(a.addEdgeTo(d)); + CHECK(b.addEdgeTo(c)); + CHECK(c.addEdgeTo(f)); + CHECK(d.addEdgeTo(e)); + CHECK(e.addEdgeTo(f)); + + mozilla::Maybe maybeShortestPaths; + { + JS::AutoCheckCannotGC noGC(rt); + + JS::ubi::NodeSet targets; + CHECK(targets.init()); + CHECK(targets.put(&f)); + + maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, + mozilla::Move(targets)); + } + + CHECK(maybeShortestPaths); + auto& paths = *maybeShortestPaths; + + size_t numPathsFound = 0; + bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) { + numPathsFound++; + dumpPath(path); + + switch (path.back()->predecessor().as()->name) { + case 'a': { + LAMBDA_CHECK(path.length() == 1); + break; + } + + case 'c': { + LAMBDA_CHECK(path.length() == 3); + LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); + LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&b)); + LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&c)); + break; + } + + case 'e': { + LAMBDA_CHECK(path.length() == 3); + LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); + LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&d)); + LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&e)); + break; + } + + default: { + // Unexpected path! + LAMBDA_CHECK(false); + } + } + + return true; + }); + + CHECK(ok); + fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); + CHECK(numPathsFound == 3); + + return true; +} +END_TEST(test_JS_ubi_ShortestPaths_multiple_paths) + +BEGIN_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max) +{ + // Create the following graph: + // + // .---. + // .-----| a |-----. + // | '---' | + // V | V + // .---. | .---. + // | b | | | d | + // '---' | '---' + // | | | + // V | V + // .---. | .---. + // | c | | | e | + // '---' V '---' + // | .---. | + // '---->| f |<----' + // '---' + FakeNode a('a'); + FakeNode b('b'); + FakeNode c('c'); + FakeNode d('d'); + FakeNode e('e'); + FakeNode f('f'); + CHECK(a.addEdgeTo(b)); + CHECK(a.addEdgeTo(f)); + CHECK(a.addEdgeTo(d)); + CHECK(b.addEdgeTo(c)); + CHECK(c.addEdgeTo(f)); + CHECK(d.addEdgeTo(e)); + CHECK(e.addEdgeTo(f)); + + mozilla::Maybe maybeShortestPaths; + { + JS::AutoCheckCannotGC noGC(rt); + + JS::ubi::NodeSet targets; + CHECK(targets.init()); + CHECK(targets.put(&f)); + + maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 1, &a, + mozilla::Move(targets)); + } + + CHECK(maybeShortestPaths); + auto& paths = *maybeShortestPaths; + + size_t numPathsFound = 0; + bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) { + numPathsFound++; + dumpPath(path); + return true; + }); + + CHECK(ok); + fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); + CHECK(numPathsFound == 1); + + return true; +} +END_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max) + +BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target) +{ + // Create the following graph: + // + // .---. + // .-----| a |-----. + // | '---' | + // | | | + // |x |y |z + // | | | + // | V | + // | .---. | + // '---->| b |<----' + // '---' + FakeNode a('a'); + FakeNode b('b'); + CHECK(a.addEdgeTo(b, MOZ_UTF16("x"))); + CHECK(a.addEdgeTo(b, MOZ_UTF16("y"))); + CHECK(a.addEdgeTo(b, MOZ_UTF16("z"))); + + mozilla::Maybe maybeShortestPaths; + { + JS::AutoCheckCannotGC noGC(rt); + + JS::ubi::NodeSet targets; + CHECK(targets.init()); + CHECK(targets.put(&b)); + + maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, + mozilla::Move(targets)); + } + + CHECK(maybeShortestPaths); + auto& paths = *maybeShortestPaths; + + size_t numPathsFound = 0; + bool foundX = false; + bool foundY = false; + bool foundZ = false; + + bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { + numPathsFound++; + dumpPath(path); + + LAMBDA_CHECK(path.length() == 1); + LAMBDA_CHECK(path.back()->name()); + LAMBDA_CHECK(js_strlen(path.back()->name().get()) == 1); + + auto c = uint8_t(path.back()->name().get()[0]); + fprintf(stderr, "Edge name = '%c'\n", c); + + switch (c) { + case 'x': { + foundX = true; + break; + } + case 'y': { + foundY = true; + break; + } + case 'z': { + foundZ = true; + break; + } + default: { + // Unexpected edge! + LAMBDA_CHECK(false); + } + } + + return true; + }); + + CHECK(ok); + fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); + CHECK(numPathsFound == 3); + CHECK(foundX); + CHECK(foundY); + CHECK(foundZ); + + return true; +} +END_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target) + +#undef LAMBDA_CHECK diff --git a/js/src/moz.build b/js/src/moz.build index aa775c90474..47b5627768b 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -136,6 +136,7 @@ EXPORTS.js += [ '../public/UbiNodeCensus.h', '../public/UbiNodeDominatorTree.h', '../public/UbiNodePostOrder.h', + '../public/UbiNodeShortestPaths.h', '../public/UniquePtr.h', '../public/Utility.h', '../public/Value.h', @@ -339,6 +340,7 @@ UNIFIED_SOURCES += [ 'vm/TypeInference.cpp', 'vm/UbiNode.cpp', 'vm/UbiNodeCensus.cpp', + 'vm/UbiNodeShortestPaths.cpp', 'vm/UnboxedObject.cpp', 'vm/Unicode.cpp', 'vm/Value.cpp', diff --git a/js/src/vm/UbiNodeShortestPaths.cpp b/js/src/vm/UbiNodeShortestPaths.cpp new file mode 100644 index 00000000000..46308f606d0 --- /dev/null +++ b/js/src/vm/UbiNodeShortestPaths.cpp @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "js/UbiNodeShortestPaths.h" + +#include "jsstr.h" + +namespace JS { +namespace ubi { + +JS_PUBLIC_API(BackEdge::Ptr) +BackEdge::clone() const +{ + BackEdge::Ptr clone(js_new()); + if (!clone) + return nullptr; + + clone->predecessor_ = predecessor(); + if (name()) { + clone->name_ = js::DuplicateString(name().get()); + if (!clone->name_) + return nullptr; + } + return mozilla::Move(clone); +} + +} // namespace ubi +} // namespace JS From 09326a7b55eee7122f7df6a10013c747a19bd262 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 11 Feb 2016 14:20:00 +0100 Subject: [PATCH 178/187] Bug 1228137 - Test that mouse events on the finished animation is surely fired on the animation element when the mouse events happen immediately after animation.finish() is called. r=birtles --- .../test/file_animations_styles_on_event.html | 70 +++++++++++++++++++ layout/style/test/mochitest.ini | 2 + .../test/test_animations_styles_on_event.html | 28 ++++++++ 3 files changed, 100 insertions(+) create mode 100644 layout/style/test/file_animations_styles_on_event.html create mode 100644 layout/style/test/test_animations_styles_on_event.html diff --git a/layout/style/test/file_animations_styles_on_event.html b/layout/style/test/file_animations_styles_on_event.html new file mode 100644 index 00000000000..b9cdb430c74 --- /dev/null +++ b/layout/style/test/file_animations_styles_on_event.html @@ -0,0 +1,70 @@ + + + + + + + + + + +

+ + + diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 20e636bd6ad..b27947f22ff 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -50,6 +50,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017 support-files = file_animations_pausing.html [test_animations_playbackrate.html] support-files = file_animations_playbackrate.html +[test_animations_styles_on_event.html] +support-files = file_animations_styles_on_event.html [test_any_dynamic.html] [test_at_rule_parse_serialize.html] [test_attribute_selector_eof_behavior.html] diff --git a/layout/style/test/test_animations_styles_on_event.html b/layout/style/test/test_animations_styles_on_event.html new file mode 100644 index 00000000000..da7680727de --- /dev/null +++ b/layout/style/test/test_animations_styles_on_event.html @@ -0,0 +1,28 @@ + + + + + Test that mouse movement immediately after finish() should involve restyling for finished state(Bug 1228137) + + + + +Mozilla Bug 1228137 +
+
+
+
+ + From 53efdf57df3531b1280c6b442b280ed8a43ceb76 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 11 Feb 2016 20:20:10 -0800 Subject: [PATCH 179/187] Back out 95d45bb51f96 (bug 1245937) because browser_chatwindow.js still exceeds the timeout threshold --- browser/base/content/test/chat/browser.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/base/content/test/chat/browser.ini b/browser/base/content/test/chat/browser.ini index 31a280f11ff..2eea02f565c 100644 --- a/browser/base/content/test/chat/browser.ini +++ b/browser/base/content/test/chat/browser.ini @@ -5,6 +5,7 @@ support-files = chat.html [browser_chatwindow.js] +skip-if = true # Bug 1245937 - Intermittent failures/timeouts. [browser_focus.js] [browser_tearoff.js] skip-if = true # Bug 1245805 - tearing off chat windows causes a browser crash. From 51f86649ae91cebf5c77a85192856398ab39b126 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 11 Feb 2016 21:50:07 -0800 Subject: [PATCH 180/187] Back out c578a05bb9d6 (bug 1243131) for browser_memory_keyboard-snapshot-list.js timeout threshold exceeded on Linux32 debug --- devtools/client/memory/app.js | 33 ------- .../client/memory/test/browser/browser.ini | 1 - .../browser_memory_keyboard-snapshot-list.js | 91 ------------------- devtools/client/memory/test/browser/head.js | 23 ----- .../test/mochitest/test_tree_06.html | 29 ------ devtools/client/shared/components/tree.js | 5 - 6 files changed, 182 deletions(-) delete mode 100644 devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js diff --git a/devtools/client/memory/app.js b/devtools/client/memory/app.js index 35eb21834b8..0825cb9a024 100644 --- a/devtools/client/memory/app.js +++ b/devtools/client/memory/app.js @@ -3,7 +3,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const { assert } = require("devtools/shared/DevToolsUtils"); -const { appinfo } = require("Services"); const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const { breakdowns, diffingState, viewState } = require("./constants"); @@ -54,17 +53,6 @@ const MemoryApp = createClass({ return {}; }, - componentDidMount() { - // Attach the keydown listener directly to the window. When an element that - // has the focus (such as a tree node) is removed from the DOM, the focus - // falls back to the body. - window.addEventListener("keydown", this.onKeyDown); - }, - - componentWillUnmount() { - window.removeEventListener("keydown", this.onKeyDown); - }, - childContextTypes: { front: PropTypes.any, heapWorker: PropTypes.any, @@ -79,27 +67,6 @@ const MemoryApp = createClass({ }; }, - onKeyDown(e) { - let { snapshots, dispatch, heapWorker } = this.props; - const selectedSnapshot = snapshots.find(s => s.selected); - const selectedIndex = snapshots.indexOf(selectedSnapshot); - - let isOSX = appinfo.OS == "Darwin"; - let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey); - // On ACCEL+UP, select previous snapshot. - if (isAccelKey && e.key === "ArrowUp") { - let previousIndex = Math.max(0, selectedIndex - 1); - let previousSnapshotId = snapshots[previousIndex].id; - dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId)); - } - // On ACCEL+DOWN, select next snapshot. - if (isAccelKey && e.key === "ArrowDown") { - let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1); - let nextSnapshotId = snapshots[nextIndex].id; - dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId)); - } - }, - render() { let { dispatch, diff --git a/devtools/client/memory/test/browser/browser.ini b/devtools/client/memory/test/browser/browser.ini index 769c61b3ca9..29fee425dc4 100644 --- a/devtools/client/memory/test/browser/browser.ini +++ b/devtools/client/memory/test/browser/browser.ini @@ -15,7 +15,6 @@ support-files = [browser_memory_dominator_trees_02.js] [browser_memory_filter_01.js] [browser_memory_keyboard.js] -[browser_memory_keyboard-snapshot-list.js] [browser_memory_no_allocation_stacks.js] [browser_memory_no_auto_expand.js] skip-if = debug # bug 1219554 diff --git a/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js b/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js deleted file mode 100644 index 20c08b9a304..00000000000 --- a/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js +++ /dev/null @@ -1,91 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Test that using ACCEL+UP/DOWN, the user can navigate between snapshots. - -"use strict"; - -const { - snapshotState -} = require("devtools/client/memory/constants"); -const { - takeSnapshotAndCensus -} = require("devtools/client/memory/actions/snapshot"); - -const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html"; - -this.test = makeMemoryTest(TEST_URL, function* ({ panel }) { - const heapWorker = panel.panelWin.gHeapAnalysesClient; - const front = panel.panelWin.gFront; - const store = panel.panelWin.gStore; - const { dispatch } = store; - const doc = panel.panelWin.document; - - info("Take 3 snapshots"); - dispatch(takeSnapshotAndCensus(front, heapWorker)); - dispatch(takeSnapshotAndCensus(front, heapWorker)); - dispatch(takeSnapshotAndCensus(front, heapWorker)); - - yield waitUntilState(store, state => - state.snapshots.length == 3 && - state.snapshots.every(s => s.state === snapshotState.SAVED_CENSUS)); - ok(true, "All snapshots are in SAVED_CENSUS state"); - - yield waitUntilSnapshotSelected(store, 2); - ok(true, "Third snapshot selected after creating all snapshots."); - - info("Press ACCEL+UP key, expect second snapshot selected."); - EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); - yield waitUntilSnapshotSelected(store, 1); - ok(true, "Second snapshot selected after alt+UP."); - - info("Press ACCEL+UP key, expect first snapshot selected."); - EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); - yield waitUntilSnapshotSelected(store, 0); - ok(true, "First snapshot is selected after ACCEL+UP"); - - info("Check ACCEL+UP is a noop when the first snapshot is selected."); - EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin); - // We assume the snapshot selection should be synchronous here. - is(getSelectedSnapshotIndex(store), 0, "First snapshot is still selected"); - - info("Press ACCEL+DOWN key, expect second snapshot selected."); - EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); - yield waitUntilSnapshotSelected(store, 1); - ok(true, "Second snapshot is selected after ACCEL+DOWN"); - - info("Click on first node."); - let firstNode = doc.querySelector(".tree .heap-tree-item-name"); - EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin); - yield waitUntilState(store, state => state.snapshots[1].census.focused === - state.snapshots[1].census.report.children[0] - ); - ok(true, "First root is selected after click."); - - info("Press DOWN key, expect second root focused."); - EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin); - yield waitUntilState(store, state => state.snapshots[1].census.focused === - state.snapshots[1].census.report.children[1] - ); - ok(true, "Second root is selected after pressing DOWN."); - is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected"); - - info("Press UP key, expect second root focused."); - EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin); - yield waitUntilState(store, state => state.snapshots[1].census.focused === - state.snapshots[1].census.report.children[0] - ); - ok(true, "First root is selected after pressing UP."); - is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected"); - - info("Press ACCEL+DOWN key, expect third snapshot selected."); - EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); - yield waitUntilSnapshotSelected(store, 2); - ok(true, "Thirdˆ snapshot is selected after ACCEL+DOWN"); - - info("Check ACCEL+DOWN is a noop when the last snapshot is selected."); - EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin); - // We assume the snapshot selection should be synchronous here. - is(getSelectedSnapshotIndex(store), 2, "Third snapshot is still selected"); - -}); diff --git a/devtools/client/memory/test/browser/head.js b/devtools/client/memory/test/browser/head.js index a9d7f445e68..a6f51e8381b 100644 --- a/devtools/client/memory/test/browser/head.js +++ b/devtools/client/memory/test/browser/head.js @@ -142,26 +142,3 @@ function getDisplayedSnapshotStatus(document) { const status = document.querySelector(".snapshot-status"); return status ? status.textContent.trim() : null; } - -/** - * Get the index of the currently selected snapshot. - * - * @return {Number} - */ -function getSelectedSnapshotIndex(store) { - let snapshots = store.getState().snapshots; - let selectedSnapshot = snapshots.find(s => s.selected); - return snapshots.indexOf(selectedSnapshot); -} - -/** - * Returns a promise that will resolve when the snapshot with provided index - * becomes selected. - * - * @return {Promise} - */ -function waitUntilSnapshotSelected(store, snapshotIndex) { - return waitUntilState(store, state => - state.snapshots[snapshotIndex] && - state.snapshots[snapshotIndex].selected === true); -} diff --git a/devtools/client/shared/components/test/mochitest/test_tree_06.html b/devtools/client/shared/components/test/mochitest/test_tree_06.html index 8b673e74d99..5e4259358fb 100644 --- a/devtools/client/shared/components/test/mochitest/test_tree_06.html +++ b/devtools/client/shared/components/test/mochitest/test_tree_06.html @@ -276,35 +276,6 @@ window.onload = Task.async(function* () { "-N:false", "--O:false", ], "After the RIGHT, K should be focused."); - - // Check that keys are ignored if any modifier is present. - let keysWithModifier = [ - { key: "ArrowDown", altKey: true }, - { key: "ArrowDown", ctrlKey: true }, - { key: "ArrowDown", metaKey: true }, - { key: "ArrowDown", shiftKey: true }, - ]; - for (let key of keysWithModifier) { - Simulate.keyDown(document.querySelector(".tree"), key); - yield forceRender(tree); - isRenderedTree(document.body.textContent, [ - "A:false", - "-B:false", - "--E:false", - "---K:true", - "---L:false", - "--F:false", - "--G:false", - "-C:false", - "--H:false", - "--I:false", - "-D:false", - "--J:false", - "M:false", - "-N:false", - "--O:false", - ], "After DOWN + (alt|ctrl|meta|shift), K should remain focused."); - } } catch(e) { ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); } finally { diff --git a/devtools/client/shared/components/tree.js b/devtools/client/shared/components/tree.js index bb6cfb38912..bf441ad3d51 100644 --- a/devtools/client/shared/components/tree.js +++ b/devtools/client/shared/components/tree.js @@ -405,11 +405,6 @@ const Tree = module.exports = createClass({ return; } - // Allow parent nodes to use navigation arrows with modifiers. - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { - return; - } - // Prevent scrolling when pressing navigation keys. Guard against mocked // events received when testing. if (e.nativeEvent && e.nativeEvent.preventDefault) { From 000a60c5f7bb9f23b90a442830d4efe140c20471 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 11 Feb 2016 23:39:36 -0800 Subject: [PATCH 181/187] Back out 6 changesets (bug 1245463) for !mAppendRunning (Append is running) assertion failures CLOSED TREE Backed out changeset 75d96e417aaf (bug 1245463) Backed out changeset dca0577d741c (bug 1245463) Backed out changeset 17ffbe7ca24e (bug 1245463) Backed out changeset 83a2de2fcf02 (bug 1245463) Backed out changeset e8bb35ea72e4 (bug 1245463) Backed out changeset 48364b868055 (bug 1245463) --- dom/media/mediasource/MediaSource.cpp | 14 ++ dom/media/mediasource/MediaSource.h | 6 + dom/media/mediasource/MediaSourceDecoder.cpp | 6 - dom/media/mediasource/MediaSourceDemuxer.cpp | 2 +- dom/media/mediasource/SourceBuffer.cpp | 81 ++++++-- dom/media/mediasource/SourceBuffer.h | 11 +- .../mediasource/SourceBufferContentManager.h | 4 + dom/media/mediasource/SourceBufferList.cpp | 10 + dom/media/mediasource/SourceBufferList.h | 4 + dom/media/mediasource/TrackBuffersManager.cpp | 174 +++++++++++++----- dom/media/mediasource/TrackBuffersManager.h | 23 ++- 11 files changed, 256 insertions(+), 79 deletions(-) diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 0faab525a17..dfa3d28cd51 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -487,6 +487,20 @@ MediaSource::NotifyEvicted(double aStart, double aEnd) mSourceBuffers->Evict(aStart, aEnd); } +#if defined(DEBUG) +void +MediaSource::Dump(const char* aPath) +{ + char buf[255]; + PR_snprintf(buf, sizeof(buf), "%s/mediasource-%p", aPath, this); + PR_MkDir(buf, 0700); + + if (mSourceBuffers) { + mSourceBuffers->Dump(buf); + } +} +#endif + void MediaSource::GetMozDebugReaderData(nsAString& aString) { diff --git a/dom/media/mediasource/MediaSource.h b/dom/media/mediasource/MediaSource.h index ab1b0571f72..9b318744c74 100644 --- a/dom/media/mediasource/MediaSource.h +++ b/dom/media/mediasource/MediaSource.h @@ -100,6 +100,12 @@ public: // that were evicted are provided. void NotifyEvicted(double aStart, double aEnd); +#if defined(DEBUG) + // Dump the contents of each SourceBuffer to a series of files under aPath. + // aPath must exist. Debug only, invoke from your favourite debugger. + void Dump(const char* aPath); +#endif + // Returns a string describing the state of the MediaSource internal // buffered data. Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 3505801c086..9e2e7fdf64a 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -256,12 +256,6 @@ MediaDecoderOwner::NextFrameStatus MediaSourceDecoder::NextFrameBufferedStatus() { MOZ_ASSERT(NS_IsMainThread()); - - if (!mMediaSource || - mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) { - return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; - } - // Next frame hasn't been decoded yet. // Use the buffered range to consider if we have the next frame available. TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index f7fcefa2054..46326feda0e 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -22,7 +22,7 @@ using media::TimeIntervals; MediaSourceDemuxer::MediaSourceDemuxer() : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), - /* aSupportsTailDispatch = */ false)) + /* aSupportsTailDispatch = */ true)) , mMonitor("MediaSourceDemuxer") { MOZ_ASSERT(NS_IsMainThread()); diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 27cf3b245e0..1c9bcd2223f 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -39,6 +39,27 @@ using media::TimeUnit; namespace dom { +class BufferAppendRunnable : public nsRunnable { +public: + BufferAppendRunnable(SourceBuffer* aSourceBuffer, + uint32_t aUpdateID) + : mSourceBuffer(aSourceBuffer) + , mUpdateID(aUpdateID) + { + } + + NS_IMETHOD Run() override final { + + mSourceBuffer->BufferAppend(mUpdateID); + + return NS_OK; + } + +private: + RefPtr mSourceBuffer; + uint32_t mUpdateID; +}; + void SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv) { @@ -205,13 +226,10 @@ void SourceBuffer::AbortBufferAppend() { if (mUpdating) { - if (mPendingAppend.Exists()) { - mPendingAppend.Disconnect(); - mContentManager->AbortAppendData(); - // Some data may have been added by the Segment Parser Loop. - // Check if we need to update the duration. - CheckEndTime(); - } + mPendingAppend.DisconnectIfExists(); + // TODO: Abort stream append loop algorithms. + // cancel any pending buffer append. + mContentManager->AbortAppendData(); AbortUpdating(); } } @@ -291,6 +309,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType) , mMediaSource(aMediaSource) , mUpdating(false) , mActive(false) + , mUpdateID(0) , mType(aType) { MOZ_ASSERT(NS_IsMainThread()); @@ -362,6 +381,7 @@ SourceBuffer::StartUpdating() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mUpdating); mUpdating = true; + mUpdateID++; QueueAsyncSimpleEvent("updatestart"); } @@ -370,8 +390,12 @@ SourceBuffer::StopUpdating() { MOZ_ASSERT(NS_IsMainThread()); if (!mUpdating) { - // The buffer append or range removal algorithm has been interrupted by - // abort(). + // The buffer append algorithm has been interrupted by abort(). + // + // If the sequence appendBuffer(), abort(), appendBuffer() occurs before + // the first StopUpdating() runnable runs, then a second StopUpdating() + // runnable will be scheduled, but still only one (the first) will queue + // events. return; } mUpdating = false; @@ -383,6 +407,7 @@ void SourceBuffer::AbortUpdating() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mUpdating); mUpdating = false; QueueAsyncSimpleEvent("abort"); QueueAsyncSimpleEvent("updateend"); @@ -413,13 +438,23 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR StartUpdating(); - BufferAppend(); + nsCOMPtr task = new BufferAppendRunnable(this, mUpdateID); + NS_DispatchToMainThread(task); } void -SourceBuffer::BufferAppend() +SourceBuffer::BufferAppend(uint32_t aUpdateID) { - MOZ_ASSERT(mUpdating); + if (!mUpdating || aUpdateID != mUpdateID) { + // The buffer append algorithm has been interrupted by abort(). + // + // If the sequence appendBuffer(), abort(), appendBuffer() occurs before + // the first StopUpdating() runnable runs, then a second StopUpdating() + // runnable will be scheduled, but still only one (the first) will queue + // events. + return; + } + MOZ_ASSERT(mMediaSource); MOZ_ASSERT(!mPendingAppend.Exists()); @@ -432,8 +467,11 @@ SourceBuffer::BufferAppend() void SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) { - MOZ_ASSERT(mUpdating); mPendingAppend.Complete(); + if (!mUpdating) { + // The buffer append algorithm has been interrupted by abort(). + return; + } if (aHasActiveTracks) { if (!mActive) { @@ -456,9 +494,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) void SourceBuffer::AppendDataErrored(nsresult aError) { - MOZ_ASSERT(mUpdating); mPendingAppend.Complete(); - switch (aError) { case NS_ERROR_ABORT: // Nothing further to do as the trackbuffer has been shutdown. @@ -474,7 +510,10 @@ void SourceBuffer::AppendError(bool aDecoderError) { MOZ_ASSERT(NS_IsMainThread()); - + if (!mUpdating) { + // The buffer append algorithm has been interrupted by abort(). + return; + } mContentManager->ResetParserState(); mUpdating = false; @@ -586,6 +625,16 @@ SourceBuffer::Evict(double aStart, double aEnd) mContentManager->EvictBefore(TimeUnit::FromSeconds(evictTime)); } +#if defined(DEBUG) +void +SourceBuffer::Dump(const char* aPath) +{ + if (mContentManager) { + mContentManager->Dump(aPath); + } +} +#endif + NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer) diff --git a/dom/media/mediasource/SourceBuffer.h b/dom/media/mediasource/SourceBuffer.h index a4c57119ab2..a67537bba5c 100644 --- a/dom/media/mediasource/SourceBuffer.h +++ b/dom/media/mediasource/SourceBuffer.h @@ -213,6 +213,10 @@ public: return mActive; } +#if defined(DEBUG) + void Dump(const char* aPath); +#endif + private: ~SourceBuffer(); @@ -234,7 +238,7 @@ private: // Shared implementation of AppendBuffer overloads. void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); - void BufferAppend(); + void BufferAppend(uint32_t aAppendID); // Implement the "Append Error Algorithm". // Will call endOfStream() with "decode" error if aDecodeError is true. @@ -262,6 +266,11 @@ private: mozilla::Atomic mActive; + // Each time mUpdating is set to true, mUpdateID will be incremented. + // This allows for a queued AppendData task to identify if it was earlier + // aborted and another AppendData queued. + uint32_t mUpdateID; + MozPromiseRequestHolder mPendingAppend; const nsCString mType; diff --git a/dom/media/mediasource/SourceBufferContentManager.h b/dom/media/mediasource/SourceBufferContentManager.h index c2515d7f958..e1bbd4a73fa 100644 --- a/dom/media/mediasource/SourceBufferContentManager.h +++ b/dom/media/mediasource/SourceBufferContentManager.h @@ -108,6 +108,10 @@ public: virtual void RestartGroupStartTimestamp() {} virtual media::TimeUnit GroupEndTimestamp() = 0; +#if defined(DEBUG) + virtual void Dump(const char* aPath) { } +#endif + protected: virtual ~SourceBufferContentManager() { } }; diff --git a/dom/media/mediasource/SourceBufferList.cpp b/dom/media/mediasource/SourceBufferList.cpp index 58591843d51..bea935f3d1e 100644 --- a/dom/media/mediasource/SourceBufferList.cpp +++ b/dom/media/mediasource/SourceBufferList.cpp @@ -176,6 +176,16 @@ SourceBufferList::QueueAsyncSimpleEvent(const char* aName) NS_DispatchToMainThread(event); } +#if defined(DEBUG) +void +SourceBufferList::Dump(const char* aPath) +{ + for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) { + mSourceBuffers[i]->Dump(aPath); + } +} +#endif + SourceBufferList::SourceBufferList(MediaSource* aMediaSource) : DOMEventTargetHelper(aMediaSource->GetParentObject()) , mMediaSource(aMediaSource) diff --git a/dom/media/mediasource/SourceBufferList.h b/dom/media/mediasource/SourceBufferList.h index 7ec46665918..c974489b869 100644 --- a/dom/media/mediasource/SourceBufferList.h +++ b/dom/media/mediasource/SourceBufferList.h @@ -84,6 +84,10 @@ public: // No event is fired and no action is performed on the sourcebuffers. void ClearSimple(); +#if defined(DEBUG) + void Dump(const char* aPath); +#endif + private: ~SourceBufferList(); diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 59bbf116364..a83e5e8059b 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -96,17 +96,24 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute , mType(aType) , mParser(ContainerParser::CreateForMIMEType(aType)) , mProcessedInput(0) + , mAppendRunning(false) , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue()) , mSourceBufferAttributes(aAttributes) , mParentDecoder(new nsMainThreadPtrHolder(aParentDecoder, false /* strict */)) + , mMediaSourceDuration(mTaskQueue, Maybe(), "TrackBuffersManager::mMediaSourceDuration (Mirror)") + , mAbort(false) , mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold", 100 * (1 << 20))) , mEvictionOccurred(false) , mMonitor("TrackBuffersManager") - , mAppendRunning(false) - , mSegmentParserLoopRunning(false) { MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread"); + RefPtr self = this; + nsCOMPtr task = + NS_NewRunnableFunction([self] () { + self->mMediaSourceDuration.Connect(self->mParentDecoder->CanonicalExplicitDuration()); + }); + GetTaskQueue()->Dispatch(task.forget()); } TrackBuffersManager::~TrackBuffersManager() @@ -135,6 +142,7 @@ TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData) { MOZ_ASSERT(OnTaskQueue()); mIncomingBuffers.AppendElement(aData); + mAbort = false; } RefPtr @@ -143,42 +151,49 @@ TrackBuffersManager::BufferAppend() MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); - mAppendRunning = true; return InvokeAsync(GetTaskQueue(), this, __func__, &TrackBuffersManager::InitSegmentParserLoop); } -// The MSE spec requires that we abort the current SegmentParserLoop -// which is then followed by a call to ResetParserState. -// However due to our asynchronous design this causes inherent difficulities. -// As the spec behaviour is non deterministic anyway, we instead wait until the -// current AppendData has completed its run. +// Abort any pending AppendData. +// We don't really care about really aborting our inner loop as by spec the +// process is happening asynchronously, as such where and when we would abort is +// non-deterministic. The SourceBuffer also makes sure BufferAppend +// isn't called should the appendBuffer be immediately aborted. +// We do however want to ensure that no new task will be dispatched on our task +// queue and only let the current one finish its job. For this we set mAbort +// to true. void TrackBuffersManager::AbortAppendData() { MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); - MonitorAutoLock mon(mMonitor); - while (mAppendRunning) { - mon.Wait(); - } + mAbort = true; } void TrackBuffersManager::ResetParserState() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called"); + MOZ_ASSERT(!mAppendRunning, "AbortAppendData must have been called"); MSE_DEBUG(""); // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed. - // SourceBuffer.abort() has ensured that all complete coded frames have been - // processed. As such, we don't need to check for the value of mAppendState. - nsCOMPtr task = - NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState); - GetTaskQueue()->Dispatch(task.forget()); + if (mAppendState == AppendState::PARSING_MEDIA_SEGMENT) { + nsCOMPtr task = + NS_NewRunnableMethod(this, &TrackBuffersManager::FinishCodedFrameProcessing); + GetTaskQueue()->Dispatch(task.forget()); + } else { + nsCOMPtr task = + NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState); + GetTaskQueue()->Dispatch(task.forget()); + } + // Our ResetParserState is really asynchronous, the current task has been + // interrupted and will complete shortly (or has already completed). + // We must however present to the main thread a stable, reset state. + // So we run the following operation now in the main thread. // 7. Set append state to WAITING_FOR_SEGMENT. SetAppendState(AppendState::WAITING_FOR_SEGMENT); } @@ -187,7 +202,6 @@ RefPtr TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running"); MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds()); mEnded = false; @@ -295,13 +309,54 @@ TrackBuffersManager::Detach() { MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG(""); + + // Abort pending operations if any. + AbortAppendData(); + + RefPtr self = this; + nsCOMPtr task = + NS_NewRunnableFunction([self] () { + // Clear our sourcebuffer + self->CodedFrameRemoval(TimeInterval(TimeUnit::FromSeconds(0), + TimeUnit::FromInfinity())); + self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__); + self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__); + self->mMediaSourceDuration.DisconnectIfConnected(); + }); + GetTaskQueue()->Dispatch(task.forget()); +} + +#if defined(DEBUG) +void +TrackBuffersManager::Dump(const char* aPath) +{ + +} +#endif + +void +TrackBuffersManager::FinishCodedFrameProcessing() +{ + MOZ_ASSERT(OnTaskQueue()); + + if (mProcessingRequest.Exists()) { + NS_WARNING("Processing request pending"); + mProcessingRequest.Disconnect(); + } + // The spec requires us to complete parsing synchronously any outstanding + // frames in the current media segment. This can't be implemented in a way + // that makes sense. + // As such we simply completely ignore the result of any pending input buffer. + // TODO: Link to W3C bug. + + CompleteResetParserState(); } void TrackBuffersManager::CompleteResetParserState() { MOZ_ASSERT(OnTaskQueue()); - MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning); + MOZ_ASSERT(!mAppendRunning); MSE_DEBUG(""); for (auto& track : GetTracksList()) { @@ -437,11 +492,19 @@ bool TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) { MOZ_ASSERT(OnTaskQueue()); - MOZ_ASSERT(!mSegmentParserLoopRunning, "Logic error: Append in progress"); + MOZ_ASSERT(!mAppendRunning, "Logic error: Append in progress"); MSE_DEBUG("From %.2fs to %.2f", aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds()); + if (mMediaSourceDuration.Ref().isNothing() || + IsNaN(mMediaSourceDuration.Ref().ref())) { + MSE_DEBUG("Nothing to remove, aborting"); + return false; + } + TimeUnit duration{TimeUnit::FromSeconds(mMediaSourceDuration.Ref().ref())}; + #if DEBUG + MSE_DEBUG("duration:%.2f", duration.ToSeconds()); if (HasVideo()) { MSE_DEBUG("before video ranges=%s", DumpTimeRanges(mVideoTracks.mBufferedRanges).get()); @@ -464,14 +527,7 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) MSE_DEBUGV("Processing %s track", track->mInfo->mMimeType.get()); // 1. Let remove end timestamp be the current value of duration // See bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=28727 - // At worse we will remove all frames until the end, unless a key frame is - // found between the current interval's end and the trackbuffer's end. - TimeUnit removeEndTimestamp = track->mBufferedRanges.GetEnd(); - - if (start > removeEndTimestamp) { - // Nothing to remove. - continue; - } + TimeUnit removeEndTimestamp = std::max(duration, track->mBufferedRanges.GetEnd()); // 2. If this track buffer has a random access point timestamp that is greater than or equal to end, // then update remove end timestamp to that random access point timestamp. @@ -539,9 +595,8 @@ RefPtr TrackBuffersManager::InitSegmentParserLoop() { MOZ_ASSERT(OnTaskQueue()); - MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty()); - MSE_DEBUG(""); + MOZ_ASSERT(mAppendPromise.IsEmpty() && !mAppendRunning); RefPtr p = mAppendPromise.Ensure(__func__); AppendIncomingBuffers(); @@ -575,9 +630,6 @@ void TrackBuffersManager::SegmentParserLoop() { MOZ_ASSERT(OnTaskQueue()); - - mSegmentParserLoopRunning = true; - while (true) { // 1. If the input buffer is empty, then jump to the need more data step below. if (!mInputBuffer || mInputBuffer->IsEmpty()) { @@ -681,7 +733,7 @@ TrackBuffersManager::SegmentParserLoop() ->Then(GetTaskQueue(), __func__, [self] (bool aNeedMoreData) { self->mProcessingRequest.Complete(); - if (aNeedMoreData) { + if (aNeedMoreData || self->mAbort) { self->NeedMoreData(); } else { self->ScheduleSegmentParserLoop(); @@ -700,25 +752,19 @@ void TrackBuffersManager::NeedMoreData() { MSE_DEBUG(""); - RestoreCachedVariables(); - mAppendPromise.ResolveIfExists(mActiveTrack, __func__); - mSegmentParserLoopRunning = false; - // Wake-up any pending Abort() - MonitorAutoLock mon(mMonitor); + if (!mAbort) { + RestoreCachedVariables(); + } mAppendRunning = false; - mon.NotifyAll(); + mAppendPromise.ResolveIfExists(mActiveTrack, __func__); } void TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName) { MSE_DEBUG("rv=%d", aRejectValue); - mAppendPromise.RejectIfExists(aRejectValue, aName); - mSegmentParserLoopRunning = false; - // Wake-up any pending Abort() - MonitorAutoLock mon(mMonitor); mAppendRunning = false; - mon.NotifyAll(); + mAppendPromise.RejectIfExists(aRejectValue, aName); } void @@ -794,7 +840,12 @@ void TrackBuffersManager::OnDemuxerResetDone(nsresult) { MOZ_ASSERT(OnTaskQueue()); + MSE_DEBUG("mAbort:%d", static_cast(mAbort)); mDemuxerInitRequest.Complete(); + if (mAbort) { + RejectAppend(NS_ERROR_ABORT, __func__); + return; + } // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // request was being processed. See bug 1239983. MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); @@ -866,8 +917,13 @@ void TrackBuffersManager::OnDemuxerInitDone(nsresult) { MOZ_ASSERT(OnTaskQueue()); + MSE_DEBUG("mAbort:%d", static_cast(mAbort)); mDemuxerInitRequest.Complete(); + if (mAbort) { + RejectAppend(NS_ERROR_ABORT, __func__); + return; + } // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // request was being processed. See bug 1239983. MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); @@ -1127,8 +1183,9 @@ TrackBuffersManager::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure) { MOZ_ASSERT(OnTaskQueue()); - MSE_DEBUG("Failed to demux %s, failure:%d", - aTrack == TrackType::kVideoTrack ? "video" : "audio", aFailure); + MSE_DEBUG("Failed to demux %s, failure:%d mAbort:%d", + aTrack == TrackType::kVideoTrack ? "video" : "audio", + aFailure, static_cast(mAbort)); switch (aFailure) { case DemuxerFailureReason::END_OF_STREAM: case DemuxerFailureReason::WAITING_FOR_DATA: @@ -1155,10 +1212,15 @@ void TrackBuffersManager::DoDemuxVideo() { MOZ_ASSERT(OnTaskQueue()); + MSE_DEBUG("mAbort:%d", static_cast(mAbort)); if (!HasVideo()) { DoDemuxAudio(); return; } + if (mAbort) { + RejectProcessing(NS_ERROR_ABORT, __func__); + return; + } mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1) ->Then(GetTaskQueue(), __func__, this, &TrackBuffersManager::OnVideoDemuxCompleted, @@ -1179,10 +1241,15 @@ void TrackBuffersManager::DoDemuxAudio() { MOZ_ASSERT(OnTaskQueue()); + MSE_DEBUG("mAbort:%d", static_cast(mAbort)); if (!HasAudio()) { CompleteCodedFrameProcessing(); return; } + if (mAbort) { + RejectProcessing(NS_ERROR_ABORT, __func__); + return; + } mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1) ->Then(GetTaskQueue(), __func__, this, &TrackBuffersManager::OnAudioDemuxCompleted, @@ -1203,6 +1270,7 @@ void TrackBuffersManager::CompleteCodedFrameProcessing() { MOZ_ASSERT(OnTaskQueue()); + MSE_DEBUG("mAbort:%d", static_cast(mAbort)); // 1. For each coded frame in the media segment run the following steps: // Coded Frame Processing steps 1.1 to 1.21. @@ -1273,12 +1341,22 @@ TrackBuffersManager::CompleteCodedFrameProcessing() void TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName) { + if (mAbort) { + // mAppendPromise will be resolved immediately upon mProcessingPromise + // completing. + mAppendRunning = false; + } mProcessingPromise.RejectIfExists(aRejectValue, __func__); } void TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName) { + if (mAbort) { + // mAppendPromise will be resolved immediately upon mProcessingPromise + // completing. + mAppendRunning = false; + } mProcessingPromise.ResolveIfExists(aResolveValue, __func__); } diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index f150e0d4981..1ba68dd97da 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -98,6 +98,10 @@ public: bool& aError); media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack); +#if defined(DEBUG) + void Dump(const char* aPath) override; +#endif + void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes); private: @@ -119,7 +123,9 @@ private: // media segment have been processed. RefPtr CodedFrameProcessing(); void CompleteCodedFrameProcessing(); - // Called by ResetParserState. + // Called by ResetParserState. Complete parsing the input buffer for the + // current media segment. + void FinishCodedFrameProcessing(); void CompleteResetParserState(); RefPtr CodedFrameRemovalWithPromise(media::TimeInterval aInterval); @@ -304,6 +310,10 @@ private: MozPromiseHolder mProcessingPromise; MozPromiseHolder mAppendPromise; + // Set to true while SegmentParserLoop is running. This is used for diagnostic + // purposes only. We can't rely on mAppendPromise to be empty as it is only + // cleared in a follow up task. + bool mAppendRunning; // Trackbuffers definition. nsTArray GetTracksList(); @@ -339,6 +349,11 @@ private: RefPtr mSourceBufferAttributes; nsMainThreadPtrHandle mParentDecoder; + // MediaSource duration mirrored from MediaDecoder on the main thread.. + Mirror> mMediaSourceDuration; + + // Set to true if abort was called. + Atomic mAbort; // Set to true if mediasource state changed to ended. Atomic mEnded; @@ -348,13 +363,7 @@ private: Atomic mEvictionOccurred; // Monitor to protect following objects accessed across multipple threads. - // mMonitor is also notified if the value of mAppendRunning becomes false. mutable Monitor mMonitor; - // Set to true while SegmentParserLoop is running. - Atomic mAppendRunning; - // Set to true while SegmentParserLoop is running. - // This is for diagnostic only. Only accessed on the task queue. - bool mSegmentParserLoopRunning; // Stable audio and video track time ranges. media::TimeIntervals mVideoBufferedRanges; media::TimeIntervals mAudioBufferedRanges; From ccea027b2e4b3f37d924dead72575560c3101612 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 08:41:24 +0100 Subject: [PATCH 182/187] Backed out changeset 09836ef7b0f6 (bug 961323) for bustage on a CLOSED TREE --- js/public/UbiNode.h | 2 - js/public/UbiNodeDominatorTree.h | 2 + js/public/UbiNodeShortestPaths.h | 331 ---------------- js/src/builtin/TestingFunctions.cpp | 179 --------- .../tests/heap-analysis/shortestPaths.js | 44 --- js/src/jsapi-tests/testUbiNode.cpp | 358 +----------------- js/src/moz.build | 2 - js/src/vm/UbiNodeShortestPaths.cpp | 31 -- 8 files changed, 3 insertions(+), 946 deletions(-) delete mode 100644 js/public/UbiNodeShortestPaths.h delete mode 100644 js/src/jit-test/tests/heap-analysis/shortestPaths.js delete mode 100644 js/src/vm/UbiNodeShortestPaths.cpp diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index c8e2c110fb1..0e1a231789d 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -808,8 +808,6 @@ class Node { }; }; -using NodeSet = js::HashSet, js::SystemAllocPolicy>; -using NodeSetPtr = mozilla::UniquePtr>; /*** Edge and EdgeRange ***************************************************************************/ diff --git a/js/public/UbiNodeDominatorTree.h b/js/public/UbiNodeDominatorTree.h index 3b583b71cbb..70548e400e7 100644 --- a/js/public/UbiNodeDominatorTree.h +++ b/js/public/UbiNodeDominatorTree.h @@ -71,6 +71,8 @@ class JS_PUBLIC_API(DominatorTree) private: // Types. + using NodeSet = js::HashSet, js::SystemAllocPolicy>; + using NodeSetPtr = mozilla::UniquePtr>; using PredecessorSets = js::HashMap, js::SystemAllocPolicy>; using NodeToIndexMap = js::HashMap, diff --git a/js/public/UbiNodeShortestPaths.h b/js/public/UbiNodeShortestPaths.h deleted file mode 100644 index 90e0856576c..00000000000 --- a/js/public/UbiNodeShortestPaths.h +++ /dev/null @@ -1,331 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef js_UbiNodeShortestPaths_h -#define js_UbiNodeShortestPaths_h - -#include "mozilla/Maybe.h" -#include "mozilla/Move.h" - -#include "jsalloc.h" - -#include "js/UbiNodeBreadthFirst.h" -#include "js/Vector.h" - -namespace JS { -namespace ubi { - -/** - * A back edge along a path in the heap graph. - */ -struct JS_PUBLIC_API(BackEdge) -{ - private: - Node predecessor_; - EdgeName name_; - - public: - using Ptr = mozilla::UniquePtr>; - - BackEdge() : predecessor_(), name_(nullptr) { } - - bool init(const Node& predecessor, Edge& edge) { - MOZ_ASSERT(!predecessor_); - MOZ_ASSERT(!name_); - - predecessor_ = predecessor; - name_ = mozilla::Move(edge.name); - return true; - } - - BackEdge(const BackEdge&) = delete; - BackEdge& operator=(const BackEdge&) = delete; - - BackEdge(BackEdge&& rhs) - : predecessor_(rhs.predecessor_) - , name_(mozilla::Move(rhs.name_)) - { - MOZ_ASSERT(&rhs != this); - } - - BackEdge& operator=(BackEdge&& rhs) { - this->~BackEdge(); - new(this) BackEdge(Move(rhs)); - return *this; - } - - Ptr clone() const; - - const EdgeName& name() const { return name_; } - EdgeName& name() { return name_; } - - const JS::ubi::Node& predecessor() const { return predecessor_; } -}; - -/** - * A path is a series of back edges from which we discovered a target node. - */ -using Path = mozilla::Vector; - -/** - * The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest - * retaining paths for each of a target set of nodes, starting from the same - * root node. - */ -struct JS_PUBLIC_API(ShortestPaths) -{ - private: - // Types, type aliases, and data members. - - using BackEdgeVector = mozilla::Vector; - using NodeToBackEdgeVectorMap = js::HashMap, - js::SystemAllocPolicy>; - - struct Handler; - using Traversal = BreadthFirst; - - /** - * A `JS::ubi::BreadthFirst` traversal handler that records back edges for - * how we reached each node, allowing us to reconstruct the shortest - * retaining paths after the traversal. - */ - struct Handler - { - using NodeData = BackEdge; - - ShortestPaths& shortestPaths; - size_t totalMaxPathsToRecord; - size_t totalPathsRecorded; - - explicit Handler(ShortestPaths& shortestPaths) - : shortestPaths(shortestPaths) - , totalMaxPathsToRecord(shortestPaths.targets_.count() * shortestPaths.maxNumPaths_) - , totalPathsRecorded(0) - { - } - - bool - operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge, - BackEdge* back, bool first) - { - MOZ_ASSERT(back); - MOZ_ASSERT(traversal.visited.has(origin)); - MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord); - - if (first && !back->init(origin, edge)) - return false; - - if (!shortestPaths.targets_.has(edge.referent)) - return true; - - // If `first` is true, then we moved the edge's name into `back` in - // the above call to `init`. So clone that back edge to get the - // correct edge name. If `first` is not true, then our edge name is - // still in `edge`. This accounts for the asymmetry between - // `back->clone()` in the first branch, and the `init` call in the - // second branch. - - auto ptr = shortestPaths.paths_.lookupForAdd(edge.referent); - if (first) { - MOZ_ASSERT(!ptr); - BackEdgeVector paths; - if (!paths.reserve(shortestPaths.maxNumPaths_)) - return false; - auto cloned = back->clone(); - if (!cloned) - return false; - paths.infallibleAppend(mozilla::Move(cloned)); - if (!shortestPaths.paths_.add(ptr, edge.referent, mozilla::Move(paths))) - return false; - totalPathsRecorded++; - } else if (ptr->value().length() < shortestPaths.maxNumPaths_) { - MOZ_ASSERT(ptr); - BackEdge::Ptr thisBackEdge(js_new()); - if (!thisBackEdge || !thisBackEdge->init(origin, edge)) - return false; - ptr->value().infallibleAppend(mozilla::Move(thisBackEdge)); - totalPathsRecorded++; - } - - MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord); - if (totalPathsRecorded == totalMaxPathsToRecord) - traversal.stop(); - - return true; - } - - }; - - // The maximum number of paths to record for each node. - uint32_t maxNumPaths_; - - // The root node we are starting the search from. - Node root_; - - // The set of nodes we are searching for paths to. - NodeSet targets_; - - // The resulting paths. - NodeToBackEdgeVectorMap paths_; - - // Need to keep alive the traversal's back edges so we can walk them later - // when the traversal is over when recreating the shortest paths. - Traversal::NodeMap backEdges_; - - private: - // Private methods. - - ShortestPaths(uint32_t maxNumPaths, const Node& root, NodeSet&& targets) - : maxNumPaths_(maxNumPaths) - , root_(root) - , targets_(mozilla::Move(targets)) - , paths_() - , backEdges_() - { - MOZ_ASSERT(maxNumPaths_ > 0); - MOZ_ASSERT(root_); - MOZ_ASSERT(targets_.initialized()); - } - - bool initialized() const { - return targets_.initialized() && - paths_.initialized() && - backEdges_.initialized(); - } - - public: - // Public methods. - - ShortestPaths(ShortestPaths&& rhs) - : maxNumPaths_(rhs.maxNumPaths_) - , root_(rhs.root_) - , targets_(mozilla::Move(rhs.targets_)) - , paths_(mozilla::Move(rhs.paths_)) - , backEdges_(mozilla::Move(rhs.backEdges_)) - { - MOZ_ASSERT(this != &rhs, "self-move is not allowed"); - } - - ShortestPaths& operator=(ShortestPaths&& rhs) { - this->~ShortestPaths(); - new (this) ShortestPaths(mozilla::Move(rhs)); - return *this; - } - - ShortestPaths(const ShortestPaths&) = delete; - ShortestPaths& operator=(const ShortestPaths&) = delete; - - /** - * Construct a new `JS::ubi::ShortestPaths`, finding up to `maxNumPaths` - * shortest retaining paths for each target node in `targets` starting from - * `root`. - * - * The resulting `ShortestPaths` instance must not outlive the - * `JS::ubi::Node` graph it was constructed from. - * - * - For `JS::ubi::Node` graphs backed by the live heap graph, this means - * that the `ShortestPaths`'s lifetime _must_ be contained within the - * scope of the provided `AutoCheckCannotGC` reference because a GC will - * invalidate the nodes. - * - * - For `JS::ubi::Node` graphs backed by some other offline structure - * provided by the embedder, the resulting `ShortestPaths`'s lifetime is - * bounded by that offline structure's lifetime. - * - * Returns `mozilla::Nothing()` on OOM failure. It is the caller's - * responsibility to handle and report the OOM. - */ - static mozilla::Maybe - Create(JSRuntime* rt, AutoCheckCannotGC& noGC, uint32_t maxNumPaths, const Node& root, NodeSet&& targets) { - MOZ_ASSERT(targets.count() > 0); - MOZ_ASSERT(maxNumPaths > 0); - - size_t count = targets.count(); - ShortestPaths paths(maxNumPaths, root, mozilla::Move(targets)); - if (!paths.paths_.init(count)) - return mozilla::Nothing(); - - Handler handler(paths); - Traversal traversal(rt, handler, noGC); - traversal.wantNames = true; - if (!traversal.init() || !traversal.addStartVisited(root) || !traversal.traverse()) - return mozilla::Nothing(); - - // Take ownership of the back edges we created while traversing the - // graph so that we can follow them from `paths_` and don't - // use-after-free. - paths.backEdges_ = mozilla::Move(traversal.visited); - - MOZ_ASSERT(paths.initialized()); - return mozilla::Some(mozilla::Move(paths)); - } - - /** - * Get a range that iterates over each target node we searched for retaining - * paths for. The returned range must not outlive the `ShortestPaths` - * instance. - */ - NodeSet::Range eachTarget() const { - MOZ_ASSERT(initialized()); - return targets_.all(); - } - - /** - * Invoke the provided functor/lambda/callable once for each retaining path - * discovered for `target`. The `func` is passed a single `JS::ubi::Path&` - * argument, which contains each edge along the path ordered starting from - * the root and ending at the target, and must not outlive the scope of the - * call. - * - * Note that it is possible that we did not find any paths from the root to - * the given target, in which case `func` will not be invoked. - */ - template - bool forEachPath(const Node& target, Func func) { - MOZ_ASSERT(initialized()); - MOZ_ASSERT(targets_.has(target)); - - auto ptr = paths_.lookup(target); - - // We didn't find any paths to this target, so nothing to do here. - if (!ptr) - return true; - - MOZ_ASSERT(ptr->value().length() <= maxNumPaths_); - - Path path; - for (const auto& backEdge : ptr->value()) { - path.clear(); - - if (!path.append(backEdge.get())) - return false; - - Node here = backEdge->predecessor(); - MOZ_ASSERT(here); - - while (here != root_) { - auto p = backEdges_.lookup(here); - MOZ_ASSERT(p); - if (!path.append(&p->value())) - return false; - here = p->value().predecessor(); - MOZ_ASSERT(here); - } - - path.reverse(); - - if (!func(path)) - return false; - } - - return true; - } -}; - -} // namespace ubi -} // namespace JS - -#endif // js_UbiNodeShortestPaths_h diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 26632d20463..4b56efd1ac7 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -27,7 +27,6 @@ #include "js/StructuredClone.h" #include "js/UbiNode.h" #include "js/UbiNodeBreadthFirst.h" -#include "js/UbiNodeShortestPaths.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/GlobalObject.h" @@ -2542,177 +2541,6 @@ FindPath(JSContext* cx, unsigned argc, Value* vp) return true; } -static bool -ShortestPaths(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (!args.requireAtLeast(cx, "shortestPaths", 3)) - return false; - - // We don't ToString non-objects given as 'start' or 'target', because this - // test is all about object identity, and ToString doesn't preserve that. - // Non-GCThing endpoints don't make much sense. - if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, - JSDVG_SEARCH_STACK, args[0], nullptr, - "not an object, string, or symbol", nullptr); - return false; - } - - if (!args[1].isObject() || !args[1].toObject().is()) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, - JSDVG_SEARCH_STACK, args[1], nullptr, - "not an array object", nullptr); - return false; - } - - RootedArrayObject objs(cx, &args[1].toObject().as()); - size_t length = objs->getDenseInitializedLength(); - if (length == 0) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, - JSDVG_SEARCH_STACK, args[1], nullptr, - "not a dense array object with one or more elements", nullptr); - return false; - } - - for (size_t i = 0; i < length; i++) { - RootedValue el(cx, objs->getDenseElement(i)); - if (!el.isObject() && !el.isString() && !el.isSymbol()) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, - JSDVG_SEARCH_STACK, el, nullptr, - "not an object, string, or symbol", nullptr); - return false; - } - } - - int32_t maxNumPaths; - if (!JS::ToInt32(cx, args[2], &maxNumPaths)) - return false; - if (maxNumPaths <= 0) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, - JSDVG_SEARCH_STACK, args[2], nullptr, - "not greater than 0", nullptr); - return false; - } - - // We accumulate the results into a GC-stable form, due to the fact that the - // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph) - // is bounded within an AutoCheckCannotGC. - Rooted>>> values(cx, GCVector>>(cx)); - Vector>> names(cx); - - { - JS::AutoCheckCannotGC noGC(cx->runtime()); - - JS::ubi::NodeSet targets; - if (!targets.init()) { - ReportOutOfMemory(cx); - return false; - } - - for (size_t i = 0; i < length; i++) { - RootedValue val(cx, objs->getDenseElement(i)); - JS::ubi::Node node(val); - if (!targets.put(node)) { - ReportOutOfMemory(cx); - return false; - } - } - - JS::ubi::Node root(args[0]); - auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(cx->runtime(), noGC, maxNumPaths, - root, mozilla::Move(targets)); - if (maybeShortestPaths.isNothing()) { - ReportOutOfMemory(cx); - return false; - } - auto& shortestPaths = *maybeShortestPaths; - - for (size_t i = 0; i < length; i++) { - if (!values.append(GCVector>(cx)) || - !names.append(Vector>(cx))) - { - return false; - } - - RootedValue val(cx, objs->getDenseElement(i)); - JS::ubi::Node target(val); - - bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) { - Rooted> pathVals(cx, GCVector(cx)); - Vector pathNames(cx); - - for (auto& part : path) { - if (!pathVals.append(part->predecessor().exposeToJS()) || - !pathNames.append(mozilla::Move(part->name()))) - { - return false; - } - } - - return values.back().append(mozilla::Move(pathVals.get())) && - names.back().append(mozilla::Move(pathNames)); - }); - - if (!ok) - return false; - } - } - - MOZ_ASSERT(values.length() == names.length()); - MOZ_ASSERT(values.length() == length); - - RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length)); - if (!results) - return false; - results->ensureDenseInitializedLength(cx, 0, length); - - for (size_t i = 0; i < length; i++) { - size_t numPaths = values[i].length(); - MOZ_ASSERT(names[i].length() == numPaths); - - RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths)); - if (!pathsArray) - return false; - pathsArray->ensureDenseInitializedLength(cx, 0, numPaths); - - for (size_t j = 0; j < numPaths; j++) { - size_t pathLength = values[i][j].length(); - MOZ_ASSERT(names[i][j].length() == pathLength); - - RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength)); - if (!path) - return false; - path->ensureDenseInitializedLength(cx, 0, pathLength); - - for (size_t k = 0; k < pathLength; k++) { - RootedPlainObject part(cx, NewBuiltinClassInstance(cx)); - if (!part) - return false; - - RootedValue predecessor(cx, values[i][j][k]); - if (!JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE)) - return false; - - if (names[i][j][k]) { - RootedString edge(cx, NewStringCopyZ(cx, names[i][j][k].get())); - if (!edge || !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) - return false; - } - - path->setDenseElement(k, ObjectValue(*part)); - } - - pathsArray->setDenseElement(j, ObjectValue(*path)); - } - - results->setDenseElement(i, ObjectValue(*pathsArray)); - } - - args.rval().setObject(*results); - return true; -} - static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) { @@ -3722,13 +3550,6 @@ gc::ZealModeHelpText), " element's edge is the node of the i+1'th array element; the destination of\n" " the last array element is implicitly |target|.\n"), - JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0, -"shortestPaths(start, targets, maxNumPaths)", -" Return an array of arrays of shortest retaining paths. There is an array of\n" -" shortest retaining paths for each object in |targets|. The maximum number of\n" -" paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n" -" path is of the form |{ predecessor, edge }|."), - #ifdef DEBUG JS_FN_HELP("dumpObject", DumpObject, 1, 0, "dumpObject()", diff --git a/js/src/jit-test/tests/heap-analysis/shortestPaths.js b/js/src/jit-test/tests/heap-analysis/shortestPaths.js deleted file mode 100644 index d0c050c297e..00000000000 --- a/js/src/jit-test/tests/heap-analysis/shortestPaths.js +++ /dev/null @@ -1,44 +0,0 @@ -// The shortestPaths function exists solely to let the fuzzers go to town and -// exercise the code paths it calls into, hence there is nothing to assert here. -// -// The actual behavior of JS::ubi::ShortestPaths is tested in -// js/src/jsapi-tests/testUbiNode.cpp, where we can actually control the -// structure of the heap graph to test specific shapes. - -function f(x) { - return x + x; -} - -var g = f.bind(null, 5); - -var o = { - p: g -}; - -function dumpPaths(results) { - results = results.map(paths => { - return paths.map(path => { - return path.map(part => { - return { - predecessor: Object.prototype.toString.call(part.predecessor), - edge: part.edge - }; - }); - }); - }); - print(JSON.stringify(results, null, 2)); -} - -print("shortestPaths(this, [Object, f, o.p], 5)"); -var paths = shortestPaths(this, [Object, f, o.p], 5); -dumpPaths(paths); - -print(); -print("shortestPaths(o, [f], 1)") -paths = shortestPaths(o, [f], 1); -dumpPaths(paths); - -print(); -print("shortestPaths(this, [f], 5)") -paths = shortestPaths(this, [f], 5); -dumpPaths(paths); diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index 756eec55d77..ff0d540f8a7 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -6,7 +6,6 @@ #include "js/UbiNode.h" #include "js/UbiNodeDominatorTree.h" #include "js/UbiNodePostOrder.h" -#include "js/UbiNodeShortestPaths.h" #include "jsapi-tests/tests.h" #include "vm/SavedFrame.h" @@ -24,15 +23,8 @@ struct FakeNode explicit FakeNode(char name) : name(name), edges() { } - bool addEdgeTo(FakeNode& referent, const char16_t* edgeName = nullptr) { + bool addEdgeTo(FakeNode& referent) { JS::ubi::Node node(&referent); - - if (edgeName) { - auto ownedName = js::DuplicateString(edgeName); - MOZ_RELEASE_ASSERT(ownedName); - return edges.emplaceBack(ownedName.release(), node); - } - return edges.emplaceBack(nullptr, node); } }; @@ -658,351 +650,3 @@ BEGIN_TEST(test_JS_ubi_Node_scriptFilename) return true; } END_TEST(test_JS_ubi_Node_scriptFilename) - -#define LAMBDA_CHECK(cond) \ - do { \ - if (!(cond)) { \ - fprintf(stderr,"%s:%d:CHECK failed: " #cond "\n", __FILE__, __LINE__); \ - return false; \ - } \ - } while (false) - -static void -dumpPath(JS::ubi::Path& path) -{ - for (size_t i = 0; i < path.length(); i++) { - fprintf(stderr, "path[%llu]->predecessor() = '%c'\n", - (long long unsigned) i, - path[i]->predecessor().as()->name); - } -} - -BEGIN_TEST(test_JS_ubi_ShortestPaths_no_path) -{ - // Create the following graph: - // - // .---. .---. .---. - // | a | <--> | c | | b | - // '---' '---' '---' - FakeNode a('a'); - FakeNode b('b'); - FakeNode c('c'); - CHECK(a.addEdgeTo(c)); - CHECK(c.addEdgeTo(a)); - - mozilla::Maybe maybeShortestPaths; - { - JS::AutoCheckCannotGC noGC(rt); - - JS::ubi::NodeSet targets; - CHECK(targets.init()); - CHECK(targets.put(&b)); - - maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, - mozilla::Move(targets)); - } - - CHECK(maybeShortestPaths); - auto& paths = *maybeShortestPaths; - - size_t numPathsFound = 0; - bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { - numPathsFound++; - dumpPath(path); - return true; - }); - CHECK(ok); - CHECK(numPathsFound == 0); - - return true; -} -END_TEST(test_JS_ubi_ShortestPaths_no_path) - -BEGIN_TEST(test_JS_ubi_ShortestPaths_one_path) -{ - // Create the following graph: - // - // .---. .---. .---. - // | a | <--> | c | --> | b | - // '---' '---' '---' - FakeNode a('a'); - FakeNode b('b'); - FakeNode c('c'); - CHECK(a.addEdgeTo(c)); - CHECK(c.addEdgeTo(a)); - CHECK(c.addEdgeTo(b)); - - mozilla::Maybe maybeShortestPaths; - { - JS::AutoCheckCannotGC noGC(rt); - - JS::ubi::NodeSet targets; - CHECK(targets.init()); - CHECK(targets.put(&b)); - - maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, - mozilla::Move(targets)); - } - - CHECK(maybeShortestPaths); - auto& paths = *maybeShortestPaths; - - size_t numPathsFound = 0; - bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { - numPathsFound++; - - dumpPath(path); - LAMBDA_CHECK(path.length() == 2); - LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); - LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&c)); - - return true; - }); - - CHECK(ok); - CHECK(numPathsFound == 1); - - return true; -} -END_TEST(test_JS_ubi_ShortestPaths_one_path) - -BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_paths) -{ - // Create the following graph: - // - // .---. - // .-----| a |-----. - // | '---' | - // V | V - // .---. | .---. - // | b | | | d | - // '---' | '---' - // | | | - // V | V - // .---. | .---. - // | c | | | e | - // '---' V '---' - // | .---. | - // '---->| f |<----' - // '---' - FakeNode a('a'); - FakeNode b('b'); - FakeNode c('c'); - FakeNode d('d'); - FakeNode e('e'); - FakeNode f('f'); - CHECK(a.addEdgeTo(b)); - CHECK(a.addEdgeTo(f)); - CHECK(a.addEdgeTo(d)); - CHECK(b.addEdgeTo(c)); - CHECK(c.addEdgeTo(f)); - CHECK(d.addEdgeTo(e)); - CHECK(e.addEdgeTo(f)); - - mozilla::Maybe maybeShortestPaths; - { - JS::AutoCheckCannotGC noGC(rt); - - JS::ubi::NodeSet targets; - CHECK(targets.init()); - CHECK(targets.put(&f)); - - maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, - mozilla::Move(targets)); - } - - CHECK(maybeShortestPaths); - auto& paths = *maybeShortestPaths; - - size_t numPathsFound = 0; - bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) { - numPathsFound++; - dumpPath(path); - - switch (path.back()->predecessor().as()->name) { - case 'a': { - LAMBDA_CHECK(path.length() == 1); - break; - } - - case 'c': { - LAMBDA_CHECK(path.length() == 3); - LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); - LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&b)); - LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&c)); - break; - } - - case 'e': { - LAMBDA_CHECK(path.length() == 3); - LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a)); - LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&d)); - LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&e)); - break; - } - - default: { - // Unexpected path! - LAMBDA_CHECK(false); - } - } - - return true; - }); - - CHECK(ok); - fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); - CHECK(numPathsFound == 3); - - return true; -} -END_TEST(test_JS_ubi_ShortestPaths_multiple_paths) - -BEGIN_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max) -{ - // Create the following graph: - // - // .---. - // .-----| a |-----. - // | '---' | - // V | V - // .---. | .---. - // | b | | | d | - // '---' | '---' - // | | | - // V | V - // .---. | .---. - // | c | | | e | - // '---' V '---' - // | .---. | - // '---->| f |<----' - // '---' - FakeNode a('a'); - FakeNode b('b'); - FakeNode c('c'); - FakeNode d('d'); - FakeNode e('e'); - FakeNode f('f'); - CHECK(a.addEdgeTo(b)); - CHECK(a.addEdgeTo(f)); - CHECK(a.addEdgeTo(d)); - CHECK(b.addEdgeTo(c)); - CHECK(c.addEdgeTo(f)); - CHECK(d.addEdgeTo(e)); - CHECK(e.addEdgeTo(f)); - - mozilla::Maybe maybeShortestPaths; - { - JS::AutoCheckCannotGC noGC(rt); - - JS::ubi::NodeSet targets; - CHECK(targets.init()); - CHECK(targets.put(&f)); - - maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 1, &a, - mozilla::Move(targets)); - } - - CHECK(maybeShortestPaths); - auto& paths = *maybeShortestPaths; - - size_t numPathsFound = 0; - bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) { - numPathsFound++; - dumpPath(path); - return true; - }); - - CHECK(ok); - fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); - CHECK(numPathsFound == 1); - - return true; -} -END_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max) - -BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target) -{ - // Create the following graph: - // - // .---. - // .-----| a |-----. - // | '---' | - // | | | - // |x |y |z - // | | | - // | V | - // | .---. | - // '---->| b |<----' - // '---' - FakeNode a('a'); - FakeNode b('b'); - CHECK(a.addEdgeTo(b, MOZ_UTF16("x"))); - CHECK(a.addEdgeTo(b, MOZ_UTF16("y"))); - CHECK(a.addEdgeTo(b, MOZ_UTF16("z"))); - - mozilla::Maybe maybeShortestPaths; - { - JS::AutoCheckCannotGC noGC(rt); - - JS::ubi::NodeSet targets; - CHECK(targets.init()); - CHECK(targets.put(&b)); - - maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a, - mozilla::Move(targets)); - } - - CHECK(maybeShortestPaths); - auto& paths = *maybeShortestPaths; - - size_t numPathsFound = 0; - bool foundX = false; - bool foundY = false; - bool foundZ = false; - - bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) { - numPathsFound++; - dumpPath(path); - - LAMBDA_CHECK(path.length() == 1); - LAMBDA_CHECK(path.back()->name()); - LAMBDA_CHECK(js_strlen(path.back()->name().get()) == 1); - - auto c = uint8_t(path.back()->name().get()[0]); - fprintf(stderr, "Edge name = '%c'\n", c); - - switch (c) { - case 'x': { - foundX = true; - break; - } - case 'y': { - foundY = true; - break; - } - case 'z': { - foundZ = true; - break; - } - default: { - // Unexpected edge! - LAMBDA_CHECK(false); - } - } - - return true; - }); - - CHECK(ok); - fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound); - CHECK(numPathsFound == 3); - CHECK(foundX); - CHECK(foundY); - CHECK(foundZ); - - return true; -} -END_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target) - -#undef LAMBDA_CHECK diff --git a/js/src/moz.build b/js/src/moz.build index 47b5627768b..aa775c90474 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -136,7 +136,6 @@ EXPORTS.js += [ '../public/UbiNodeCensus.h', '../public/UbiNodeDominatorTree.h', '../public/UbiNodePostOrder.h', - '../public/UbiNodeShortestPaths.h', '../public/UniquePtr.h', '../public/Utility.h', '../public/Value.h', @@ -340,7 +339,6 @@ UNIFIED_SOURCES += [ 'vm/TypeInference.cpp', 'vm/UbiNode.cpp', 'vm/UbiNodeCensus.cpp', - 'vm/UbiNodeShortestPaths.cpp', 'vm/UnboxedObject.cpp', 'vm/Unicode.cpp', 'vm/Value.cpp', diff --git a/js/src/vm/UbiNodeShortestPaths.cpp b/js/src/vm/UbiNodeShortestPaths.cpp deleted file mode 100644 index 46308f606d0..00000000000 --- a/js/src/vm/UbiNodeShortestPaths.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "js/UbiNodeShortestPaths.h" - -#include "jsstr.h" - -namespace JS { -namespace ubi { - -JS_PUBLIC_API(BackEdge::Ptr) -BackEdge::clone() const -{ - BackEdge::Ptr clone(js_new()); - if (!clone) - return nullptr; - - clone->predecessor_ = predecessor(); - if (name()) { - clone->name_ = js::DuplicateString(name().get()); - if (!clone->name_) - return nullptr; - } - return mozilla::Move(clone); -} - -} // namespace ubi -} // namespace JS From fdd0ae621af68fd1d70577b8acf13c00c97fb054 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 08:49:28 +0100 Subject: [PATCH 183/187] Backed out changeset 8e13ba75bccf (bug 1247775) --- gfx/2d/2D.h | 11 + gfx/2d/DrawTargetD2D.cpp | 2831 +++++++++++++++++++++++++++ gfx/2d/DrawTargetD2D.h | 290 +++ gfx/2d/DrawTargetD2D1.cpp | 74 +- gfx/2d/Factory.cpp | 99 +- gfx/2d/FilterNodeD2D1.cpp | 5 + gfx/2d/HelpersD2D.h | 3 +- gfx/2d/NativeFontResourceDWrite.cpp | 6 +- gfx/2d/PathD2D.cpp | 4 +- gfx/2d/ScaledFontDWrite.cpp | 4 +- gfx/2d/SourceSurfaceD2D.cpp | 317 +++ gfx/2d/SourceSurfaceD2D.h | 90 + gfx/2d/SourceSurfaceD2DTarget.cpp | 325 +++ gfx/2d/SourceSurfaceD2DTarget.h | 90 + gfx/2d/Types.h | 2 +- gfx/2d/moz.build | 3 + 16 files changed, 4076 insertions(+), 78 deletions(-) create mode 100644 gfx/2d/DrawTargetD2D.cpp create mode 100644 gfx/2d/DrawTargetD2D.h create mode 100644 gfx/2d/SourceSurfaceD2D.cpp create mode 100644 gfx/2d/SourceSurfaceD2D.h create mode 100644 gfx/2d/SourceSurfaceD2DTarget.cpp create mode 100644 gfx/2d/SourceSurfaceD2DTarget.h diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index f233e4346f9..6913ce9b849 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -36,6 +36,8 @@ typedef _cairo_surface cairo_surface_t; struct _cairo_scaled_font; typedef _cairo_scaled_font cairo_scaled_font_t; +struct ID3D10Device1; +struct ID3D10Texture2D; struct ID3D11Texture2D; struct ID3D11Device; struct ID2D1Device; @@ -1362,6 +1364,14 @@ public: #endif #ifdef WIN32 + static already_AddRefed CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); + static already_AddRefed + CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, + ID3D10Texture2D *aTextureB, + SurfaceFormat aFormat); + + static void SetDirect3D10Device(ID3D10Device1 *aDevice); + static ID3D10Device1 *GetDirect3D10Device(); static already_AddRefed CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat); static void SetDirect3D11Device(ID3D11Device *aDevice); @@ -1384,6 +1394,7 @@ public: private: static ID2D1Device *mD2D1Device; + static ID3D10Device1 *mD3D10Device; static ID3D11Device *mD3D11Device; #endif diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp new file mode 100644 index 00000000000..f1e2731090a --- /dev/null +++ b/gfx/2d/DrawTargetD2D.cpp @@ -0,0 +1,2831 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#include +#include "DrawTargetD2D.h" +#include "SourceSurfaceD2D.h" +#include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2DTarget.h" +#include "ShadersD2D.h" +#include "PathD2D.h" +#include "GradientStopsD2D.h" +#include "ScaledFontDWrite.h" +#include "ImageScaling.h" +#include "Logging.h" +#include "Tools.h" +#include +#include "FilterNodeSoftware.h" + +#include "FilterNodeD2D1.h" +#include "ExtendInputEffectD2D1.h" + +#include +#include + +// decltype is not usable for overloaded functions. +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + +using namespace std; + +namespace mozilla { +namespace gfx { + +struct Vertex { + float x; + float y; +}; + +ID2D1Factory *DrawTargetD2D::mFactory; +IDWriteFactory *DrawTargetD2D::mDWriteFactory; +uint64_t DrawTargetD2D::mVRAMUsageDT; +uint64_t DrawTargetD2D::mVRAMUsageSS; + +// Helper class to restore surface contents that was clipped out but may have +// been altered by a drawing call. +class AutoSaveRestoreClippedOut +{ +public: + AutoSaveRestoreClippedOut(DrawTargetD2D *aDT) + : mDT(aDT) + {} + + void Save() { + if (!mDT->mPushedClips.size()) { + return; + } + + mDT->Flush(); + + RefPtr tmpTexture; + IntSize size = mDT->mSize; + SurfaceFormat format = mDT->mFormat; + + CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, + 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1 CreateTexture2D failure " << size << " Code: " << hexa(hr) << " format " << (int)format; + return; + } + mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture); + + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format)); + + RefPtr surf; + + tmpTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surf)); + + hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf, + &props, getter_AddRefs(mOldSurfBitmap)); + + if (FAILED(hr)) { + gfxCriticalError() << "[D2D] CreateSharedBitmap failure " << size << " Code: " << hexa(hr); + return; + } + + IntRect clipBounds; + mClippedArea = mDT->GetClippedGeometry(&clipBounds); + + if (!clipBounds.IsEqualEdges(IntRect(IntPoint(0, 0), mDT->mSize))) { + // We still need to take into account clipBounds if it contains additional + // clipping information. + RefPtr rectGeom; + factory()->CreateRectangleGeometry(D2D1::Rect(Float(clipBounds.x), + Float(clipBounds.y), + Float(clipBounds.XMost()), + Float(clipBounds.YMost())), + getter_AddRefs(rectGeom)); + + mClippedArea = IntersectGeometry(mClippedArea, rectGeom); + } + } + + ID2D1Factory *factory() { return mDT->factory(); } + + ~AutoSaveRestoreClippedOut() + { + if (!mOldSurfBitmap) { + return; + } + + ID2D1RenderTarget *rt = mDT->mRT; + + // Write the area that was clipped out back to the surface. This all + // happens in device space. + rt->SetTransform(D2D1::IdentityMatrix()); + mDT->mTransformDirty = true; + + RefPtr rectGeom; + factory()->CreateRectangleGeometry( + D2D1::RectF(0, 0, float(mDT->mSize.width), float(mDT->mSize.height)), + getter_AddRefs(rectGeom)); + + RefPtr invClippedArea; + factory()->CreatePathGeometry(getter_AddRefs(invClippedArea)); + RefPtr sink; + invClippedArea->Open(getter_AddRefs(sink)); + + rectGeom->CombineWithGeometry(mClippedArea, D2D1_COMBINE_MODE_EXCLUDE, nullptr, sink); + sink->Close(); + + RefPtr brush; + HRESULT hr = rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), getter_AddRefs(brush)); + if (FAILED(hr)) { + gfxCriticalNote << "[D2D] CreateBitmapBrush failure " << hexa(hr); + return; + } + + rt->FillGeometry(invClippedArea, brush); + } + +private: + + DrawTargetD2D *mDT; + + // If we have an operator unbound by the source, this will contain a bitmap + // with the old dest surface data. + RefPtr mOldSurfBitmap; + // This contains the area drawing is clipped to. + RefPtr mClippedArea; +}; + +ID2D1Factory *D2DFactory() +{ + return DrawTargetD2D::factory(); +} + +DrawTargetD2D::DrawTargetD2D() + : mCurrentCachedLayer(0) + , mClipsArePushed(false) + , mPrivateData(nullptr) +{ +} + +DrawTargetD2D::~DrawTargetD2D() +{ + if (mRT) { + PopAllClips(); + + mRT->EndDraw(); + + mVRAMUsageDT -= GetByteSize(); + } + if (mTempRT) { + mTempRT->EndDraw(); + + mVRAMUsageDT -= GetByteSize(); + } + + if (mSnapshot) { + // We may hold the only reference. MarkIndependent will clear mSnapshot; + // keep the snapshot object alive so it doesn't get destroyed while + // MarkIndependent is running. + RefPtr deathGrip = mSnapshot; + // mSnapshot can be treated as independent of this DrawTarget since we know + // this DrawTarget won't change again. + deathGrip->MarkIndependent(); + // mSnapshot will be cleared now. + } + + for (int i = 0; i < kLayerCacheSize; i++) { + if (mCachedLayers[i]) { + mCachedLayers[i] = nullptr; + mVRAMUsageDT -= GetByteSize(); + } + } + + // Targets depending on us can break that dependency, since we're obviously not going to + // be modified in the future. + for (TargetSet::iterator iter = mDependentTargets.begin(); + iter != mDependentTargets.end(); iter++) { + (*iter)->mDependingOnTargets.erase(this); + } + // Our dependencies on other targets no longer matter. + for (TargetSet::iterator iter = mDependingOnTargets.begin(); + iter != mDependingOnTargets.end(); iter++) { + (*iter)->mDependentTargets.erase(this); + } +} + +/* + * DrawTarget Implementation + */ +already_AddRefed +DrawTargetD2D::Snapshot() +{ + if (!mSnapshot) { + mSnapshot = new SourceSurfaceD2DTarget(this, mTexture, mFormat); + Flush(); + } + + RefPtr snapshot(mSnapshot); + return snapshot.forget(); +} + +void +DrawTargetD2D::Flush() +{ + PopAllClips(); + + HRESULT hr = mRT->Flush(); + + if (FAILED(hr)) { + gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hexa(hr); + } + + // We no longer depend on any target. + for (TargetSet::iterator iter = mDependingOnTargets.begin(); + iter != mDependingOnTargets.end(); iter++) { + (*iter)->mDependentTargets.erase(this); + } + mDependingOnTargets.clear(); +} + +void +DrawTargetD2D::AddDependencyOnSource(SourceSurfaceD2DTarget* aSource) +{ + if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { + aSource->mDrawTarget->mDependentTargets.insert(this); + mDependingOnTargets.insert(aSource->mDrawTarget); + } +} + +already_AddRefed +DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, + Rect &aSource) +{ + RefPtr bitmap; + + switch (aSurface->GetType()) { + + case SurfaceType::D2D1_BITMAP: + { + SourceSurfaceD2D *srcSurf = static_cast(aSurface); + bitmap = srcSurf->GetBitmap(); + } + break; + case SurfaceType::D2D1_DRAWTARGET: + { + SourceSurfaceD2DTarget *srcSurf = static_cast(aSurface); + bitmap = srcSurf->GetBitmap(mRT); + AddDependencyOnSource(srcSurf); + } + break; + default: + { + RefPtr srcSurf = aSurface->GetDataSurface(); + + if (!srcSurf) { + gfxDebug() << "Not able to deal with non-data source surface."; + return nullptr; + } + + // We need to include any pixels that are overlapped by aSource + Rect sourceRect(aSource); + sourceRect.RoundOut(); + + if (sourceRect.IsEmpty()) { + gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail."; + return nullptr; + } + + if (sourceRect.width > mRT->GetMaximumBitmapSize() || + sourceRect.height > mRT->GetMaximumBitmapSize()) { + gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail."; + // Don't know how to deal with this yet. + return nullptr; + } + + HRESULT hr; + { + DataSourceSurface::ScopedMap srcMap(srcSurf, DataSourceSurface::READ); + if (MOZ2D_WARN_IF(!srcMap.IsMapped())) { + return nullptr; + } + + int stride = srcMap.GetStride(); + unsigned char *data = srcMap.GetData() + + (uint32_t)sourceRect.y * stride + + (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat()); + + D2D1_BITMAP_PROPERTIES props = + D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat())); + hr = mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, getter_AddRefs(bitmap)); + } + if (FAILED(hr)) { + IntSize size(sourceRect.width, sourceRect.height); + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1CreateBitmap failure " << size << " Code: " << hexa(hr) << " format " << (int)srcSurf->GetFormat(); + return nullptr; + } + + // subtract the integer part leaving the fractional part + aSource.x -= (uint32_t)aSource.x; + aSource.y -= (uint32_t)aSource.y; + } + break; + } + + return bitmap.forget(); +} + +already_AddRefed +DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface) +{ + RefPtr image; + + Rect r(Point(), Size(aSurface->GetSize())); + image = GetBitmapForSurface(aSurface, r); + + return image.forget(); +} + +void +DrawTargetD2D::DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) +{ + RefPtr bitmap; + + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + Rect srcRect = aSource; + + bitmap = GetBitmapForSurface(aSurface, srcRect); + if (!bitmap) { + return; + } + + rt->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(srcRect)); + + FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), aDest); +} + +void +DrawTargetD2D::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + RefPtr dc; + HRESULT hr; + + hr = mRT->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); + + if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) { + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + hr = rt->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); + + if (SUCCEEDED(hr)) { + FilterNodeD2D1* node = static_cast(aNode); + node->WillDraw(this); + + dc->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); + + Rect destRect = aSourceRect; + destRect.MoveBy(aDestPoint); + FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect); + return; + } + } + + if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) { + gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!"; + return; + } + + FilterNodeSoftware* filter = static_cast(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); +} + +void +DrawTargetD2D::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + RefPtr bitmap; + + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); + + PrepareForDrawing(rt); + + // FillOpacityMask only works if the antialias mode is MODE_ALIASED + rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + IntSize size = aMask->GetSize(); + Rect maskRect = Rect(0.f, 0.f, size.width, size.height); + bitmap = GetBitmapForSurface(aMask, maskRect); + if (!bitmap) { + return; + } + + Rect dest = Rect(aOffset.x, aOffset.y, size.width, size.height); + RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); + rt->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); + + FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), dest); +} + +void +DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) +{ + RefPtr srView = nullptr; + if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) { + return; + } + + SetScissorToRect(nullptr); + + // XXX - This function is way too long, it should be split up soon to make + // it more graspable! + + Flush(); + + AutoSaveRestoreClippedOut restoreClippedOut(this); + + if (!IsOperatorBoundByMask(aOperator)) { + restoreClippedOut.Save(); + } + + srView = static_cast(aSurface)->GetSRView(); + if (!srView) { + return; + } + + EnsureViews(); + + if (!mTempRTView) { + // This view is only needed in this path. + HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, getter_AddRefs(mTempRTView)); + + if (FAILED(hr)) { + gfxWarning() << "Failure to create RenderTargetView. Code: " << hexa(hr); + return; + } + } + + + RefPtr destRTView = mRTView; + RefPtr destTexture; + HRESULT hr; + + RefPtr maskTexture; + RefPtr maskSRView; + IntRect clipBounds; + if (mPushedClips.size()) { + EnsureClipMaskTexture(&clipBounds); + + mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, getter_AddRefs(maskSRView)); + } + + IntSize srcSurfSize; + ID3D10RenderTargetView *rtViews; + D3D10_VIEWPORT viewport; + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = mPrivateData->mVB; + + mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + mDevice->IASetInputLayout(mPrivateData->mInputLayout); + + mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); + mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); + + // If we create a downsampled source surface we need to correct aOffset for that. + Point correctedOffset = aOffset + aDest; + + // The 'practical' scaling factors. + Float dsFactorX = 1.0f; + Float dsFactorY = 1.0f; + + if (aSigma > 1.7f) { + // In this case 9 samples of our original will not cover it. Generate the + // mip levels for the original and create a downsampled version from + // them. We generate a version downsampled so that a kernel for a sigma + // of 1.7 will produce the right results. + float blurWeights[9] = { 0.234671f, 0.197389f, 0.197389f, 0.117465f, 0.117465f, 0.049456f, 0.049456f, 0.014732f, 0.014732f }; + mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); + + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + aSurface->GetSize().width, + aSurface->GetSize().height); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; + + RefPtr mipTexture; + hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mipTexture)); + + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSurface->GetSize()))) << "[D2D] 2 CreateTexture2D failure " << aSurface->GetSize() << " Code: " << hexa(hr); + return; + } + + IntSize dsSize = IntSize(int32_t(aSurface->GetSize().width * (1.7f / aSigma)), + int32_t(aSurface->GetSize().height * (1.7f / aSigma))); + + if (dsSize.width < 1) { + dsSize.width = 1; + } + if (dsSize.height < 1) { + dsSize.height = 1; + } + + dsFactorX = dsSize.width / Float(aSurface->GetSize().width); + dsFactorY = dsSize.height / Float(aSurface->GetSize().height); + correctedOffset.x *= dsFactorX; + correctedOffset.y *= dsFactorY; + + desc = CD3D10_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, + dsSize.width, + dsSize.height, 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + RefPtr tmpDSTexture; + hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpDSTexture)); + + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(dsSize))) << "[D2D] 3 CreateTexture2D failure " << dsSize << " Code: " << hexa(hr); + return; + } + + D3D10_BOX box; + box.left = box.top = box.front = 0; + box.back = 1; + box.right = aSurface->GetSize().width; + box.bottom = aSurface->GetSize().height; + mDevice->CopySubresourceRegion(mipTexture, 0, 0, 0, 0, static_cast(aSurface)->mTexture, 0, &box); + + mDevice->CreateShaderResourceView(mipTexture, nullptr, getter_AddRefs(srView)); + mDevice->GenerateMips(srView); + + RefPtr dsRTView; + RefPtr dsSRView; + mDevice->CreateRenderTargetView(tmpDSTexture, nullptr, getter_AddRefs(dsRTView)); + mDevice->CreateShaderResourceView(tmpDSTexture, nullptr, getter_AddRefs(dsSRView)); + + // We're not guaranteed the texture we created will be empty, we've + // seen old content at least on NVidia drivers. + float color[4] = { 0, 0, 0, 0 }; + mDevice->ClearRenderTargetView(dsRTView, color); + + rtViews = dsRTView; + mDevice->OMSetRenderTargets(1, &rtViews, nullptr); + + viewport.MaxDepth = 1; + viewport.MinDepth = 0; + viewport.Height = dsSize.height; + viewport.Width = dsSize.width; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + mDevice->RSSetViewports(1, &viewport); + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); + mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> + GetPassByIndex(0)->Apply(0); + + mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff); + + mDevice->Draw(4, 0); + + srcSurfSize = dsSize; + + srView = dsSRView; + } else { + // In this case generate a kernel to draw the blur directly to the temp + // surf in one direction and to final in the other. + float blurWeights[9]; + + float normalizeFactor = 1.0f; + if (aSigma != 0) { + normalizeFactor = 1.0f / Float(sqrt(2 * M_PI * pow(aSigma, 2))); + } + + blurWeights[0] = normalizeFactor; + + // XXX - We should actually optimize for Sigma = 0 here. We could use a + // much simpler shader and save a lot of texture lookups. + for (int i = 1; i < 9; i += 2) { + if (aSigma != 0) { + blurWeights[i] = blurWeights[i + 1] = normalizeFactor * + exp(-pow(float((i + 1) / 2), 2) / (2 * pow(aSigma, 2))); + } else { + blurWeights[i] = blurWeights[i + 1] = 0; + } + } + + mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); + + viewport.MaxDepth = 1; + viewport.MinDepth = 0; + viewport.Height = aSurface->GetSize().height; + viewport.Width = aSurface->GetSize().width; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + mDevice->RSSetViewports(1, &viewport); + + srcSurfSize = aSurface->GetSize(); + } + + // We may need to draw to a different intermediate surface if our temp + // texture isn't big enough. + bool needBiggerTemp = srcSurfSize.width > mSize.width || + srcSurfSize.height > mSize.height; + + RefPtr tmpRTView; + RefPtr tmpSRView; + RefPtr tmpTexture; + + IntSize tmpSurfSize = mSize; + + if (!needBiggerTemp) { + tmpRTView = mTempRTView; + tmpSRView = mSRView; + + // There could still be content here! + float color[4] = { 0, 0, 0, 0 }; + mDevice->ClearRenderTargetView(tmpRTView, color); + } else { + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + srcSurfSize.width, + srcSurfSize.height, + 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); + mDevice->CreateRenderTargetView(tmpTexture, nullptr, getter_AddRefs(tmpRTView)); + mDevice->CreateShaderResourceView(tmpTexture, nullptr, getter_AddRefs(tmpSRView)); + + tmpSurfSize = srcSurfSize; + } + + rtViews = tmpRTView; + mDevice->OMSetRenderTargets(1, &rtViews, nullptr); + + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); + + // Premultiplied! + float shadowColor[4] = { aColor.r * aColor.a, aColor.g * aColor.a, + aColor.b * aColor.a, aColor.a }; + mPrivateData->mEffect->GetVariableByName("ShadowColor")->AsVector()-> + SetFloatVector(shadowColor); + + float pixelOffset = 1.0f / float(srcSurfSize.width); + float blurOffsetsH[9] = { 0, pixelOffset, -pixelOffset, + 2.0f * pixelOffset, -2.0f * pixelOffset, + 3.0f * pixelOffset, -3.0f * pixelOffset, + 4.0f * pixelOffset, - 4.0f * pixelOffset }; + + pixelOffset = 1.0f / float(tmpSurfSize.height); + float blurOffsetsV[9] = { 0, pixelOffset, -pixelOffset, + 2.0f * pixelOffset, -2.0f * pixelOffset, + 3.0f * pixelOffset, -3.0f * pixelOffset, + 4.0f * pixelOffset, - 4.0f * pixelOffset }; + + mPrivateData->mEffect->GetVariableByName("BlurOffsetsH")-> + SetRawValue(blurOffsetsH, 0, sizeof(blurOffsetsH)); + mPrivateData->mEffect->GetVariableByName("BlurOffsetsV")-> + SetRawValue(blurOffsetsV, 0, sizeof(blurOffsetsV)); + + mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> + GetPassByIndex(0)->Apply(0); + + mDevice->Draw(4, 0); + + viewport.MaxDepth = 1; + viewport.MinDepth = 0; + viewport.Height = mSize.height; + viewport.Width = mSize.width; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + mDevice->RSSetViewports(1, &viewport); + + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(tmpSRView); + + rtViews = destRTView; + mDevice->OMSetRenderTargets(1, &rtViews, nullptr); + + Point shadowDest = aDest + aOffset; + + mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((shadowDest.x / mSize.width) * 2.0f), + 1.0f - (shadowDest.y / mSize.height * 2.0f), + (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, + (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); + mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, Float(srcSurfSize.width) / tmpSurfSize.width, + Float(srcSurfSize.height) / tmpSurfSize.height)); + + if (mPushedClips.size()) { + mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView); + mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height, + Float(aSurface->GetSize().width) / mSize.width, + Float(aSurface->GetSize().height) / mSize.height)); + mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> + GetPassByIndex(2)->Apply(0); + SetScissorToRect(&clipBounds); + } else { + mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> + GetPassByIndex(1)->Apply(0); + } + + mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); + + mDevice->Draw(4, 0); + + srView = static_cast(aSurface)->GetSRView(); + if (!srView) { + return; + } + + mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((aDest.x / mSize.width) * 2.0f), + 1.0f - (aDest.y / mSize.height * 2.0f), + (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, + (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(static_cast(aSurface)->GetSRView()); + mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); + + if (mPushedClips.size()) { + mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height, + Float(aSurface->GetSize().width) / mSize.width, + Float(aSurface->GetSize().height) / mSize.height)); + mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")-> + GetPassByIndex(0)->Apply(0); + // We've set the scissor rect here for the previous draw call. + } else { + mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> + GetPassByIndex(0)->Apply(0); + } + + mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); + + mDevice->Draw(4, 0); +} + +void +DrawTargetD2D::ClearRect(const Rect &aRect) +{ + MarkChanged(); + PushClipRect(aRect); + + PopAllClips(); + + AutoSaveRestoreClippedOut restoreClippedOut(this); + + D2D1_RECT_F clipRect; + bool isPixelAligned; + bool pushedClip = false; + if (mTransform.IsRectilinear() && + GetDeviceSpaceClipRect(clipRect, isPixelAligned)) { + if (mTransformDirty || + !mTransform.IsIdentity()) { + mRT->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + } + + mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + pushedClip = true; + } else { + FlushTransformToRT(); + restoreClippedOut.Save(); + } + + mRT->Clear(D2D1::ColorF(0, 0.0f)); + + if (pushedClip) { + mRT->PopAxisAlignedClip(); + } + + PopClip(); + return; +} + +void +DrawTargetD2D::CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) +{ + MarkChanged(); + + Rect srcRect(Float(aSourceRect.x), Float(aSourceRect.y), + Float(aSourceRect.width), Float(aSourceRect.height)); + Rect dstRect(Float(aDestination.x), Float(aDestination.y), + Float(aSourceRect.width), Float(aSourceRect.height)); + + mRT->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + mRT->PushAxisAlignedClip(D2DRect(dstRect), D2D1_ANTIALIAS_MODE_ALIASED); + mRT->Clear(D2D1::ColorF(0, 0.0f)); + mRT->PopAxisAlignedClip(); + + RefPtr bitmap = GetBitmapForSurface(aSurface, srcRect); + if (!bitmap) { + return; + } + + if (aSurface->GetFormat() == SurfaceFormat::A8) { + RefPtr brush; + mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), + D2D1::BrushProperties(), getter_AddRefs(brush)); + mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); + } else { + mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f, + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, + D2DRect(srcRect)); + } +} + +void +DrawTargetD2D::FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + if (brush) { + rt->FillRectangle(D2DRect(aRect), brush); + } + + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); +} + +void +DrawTargetD2D::StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + if (brush && strokeStyle) { + rt->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); + } + + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); +} + +void +DrawTargetD2D::StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + if (brush && strokeStyle) { + rt->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); + } + + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); +} + +void +DrawTargetD2D::Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + + const PathD2D *d2dPath = static_cast(aPath); + + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + if (brush && strokeStyle) { + rt->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); + } + + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); +} + +void +DrawTargetD2D::Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + + const PathD2D *d2dPath = static_cast(aPath); + + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + if (brush) { + rt->FillGeometry(d2dPath->mGeometry, brush); + } + + Rect bounds; + if (aOptions.mCompositionOp != CompositionOp::OP_OVER) { + D2D1_RECT_F d2dbounds; + d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds); + bounds = ToRect(d2dbounds); + } + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, bounds); +} + +void +DrawTargetD2D::FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions, + const GlyphRenderingOptions* aRenderOptions) +{ + if (aFont->GetType() != FontType::DWRITE) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; + return; + } + + ScaledFontDWrite *font = static_cast(aFont); + + IDWriteRenderingParams *params = nullptr; + if (aRenderOptions) { + if (aRenderOptions->GetType() != FontType::DWRITE) { + gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; + // This should never happen. + MOZ_ASSERT(false); + } else { + params = static_cast(aRenderOptions)->mParams; + } + } + + AntialiasMode aaMode = font->GetDefaultAAMode(); + + if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { + aaMode = aOptions.mAntialiasMode; + } + + if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && + aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR && + aaMode == AntialiasMode::SUBPIXEL) { + if (FillGlyphsManual(font, aBuffer, + static_cast(&aPattern)->mColor, + params, aOptions)) { + return; + } + } + + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); + + PrepareForDrawing(rt); + + D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + + switch (aaMode) { + case AntialiasMode::NONE: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + case AntialiasMode::GRAY: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case AntialiasMode::SUBPIXEL: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + default: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + } + + if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && + mFormat != SurfaceFormat::B8G8R8X8) { + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + } + + rt->SetTextAntialiasMode(d2dAAMode); + + if (rt != mRT || params != mTextRenderingParams) { + rt->SetTextRenderingParams(params); + if (rt == mRT) { + mTextRenderingParams = params; + } + } + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + AutoDWriteGlyphRun autoRun; + DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); + + if (brush) { + rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); + } + + FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); +} + +void +DrawTargetD2D::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions) +{ + ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aSource); + + PrepareForDrawing(rt); + + RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); + RefPtr maskBrush = CreateBrushForPattern(aMask, 1.0f); + + RefPtr layer; + + layer = GetCachedLayer(); + + rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0f, maskBrush), + layer); + + Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); + Matrix mat = mTransform; + mat.Invert(); + + rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush); + PopCachedLayer(rt); + + FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); +} + +void +DrawTargetD2D::PushClip(const Path *aPath) +{ + if (aPath->GetBackendType() != BackendType::DIRECT2D) { + gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; + return; + } + + mCurrentClipMaskTexture = nullptr; + mCurrentClippedGeometry = nullptr; + + RefPtr pathD2D = static_cast(const_cast(aPath)); + + PushedClip clip; + clip.mTransform = D2DMatrix(mTransform); + clip.mPath = pathD2D; + + pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); + + clip.mLayer = GetCachedLayer(); + + mPushedClips.push_back(clip); + + // The transform of clips is relative to the world matrix, since we use the total + // transform for the clips, make the world matrix identity. + mRT->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + PushD2DLayer(mRT, pathD2D->mGeometry, clip.mLayer, clip.mTransform); + } +} + +void +DrawTargetD2D::PushClipRect(const Rect &aRect) +{ + mCurrentClipMaskTexture = nullptr; + mCurrentClippedGeometry = nullptr; + if (!mTransform.IsRectilinear()) { + // Whoops, this isn't a rectangle in device space, Direct2D will not deal + // with this transform the way we want it to. + // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx + + RefPtr pathBuilder = CreatePathBuilder(); + pathBuilder->MoveTo(aRect.TopLeft()); + pathBuilder->LineTo(aRect.TopRight()); + pathBuilder->LineTo(aRect.BottomRight()); + pathBuilder->LineTo(aRect.BottomLeft()); + pathBuilder->Close(); + RefPtr path = pathBuilder->Finish(); + return PushClip(path); + } + + PushedClip clip; + Rect rect = mTransform.TransformBounds(aRect); + IntRect intRect; + clip.mIsPixelAligned = rect.ToIntRect(&intRect); + + // Do not store the transform, just store the device space rectangle directly. + clip.mBounds = D2DRect(rect); + + mPushedClips.push_back(clip); + + mRT->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + mRT->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } +} + +void +DrawTargetD2D::PopClip() +{ + mCurrentClipMaskTexture = nullptr; + mCurrentClippedGeometry = nullptr; + if (mClipsArePushed) { + if (mPushedClips.back().mLayer) { + PopCachedLayer(mRT); + } else { + mRT->PopAxisAlignedClip(); + } + } + mPushedClips.pop_back(); +} + +already_AddRefed +DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const +{ + RefPtr newSurf = new SourceSurfaceD2D(); + + if (!newSurf->InitFromData(aData, aSize, aStride, aFormat, mRT)) { + return nullptr; + } + + return newSurf.forget(); +} + +already_AddRefed +DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const +{ + if (aSurface->GetType() == SurfaceType::D2D1_BITMAP || + aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) { + RefPtr surface(aSurface); + return surface.forget(); + } + + RefPtr data = aSurface->GetDataSurface(); + + DataSourceSurface::MappedSurface map; + if (!data->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + RefPtr newSurf = new SourceSurfaceD2D(); + bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT); + + data->Unmap(); + + if (!success) { + return data.forget(); + } + return newSurf.forget(); +} + +already_AddRefed +DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const +{ + if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) { + gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface."; + return nullptr; + } + RefPtr newSurf = new SourceSurfaceD2D(); + + if (!newSurf->InitFromTexture(static_cast(aSurface.mSurface), + aSurface.mFormat, + mRT)) + { + gfxWarning() << *this << ": Failed to create SourceSurface from texture."; + return nullptr; + } + + return newSurf.forget(); +} + +already_AddRefed +DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const +{ + RefPtr newTarget = + new DrawTargetD2D(); + + if (!newTarget->Init(aSize, aFormat)) { + gfxDebug() << *this << ": Failed to create optimal draw target. Size: " << aSize; + return nullptr; + } + + return newTarget.forget(); +} + +already_AddRefed +DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const +{ + RefPtr path; + HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hexa(hr); + return nullptr; + } + + RefPtr sink; + hr = path->Open(getter_AddRefs(sink)); + if (FAILED(hr)) { + gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hexa(hr); + return nullptr; + } + + if (aFillRule == FillRule::FILL_WINDING) { + sink->SetFillMode(D2D1_FILL_MODE_WINDING); + } + + return MakeAndAddRef(sink, path, aFillRule, BackendType::DIRECT2D); +} + +already_AddRefed +DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const +{ + D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; + + for (uint32_t i = 0; i < aNumStops; i++) { + stops[i].position = rawStops[i].offset; + stops[i].color = D2DColor(rawStops[i].color); + } + + RefPtr stopCollection; + + HRESULT hr = + mRT->CreateGradientStopCollection(stops, aNumStops, + D2D1_GAMMA_2_2, D2DExtend(aExtendMode, Axis::BOTH), + getter_AddRefs(stopCollection)); + delete [] stops; + + if (FAILED(hr)) { + gfxWarning() << "Failed to create GradientStopCollection. Code: " << hexa(hr); + return nullptr; + } + + return MakeAndAddRef(stopCollection, Factory::GetDirect3D11Device()); +} + +already_AddRefed +DrawTargetD2D::CreateFilter(FilterType aType) +{ + RefPtr dc; + HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)getter_AddRefs(dc)); + + if (SUCCEEDED(hr)) { + return FilterNodeD2D1::Create(dc, aType); + } + return FilterNodeSoftware::Create(aType); +} + +void* +DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType) +{ + if (aType != NativeSurfaceType::D3D10_TEXTURE) { + return nullptr; + } + + return mTexture; +} + +/* + * Public functions + */ +bool +DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat) +{ + HRESULT hr; + + mSize = aSize; + mFormat = aFormat; + + if (!Factory::GetDirect3D10Device()) { + gfxCriticalError() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)"; + return false; + } + mDevice = Factory::GetDirect3D10Device(); + + CD3D10_TEXTURE2D_DESC desc(DXGIFormat(aFormat), + mSize.width, + mSize.height, + 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); + + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hexa(hr); + return false; + } + + if (!InitD2DRenderTarget()) { + return false; + } + + mRT->Clear(D2D1::ColorF(0, 0)); + return true; +} + +bool +DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) +{ + HRESULT hr; + + mTexture = aTexture; + mFormat = aFormat; + + if (!mTexture) { + gfxCriticalError() << "No valid texture for Direct2D draw target initialization."; + return false; + } + + RefPtr device; + mTexture->GetDevice(getter_AddRefs(device)); + + hr = device->QueryInterface((ID3D10Device1**)getter_AddRefs(mDevice)); + + if (FAILED(hr)) { + gfxCriticalError() << "Failed to get D3D10 device from texture." << " format " << (int)aFormat; + return false; + } + + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + mSize.width = desc.Width; + mSize.height = desc.Height; + + return InitD2DRenderTarget(); +} + +// {0D398B49-AE7B-416F-B26D-EA3C137D1CF7} +static const GUID sPrivateDataD2D = +{ 0xd398b49, 0xae7b, 0x416f, { 0xb2, 0x6d, 0xea, 0x3c, 0x13, 0x7d, 0x1c, 0xf7 } }; + +bool +DrawTargetD2D::InitD3D10Data() +{ + HRESULT hr; + + UINT privateDataSize; + privateDataSize = sizeof(mPrivateData); + hr = mDevice->GetPrivateData(sPrivateDataD2D, &privateDataSize, &mPrivateData); + + if (SUCCEEDED(hr)) { + return true; + } + + mPrivateData = new PrivateD3D10DataD2D; + + decltype(D3D10CreateEffectFromMemory)* createD3DEffect; + HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll"); + createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*) + GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory"); + + hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, getter_AddRefs(mPrivateData->mEffect)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hexa(hr); + return false; + } + + privateDataSize = sizeof(mPrivateData); + mDevice->SetPrivateData(sPrivateDataD2D, privateDataSize, &mPrivateData); + + D3D10_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + D3D10_PASS_DESC passDesc; + + mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->GetDesc(&passDesc); + + hr = mDevice->CreateInputLayout(layout, + sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), + passDesc.pIAInputSignature, + passDesc.IAInputSignatureSize, + getter_AddRefs(mPrivateData->mInputLayout)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hexa(hr); + return false; + } + + D3D10_SUBRESOURCE_DATA data; + Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; + data.pSysMem = vertices; + CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); + + hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mPrivateData->mVB)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hexa(hr); + return false; + } + + return true; +} + +/* + * Private helpers + */ +uint32_t +DrawTargetD2D::GetByteSize() const +{ + return mSize.width * mSize.height * BytesPerPixel(mFormat); +} + +already_AddRefed +DrawTargetD2D::GetCachedLayer() +{ + RefPtr layer; + + if (mCurrentCachedLayer < 5) { + if (!mCachedLayers[mCurrentCachedLayer]) { + mRT->CreateLayer(getter_AddRefs(mCachedLayers[mCurrentCachedLayer])); + mVRAMUsageDT += GetByteSize(); + } + layer = mCachedLayers[mCurrentCachedLayer]; + } else { + mRT->CreateLayer(getter_AddRefs(layer)); + } + + mCurrentCachedLayer++; + return layer.forget(); +} + +void +DrawTargetD2D::PopCachedLayer(ID2D1RenderTarget *aRT) +{ + aRT->PopLayer(); + mCurrentCachedLayer--; +} + +bool +DrawTargetD2D::InitD2DRenderTarget() +{ + if (!factory()) { + gfxCriticalError() << "No valid D2D factory available."; + return false; + } + + mRT = CreateRTForTexture(mTexture, mFormat); + + if (!mRT) { + return false; + } + + mRT->BeginDraw(); + + if (mFormat == SurfaceFormat::B8G8R8X8) { + mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); + } + + mVRAMUsageDT += GetByteSize(); + + return InitD3D10Data(); +} + +void +DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT) +{ + if (!mClipsArePushed || aRT == mTempRT) { + if (mPushedClips.size()) { + // The transform of clips is relative to the world matrix, since we use the total + // transform for the clips, make the world matrix identity. + aRT->SetTransform(D2D1::IdentityMatrix()); + if (aRT == mRT) { + mTransformDirty = true; + mClipsArePushed = true; + } + PushClipsToRT(aRT); + } + } + FlushTransformToRT(); + MarkChanged(); + + if (aRT == mTempRT) { + mTempRT->SetTransform(D2DMatrix(mTransform)); + } +} + +void +DrawTargetD2D::MarkChanged() +{ + if (mSnapshot) { + if (mSnapshot->hasOneRef()) { + // Just destroy it, since no-one else knows about it. + mSnapshot = nullptr; + } else { + mSnapshot->DrawTargetWillChange(); + // The snapshot will no longer depend on this target. + MOZ_ASSERT(!mSnapshot); + } + } + if (mDependentTargets.size()) { + // Copy mDependentTargets since the Flush()es below will modify it. + TargetSet tmpTargets = mDependentTargets; + for (TargetSet::iterator iter = tmpTargets.begin(); + iter != tmpTargets.end(); iter++) { + (*iter)->Flush(); + } + // The Flush() should have broken all dependencies on this target. + MOZ_ASSERT(!mDependentTargets.size()); + } +} + +ID3D10BlendState* +DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) +{ + size_t operatorIndex = static_cast(aOperator); + if (mPrivateData->mBlendStates[operatorIndex]) { + return mPrivateData->mBlendStates[operatorIndex]; + } + + D3D10_BLEND_DESC desc; + + memset(&desc, 0, sizeof(D3D10_BLEND_DESC)); + + desc.AlphaToCoverageEnable = FALSE; + desc.BlendEnable[0] = TRUE; + desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + + switch (aOperator) { + case CompositionOp::OP_ADD: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + break; + case CompositionOp::OP_IN: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + break; + case CompositionOp::OP_OUT: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + break; + case CompositionOp::OP_ATOP: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + break; + case CompositionOp::OP_DEST_IN: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + break; + case CompositionOp::OP_DEST_OUT: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + break; + case CompositionOp::OP_DEST_ATOP: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + break; + case CompositionOp::OP_DEST_OVER: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + break; + case CompositionOp::OP_XOR: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + break; + case CompositionOp::OP_SOURCE: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + break; + default: + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + } + + mDevice->CreateBlendState(&desc, getter_AddRefs(mPrivateData->mBlendStates[operatorIndex])); + + return mPrivateData->mBlendStates[operatorIndex]; +} + +/* This function prepares the temporary RT for drawing and returns it when a + * drawing operation other than OVER is required. + */ +ID2D1RenderTarget* +DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern) +{ + if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { + return mRT; + } + + PopAllClips(); + + if (aOperator > CompositionOp::OP_XOR) { + mRT->Flush(); + } + + if (mTempRT) { + mTempRT->Clear(D2D1::ColorF(0, 0)); + return mTempRT; + } + + EnsureViews(); + + if (!mRTView || !mSRView) { + gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER."; + return mRT; + } + + mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8); + + if (!mTempRT) { + return mRT; + } + + mVRAMUsageDT += GetByteSize(); + + mTempRT->BeginDraw(); + + mTempRT->Clear(D2D1::ColorF(0, 0)); + + return mTempRT; +} + +/* This function blends back the content of a drawing operation (drawn to an + * empty surface with OVER, so the surface now contains the source operation + * contents) to the rendertarget using the requested composition operation. + * In order to respect clip for operations which are unbound by their mask, + * the old content of the surface outside the clipped area may be blended back + * to the surface. + */ +void +DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds) +{ + if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { + return; + } + + if (!mTempRT) { + return; + } + + PopClipsFromRT(mTempRT); + + mRT->Flush(); + mTempRT->Flush(); + + AutoSaveRestoreClippedOut restoreClippedOut(this); + + bool needsWriteBack = + !IsOperatorBoundByMask(aOperator) && mPushedClips.size(); + + if (needsWriteBack) { + restoreClippedOut.Save(); + } + + ID3D10RenderTargetView *rtViews = mRTView; + mDevice->OMSetRenderTargets(1, &rtViews, nullptr); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = mPrivateData->mVB; + + mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + mDevice->IASetInputLayout(mPrivateData->mInputLayout); + + D3D10_VIEWPORT viewport; + viewport.MaxDepth = 1; + viewport.MinDepth = 0; + viewport.Height = mSize.height; + viewport.Width = mSize.width; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + RefPtr tmpTexture; + RefPtr mBckSRView; + + mDevice->RSSetViewports(1, &viewport); + mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); + + if (IsPatternSupportedByD2D(aPattern)) { + mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView); + + // Handle the case where we blend with the backdrop + if (aOperator > CompositionOp::OP_XOR) { + IntSize size = mSize; + SurfaceFormat format = mFormat; + + CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(tmpTexture)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create temporary texture to hold surface data."; + return; + } + + mDevice->CopyResource(tmpTexture, mTexture); + if (FAILED(hr)) { + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); + return; + } + + DrawTargetD2D::Flush(); + + hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, getter_AddRefs(mBckSRView)); + + if (FAILED(hr)) { + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); + return; + } + + unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR; + mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView); + mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop); + + if (aOperator > CompositionOp::OP_EXCLUSION) + mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")-> + GetPassByIndex(0)->Apply(0); + else if (aOperator > CompositionOp::OP_COLOR_DODGE) + mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")-> + GetPassByIndex(0)->Apply(0); + else + mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_1")-> + GetPassByIndex(0)->Apply(0); + } + else { + mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0); + } + + } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { + const RadialGradientPattern *pat = static_cast(&aPattern); + + if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) { + // Draw nothing! + return; + } + + mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(mSRView); + + SetupEffectForRadialGradient(pat); + } + + mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); + + SetScissorToRect(nullptr); + mDevice->Draw(4, 0); +} + +static D2D1_RECT_F +IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) +{ + D2D1_RECT_F result; + result.left = max(aRect1.left, aRect2.left); + result.top = max(aRect1.top, aRect2.top); + result.right = min(aRect1.right, aRect2.right); + result.bottom = min(aRect1.bottom, aRect2.bottom); + + result.right = max(result.right, result.left); + result.bottom = max(result.bottom, result.top); + + return result; +} + +bool +DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) +{ + if (!mPushedClips.size()) { + return false; + } + + std::vector::iterator iter = mPushedClips.begin(); + if (iter->mPath) { + return false; + } + aClipRect = iter->mBounds; + aIsPixelAligned = iter->mIsPixelAligned; + + iter++; + for (;iter != mPushedClips.end(); iter++) { + if (iter->mPath) { + return false; + } + aClipRect = IntersectRect(aClipRect, iter->mBounds); + if (!iter->mIsPixelAligned) { + aIsPixelAligned = false; + } + } + return true; +} + +already_AddRefed +DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds) +{ + if (mCurrentClippedGeometry) { + *aClipBounds = mCurrentClipBounds; + RefPtr clippedGeometry(mCurrentClippedGeometry); + return clippedGeometry.forget(); + } + + mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize); + + // if pathGeom is null then pathRect represents the path. + RefPtr pathGeom; + D2D1_RECT_F pathRect; + bool pathRectIsAxisAligned = false; + std::vector::iterator iter = mPushedClips.begin(); + + if (iter->mPath) { + pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); + } else { + pathRect = iter->mBounds; + pathRectIsAxisAligned = iter->mIsPixelAligned; + } + + iter++; + for (;iter != mPushedClips.end(); iter++) { + // Do nothing but add it to the current clip bounds. + if (!iter->mPath && iter->mIsPixelAligned) { + mCurrentClipBounds.IntersectRect(mCurrentClipBounds, + IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top), + int32_t(iter->mBounds.right - iter->mBounds.left), + int32_t(iter->mBounds.bottom - iter->mBounds.top))); + continue; + } + + if (!pathGeom) { + if (pathRectIsAxisAligned) { + mCurrentClipBounds.IntersectRect(mCurrentClipBounds, + IntRect(int32_t(pathRect.left), int32_t(pathRect.top), + int32_t(pathRect.right - pathRect.left), + int32_t(pathRect.bottom - pathRect.top))); + } + if (iter->mPath) { + // See if pathRect needs to go into the path geometry. + if (!pathRectIsAxisAligned) { + pathGeom = ConvertRectToGeometry(pathRect); + } else { + pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); + } + } else { + pathRect = IntersectRect(pathRect, iter->mBounds); + pathRectIsAxisAligned = false; + continue; + } + } + + RefPtr newGeom; + factory()->CreatePathGeometry(getter_AddRefs(newGeom)); + + RefPtr currentSink; + newGeom->Open(getter_AddRefs(currentSink)); + + if (iter->mPath) { + pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT, + iter->mTransform, currentSink); + } else { + RefPtr rectGeom = ConvertRectToGeometry(iter->mBounds); + pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT, + D2D1::IdentityMatrix(), currentSink); + } + + currentSink->Close(); + + pathGeom = newGeom.forget(); + } + + // For now we need mCurrentClippedGeometry to always be non-nullptr. This + // method might seem a little strange but it is just fine, if pathGeom is + // nullptr pathRect will always still contain 1 clip unaccounted for + // regardless of mCurrentClipBounds. + if (!pathGeom) { + pathGeom = ConvertRectToGeometry(pathRect); + } + mCurrentClippedGeometry = pathGeom.forget(); + *aClipBounds = mCurrentClipBounds; + RefPtr clippedGeometry(mCurrentClippedGeometry); + return clippedGeometry.forget(); +} + +already_AddRefed +DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) +{ + HRESULT hr; + + RefPtr surface; + RefPtr rt; + + hr = aTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surface)); + + if (FAILED(hr)) { + gfxCriticalError() << "Failed to QI texture to surface. Code: " << hexa(hr); + return nullptr; + } + + D3D10_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + + D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + + if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) { + alphaMode = D2D1_ALPHA_MODE_IGNORE; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode)); + hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, getter_AddRefs(rt)); + + if (FAILED(hr)) { + gfxCriticalError() << "Failed to create D2D render target for texture. Code: " + << hexa(hr) << " " << mSize << " Format: " << uint32_t(aFormat); + return nullptr; + } + + return rt.forget(); +} + +void +DrawTargetD2D::EnsureViews() +{ + if (mTempTexture && mSRView && mRTView) { + return; + } + + HRESULT hr; + + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + mSize.width, + mSize.height, + 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTempTexture)); + + if (FAILED(hr)) { + gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: " + << mSize << " Code: " << hexa(hr); + return; + } + + hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, getter_AddRefs(mSRView)); + + if (FAILED(hr)) { + gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hexa(hr); + return; + } + + hr = mDevice->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(mRTView)); + + if (FAILED(hr)) { + gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hexa(hr); + } +} + +void +DrawTargetD2D::PopAllClips() +{ + if (mClipsArePushed) { + PopClipsFromRT(mRT); + + mClipsArePushed = false; + } +} + +void +DrawTargetD2D::PushClipsToRT(ID2D1RenderTarget *aRT) +{ + for (std::vector::iterator iter = mPushedClips.begin(); + iter != mPushedClips.end(); iter++) { + if (iter->mLayer) { + PushD2DLayer(aRT, iter->mPath->mGeometry, iter->mLayer, iter->mTransform); + } else { + aRT->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + } +} + +void +DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT) +{ + for (int i = mPushedClips.size() - 1; i >= 0; i--) { + if (mPushedClips[i].mLayer) { + aRT->PopLayer(); + } else { + aRT->PopAxisAlignedClip(); + } + } +} + +void +DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds) +{ + if (mCurrentClipMaskTexture || mPushedClips.empty()) { + *aBounds = mCurrentClipBounds; + return; + } + + RefPtr geometry = GetClippedGeometry(aBounds); + + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM, + mSize.width, + mSize.height, + 1, 1); + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mCurrentClipMaskTexture)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create texture for ClipMask!"; + return; + } + + RefPtr rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8); + + if (!rt) { + gfxWarning() << "Failed to create RT for ClipMask!"; + return; + } + + RefPtr brush; + rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush)); + + rt->BeginDraw(); + rt->Clear(D2D1::ColorF(0, 0)); + rt->FillGeometry(geometry, brush); + rt->EndDraw(); +} + +bool +DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont, + const GlyphBuffer &aBuffer, + const Color &aColor, + IDWriteRenderingParams *aParams, + const DrawOptions &aOptions) +{ + HRESULT hr; + + RefPtr params; + + if (aParams) { + params = aParams; + } else { + mRT->GetTextRenderingParams(getter_AddRefs(params)); + } + + DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; + if (params) { + hr = aFont->mFontFace->GetRecommendedRenderingMode( + (FLOAT)aFont->GetSize(), + 1.0f, + DWRITE_MEASURING_MODE_NATURAL, + params, + &renderMode); + if (FAILED(hr)) { + // this probably never happens, but let's play it safe + renderMode = DWRITE_RENDERING_MODE_DEFAULT; + } + } + + // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. + switch (renderMode) { + case DWRITE_RENDERING_MODE_ALIASED: + // ClearType texture creation will fail in this mode, so bail out + return false; + case DWRITE_RENDERING_MODE_DEFAULT: + // As per DWRITE_RENDERING_MODE documentation, pick Natural for font + // sizes under 16 ppem + if (aFont->GetSize() < 16.0f) { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + } else { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + } + break; + case DWRITE_RENDERING_MODE_OUTLINE: + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + break; + default: + break; + } + + DWRITE_MEASURING_MODE measureMode = + renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : + renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : + DWRITE_MEASURING_MODE_NATURAL; + + DWRITE_MATRIX mat = DWriteMatrixFromMatrix(mTransform); + + AutoDWriteGlyphRun autoRun; + DWriteGlyphRunFromGlyphs(aBuffer, aFont, &autoRun); + + RefPtr analysis; + hr = GetDWriteFactory()->CreateGlyphRunAnalysis(&autoRun, 1.0f, &mat, + renderMode, measureMode, 0, 0, getter_AddRefs(analysis)); + + if (FAILED(hr)) { + return false; + } + + RECT bounds; + hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds); + + if (bounds.bottom <= bounds.top || bounds.right <= bounds.left) { + // DWrite seems to do this sometimes. I'm not 100% sure why. See bug 758980. + gfxDebug() << "Empty alpha texture bounds! Falling back to regular drawing."; + return false; + } + IntRect rectBounds(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top); + IntRect surfBounds(IntPoint(0, 0), mSize); + + rectBounds.IntersectRect(rectBounds, surfBounds); + + if (rectBounds.IsEmpty()) { + // Nothing to do. + return true; + } + + RefPtr tex = CreateTextureForAnalysis(analysis, rectBounds); + + if (!tex) { + return false; + } + + RefPtr srView; + hr = mDevice->CreateShaderResourceView(tex, nullptr, getter_AddRefs(srView)); + + if (FAILED(hr)) { + return false; + } + + MarkChanged(); + + // Prepare our background texture for drawing. + PopAllClips(); + mRT->Flush(); + + SetupStateForRendering(); + + ID3D10EffectTechnique *technique = mPrivateData->mEffect->GetTechniqueByName("SampleTextTexture"); + + mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((Float(rectBounds.x) / mSize.width) * 2.0f), + 1.0f - (Float(rectBounds.y) / mSize.height * 2.0f), + (Float(rectBounds.width) / mSize.width) * 2.0f, + (-Float(rectBounds.height) / mSize.height) * 2.0f)); + mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); + FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a }; + mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()-> + SetFloatVector(color); + + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); + + bool isMasking = false; + + IntRect clipBoundsStorage; + IntRect *clipBounds = nullptr; + + if (!mPushedClips.empty()) { + clipBounds = &clipBoundsStorage; + RefPtr geom = GetClippedGeometry(clipBounds); + + RefPtr rectGeom; + factory()->CreateRectangleGeometry(D2D1::RectF(Float(rectBounds.x), + Float(rectBounds.y), + Float(rectBounds.width + rectBounds.x), + Float(rectBounds.height + rectBounds.y)), + getter_AddRefs(rectGeom)); + + D2D1_GEOMETRY_RELATION relation; + if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) || + relation != D2D1_GEOMETRY_RELATION_CONTAINS ) { + isMasking = true; + } + } + + if (isMasking) { + clipBounds = &clipBoundsStorage; + EnsureClipMaskTexture(clipBounds); + + RefPtr srViewMask; + hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, getter_AddRefs(srViewMask)); + + if (FAILED(hr)) { + return false; + } + + mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(srViewMask); + + mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(Float(rectBounds.x) / mSize.width, Float(rectBounds.y) / mSize.height, + Float(rectBounds.width) / mSize.width, Float(rectBounds.height) / mSize.height)); + + technique->GetPassByIndex(1)->Apply(0); + } else { + technique->GetPassByIndex(0)->Apply(0); + } + + RefPtr rtView; + ID3D10RenderTargetView *rtViews; + mDevice->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(rtView)); + + rtViews = rtView; + mDevice->OMSetRenderTargets(1, &rtViews, nullptr); + SetScissorToRect(clipBounds); + mDevice->Draw(4, 0); + return true; +} + +already_AddRefed +DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) +{ + if (!IsPatternSupportedByD2D(aPattern)) { + RefPtr colBrush; + mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), getter_AddRefs(colBrush)); + return colBrush.forget(); + } + + if (aPattern.GetType() == PatternType::COLOR) { + RefPtr colBrush; + Color color = static_cast(&aPattern)->mColor; + mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, + color.b, color.a), + D2D1::BrushProperties(aAlpha), + getter_AddRefs(colBrush)); + return colBrush.forget(); + } + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { + RefPtr gradBrush; + const LinearGradientPattern *pat = + static_cast(&aPattern); + + GradientStopsD2D *stops = static_cast(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return nullptr; + } + + if (pat->mBegin == pat->mEnd) { + RefPtr colBrush; + uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); + vector d2dStops(stopCount); + stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); + mRT->CreateSolidColorBrush(d2dStops.back().color, + D2D1::BrushProperties(aAlpha), + getter_AddRefs(colBrush)); + return colBrush.forget(); + } + + mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), + D2DPoint(pat->mEnd)), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + getter_AddRefs(gradBrush)); + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { + RefPtr gradBrush; + const RadialGradientPattern *pat = + static_cast(&aPattern); + + GradientStopsD2D *stops = static_cast(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return nullptr; + } + + // This will not be a complex radial gradient brush. + mRT->CreateRadialGradientBrush( + D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), + D2DPoint(pat->mCenter1 - pat->mCenter2), + pat->mRadius2, pat->mRadius2), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + getter_AddRefs(gradBrush)); + + return gradBrush.forget(); + } + if (aPattern.GetType() == PatternType::SURFACE) { + RefPtr bmBrush; + const SurfacePattern *pat = + static_cast(&aPattern); + + if (!pat->mSurface) { + gfxDebug() << "No source surface specified for surface pattern"; + return nullptr; + } + + RefPtr bitmap; + + Matrix mat = pat->mMatrix; + + RefPtr source = pat->mSurface; + + if (!pat->mSamplingRect.IsEmpty() && + (source->GetType() == SurfaceType::D2D1_BITMAP || + source->GetType() == SurfaceType::D2D1_DRAWTARGET)) { + IntRect samplingRect = pat->mSamplingRect; + + RefPtr dt = new DrawTargetD2D(); + if (!dt->Init(samplingRect.Size(), + source->GetFormat())) { + // FIXME: Uncomment assertion, bug 1068195 + // MOZ_ASSERT(false, "Invalid sampling rect size!"); + return nullptr; + } + + dt->CopySurface(source, samplingRect, IntPoint()); + source = dt->Snapshot(); + + mat.PreTranslate(samplingRect.x, samplingRect.y); + } + + switch (source->GetType()) { + case SurfaceType::D2D1_BITMAP: + { + SourceSurfaceD2D *surf = static_cast(source.get()); + + bitmap = surf->mBitmap; + + if (!bitmap) { + return nullptr; + } + } + break; + case SurfaceType::D2D1_DRAWTARGET: + { + SourceSurfaceD2DTarget *surf = + static_cast(source.get()); + bitmap = surf->GetBitmap(mRT); + AddDependencyOnSource(surf); + } + break; + default: + { + RefPtr dataSurf = source->GetDataSurface(); + if (!dataSurf) { + gfxWarning() << "Invalid surface type."; + return nullptr; + } + + IntRect sourceRect = pat->mSamplingRect; + if (sourceRect.IsEmpty()) { + sourceRect = IntRect(0, 0, source->GetSize().width, source->GetSize().height); + } + + bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT, &sourceRect); + if (!bitmap) { + RefPtr colBrush; + mRT->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(colBrush)); + return colBrush.forget(); + } + } + break; + } + + D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS); + D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS); + HRESULT hr = mRT->CreateBitmapBrush(bitmap, + D2D1::BitmapBrushProperties(xRepeat, + yRepeat, + D2DFilter(pat->mFilter)), + D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), + getter_AddRefs(bmBrush)); + if (FAILED(hr)) { + gfxCriticalError() << "[D2D] 1CreateBitmapBrush failure: " << hexa(hr); + return nullptr; + } + return bmBrush.forget(); + } + + gfxWarning() << "Invalid pattern type detected."; + return nullptr; +} + +already_AddRefed +DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops) +{ + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 4096, 1, 1, 1); + + std::vector rawStops; + rawStops.resize(aStops->mStopCollection->GetGradientStopCount()); + aStops->mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size()); + + std::vector textureData; + textureData.resize(4096 * 4); + unsigned char *texData = &textureData.front(); + + float prevColorPos = 0; + float nextColorPos = 1.0f; + D2D1_COLOR_F prevColor = rawStops[0].color; + D2D1_COLOR_F nextColor = prevColor; + + if (rawStops.size() >= 2) { + nextColor = rawStops[1].color; + nextColorPos = rawStops[1].position; + } + + uint32_t stopPosition = 2; + + // Not the most optimized way but this will do for now. + for (int i = 0; i < 4096; i++) { + // The 4095 seems a little counter intuitive, but we want the gradient + // color at offset 0 at the first pixel, and at offset 1.0f at the last + // pixel. + float pos = float(i) / 4095; + + while (pos > nextColorPos) { + prevColor = nextColor; + prevColorPos = nextColorPos; + if (rawStops.size() > stopPosition) { + nextColor = rawStops[stopPosition].color; + nextColorPos = rawStops[stopPosition++].position; + } else { + nextColorPos = 1.0f; + } + } + + float interp; + + if (nextColorPos != prevColorPos) { + interp = (pos - prevColorPos) / (nextColorPos - prevColorPos); + } else { + interp = 0; + } + + Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp, + prevColor.g + (nextColor.g - prevColor.g) * interp, + prevColor.b + (nextColor.b - prevColor.b) * interp, + prevColor.a + (nextColor.a - prevColor.a) * interp); + + texData[i * 4] = (char)(255.0f * newColor.b); + texData[i * 4 + 1] = (char)(255.0f * newColor.g); + texData[i * 4 + 2] = (char)(255.0f * newColor.r); + texData[i * 4 + 3] = (char)(255.0f * newColor.a); + } + + D3D10_SUBRESOURCE_DATA data; + data.pSysMem = &textureData.front(); + data.SysMemPitch = 4096 * 4; + + RefPtr tex; + mDevice->CreateTexture2D(&desc, &data, getter_AddRefs(tex)); + + return tex.forget(); +} + +already_AddRefed +DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds) +{ + HRESULT hr; + + uint32_t bufferSize = aBounds.width * aBounds.height * 3; + + RECT bounds; + bounds.left = aBounds.x; + bounds.top = aBounds.y; + bounds.right = aBounds.x + aBounds.width; + bounds.bottom = aBounds.y + aBounds.height; + + // Add one byte so we can safely read a 32-bit int when copying the last + // 3 bytes. + BYTE *texture = new BYTE[bufferSize + 1]; + hr = aAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize); + + if (FAILED(hr)) { + delete [] texture; + return nullptr; + } + + int alignedBufferSize = aBounds.width * aBounds.height * 4; + + // Create a one-off immutable texture from system memory. + BYTE *alignedTextureData = new BYTE[alignedBufferSize]; + for (int y = 0; y < aBounds.height; y++) { + for (int x = 0; x < aBounds.width; x++) { + // Copy 3 Bpp source to 4 Bpp destination memory used for + // texture creation. D3D10 has no 3 Bpp texture format we can + // use. + // + // Since we don't care what ends up in the alpha pixel of the + // destination, therefor we can simply copy a normal 32 bit + // integer each time, filling the alpha pixel of the destination + // with the first subpixel of the next pixel from the source. + *((int*)(alignedTextureData + (y * aBounds.width + x) * 4)) = + *((int*)(texture + (y * aBounds.width + x) * 3)); + } + } + + D3D10_SUBRESOURCE_DATA data; + + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + aBounds.width, aBounds.height, + 1, 1); + desc.Usage = D3D10_USAGE_IMMUTABLE; + + data.SysMemPitch = aBounds.width * 4; + data.pSysMem = alignedTextureData; + + RefPtr tex; + hr = mDevice->CreateTexture2D(&desc, &data, getter_AddRefs(tex)); + + delete [] alignedTextureData; + delete [] texture; + + if (FAILED(hr)) { + return nullptr; + } + + return tex.forget(); +} + +void +DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern) +{ + mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0); + mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> + SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); + + float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 }; + mPrivateData->mEffect->GetVariableByName("dimensions")->AsVector()-> + SetFloatVector(dimensions); + + const GradientStopsD2D *stops = + static_cast(aPattern->mStops.get()); + + RefPtr tex = CreateGradientTexture(stops); + + RefPtr srView; + mDevice->CreateShaderResourceView(tex, nullptr, getter_AddRefs(srView)); + + mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); + + Point dc = aPattern->mCenter2 - aPattern->mCenter1; + float dr = aPattern->mRadius2 - aPattern->mRadius1; + + float diffv[] = { dc.x, dc.y, dr, 0 }; + mPrivateData->mEffect->GetVariableByName("diff")->AsVector()-> + SetFloatVector(diffv); + + float center1[] = { aPattern->mCenter1.x, aPattern->mCenter1.y, dr, 0 }; + mPrivateData->mEffect->GetVariableByName("center1")->AsVector()-> + SetFloatVector(center1); + + mPrivateData->mEffect->GetVariableByName("radius1")->AsScalar()-> + SetFloat(aPattern->mRadius1); + mPrivateData->mEffect->GetVariableByName("sq_radius1")->AsScalar()-> + SetFloat(pow(aPattern->mRadius1, 2)); + + Matrix invTransform = mTransform; + + if (!invTransform.Invert()) { + // Bail if the matrix is singular. + return; + } + float matrix[] = { invTransform._11, invTransform._12, 0, 0, + invTransform._21, invTransform._22, 0, 0, + invTransform._31, invTransform._32, 1.0f, 0, + 0, 0, 0, 1.0f }; + + mPrivateData->mEffect->GetVariableByName("DeviceSpaceToUserSpace")-> + AsMatrix()->SetMatrix(matrix); + + float A = dc.x * dc.x + dc.y * dc.y - dr * dr; + + uint32_t offset = 0; + switch (stops->mStopCollection->GetExtendMode()) { + case D2D1_EXTEND_MODE_WRAP: + offset = 1; + break; + case D2D1_EXTEND_MODE_MIRROR: + offset = 2; + break; + default: + gfxWarning() << "This shouldn't happen! Invalid extend mode for gradient stops."; + } + + if (A == 0) { + mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> + GetPassByIndex(offset * 2 + 1)->Apply(0); + } else { + mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A); + mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> + GetPassByIndex(offset * 2)->Apply(0); + } +} + +void +DrawTargetD2D::SetupStateForRendering() +{ + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = mPrivateData->mVB; + + mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + mDevice->IASetInputLayout(mPrivateData->mInputLayout); + + D3D10_VIEWPORT viewport; + viewport.MaxDepth = 1; + viewport.MinDepth = 0; + viewport.Height = mSize.height; + viewport.Width = mSize.width; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + mDevice->RSSetViewports(1, &viewport); +} + +ID2D1Factory* +DrawTargetD2D::factory() +{ + if (mFactory) { + return mFactory; + } + + D2D1CreateFactoryFunc createD2DFactory; + HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(d2dModule, "D2D1CreateFactory"); + + if (!createD2DFactory) { + gfxWarning() << "Failed to locate D2D1CreateFactory function."; + return nullptr; + } + + D2D1_FACTORY_OPTIONS options; +#ifdef _DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; + + HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&mFactory); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create Direct2D factory."; + } + + RefPtr factoryD2D1; + hr = mFactory->QueryInterface((ID2D1Factory1**)getter_AddRefs(factoryD2D1)); + if (SUCCEEDED(hr)) { + ExtendInputEffectD2D1::Register(factoryD2D1); + } + + return mFactory; +} + +void +DrawTargetD2D::CleanupD2D() +{ + if (mFactory) { + RefPtr factoryD2D1; + HRESULT hr = mFactory->QueryInterface((ID2D1Factory1**)getter_AddRefs(factoryD2D1)); + if (SUCCEEDED(hr)) { + ExtendInputEffectD2D1::Unregister(factoryD2D1); + } + + mFactory->Release(); + mFactory = nullptr; + } +} + +IDWriteFactory* +DrawTargetD2D::GetDWriteFactory() +{ + if (mDWriteFactory) { + return mDWriteFactory; + } + + decltype(DWriteCreateFactory)* createDWriteFactory; + HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); + createDWriteFactory = (decltype(DWriteCreateFactory)*) + GetProcAddress(dwriteModule, "DWriteCreateFactory"); + + if (!createDWriteFactory) { + gfxWarning() << "Failed to locate DWriteCreateFactory function."; + return nullptr; + } + + HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast(&mDWriteFactory)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create DWrite Factory."; + } + + return mDWriteFactory; +} + +void +DrawTargetD2D::SetScissorToRect(IntRect *aRect) +{ + D3D10_RECT rect; + if (aRect) { + rect.left = aRect->x; + rect.right = aRect->XMost(); + rect.top = aRect->y; + rect.bottom = aRect->YMost(); + } else { + rect.left = rect.top = INT32_MIN; + rect.right = rect.bottom = INT32_MAX; + } + + mDevice->RSSetScissorRects(1, &rect); +} + +void +DrawTargetD2D::PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform) +{ + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE; + + if (aRT->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { + options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; + options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + RefPtr dc; + HRESULT hr = aRT->QueryInterface(IID_ID2D1DeviceContext, (void**)((ID2D1DeviceContext**)getter_AddRefs(dc))); + + if (FAILED(hr)) { + aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), aGeometry, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, + 1.0, nullptr, options), + aLayer); + } else { + dc->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, + 1.0, nullptr, options1), + aLayer); + } +} + +} +} diff --git a/gfx/2d/DrawTargetD2D.h b/gfx/2d/DrawTargetD2D.h new file mode 100644 index 00000000000..7c0a5b403e8 --- /dev/null +++ b/gfx/2d/DrawTargetD2D.h @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DRAWTARGETD2D_H_ +#define MOZILLA_GFX_DRAWTARGETD2D_H_ + +#include "2D.h" +#include "PathD2D.h" +#include +#include "HelpersD2D.h" + +#include +#include + +#include + +struct IDWriteFactory; + +namespace mozilla { +namespace gfx { + +class SourceSurfaceD2DTarget; +class SourceSurfaceD2D; +class GradientStopsD2D; +class ScaledFontDWrite; + +const int32_t kLayerCacheSize = 5; + +struct PrivateD3D10DataD2D +{ + RefPtr mEffect; + RefPtr mInputLayout; + RefPtr mVB; + RefPtr mBlendStates[size_t(CompositionOp::OP_COUNT)]; +}; + +class DrawTargetD2D : public DrawTarget +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D, override) + DrawTargetD2D(); + virtual ~DrawTargetD2D(); + + virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; } + virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D; } + virtual already_AddRefed Snapshot() override; + virtual IntSize GetSize() override { return mSize; } + + virtual void Flush() override; + virtual void DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) override; + virtual void ClearRect(const Rect &aRect) override; + virtual void MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions = DrawOptions()) override; + + + virtual void CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) override; + + virtual void FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()) override; + virtual void Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions(), + const GlyphRenderingOptions *aRenderingOptions = nullptr) override; + virtual void Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions = DrawOptions()) override; + virtual void PushClip(const Path *aPath) override; + virtual void PushClipRect(const Rect &aRect) override; + virtual void PopClip() override; + + virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const override; + virtual already_AddRefed OptimizeSourceSurface(SourceSurface *aSurface) const override; + + virtual already_AddRefed + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override; + + virtual already_AddRefed + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override; + + virtual already_AddRefed CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; + + virtual already_AddRefed + CreateGradientStops(GradientStop *aStops, + uint32_t aNumStops, + ExtendMode aExtendMode = ExtendMode::CLAMP) const override; + + virtual already_AddRefed CreateFilter(FilterType aType) override; + + virtual bool SupportsRegionClipping() const override { return false; } + + virtual void *GetNativeSurface(NativeSurfaceType aType) override; + + bool Init(const IntSize &aSize, SurfaceFormat aFormat); + bool Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); + bool InitD3D10Data(); + uint32_t GetByteSize() const; + already_AddRefed GetCachedLayer(); + void PopCachedLayer(ID2D1RenderTarget *aRT); + + already_AddRefed GetImageForSurface(SourceSurface *aSurface); + + static ID2D1Factory *factory(); + static void CleanupD2D(); + static IDWriteFactory *GetDWriteFactory(); + ID2D1RenderTarget *GetRT() { return mRT; } + + static uint32_t GetMaxSurfaceSize() { + return D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + } + + operator std::string() const { + std::stringstream stream; + stream << "DrawTargetD2D(" << this << ")"; + return stream.str(); + } + + static uint64_t mVRAMUsageDT; + static uint64_t mVRAMUsageSS; + +private: + already_AddRefed + GetBitmapForSurface(SourceSurface *aSurface, + Rect &aSource); + friend class AutoSaveRestoreClippedOut; + friend class SourceSurfaceD2DTarget; + + typedef std::unordered_set TargetSet; + + bool InitD2DRenderTarget(); + void PrepareForDrawing(ID2D1RenderTarget *aRT); + + // This function will mark the surface as changing, and make sure any + // copy-on-write snapshots are notified. + void MarkChanged(); + void FlushTransformToRT() { + if (mTransformDirty) { + mRT->SetTransform(D2DMatrix(mTransform)); + mTransformDirty = false; + } + } + void AddDependencyOnSource(SourceSurfaceD2DTarget* aSource); + + ID3D10BlendState *GetBlendStateForOperator(CompositionOp aOperator); + ID2D1RenderTarget *GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern); + void FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds); void EnsureViews(); + void PopAllClips(); + void PushClipsToRT(ID2D1RenderTarget *aRT); + void PopClipsFromRT(ID2D1RenderTarget *aRT); + + // This function ensures mCurrentClipMaskTexture contains a texture containing + // a mask corresponding with the current DrawTarget clip. See + // GetClippedGeometry for a description of aClipBounds. + void EnsureClipMaskTexture(IntRect *aClipBounds); + + bool FillGlyphsManual(ScaledFontDWrite *aFont, + const GlyphBuffer &aBuffer, + const Color &aColor, + IDWriteRenderingParams *aParams, + const DrawOptions &aOptions = DrawOptions()); + + already_AddRefed CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); + + // This returns the clipped geometry, in addition it returns aClipBounds which + // represents the intersection of all pixel-aligned rectangular clips that + // are currently set. The returned clipped geometry must be clipped by these + // bounds to correctly reflect the total clip. This is in device space. + already_AddRefed GetClippedGeometry(IntRect *aClipBounds); + + bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned); + + already_AddRefed CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f); + + already_AddRefed CreateGradientTexture(const GradientStopsD2D *aStops); + already_AddRefed CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds); + + void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern); + void SetupStateForRendering(); + + // Set the scissor rect to a certain IntRects, resets the scissor rect to + // surface bounds when nullptr is specified. + void SetScissorToRect(IntRect *aRect); + + void PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform); + + static const uint32_t test = 4; + + IntSize mSize; + + RefPtr mDevice; + RefPtr mTexture; + RefPtr mCurrentClipMaskTexture; + RefPtr mCurrentClippedGeometry; + // This is only valid if mCurrentClippedGeometry is non-null. And will + // only be the intersection of all pixel-aligned retangular clips. This is in + // device space. + IntRect mCurrentClipBounds; + mutable RefPtr mRT; + + // We store this to prevent excessive SetTextRenderingParams calls. + RefPtr mTextRenderingParams; + + // Temporary texture and render target used for supporting alternative operators. + RefPtr mTempTexture; + RefPtr mRTView; + RefPtr mSRView; + RefPtr mTempRT; + RefPtr mTempRTView; + + // List of pushed clips. + struct PushedClip + { + RefPtr mLayer; + D2D1_RECT_F mBounds; + union { + // If mPath is non-nullptr, the mTransform member will be used, otherwise + // the mIsPixelAligned member is valid. + D2D1_MATRIX_3X2_F mTransform; + bool mIsPixelAligned; + }; + RefPtr mPath; + }; + std::vector mPushedClips; + + // We cache ID2D1Layer objects as it causes D2D to keep around textures that + // serve as the temporary surfaces for these operations. As texture creation + // is quite expensive this considerably improved performance. + // Careful here, RAII will not ensure destruction of the RefPtrs. + RefPtr mCachedLayers[kLayerCacheSize]; + uint32_t mCurrentCachedLayer; + + // The latest snapshot of this surface. This needs to be told when this + // target is modified. We keep it alive as a cache. + RefPtr mSnapshot; + // A list of targets we need to flush when we're modified. + TargetSet mDependentTargets; + // A list of targets which have this object in their mDependentTargets set + TargetSet mDependingOnTargets; + + // True of the current clip stack is pushed to the main RT. + bool mClipsArePushed; + PrivateD3D10DataD2D *mPrivateData; + static ID2D1Factory *mFactory; + static IDWriteFactory *mDWriteFactory; +}; + +} +} + +#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */ diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp index 8aa23c6b80a..cb503ff493c 100644 --- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -5,32 +5,24 @@ #include #include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" #include "FilterNodeSoftware.h" #include "GradientStopsD2D.h" #include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2D.h" #include "RadialGradientEffectD2D1.h" #include "HelpersD2D.h" #include "FilterNodeD2D1.h" -#include "ExtendInputEffectD2D1.h" #include "Tools.h" using namespace std; -// decltype is not usable for overloaded functions. -typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( - D2D1_FACTORY_TYPE factoryType, - REFIID iid, - CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, - void **factory -); - namespace mozilla { namespace gfx { uint64_t DrawTargetD2D1::mVRAMUsageDT; uint64_t DrawTargetD2D1::mVRAMUsageSS; -IDWriteFactory *DrawTargetD2D1::mDWriteFactory; ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; ID2D1Factory1 *D2DFactory1() @@ -612,7 +604,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, DWRITE_MEASURING_MODE_NATURAL, &userRect); RefPtr path; - factory()->CreatePathGeometry(getter_AddRefs(path)); + D2DFactory()->CreatePathGeometry(getter_AddRefs(path)); RefPtr sink; path->Open(getter_AddRefs(sink)); AddRectToSink(sink, userRect); @@ -1068,79 +1060,27 @@ DrawTargetD2D1::factory() return mFactory; } - RefPtr factory; - D2D1CreateFactoryFunc createD2DFactory; - HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); - createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(d2dModule, "D2D1CreateFactory"); - - if (!createD2DFactory) { - gfxWarning() << "Failed to locate D2D1CreateFactory function."; + ID2D1Factory* d2dFactory = D2DFactory(); + if (!d2dFactory) { return nullptr; } - D2D1_FACTORY_OPTIONS options; -#ifdef _DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - //options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; + HRESULT hr = d2dFactory->QueryInterface((ID2D1Factory1**)&mFactory); - HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, - __uuidof(ID2D1Factory), - &options, - getter_AddRefs(factory)); - - if (FAILED(hr) || !factory) { - gfxWarning() << "Failed to create Direct2D factory."; + if (FAILED(hr)) { return nullptr; } - hr = factory->QueryInterface((ID2D1Factory1**)&mFactory); - if (FAILED(hr) || !mFactory) { - return nullptr; - } - - ExtendInputEffectD2D1::Register(mFactory); RadialGradientEffectD2D1::Register(mFactory); return mFactory; } -IDWriteFactory* -DrawTargetD2D1::GetDWriteFactory() -{ - if (mDWriteFactory) { - return mDWriteFactory; - } - - decltype(DWriteCreateFactory)* createDWriteFactory; - HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); - createDWriteFactory = (decltype(DWriteCreateFactory)*) - GetProcAddress(dwriteModule, "DWriteCreateFactory"); - - if (!createDWriteFactory) { - gfxWarning() << "Failed to locate DWriteCreateFactory function."; - return nullptr; - } - - HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), - reinterpret_cast(&mDWriteFactory)); - - if (FAILED(hr)) { - gfxWarning() << "Failed to create DWrite Factory."; - } - - return mDWriteFactory; -} - void DrawTargetD2D1::CleanupD2D() { if (mFactory) { RadialGradientEffectD2D1::Unregister(mFactory); - ExtendInputEffectD2D1::Unregister(mFactory); mFactory->Release(); mFactory = nullptr; } diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index acbb445b590..3d3fb0d63e0 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -35,6 +35,7 @@ #endif #ifdef WIN32 +#include "DrawTargetD2D.h" #include "DrawTargetD2D1.h" #include "ScaledFontDWrite.h" #include "NativeFontResourceDWrite.h" @@ -160,6 +161,7 @@ int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT); #ifdef WIN32 +ID3D10Device1 *Factory::mD3D10Device; ID3D11Device *Factory::mD3D11Device; ID2D1Device *Factory::mD2D1Device; #endif @@ -313,6 +315,15 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor RefPtr retVal; switch (aBackend) { #ifdef WIN32 + case BackendType::DIRECT2D: + { + RefPtr newTarget; + newTarget = new DrawTargetD2D(); + if (newTarget->Init(aSize, aFormat)) { + retVal = newTarget; + } + break; + } case BackendType::DIRECT2D1_1: { RefPtr newTarget; @@ -488,6 +499,8 @@ Factory::GetMaxSurfaceSize(BackendType aType) return DrawTargetSkia::GetMaxSurfaceSize(); #endif #ifdef WIN32 + case BackendType::DIRECT2D: + return DrawTargetD2D::GetMaxSurfaceSize(); case BackendType::DIRECT2D1_1: return DrawTargetD2D1::GetMaxSurfaceSize(); #endif @@ -598,6 +611,87 @@ Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB) #ifdef WIN32 +already_AddRefed +Factory::CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) +{ + MOZ_ASSERT(aTexture); + + RefPtr newTarget; + + newTarget = new DrawTargetD2D(); + if (newTarget->Init(aTexture, aFormat)) { + RefPtr retVal = newTarget; + + if (mRecorder) { + retVal = new DrawTargetRecording(mRecorder, retVal, true); + } + + return retVal.forget(); + } + + gfxWarning() << "Failed to create draw target for D3D10 texture."; + + // Failed + return nullptr; +} + +already_AddRefed +Factory::CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA, + ID3D10Texture2D *aTextureB, + SurfaceFormat aFormat) +{ + MOZ_ASSERT(aTextureA && aTextureB); + RefPtr newTargetA; + RefPtr newTargetB; + + newTargetA = new DrawTargetD2D(); + if (!newTargetA->Init(aTextureA, aFormat)) { + gfxWarning() << "Failed to create dual draw target for D3D10 texture."; + return nullptr; + } + + newTargetB = new DrawTargetD2D(); + if (!newTargetB->Init(aTextureB, aFormat)) { + gfxWarning() << "Failed to create new draw target for D3D10 texture."; + return nullptr; + } + + RefPtr newTarget = + new DrawTargetDual(newTargetA, newTargetB); + + RefPtr retVal = newTarget; + + if (mRecorder) { + retVal = new DrawTargetRecording(mRecorder, retVal); + } + + return retVal.forget(); +} + +void +Factory::SetDirect3D10Device(ID3D10Device1 *aDevice) +{ + // do not throw on failure; return error codes and disconnect the device + // On Windows 8 error codes are the default, but on Windows 7 the + // default is to throw (or perhaps only with some drivers?) + if (aDevice) { + aDevice->SetExceptionMode(0); + } + mD3D10Device = aDevice; +} + +ID3D10Device1* +Factory::GetDirect3D10Device() +{ +#ifdef DEBUG + if (mD3D10Device) { + UINT mode = mD3D10Device->GetExceptionMode(); + MOZ_ASSERT(0 == mode); + } +#endif + return mD3D10Device; +} + already_AddRefed Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat) { @@ -673,13 +767,13 @@ Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams) uint64_t Factory::GetD2DVRAMUsageDrawTarget() { - return DrawTargetD2D1::mVRAMUsageDT; + return DrawTargetD2D::mVRAMUsageDT; } uint64_t Factory::GetD2DVRAMUsageSourceSurface() { - return DrawTargetD2D1::mVRAMUsageSS; + return DrawTargetD2D::mVRAMUsageSS; } void @@ -690,6 +784,7 @@ Factory::D2DCleanup() mD2D1Device = nullptr; } DrawTargetD2D1::CleanupD2D(); + DrawTargetD2D::CleanupD2D(); } already_AddRefed diff --git a/gfx/2d/FilterNodeD2D1.cpp b/gfx/2d/FilterNodeD2D1.cpp index 56c95299015..c7f798e5bbc 100644 --- a/gfx/2d/FilterNodeD2D1.cpp +++ b/gfx/2d/FilterNodeD2D1.cpp @@ -8,6 +8,9 @@ #include "Logging.h" #include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2D.h" +#include "SourceSurfaceD2DTarget.h" +#include "DrawTargetD2D.h" #include "DrawTargetD2D1.h" #include "ExtendInputEffectD2D1.h" @@ -156,6 +159,8 @@ already_AddRefed GetImageForSourceSurface(DrawTarget *aDT, SourceSur switch (aDT->GetBackendType()) { case BackendType::DIRECT2D1_1: return static_cast(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP); + case BackendType::DIRECT2D: + return static_cast(aDT)->GetImageForSurface(aSurface); default: gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType(); return nullptr; diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index 869003a17fe..bde54fc6507 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -25,8 +25,9 @@ namespace mozilla { namespace gfx { +ID2D1Factory* D2DFactory(); + ID2D1Factory1* D2DFactory1(); -static ID2D1Factory* D2DFactory() { return D2DFactory1(); } static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) { diff --git a/gfx/2d/NativeFontResourceDWrite.cpp b/gfx/2d/NativeFontResourceDWrite.cpp index 92ac6e88407..10b0486de83 100644 --- a/gfx/2d/NativeFontResourceDWrite.cpp +++ b/gfx/2d/NativeFontResourceDWrite.cpp @@ -8,7 +8,7 @@ #include -#include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" #include "Logging.h" #include "mozilla/RefPtr.h" @@ -69,7 +69,7 @@ public: { if (!mInstance) { mInstance = new DWriteFontFileLoader(); - DrawTargetD2D1::GetDWriteFactory()-> + DrawTargetD2D::GetDWriteFactory()-> RegisterFontFileLoader(mInstance); } return mInstance; @@ -221,7 +221,7 @@ already_AddRefed NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo) { - IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory(); + IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); if (!factory) { gfxWarning() << "Failed to get DWrite Factory."; return nullptr; diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp index eb62595a261..59e7e82c14e 100644 --- a/gfx/2d/PathD2D.cpp +++ b/gfx/2d/PathD2D.cpp @@ -6,7 +6,7 @@ #include "PathD2D.h" #include "HelpersD2D.h" #include -#include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" #include "Logging.h" namespace mozilla { @@ -351,7 +351,7 @@ already_AddRefed PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const { RefPtr path; - HRESULT hr = DrawTargetD2D1::factory()->CreatePathGeometry(getter_AddRefs(path)); + HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(getter_AddRefs(path)); if (FAILED(hr)) { gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr); diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index a9c9e8f8051..6da47bf4d06 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" #include "ScaledFontDWrite.h" #include "PathD2D.h" @@ -120,7 +120,7 @@ ScaledFontDWrite::GetSkTypeface() { MOZ_ASSERT(mFont); if (!mTypeface) { - IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory(); + IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mFont, mFontFamily); } return mTypeface; diff --git a/gfx/2d/SourceSurfaceD2D.cpp b/gfx/2d/SourceSurfaceD2D.cpp new file mode 100644 index 00000000000..650b54473b9 --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D.cpp @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#include "SourceSurfaceD2D.h" +#include "DrawTargetD2D.h" +#include "Logging.h" +#include "Tools.h" + +namespace mozilla { +namespace gfx { + +SourceSurfaceD2D::SourceSurfaceD2D() +{ +} + +SourceSurfaceD2D::~SourceSurfaceD2D() +{ + if (mBitmap) { + DrawTargetD2D::mVRAMUsageSS -= GetByteSize(); + } +} + +IntSize +SourceSurfaceD2D::GetSize() const +{ + return mSize; +} + +SurfaceFormat +SourceSurfaceD2D::GetFormat() const +{ + return mFormat; +} + +bool +SourceSurfaceD2D::IsValid() const +{ + return mDevice == Factory::GetDirect3D10Device(); +} + +already_AddRefed +SourceSurfaceD2D::GetDataSurface() +{ + RefPtr result = new DataSourceSurfaceD2D(this); + if (result->IsValid()) { + return result.forget(); + } + return nullptr; +} + +bool +SourceSurfaceD2D::InitFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat, + ID2D1RenderTarget *aRT) +{ + HRESULT hr; + + mFormat = aFormat; + mSize = aSize; + + if ((uint32_t)aSize.width > aRT->GetMaximumBitmapSize() || + (uint32_t)aSize.height > aRT->GetMaximumBitmapSize()) { + gfxDebug() << "Bitmap does not fit in texture."; + return false; + } + + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); + hr = aRT->CreateBitmap(D2DIntSize(aSize), props, getter_AddRefs(mBitmap)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create D2D Bitmap for data. Code: " << hexa(hr); + return false; + } + + hr = mBitmap->CopyFromMemory(nullptr, aData, aStride); + + if (FAILED(hr)) { + gfxWarning() << "Failed to copy data to D2D bitmap. Code: " << hexa(hr); + return false; + } + + DrawTargetD2D::mVRAMUsageSS += GetByteSize(); + mDevice = Factory::GetDirect3D10Device(); + + return true; +} + +bool +SourceSurfaceD2D::InitFromTexture(ID3D10Texture2D *aTexture, + SurfaceFormat aFormat, + ID2D1RenderTarget *aRT) +{ + HRESULT hr; + + RefPtr surf; + + hr = aTexture->QueryInterface((IDXGISurface**)&surf); + + if (FAILED(hr)) { + gfxWarning() << "Failed to QI texture to surface. Code: " << hexa(hr); + return false; + } + + D3D10_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + + mSize = IntSize(desc.Width, desc.Height); + mFormat = aFormat; + + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(aFormat)); + hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, getter_AddRefs(mBitmap)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create SharedBitmap. Code: " << hexa(hr); + return false; + } + + aTexture->GetDevice(getter_AddRefs(mDevice)); + DrawTargetD2D::mVRAMUsageSS += GetByteSize(); + + return true; +} + +uint32_t +SourceSurfaceD2D::GetByteSize() const +{ + return mSize.width * mSize.height * BytesPerPixel(mFormat); +} + +DataSourceSurfaceD2D::DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface) + : mTexture(nullptr) + , mFormat(aSourceSurface->mFormat) + , mSize(aSourceSurface->mSize) + , mMapped(false) +{ + // We allocate ourselves a regular D3D surface (sourceTexture) and paint the + // D2D bitmap into it via a DXGI render target. Then we need to copy + // sourceTexture into a staging texture (mTexture), which we will lazily map + // to get the data. + + CD3D10_TEXTURE2D_DESC desc(DXGIFormat(mFormat), mSize.width, mSize.height); + desc.MipLevels = 1; + desc.Usage = D3D10_USAGE_DEFAULT; + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + RefPtr sourceTexture; + HRESULT hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, + getter_AddRefs(sourceTexture)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create texture. Code: " << hexa(hr); + return; + } + + RefPtr dxgiSurface; + hr = sourceTexture->QueryInterface((IDXGISurface**)getter_AddRefs(dxgiSurface)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create DXGI surface. Code: " << hexa(hr); + return; + } + + D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); + + RefPtr renderTarget; + hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(dxgiSurface, + &rtProps, + getter_AddRefs(renderTarget)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create render target. Code: " << hexa(hr); + return; + } + + renderTarget->BeginDraw(); + renderTarget->Clear(D2D1::ColorF(0, 0.0f)); + if (aSourceSurface->GetFormat() != SurfaceFormat::A8) { + renderTarget->DrawBitmap(aSourceSurface->mBitmap, + D2D1::RectF(0, 0, + Float(mSize.width), + Float(mSize.height))); + } else { + RefPtr brush; + renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush)); + renderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + renderTarget->FillOpacityMask(aSourceSurface->mBitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); + } + hr = renderTarget->EndDraw(); + if (FAILED(hr)) { + gfxWarning() << "Failed to draw bitmap. Code: " << hexa(hr); + return; + } + + desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE; + desc.Usage = D3D10_USAGE_STAGING; + desc.BindFlags = 0; + hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create staging texture. Code: " << hexa(hr); + mTexture = nullptr; + return; + } + + aSourceSurface->mDevice->CopyResource(mTexture, sourceTexture); +} + +DataSourceSurfaceD2D::~DataSourceSurfaceD2D() +{ + if (mMapped) { + mTexture->Unmap(0); + } +} + +unsigned char* +DataSourceSurfaceD2D::GetData() +{ + EnsureMappedTexture(); + if (!mMapped) { + return nullptr; + } + + return reinterpret_cast(mData.pData); +} + +int32_t +DataSourceSurfaceD2D::Stride() +{ + EnsureMappedTexture(); + if (!mMapped) { + return 0; + } + + return mData.RowPitch; +} + +IntSize +DataSourceSurfaceD2D::GetSize() const +{ + return mSize; +} + +SurfaceFormat +DataSourceSurfaceD2D::GetFormat() const +{ + return mFormat; +} + +bool +DataSourceSurfaceD2D::Map(MapType aMapType, MappedSurface *aMappedSurface) +{ + // DataSourceSurfaces used with the new Map API should not be used with GetData!! + MOZ_ASSERT(!mMapped); + MOZ_ASSERT(!mIsMapped); + + if (!mTexture) { + return false; + } + + D3D10_MAP mapType; + + if (aMapType == MapType::READ) { + mapType = D3D10_MAP_READ; + } else if (aMapType == MapType::WRITE) { + mapType = D3D10_MAP_WRITE; + } else { + mapType = D3D10_MAP_READ_WRITE; + } + + D3D10_MAPPED_TEXTURE2D map; + + HRESULT hr = mTexture->Map(0, mapType, 0, &map); + + if (FAILED(hr)) { + gfxWarning() << "Texture map failed with code: " << hexa(hr); + return false; + } + + aMappedSurface->mData = (uint8_t*)map.pData; + aMappedSurface->mStride = map.RowPitch; + mIsMapped = !!aMappedSurface->mData; + + return mIsMapped; +} + +void +DataSourceSurfaceD2D::Unmap() +{ + MOZ_ASSERT(mIsMapped); + + mIsMapped = false; + mTexture->Unmap(0); +} + +void +DataSourceSurfaceD2D::EnsureMappedTexture() +{ + // Do not use GetData() after having used Map! + MOZ_ASSERT(!mIsMapped); + + if (mMapped || + !mTexture) { + return; + } + + HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mData); + if (FAILED(hr)) { + gfxWarning() << "Failed to map texture. Code: " << hexa(hr); + mTexture = nullptr; + } else { + mMapped = true; + } +} + +} +} diff --git a/gfx/2d/SourceSurfaceD2D.h b/gfx/2d/SourceSurfaceD2D.h new file mode 100644 index 00000000000..22ee5f125f9 --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SOURCESURFACED2D_H_ +#define MOZILLA_GFX_SOURCESURFACED2D_H_ + +#include "2D.h" +#include "HelpersD2D.h" +#include + +namespace mozilla { +namespace gfx { + +class DataSourceSurfaceD2D; + +class SourceSurfaceD2D : public SourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2D) + SourceSurfaceD2D(); + ~SourceSurfaceD2D(); + + virtual SurfaceType GetType() const { return SurfaceType::D2D1_BITMAP; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const; + virtual bool IsValid() const; + + virtual already_AddRefed GetDataSurface(); + + ID2D1Bitmap *GetBitmap() { return mBitmap; } + + bool InitFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat, + ID2D1RenderTarget *aRT); + bool InitFromTexture(ID3D10Texture2D *aTexture, + SurfaceFormat aFormat, + ID2D1RenderTarget *aRT); +private: + friend class DrawTargetD2D; + friend class DataSourceSurfaceD2D; + + uint32_t GetByteSize() const; + + RefPtr mBitmap; + // We need to keep this pointer here to check surface validity. + RefPtr mDevice; + SurfaceFormat mFormat; + IntSize mSize; +}; + + +class DataSourceSurfaceD2D : public DataSourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D) + DataSourceSurfaceD2D(SourceSurfaceD2D* aSourceSurface); + virtual ~DataSourceSurfaceD2D(); + + virtual unsigned char* GetData(); + virtual int32_t Stride(); + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const; + virtual bool Map(MapType, MappedSurface *aMappedSurface); + virtual void Unmap(); + + bool IsValid() + { + return mTexture; + } + +private: + void EnsureMappedTexture(); + + RefPtr mTexture; + + D3D10_MAPPED_TEXTURE2D mData; + + SurfaceFormat mFormat; + IntSize mSize; + bool mMapped; +}; + +} +} + +#endif /* MOZILLA_GFX_SOURCESURFACED2D_H_ */ diff --git a/gfx/2d/SourceSurfaceD2DTarget.cpp b/gfx/2d/SourceSurfaceD2DTarget.cpp new file mode 100644 index 00000000000..464a64f4230 --- /dev/null +++ b/gfx/2d/SourceSurfaceD2DTarget.cpp @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#include "SourceSurfaceD2DTarget.h" +#include "Logging.h" +#include "DrawTargetD2D.h" +#include "Tools.h" + +#include + +namespace mozilla { +namespace gfx { + +SourceSurfaceD2DTarget::SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, + ID3D10Texture2D* aTexture, + SurfaceFormat aFormat) + : mDrawTarget(aDrawTarget) + , mTexture(aTexture) + , mFormat(aFormat) + , mOwnsCopy(false) +{ +} + +SourceSurfaceD2DTarget::~SourceSurfaceD2DTarget() +{ + // We don't need to do anything special here to notify our mDrawTarget. It must + // already have cleared its mSnapshot field, otherwise this object would + // be kept alive. + if (mOwnsCopy) { + IntSize size = GetSize(); + + DrawTargetD2D::mVRAMUsageSS -= size.width * size.height * BytesPerPixel(mFormat); + } +} + +IntSize +SourceSurfaceD2DTarget::GetSize() const +{ + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + + return IntSize(desc.Width, desc.Height); +} + +SurfaceFormat +SourceSurfaceD2DTarget::GetFormat() const +{ + return mFormat; +} + +already_AddRefed +SourceSurfaceD2DTarget::GetDataSurface() +{ + RefPtr dataSurf = + new DataSourceSurfaceD2DTarget(mFormat); + + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + + desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; + desc.Usage = D3D10_USAGE_STAGING; + desc.BindFlags = 0; + desc.MiscFlags = 0; + + if (!Factory::GetDirect3D10Device()) { + gfxCriticalError() << "Invalid D3D10 device in D2D target surface (GDS)"; + return nullptr; + } + + HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(dataSurf->mTexture)); + + if (FAILED(hr)) { + gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr); + return nullptr; + } + Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture); + + return dataSurf.forget(); +} + +void* +SourceSurfaceD2DTarget::GetNativeSurface(NativeSurfaceType aType) +{ + if (aType == NativeSurfaceType::D3D10_TEXTURE) { + return static_cast(mTexture.get()); + } + return nullptr; +} + +ID3D10ShaderResourceView* +SourceSurfaceD2DTarget::GetSRView() +{ + if (mSRView) { + return mSRView; + } + + if (!Factory::GetDirect3D10Device()) { + gfxCriticalError() << "Invalid D3D10 device in D2D target surface (SRV)"; + return nullptr; + } + + HRESULT hr = Factory::GetDirect3D10Device()->CreateShaderResourceView(mTexture, nullptr, getter_AddRefs(mSRView)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create ShaderResourceView. Code: " << hexa(hr); + } + + return mSRView; +} + +void +SourceSurfaceD2DTarget::DrawTargetWillChange() +{ + RefPtr oldTexture = mTexture; + + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + + // Our original texture might implement the keyed mutex flag. We shouldn't + // need that here. We actually specifically don't want it since we don't lock + // our texture for usage! + desc.MiscFlags = 0; + + // Get a copy of the surface data so the content at snapshot time was saved. + Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); + Factory::GetDirect3D10Device()->CopyResource(mTexture, oldTexture); + + mBitmap = nullptr; + + DrawTargetD2D::mVRAMUsageSS += desc.Width * desc.Height * BytesPerPixel(mFormat); + mOwnsCopy = true; + + // We now no longer depend on the source surface content remaining the same. + MarkIndependent(); +} + +ID2D1Bitmap* +SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT) +{ + if (mBitmap) { + return mBitmap; + } + + HRESULT hr; + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + + IntSize size(desc.Width, desc.Height); + + RefPtr surf; + hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surf)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hexa(hr); + return nullptr; + } + + D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(mFormat)); + hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, getter_AddRefs(mBitmap)); + + if (FAILED(hr)) { + // This seems to happen for SurfaceFormat::A8 sometimes... + hr = aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height), + D2D1::BitmapProperties(D2DPixelFormat(mFormat)), + getter_AddRefs(mBitmap)); + + if (FAILED(hr)) { + gfxWarning() << "Failed in CreateBitmap. Code: " << hexa(hr); + return nullptr; + } + + RefPtr rt; + + if (mDrawTarget) { + rt = mDrawTarget->mRT; + } + + if (!rt) { + // Okay, we already separated from our drawtarget. And we're an A8 + // surface the only way we can get to a bitmap is by creating a + // a rendertarget and from there copying to a bitmap! Terrible! + RefPtr surface; + + hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surface)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to QI texture to surface."; + return nullptr; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2DPixelFormat(mFormat)); + hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(surface, props, getter_AddRefs(rt)); + + if (FAILED(hr)) { + gfxWarning() << "Failed to create D2D render target for texture."; + return nullptr; + } + } + + mBitmap->CopyFromRenderTarget(nullptr, rt, nullptr); + return mBitmap; + } + + return mBitmap; +} + +void +SourceSurfaceD2DTarget::MarkIndependent() +{ + if (mDrawTarget) { + MOZ_ASSERT(mDrawTarget->mSnapshot == this); + mDrawTarget->mSnapshot = nullptr; + mDrawTarget = nullptr; + } +} + +DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget(SurfaceFormat aFormat) + : mFormat(aFormat) + , mMapped(false) +{ +} + +DataSourceSurfaceD2DTarget::~DataSourceSurfaceD2DTarget() +{ + if (mMapped) { + mTexture->Unmap(0); + } +} + +IntSize +DataSourceSurfaceD2DTarget::GetSize() const +{ + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + + return IntSize(desc.Width, desc.Height); +} + +SurfaceFormat +DataSourceSurfaceD2DTarget::GetFormat() const +{ + return mFormat; +} + +uint8_t* +DataSourceSurfaceD2DTarget::GetData() +{ + EnsureMapped(); + + return (unsigned char*)mMap.pData; +} + +int32_t +DataSourceSurfaceD2DTarget::Stride() +{ + EnsureMapped(); + return mMap.RowPitch; +} + +bool +DataSourceSurfaceD2DTarget::Map(MapType aMapType, MappedSurface *aMappedSurface) +{ + // DataSourceSurfaces used with the new Map API should not be used with GetData!! + MOZ_ASSERT(!mMapped); + MOZ_ASSERT(!mIsMapped); + + if (!mTexture) { + return false; + } + + D3D10_MAP mapType; + + if (aMapType == MapType::READ) { + mapType = D3D10_MAP_READ; + } else if (aMapType == MapType::WRITE) { + mapType = D3D10_MAP_WRITE; + } else { + mapType = D3D10_MAP_READ_WRITE; + } + + D3D10_MAPPED_TEXTURE2D map; + + HRESULT hr = mTexture->Map(0, mapType, 0, &map); + + if (FAILED(hr)) { + gfxWarning() << "Texture map failed with code: " << hexa(hr); + return false; + } + + aMappedSurface->mData = (uint8_t*)map.pData; + aMappedSurface->mStride = map.RowPitch; + mIsMapped = !!aMappedSurface->mData; + + return mIsMapped; +} + +void +DataSourceSurfaceD2DTarget::Unmap() +{ + MOZ_ASSERT(mIsMapped); + + mIsMapped = false; + mTexture->Unmap(0); +} + +void +DataSourceSurfaceD2DTarget::EnsureMapped() +{ + // Do not use GetData() after having used Map! + MOZ_ASSERT(!mIsMapped); + if (!mMapped) { + HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mMap); + if (FAILED(hr)) { + gfxWarning() << "Failed to map texture to memory. Code: " << hexa(hr); + return; + } + mMapped = true; + } +} + +} +} diff --git a/gfx/2d/SourceSurfaceD2DTarget.h b/gfx/2d/SourceSurfaceD2DTarget.h new file mode 100644 index 00000000000..82b5b20778c --- /dev/null +++ b/gfx/2d/SourceSurfaceD2DTarget.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ +#define MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ + +#include "2D.h" +#include "HelpersD2D.h" +#include +#include + +namespace mozilla { +namespace gfx { + +class DrawTargetD2D; + +class SourceSurfaceD2DTarget : public SourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceD2DTarget) + SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, ID3D10Texture2D* aTexture, + SurfaceFormat aFormat); + ~SourceSurfaceD2DTarget(); + + virtual SurfaceType GetType() const { return SurfaceType::D2D1_DRAWTARGET; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const; + virtual already_AddRefed GetDataSurface(); + virtual void *GetNativeSurface(NativeSurfaceType aType); + + DrawTargetD2D* GetDT() { return mDrawTarget; } + ID2D1Bitmap *GetBitmap(ID2D1RenderTarget *aRT); + +private: + friend class DrawTargetD2D; + + ID3D10ShaderResourceView *GetSRView(); + + // This function is called by the draw target this texture belongs to when + // it is about to be changed. The texture will be required to make a copy + // of itself when this happens. + void DrawTargetWillChange(); + + // This will mark the surface as no longer depending on its drawtarget, + // this may happen on destruction or copying. + void MarkIndependent(); + + RefPtr mSRView; + RefPtr mBitmap; + // Non-null if this is a "lazy copy" of the given draw target. + // Null if we've made a copy. The target is not kept alive, otherwise we'd + // have leaks since it might keep us alive. If the target is destroyed, it + // will notify us. + DrawTargetD2D* mDrawTarget; + mutable RefPtr mTexture; + SurfaceFormat mFormat; + bool mOwnsCopy; +}; + +class DataSourceSurfaceD2DTarget : public DataSourceSurface +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2DTarget) + DataSourceSurfaceD2DTarget(SurfaceFormat aFormat); + ~DataSourceSurfaceD2DTarget(); + + virtual SurfaceType GetType() const { return SurfaceType::DATA; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const; + virtual uint8_t *GetData(); + virtual int32_t Stride(); + virtual bool Map(MapType, MappedSurface *aMappedSurface); + virtual void Unmap(); + +private: + friend class SourceSurfaceD2DTarget; + void EnsureMapped(); + + mutable RefPtr mTexture; + SurfaceFormat mFormat; + D3D10_MAPPED_TEXTURE2D mMap; + bool mMapped; +}; + +} +} + +#endif /* MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ */ diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 11199947da3..3da1523b50d 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -123,7 +123,7 @@ enum class DrawTargetType : int8_t { enum class BackendType : int8_t { NONE = 0, - DIRECT2D, // Used for version independent D2D objects. + DIRECT2D, COREGRAPHICS, COREGRAPHICS_ACCELERATED, CAIRO, diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index eb674d531d2..e945563ecd6 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -66,6 +66,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'): ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': SOURCES += [ + 'DrawTargetD2D.cpp', 'DrawTargetD2D1.cpp', 'ExtendInputEffectD2D1.cpp', 'FilterNodeD2D1.cpp', @@ -76,7 +77,9 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': 'RadialGradientEffectD2D1.cpp', 'ScaledFontDWrite.cpp', 'ScaledFontWin.cpp', + 'SourceSurfaceD2D.cpp', 'SourceSurfaceD2D1.cpp', + 'SourceSurfaceD2DTarget.cpp', ] DEFINES['WIN32'] = True From 3eb54ae1facfc269cd1e44086a9f3c4bfab87e51 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 08:49:29 +0100 Subject: [PATCH 184/187] Backed out changeset 4adb7586410a (bug 1247775) --- gfx/ipc/GraphicsMessages.ipdlh | 1 + gfx/thebes/gfxBlur.cpp | 2 +- gfx/thebes/gfxPrefs.h | 2 + gfx/thebes/gfxUtils.cpp | 3 +- gfx/thebes/gfxWindowsPlatform.cpp | 263 ++++++++++++++++++++++++++---- gfx/thebes/gfxWindowsPlatform.h | 22 ++- modules/libpref/init/all.js | 5 +- widget/windows/GfxInfo.cpp | 6 +- 8 files changed, 260 insertions(+), 44 deletions(-) diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh index 09fb18416c6..f72f09abb89 100644 --- a/gfx/ipc/GraphicsMessages.ipdlh +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -19,6 +19,7 @@ struct DeviceInitData bool useD3D11WARP; bool useD3D11ImageBridge; bool d3d11TextureSharingWorks; + bool useD2D; bool useD2D1; DxgiAdapterDesc adapter; }; diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index a22aa3ab5e0..2f4e1fc05b7 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -594,7 +594,7 @@ RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, if ((!aDT.GetTransform().IsRectilinear() && aDT.GetBackendType() != BackendType::CAIRO) || - (aDT.GetBackendType() == BackendType::DIRECT2D1_1)) { + (aDT.GetBackendType() == BackendType::DIRECT2D)) { // Use stretching if possible, since it leads to less seams when the // destination is transformed. However, don't do this if we're using cairo, // because if cairo is using pixman it won't render anything for large diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index e7a1be313bc..d98ef69a415 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -242,6 +242,8 @@ private: DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false); DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false); + DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false); + DECL_GFX_PREF(Live, "gfx.direct2d.allow1_0", Direct2DAllow1_0, bool, false); DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false); DECL_GFX_PREF(Once, "gfx.e10s.hide-plugins-for-scroll", HidePluginsForScroll, bool, true); DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.force-enabled", DirectWriteFontRenderingForceEnabled, bool, false); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index bd751e5e6a3..abdd342e84b 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -422,7 +422,8 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable, js::ProfileEntry::Category::GRAPHICS); DrawTarget* destDrawTarget = aContext->GetDrawTarget(); - if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) { + if ((destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) || + (destDrawTarget->GetBackendType() == BackendType::DIRECT2D)) { return nullptr; } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 7f455a138c6..ce39be4458e 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -43,10 +43,12 @@ #include "WinUtils.h" +#ifdef CAIRO_HAS_DWRITE_FONT #include "gfxDWriteFontList.h" #include "gfxDWriteFonts.h" #include "gfxDWriteCommon.h" #include +#endif #include "gfxTextRun.h" #include "gfxUserFontSet.h" @@ -55,11 +57,13 @@ #include +#ifdef CAIRO_HAS_D2D_SURFACE #include #include "mozilla/gfx/2D.h" #include "nsMemory.h" +#endif #include @@ -110,11 +114,14 @@ DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget) } } +#ifdef CAIRO_HAS_D2D_SURFACE + static const char *kFeatureLevelPref = "gfx.direct3d.last_used_feature_level_idx"; static const int kSupportedFeatureLevels[] = { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0 }; +#endif class GfxD2DVramReporter final : public nsIMemoryReporter { @@ -159,16 +166,16 @@ NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter) class GPUAdapterReporter final : public nsIMemoryReporter { // Callers must Release the DXGIAdapter after use or risk mem-leak - static bool GetDXGIAdapter(IDXGIAdapter **aDXGIAdapter) + static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter) { - ID3D11Device *d3d11Device; - IDXGIDevice *dxgiDevice; + ID3D10Device1 *D2D10Device; + IDXGIDevice *DXGIDevice; bool result = false; - if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) { - if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice) == S_OK) { - result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK); - dxgiDevice->Release(); + if ((D2D10Device = mozilla::gfx::Factory::GetDirect3D10Device())) { + if (D2D10Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice) == S_OK) { + result = (DXGIDevice->GetAdapter(DXGIAdapter) == S_OK); + DXGIDevice->Release(); } } @@ -369,6 +376,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() , mCompositorD3D11TextureSharingWorks(false) , mAcceleration(FeatureStatus::Unused) , mD3D11Status(FeatureStatus::Unused) + , mD2DStatus(FeatureStatus::Unused) , mD2D1Status(FeatureStatus::Unused) { mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; @@ -403,6 +411,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() gfxWindowsPlatform::~gfxWindowsPlatform() { mDeviceManager = nullptr; + mD3D10Device = nullptr; mD3D11Device = nullptr; mD3D11ContentDevice = nullptr; mD3D11ImageBridgeDevice = nullptr; @@ -480,7 +489,9 @@ gfxWindowsPlatform::HandleDeviceReset() // Remove devices and adapters. ResetD3D11Devices(); + mD3D10Device = nullptr; mAdapter = nullptr; + Factory::SetDirect3D10Device(nullptr); // Reset local state. Note: we leave feature status variables as-is. They // will be recomputed by InitializeDevices(). @@ -508,11 +519,17 @@ gfxWindowsPlatform::UpdateBackendPrefs() uint32_t canvasMask = BackendTypeBit(SOFTWARE_BACKEND); uint32_t contentMask = BackendTypeBit(SOFTWARE_BACKEND); BackendType defaultBackend = SOFTWARE_BACKEND; - if (GetD2D1Status() == FeatureStatus::Available) { + if (GetD2DStatus() == FeatureStatus::Available) { mRenderMode = RENDER_DIRECT2D; - contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); - defaultBackend = BackendType::DIRECT2D1_1; + canvasMask |= BackendTypeBit(BackendType::DIRECT2D); + contentMask |= BackendTypeBit(BackendType::DIRECT2D); + if (GetD2D1Status() == FeatureStatus::Available) { + contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); + defaultBackend = BackendType::DIRECT2D1_1; + } else { + defaultBackend = BackendType::DIRECT2D; + } } else { mRenderMode = RENDER_GDI; canvasMask |= BackendTypeBit(BackendType::SKIA); @@ -554,11 +571,129 @@ gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers) return SOFTWARE_BACKEND; } +#ifdef CAIRO_HAS_D2D_SURFACE +HRESULT +gfxWindowsPlatform::CreateDevice(RefPtr &adapter1, + int featureLevelIndex) +{ + nsModuleHandle d3d10module(LoadLibrarySystem32(L"d3d10_1.dll")); + if (!d3d10module) + return E_FAIL; + decltype(D3D10CreateDevice1)* createD3DDevice = + (decltype(D3D10CreateDevice1)*) GetProcAddress(d3d10module, "D3D10CreateDevice1"); + if (!createD3DDevice) + return E_FAIL; + + ID3D10Device1* device = nullptr; + HRESULT hr = + createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr, +#ifdef DEBUG + // This isn't set because of bug 1078411 + // D3D10_CREATE_DEVICE_DEBUG | +#endif + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + static_cast(kSupportedFeatureLevels[featureLevelIndex]), + D3D10_1_SDK_VERSION, &device); + + // If we fail here, the DirectX version or video card probably + // changed. We previously could use 10.1 but now we can't + // anymore. Revert back to doing a 10.0 check first before + // the 10.1 check. + if (device) { + mD3D10Device = device; + + // Leak the module while the D3D 10 device is being used. + d3d10module.disown(); + + // Setup a pref for future launch optimizaitons when in main process. + if (XRE_IsParentProcess()) { + Preferences::SetInt(kFeatureLevelPref, featureLevelIndex); + } + } + + return device ? S_OK : hr; +} +#endif + +void +gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce) +{ + if ((!Factory::SupportsD2D1() || !gfxPrefs::Direct2DUse1_1()) && !gfxPrefs::Direct2DAllow1_0()) { + return; + } + +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD3D10Device) { + if (SUCCEEDED(mD3D10Device->GetDeviceRemovedReason())) { + return; + } + mD3D10Device = nullptr; + + // Surface cache needs to be invalidated since it may contain vector + // images rendered with our old, broken D2D device. + SurfaceCache::DiscardAll(); + } + + mozilla::ScopedGfxFeatureReporter reporter("D2D", aAttemptForce); + + int supportedFeatureLevelsCount = ArrayLength(kSupportedFeatureLevels); + + RefPtr adapter1 = GetDXGIAdapter(); + + if (!adapter1) { + // Unable to create adapter, abort acceleration. + return; + } + + // It takes a lot of time (5-10% of startup time or ~100ms) to do both + // a createD3DDevice on D3D10_FEATURE_LEVEL_10_0. We therefore store + // the last used feature level to go direct to that. + int featureLevelIndex = Preferences::GetInt(kFeatureLevelPref, 0); + if (featureLevelIndex >= supportedFeatureLevelsCount || featureLevelIndex < 0) + featureLevelIndex = 0; + + // Start with the last used feature level, and move to lower DX versions + // until we find one that works. + HRESULT hr = E_FAIL; + for (int i = featureLevelIndex; i < supportedFeatureLevelsCount; i++) { + hr = CreateDevice(adapter1, i); + // If it succeeded we found the first available feature level + if (SUCCEEDED(hr)) + break; + } + + // If we succeeded in creating a device, try for a newer device + // that we haven't tried yet. + if (SUCCEEDED(hr)) { + for (int i = featureLevelIndex - 1; i >= 0; i--) { + hr = CreateDevice(adapter1, i); + // If it failed then we don't have new hardware + if (FAILED(hr)) { + break; + } + } + } + + if (mD3D10Device) { + reporter.SetSuccessful(); + mozilla::gfx::Factory::SetDirect3D10Device(mD3D10Device); + } + + ScopedGfxFeatureReporter reporter1_1("D2D1.1V"); + + if (Factory::SupportsD2D1()) { + reporter1_1.SetSuccessful(); + } +#endif +} + gfxPlatformFontList* gfxWindowsPlatform::CreatePlatformFontList() { gfxPlatformFontList *pfl; +#ifdef CAIRO_HAS_DWRITE_FONT // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd // crashers so blacklist them altogether if (IsNotWin7PreRTM() && GetDWriteFactory()) { @@ -572,7 +707,7 @@ gfxWindowsPlatform::CreatePlatformFontList() gfxPlatformFontList::Shutdown(); DisableD2D(); } - +#endif pfl = new gfxGDIFontList(); if (NS_SUCCEEDED(pfl->InitFontList())) { @@ -592,8 +727,10 @@ gfxWindowsPlatform::CreatePlatformFontList() void gfxWindowsPlatform::DisableD2D() { + mD2DStatus = FeatureStatus::Failed; mD2D1Status = FeatureStatus::Failed; Factory::SetDirect3D11Device(nullptr); + Factory::SetDirect3D10Device(nullptr); UpdateBackendPrefs(); } @@ -1041,6 +1178,12 @@ gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason) return true; } } + if (GetD3D10Device()) { + HRESULT hr = GetD3D10Device()->GetDeviceRemovedReason(); + if (IsDeviceReset(hr, aResetReason)) { + return true; + } + } if (XRE_IsParentProcess() && gfxPrefs::DeviceResetForTesting()) { TestDeviceReset((DeviceResetReason)gfxPrefs::DeviceResetForTesting()); if (aResetReason) { @@ -1304,6 +1447,7 @@ gfxWindowsPlatform::FontsPrefsChanged(const char *aPref) void gfxWindowsPlatform::SetupClearTypeParams() { +#if CAIRO_HAS_DWRITE_FONT if (GetDWriteFactory()) { // any missing prefs will default to invalid (-1) and be ignored; // out-of-range values will also be ignored @@ -1411,6 +1555,7 @@ gfxWindowsPlatform::SetupClearTypeParams() dwriteGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, getter_AddRefs(mRenderingParams[TEXT_RENDERING_GDI_CLASSIC])); } +#endif } void @@ -2272,7 +2417,7 @@ gfxWindowsPlatform::InitializeDevices() // Usually we want D2D in order to use DWrite, but if the users have it // forced, we'll let them have it, as unsupported configuration. if (gfxPrefs::DirectWriteFontRenderingForceEnabled() && - IsFeatureStatusFailure(mD2D1Status) && + IsFeatureStatusFailure(mD2DStatus) && !mDWriteFactory) { gfxCriticalNote << "Attempting DWrite without D2D support"; InitDWriteSupport(); @@ -2438,11 +2583,17 @@ IsD2DBlacklisted() // not change after a TDR (like the OS version), we could find a driver change // that runs us into the blacklist. FeatureStatus -gfxWindowsPlatform::CheckD2D1Support() +gfxWindowsPlatform::CheckD2DSupport() { - // Don't revive D2D1 support after a failure. - if (IsFeatureStatusFailure(mD2D1Status)) { - return mD2D1Status; + // Don't revive D2D support after a failure. + if (IsFeatureStatusFailure(mD2DStatus)) { + return mD2DStatus; + } + + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD2D() + ? FeatureStatus::Available + : FeatureStatus::Blocked; } if (!gfxPrefs::Direct2DForceEnabled() && IsD2DBlacklisted()) { @@ -2466,38 +2617,70 @@ gfxWindowsPlatform::CheckD2D1Support() if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) { return FeatureStatus::Blocked; } - - if (!Factory::SupportsD2D1()) { - return FeatureStatus::Unavailable; - } - - if (XRE_IsContentProcess()) { - return GetParentDevicePrefs().useD2D1() - ? FeatureStatus::Available - : FeatureStatus::Blocked; - } - return FeatureStatus::Available; } void gfxWindowsPlatform::InitializeD2D() { - ScopedGfxFeatureReporter d2d1_1("D2D1.1"); - - mD2D1Status = CheckD2D1Support(); - if (IsFeatureStatusFailure(mD2D1Status)) { + mD2DStatus = CheckD2DSupport(); + if (IsFeatureStatusFailure(mD2DStatus)) { return; } if (!mCompositorD3D11TextureSharingWorks) { - mD2D1Status = FeatureStatus::Failed; + mD2DStatus = FeatureStatus::Failed; return; } // Using Direct2D depends on DWrite support. if (!mDWriteFactory && !InitDWriteSupport()) { - mD2D1Status = FeatureStatus::Failed; + mD2DStatus = FeatureStatus::Failed; + return; + } + + // Initialize D2D 1.1. + InitializeD2D1(); + + // Initialize D2D 1.0. + VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); + if (!mD3D10Device) { + mDWriteFactory = nullptr; + mD2DStatus = FeatureStatus::Failed; + return; + } + + mD2DStatus = FeatureStatus::Available; +} + +FeatureStatus +gfxWindowsPlatform::CheckD2D1Support() +{ + // Don't revive D2D1 support after a failure. + if (IsFeatureStatusFailure(mD2D1Status)) { + return mD2D1Status; + } + if (!Factory::SupportsD2D1()) { + return FeatureStatus::Unavailable; + } + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD2D1() + ? FeatureStatus::Available + : FeatureStatus::Blocked; + } + if (!gfxPrefs::Direct2DUse1_1()) { + return FeatureStatus::Disabled; + } + return FeatureStatus::Available; +} + +void +gfxWindowsPlatform::InitializeD2D1() +{ + ScopedGfxFeatureReporter d2d1_1("D2D1.1"); + + mD2D1Status = CheckD2D1Support(); + if (IsFeatureStatusFailure(mD2D1Status)) { return; } @@ -2510,8 +2693,6 @@ gfxWindowsPlatform::InitializeD2D() Factory::SetDirect3D11Device(mD3D11ContentDevice); d2d1_1.SetSuccessful(); - - mD2D1Status = FeatureStatus::Available; } bool @@ -2870,6 +3051,15 @@ gfxWindowsPlatform::GetD3D11Status() const return mD3D11Status; } +FeatureStatus +gfxWindowsPlatform::GetD2DStatus() const +{ + if (GetD3D11Status() != FeatureStatus::Available) { + return FeatureStatus::Unavailable; + } + return mD2DStatus; +} + FeatureStatus gfxWindowsPlatform::GetD2D1Status() const { @@ -2906,6 +3096,7 @@ gfxWindowsPlatform::GetDeviceInitData(DeviceInitData* aOut) aOut->useD3D11ImageBridge() = !!mD3D11ImageBridgeDevice; aOut->d3d11TextureSharingWorks() = mCompositorD3D11TextureSharingWorks; aOut->useD3D11WARP() = mIsWARP; + aOut->useD2D() = (GetD2DStatus() == FeatureStatus::Available); aOut->useD2D1() = (GetD2D1Status() == FeatureStatus::Available); if (mD3D11Device) { diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 51011799efc..8716506cf09 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -17,7 +17,9 @@ #include "gfxFontUtils.h" #include "gfxWindowsSurface.h" #include "gfxFont.h" +#ifdef CAIRO_HAS_DWRITE_FONT #include "gfxDWriteFonts.h" +#endif #include "gfxPlatform.h" #include "gfxTelemetry.h" #include "gfxTypes.h" @@ -31,7 +33,9 @@ #include #include +#ifdef CAIRO_HAS_D2D_SURFACE #include +#endif // This header is available in the June 2010 SDK and in the Win8 SDK #include @@ -159,6 +163,10 @@ public: */ void VerifyD2DDevice(bool aAttemptForce); +#ifdef CAIRO_HAS_D2D_SURFACE + HRESULT CreateDevice(RefPtr &adapter1, int featureLevelIndex); +#endif + nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) override; @@ -222,16 +230,20 @@ public: void SetupClearTypeParams(); +#ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } inline bool DWriteEnabled() { return !!mDWriteFactory; } inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; } IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode) { return mRenderingParams[aRenderMode]; } - +#else + inline bool DWriteEnabled() { return false; } +#endif void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager); mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager(); IDirect3DDevice9* GetD3D9Device(); + ID3D10Device1 *GetD3D10Device() { return mD3D10Device; } ID3D11Device *GetD3D11Device(); ID3D11Device *GetD3D11ContentDevice(); ID3D11Device* GetD3D11DeviceForCurrentThread(); @@ -269,6 +281,7 @@ public: // initialization has not been attempted, this returns // FeatureStatus::Unused. mozilla::gfx::FeatureStatus GetD3D11Status() const; + mozilla::gfx::FeatureStatus GetD2DStatus() const; mozilla::gfx::FeatureStatus GetD2D1Status() const; unsigned GetD3D11Version(); @@ -306,12 +319,14 @@ private: void InitializeDevices(); void InitializeD3D11(); void InitializeD2D(); + void InitializeD2D1(); bool InitDWriteSupport(); void DisableD2D(); mozilla::gfx::FeatureStatus CheckAccelerationSupport(); mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware); + mozilla::gfx::FeatureStatus CheckD2DSupport(); mozilla::gfx::FeatureStatus CheckD2D1Support(); mozilla::gfx::FeatureStatus AttemptD3D11DeviceCreation(); @@ -337,12 +352,14 @@ private: IDXGIAdapter1 *GetDXGIAdapter(); bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason); +#ifdef CAIRO_HAS_DWRITE_FONT RefPtr mDWriteFactory; RefPtr mRenderingParams[TEXT_RENDERING_COUNT]; DWRITE_MEASURING_MODE mMeasuringMode; - +#endif RefPtr mAdapter; RefPtr mDeviceManager; + RefPtr mD3D10Device; RefPtr mD3D11Device; RefPtr mD3D11ContentDevice; RefPtr mD3D11ImageBridgeDevice; @@ -357,6 +374,7 @@ private: // accessors instead. mozilla::gfx::FeatureStatus mAcceleration; mozilla::gfx::FeatureStatus mD3D11Status; + mozilla::gfx::FeatureStatus mD2DStatus; mozilla::gfx::FeatureStatus mD2D1Status; nsTArray mFeatureLevels; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 047c67f1391..1082106d72d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -713,8 +713,8 @@ pref("gfx.font_rendering.opentype_svg.enabled", true); #ifdef XP_WIN // comma separated list of backends to use in order of preference // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo"); -pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo"); -pref("gfx.content.azure.backends", "direct2d1.1,cairo"); +pref("gfx.canvas.azure.backends", "direct2d1.1,direct2d,skia,cairo"); +pref("gfx.content.azure.backends", "direct2d1.1,direct2d,cairo"); #else #ifdef XP_MACOSX pref("gfx.content.azure.backends", "cg"); @@ -4447,6 +4447,7 @@ pref("gfx.content.use-native-pushlayer", true); // Whether to disable the automatic detection and use of direct2d. pref("gfx.direct2d.disabled", false); +pref("gfx.direct2d.use1_1", true); // Whether to attempt to enable Direct2D regardless of automatic detection or // blacklisting diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index acadd920d52..6c8e220af45 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1272,12 +1272,14 @@ GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle aObj) JS_SetProperty(aCx, obj, "blacklisted", val); } - gfx::FeatureStatus d2d = platform->GetD2D1Status(); + gfx::FeatureStatus d2d = platform->GetD2DStatus(); if (!InitFeatureObject(aCx, aObj, "d2d", d2d, &obj)) { return; } { - const char* version = "1.1"; + const char* version = "1.0"; + if (platform->GetD2D1Status() == gfx::FeatureStatus::Available) + version = "1.1"; JS::Rooted str(aCx, JS_NewStringCopyZ(aCx, version)); JS::Rooted val(aCx, JS::StringValue(str)); JS_SetProperty(aCx, obj, "version", val); From 64bcc1ca6dab1cfaa01bb9a87b96f1c344847c44 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 08:49:50 +0100 Subject: [PATCH 185/187] Backed out changeset 9320c843aa7e (bug 1247775) for breaking win 8 reftests --- gfx/layers/client/ContentClient.cpp | 2 +- gfx/layers/d3d11/TextureD3D11.cpp | 267 +++++++++++++++++++++++++++- gfx/layers/d3d11/TextureD3D11.h | 42 +++++ 3 files changed, 309 insertions(+), 2 deletions(-) diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 92649e4c691..434f1b4b316 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -73,7 +73,7 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) #ifdef XP_WIN if (backend == LayersBackend::LAYERS_D3D11) { - useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->GetRenderMode() == gfxWindowsPlatform::RENDER_DIRECT2D; + useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); } else #endif #ifdef MOZ_WIDGET_GTK diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 659f385c308..60074f4fe38 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -253,6 +253,31 @@ D3D11TextureData::~D3D11TextureData() #endif } +D3D10TextureData::D3D10TextureData(ID3D10Texture2D* aTexture, + gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + bool aNeedsClear, bool aNeedsClearWhite, + bool aIsForOutOfBandContent) +: DXGITextureData(aSize, aFormat, aNeedsClear, aNeedsClearWhite, aIsForOutOfBandContent) +, mTexture(aTexture) +{ + MOZ_ASSERT(aTexture); + mHasSynchronization = HasKeyedMutex(aTexture); +} + +D3D10TextureData::~D3D10TextureData() +{ +#ifdef DEBUG + // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is + // when it calls EndDraw. This EndDraw should not execute anything so it + // shouldn't -really- need the lock but the debug layer chokes on this. + if (mDrawTarget) { + Lock(OpenMode::OPEN_NONE, nullptr); + mDrawTarget = nullptr; + Unlock(); + } +#endif +} + bool D3D11TextureData::Lock(OpenMode aMode, FenceHandle*) { @@ -270,6 +295,23 @@ D3D11TextureData::Lock(OpenMode aMode, FenceHandle*) return true; } +bool +D3D10TextureData::Lock(OpenMode aMode, FenceHandle*) +{ + if (!LockD3DTexture(mTexture.get())) { + return false; + } + + if (NS_IsMainThread() && !mIsForOutOfBandContent) { + if (!PrepareDrawTargetInLock(aMode)) { + Unlock(); + return false; + } + } + + return true; +} + bool DXGITextureData::PrepareDrawTargetInLock(OpenMode aMode) { @@ -300,6 +342,12 @@ D3D11TextureData::Unlock() UnlockD3DTexture(mTexture.get()); } +void +D3D10TextureData::Unlock() +{ + UnlockD3DTexture(mTexture.get()); +} + void D3D11TextureData::SyncWithObject(SyncObject* aSyncObject) { @@ -313,6 +361,19 @@ D3D11TextureData::SyncWithObject(SyncObject* aSyncObject) sync->RegisterTexture(mTexture); } +void +D3D10TextureData::SyncWithObject(SyncObject* aSyncObject) +{ + if (!aSyncObject || !NS_IsMainThread()) { + // When off the main thread we sync using a keyed mutex per texture. + return; + } + + MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11); + SyncObjectD3D11* sync = static_cast(aSyncObject); + sync->RegisterTexture(mTexture); +} + bool DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { @@ -332,6 +393,34 @@ DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor) return true; } +bool +D3D10TextureData::ReadBack(TextureReadbackSink* aReadbackSinc) +{ + if (NS_IsMainThread() && aReadbackSinc && mTexture) { + ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); + + D3D10_TEXTURE2D_DESC desc; + mTexture->GetDesc(&desc); + desc.BindFlags = 0; + desc.Usage = D3D10_USAGE_STAGING; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; + desc.MiscFlags = 0; + + RefPtr tex; + HRESULT hr = device->CreateTexture2D(&desc, nullptr, getter_AddRefs(tex)); + + if (SUCCEEDED(hr)) { + device->CopyResource(tex, mTexture); + gfxWindowsPlatform::GetPlatform()->GetReadbackManager()->PostTask(tex, aReadbackSinc); + } else { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D3D11] CreateTexture2D failure " << mSize << " Code: " << gfx::hexa(hr); + aReadbackSinc->ProcessReadback(nullptr); + } + } + + return true; +} + DXGITextureData* DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags) { @@ -340,7 +429,19 @@ DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationF return nullptr; } - return D3D11TextureData::Create(aSize, aFormat, aFlags); + gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform(); + // When we're not on the main thread we're not going to be using Direct2D + // to access the contents of this texture client so we will always use D3D11. + bool useD3D11 = + windowsPlatform->GetContentBackendFor(LayersBackend::LAYERS_D3D11) == BackendType::DIRECT2D1_1 || + !NS_IsMainThread() || + (aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT); + + if (useD3D11) { + return D3D11TextureData::Create(aSize, aFormat, aFlags); + } else { + return D3D10TextureData::Create(aSize, aFormat, aFlags); + } } DXGITextureData* @@ -406,12 +507,59 @@ D3D11TextureData::CreateSimilar(ISurfaceAllocator* aAllocator, return D3D11TextureData::Create(mSize, mFormat, aAllocFlags); } +DXGITextureData* +D3D10TextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags) +{ + RefPtr texture10; + ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); + + CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM, + aSize.width, aSize.height, 1, 1, + D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE); + + newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED; + + HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(texture10)); + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) + << "[D3D10] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr); + return nullptr; + } + texture10->SetPrivateDataInterface(sD3D11TextureUsage, + new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4)); + + return new D3D10TextureData(texture10, aSize, aFormat, + aFlags & ALLOC_CLEAR_BUFFER, + aFlags & ALLOC_CLEAR_BUFFER_WHITE, + false /* aIsForOutOfBandContent */); +} + +void +D3D10TextureData::Deallocate(ISurfaceAllocator* aAllocator) +{ + mTexture = nullptr; +} + +TextureData* +D3D10TextureData::CreateSimilar(ISurfaceAllocator* aAllocator, + TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + return D3D10TextureData::Create(mSize, mFormat, aAllocFlags); +} + void D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource) { mTexture->QueryInterface(aOutResource); } +void +D3D10TextureData::GetDXGIResource(IDXGIResource** aOutResource) +{ + mTexture->QueryInterface(aOutResource); +} + DXGIYCbCrTextureData* DXGIYCbCrTextureData::Create(ISurfaceAllocator* aAllocator, TextureFlags aFlags, @@ -560,6 +708,23 @@ D3D11TextureData::BorrowDrawTarget() return result.forget(); } +already_AddRefed +D3D10TextureData::BorrowDrawTarget() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mDrawTarget && mTexture) { + // This may return a null DrawTarget + mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, mFormat); + if (!mDrawTarget) { + gfxCriticalNote << "Could not borrow DrawTarget (D3D10) " << (int)mFormat; + } + } + + RefPtr result = mDrawTarget; + return result.forget(); +} + bool D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) { @@ -593,6 +758,39 @@ D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) return true; } +bool +D3D10TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) +{ + RefPtr srcSurf = aSurface->GetDataSurface(); + + if (!srcSurf) { + gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (D3D10)."; + return false; + } + + DataSourceSurface::MappedSurface sourceMap; + if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) { + gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (D3D10)."; + return false; + } + + RefPtr device; + mTexture->GetDevice(getter_AddRefs(device)); + + D3D10_BOX box; + box.front = 0; + box.back = 1; + box.top = box.left = 0; + box.right = aSurface->GetSize().width; + box.bottom = aSurface->GetSize().height; + + device->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0); + + srcSurf->Unmap(); + + return true; +} + DXGITextureHostD3D11::DXGITextureHostD3D11(TextureFlags aFlags, const SurfaceDescriptorD3D10& aDescriptor) : TextureHost(aFlags) @@ -1029,11 +1227,47 @@ SyncObjectD3D11::RegisterTexture(ID3D11Texture2D* aTexture) mD3D11SyncedTextures.push_back(aTexture); } +void +SyncObjectD3D11::RegisterTexture(ID3D10Texture2D* aTexture) +{ + mD3D10SyncedTextures.push_back(aTexture); +} + void SyncObjectD3D11::FinalizeFrame() { HRESULT hr; + if (!mD3D10Texture && mD3D10SyncedTextures.size()) { + ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); + if (!device) { + return; + } + + hr = device->OpenSharedResource(mHandle, __uuidof(ID3D10Texture2D), (void**)(ID3D10Texture2D**)getter_AddRefs(mD3D10Texture)); + + if (FAILED(hr) || !mD3D10Texture) { + gfxCriticalError() << "Failed to D3D10 OpenSharedResource for frame finalization: " << hexa(hr); + + if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) { + return; + } + + gfxDevCrash(LogReason::D3D10FinalizeFrame) << "Without device reset: " << hexa(hr); + } + + // test QI + RefPtr mutex; + hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); + + if (FAILED(hr) || !mutex) { + // Leave both the critical error and MOZ_CRASH for now; the critical error lets + // us "save" the hr value. We will probably eventuall replace this with gfxDevCrash. + gfxCriticalError() << "Failed to get KeyedMutex (1): " << hexa(hr); + MOZ_CRASH("GFX: Cannot get D3D10 KeyedMutex"); + } + } + if (!mD3D11Texture && mD3D11SyncedTextures.size()) { ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice(); @@ -1061,6 +1295,37 @@ SyncObjectD3D11::FinalizeFrame() } } + if (mD3D10SyncedTextures.size()) { + RefPtr mutex; + hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); + hr = mutex->AcquireSync(0, 20000); + + if (hr == WAIT_TIMEOUT) { + if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) { + gfxWarning() << "AcquireSync timed out because of device reset."; + return; + } + gfxDevCrash(LogReason::D3D10SyncLock) << "Timeout on the D3D10 sync lock"; + } + + D3D10_BOX box; + box.front = box.top = box.left = 0; + box.back = box.bottom = box.right = 1; + + ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); + if (!device) { + return; + } + + for (auto iter = mD3D10SyncedTextures.begin(); iter != mD3D10SyncedTextures.end(); iter++) { + device->CopySubresourceRegion(mD3D10Texture, 0, 0, 0, 0, *iter, 0, &box); + } + + mutex->ReleaseSync(0); + + mD3D10SyncedTextures.clear(); + } + if (mD3D11SyncedTextures.size()) { RefPtr mutex; hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index 7ec59cfbf7d..f4bc9f69582 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -109,6 +109,45 @@ CreateD3D11extureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat ID3D11Device* aDevice, ISurfaceAllocator* aAllocator); + +class D3D10TextureData : public DXGITextureData +{ +public: + static DXGITextureData* + Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags); + + virtual bool Lock(OpenMode aMode, FenceHandle*) override; + + virtual void Unlock() override; + + virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override; + + virtual already_AddRefed BorrowDrawTarget() override; + + virtual TextureData* + CreateSimilar(ISurfaceAllocator* aAllocator, + TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const override; + + // TODO - merge this with the FenceHandle API! + virtual void SyncWithObject(SyncObject* aSync) override; + + virtual bool ReadBack(TextureReadbackSink* aReadbackSinc) override; + + virtual void Deallocate(ISurfaceAllocator* aAllocator) override; + + ~D3D10TextureData(); +protected: + D3D10TextureData(ID3D10Texture2D* aTexture, + gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + bool aNeedsClear, bool aNeedsClearWhite, + bool aIsForOutOfBandContent); + + virtual void GetDXGIResource(IDXGIResource** aOutResource) override; + + RefPtr mTexture; +}; + class DXGIYCbCrTextureData : public TextureData { public: @@ -392,9 +431,12 @@ public: virtual SyncType GetSyncType() { return SyncType::D3D11; } void RegisterTexture(ID3D11Texture2D* aTexture); + void RegisterTexture(ID3D10Texture2D* aTexture); private: RefPtr mD3D11Texture; + RefPtr mD3D10Texture; + std::vector mD3D10SyncedTextures; std::vector mD3D11SyncedTextures; SyncHandle mHandle; }; From c7c83d7d7d1d327a665e94a63fc963261a558752 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 15:14:36 +0100 Subject: [PATCH 186/187] Backed out changeset 306cf0271d3e (bug 1119520) for causing merge conflicts with m-i to m-c merge --- configure.in | 24 +----------- mobile/android/base/Makefile.in | 46 +++-------------------- mobile/android/mach_commands.py | 10 +++++ mobile/android/moz.build | 7 ++-- python/mozbuild/mozbuild/mozconfig.py | 2 +- testing/instrumentation/Makefile.in | 2 - testing/mochitest/Makefile.in | 10 ++--- toolkit/mozapps/installer/upload-files.mk | 26 ++++--------- 8 files changed, 31 insertions(+), 96 deletions(-) diff --git a/configure.in b/configure.in index 4f0967538b7..aedfd392a0c 100644 --- a/configure.in +++ b/configure.in @@ -3758,7 +3758,6 @@ MOZ_SCTP= MOZ_ANDROID_OMX= MOZ_MEDIA_NAVIGATOR= MOZ_OMX_PLUGIN= -MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE= MOZ_VPX_ERROR_CONCEALMENT= MOZ_WEBSPEECH=1 MOZ_WEBSPEECH_MODELS= @@ -5367,26 +5366,8 @@ if test -n "$MOZ_OMX_PLUGIN"; then dnl Only allow building OMX plugin on Gonk (B2G) or Android AC_DEFINE(MOZ_OMX_PLUGIN) else - dnl fail if we're not building on Gonk or Android - AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android]) - fi -fi - -dnl ======================================================== -dnl = Enable building mobile/android with Gradle -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(gradle-mobile-android-builds, -[ --enable-gradle-mobile-android-builds Enable building mobile/android with Gradle], - MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=1, - MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=) - -if test -n "$MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE"; then - if test "$OS_TARGET" = "Android" -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then - dnl Only allow building mobile/android with Gradle. - AC_DEFINE(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE) - else - dnl fail if we're not building mobile/android. - AC_MSG_ERROR([Can only build mobile/android with Gradle]) + dnl fail if we're not building on Gonk or Android + AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android]) fi fi @@ -8926,7 +8907,6 @@ AC_SUBST(MOZ_DIRECTSHOW) AC_SUBST(MOZ_ANDROID_OMX) AC_SUBST(MOZ_APPLEMEDIA) AC_SUBST(MOZ_OMX_PLUGIN) -AC_SUBST(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE) AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT) AC_SUBST(VPX_AS) AC_SUBST_LIST(VPX_ASFLAGS) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index ee2843a5265..0a455191c09 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -2,12 +2,6 @@ # 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/. -# We call mach -> Make -> gradle -> mach, which races to find and -# create .mozconfig files and to generate targets. -ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -.NOTPARALLEL: -endif - MOZ_BUILDID := $(shell cat $(DEPTH)/config/buildid) # Set the appropriate version code, based on the existance of the @@ -207,21 +201,9 @@ endif # MOZ_INSTALL_TRACKING library_jars := $(subst $(NULL) ,:,$(strip $(library_jars))) -gradle_dir := $(topobjdir)/gradle/build/mobile/android - -ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -.gradle.deps: .aapt.deps FORCE - @$(TOUCH) $@ - $(topsrcdir)/mach gradle --no-daemon --offline app:dexAutomationDebug app:assembleAutomationDebugAndroidTest -x lint - -classes.dex: .gradle.deps - $(REPORT_BUILD) - cp $(gradle_dir)/app/intermediates/dex/automation/debug/classes.dex $@ -else classes.dex: .proguard.deps $(REPORT_BUILD) $(DX) --dex --output=classes.dex jars-proguarded -endif ifdef MOZ_DISABLE_PROGUARD PROGUARD_PASSES=0 @@ -519,13 +501,8 @@ endef # .aapt.deps: $(all_resources) $(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./)) -ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -.aapt.nodeps: FORCE - cp $(gradle_dir)/app/intermediates/res/resources-automation-debug.ap_ gecko-nodeps.ap_ -else -# .aapt.nodeps: $(CURDIR)/AndroidManifest.xml FORCE -$(eval $(call aapt_command,.aapt.nodeps,$(CURDIR)/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) -endif +# .aapt.nodeps: $(abspath $(CURDIR)/AndroidManifest.xml) FORCE +$(eval $(call aapt_command,.aapt.nodeps,$(abspath $(CURDIR)/AndroidManifest.xml) FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk @@ -555,14 +532,7 @@ gradle-targets: $(foreach f,$(constants_PP_JAVAFILES),$(f)) gradle-targets: $(abspath AndroidManifest.xml) gradle-targets: $(ANDROID_GENERATED_RESFILES) -ifndef MOZILLA_OFFICIAL -# Local developers update omni.ja during their builds. There's a -# chicken-and-egg problem here. -gradle-omnijar: $(abspath $(DIST)/fennec/$(OMNIJAR_NAME)) -else -# In automation, omni.ja is built only during packaging. -gradle-omnijar: -endif +gradle-omnijar: $(ABS_DIST)/fennec/$(OMNIJAR_NAME) .PHONY: gradle-targets gradle-omnijar @@ -573,8 +543,8 @@ endif # GeneratedJNIWrappers.cpp target also generates # GeneratedJNIWrappers.h and GeneratedJNINatives.h -ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -libs:: jni-stubs.inc GeneratedJNIWrappers.cpp +libs:: classes.dex jni-stubs.inc GeneratedJNIWrappers.cpp $(CURDIR)/fennec_ids.txt + $(INSTALL) classes.dex $(FINAL_TARGET) @(diff jni-stubs.inc $(topsrcdir)/mozglue/android/jni-stubs.inc >/dev/null && \ diff GeneratedJNIWrappers.cpp $(topsrcdir)/widget/android/GeneratedJNIWrappers.cpp >/dev/null && \ diff GeneratedJNIWrappers.h $(topsrcdir)/widget/android/GeneratedJNIWrappers.h >/dev/null && \ @@ -588,9 +558,3 @@ libs:: jni-stubs.inc GeneratedJNIWrappers.cpp echo '* Repeat the build, and check in any changes. *' && \ echo '*****************************************************' && \ exit 1) -endif - -libs:: $(CURDIR)/fennec_ids.txt - -libs:: classes.dex - $(INSTALL) classes.dex $(FINAL_TARGET) diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py index 11cafbdcbbb..329ca25e502 100644 --- a/mobile/android/mach_commands.py +++ b/mobile/android/mach_commands.py @@ -21,6 +21,16 @@ from mach.decorators import ( Command, ) +SUCCESS = ''' +You should be ready to build with Gradle and import into IntelliJ! Test with + + ./mach gradle build + +and in IntelliJ select File > Import project... and choose + + {topobjdir}/mobile/android/gradle +''' + # NOTE python/mach/mach/commands/commandinfo.py references this function # by name. If this function is renamed or removed, that file should diff --git a/mobile/android/moz.build b/mobile/android/moz.build index d7d6eb6f2af..049293ed682 100644 --- a/mobile/android/moz.build +++ b/mobile/android/moz.build @@ -31,9 +31,8 @@ if CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']: DIRS += ['../../xulrunner/tools/redit'] -if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: - TEST_DIRS += [ - 'tests', - ] +TEST_DIRS += [ + 'tests', +] SPHINX_TREES['fennec'] = 'docs' diff --git a/python/mozbuild/mozbuild/mozconfig.py b/python/mozbuild/mozbuild/mozconfig.py index a60b79774dd..2e829422d8d 100644 --- a/python/mozbuild/mozbuild/mozconfig.py +++ b/python/mozbuild/mozbuild/mozconfig.py @@ -107,7 +107,7 @@ class MozconfigLoader(ProcessExecutionMixin): if 'MOZ_MYCONFIG' in env: raise MozconfigFindException(MOZ_MYCONFIG_ERROR) - env_path = env.get('MOZCONFIG', None) or None + env_path = env.get('MOZCONFIG', None) if env_path is not None: if not os.path.isabs(env_path): potential_roots = [self.topsrcdir, os.getcwd()] diff --git a/testing/instrumentation/Makefile.in b/testing/instrumentation/Makefile.in index 0ccb15a2c8f..6b1c225c87b 100644 --- a/testing/instrumentation/Makefile.in +++ b/testing/instrumentation/Makefile.in @@ -14,12 +14,10 @@ include $(topsrcdir)/config/android-common.mk stage-package: $(NSINSTALL) -D $(_DEST_DIR) -ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE $(call RELEASE_SIGN_ANDROID_APK,\ $(DEPTH)/mobile/android/tests/background/junit3/background-junit3-debug-unsigned-unaligned.apk,\ $(_DEST_DIR)/background-junit3.apk) $(call RELEASE_SIGN_ANDROID_APK,\ $(DEPTH)/mobile/android/tests/browser/junit3/browser-junit3-debug-unsigned-unaligned.apk,\ $(_DEST_DIR)/browser-junit3.apk) -endif @(cd $(DEPTH)/_tests/ && tar $(TAR_CREATE_FLAGS) - instrumentation) | (cd $(PKG_STAGE) && tar -xf -) diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index a0448dcd7bb..e4eedf120bf 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -107,15 +107,11 @@ $(_DEST_DIR): ifeq ($(MOZ_BUILD_APP),mobile/android) include $(topsrcdir)/config/android-common.mk -ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -robocop_apk := $(topobjdir)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk -else -robocop_apk := $(topobjdir)/gradle/build/mobile/android/app/outputs/apk/app-automation-debug-androidTest-unaligned.apk -endif - stage-package-android: $(NSINSTALL) -D $(_DEST_DIR) - $(call RELEASE_SIGN_ANDROID_APK,$(robocop_apk),$(_DEST_DIR)/robocop.apk) + $(call RELEASE_SIGN_ANDROID_APK,\ + $(DEPTH)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk,\ + $(_DEST_DIR)/robocop.apk) stage-package: stage-package-android endif diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index d4297fa3fd8..9cfe53c11c3 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -325,18 +325,13 @@ UPLOAD_EXTRA_FILES += geckoview_library/geckoview_assets.zip # Robocop/Robotium tests, Android Background tests, and Fennec need to # be signed with the same key, which means release signing them all. -ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -robocop_apk := $(topobjdir)/mobile/android/tests/browser/robocop/robocop-debug-unsigned-unaligned.apk -else -robocop_apk := $(topobjdir)/gradle/build/mobile/android/app/outputs/apk/app-automation-debug-androidTest-unaligned.apk -endif - +ROBOCOP_PATH = $(topobjdir)/mobile/android/tests/browser/robocop # Normally, $(NSINSTALL) would be used instead of cp, but INNER_ROBOCOP_PACKAGE # is used in a series of commands that run under a "cd something", while # $(NSINSTALL) is relative. INNER_ROBOCOP_PACKAGE= \ cp $(GECKO_APP_AP_PATH)/fennec_ids.txt $(ABS_DIST) && \ - $(call RELEASE_SIGN_ANDROID_APK,$(robocop_apk),$(ABS_DIST)/robocop.apk) + $(call RELEASE_SIGN_ANDROID_APK,$(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk,$(ABS_DIST)/robocop.apk) endif else INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Android Robocop for you' @@ -475,15 +470,6 @@ PKG_SUFFIX = .apk INNER_SZIP_LIBRARIES = \ $(if $(ALREADY_SZIPPED),,$(foreach lib,$(SZIP_LIBRARIES),host/bin/szip $(MOZ_SZIP_FLAGS) $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/$(lib) && )) true -ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE -INNER_CHECK_R_TXT=echo 'No R.txt checking for you!' -else -INNER_CHECK_R_TXT=\ - ((test ! -f $(GECKO_APP_AP_PATH)/R.txt && echo "*** Warning: The R.txt that is being packaged might not agree with the R.txt that was built. This is normal during l10n repacks.") || \ - diff $(GECKO_APP_AP_PATH)/R.txt $(GECKO_APP_AP_PATH)/gecko-nodeps/R.txt >/dev/null || \ - (echo "*** Error: The R.txt that was built and the R.txt that is being packaged are not the same. Rebuild mobile/android/base and re-package." && exit 1)) -endif - # Insert $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex into # $(ABS_DIST)/gecko.ap_, producing $(ABS_DIST)/gecko.apk. INNER_MAKE_APK = \ @@ -504,11 +490,13 @@ INNER_MAKE_APK = \ $(ZIPALIGN) -f -v 4 $(ABS_DIST)/gecko.apk $(PACKAGE) ifeq ($(MOZ_BUILD_APP),mobile/android) -INNER_MAKE_PACKAGE = \ +INNER_MAKE_PACKAGE = \ $(INNER_SZIP_LIBRARIES) && \ make -C $(GECKO_APP_AP_PATH) gecko-nodeps.ap_ && \ cp $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ $(ABS_DIST)/gecko.ap_ && \ - $(INNER_CHECK_R_TXT) && \ + ( (test ! -f $(GECKO_APP_AP_PATH)/R.txt && echo "*** Warning: The R.txt that is being packaged might not agree with the R.txt that was built. This is normal during l10n repacks.") || \ + diff $(GECKO_APP_AP_PATH)/R.txt $(GECKO_APP_AP_PATH)/gecko-nodeps/R.txt >/dev/null || \ + (echo "*** Error: The R.txt that was built and the R.txt that is being packaged are not the same. Rebuild mobile/android/base and re-package." && exit 1)) && \ $(INNER_MAKE_APK) && \ $(INNER_ROBOCOP_PACKAGE) && \ $(INNER_INSTALL_BOUNCER_PACKAGE) && \ @@ -517,7 +505,7 @@ INNER_MAKE_PACKAGE = \ endif ifeq ($(MOZ_BUILD_APP),mobile/android/b2gdroid) -INNER_MAKE_PACKAGE = \ +INNER_MAKE_PACKAGE = \ $(INNER_SZIP_LIBRARIES) && \ cp $(topobjdir)/mobile/android/b2gdroid/app/classes.dex $(ABS_DIST)/classes.dex && \ cp $(topobjdir)/mobile/android/b2gdroid/app/b2gdroid-unsigned-unaligned.apk $(ABS_DIST)/gecko.ap_ && \ From 7ffe91262a0fa24c52ef9da8f3ed0766e718e140 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 12 Feb 2016 17:11:52 +0100 Subject: [PATCH 187/187] Backed out changeset 88735739af7a (bug 1244893) for causing failing Initial decision task for mozilla-central --- testing/mozharness/configs/b2g/releng-fota-updates.py | 2 +- .../mozharness/configs/b2g/taskcluster-spark-ota-balrog.py | 2 +- testing/mozharness/configs/b2g/taskcluster-spark-ota.py | 1 + testing/taskcluster/scripts/phone-builder/build-phone-ota.sh | 4 ++++ .../tasks/builds/b2g_aries_spark_ota_balrog_debug.yml | 2 ++ .../tasks/builds/b2g_aries_spark_ota_balrog_opt.yml | 2 ++ testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml | 2 +- testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml | 2 +- testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml | 4 +++- testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml | 4 +++- 10 files changed, 19 insertions(+), 6 deletions(-) diff --git a/testing/mozharness/configs/b2g/releng-fota-updates.py b/testing/mozharness/configs/b2g/releng-fota-updates.py index 9162a13f52b..f135db39ffc 100644 --- a/testing/mozharness/configs/b2g/releng-fota-updates.py +++ b/testing/mozharness/configs/b2g/releng-fota-updates.py @@ -14,7 +14,7 @@ config = { # bug 1222227 - temporarily disable for S3 migration # 'make-socorro-json', # 'upload-source-manifest', - # 'submit-to-balrog', + 'submit-to-balrog', ], "upload": { "default": { diff --git a/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py b/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py index 7a089b7738d..797e96f39fa 100644 --- a/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py +++ b/testing/mozharness/configs/b2g/taskcluster-spark-ota-balrog.py @@ -8,7 +8,7 @@ config = { 'build-symbols', 'make-updates', 'prep-upload', - #'submit-to-balrog' + 'submit-to-balrog' ], "balrog_credentials_file": "balrog_credentials", "nightly_build": True, diff --git a/testing/mozharness/configs/b2g/taskcluster-spark-ota.py b/testing/mozharness/configs/b2g/taskcluster-spark-ota.py index 9319d26ac9e..d4e92790483 100644 --- a/testing/mozharness/configs/b2g/taskcluster-spark-ota.py +++ b/testing/mozharness/configs/b2g/taskcluster-spark-ota.py @@ -9,6 +9,7 @@ config = { 'make-updates', 'prep-upload' ], + "balrog_credentials_file": "balrog_credentials", "nightly_build": True, "env": { "GAIA_OPTIMIZE": "1", diff --git a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh index 7c1f44f5dc2..1981513cf20 100755 --- a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh +++ b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh @@ -10,6 +10,8 @@ fi PLATFORM=${TARGET%%-*} +aws s3 cp s3://b2g-nightly-credentials/balrog_credentials . + # We need different platform names for each variant (user, userdebug and # eng). We do not append variant suffix for "user" to keep compability with # verions already installed in the phones. @@ -18,12 +20,14 @@ if [ 0$DOGFOOD -ne 1 -a $VARIANT != "user" ]; then fi MOZHARNESS_CONFIG=${MOZHARNESS_CONFIG:=b2g/taskcluster-phone-ota.py} +BALROG_SERVER_CONFIG=${BALROG_SERVER_CONFIG:=balrog/docker-worker.py} rm -rf $WORKSPACE/B2G/upload-public/ rm -rf $WORKSPACE/B2G/upload/ $WORKSPACE/gecko/testing/mozharness/scripts/b2g_build.py \ --config $MOZHARNESS_CONFIG \ + --config $BALROG_SERVER_CONFIG \ "$debug_flag" \ --disable-mock \ --variant=$VARIANT \ diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml index e516aca68be..78e5658b02d 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_debug.yml @@ -2,8 +2,10 @@ $inherits: from: 'tasks/builds/b2g_aries_spark_ota_debug.yml' task: scopes: + - 'docker-worker:feature:balrogVPNProxy' payload: features: + balrogVPNProxy: true env: MOZHARNESS_CONFIG: b2g/taskcluster-spark-ota-balrog.py diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml index 5caeb748009..0405d1030f9 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_balrog_opt.yml @@ -2,7 +2,9 @@ $inherits: from: 'tasks/builds/b2g_aries_spark_ota_opt.yml' task: scopes: + - 'docker-worker:feature:balrogVPNProxy' payload: features: + balrogVPNProxy: true env: MOZHARNESS_CONFIG: b2g/taskcluster-spark-ota-balrog.py diff --git a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml index 96a1ecda8ce..fd7fb60cdb9 100644 --- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_base.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_phone_base.yml' task: - workerType: flame-kk + workerType: balrog payload: env: diff --git a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml index 78df46b33af..6cd485f87c9 100644 --- a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_base.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_phone_base.yml' task: - workerType: flame-kk + workerType: balrog payload: env: TARGET: 'flame-kk' diff --git a/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml b/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml index 863d2aebe4f..dde8124659b 100644 --- a/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_nexus_4_kk_ota_debug.yml @@ -4,15 +4,17 @@ $inherits: build_name: 'nexus-4-kk-ota' build_type: 'debug' task: - workerType: flame-kk + workerType: balrog metadata: name: '[TC] B2G Nexus 4 KK OTA (userdebug)' scopes: - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug' - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug-objdir-gecko' + - 'docker-worker:feature:balrogVPNProxy' payload: features: + balrogVPNProxy: true cache: level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug: /home/worker/workspace level-{{level}}-{{project}}-build-nexus-4-kk-ota-debug-objdir-gecko: /home/worker/objdir-gecko diff --git a/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml b/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml index f5c6c22aaa6..b419c27ef48 100644 --- a/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_nexus_5l_ota_debug.yml @@ -4,15 +4,17 @@ $inherits: build_name: 'nexus-5-l-ota' build_type: 'debug' task: - workerType: flame-kk + workerType: balrog metadata: name: '[TC] B2G Nexus 5L OTA (userdebug)' scopes: - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-5l-ota-debug' - 'docker-worker:cache:level-{{level}}-{{project}}-build-nexus-5l-ota-debug-objdir-gecko' + - 'docker-worker:feature:balrogVPNProxy' payload: features: + balrogVPNProxy: true cache: level-{{level}}-{{project}}-build-nexus-5l-ota-debug: /home/worker/workspace level-{{level}}-{{project}}-build-nexus-5l-ota-debug-objdir-gecko: /home/worker/objdir-gecko