From 58895ea9e538e4d29130733ce6ed4c794a4f2076 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Sun, 17 Jun 2018 23:48:50 +0800 Subject: [PATCH] cherry-picked mozilla upstream changes: bug1355414, bug1313977, bug1357366, bug1362889, bug1152353, bug1345893, bug1343172, bug1352348, bug1356843, bug1354308, bug1355340, bug1360574, bug1358776, bug1304566, bug1334097, bug1338574 --- dom/file/ipc/Blob.cpp | 4 + dom/filesystem/FileSystemSecurity.cpp | 10 +- dom/filesystem/compat/tests/script_entries.js | 2 +- dom/filesystem/compat/tests/test_basic.html | 8 +- dom/media/MediaShutdownManager.cpp | 9 +- dom/media/webrtc/RTCCertificate.cpp | 2 +- dom/quota/ActorsParent.cpp | 12 ++ dom/xml/nsXMLFragmentContentSink.cpp | 2 +- editor/composer/nsEditorSpellCheck.cpp | 4 + .../apz/public/GoannaContentController.h | 2 + gfx/layers/apz/src/APZCTreeManager.cpp | 23 ++- gfx/layers/apz/src/APZCTreeManager.h | 2 + gfx/layers/apz/test/gtest/APZCBasicTester.h | 1 + .../apz/test/gtest/APZCTreeManagerTester.h | 1 + gfx/layers/apz/test/gtest/APZTestCommon.h | 5 + gfx/layers/apz/util/APZCCallbackHelper.cpp | 9 + gfx/layers/apz/util/APZCCallbackHelper.h | 2 + .../apz/util/ChromeProcessController.cpp | 13 ++ gfx/layers/apz/util/ChromeProcessController.h | 1 + .../apz/util/ContentProcessController.cpp | 6 + .../apz/util/ContentProcessController.h | 2 + gfx/layers/ipc/APZChild.cpp | 7 + gfx/layers/ipc/APZChild.h | 2 + gfx/layers/ipc/PAPZ.ipdl | 2 + gfx/layers/ipc/RemoteContentController.cpp | 15 ++ gfx/layers/ipc/RemoteContentController.h | 2 + gfx/skia/skia/src/core/SkBitmapProcState.cpp | 2 +- .../core/SkBitmapProcState_matrixProcs.cpp | 2 +- ipc/glue/BackgroundChildImpl.cpp | 4 +- ipc/glue/MessageChannel.cpp | 112 ++++++----- ipc/glue/MessageChannel.h | 1 + js/src/gc/Memory.cpp | 22 +-- js/src/jsarray.cpp | 2 +- js/src/shell/js.cpp | 5 +- layout/generic/nsGfxScrollFrame.cpp | 28 +++ layout/generic/nsGfxScrollFrame.h | 10 + layout/generic/nsIScrollableFrame.h | 2 + layout/xul/nsSliderFrame.cpp | 71 +++++-- layout/xul/nsSliderFrame.h | 10 +- memory/replace/logalloc/replay/moz.build | 3 + mfbt/Assertions.cpp | 55 +++++- mfbt/Assertions.h | 72 ++++++- netwerk/ipc/NeckoParent.cpp | 3 +- netwerk/protocol/http/nsHttpChannel.cpp | 65 +++++-- netwerk/protocol/http/nsHttpChannel.h | 3 +- .../http/nsHttpChannelAuthProvider.cpp | 4 - netwerk/protocol/http/nsHttpTransaction.cpp | 5 - netwerk/protocol/http/nsHttpTransaction.h | 3 +- .../http/nsIHttpAuthenticableChannel.idl | 6 - .../unit/test_suspend_channel_on_modified.js | 175 ++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 1 + toolkit/components/places/Database.cpp | 50 ++++- toolkit/components/places/Database.h | 6 + .../test_current_from_v34_no_roots.js | 21 +++ .../places/tests/migration/xpcshell.ini | 1 + .../exthandler/nsExternalHelperAppService.cpp | 16 +- .../exthandler/nsExternalHelperAppService.h | 9 + .../exthandler/win/nsOSHelperAppService.cpp | 94 ++++++---- .../exthandler/win/nsOSHelperAppService.h | 2 + widget/cocoa/nsChildView.h | 3 + widget/cocoa/nsChildView.mm | 35 +++- widget/cocoa/nsCocoaUtils.h | 7 + widget/windows/nsAppShell.cpp | 28 +++ widget/windows/nsAppShell.h | 5 + xpcom/ds/nsAtomTable.cpp | 9 +- xpcom/glue/nsTArray.cpp | 15 +- 66 files changed, 876 insertions(+), 239 deletions(-) create mode 100644 netwerk/test/unit/test_suspend_channel_on_modified.js create mode 100644 toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js diff --git a/dom/file/ipc/Blob.cpp b/dom/file/ipc/Blob.cpp index f599200f6..2e1165ea7 100644 --- a/dom/file/ipc/Blob.cpp +++ b/dom/file/ipc/Blob.cpp @@ -978,6 +978,10 @@ RemoteInputStream::SetStream(nsIInputStream* aStream) nsresult RemoteInputStream::BlockAndWaitForStream() { + if (mStream) { + return NS_OK; + } + if (IsOnOwningThread()) { if (NS_IsMainThread()) { NS_WARNING("Blocking the main thread is not supported!"); diff --git a/dom/filesystem/FileSystemSecurity.cpp b/dom/filesystem/FileSystemSecurity.cpp index d7e7778a6..b3d425817 100644 --- a/dom/filesystem/FileSystemSecurity.cpp +++ b/dom/filesystem/FileSystemSecurity.cpp @@ -89,9 +89,17 @@ FileSystemSecurity::ContentProcessHasAccessTo(ContentParentId aId, MOZ_ASSERT(NS_IsMainThread()); AssertIsInMainProcess(); - if (FindInReadable(NS_LITERAL_STRING(".."), aPath)) { +#if defined(XP_WIN) + if (StringBeginsWith(aPath, NS_LITERAL_STRING("..\\")) || + FindInReadable(NS_LITERAL_STRING("\\..\\"), aPath)) { return false; } +#elif defined(XP_UNIX) + if (StringBeginsWith(aPath, NS_LITERAL_STRING("../")) || + FindInReadable(NS_LITERAL_STRING("/../"), aPath)) { + return false; + } +#endif nsTArray* paths; if (!mPaths.Get(aId, &paths)) { diff --git a/dom/filesystem/compat/tests/script_entries.js b/dom/filesystem/compat/tests/script_entries.js index 47f7a71ea..8083214c9 100644 --- a/dom/filesystem/compat/tests/script_entries.js +++ b/dom/filesystem/compat/tests/script_entries.js @@ -28,7 +28,7 @@ addMessageListener("entries.open", function (e) { dir1.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); var file2 = dir1.clone(); - file2.append('bar.txt'); + file2.append('bar..txt'); // Note the double .. file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); var dir2 = dir1.clone(); diff --git a/dom/filesystem/compat/tests/test_basic.html b/dom/filesystem/compat/tests/test_basic.html index 968435f31..85a7418d5 100644 --- a/dom/filesystem/compat/tests/test_basic.html +++ b/dom/filesystem/compat/tests/test_basic.html @@ -176,9 +176,9 @@ function test_directoryEntry_getFile_simple() { } function test_directoryEntry_getFile_deep() { - directoryEntry.getFile("subdir/bar.txt", {}, + directoryEntry.getFile("subdir/bar..txt", {}, function(e) { - is(e.name, "bar.txt", "We have the right FileEntry."); + is(e.name, "bar..txt", "We have the right FileEntry."); test_getParent(e, directoryEntry, /* nested */ true); }, function(e) { ok(false, "This should not happen."); @@ -316,9 +316,9 @@ function test_root_getFile_simple() { } function test_root_getFile_deep() { - fileEntry.filesystem.root.getFile(directoryEntry.name + "/subdir/bar.txt", {}, + fileEntry.filesystem.root.getFile(directoryEntry.name + "/subdir/bar..txt", {}, function(e) { - is(e.name, "bar.txt", "We have the right FileEntry."); + is(e.name, "bar..txt", "We have the right FileEntry."); next(); }, function(e) { ok(false, "This should not happen."); diff --git a/dom/media/MediaShutdownManager.cpp b/dom/media/MediaShutdownManager.cpp index 142c16c8e..482c941db 100644 --- a/dom/media/MediaShutdownManager.cpp +++ b/dom/media/MediaShutdownManager.cpp @@ -74,14 +74,7 @@ MediaShutdownManager::InitStatics() sInstance, NS_LITERAL_STRING(__FILE__), __LINE__, NS_LITERAL_STRING("MediaShutdownManager shutdown")); if (NS_FAILED(rv)) { - // Leak the buffer on the heap to make sure that it lives long enough, - // as MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to - // the end of the program. - const size_t CAPACITY = 256; - auto buf = new char[CAPACITY]; - snprintf(buf, CAPACITY, "Failed to add shutdown blocker! rv=%x", uint32_t(rv)); - MOZ_CRASH_ANNOTATE(buf); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF("Failed to add shutdown blocker! rv=%x", uint32_t(rv)); } } diff --git a/dom/media/webrtc/RTCCertificate.cpp b/dom/media/webrtc/RTCCertificate.cpp index d956d233d..09e458dab 100644 --- a/dom/media/webrtc/RTCCertificate.cpp +++ b/dom/media/webrtc/RTCCertificate.cpp @@ -75,7 +75,7 @@ private: char buf[sizeof(randomName) * 2 + 4]; PL_strncpy(buf, "CN=", 3); for (size_t i = 0; i < sizeof(randomName); ++i) { - snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]); + snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]); } buf[sizeof(buf) - 1] = '\0'; diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 8e44095ec..cebdb2324 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -2458,6 +2458,18 @@ ShutdownObserver::Observe(nsISupports* aSubject, MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID)); MOZ_ASSERT(gInstance); + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; + } + + // Unregister ourselves from the observer service first to make sure the + // nested event loop below will not cause re-entrancy issues. + Unused << + observerService->RemoveObserver(this, + PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID); + QuotaManagerService* qms = QuotaManagerService::Get(); MOZ_ASSERT(qms); diff --git a/dom/xml/nsXMLFragmentContentSink.cpp b/dom/xml/nsXMLFragmentContentSink.cpp index 7c2c1ce61..7fa815c84 100644 --- a/dom/xml/nsXMLFragmentContentSink.cpp +++ b/dom/xml/nsXMLFragmentContentSink.cpp @@ -226,7 +226,7 @@ nsXMLFragmentContentSink::CloseElement(nsIContent* aContent) { // don't do fancy stuff in nsXMLContentSink if (mPreventScriptExecution && - (aContent->IsHTMLElement(nsGkAtoms::script), + (aContent->IsHTMLElement(nsGkAtoms::script) || aContent->IsSVGElement(nsGkAtoms::script))) { nsCOMPtr sele = do_QueryInterface(aContent); NS_ASSERTION(sele, "script did QI correctly!"); diff --git a/editor/composer/nsEditorSpellCheck.cpp b/editor/composer/nsEditorSpellCheck.cpp index 4c144ada4..790480034 100644 --- a/editor/composer/nsEditorSpellCheck.cpp +++ b/editor/composer/nsEditorSpellCheck.cpp @@ -644,6 +644,10 @@ nsEditorSpellCheck::DeleteSuggestedWordList() NS_IMETHODIMP nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallback) { + if (NS_WARN_IF(!mSpellChecker)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsresult rv; RefPtr kungFuDeathGrip = this; diff --git a/gfx/layers/apz/public/GoannaContentController.h b/gfx/layers/apz/public/GoannaContentController.h index f80e9de34..dc4ac2e67 100644 --- a/gfx/layers/apz/public/GoannaContentController.h +++ b/gfx/layers/apz/public/GoannaContentController.h @@ -157,6 +157,8 @@ public: */ virtual void NotifyFlushComplete() = 0; + virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) = 0; + virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {} virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {} virtual void SetScrollingRootContent(bool isRootContent) {} diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index b3ff7bdfb..6f661bc43 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -53,10 +53,12 @@ typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Point4D Point4D; typedef mozilla::gfx::Matrix4x4 Matrix4x4; +typedef CompositorBridgeParent::LayerTreeState LayerTreeState; + float APZCTreeManager::sDPI = 160.0; struct APZCTreeManager::TreeBuildingState { - TreeBuildingState(const CompositorBridgeParent::LayerTreeState* const aLayerTreeState, + TreeBuildingState(const LayerTreeState* const aLayerTreeState, bool aIsFirstPaint, uint64_t aOriginatingLayersId, APZTestData* aTestData, uint32_t aPaintSequence) : mLayerTreeState(aLayerTreeState) @@ -67,7 +69,7 @@ struct APZCTreeManager::TreeBuildingState { } // State that doesn't change as we recurse in the tree building - const CompositorBridgeParent::LayerTreeState* const mLayerTreeState; + const LayerTreeState* const mLayerTreeState; const bool mIsFirstPaint; const uint64_t mOriginatingLayersId; const APZPaintLogHelper mPaintLogger; @@ -226,13 +228,13 @@ APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId, // the layers id that originated this update. APZTestData* testData = nullptr; if (gfxPrefs::APZTestLoggingEnabled()) { - if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) { + if (LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) { testData = &state->mApzTestData; testData->StartNewPaint(aPaintSequenceNumber); } } - const CompositorBridgeParent::LayerTreeState* treeState = + const LayerTreeState* treeState = CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId); MOZ_ASSERT(treeState); TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId, @@ -439,6 +441,7 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid, RefPtr apzc = GetTargetAPZC(aGuid); if (!apzc) { + NotifyScrollbarDragRejected(aGuid); return; } @@ -446,6 +449,14 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid, mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics); } +void +APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const +{ + const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId); + MOZ_ASSERT(state && state->mController); + state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId); +} + HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, const FrameMetrics& aMetrics, @@ -462,7 +473,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, needsApzc = false; } - const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId); if (!(state && state->mController.get())) { needsApzc = false; } @@ -690,7 +701,7 @@ APZCTreeManager::FlushApzRepaints(uint64_t aLayersId) // ensure any pending paints were flushed. Now, paints are flushed // immediately, so it is safe to simply send a notification now. APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId); - const CompositorBridgeParent::LayerTreeState* state = + const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId); MOZ_ASSERT(state && state->mController); state->mController->DispatchToRepaintThread(NewRunnableMethod( diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index 357879502..bb2829986 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -477,6 +477,8 @@ private: void PrintAPZCInfo(const LayerMetricsWrapper& aLayer, const AsyncPanZoomController* apzc); + void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const; + protected: /* The input queue where input events are held until we know enough to * figure out where they're going. Protected so gtests can access it. diff --git a/gfx/layers/apz/test/gtest/APZCBasicTester.h b/gfx/layers/apz/test/gtest/APZCBasicTester.h index 3d3937c51..79a69301f 100644 --- a/gfx/layers/apz/test/gtest/APZCBasicTester.h +++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h @@ -49,6 +49,7 @@ protected: while (mcc->RunThroughDelayedTasks()); apzc->Destroy(); tm->ClearTree(); + tm->ClearContentController(); } void MakeApzcWaitForMainThread() diff --git a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h index 1f19680a4..4eeed1e7e 100644 --- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h +++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h @@ -29,6 +29,7 @@ protected: virtual void TearDown() { while (mcc->RunThroughDelayedTasks()); manager->ClearTree(); + manager->ClearContentController(); } /** diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h index d75fb5ac4..c81312abd 100644 --- a/gfx/layers/apz/test/gtest/APZTestCommon.h +++ b/gfx/layers/apz/test/gtest/APZTestCommon.h @@ -91,6 +91,7 @@ public: } MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg)); MOCK_METHOD0(NotifyFlushComplete, void()); + MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, void(const FrameMetrics::ViewID&)); }; class MockContentControllerDelayed : public MockContentController { @@ -174,6 +175,10 @@ public: return mInputQueue; } + void ClearContentController() { + mcc = nullptr; + } + protected: AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId, GoannaContentController* aController) override; diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index 5e6fe0e12..6bf6be62f 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -918,6 +918,15 @@ APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame) || aFrame->LastSmoothScrollOrigin(); } +/* static */ void +APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) { + scrollFrame->AsyncScrollbarDragRejected(); + } +} + /* static */ void APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, LayoutDeviceCoord aSpanChange, diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h index 20ce5f341..482f7b964 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -164,6 +164,8 @@ public: /* Notify content that the repaint flush is complete. */ static void NotifyFlushComplete(nsIPresShell* aShell); + static void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId); + /* Temporarily ignore the Displayport for better paint performance. If at * all possible, pass in a presShell if you have one at the call site, we * use it to trigger a repaint once suppression is disabled. Without that diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp index ac8b3824f..22f597bc0 100644 --- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -274,3 +274,16 @@ ChromeProcessController::NotifyFlushComplete() APZCCallbackHelper::NotifyFlushComplete(GetPresShell()); } + +void +ChromeProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) +{ + if (MessageLoop::current() != mUILoop) { + mUILoop->PostTask(NewRunnableMethod(this, + &ChromeProcessController::NotifyAsyncScrollbarDragRejected, + aScrollId)); + return; + } + + APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId); +} diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h index 64fabd2d2..8e4a89440 100644 --- a/gfx/layers/apz/util/ChromeProcessController.h +++ b/gfx/layers/apz/util/ChromeProcessController.h @@ -63,6 +63,7 @@ public: virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) override; virtual void NotifyFlushComplete() override; + virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override; private: nsCOMPtr mWidget; RefPtr mAPZEventState; diff --git a/gfx/layers/apz/util/ContentProcessController.cpp b/gfx/layers/apz/util/ContentProcessController.cpp index 997cb7acc..4e5f6108c 100644 --- a/gfx/layers/apz/util/ContentProcessController.cpp +++ b/gfx/layers/apz/util/ContentProcessController.cpp @@ -85,6 +85,12 @@ ContentProcessController::NotifyFlushComplete() } } +void +ContentProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) +{ + APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId); +} + void ContentProcessController::PostDelayedTask(already_AddRefed aRunnable, int aDelayMs) { diff --git a/gfx/layers/apz/util/ContentProcessController.h b/gfx/layers/apz/util/ContentProcessController.h index 1c05ed94b..1a96385fe 100644 --- a/gfx/layers/apz/util/ContentProcessController.h +++ b/gfx/layers/apz/util/ContentProcessController.h @@ -62,6 +62,8 @@ public: void NotifyFlushComplete() override; + void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override; + void PostDelayedTask(already_AddRefed aRunnable, int aDelayMs) override; bool IsRepaintThread() override; diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp index 7c9440591..e4f911a6c 100644 --- a/gfx/layers/ipc/APZChild.cpp +++ b/gfx/layers/ipc/APZChild.cpp @@ -85,6 +85,13 @@ APZChild::RecvNotifyFlushComplete() return IPC_OK(); } +mozilla::ipc::IPCResult +APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) +{ + mController->NotifyAsyncScrollbarDragRejected(aScrollId); + return IPC_OK(); +} + mozilla::ipc::IPCResult APZChild::RecvDestroy() { diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h index 9b421af59..eb515c708 100644 --- a/gfx/layers/ipc/APZChild.h +++ b/gfx/layers/ipc/APZChild.h @@ -42,6 +42,8 @@ public: mozilla::ipc::IPCResult RecvNotifyFlushComplete() override; + mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override; + mozilla::ipc::IPCResult RecvDestroy() override; private: diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl index 31b1551d6..d6da8e0f4 100644 --- a/gfx/layers/ipc/PAPZ.ipdl +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -64,6 +64,8 @@ child: async NotifyFlushComplete(); + async NotifyAsyncScrollbarDragRejected(ViewID aScrollId); + async Destroy(); }; diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp index 1ace44392..ce5ea26e5 100644 --- a/gfx/layers/ipc/RemoteContentController.cpp +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -244,6 +244,21 @@ RemoteContentController::NotifyFlushComplete() } } +void +RemoteContentController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) +{ + if (MessageLoop::current() != mCompositorThread) { + // We have to send messages from the compositor thread + mCompositorThread->PostTask(NewRunnableMethod(this, + &RemoteContentController::NotifyAsyncScrollbarDragRejected, aScrollId)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId); + } +} + void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) { diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h index 21967f378..eeb1adb02 100644 --- a/gfx/layers/ipc/RemoteContentController.h +++ b/gfx/layers/ipc/RemoteContentController.h @@ -74,6 +74,8 @@ public: virtual void NotifyFlushComplete() override; + virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override; + virtual void ActorDestroy(ActorDestroyReason aWhy) override; virtual void Destroy() override; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.cpp b/gfx/skia/skia/src/core/SkBitmapProcState.cpp index 5bc1b47f6..183016e69 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState.cpp @@ -300,7 +300,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) return false; } -#if !defined(SK_ARM_HAS_NEON) +#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON) static const SampleProc32 gSkBitmapProcStateSample32[] = { S32_opaque_D32_nofilter_DXDY, S32_alpha_D32_nofilter_DXDY, diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp index 970ea47a3..50b59b8bd 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp @@ -56,7 +56,7 @@ extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[]; #endif // defined(SK_ARM_HAS_NEON) // Compile non-neon code path if needed -#if !defined(SK_ARM_HAS_NEON) +#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON) #define MAKENAME(suffix) ClampX_ClampY ## suffix #define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) #define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp index 28ba4d205..699657f0e 100644 --- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -136,9 +136,7 @@ BackgroundChildImpl::ProcessingError(Result aCode, const char* aReason) MOZ_CRASH("Unknown error code!"); } - // This is just MOZ_CRASH() un-inlined so that we can pass the result code as - // a string. MOZ_CRASH() only supports string literals at the moment. - MOZ_ReportCrash(abortMessage.get(), __FILE__, __LINE__); MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF("%s: %s", abortMessage.get(), aReason); } void diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 206f33a7c..69239ab39 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -1802,6 +1802,50 @@ MessageChannel::DispatchAsyncMessage(const Message& aMsg) MaybeHandleError(rv, aMsg, "DispatchAsyncMessage"); } +bool +MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth) +{ + AssertWorkerThread(); + + // We may or may not own the lock in this function, so don't access any + // channel state. + + IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type"); + + // Race detection: see the long comment near mRemoteStackDepthGuess in + // MessageChannel.h. "Remote" stack depth means our side, and "local" means + // the other side. + if (aMsg.interrupt_remote_stack_depth_guess() == RemoteViewOfStackDepth(aStackDepth)) { + return false; + } + + // Interrupt in-calls have raced. The winner, if there is one, gets to defer + // processing of the other side's in-call. + bool defer; + const char* winner; + const MessageInfo parentMsgInfo = + (mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top(); + const MessageInfo childMsgInfo = + (mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg); + switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo)) + { + case RIPChildWins: + winner = "child"; + defer = (mSide == ChildSide); + break; + case RIPParentWins: + winner = "parent"; + defer = (mSide != ChildSide); + break; + case RIPError: + MOZ_CRASH("NYI: 'Error' Interrupt race policy"); + default: + MOZ_CRASH("not reached"); + } + + return defer; +} + void MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth) { @@ -1810,56 +1854,18 @@ MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth) IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type"); - // Race detection: see the long comment near mRemoteStackDepthGuess in - // MessageChannel.h. "Remote" stack depth means our side, and "local" means - // the other side. - if (aMsg.interrupt_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) { - // Interrupt in-calls have raced. The winner, if there is one, gets to defer - // processing of the other side's in-call. - bool defer; - const char* winner; - const MessageInfo parentMsgInfo = - (mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top(); - const MessageInfo childMsgInfo = - (mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg); - switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo)) - { - case RIPChildWins: - winner = "child"; - defer = (mSide == ChildSide); - break; - case RIPParentWins: - winner = "parent"; - defer = (mSide != ChildSide); - break; - case RIPError: - MOZ_CRASH("NYI: 'Error' Interrupt race policy"); - return; - default: - MOZ_CRASH("not reached"); - return; - } - - if (LoggingEnabled()) { - printf_stderr(" (%s: %s won, so we're%sdeferring)\n", - (mSide == ChildSide) ? "child" : "parent", - winner, - defer ? " " : " not "); - } - - if (defer) { - // We now know the other side's stack has one more frame - // than we thought. - ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred() - mDeferred.push(Move(aMsg)); - return; - } - - // We "lost" and need to process the other side's in-call. Don't need - // to fix up the mRemoteStackDepthGuess here, because we're just about - // to increment it in DispatchCall(), which will make it correct again. + if (ShouldDeferInterruptMessage(aMsg, stackDepth)) { + // We now know the other side's stack has one more frame + // than we thought. + ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred() + mDeferred.push(Move(aMsg)); + return; } + // If we "lost" a race and need to process the other side's in-call, we + // don't need to fix up the mRemoteStackDepthGuess here, because we're just + // about to increment it, which will make it correct again. + #ifdef OS_WIN SyncStackFrame frame(this, true); #endif @@ -1895,12 +1901,18 @@ MessageChannel::MaybeUndeferIncall() size_t stackDepth = InterruptStackDepth(); + Message& deferred = mDeferred.top(); + // the other side can only *under*-estimate our actual stack depth - IPC_ASSERT(mDeferred.top().interrupt_remote_stack_depth_guess() <= stackDepth, + IPC_ASSERT(deferred.interrupt_remote_stack_depth_guess() <= stackDepth, "fatal logic error"); + if (ShouldDeferInterruptMessage(deferred, stackDepth)) { + return; + } + // maybe time to process this message - Message call(Move(mDeferred.top())); + Message call(Move(deferred)); mDeferred.pop(); // fix up fudge factor we added to account for race diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 42445e544..a28019894 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -419,6 +419,7 @@ class MessageChannel : HasResultCodes bool WasTransactionCanceled(int transaction); bool ShouldDeferMessage(const Message& aMsg); + bool ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth); void OnMessageReceivedFromLink(Message&& aMsg); void OnChannelErrorFromLink(); diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index 74be01bdb..7c632595f 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -843,10 +843,6 @@ DeallocateMappedContent(void* p, size_t length) #error "Memory mapping functions are not defined for your OS." #endif -#ifdef XP_WIN -static char sCrashReason[256]; -#endif - void ProtectPages(void* p, size_t size) { @@ -856,10 +852,8 @@ ProtectPages(void* p, size_t size) #if defined(XP_WIN) DWORD oldProtect; if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect)) { - SprintfLiteral(sCrashReason, - "MOZ_CRASH(VirtualProtect(PAGE_NOACCESS) failed! Error code: %u)", GetLastError()); - MOZ_CRASH_ANNOTATE(sCrashReason); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_NOACCESS) failed! Error code: %u", + GetLastError()); } MOZ_ASSERT(oldProtect == PAGE_READWRITE); #else // assume Unix @@ -877,10 +871,8 @@ MakePagesReadOnly(void* p, size_t size) #if defined(XP_WIN) DWORD oldProtect; if (!VirtualProtect(p, size, PAGE_READONLY, &oldProtect)) { - SprintfLiteral(sCrashReason, - "MOZ_CRASH(VirtualProtect(PAGE_READONLY) failed! Error code: %u)", GetLastError()); - MOZ_CRASH_ANNOTATE(sCrashReason); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READONLY) failed! Error code: %u", + GetLastError()); } MOZ_ASSERT(oldProtect == PAGE_READWRITE); #else // assume Unix @@ -898,10 +890,8 @@ UnprotectPages(void* p, size_t size) #if defined(XP_WIN) DWORD oldProtect; if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect)) { - SprintfLiteral(sCrashReason, - "MOZ_CRASH(VirtualProtect(PAGE_READWRITE) failed! Error code: %u)", GetLastError()); - MOZ_CRASH_ANNOTATE(sCrashReason); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READWRITE) failed! Error code: %u", + GetLastError()); } MOZ_ASSERT(oldProtect == PAGE_NOACCESS || oldProtect == PAGE_READONLY); #else // assume Unix diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index c9f060200..b9fe3a3d8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3689,7 +3689,7 @@ js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup ShouldUpdateTypes::Update); MOZ_ASSERT(result.value == DenseElementResult::Success); } - objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true); + objects->maybeAnalyze(cx, group, /* force = */ true); } } return true; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index cc63db389..114eba8f2 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3105,9 +3105,10 @@ Crash(JSContext* cx, unsigned argc, Value* vp) char* utf8chars = JS_EncodeStringToUTF8(cx, message); if (!utf8chars) return false; +#ifndef DEBUG MOZ_ReportCrash(utf8chars, __FILE__, __LINE__); - MOZ_CRASH_ANNOTATE("MOZ_CRASH(dynamic)"); - MOZ_REALLY_CRASH(); +#endif + MOZ_CRASH_UNSAFE_OOL(utf8chars); } static bool diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index c47b95dac..cbdf835c0 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -63,6 +63,7 @@ #include "ScrollSnap.h" #include "UnitTransforms.h" #include "nsPluginFrame.h" +#include "nsSliderFrame.h" #include "mozilla/layers/APZCCallbackHelper.h" #include #include @@ -6245,3 +6246,30 @@ ScrollFrameHelper::DragScroll(WidgetEvent* aEvent) return willScroll; } + +static void +AsyncScrollbarDragRejected(nsIFrame* aScrollbar) +{ + if (!aScrollbar) { + return; + } + + for (nsIFrame::ChildListIterator childLists(aScrollbar); + !childLists.IsDone(); + childLists.Next()) { + for (nsIFrame* frame : childLists.CurrentList()) { + if (nsSliderFrame* sliderFrame = do_QueryFrame(frame)) { + sliderFrame->AsyncScrollbarDragRejected(); + } + } + } +} + +void +ScrollFrameHelper::AsyncScrollbarDragRejected() +{ + // We don't get told which scrollbar requested the async drag, + // so we notify both. + ::AsyncScrollbarDragRejected(mHScrollbarBox); + ::AsyncScrollbarDragRejected(mVScrollbarBox); +} diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 4eb47f801..9d1c8be64 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -462,6 +462,8 @@ public: bool DragScroll(WidgetEvent* aEvent); + void AsyncScrollbarDragRejected(); + // owning references to the nsIAnonymousContentCreator-built content nsCOMPtr mHScrollbarContent; nsCOMPtr mVScrollbarContent; @@ -1045,6 +1047,10 @@ public: return mHelper.DragScroll(aEvent); } + virtual void AsyncScrollbarDragRejected() override { + return mHelper.AsyncScrollbarDragRejected(); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif @@ -1478,6 +1484,10 @@ public: return mHelper.DragScroll(aEvent); } + virtual void AsyncScrollbarDragRejected() override { + return mHelper.AsyncScrollbarDragRejected(); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index 018218d3f..b7a2691c5 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -483,6 +483,8 @@ public: * caller should look for an ancestor to scroll. */ virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0; + + virtual void AsyncScrollbarDragRejected() = 0; }; #endif diff --git a/layout/xul/nsSliderFrame.cpp b/layout/xul/nsSliderFrame.cpp index 3e8b5f3fa..d80f69fbd 100644 --- a/layout/xul/nsSliderFrame.cpp +++ b/layout/xul/nsSliderFrame.cpp @@ -991,36 +991,36 @@ ScrollFrameWillBuildScrollInfoLayer(nsIFrame* aScrollFrame) return false; } -bool +void nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent) { if (!aEvent->mFlags.mHandledByAPZ) { - return false; + return; } if (!gfxPlatform::GetPlatform()->SupportsApzDragInput()) { - return false; + return; } nsContainerFrame* scrollFrame = GetScrollbar()->GetParent(); if (!scrollFrame) { - return false; + return; } nsIContent* scrollableContent = scrollFrame->GetContent(); if (!scrollableContent) { - return false; + return; } nsIScrollableFrame* scrollFrameAsScrollable = do_QueryFrame(scrollFrame); if (!scrollFrameAsScrollable) { - return false; + return; } // APZ dragging requires the scrollbar to be layerized, which doesn't // happen for scroll info layers. if (ScrollFrameWillBuildScrollInfoLayer(scrollFrame)) { - return false; + return; } mozilla::layers::FrameMetrics::ViewID scrollTargetId; @@ -1028,7 +1028,7 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent) bool hasAPZView = hasID && (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID); if (!hasAPZView) { - return false; + return; } nsIFrame* scrollbarBox = GetScrollbar(); @@ -1053,9 +1053,15 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent) AsyncDragMetrics::VERTICAL); if (!nsLayoutUtils::HasDisplayPort(scrollableContent)) { - return false; + return; } + // It's important to set this before calling nsIWidget::StartAsyncScrollbarDrag(), + // because in some configurations, that can call AsyncScrollbarDragRejected() + // synchronously, which clears the flag (and we want it to stay cleared in + // that case). + mScrollingWithAPZ = true; + // When we start an APZ drag, we wont get mouse events for the drag. // APZ will consume them all and only notify us of the new scroll position. bool waitForRefresh = InputAPZContext::HavePendingLayerization(); @@ -1067,7 +1073,6 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent) if (!waitForRefresh) { widget->StartAsyncScrollbarDrag(dragMetrics); } - return true; } nsresult @@ -1137,16 +1142,15 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent) mDragStart = pos - mThumbStart; - mScrollingWithAPZ = StartAPZDrag(event); + mScrollingWithAPZ = false; + StartAPZDrag(event); // sets mScrollingWithAPZ=true if appropriate #ifdef DEBUG_SLIDER printf("Pressed mDragStart=%d\n",mDragStart); #endif - if (!mScrollingWithAPZ && !mSuppressionActive) { - MOZ_ASSERT(PresContext()->PresShell()); - APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell()); - mSuppressionActive = true; + if (!mScrollingWithAPZ) { + SuppressDisplayport(); } return NS_OK; @@ -1160,11 +1164,7 @@ nsSliderFrame::StopDrag() mScrollingWithAPZ = false; - if (mSuppressionActive) { - MOZ_ASSERT(PresContext()->PresShell()); - APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell()); - mSuppressionActive = false; - } + UnsuppressDisplayport(); #ifdef MOZ_WIDGET_GTK nsIFrame* thumbFrame = mFrames.FirstChild(); @@ -1536,5 +1536,36 @@ nsSliderFrame::GetThumbRatio() const return mRatio / mozilla::AppUnitsPerCSSPixel(); } +void +nsSliderFrame::AsyncScrollbarDragRejected() +{ + mScrollingWithAPZ = false; + // Only suppress the displayport if we're still dragging the thumb. + // Otherwise, no one will unsuppress it. + if (isDraggingThumb()) { + SuppressDisplayport(); + } +} + +void +nsSliderFrame::SuppressDisplayport() +{ + if (!mSuppressionActive) { + MOZ_ASSERT(PresContext()->PresShell()); + APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell()); + mSuppressionActive = true; + } +} + +void +nsSliderFrame::UnsuppressDisplayport() +{ + if (mSuppressionActive) { + MOZ_ASSERT(PresContext()->PresShell()); + APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell()); + mSuppressionActive = false; + } +} + NS_IMPL_ISUPPORTS(nsSliderMediator, nsIDOMEventListener) diff --git a/layout/xul/nsSliderFrame.h b/layout/xul/nsSliderFrame.h index e2365428a..748166819 100644 --- a/layout/xul/nsSliderFrame.h +++ b/layout/xul/nsSliderFrame.h @@ -100,7 +100,7 @@ public: nsresult StartDrag(nsIDOMEvent* aEvent); nsresult StopDrag(); - bool StartAPZDrag(WidgetGUIEvent* aEvent); + void StartAPZDrag(mozilla::WidgetGUIEvent* aEvent); static int32_t GetCurrentPosition(nsIContent* content); static int32_t GetMinPosition(nsIContent* content); @@ -137,6 +137,11 @@ public: // scrolled frame. float GetThumbRatio() const; + // Notify the slider frame than an async scrollbar drag requested in + // StartAPZDrag() was rejected by APZ, and the slider frame should + // fall back to main-thread dragging. + void AsyncScrollbarDragRejected(); + private: bool GetScrollToClick(); @@ -158,6 +163,9 @@ private: void RemoveListener(); bool isDraggingThumb(); + void SuppressDisplayport(); + void UnsuppressDisplayport(); + void StartRepeat() { nsRepeatService::GetInstance()->Start(Notify, this); } diff --git a/memory/replace/logalloc/replay/moz.build b/memory/replace/logalloc/replay/moz.build index cbc35ed51..bb976908f 100644 --- a/memory/replace/logalloc/replay/moz.build +++ b/memory/replace/logalloc/replay/moz.build @@ -21,4 +21,7 @@ USE_LIBS += [ 'memory', ] +# The memory library defines this, so it's needed here too. +DEFINES['IMPL_MFBT'] = True + DISABLE_STL_WRAPPING = True diff --git a/mfbt/Assertions.cpp b/mfbt/Assertions.cpp index 6adb35e87..62630842d 100644 --- a/mfbt/Assertions.cpp +++ b/mfbt/Assertions.cpp @@ -3,7 +3,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/. */ -#include "mozilla/Types.h" +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" + +#include + +MOZ_BEGIN_EXTERN_C /* * The crash reason is defined as a global variable here rather than in the @@ -11,7 +16,51 @@ * JS that don't link with the crash reporter directly. This value will only * be consumed if the crash reporter is used by the target application. */ +MFBT_DATA const char* gMozCrashReason = nullptr; + +#ifndef DEBUG +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void +MOZ_CrashOOL(int aLine, const char* aReason) +#else +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void +MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason) +#endif +{ +#ifdef DEBUG + MOZ_ReportCrash(aReason, aFilename, aLine); +#endif + MOZ_CRASH_ANNOTATE(aReason); + MOZ_REALLY_CRASH(aLine); +} + +static char sPrintfCrashReason[sPrintfCrashReasonSize] = {}; +static mozilla::Atomic sCrashing(false); + +#ifndef DEBUG +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void +MOZ_CrashPrintf(int aLine, const char* aFormat, ...) +#else +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void +MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...) +#endif +{ + if (!sCrashing.compareExchange(false, true)) { + // In the unlikely event of a race condition, skip + // setting the crash reason and just crash safely. + MOZ_REALLY_CRASH(aLine); + } + va_list aArgs; + va_start(aArgs, aFormat); + int ret = vsnprintf(sPrintfCrashReason, sPrintfCrashReasonSize, + aFormat, aArgs); + va_end(aArgs); + MOZ_RELEASE_ASSERT(ret >= 0 && size_t(ret) < sPrintfCrashReasonSize, + "Could not write the explanation string to the supplied buffer!"); +#ifdef DEBUG + MOZ_ReportCrash(sPrintfCrashReason, aFilename, aLine); +#endif + MOZ_CRASH_ANNOTATE(sPrintfCrashReason); + MOZ_REALLY_CRASH(aLine); +} -MOZ_BEGIN_EXTERN_C -MOZ_EXPORT const char* gMozCrashReason = nullptr; MOZ_END_EXTERN_C diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 6bbbe4422..e355b8064 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -214,22 +214,22 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine) TerminateProcess(GetCurrentProcess(), 3); } -# define MOZ_REALLY_CRASH() \ +# define MOZ_REALLY_CRASH(line) \ do { \ __debugbreak(); \ - MOZ_NoReturn(__LINE__); \ + MOZ_NoReturn(line); \ } while (0) #else # ifdef __cplusplus -# define MOZ_REALLY_CRASH() \ +# define MOZ_REALLY_CRASH(line) \ do { \ - *((volatile int*) NULL) = __LINE__; \ + *((volatile int*) NULL) = line; \ ::abort(); \ } while (0) # else -# define MOZ_REALLY_CRASH() \ +# define MOZ_REALLY_CRASH(line) \ do { \ - *((volatile int*) NULL) = __LINE__; \ + *((volatile int*) NULL) = line; \ abort(); \ } while (0) # endif @@ -260,17 +260,69 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine) # define MOZ_CRASH(...) \ do { \ MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \ - MOZ_REALLY_CRASH(); \ + MOZ_REALLY_CRASH(__LINE__); \ } while (0) #else # define MOZ_CRASH(...) \ do { \ MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \ MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \ - MOZ_REALLY_CRASH(); \ + MOZ_REALLY_CRASH(__LINE__); \ } while (0) #endif +/* + * MOZ_CRASH_UNSAFE_OOL(explanation-string) can be used if the explanation + * string cannot be a string literal (but no other processing needs to be done + * on it). A regular MOZ_CRASH() is preferred wherever possible, as passing + * arbitrary strings from a potentially compromised process is not without risk. + * If the string being passed is the result of a printf-style function, + * consider using MOZ_CRASH_UNSAFE_PRINTF instead. + */ +#ifndef DEBUG +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void +MOZ_CrashOOL(int aLine, const char* aReason); +# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__LINE__, reason) +#else +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void +MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason); +# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__FILE__, __LINE__, reason) +#endif + +static const size_t sPrintfMaxArgs = 4; +static const size_t sPrintfCrashReasonSize = 1024; + +#ifndef DEBUG +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void +MOZ_CrashPrintf(int aLine, const char* aFormat, ...); +# define MOZ_CALL_CRASH_PRINTF(format, ...) \ + MOZ_CrashPrintf(__LINE__, format, __VA_ARGS__) +#else +MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void +MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...); +# define MOZ_CALL_CRASH_PRINTF(format, ...) \ + MOZ_CrashPrintf(__FILE__, __LINE__, format, __VA_ARGS__) +#endif + +/* + * MOZ_CRASH_UNSAFE_PRINTF(format, arg1 [, args]) can be used when more + * information is desired than a string literal can supply. The caller provides + * a printf-style format string, which must be a string literal and between + * 1 and 4 additional arguments. A regular MOZ_CRASH() is preferred wherever + * possible, as passing arbitrary strings to printf from a potentially + * compromised process is not without risk. + */ +#define MOZ_CRASH_UNSAFE_PRINTF(format, ...) \ + do { \ + MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \ + static_assert( \ + MOZ_PASTE_PREFIX_AND_ARG_COUNT(, __VA_ARGS__) <= sPrintfMaxArgs, \ + "Only up to 4 additional arguments are allowed!"); \ + static_assert(sizeof(format) <= sPrintfCrashReasonSize, \ + "The supplied format string is too long!"); \ + MOZ_CALL_CRASH_PRINTF("" format, __VA_ARGS__); \ + } while (0) + MOZ_END_EXTERN_C /* @@ -366,7 +418,7 @@ struct AssertionConditionType if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ MOZ_REPORT_ASSERTION_FAILURE(#expr, __FILE__, __LINE__); \ MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \ - MOZ_REALLY_CRASH(); \ + MOZ_REALLY_CRASH(__LINE__); \ } \ } while (0) /* Now the two-argument form. */ @@ -376,7 +428,7 @@ struct AssertionConditionType if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ MOZ_REPORT_ASSERTION_FAILURE(#expr " (" explain ")", __FILE__, __LINE__); \ MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \ - MOZ_REALLY_CRASH(); \ + MOZ_REALLY_CRASH(__LINE__); \ } \ } while (0) diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index d7dab5ac3..ddb6feaa6 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -148,8 +148,7 @@ static MOZ_COLD void CrashWithReason(const char * reason) { #ifndef RELEASE_OR_BETA - MOZ_CRASH_ANNOTATE(reason); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_OOL(reason); #endif } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index fe8a0d9a0..a5c008017 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -5660,25 +5660,6 @@ NS_IMETHODIMP nsHttpChannel::CloseStickyConnection() return NS_OK; } -NS_IMETHODIMP nsHttpChannel::ForceNoSpdy() -{ - LOG(("nsHttpChannel::ForceNoSpdy this=%p", this)); - - MOZ_ASSERT(mTransaction); - if (!mTransaction) { - return NS_ERROR_UNEXPECTED; - } - - mAllowSpdy = 0; - mCaps |= NS_HTTP_DISALLOW_SPDY; - - if (!(mTransaction->Caps() & NS_HTTP_DISALLOW_SPDY)) { - mTransaction->DisableSpdy(); - } - - return NS_OK; -} - //----------------------------------------------------------------------------- // nsHttpChannel::nsISupports //----------------------------------------------------------------------------- @@ -6062,6 +6043,52 @@ nsHttpChannel::BeginConnect() SetLoadGroupUserAgentOverride(); + // Check if request was cancelled during on-modify-request or on-useragent. + if (mCanceled) { + return mStatus; + } + + if (mSuspendCount) { + LOG(("Waiting until resume BeginConnect [this=%p]\n", this)); + MOZ_ASSERT(!mCallOnResume); + mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue; + return NS_OK; + } + + return BeginConnectContinue(); +} + +void +nsHttpChannel::HandleBeginConnectContinue() +{ + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); + nsresult rv; + + if (mSuspendCount) { + LOG(("Waiting until resume BeginConnect [this=%p]\n", this)); + mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue; + return; + } + + LOG(("nsHttpChannel::HandleBeginConnectContinue [this=%p]\n", this)); + rv = BeginConnectContinue(); + if (NS_FAILED(rv)) { + CloseCacheEntry(false); + Unused << AsyncAbort(rv); + } +} + +nsresult +nsHttpChannel::BeginConnectContinue() +{ + nsresult rv; + + // Check if request was cancelled during suspend AFTER on-modify-request or + // on-useragent. + if (mCanceled) { + return mStatus; + } + // Check to see if we should redirect this channel elsewhere by // nsIHttpChannel.redirectTo API request if (mAPIRedirectToURI) { diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 97bff94b7..b5cfef741 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -111,7 +111,6 @@ public: NS_IMETHOD OnAuthAvailable() override; NS_IMETHOD OnAuthCancelled(bool userCancel) override; NS_IMETHOD CloseStickyConnection() override; - NS_IMETHOD ForceNoSpdy() override; // Functions we implement from nsIHttpAuthenticableChannel but are // declared in HttpBaseChannel must be implemented in this class. We // just call the HttpBaseChannel:: impls. @@ -284,6 +283,8 @@ private: bool RequestIsConditional(); nsresult BeginConnect(); + void HandleBeginConnectContinue(); + MOZ_MUST_USE nsresult BeginConnectContinue(); nsresult ContinueBeginConnectWithResult(); void ContinueBeginConnect(); nsresult Connect(); diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index c7d073a8a..5e2a40828 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -820,10 +820,6 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, } mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED); - if (mConnectionBased) { - rv = mAuthChannel->ForceNoSpdy(); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } if (identityInvalid) { if (entry) { diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 5e65703dd..e12d3afbc 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -2031,11 +2031,6 @@ nsHttpTransaction::CheckForStickyAuthScheme() MOZ_ASSERT(mResponseHead); MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - if (mClosed) { - LOG((" closed, not checking")); - return; - } - CheckForStickyAuthSchemeAt(nsHttp::WWW_Authenticate); CheckForStickyAuthSchemeAt(nsHttp::Proxy_Authenticate); } diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index 1107c7480..62c1c67ad 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -134,8 +134,6 @@ public: void DispatchedAsBlocking(); void RemoveDispatchedAsBlocking(); - void DisableSpdy() override; - nsHttpTransaction *QueryHttpTransaction() override { return this; } Http2PushedStream *GetPushedStream() { return mPushedStream; } @@ -203,6 +201,7 @@ private: bool ResponseTimeoutEnabled() const final; + void DisableSpdy() override; void ReuseConnectionOnRestartOK(bool reuseOk) override { mReuseOnRestart = reuseOk; } // Called right after we parsed the response head. Checks for connection based diff --git a/netwerk/protocol/http/nsIHttpAuthenticableChannel.idl b/netwerk/protocol/http/nsIHttpAuthenticableChannel.idl index fe8e8c848..cd2bfe166 100644 --- a/netwerk/protocol/http/nsIHttpAuthenticableChannel.idl +++ b/netwerk/protocol/http/nsIHttpAuthenticableChannel.idl @@ -112,10 +112,4 @@ interface nsIHttpAuthenticableChannel : nsIProxiedChannel * the same connection. */ void closeStickyConnection(); - - /** - * Tells the channel to not use SPDY-like protocols, since this will be - * using connection-oriented auth. - */ - void forceNoSpdy(); }; diff --git a/netwerk/test/unit/test_suspend_channel_on_modified.js b/netwerk/test/unit/test_suspend_channel_on_modified.js new file mode 100644 index 000000000..a4f7c221e --- /dev/null +++ b/netwerk/test/unit/test_suspend_channel_on_modified.js @@ -0,0 +1,175 @@ +// This file tests async handling of a channel suspended in http-on-modify-request. + +var CC = Components.Constructor; + +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +var obs = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + +var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + +// baseUrl is always the initial connection attempt and is handled by +// failResponseHandler since every test expects that request will either be +// redirected or cancelled. +var baseUrl; + +function failResponseHandler(metadata, response) +{ + var text = "failure response"; + response.setHeader("Content-Type", "text/plain", false); + response.bodyOutputStream.write(text, text.length); + do_check_true(false, "Received request when we shouldn't."); +} + +function successResponseHandler(metadata, response) +{ + var text = "success response"; + response.setHeader("Content-Type", "text/plain", false); + response.bodyOutputStream.write(text, text.length); + do_check_true(true, "Received expected request."); +} + +function onModifyListener(callback) { + obs.addObserver({ + observe: function(subject, topic, data) { + var obs = Cc["@mozilla.org/observer-service;1"].getService(); + obs = obs.QueryInterface(Ci.nsIObserverService); + obs.removeObserver(this, "http-on-modify-request"); + callback(subject.QueryInterface(Ci.nsIHttpChannel)); + } + }, "http-on-modify-request", false); +} + +function startChannelRequest(baseUrl, flags, expectedResponse=null) { + var chan = NetUtil.newChannel({ + uri: baseUrl, + loadUsingSystemPrincipal: true + }); + chan.asyncOpen2(new ChannelListener((request, data, context) => { + if (expectedResponse) { + do_check_eq(data, expectedResponse); + } else { + do_check_true(!!!data, "no response"); + } + do_execute_soon(run_next_test) + }, null, flags)); +} + + +add_test(function testSimpleRedirect() { + onModifyListener(chan => { + chan.redirectTo(ios.newURI(`${baseUrl}/success`)); + }); + startChannelRequest(baseUrl, undefined, "success response"); +}); + +add_test(function testSimpleCancel() { + onModifyListener(chan => { + chan.cancel(Cr.NS_BINDING_ABORTED); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +add_test(function testSimpleCancelRedirect() { + onModifyListener(chan => { + chan.redirectTo(ios.newURI(`${baseUrl}/fail`)); + chan.cancel(Cr.NS_BINDING_ABORTED); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +// Test a request that will get redirected asynchronously. baseUrl should +// not be requested, we should receive the request for the redirectedUrl. +add_test(function testAsyncRedirect() { + onModifyListener(chan => { + // Suspend the channel then yield to make this async. + chan.suspend(); + Promise.resolve().then(() => { + chan.redirectTo(ios.newURI(`${baseUrl}/success`)); + chan.resume(); + }); + }); + startChannelRequest(baseUrl, undefined, "success response"); +}); + +add_test(function testSyncRedirect() { + onModifyListener(chan => { + chan.suspend(); + chan.redirectTo(ios.newURI(`${baseUrl}/success`)); + Promise.resolve().then(() => { + chan.resume(); + }); + }); + startChannelRequest(baseUrl, undefined, "success response"); +}); + +add_test(function testAsyncCancel() { + onModifyListener(chan => { + // Suspend the channel then yield to make this async. + chan.suspend(); + Promise.resolve().then(() => { + chan.cancel(Cr.NS_BINDING_ABORTED); + chan.resume(); + }); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +add_test(function testSyncCancel() { + onModifyListener(chan => { + chan.suspend(); + chan.cancel(Cr.NS_BINDING_ABORTED); + Promise.resolve().then(() => { + chan.resume(); + }); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +// Test request that will get redirected and cancelled asynchronously, +// ensure no connection is made. +add_test(function testAsyncCancelRedirect() { + onModifyListener(chan => { + // Suspend the channel then yield to make this async. + chan.suspend(); + Promise.resolve().then(() => { + chan.cancel(Cr.NS_BINDING_ABORTED); + chan.redirectTo(ios.newURI(`${baseUrl}/fail`)); + chan.resume(); + }); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +// Test a request that will get cancelled synchronously, ensure async redirect +// is not made. +add_test(function testSyncCancelRedirect() { + onModifyListener(chan => { + chan.suspend(); + chan.cancel(Cr.NS_BINDING_ABORTED); + Promise.resolve().then(() => { + chan.redirectTo(ios.newURI(`${baseUrl}/fail`)); + chan.resume(); + }); + }); + startChannelRequest(baseUrl, CL_EXPECT_FAILURE); +}); + +function run_test() { + var httpServer = new HttpServer(); + httpServer.registerPathHandler("/", failResponseHandler); + httpServer.registerPathHandler("/fail", failResponseHandler); + httpServer.registerPathHandler("/success", successResponseHandler); + httpServer.start(-1); + + baseUrl = `http://localhost:${httpServer.identity.primaryPort}`; + + run_next_test(); + + do_register_cleanup(function(){ + httpServer.stop(() => {}); + }); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index e398266c8..2df40241f 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -359,6 +359,7 @@ skip-if = os == "android" [test_1073747.js] [test_safeoutputstream_append.js] [test_suspend_channel_before_connect.js] +[test_suspend_channel_on_modified.js] [test_inhibit_caching.js] [test_dns_disable_ipv4.js] [test_dns_disable_ipv6.js] diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index f65c9a18f..27a4048d7 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -598,27 +598,49 @@ Database::BackupAndReplaceDatabaseFile(nsCOMPtr& aStorage) profDir, getter_AddRefs(backup)); } + // If anything fails from this point on, we have a stale connection or + // database file, and there's not much more we can do. + // The only thing we can try to do is to replace the database on the next + // start, and enforce a crash, so it gets reported to us. + // Close database connection if open. if (mMainConn) { - // If there's any not finalized statement or this fails for any reason - // we won't be able to remove the database. rv = mMainConn->Close(); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, ForceCrashAndReplaceDatabase( + NS_LITERAL_CSTRING("Unable to close the corrupt database."))); } // Remove the broken database. rv = databaseFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { + return ForceCrashAndReplaceDatabase( + NS_LITERAL_CSTRING("Unable to remove the corrupt database file.")); + } // Create a new database file. // Use an unshared connection, it will consume more memory but avoid shared // cache contentions across threads. rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn)); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, ForceCrashAndReplaceDatabase( + NS_LITERAL_CSTRING("Unable to open a new database connection."))); return NS_OK; } +nsresult +Database::ForceCrashAndReplaceDatabase(const nsCString& aReason) +{ + Preferences::SetBool(PREF_FORCE_DATABASE_REPLACEMENT, true); + // Ensure that prefs get saved, or we could crash before storing them. + nsIPrefService* prefService = Preferences::GetService(); + if (prefService && NS_SUCCEEDED(prefService->SavePrefFile(nullptr))) { + // We could force an application restart here, but we'd like to get these + // cases reported to us, so let's force a crash instead. + MOZ_CRASH_UNSAFE_OOL(aReason.get()); + } + return NS_ERROR_FAILURE; +} + nsresult Database::InitSchema(bool* aDatabaseMigrated) { @@ -1896,7 +1918,23 @@ Database::MigrateV35Up() { MOZ_ASSERT(NS_IsMainThread()); int64_t mobileRootId = CreateMobileRoot(); - if (mobileRootId <= 0) return NS_ERROR_FAILURE; + if (mobileRootId <= 0) { + // Either the schema is broken or there isn't any root. The latter can + // happen if a consumer, for example Thunderbird, never used bookmarks. + // If there are no roots, this migration should not run. + nsCOMPtr checkRootsStmt; + nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id FROM moz_bookmarks WHERE parent = 0" + ), getter_AddRefs(checkRootsStmt)); + NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(checkRootsStmt); + bool hasResult = false; + rv = checkRootsStmt->ExecuteStep(&hasResult); + if (NS_SUCCEEDED(rv) && !hasResult) { + return NS_OK; + } + return NS_ERROR_FAILURE; + } // At this point, we should have no more than two folders with the mobile // bookmarks anno: the new root, and the old folder if one exists. If, for diff --git a/toolkit/components/places/Database.h b/toolkit/components/places/Database.h index 15fdcb7e4..12cad2505 100644 --- a/toolkit/components/places/Database.h +++ b/toolkit/components/places/Database.h @@ -224,6 +224,12 @@ protected: */ nsresult BackupAndReplaceDatabaseFile(nsCOMPtr& aStorage); + /** + * This should be used as a last resort in case the database is corrupt and + * there's no way to fix it in-place. + */ + nsresult ForceCrashAndReplaceDatabase(const nsCString& aReason); + /** * Initializes the database. This performs any necessary migrations for the * database. All migration is done inside a transaction that is rolled back diff --git a/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js b/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js new file mode 100644 index 000000000..871fe8993 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v34.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + // Remove all the roots. + yield db.execute("DELETE FROM moz_bookmarks"); + yield db.close(); +}); + +add_task(function* database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); diff --git a/toolkit/components/places/tests/migration/xpcshell.ini b/toolkit/components/places/tests/migration/xpcshell.ini index 9b46551cd..3cc3bf59e 100644 --- a/toolkit/components/places/tests/migration/xpcshell.ini +++ b/toolkit/components/places/tests/migration/xpcshell.ini @@ -33,4 +33,5 @@ support-files = [test_current_from_v27.js] [test_current_from_v31.js] [test_current_from_v34.js] +[test_current_from_v34_no_roots.js] [test_current_from_v35.js] diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 7b448ea42..ff1a00779 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -2716,14 +2716,12 @@ nsExternalHelperAppService::GetTypeFromExtension(const nsACString& aFileExt, } // Ask OS. - bool found = false; - nsCOMPtr mi = GetMIMEInfoFromOS(EmptyCString(), aFileExt, &found); - if (mi && found) { - return mi->GetMIMEType(aContentType); + if (GetMIMETypeFromOSForExtension(aFileExt, aContentType)) { + return NS_OK; } // Check extras array. - found = GetTypeFromExtras(aFileExt, aContentType); + bool found = GetTypeFromExtras(aFileExt, aContentType); if (found) { return NS_OK; } @@ -2922,3 +2920,11 @@ bool nsExternalHelperAppService::GetTypeFromExtras(const nsACString& aExtension, return false; } + +bool +nsExternalHelperAppService::GetMIMETypeFromOSForExtension(const nsACString& aExtension, nsACString& aMIMEType) +{ + bool found = false; + nsCOMPtr mimeInfo = GetMIMEInfoFromOS(EmptyCString(), aExtension, &found); + return found && mimeInfo && NS_SUCCEEDED(mimeInfo->GetMIMEType(aMIMEType)); +} diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h index ca472ac71..ceec66661 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -108,6 +108,15 @@ public: virtual nsresult OSProtocolHandlerExists(const char *aScheme, bool *aExists) = 0; + /** + * Given an extension, get a MIME type string. If not overridden by + * the OS-specific nsOSHelperAppService, will call into GetMIMEInfoFromOS + * with an empty mimetype. + * @return true if we successfully found a mimetype. + */ + virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension, + nsACString& aMIMEType); + protected: virtual ~nsExternalHelperAppService(); diff --git a/uriloader/exthandler/win/nsOSHelperAppService.cpp b/uriloader/exthandler/win/nsOSHelperAppService.cpp index c5a8dc039..f01f3b49b 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp @@ -395,43 +395,25 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl if (aFileExt.IsEmpty()) return nullptr; - // windows registry assumes your file extension is going to include the '.'. - // so make sure it's there... + // Determine the mime type. + nsAutoCString typeToUse; + if (aTypeHint && *aTypeHint) { + typeToUse.Assign(aTypeHint); + } else if (!GetMIMETypeFromOSForExtension(NS_ConvertUTF16toUTF8(aFileExt), typeToUse)) { + return nullptr; + } + + RefPtr mimeInfo = new nsMIMEInfoWin(typeToUse); + + // windows registry assumes your file extension is going to include the '.', + // but our APIs expect it to not be there, so make sure we normalize that bit. nsAutoString fileExtToUse; if (aFileExt.First() != char16_t('.')) fileExtToUse = char16_t('.'); fileExtToUse.Append(aFileExt); - // Try to get an entry from the windows registry. - nsCOMPtr regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1"); - if (!regKey) - return nullptr; - - nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, - fileExtToUse, - nsIWindowsRegKey::ACCESS_QUERY_VALUE); - if (NS_FAILED(rv)) - return nullptr; - - nsAutoCString typeToUse; - if (aTypeHint && *aTypeHint) { - typeToUse.Assign(aTypeHint); - } - else { - nsAutoString temp; - if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), - temp)) || temp.IsEmpty()) { - return nullptr; - } - // Content-Type is always in ASCII - LossyAppendUTF16toASCII(temp, typeToUse); - } - - RefPtr mimeInfo = new nsMIMEInfoWin(typeToUse); - - // don't append the '.' + // don't append the '.' for our APIs. mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1))); mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); @@ -458,8 +440,17 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl } else { - found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), - appInfo)); + nsCOMPtr regKey = + do_CreateInstance("@mozilla.org/windows-registry-key;1"); + if (!regKey) + return nullptr; + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, + fileExtToUse, + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_SUCCEEDED(rv)) { + found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), + appInfo)); + } } // Bug 358297 - ignore the default handler, force the user to choose app @@ -596,3 +587,40 @@ nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, return NS_OK; } +bool +nsOSHelperAppService::GetMIMETypeFromOSForExtension(const nsACString& aExtension, + nsACString& aMIMEType) +{ + if (aExtension.IsEmpty()) + return false; + + // windows registry assumes your file extension is going to include the '.'. + // so make sure it's there... + nsAutoString fileExtToUse; + if (aExtension.First() != '.') + fileExtToUse = char16_t('.'); + + AppendUTF8toUTF16(aExtension, fileExtToUse); + + // Try to get an entry from the windows registry. + nsCOMPtr regKey = + do_CreateInstance("@mozilla.org/windows-registry-key;1"); + if (!regKey) + return false; + + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, + fileExtToUse, + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_FAILED(rv)) + return false; + + nsAutoString mimeType; + if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), + mimeType)) || mimeType.IsEmpty()) { + return false; + } + // Content-Type is always in ASCII + aMIMEType.Truncate(); + LossyAppendUTF16toASCII(mimeType, aMIMEType); + return true; +} diff --git a/uriloader/exthandler/win/nsOSHelperAppService.h b/uriloader/exthandler/win/nsOSHelperAppService.h index c5e707256..b00529433 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.h +++ b/uriloader/exthandler/win/nsOSHelperAppService.h @@ -40,6 +40,8 @@ public: NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme, bool *found, nsIHandlerInfo **_retval); + virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension, + nsACString& aMIMEType) override; /** Get the string value of a registry value and store it in result. * @return true on success, false on failure diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index f79d0d208..74850c84f 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -493,6 +493,9 @@ public: LayoutDeviceIntPoint CocoaPointsToDevPixels(const NSPoint& aPt) const { return nsCocoaUtils::CocoaPointsToDevPixels(aPt, BackingScaleFactor()); } + LayoutDeviceIntPoint CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt) const { + return nsCocoaUtils::CocoaPointsToDevPixelsRoundDown(aPt, BackingScaleFactor()); + } LayoutDeviceIntRect CocoaPointsToDevPixels(const NSRect& aRect) const { return nsCocoaUtils::CocoaPointsToDevPixels(aRect, BackingScaleFactor()); } diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 5ad518351..1063de489 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -195,6 +195,7 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; #endif - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint; +- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint; - (IAPZCTreeManager*)apzctm; - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent; @@ -4832,16 +4833,20 @@ AccumulateIntegerDelta(NSEvent* aEvent) static gfx::IntPoint GetIntegerDeltaForEvent(NSEvent* aEvent) { - if (nsCocoaFeatures::OnSierraOrLater()) { + if (nsCocoaFeatures::OnSierraOrLater() && [aEvent hasPreciseScrollingDeltas]) { + // Pixel scroll events (events with hasPreciseScrollingDeltas == YES) + // carry pixel deltas in the scrollingDeltaX/Y fields and line scroll + // information in the deltaX/Y fields. + // Prior to 10.12, these line scroll fields would be zero for most pixel + // scroll events and non-zero for some, whenever at least a full line + // worth of pixel scrolling had accumulated. That's the behavior we want. + // Starting with 10.12 however, pixel scroll events no longer accumulate + // deltaX and deltaY; they just report floating point values for every + // single event. So we need to do our own accumulation. return AccumulateIntegerDelta(aEvent); } - // Pre-10.12, deltaX/deltaY had the accumulation behavior that we want, and - // it worked more reliably than doing it on our own, so use it on pre-10.12 - // versions. For example, with a traditional USB mouse, the first wheel - // "tick" would always senda line scroll of at least one line, but with our - // own accumulation you sometimes need to do multiple wheel ticks before one - // line has been accumulated. + // For line scrolls, or pre-10.12, just use the rounded up value of deltaX / deltaY. return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY])); } @@ -4889,8 +4894,12 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(theEvent, [self window]); + // Use convertWindowCoordinatesRoundDown when converting the position to + // integer screen pixels in order to ensure that coordinates which are just + // inside the right / bottom edges of the window don't end up outside of the + // window after rounding. ScreenPoint position = ViewAs( - [self convertWindowCoordinates:locationInWindow], + [self convertWindowCoordinatesRoundDown:locationInWindow], PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent); bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(theEvent) && @@ -5603,6 +5612,16 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) return mGoannaChild->CocoaPointsToDevPixels(localPoint); } +- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint +{ + if (!mGoannaChild) { + return LayoutDeviceIntPoint(0, 0); + } + + NSPoint localPoint = [self convertPoint:aPoint fromView:nil]; + return mGoannaChild->CocoaPointsToDevPixelsRoundDown(localPoint); +} + - (IAPZCTreeManager*)apzctm { return mGoannaChild ? mGoannaChild->APZCTM() : nullptr; diff --git a/widget/cocoa/nsCocoaUtils.h b/widget/cocoa/nsCocoaUtils.h index 6c40d0839..0d29dfac3 100644 --- a/widget/cocoa/nsCocoaUtils.h +++ b/widget/cocoa/nsCocoaUtils.h @@ -144,6 +144,13 @@ public: NSToIntRound(aPt.y * aBackingScale)); } + static LayoutDeviceIntPoint + CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt, CGFloat aBackingScale) + { + return LayoutDeviceIntPoint(NSToIntFloor(aPt.x * aBackingScale), + NSToIntFloor(aPt.y * aBackingScale)); + } + static LayoutDeviceIntRect CocoaPointsToDevPixels(const NSRect& aRect, CGFloat aBackingScale) { diff --git a/widget/windows/nsAppShell.cpp b/widget/windows/nsAppShell.cpp index a0eac50e4..aa997308d 100644 --- a/widget/windows/nsAppShell.cpp +++ b/widget/windows/nsAppShell.cpp @@ -25,6 +25,12 @@ #include "nsComponentManagerUtils.h" #include "nsITimer.h" +// These are two messages that the code in winspool.drv on Windows 7 explicitly +// waits for while it is pumping other Windows messages, during display of the +// Printer Properties dialog. +#define MOZ_WM_PRINTER_PROPERTIES_COMPLETION 0x5b7a +#define MOZ_WM_PRINTER_PROPERTIES_FAILURE 0x5b7f + using namespace mozilla; using namespace mozilla::widget; @@ -367,6 +373,15 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) continue; // the message is consumed. } + // Store Printer Properties messages for reposting, because they are not + // processed by a window procedure, but are explicitly waited for in the + // winspool.drv code that will be further up the stack. + if (msg.message == MOZ_WM_PRINTER_PROPERTIES_COMPLETION || + msg.message == MOZ_WM_PRINTER_PROPERTIES_FAILURE) { + mMsgsToRepost.push_back(msg); + continue; + } + ::TranslateMessage(&msg); ::DispatchMessageW(&msg); } @@ -402,3 +417,16 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) return gotMessage; } + +nsresult +nsAppShell::AfterProcessNextEvent(nsIThreadInternal* /* unused */, + bool /* unused */) +{ + if (!mMsgsToRepost.empty()) { + for (MSG msg : mMsgsToRepost) { + ::PostMessageW(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + mMsgsToRepost.clear(); + } + return NS_OK; +} diff --git a/widget/windows/nsAppShell.h b/widget/windows/nsAppShell.h index 874090711..f14629a47 100644 --- a/widget/windows/nsAppShell.h +++ b/widget/windows/nsAppShell.h @@ -8,6 +8,7 @@ #include "nsBaseAppShell.h" #include +#include #include "mozilla/TimeStamp.h" #include "mozilla/Mutex.h" @@ -34,6 +35,9 @@ public: static UINT GetTaskbarButtonCreatedMessage(); + NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal* thread, + bool eventWasProcessed) final; + protected: NS_IMETHOD Run(); NS_IMETHOD Exit(); @@ -49,6 +53,7 @@ protected: Mutex mLastNativeEventScheduledMutex; TimeStamp mLastNativeEventScheduled; + std::vector mMsgsToRepost; }; #endif // nsAppShell_h__ diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp index bbd69abea..3dd3bd36c 100644 --- a/xpcom/ds/nsAtomTable.cpp +++ b/xpcom/ds/nsAtomTable.cpp @@ -634,13 +634,8 @@ RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount) if (!atom->IsStaticAtom()) { nsAutoCString name; atom->ToUTF8String(name); - - static char sCrashReason[1024]; - SprintfLiteral(sCrashReason, - "static atom registration for %s should be pushed back", - name.get()); - MOZ_CRASH_ANNOTATE(sCrashReason); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF( + "Static atom registration for %s should be pushed back", name.get()); } } else { atom = new StaticAtom(stringBuffer, stringLen, hash); diff --git a/xpcom/glue/nsTArray.cpp b/xpcom/glue/nsTArray.cpp index 81a5799f1..fd8422ec7 100644 --- a/xpcom/glue/nsTArray.cpp +++ b/xpcom/glue/nsTArray.cpp @@ -9,6 +9,7 @@ #include "nsXPCOM.h" #include "nsDebug.h" #include "mozilla/CheckedInt.h" +#include "mozilla/IntegerPrintfMacros.h" nsTArrayHeader nsTArrayHeader::sEmptyHdr = { 0, 0, 0 }; @@ -22,15 +23,7 @@ IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, size_t aElemSize) MOZ_NORETURN MOZ_COLD void InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength) { - const size_t CAPACITY = 512; - // Leak the buffer on the heap to make sure that it lives long enough, as - // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of - // the program. - auto* buffer = new char[CAPACITY]; - snprintf(buffer, CAPACITY, - "ElementAt(aIndex = %llu, aLength = %llu)", - (long long unsigned) aIndex, - (long long unsigned) aLength); - MOZ_CRASH_ANNOTATE(buffer); - MOZ_REALLY_CRASH(); + MOZ_CRASH_UNSAFE_PRINTF( + "ElementAt(aIndex = %" PRIu64 ", aLength = %" PRIu64 ")", + static_cast(aIndex), static_cast(aLength)); }