From 7c37532cdfde32babf56ebf3dbb590b5f9c8af94 Mon Sep 17 00:00:00 2001 From: Nomis101 Date: Mon, 14 Jan 2019 00:08:54 +0100 Subject: [PATCH 01/33] Bug 1519740 - Include MacPorts Mojave package in bootstrap.py. r=glandium --HG-- extra : amend_source : 7457ca919e02b7d803821bc98ceede57d6be9d1d --- python/mozboot/mozboot/osx.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/python/mozboot/mozboot/osx.py b/python/mozboot/mozboot/osx.py index c81636cf8d4f..2aa3d0cceeb4 100644 --- a/python/mozboot/mozboot/osx.py +++ b/python/mozboot/mozboot/osx.py @@ -24,14 +24,15 @@ XCODE_LEGACY = ('https://developer.apple.com/downloads/download.action?path=Deve 'xcode_3.2.6_and_ios_sdk_4.3__final/xcode_3.2.6_and_ios_sdk_4.3.dmg') MACPORTS_URL = { - '13': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.13-HighSierra.pkg', - '12': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.12-Sierra.pkg', - '11': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.11-ElCapitan.pkg', - '10': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.10-Yosemite.pkg', - '9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.9-Mavericks.pkg', - '8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.8-MountainLion.pkg', - '7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.7-Lion.pkg', - '6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.6-SnowLeopard.pkg', } + '14': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.14-Mojave.pkg', + '13': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.13-HighSierra.pkg', + '12': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.12-Sierra.pkg', + '11': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.11-ElCapitan.pkg', + '10': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.10-Yosemite.pkg', + '9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.9-Mavericks.pkg', + '8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.8-MountainLion.pkg', + '7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.7-Lion.pkg', + '6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.6-SnowLeopard.pkg', } RE_CLANG_VERSION = re.compile('Apple (?:clang|LLVM) version (\d+\.\d+)') From 26f7ea0dec5ffdd73df7de3979b53b8a19cba4f6 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 15 Jan 2019 14:00:31 +0100 Subject: [PATCH 02/33] Bug 1519092 - Fix a shadowing variable in WptreportHandler; r=jgraham --- js/src/tests/lib/wptreport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/tests/lib/wptreport.py b/js/src/tests/lib/wptreport.py index 7d9c15847ea8..19b742bbd2b5 100644 --- a/js/src/tests/lib/wptreport.py +++ b/js/src/tests/lib/wptreport.py @@ -66,8 +66,8 @@ class WptreportHandler(object): "time": start_time, }) - for result in result["subtests"]: - self.formatter.test_status(result) + for subtest in result["subtests"]: + self.formatter.test_status(subtest) self.formatter.test_end({ "test": testname, From d138c7661ffb51aed4e80c2a0f18eceb86f0a1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 14 Jan 2019 01:52:12 -0800 Subject: [PATCH 03/33] Bug 1519795: Adjust CallInfo::newTarget to allow inlining polymorphic construct-calls of native functions. r=jandem --HG-- extra : rebase_source : 81f41b314eebb9980f81f44f5e9beef82b65ac32 --- js/src/jit/IonBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 47e52b280af9..5291ed2c551e 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -4796,6 +4796,10 @@ AbortReasonOr IonBuilder::inlineCalls(CallInfo& callInfo, inlineInfo.popCallStack(inlineBlock); inlineInfo.setFun(funcDef); + if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun()) { + inlineInfo.setNewTarget(funcDef); + } + if (maybeCache) { // Assign the 'this' value a TypeSet specialized to the groups that // can generate this inlining target. From acd97a76081b8be8d75c94415547de03d4230d89 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Tue, 15 Jan 2019 15:12:30 +0100 Subject: [PATCH 04/33] Bug 1519401 - ARM64: Fix CodeGenerator::LBitAndAndBranch to use the condition flag of the MIR. r=sstangl --- js/src/jit/arm64/CodeGenerator-arm64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp index 006dfb9b4990..a774b4a0cb0d 100644 --- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -1456,7 +1456,7 @@ void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) { } else { masm.Tst(toWRegister(baab->left()), toWRegister(baab->right())); } - emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse()); + emitBranch(baab->cond(), baab->ifTrue(), baab->ifFalse()); } void CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir) { From 2d6036e8040b7f085e86a8edc97c052d6c516c56 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Mon, 17 Dec 2018 08:31:04 -0500 Subject: [PATCH 05/33] Bug 1510139 - Part 1. Ensure we honor invalidations if using vector image containers. r=tnikkel If a vector image has an image container, it is unlikely the caller will call VectorImage::Draw (and thus Show indirectly) to display the image. As such, WebRender was missing subsequent invalidations and not regenerating the rasterized surface as expected. Thus we now resume honoring the invalidations if we updated the image container. Differential Revision: https://phabricator.services.mozilla.com/D15667 --- image/Image.cpp | 4 +++- image/Image.h | 7 ++++++- image/VectorImage.cpp | 13 ++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/image/Image.cpp b/image/Image.cpp index a0889729ebfd..dfdf09b71920 100644 --- a/image/Image.cpp +++ b/image/Image.cpp @@ -262,7 +262,7 @@ ImgDrawResult ImageResource::GetImageContainerImpl( return drawResult; } -void ImageResource::UpdateImageContainer(const Maybe& aDirtyRect) { +bool ImageResource::UpdateImageContainer(const Maybe& aDirtyRect) { MOZ_ASSERT(NS_IsMainThread()); for (int i = mImageContainers.Length() - 1; i >= 0; --i) { @@ -289,6 +289,8 @@ void ImageResource::UpdateImageContainer(const Maybe& aDirtyRect) { mImageContainers.RemoveElementAt(i); } } + + return !mImageContainers.IsEmpty(); } void ImageResource::ReleaseImageContainer() { diff --git a/image/Image.h b/image/Image.h index f436d1b8ef6a..58202b398e2f 100644 --- a/image/Image.h +++ b/image/Image.h @@ -368,7 +368,12 @@ class ImageResource : public Image { uint32_t aFlags, layers::ImageContainer** aContainer); - void UpdateImageContainer(const Maybe& aDirtyRect); + /** + * Re-requests the appropriate frames for each image container using + * GetFrameInternal. + * @returns True if any image containers were updated, else false. + */ + bool UpdateImageContainer(const Maybe& aDirtyRect); void ReleaseImageContainer(); diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 6e584819525e..c62863e73733 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -563,13 +563,20 @@ void VectorImage::SendInvalidationNotifications() { // we would miss the subsequent invalidations if we didn't send out the // notifications directly in |InvalidateObservers...|. + SurfaceCache::RemoveImage(ImageKey(this)); + + if (UpdateImageContainer(Nothing())) { + // If we have image containers, that means we probably won't get a Draw call + // from the owner since they are using the container. We must assume all + // invalidations need to be handled. + MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now"); + mRenderingObserver->ResumeHonoringInvalidations(); + } + if (mProgressTracker) { - SurfaceCache::RemoveImage(ImageKey(this)); mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE, GetMaxSizedIntRect()); } - - UpdateImageContainer(Nothing()); } NS_IMETHODIMP_(IntRect) From ddb99ba9aff495fc6335b114332b5eb2b46e93fd Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Thu, 3 Jan 2019 12:55:06 -0500 Subject: [PATCH 06/33] Bug 1510139 - Part 2. Do not invalidate vector image containers synchronously. r=tnikkel Originally we would invalidate image containers synchronously when SVGRootRenderingObserver::OnRenderingChange was called. However at this point in time, the layout tree is in the middle of updating its own state, and triggering a paint will be problematic. Animated vector images did not suffer from this problem because they would defer to the next refresh tick, but non-animated do not receive refresh tick events. As such, we should just defer the invalidation to immediately after the layout tree has finished updating itself. Differential Revision: https://phabricator.services.mozilla.com/D15668 --- image/VectorImage.cpp | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index c62863e73733..f194f4c043f5 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -542,7 +542,6 @@ VectorImage::RequestRefresh(const TimeStamp& aTime) { mSVGDocumentWrapper->TickRefreshDriver(); if (mHasPendingInvalidation) { - mHasPendingInvalidation = false; SendInvalidationNotifications(); } } @@ -555,14 +554,16 @@ void VectorImage::SendInvalidationNotifications() { // notifications there to ensure that there is actually a document observing // us. Otherwise, the notifications are just wasted effort. // - // Non-animated images call this method directly from + // Non-animated images post an event to call this method from // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never // called for them. Ordinarily this isn't needed, since we send out // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the // SVG document may not be 100% ready to render at that time. In those cases // we would miss the subsequent invalidations if we didn't send out the - // notifications directly in |InvalidateObservers...|. + // notifications indirectly in |InvalidateObservers...|. + MOZ_ASSERT(mHasPendingInvalidation); + mHasPendingInvalidation = false; SurfaceCache::RemoveImage(ImageKey(this)); if (UpdateImageContainer(Nothing())) { @@ -1472,11 +1473,37 @@ VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt, // Invalidation helper method void VectorImage::InvalidateObserversOnNextRefreshDriverTick() { - if (mHaveAnimations) { - mHasPendingInvalidation = true; - } else { - SendInvalidationNotifications(); + if (mHasPendingInvalidation) { + return; } + + mHasPendingInvalidation = true; + + // Animated images can wait for the refresh tick. + if (mHaveAnimations) { + return; + } + + // Non-animated images won't get the refresh tick, so we should just send an + // invalidation outside the current execution context. We need to defer + // because the layout tree is in the middle of invalidation, and the tree + // state needs to be consistent. Specifically only some of the frames have + // had the NS_FRAME_DESCENDANT_NEEDS_PAINT and/or NS_FRAME_NEEDS_PAINT bits + // set by InvalidateFrameInternal in layout/generic/nsFrame.cpp. These bits + // get cleared when we repaint the SVG into a surface by + // nsIFrame::ClearInvalidationStateBits in nsDisplayList::PaintRoot. + nsCOMPtr eventTarget; + if (mProgressTracker) { + eventTarget = mProgressTracker->GetEventTarget(); + } else { + eventTarget = do_GetMainThread(); + } + + RefPtr self(this); + nsCOMPtr ev(NS_NewRunnableFunction( + "VectorImage::SendInvalidationNotifications", + [=]() -> void { self->SendInvalidationNotifications(); })); + eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL); } void VectorImage::PropagateUseCounters(Document* aParentDocument) { From 815739f24ab3ef4cdfcba33b76975098d5f481ca Mon Sep 17 00:00:00 2001 From: Mark Striemer Date: Tue, 15 Jan 2019 08:52:42 -0600 Subject: [PATCH 07/33] Bug 1303384 - Part 5: Fix TODO and string in .ftl r=flod --HG-- extra : rebase_source : 3b39c63185633a44653b1c63497e71c78329bf2e --- toolkit/locales/en-US/toolkit/about/aboutAddons.ftl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl index d4869d9b19bd..72a40d5e042d 100644 --- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl +++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl @@ -281,8 +281,7 @@ extensions-updates-update-selected = shortcuts-manage = .label = Keyboard Shortcuts shortcuts-empty-message = There are no shortcuts for this extension. -# TODO: Confirm this copy. -shortcuts-no-addons = You don't have any active add-ons. +shortcuts-no-addons = You don’t have any extensions enabled. shortcuts-input = .placeholder = Type a shortcut From 4ca052c31051730459d7df2cef1791d1034a7cd2 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 15 Jan 2019 05:38:46 -1000 Subject: [PATCH 08/33] Bug 1517837 - Move web replay tests to their own directory, r=lsmyth. --HG-- rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js => devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js => devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js => devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js => devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js => devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js => devtools/client/webreplay/mochitest/browser_dbg_rr_record.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js => devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js => devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js => devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js => devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js rename : devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js => devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js rename : devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html => devtools/client/webreplay/mochitest/examples/doc_rr_basic.html rename : devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html => devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html rename : devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html => devtools/client/webreplay/mochitest/examples/doc_rr_error.html rename : devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html => devtools/client/webreplay/mochitest/examples/doc_rr_logs.html rename : devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html => devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html extra : rebase_source : 931ea1f66514c15f32e1e77d28afe48ef5cfde20 --- .eslintignore | 1 + .../debugger/new/test/mochitest/browser.ini | 38 ---- .../new/test/mochitest/browser_dbg-console.js | 2 +- .../browser_dbg_rr_console_warp-02.js | 48 ----- .../debugger/new/test/mochitest/head.js | 200 ------------------ .../debugger/new/test/mochitest/helpers.js | 72 +++++++ .../client/webreplay/mochitest/browser.ini | 38 ++++ .../browser_dbg_rr_breakpoints-01.js | 12 +- .../browser_dbg_rr_breakpoints-02.js | 15 +- .../browser_dbg_rr_breakpoints-03.js | 16 +- .../browser_dbg_rr_breakpoints-04.js | 14 +- .../browser_dbg_rr_breakpoints-05.js | 14 +- .../browser_dbg_rr_console_warp-01.js | 30 +-- .../browser_dbg_rr_console_warp-02.js | 33 +++ .../mochitest/browser_dbg_rr_record.js | 14 +- .../mochitest/browser_dbg_rr_recovery-01.js | 27 ++- .../mochitest/browser_dbg_rr_replay-01.js | 24 ++- .../mochitest/browser_dbg_rr_replay-02.js | 21 +- .../mochitest/browser_dbg_rr_replay-03.js | 22 +- .../mochitest/browser_dbg_rr_stepping-01.js | 16 +- .../mochitest/browser_dbg_rr_stepping-02.js | 16 +- .../mochitest/browser_dbg_rr_stepping-03.js | 18 +- .../mochitest/browser_dbg_rr_stepping-04.js | 16 +- .../mochitest/examples/doc_rr_basic.html | 0 .../mochitest/examples/doc_rr_continuous.html | 0 .../mochitest/examples/doc_rr_error.html | 0 .../mochitest/examples/doc_rr_logs.html | 0 .../mochitest/examples/doc_rr_recovery.html | 0 devtools/client/webreplay/mochitest/head.js | 144 +++++++++++++ devtools/client/webreplay/moz.build | 2 + 30 files changed, 442 insertions(+), 411 deletions(-) delete mode 100644 devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js create mode 100644 devtools/client/webreplay/mochitest/browser.ini rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_breakpoints-01.js (92%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_breakpoints-02.js (82%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_breakpoints-03.js (77%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_breakpoints-04.js (86%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_breakpoints-05.js (84%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_console_warp-01.js (57%) create mode 100644 devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_record.js (69%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_recovery-01.js (53%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_replay-01.js (65%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_replay-02.js (74%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_replay-03.js (63%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_stepping-01.js (76%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_stepping-02.js (75%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_stepping-03.js (69%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/browser_dbg_rr_stepping-04.js (82%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/examples/doc_rr_basic.html (100%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/examples/doc_rr_continuous.html (100%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/examples/doc_rr_error.html (100%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/examples/doc_rr_logs.html (100%) rename devtools/client/{debugger/new/test => webreplay}/mochitest/examples/doc_rr_recovery.html (100%) create mode 100644 devtools/client/webreplay/mochitest/head.js diff --git a/.eslintignore b/.eslintignore index 35dfd77cb57a..e7999a5eafee 100644 --- a/.eslintignore +++ b/.eslintignore @@ -106,6 +106,7 @@ devtools/client/shared/webpack/shims/test/test_clipboard.html devtools/shared/qrcode/tests/mochitest/test_decode.html devtools/shared/tests/mochitest/*.html devtools/shared/webconsole/test/test_*.html +devtools/client/webreplay/mochitest/examples/*.html # Soon to be removed, the new/ directory is explicitly excluded below due to # also being an imported repository. diff --git a/devtools/client/debugger/new/test/mochitest/browser.ini b/devtools/client/debugger/new/test/mochitest/browser.ini index 497d58d9ab09..a28bb68efefc 100644 --- a/devtools/client/debugger/new/test/mochitest/browser.ini +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -656,11 +656,6 @@ support-files = examples/simple-worker.js examples/doc-event-handler.html examples/doc-eval-throw.html - examples/doc_rr_basic.html - examples/doc_rr_continuous.html - examples/doc_rr_logs.html - examples/doc_rr_recovery.html - examples/doc_rr_error.html [browser_dbg-asm.js] [browser_dbg-async-stepping.js] @@ -775,36 +770,3 @@ skip-if = true [browser_dbg-windowless-workers.js] [browser_dbg-event-handler.js] [browser_dbg-eval-throw.js] -[browser_dbg_rr_breakpoints-01.js] -skip-if = true # bug 1513057 os != "mac" || debug || !nightly_build -[browser_dbg_rr_breakpoints-02.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_breakpoints-03.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_breakpoints-04.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_breakpoints-05.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_record.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_stepping-01.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_stepping-02.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_stepping-03.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_stepping-04.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_recovery-01.js] -skip-if = true # See bug 1481009 -[browser_dbg_rr_replay-01.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_replay-02.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_replay-03.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_console_warp-01.js] -skip-if = true # os != "mac" || debug || !nightly_build -[browser_dbg_rr_console_warp-02.js] -skip-if = true # os != "mac" || debug || !nightly_build - diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js index 965b36b84d55..cc5d2acd52f7 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js @@ -5,7 +5,7 @@ add_task(async function() { await selectSource(dbg, "switching-01"); // open the console - await getSplitConsole(dbg); + await getDebuggerSplitConsole(dbg); ok(dbg.toolbox.splitConsole, "Split console is shown."); // close the console diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js deleted file mode 100644 index 6502156c75b4..000000000000 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - - -// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js, -// since this test straddles both the web console and the debugger. I couldn't -// figure out how to load that script directly here. -function waitForThreadEvents(threadClient, eventName) { - info(`Waiting for thread event '${eventName}' to fire.`); - - return new Promise(function(resolve, reject) { - threadClient.addListener(eventName, function onEvent(eventName, ...args) { - info(`Thread event '${eventName}' fired.`); - threadClient.removeListener(eventName, onEvent); - resolve.apply(resolve, args); - }); - }); -} - - -// Test basic console time warping functionality in web replay. -async function test() { - waitForExplicitFinish(); - - const dbg = await attachRecordingDebugger( - "doc_rr_logs.html", - { waitForRecording: true } - ); - - const {tab, toolbox, threadClient} = dbg; - const console = await getSplitConsole(dbg); - const hud = console.hud; - - let message = await warpToMessage(hud, threadClient, "number: 1"); - ok(!message.classList.contains("paused-before"), "paused before message is not shown"); - - await stepOverToLine(threadClient, 18); - await reverseStepOverToLine(threadClient, 17); - - message = findMessage(hud, "number: 1") - ok(message.classList.contains("paused-before"), "paused before message is shown"); - - await toolbox.destroy(); - await gBrowser.removeTab(tab); - finish(); -} diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js index 7b694374d9db..4ea3838d6668 100644 --- a/devtools/client/debugger/new/test/mochitest/head.js +++ b/devtools/client/debugger/new/test/mochitest/head.js @@ -47,16 +47,6 @@ const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/"; -async function waitUntilPredicate(predicate) { - let result; - await waitUntil(() => { - result = predicate(); - return result; - }) - - return result; -} - // NOTE: still experimental, the screenshots might not be exactly correct async function takeScreenshot(dbg) { let canvas = dbg.win.document.createElementNS( @@ -70,193 +60,3 @@ async function takeScreenshot(dbg) { await waitForTime(1000); dump(`[SCREENSHOT] ${canvas.toDataURL()}\n`); } - -// Attach a debugger to a tab, returning a promise that resolves with the -// debugger's toolbox. -async function attachDebugger(tab) { - let target = await TargetFactory.forTab(tab); - let toolbox = await gDevTools.showToolbox(target, "jsdebugger"); - return toolbox; -} - -async function attachRecordingDebugger(url, { waitForRecording } = { waitForRecording: false }) { - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); - gBrowser.selectedTab = tab; - openTrustedLinkIn(EXAMPLE_URL + url, "current"); - - if (waitForRecording) { - await once(Services.ppmm, "RecordingFinished"); - } - const toolbox = await attachDebugger(tab); - const dbg = createDebuggerContext(toolbox) - const threadClient = dbg.toolbox.threadClient; - - await threadClient.interrupt(); - return {...dbg, tab, threadClient}; -} - - -// Return a promise with a reference to jsterm, opening the split -// console if necessary. This cleans up the split console pref so -// it won't pollute other tests. -async function getSplitConsole(dbg) { - const { toolbox, win } = dbg; - - if (!win) { - win = toolbox.win; - } - - if (!toolbox.splitConsole) { - pressKey(dbg, "Escape"); - } - - await toolbox.openSplitConsole(); - return toolbox.getPanel("webconsole"); -} - - -// Return a promise that resolves when a breakpoint has been set. -async function setBreakpoint(threadClient, expectedFile, lineno) { - let {sources} = await threadClient.getSources(); - ok(sources.length == 1, "Got one source"); - ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile); - let sourceClient = threadClient.source(sources[0]); - await sourceClient.setBreakpoint({ line: lineno }); -} - -function resumeThenPauseAtLineFunctionFactory(method) { - return async function(threadClient, lineno) { - threadClient[method](); - await threadClient.addOneTimeListener("paused", async function(event, packet) { - let {frames} = await threadClient.getFrames(0, 1); - let frameLine = frames[0] ? frames[0].where.line : undefined; - ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno); - }); - }; -} - -// Define various methods that resume a thread in a specific way and ensure it -// pauses at a specified line. -var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind"); -var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume"); -var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver"); -var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver"); -var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn"); -var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn"); -var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut"); -var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut"); - -// Return a promise that resolves with the result of a thread evaluating a -// string in the topmost frame. -async function evaluateInTopFrame(threadClient, text) { - let {frames} = await threadClient.getFrames(0, 1); - ok(frames.length == 1, "Got one frame"); - let response = await threadClient.eval(frames[0].actor, text); - ok(response.type == "resumed", "Got resume response from eval"); - let rval; - await threadClient.addOneTimeListener("paused", function(event, packet) { - ok(packet.type == "paused" && - packet.why.type == "clientEvaluated" && - "return" in packet.why.frameFinished, "Eval returned a value"); - rval = packet.why.frameFinished["return"]; - }); - return (rval.type == "undefined") ? undefined : rval; -} - -// Return a promise that resolves when a thread evaluates a string in the -// topmost frame, ensuring the result matches the expected value. -async function checkEvaluateInTopFrame(threadClient, text, expected) { - let rval = await evaluateInTopFrame(threadClient, text); - ok(rval == expected, "Eval returned " + expected); -} - -// Return a promise that resolves when a thread evaluates a string in the -// topmost frame, with the result throwing an exception. -async function checkEvaluateInTopFrameThrows(threadClient, text) { - let {frames} = await threadClient.getFrames(0, 1); - ok(frames.length == 1, "Got one frame"); - let response = await threadClient.eval(frames[0].actor, text); - ok(response.type == "resumed", "Got resume response from eval"); - await threadClient.addOneTimeListener("paused", function(event, packet) { - ok(packet.type == "paused" && - packet.why.type == "clientEvaluated" && - "throw" in packet.why.frameFinished, "Eval threw an exception"); - }); -} - -// Return a pathname that can be used for a new recording file. -function newRecordingFile() { - ChromeUtils.import("resource://gre/modules/osfile.jsm", this); - return OS.Path.join(OS.Constants.Path.tmpDir, - "MochitestRecording" + Math.round(Math.random() * 1000000000)); -} - - -async function warpToMessage(hud, threadClient, text) { - let messages = await waitForMessages(hud, text); - ok(messages.length == 1, "Found one message"); - let message = messages.pop(); - - let menuPopup = await openConsoleContextMenu(hud, message); - console.log(`.>> menu`, menuPopup); - - - let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp"); - ok(timeWarpItem, "Time warp menu item is available"); - - timeWarpItem.click(); - - await Promise.all([ - hideConsoleContextMenu(hud), - once(Services.ppmm, "TimeWarpFinished"), - waitForThreadEvents(threadClient, 'paused') - ]); - - messages = findMessages(hud, "", ".paused"); - ok(messages.length == 1, "Found one paused message"); - - return message; -} - - -function findMessage(hud, text, selector = ".message") { - return findMessages(hud, text, selector)[0] -} - -function findMessages(hud, text, selector = ".message") { - const messages = hud.ui.outputNode.querySelectorAll(selector); - const elements = Array.prototype.filter.call( - messages, - (el) => el.textContent.includes(text) - ); - - if (elements.length == 0) { - return null; - } - - return elements; -} - -function waitForMessages(hud, text, selector = ".message") { - return waitUntilPredicate(() => findMessages(hud, text, selector)) -} - -async function openConsoleContextMenu(hud, element) { - const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open"); - synthesizeContextMenuEvent(element); - await onConsoleMenuOpened; - const doc = hud.ui.consoleOutput.owner.chromeWindow.document; - return doc.getElementById("webconsole-menu"); -} - -function hideConsoleContextMenu(hud) { - const doc = hud.ui.consoleOutput.owner.chromeWindow.document; - const popup = doc.getElementById("webconsole-menu"); - if (!popup) { - return Promise.resolve(); - } - - const onPopupHidden = once(popup, "popuphidden"); - popup.hidePopup(); - return onPopupHidden; -} diff --git a/devtools/client/debugger/new/test/mochitest/helpers.js b/devtools/client/debugger/new/test/mochitest/helpers.js index 412552adbea5..d50820ccb66f 100644 --- a/devtools/client/debugger/new/test/mochitest/helpers.js +++ b/devtools/client/debugger/new/test/mochitest/helpers.js @@ -1462,3 +1462,75 @@ async function editExpression(dbg, input) { pressKey(dbg, "Enter"); await evaluated; } + +async function waitUntilPredicate(predicate) { + let result; + await waitUntil(() => { + result = predicate(); + return result; + }) + + return result; +} + +// Return a promise with a reference to jsterm, opening the split +// console if necessary. This cleans up the split console pref so +// it won't pollute other tests. +async function getDebuggerSplitConsole(dbg) { + const { toolbox, win } = dbg; + + if (!win) { + win = toolbox.win; + } + + if (!toolbox.splitConsole) { + pressKey(dbg, "Escape"); + } + + await toolbox.openSplitConsole(); + return toolbox.getPanel("webconsole"); +} + +async function openConsoleContextMenu(hud, element) { + const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open"); + synthesizeContextMenuEvent(element); + await onConsoleMenuOpened; + const doc = hud.ui.consoleOutput.owner.chromeWindow.document; + return doc.getElementById("webconsole-menu"); +} + +function hideConsoleContextMenu(hud) { + const doc = hud.ui.consoleOutput.owner.chromeWindow.document; + const popup = doc.getElementById("webconsole-menu"); + if (!popup) { + return Promise.resolve(); + } + + const onPopupHidden = once(popup, "popuphidden"); + popup.hidePopup(); + return onPopupHidden; +} + +// Return a promise that resolves with the result of a thread evaluating a +// string in the topmost frame. +async function evaluateInTopFrame(threadClient, text) { + const {frames} = await threadClient.getFrames(0, 1); + ok(frames.length == 1, "Got one frame"); + const response = await threadClient.eval(frames[0].actor, text); + ok(response.type == "resumed", "Got resume response from eval"); + let rval; + await threadClient.addOneTimeListener("paused", function(event, packet) { + ok(packet.type == "paused" && + packet.why.type == "clientEvaluated" && + "return" in packet.why.frameFinished, "Eval returned a value"); + rval = packet.why.frameFinished.return; + }); + return (rval.type == "undefined") ? undefined : rval; +} + +// Return a promise that resolves when a thread evaluates a string in the +// topmost frame, ensuring the result matches the expected value. +async function checkEvaluateInTopFrame(threadClient, text, expected) { + const rval = await evaluateInTopFrame(threadClient, text); + ok(rval == expected, "Eval returned " + expected); +} diff --git a/devtools/client/webreplay/mochitest/browser.ini b/devtools/client/webreplay/mochitest/browser.ini new file mode 100644 index 000000000000..92e99ea8e16f --- /dev/null +++ b/devtools/client/webreplay/mochitest/browser.ini @@ -0,0 +1,38 @@ +[DEFAULT] +tags = devtools +subsuite = devtools + +# Feel free to set this to true if an impending change breaks Web Replay and +# fixing it would be annoying or difficult. This will avoid running all tests +# that use Web Replay; we don't want this experimental feature to impede +# development in the rest of Gecko. +# +# Please file a bug against the 'Core > Web Replay' component if you do so, +# so that the problem can be fixed and tests reenabled. +skip-if = os != "mac" || debug || !nightly_build + +support-files = + head.js + examples/doc_rr_basic.html + examples/doc_rr_continuous.html + examples/doc_rr_logs.html + examples/doc_rr_recovery.html + examples/doc_rr_error.html + +[browser_dbg_rr_breakpoints-01.js] +[browser_dbg_rr_breakpoints-02.js] +[browser_dbg_rr_breakpoints-03.js] +[browser_dbg_rr_breakpoints-04.js] +[browser_dbg_rr_breakpoints-05.js] +[browser_dbg_rr_record.js] +[browser_dbg_rr_stepping-01.js] +[browser_dbg_rr_stepping-02.js] +[browser_dbg_rr_stepping-03.js] +[browser_dbg_rr_stepping-04.js] +[browser_dbg_rr_recovery-01.js] +skip-if = true # See bug 1481009 +[browser_dbg_rr_replay-01.js] +[browser_dbg_rr_replay-02.js] +[browser_dbg_rr_replay-03.js] +[browser_dbg_rr_console_warp-01.js] +[browser_dbg_rr_console_warp-02.js] diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js similarity index 92% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js index d734dff0e8af..cb908c88211c 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js @@ -2,11 +2,14 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test basic breakpoint functionality in web replay. -async function test() { - waitForExplicitFinish(); - +add_task(async function() { const dbg = await attachRecordingDebugger( "doc_rr_basic.html", { waitForRecording: true } @@ -38,5 +41,4 @@ async function test() { await toolbox.closeToolbox(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js similarity index 82% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js index 96f80555784e..f753cf75a081 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js @@ -2,12 +2,16 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test unhandled divergence while evaluating at a breakpoint with Web Replay. -async function test() { - waitForExplicitFinish(); - - const dbg = await attachRecordingDebugger("doc_rr_basic.html", { waitForRecording: true }); +add_task(async function() { + const dbg = await attachRecordingDebugger("doc_rr_basic.html", + { waitForRecording: true }); const {threadClient, tab, toolbox} = dbg; await setBreakpoint(threadClient, "doc_rr_basic.html", 21); @@ -21,5 +25,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js similarity index 77% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js index 9209fef2295f..ba7786c86c8a 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js @@ -2,11 +2,14 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test some issues when stepping around after hitting a breakpoint while recording. -async function test() { - waitForExplicitFinish(); - +add_task(async function() { const dbg = await attachRecordingDebugger("doc_rr_continuous.html"); const {threadClient, tab, toolbox} = dbg; @@ -14,7 +17,9 @@ async function test() { await setBreakpoint(threadClient, "doc_rr_continuous.html", 19); await resumeToLine(threadClient, 19); await reverseStepOverToLine(threadClient, 18); - await checkEvaluateInTopFrame(threadClient, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined); + await checkEvaluateInTopFrame(threadClient, + "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", + undefined); await stepInToLine(threadClient, 22); await setBreakpoint(threadClient, "doc_rr_continuous.html", 24); await resumeToLine(threadClient, 24); @@ -23,5 +28,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js similarity index 86% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js index 2a5626f97ae2..50996d653cc6 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js @@ -2,18 +2,21 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test navigating back to earlier breakpoints while recording, then resuming // recording. -async function test() { - waitForExplicitFinish(); - +add_task(async function() { const dbg = await attachRecordingDebugger("doc_rr_continuous.html"); const {threadClient, tab, toolbox} = dbg; await setBreakpoint(threadClient, "doc_rr_continuous.html", 14); await resumeToLine(threadClient, 14); - let value = await evaluateInTopFrame(threadClient, "number"); + const value = await evaluateInTopFrame(threadClient, "number"); await resumeToLine(threadClient, 14); await checkEvaluateInTopFrame(threadClient, "number", value + 1); await rewindToLine(threadClient, 14); @@ -29,5 +32,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js similarity index 84% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js index 09640a1af8c6..8e14957b73af 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js @@ -2,14 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test hitting breakpoints when rewinding past the point where the breakpoint // script was created. -async function test() { - waitForExplicitFinish(); - +add_task(async function() { const dbg = await attachRecordingDebugger( - "doc_rr_basic.html", + "doc_rr_basic.html", { waitForRecording: true } ); @@ -26,5 +29,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js similarity index 57% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js index 2dbce382cfe0..fd3452b90303 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js @@ -2,37 +2,24 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ -// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js, -// since this test straddles both the web console and the debugger. I couldn't -// figure out how to load that script directly here. -function waitForThreadEvents(threadClient, eventName) { - info(`Waiting for thread event '${eventName}' to fire.`); - - return new Promise(function(resolve, reject) { - threadClient.addListener(eventName, function onEvent(eventName, ...args) { - info(`Thread event '${eventName}' fired.`); - threadClient.removeListener(eventName, onEvent); - resolve.apply(resolve, args); - }); - }); -} +"use strict"; +// To disable all Web Replay tests, see browser.ini // Test basic console time warping functionality in web replay. -async function test() { - waitForExplicitFinish(); - +add_task(async function() { const dbg = await attachRecordingDebugger( - "doc_rr_error.html", + "doc_rr_error.html", { waitForRecording: true } ); const {tab, toolbox, threadClient} = dbg; - const console = await getSplitConsole(dbg); + const console = await getDebuggerSplitConsole(dbg); const hud = console.hud; - await warpToMessage(hud, threadClient, "Number 5"); + await warpToMessage(hud, dbg, "Number 5"); await threadClient.interrupt(); await checkEvaluateInTopFrame(threadClient, "number", 5); @@ -49,5 +36,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js new file mode 100644 index 000000000000..2f52a5ce3293 --- /dev/null +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini + +// Test basic console time warping functionality in web replay. +add_task(async function() { + const dbg = await attachRecordingDebugger( + "doc_rr_logs.html", + { waitForRecording: true } + ); + + const {tab, toolbox, threadClient} = dbg; + const console = await getDebuggerSplitConsole(dbg); + const hud = console.hud; + + let message = await warpToMessage(hud, dbg, "number: 1"); + ok(!message.classList.contains("paused-before"), "paused before message is not shown"); + + await stepOverToLine(threadClient, 18); + await reverseStepOverToLine(threadClient, 17); + + message = findMessage(hud, "number: 1"); + ok(message.classList.contains("paused-before"), "paused before message is shown"); + + await toolbox.destroy(); + await gBrowser.removeTab(tab); +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js similarity index 69% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_record.js index dd6042cc69b1..4f3358419172 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js @@ -2,12 +2,15 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test basic recording of a tab without any debugging. -async function test() { - waitForExplicitFinish(); - - var recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); @@ -15,5 +18,4 @@ async function test() { await gBrowser.removeTab(recordingTab); ok(true, "Finished"); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js similarity index 53% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js index b2b6bf82a5c9..8d2c751c72f7 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js @@ -2,27 +2,32 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test basic recovery of crashed child processes in web replay. -async function test() { - waitForExplicitFinish(); - - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let toolbox = await attachDebugger(tab), client = toolbox.threadClient; + const toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_recovery.html", 21); await rewindToLine(client, 21); - await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", undefined); + await checkEvaluateInTopFrame(client, + "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", + undefined); await stepOverToLine(client, 22); await stepOverToLine(client, 23); - await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " + - "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", undefined); - + await checkEvaluateInTopFrame(client, + "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " + + "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", + undefined); await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js similarity index 65% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js index 3f9eec2a81b1..d864f188d3ea 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js @@ -2,27 +2,32 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Basic test for saving a recording and then replaying it in a new tab. -async function test() { - waitForExplicitFinish(); - - let recordingFile = newRecordingFile(); - let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const recordingFile = newRecordingFile(); + const recordingTab = BrowserTestUtils.addTab(gBrowser, null, + { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); - let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); + const replayingTab = BrowserTestUtils.addTab(gBrowser, null, + { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); - let toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient; + const toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -35,5 +40,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(recordingTab); await gBrowser.removeTab(replayingTab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js similarity index 74% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js index 57edaa8aa728..a529ca119d8a 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js @@ -2,13 +2,18 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test ending a recording at a breakpoint and then separately replaying to the end. -async function test() { +add_task(async function() { waitForExplicitFinish(); - let recordingFile = newRecordingFile(); - let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + const recordingFile = newRecordingFile(); + const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current"); @@ -18,9 +23,9 @@ async function test() { await resumeToLine(client, 14); await resumeToLine(client, 14); await reverseStepOverToLine(client, 13); - let lastNumberValue = await evaluateInTopFrame(client, "number"); + const lastNumberValue = await evaluateInTopFrame(client, "number"); - let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); @@ -28,7 +33,8 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(recordingTab); - let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); + const replayingTab = BrowserTestUtils.addTab(gBrowser, null, + { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); @@ -45,5 +51,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(replayingTab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js similarity index 63% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js index a004ff61af4b..f0311420c195 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js @@ -2,25 +2,30 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ -// Test for saving a recording and then replaying it in a new tab, with rewinding disabled. -async function test() { - waitForExplicitFinish(); +"use strict"; +// To disable all Web Replay tests, see browser.ini + +// Test for saving a recording and then replaying it in a new tab, +// with rewinding disabled. +add_task(async function() { await pushPref("devtools.recordreplay.enableRewinding", false); - let recordingFile = newRecordingFile(); - let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + const recordingFile = newRecordingFile(); + const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); - let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); + const replayingTab = BrowserTestUtils.addTab(gBrowser, null, + { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); @@ -28,5 +33,4 @@ async function test() { await gBrowser.removeTab(recordingTab); await gBrowser.removeTab(replayingTab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js similarity index 76% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js index fb4dc2083ba9..105b4f2e8bc9 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js @@ -2,17 +2,20 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test basic step-over/back functionality in web replay. -async function test() { - waitForExplicitFinish(); - - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let toolbox = await attachDebugger(tab), client = toolbox.threadClient; + const toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -25,5 +28,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js similarity index 75% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js index a16cac6b5ef3..9c32b8a6112f 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js @@ -2,17 +2,20 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test fixes for some simple stepping bugs. -async function test() { - waitForExplicitFinish(); - - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let toolbox = await attachDebugger(tab), client = toolbox.threadClient; + const toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 22); await rewindToLine(client, 22); @@ -26,5 +29,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js similarity index 69% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js index 2bc1c1de0dae..8fed55cd67df 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js @@ -2,20 +2,23 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Test stepping back while recording, then resuming recording. -async function test() { - waitForExplicitFinish(); - - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current"); - let toolbox = await attachDebugger(tab), client = toolbox.threadClient; + const toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_continuous.html", 13); await resumeToLine(client, 13); - let value = await evaluateInTopFrame(client, "number"); + const value = await evaluateInTopFrame(client, "number"); await reverseStepOverToLine(client, 12); await checkEvaluateInTopFrame(client, "number", value - 1); await resumeToLine(client, 13); @@ -24,5 +27,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js similarity index 82% rename from devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js rename to devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js index b5173fd099a2..6c209dc388af 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js +++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js @@ -2,17 +2,20 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +// To disable all Web Replay tests, see browser.ini // Stepping past the beginning or end of a frame should act like a step-out. -async function test() { - waitForExplicitFinish(); - - let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - let toolbox = await attachDebugger(tab), client = toolbox.threadClient; + const toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -38,5 +41,4 @@ async function test() { await toolbox.destroy(); await gBrowser.removeTab(tab); - finish(); -} +}); diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html b/devtools/client/webreplay/mochitest/examples/doc_rr_basic.html similarity index 100% rename from devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html rename to devtools/client/webreplay/mochitest/examples/doc_rr_basic.html diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html b/devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html similarity index 100% rename from devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html rename to devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html b/devtools/client/webreplay/mochitest/examples/doc_rr_error.html similarity index 100% rename from devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html rename to devtools/client/webreplay/mochitest/examples/doc_rr_error.html diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html b/devtools/client/webreplay/mochitest/examples/doc_rr_logs.html similarity index 100% rename from devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html rename to devtools/client/webreplay/mochitest/examples/doc_rr_logs.html diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html b/devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html similarity index 100% rename from devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html rename to devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html diff --git a/devtools/client/webreplay/mochitest/head.js b/devtools/client/webreplay/mochitest/head.js new file mode 100644 index 000000000000..d7fe650ce90d --- /dev/null +++ b/devtools/client/webreplay/mochitest/head.js @@ -0,0 +1,144 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable no-undef */ + +"use strict"; + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", + this +); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js", + this +); + +const EXAMPLE_URL = + "http://example.com/browser/devtools/client/webreplay/mochitest/examples/"; + +// Attach a debugger to a tab, returning a promise that resolves with the +// debugger's toolbox. +async function attachDebugger(tab) { + const target = await TargetFactory.forTab(tab); + const toolbox = await gDevTools.showToolbox(target, "jsdebugger"); + return toolbox; +} + +async function attachRecordingDebugger(url, + { waitForRecording } = { waitForRecording: false }) { + const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + gBrowser.selectedTab = tab; + openTrustedLinkIn(EXAMPLE_URL + url, "current"); + + if (waitForRecording) { + await once(Services.ppmm, "RecordingFinished"); + } + const toolbox = await attachDebugger(tab); + const dbg = createDebuggerContext(toolbox); + const threadClient = dbg.toolbox.threadClient; + + await threadClient.interrupt(); + return {...dbg, tab, threadClient}; +} + +// Return a promise that resolves when a breakpoint has been set. +async function setBreakpoint(threadClient, expectedFile, lineno) { + const {sources} = await threadClient.getSources(); + ok(sources.length == 1, "Got one source"); + ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile); + const sourceClient = threadClient.source(sources[0]); + await sourceClient.setBreakpoint({ line: lineno }); +} + +function resumeThenPauseAtLineFunctionFactory(method) { + return async function(threadClient, lineno) { + threadClient[method](); + await threadClient.addOneTimeListener("paused", async function(event, packet) { + const {frames} = await threadClient.getFrames(0, 1); + const frameLine = frames[0] ? frames[0].where.line : undefined; + ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno); + }); + }; +} + +// Define various methods that resume a thread in a specific way and ensure it +// pauses at a specified line. +var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind"); +var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume"); +var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver"); +var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver"); +var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn"); +var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn"); +var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut"); +var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut"); + +// Return a promise that resolves when a thread evaluates a string in the +// topmost frame, with the result throwing an exception. +async function checkEvaluateInTopFrameThrows(threadClient, text) { + const {frames} = await threadClient.getFrames(0, 1); + ok(frames.length == 1, "Got one frame"); + const response = await threadClient.eval(frames[0].actor, text); + ok(response.type == "resumed", "Got resume response from eval"); + await threadClient.addOneTimeListener("paused", function(event, packet) { + ok(packet.type == "paused" && + packet.why.type == "clientEvaluated" && + "throw" in packet.why.frameFinished, "Eval threw an exception"); + }); +} + +// Return a pathname that can be used for a new recording file. +function newRecordingFile() { + ChromeUtils.import("resource://gre/modules/osfile.jsm", this); + return OS.Path.join(OS.Constants.Path.tmpDir, + "MochitestRecording" + Math.round(Math.random() * 1000000000)); +} + +function findMessage(hud, text, selector = ".message") { + return findMessages(hud, text, selector)[0]; +} + +function findMessages(hud, text, selector = ".message") { + const messages = hud.ui.outputNode.querySelectorAll(selector); + const elements = Array.prototype.filter.call( + messages, + (el) => el.textContent.includes(text) + ); + + if (elements.length == 0) { + return null; + } + + return elements; +} + +function waitForMessages(hud, text, selector = ".message") { + return waitUntilPredicate(() => findMessages(hud, text, selector)); +} + +async function warpToMessage(hud, threadClient, text) { + let messages = await waitForMessages(hud, text); + ok(messages.length == 1, "Found one message"); + const message = messages.pop(); + + const menuPopup = await openConsoleContextMenu(hud, message); + console.log(`.>> menu`, menuPopup); + + const timeWarpItem = menuPopup.querySelector("#console-menu-time-warp"); + ok(timeWarpItem, "Time warp menu item is available"); + + timeWarpItem.click(); + + await Promise.all([ + hideConsoleContextMenu(hud), + once(Services.ppmm, "TimeWarpFinished"), + waitForThreadEvents(threadClient, "paused"), + ]); + + messages = findMessages(hud, "", ".paused"); + ok(messages.length == 1, "Found one paused message"); + + return message; +} diff --git a/devtools/client/webreplay/moz.build b/devtools/client/webreplay/moz.build index 5560adb742f0..b176495deef8 100644 --- a/devtools/client/webreplay/moz.build +++ b/devtools/client/webreplay/moz.build @@ -14,3 +14,5 @@ DevToolsModules( with Files('**'): BUG_COMPONENT = ('Core', 'Web Replay') + +BROWSER_CHROME_MANIFESTS += [ 'mochitest/browser.ini' ] From a3b990d95a514b91a5d8a3229d3194636f1be48a Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 14 Dec 2018 16:01:19 -0500 Subject: [PATCH 09/33] Bug 1508837: Add TextMarker payloads for simple Profiler Markers r=mstange --- dom/base/nsDOMNavigationTiming.cpp | 6 ++-- tools/profiler/core/ProfilerMarkerPayload.cpp | 7 ++++ tools/profiler/public/ProfilerMarkerPayload.h | 34 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index bcf14b51243f..cf92ef0a05cc 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -333,9 +333,9 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { int(elapsedLongTask.ToMilliseconds()), spec.get()); DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); - profiler_add_marker("TTI", MakeUnique( - NS_ConvertASCIItoUTF16(marker), mTTFI, - docShellId, docShellHistoryId)); + profiler_add_marker( + "TTI", MakeUnique(marker, mNavigationStart, mTTFI, + docShellId, docShellHistoryId)); } #endif return; diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp index f71244a74bc5..3f1982688b75 100644 --- a/tools/profiler/core/ProfilerMarkerPayload.cpp +++ b/tools/profiler/core/ProfilerMarkerPayload.cpp @@ -97,6 +97,13 @@ void UserTimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, } } +void TextMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) { + StreamCommonProps("Text", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("name", mText.get()); +} + void DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, UniqueStacks& aUniqueStacks) { diff --git a/tools/profiler/public/ProfilerMarkerPayload.h b/tools/profiler/public/ProfilerMarkerPayload.h index 743d5d8428bc..0127c4b85409 100644 --- a/tools/profiler/public/ProfilerMarkerPayload.h +++ b/tools/profiler/public/ProfilerMarkerPayload.h @@ -359,4 +359,38 @@ class LongTaskMarkerPayload : public ProfilerMarkerPayload { DECL_STREAM_PAYLOAD }; +class TextMarkerPayload : public ProfilerMarkerPayload { + public: + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime), mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::Maybe& aDocShellId, + const mozilla::Maybe& aDocShellHistoryId) + : ProfilerMarkerPayload(aStartTime, aStartTime, aDocShellId, + aDocShellHistoryId), + mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe& aDocShellId, + const mozilla::Maybe& aDocShellHistoryId) + : ProfilerMarkerPayload(aStartTime, aEndTime, aDocShellId, + aDocShellHistoryId), + mText(aText) {} + + DECL_STREAM_PAYLOAD + + private: + nsCString mText; +}; + #endif // ProfilerMarkerPayload_h From 910fc59008df35979201c6895a759c26bf7f3359 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 15 Jan 2019 12:49:03 -0500 Subject: [PATCH 10/33] Bug 1514513: Fix TTFI calculations and match proposed spec r=mstange Also adds a PageLoad category to Logs --- dom/base/nsDOMNavigationTiming.cpp | 105 +++++++++++++++-------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index cf92ef0a05cc..71d160cc5d8e 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -25,6 +25,13 @@ using namespace mozilla; +namespace mozilla { + +LazyLogModule gPageLoadLog("PageLoad"); +#define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args) + +} // namespace mozilla + nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell) { Clear(); @@ -240,36 +247,6 @@ void nsDOMNavigationTiming::TTITimeoutCallback(nsITimer* aTimer, self->TTITimeout(aTimer); } -// Return the max of aT1 and aT2, or the lower of the two if there's more -// than Nms (the window size) between them. In other words, the window -// starts at the lower of aT1 and aT2, and we only want to respect -// timestamps within the window (and pick the max of those). -// -// This approach handles the edge case of a late wakeup: where there was -// more than Nms after one (of aT1 or aT2) without the other, but the other -// happened after Nms and before we woke up. For example, if aT1 was 10 -// seconds after aT2, but we woke up late (after aT1) we don't want to -// return aT1 if the window is 5 seconds. -static const TimeStamp& MaxWithinWindowBeginningAtMin( - const TimeStamp& aT1, const TimeStamp& aT2, - const TimeDuration& aWindowSize) { - if (aT2.IsNull()) { - return aT1; - } else if (aT1.IsNull()) { - return aT2; - } - if (aT1 > aT2) { - if ((aT1 - aT2) > aWindowSize) { - return aT2; - } - return aT1; - } - if ((aT2 - aT1) > aWindowSize) { - return aT1; - } - return aT2; -} - #define TTI_WINDOW_SIZE_MS (5 * 1000) void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { @@ -281,21 +258,36 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { nsCOMPtr mainThread = do_GetMainThread(); TimeStamp lastLongTaskEnded; mainThread->GetLastLongNonIdleTaskEnd(&lastLongTaskEnded); - if (!lastLongTaskEnded.IsNull()) { - TimeDuration delta = now - lastLongTaskEnded; - if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) { - // Less than 5 seconds since the last long task. Schedule another check - aTimer->InitWithNamedFuncCallback(TTITimeoutCallback, this, - TTI_WINDOW_SIZE_MS, - nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, - "nsDOMNavigationTiming::TTITimeout"); - return; - } + // Window starts at mContentfulPaint; any long task before that is ignored + if (lastLongTaskEnded.IsNull() || lastLongTaskEnded < mContentfulPaint) { + PAGELOAD_LOG( + ("no longtask (last was %g ms before ContentfulPaint)", + lastLongTaskEnded.IsNull() + ? 0 + : (mContentfulPaint - lastLongTaskEnded).ToMilliseconds())); + lastLongTaskEnded = mContentfulPaint; } + TimeDuration delta = now - lastLongTaskEnded; + PAGELOAD_LOG(("TTI delta: %g ms", delta.ToMilliseconds())); + if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) { + // Less than 5 seconds since the last long task or start of the window. + // Schedule another check. + PAGELOAD_LOG(("TTI: waiting additional %g ms", + (TTI_WINDOW_SIZE_MS + 100) - delta.ToMilliseconds())); + aTimer->InitWithNamedFuncCallback( + TTITimeoutCallback, this, + (TTI_WINDOW_SIZE_MS + 100) - + delta.ToMilliseconds(), // slightly after the window ends + nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, + "nsDOMNavigationTiming::TTITimeout"); + return; + } + // To correctly implement TTI/TTFI as proposed, we'd need to not // fire it until there are no more than 2 network loads. By the // proposed definition, without that we're closer to - // TimeToFirstInteractive. + // TimeToFirstInteractive. There are also arguments about what sort + // of loads should qualify. // XXX check number of network loads, and if > 2 mark to check if loads // decreases to 2 (or record that point and let the normal timer here @@ -303,15 +295,25 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { // TTI has occurred! TTI is either FCP (if there are no longtasks and no // DCLEnd in the window that starts at FCP), or at the end of the last - // Long Task or DOMContentLoadedEnd (whichever is later). + // Long Task or DOMContentLoadedEnd (whichever is later). lastLongTaskEnded + // is >= FCP here. if (mTTFI.IsNull()) { - mTTFI = MaxWithinWindowBeginningAtMin( - lastLongTaskEnded, mDOMContentLoadedEventEnd, - TimeDuration::FromMilliseconds(TTI_WINDOW_SIZE_MS)); - if (mTTFI.IsNull()) { - mTTFI = mContentfulPaint; - } + // lastLongTaskEnded is >= mContentfulPaint + mTTFI = (mDOMContentLoadedEventEnd.IsNull() || + lastLongTaskEnded > mDOMContentLoadedEventEnd) + ? lastLongTaskEnded + : mDOMContentLoadedEventEnd; + PAGELOAD_LOG( + ("TTFI after %dms (LongTask was at %dms, DCL was %dms)", + int((mTTFI - mNavigationStart).ToMilliseconds()), + lastLongTaskEnded.IsNull() + ? 0 + : int((lastLongTaskEnded - mNavigationStart).ToMilliseconds()), + mDOMContentLoadedEventEnd.IsNull() + ? 0 + : int((mDOMContentLoadedEventEnd - mNavigationStart) + .ToMilliseconds()))); } // XXX Implement TTI via check number of network loads, and if > 2 mark // to check if loads decreases to 2 (or record that point and let the @@ -322,20 +324,21 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { #ifdef MOZ_GECKO_PROFILER if (profiler_is_active()) { TimeDuration elapsed = mTTFI - mNavigationStart; + MOZ_ASSERT(elapsed.ToMilliseconds() > 0); TimeDuration elapsedLongTask = lastLongTaskEnded.IsNull() ? 0 : lastLongTaskEnded - mNavigationStart; nsAutoCString spec; if (mLoadedURI) { mLoadedURI->GetSpec(spec); } - nsPrintfCString marker("TTFI after %dms (LongTask after %dms) for URL %s", + nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s", int(elapsed.ToMilliseconds()), int(elapsedLongTask.ToMilliseconds()), spec.get()); DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); profiler_add_marker( - "TTI", MakeUnique(marker, mNavigationStart, mTTFI, - docShellId, docShellHistoryId)); + "TTFI", MakeUnique(marker, mNavigationStart, mTTFI, + docShellId, docShellHistoryId)); } #endif return; From 53d77ffeda891b93a23d00fb08ac7727baa84ed6 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 15 Jan 2019 12:49:03 -0500 Subject: [PATCH 11/33] Bug 1514514: Move pageload markers to TextMarker with DocShell IDs r=mstange --- dom/base/nsDOMNavigationTiming.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index 71d160cc5d8e..010cbbf4c108 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -29,6 +29,7 @@ namespace mozilla { LazyLogModule gPageLoadLog("PageLoad"); #define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args) +#define PAGELOAD_LOG_ENABLED() MOZ_LOG_TEST(gPageLoadLog, LogLevel::Error) } // namespace mozilla @@ -322,7 +323,7 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { mTTITimer = nullptr; #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mTTFI - mNavigationStart; MOZ_ASSERT(elapsed.ToMilliseconds() > 0); TimeDuration elapsedLongTask = @@ -355,7 +356,7 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() { mNonBlankPaint = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER - if (profiler_thread_is_being_profiled()) { + if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mNonBlankPaint - mNavigationStart; nsAutoCString spec; if (mLoadedURI) { @@ -368,7 +369,12 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() { ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and first non-blank paint"); - profiler_add_marker(marker.get()); + PAGELOAD_LOG(("%s", marker.get())); + DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); + profiler_add_marker( + "FirstNonBlankPaint", + MakeUnique(marker, mNavigationStart, mNonBlankPaint, + docShellId, docShellHistoryId)); } #endif @@ -399,7 +405,7 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument() { mContentfulPaint = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mContentfulPaint - mNavigationStart; nsAutoCString spec; if (mLoadedURI) { @@ -412,7 +418,12 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument() { ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and first non-blank paint"); - profiler_add_marker(marker.get()); + DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); + PAGELOAD_LOG(("%s", marker.get())); + profiler_add_marker("FirstContentfulPaint", + MakeUnique( + marker, mNavigationStart, mContentfulPaint, + docShellId, docShellHistoryId)); } #endif @@ -439,7 +450,7 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() { mDOMContentFlushed = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER - if (profiler_thread_is_being_profiled()) { + if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mDOMContentFlushed - mNavigationStart; nsAutoCString spec; if (mLoadedURI) { @@ -452,7 +463,12 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() { ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and DOMContentFlushed"); - profiler_add_marker(marker.get()); + DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); + PAGELOAD_LOG(("%s", marker.get())); + profiler_add_marker("DOMContentFlushed", + MakeUnique( + marker, mNavigationStart, mDOMContentFlushed, + docShellId, docShellHistoryId)); } #endif } From c2e52d7fdc6558dfd283a337d2cd4cc55ee52b6c Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 15 Jan 2019 12:49:03 -0500 Subject: [PATCH 12/33] Bug 1517272: Add top-level Document Load event markers for the profiler r=mstange --- dom/base/nsDOMNavigationTiming.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index 010cbbf4c108..7d7ec5e004f3 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -148,6 +148,25 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() { mDocShell); if (IsTopLevelContentDocumentInContentProcess()) { +#ifdef MOZ_GECKO_PROFILER + if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) { + TimeDuration elapsed = mLoadEventEnd - mNavigationStart; + TimeDuration duration = mLoadEventEnd - mLoadEventStart; + nsAutoCString spec; + if (mLoadedURI) { + mLoadedURI->GetSpec(spec); + } + nsPrintfCString marker( + "Document %s loaded after %dms, load event duration %dms", spec.get(), + int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds())); + DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell); + PAGELOAD_LOG(("%s", marker.get())); + profiler_add_marker( + "DocumentLoad", + MakeUnique(marker, mNavigationStart, mLoadEventEnd, + docShellId, docShellHistoryId)); + } +#endif Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS, mNavigationStart); } From f75205467eb9276aedff73206721cdd92c95330a Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 15 Jan 2019 12:49:03 -0500 Subject: [PATCH 13/33] Bug 1518030: add keyword to mirror LOG messages into Profiler LogMarkers r=mstange,froyd --- tools/profiler/core/ProfilerMarkerPayload.cpp | 8 +++++++ tools/profiler/public/ProfilerMarkerPayload.h | 16 +++++++++++++ xpcom/base/Logging.cpp | 23 ++++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp index 3f1982688b75..b1241be744ac 100644 --- a/tools/profiler/core/ProfilerMarkerPayload.cpp +++ b/tools/profiler/core/ProfilerMarkerPayload.cpp @@ -104,6 +104,14 @@ void TextMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, aWriter.StringProperty("name", mText.get()); } +void LogMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) { + StreamCommonProps("Log", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("name", mText.get()); + aWriter.StringProperty("module", mModule.get()); +} + void DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, UniqueStacks& aUniqueStacks) { diff --git a/tools/profiler/public/ProfilerMarkerPayload.h b/tools/profiler/public/ProfilerMarkerPayload.h index 0127c4b85409..217649869411 100644 --- a/tools/profiler/public/ProfilerMarkerPayload.h +++ b/tools/profiler/public/ProfilerMarkerPayload.h @@ -11,6 +11,7 @@ #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" #include "mozilla/net/TimingStruct.h" @@ -393,4 +394,19 @@ class TextMarkerPayload : public ProfilerMarkerPayload { nsCString mText; }; +class LogMarkerPayload : public ProfilerMarkerPayload { + public: + LogMarkerPayload(const char* aModule, const char* aText, + const mozilla::TimeStamp& aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), + mModule(aModule), + mText(aText) {} + + DECL_STREAM_PAYLOAD + + private: + nsAutoCStringN<32> mModule; // longest known LazyLogModule name is ~24 + nsCString mText; +}; + #endif // ProfilerMarkerPayload_h diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp index 3c4a0bbbe09e..411de32b6d39 100644 --- a/xpcom/base/Logging.cpp +++ b/xpcom/base/Logging.cpp @@ -22,6 +22,9 @@ #include "nsDebugImpl.h" #include "NSPRLogModulesParser.h" #include "LogCommandLineHandler.h" +#ifdef MOZ_GECKO_PROFILER +#include "ProfilerMarkerPayload.h" +#endif #include "prenv.h" #ifdef XP_WIN @@ -166,6 +169,7 @@ class LogModuleManager { mMainThread(PR_GetCurrentThread()), mSetFromEnv(false), mAddTimestamp(false), + mAddProfilerMarker(false), mIsRaw(false), mIsSync(false), mRotate(0), @@ -208,6 +212,7 @@ class LogModuleManager { bool addTimestamp = false; bool isSync = false; bool isRaw = false; + bool isMarkers = false; int32_t rotate = 0; const char* modules = PR_GetEnv("MOZ_LOG"); if (!modules || !modules[0]) { @@ -230,9 +235,9 @@ class LogModuleManager { // Need to capture `this` since `sLogModuleManager` is not set until after // initialization is complete. NSPRLogModulesParser( - modules, - [this, &shouldAppend, &addTimestamp, &isSync, &isRaw, &rotate]( - const char* aName, LogLevel aLevel, int32_t aValue) mutable { + modules, [this, &shouldAppend, &addTimestamp, &isSync, &isRaw, &rotate, + &isMarkers](const char* aName, LogLevel aLevel, + int32_t aValue) mutable { if (strcmp(aName, "append") == 0) { shouldAppend = true; } else if (strcmp(aName, "timestamp") == 0) { @@ -243,6 +248,8 @@ class LogModuleManager { isRaw = true; } else if (strcmp(aName, "rotate") == 0) { rotate = (aValue << 20) / kRotateFilesNumber; + } else if (strcmp(aName, "profilermarkers") == 0) { + isMarkers = true; } else { this->CreateOrGetModule(aName)->SetLevel(aLevel); } @@ -253,6 +260,7 @@ class LogModuleManager { mIsSync = isSync; mIsRaw = isRaw; mRotate = rotate; + mAddProfilerMarker = isMarkers; if (rotate > 0 && shouldAppend) { NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!"); @@ -401,6 +409,14 @@ class LogModuleManager { charsWritten = strlen(buffToWrite); } +#ifdef MOZ_GECKO_PROFILER + if (mAddProfilerMarker && profiler_is_active()) { + profiler_add_marker( + "LogMessages", + MakeUnique(aName, buffToWrite, TimeStamp::Now())); + } +#endif + // Determine if a newline needs to be appended to the message. const char* newline = ""; if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') { @@ -524,6 +540,7 @@ class LogModuleManager { PRThread* mMainThread; bool mSetFromEnv; Atomic mAddTimestamp; + Atomic mAddProfilerMarker; Atomic mIsRaw; Atomic mIsSync; int32_t mRotate; From 0b31c53d94358a9773867cee534388c4a5fa14c1 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Tue, 15 Jan 2019 09:32:29 -0800 Subject: [PATCH 14/33] Bug 1520227 - Increase content process count to 8. r=felipe --HG-- extra : rebase_source : a458ac7b537b8ac77929abbb23ff0903ae55990c --- modules/libpref/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 40a3c6cdc530..ed705f0f4557 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3338,7 +3338,7 @@ pref("dom.ipc.plugins.asyncdrawing.enabled", true); pref("dom.ipc.plugins.forcedirect.enabled", true); // Enable multi by default. -#if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN) +#if !defined(MOZ_ASAN) pref("dom.ipc.processCount", 8); #else pref("dom.ipc.processCount", 4); From 64e7af37489ce01c3eae70348859f8fbb74dc53d Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Tue, 15 Jan 2019 13:32:39 -0500 Subject: [PATCH 15/33] Bug 1512010 - backing out local raster space text snapping change. r=jrmuizel --- gfx/wr/webrender/res/ps_text_run.glsl | 14 ++++++-------- gfx/wr/webrender/src/batch.rs | 2 +- gfx/wr/webrender/src/prim_store/text_run.rs | 7 ------- .../blurred-shadow-local-clip-rect-ref.png | Bin 19485 -> 19504 bytes .../reftests/text/raster-space-snap-ref.yaml | 10 ---------- .../reftests/text/raster-space-snap.yaml | 10 ---------- gfx/wr/wrench/reftests/text/reftest.list | 1 - gfx/wr/wrench/reftests/text/two-shadows.png | Bin 11509 -> 11350 bytes 8 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 gfx/wr/wrench/reftests/text/raster-space-snap-ref.yaml delete mode 100644 gfx/wr/wrench/reftests/text/raster-space-snap.yaml diff --git a/gfx/wr/webrender/res/ps_text_run.glsl b/gfx/wr/webrender/res/ps_text_run.glsl index 5a8123b8c3fa..5ade469ca63c 100644 --- a/gfx/wr/webrender/res/ps_text_run.glsl +++ b/gfx/wr/webrender/res/ps_text_run.glsl @@ -63,7 +63,6 @@ TextRun fetch_text_run(int address) { VertexInfo write_text_vertex(RectWithSize local_clip_rect, float z, - bool should_snap, Transform transform, PictureTask task, vec2 text_offset, @@ -74,8 +73,13 @@ VertexInfo write_text_vertex(RectWithSize local_clip_rect, vec2 snap_offset = vec2(0.0); mat2 local_transform; +#ifdef WR_FEATURE_GLYPH_TRANSFORM + bool remove_subpx_offset = true; +#else + bool remove_subpx_offset = transform.is_axis_aligned; +#endif // Compute the snapping offset only if the scroll node transform is axis-aligned. - if (should_snap) { + if (remove_subpx_offset) { // Transform from local space to device space. float device_scale = task.common_data.device_pixel_scale / transform.m[3].w; mat2 device_transform = mat2(transform.m) * device_scale; @@ -174,8 +178,6 @@ void main(void) { RectWithSize glyph_rect = RectWithSize(res.offset + glyph_transform * (text.offset + glyph.offset), res.uv_rect.zw - res.uv_rect.xy); - // Since the glyph is pre-transformed, snapping is both forced and does not depend on the transform. - bool should_snap = true; #else // Scale from glyph space to local space. float scale = res.scale / task.common_data.device_pixel_scale; @@ -183,9 +185,6 @@ void main(void) { // Compute the glyph rect in local space. RectWithSize glyph_rect = RectWithSize(scale * res.offset + text.offset + glyph.offset, scale * (res.uv_rect.zw - res.uv_rect.xy)); - - // Check if the primitive is actually safe to snap. - bool should_snap = ph.user_data.x != 0; #endif vec2 snap_bias; @@ -215,7 +214,6 @@ void main(void) { VertexInfo vi = write_text_vertex(ph.local_clip_rect, ph.z, - should_snap, transform, task, text.offset, diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs index 8c7c42a6a194..4406305e34ed 100644 --- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -829,7 +829,7 @@ impl AlphaBatchBuilder { } }; - let prim_header_index = prim_headers.push(&prim_header, z_id, [run.should_snap as i32, 0, 0]); + let prim_header_index = prim_headers.push(&prim_header, z_id, [0; 3]); let key = BatchKey::new(kind, blend_mode, textures); let base_instance = GlyphInstance::new( prim_header_index, diff --git a/gfx/wr/webrender/src/prim_store/text_run.rs b/gfx/wr/webrender/src/prim_store/text_run.rs index 4aef1f117919..7deeaf16b438 100644 --- a/gfx/wr/webrender/src/prim_store/text_run.rs +++ b/gfx/wr/webrender/src/prim_store/text_run.rs @@ -65,7 +65,6 @@ impl AsInstanceKind for TextRunKey { used_font: self.font.clone(), glyph_keys_range: storage::Range::empty(), shadow: self.shadow, - should_snap: true, }); PrimitiveInstanceKind::TextRun{ data_handle, run_index } @@ -228,7 +227,6 @@ pub struct TextRunPrimitive { pub used_font: FontInstance, pub glyph_keys_range: storage::Range, pub shadow: bool, - pub should_snap: bool, } impl TextRunPrimitive { @@ -264,11 +262,6 @@ impl TextRunPrimitive { FontTransform::identity() }; - // We can snap only if the transform is axis-aligned and in screen-space. - self.should_snap = - transform.preserves_2d_axis_alignment() && - raster_space == RasterSpace::Screen; - // If the transform or device size is different, then the caller of // this method needs to know to rebuild the glyphs. let cache_dirty = diff --git a/gfx/wr/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png b/gfx/wr/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png index e9b2cab05f4a13ce21acd745e41c417805d920ec..895d42eaf5ce4f7ff138f2fdd88893553a658ec6 100644 GIT binary patch literal 19504 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 XXN=0@<1Zn&WW<~W&Yn&YG* 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 diff --git a/gfx/wr/wrench/reftests/text/raster-space-snap-ref.yaml b/gfx/wr/wrench/reftests/text/raster-space-snap-ref.yaml deleted file mode 100644 index 43e4ccdf4697..000000000000 --- a/gfx/wr/wrench/reftests/text/raster-space-snap-ref.yaml +++ /dev/null @@ -1,10 +0,0 @@ -root: - items: - - type: stacking-context - bounds: [0, 0, 480, 80] - raster-space: screen - items: - - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" - origin: 20.5 50 - size: 20 - font: "FreeSans.ttf" diff --git a/gfx/wr/wrench/reftests/text/raster-space-snap.yaml b/gfx/wr/wrench/reftests/text/raster-space-snap.yaml deleted file mode 100644 index 4ae3f8c99f43..000000000000 --- a/gfx/wr/wrench/reftests/text/raster-space-snap.yaml +++ /dev/null @@ -1,10 +0,0 @@ -root: - items: - - type: stacking-context - bounds: [0, 0, 480, 80] - raster-space: local(1.0) - items: - - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" - origin: 20.5 50 - size: 20 - font: "FreeSans.ttf" diff --git a/gfx/wr/wrench/reftests/text/reftest.list b/gfx/wr/wrench/reftests/text/reftest.list index 4ec5174ca8a4..dfd788616f1e 100644 --- a/gfx/wr/wrench/reftests/text/reftest.list +++ b/gfx/wr/wrench/reftests/text/reftest.list @@ -68,4 +68,3 @@ fuzzy(1,113) platform(linux) == raster-space.yaml raster-space.png == shadow-image.yaml shadow-solid-ref.yaml options(disable-aa) == snap-clip.yaml snap-clip-ref.yaml platform(linux) == perspective-clip.yaml perspective-clip.png -options(disable-subpixel) != raster-space-snap.yaml raster-space-snap-ref.yaml diff --git a/gfx/wr/wrench/reftests/text/two-shadows.png b/gfx/wr/wrench/reftests/text/two-shadows.png index a3cb79b38f1d600e9ad6df328c2c601135e5c1af..1dae24cb84d0722edcab962971b69819ab5be985 100644 GIT binary patch literal 11350 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 literal 11509 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 vM#t*kwLx~^Kd_4~LjHdycK?4a_| Date: Fri, 4 Jan 2019 21:19:39 +0000 Subject: [PATCH 16/33] Bug 1517645 - log each task as it is cancelled Reviewers: bstack Subscribers: tomprince Tags: #secure-revision Bug #: 1517645 Differential Revision: https://phabricator.services.mozilla.com/D15852 --HG-- extra : rebase_source : 97d8b1582c67e9353b1d8c57813c1dfcdc7c5ffa --- taskcluster/taskgraph/actions/cancel_all.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/taskcluster/taskgraph/actions/cancel_all.py b/taskcluster/taskgraph/actions/cancel_all.py index d29f1cec3115..09025e318896 100644 --- a/taskcluster/taskgraph/actions/cancel_all.py +++ b/taskcluster/taskgraph/actions/cancel_all.py @@ -33,10 +33,14 @@ logger = logging.getLogger(__name__) context=[] ) def cancel_all_action(parameters, graph_config, input, task_group_id, task_id, task): + def do_cancel_task(task_id): + logger.info('Cancelling task {}'.format(task_id)) + cancel_task(task_id, use_proxy=True) + own_task_id = os.environ.get('TASK_ID', '') with futures.ThreadPoolExecutor(CONCURRENCY) as e: cancels_jobs = [ - e.submit(cancel_task, t, use_proxy=True) + e.submit(do_cancel_task, t) for t in list_task_group(task_group_id) if t != own_task_id ] for job in cancels_jobs: From 33e06bbbb568c4ddb594f6b86fd573c5c716a300 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 4 Jan 2019 21:42:57 +0000 Subject: [PATCH 17/33] Bug 1517645 - always use a highly concurrent requests session Reviewers: bstack Subscribers: tomprince Tags: #secure-revision Bug #: 1517645 Differential Revision: https://phabricator.services.mozilla.com/D15853 --HG-- extra : rebase_source : 44ffd0aaeeea9d4294e998027c7977a0a595f582 --- taskcluster/taskgraph/actions/cancel_all.py | 15 ++++++++------- taskcluster/taskgraph/create.py | 14 ++------------ taskcluster/taskgraph/util/taskcluster.py | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/taskcluster/taskgraph/actions/cancel_all.py b/taskcluster/taskgraph/actions/cancel_all.py index 09025e318896..a46775d1760e 100644 --- a/taskcluster/taskgraph/actions/cancel_all.py +++ b/taskcluster/taskgraph/actions/cancel_all.py @@ -10,12 +10,13 @@ import concurrent.futures as futures import logging import os -from taskgraph.util.taskcluster import list_task_group, cancel_task +from taskgraph.util.taskcluster import ( + list_task_group, + cancel_task, + CONCURRENCY, +) from .registry import register_callback_action -# the maximum number of parallel cancelTask calls to make -CONCURRENCY = 50 - logger = logging.getLogger(__name__) @@ -39,9 +40,9 @@ def cancel_all_action(parameters, graph_config, input, task_group_id, task_id, t own_task_id = os.environ.get('TASK_ID', '') with futures.ThreadPoolExecutor(CONCURRENCY) as e: - cancels_jobs = [ + cancel_futs = [ e.submit(do_cancel_task, t) for t in list_task_group(task_group_id) if t != own_task_id ] - for job in cancels_jobs: - job.result() + for f in futures.as_completed(cancel_futs): + f.result() diff --git a/taskcluster/taskgraph/create.py b/taskcluster/taskgraph/create.py index 0dae7f469780..b4648477af86 100644 --- a/taskcluster/taskgraph/create.py +++ b/taskcluster/taskgraph/create.py @@ -15,12 +15,10 @@ import logging from slugid import nice as slugid from taskgraph.util.parameterization import resolve_timestamps from taskgraph.util.time import current_json_time +from taskgraph.util.taskcluster import get_session, CONCURRENCY logger = logging.getLogger(__name__) -# the maximum number of parallel createTask calls to make -CONCURRENCY = 50 - # this is set to true for `mach taskgraph action-callback --test` testing = False @@ -28,15 +26,6 @@ testing = False def create_tasks(taskgraph, label_to_taskid, params, decision_task_id=None): taskid_to_label = {t: l for l, t in label_to_taskid.iteritems()} - session = requests.Session() - - # Default HTTPAdapter uses 10 connections. Mount custom adapter to increase - # that limit. Connections are established as needed, so using a large value - # should not negatively impact performance. - http_adapter = requests.adapters.HTTPAdapter(pool_connections=CONCURRENCY, - pool_maxsize=CONCURRENCY) - session.mount('https://', http_adapter) - session.mount('http://', http_adapter) decision_task_id = decision_task_id or os.environ.get('TASK_ID') @@ -66,6 +55,7 @@ def create_tasks(taskgraph, label_to_taskid, params, decision_task_id=None): # If `testing` is True, then run without parallelization concurrency = CONCURRENCY if not testing else 1 + session = get_session() with futures.ThreadPoolExecutor(concurrency) as e: fs = {} diff --git a/taskcluster/taskgraph/util/taskcluster.py b/taskcluster/taskgraph/util/taskcluster.py index 65f94c23adaa..9b34c13fcdf8 100644 --- a/taskcluster/taskgraph/util/taskcluster.py +++ b/taskcluster/taskgraph/util/taskcluster.py @@ -27,6 +27,9 @@ testing = False # to the production Taskcluster deployment used for CI. PRODUCTION_TASKCLUSTER_ROOT_URL = 'https://taskcluster.net' +# the maximum number of parallel Taskcluster API calls to make +CONCURRENCY = 50 + @memoize def get_root_url(): @@ -48,10 +51,20 @@ def get_root_url(): @memoize def get_session(): session = requests.Session() + retry = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) - session.mount('http://', HTTPAdapter(max_retries=retry)) - session.mount('https://', HTTPAdapter(max_retries=retry)) + + # Default HTTPAdapter uses 10 connections. Mount custom adapter to increase + # that limit. Connections are established as needed, so using a large value + # should not negatively impact performance. + http_adapter = requests.adapters.HTTPAdapter( + pool_connections=CONCURRENCY, + pool_maxsize=CONCURRENCY, + max_retries=retry) + session.mount('https://', http_adapter) + session.mount('http://', http_adapter) + return session From 96c6e859adea6930c0e1642a7313e100eb6284b2 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 7 Jan 2019 17:52:14 +0000 Subject: [PATCH 18/33] Bug 1517645 - fix list_task_group and rename it Reviewers: bstack Subscribers: tomprince Tags: #secure-revision Bug #: 1517645 Differential Revision: https://phabricator.services.mozilla.com/D15855 --HG-- extra : rebase_source : 16d17f800297ad3cde68d1981964f16fc7a7f44d --- taskcluster/taskgraph/actions/cancel_all.py | 9 ++++----- taskcluster/taskgraph/util/taskcluster.py | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/taskcluster/taskgraph/actions/cancel_all.py b/taskcluster/taskgraph/actions/cancel_all.py index a46775d1760e..56ae31bd82a4 100644 --- a/taskcluster/taskgraph/actions/cancel_all.py +++ b/taskcluster/taskgraph/actions/cancel_all.py @@ -11,7 +11,7 @@ import logging import os from taskgraph.util.taskcluster import ( - list_task_group, + list_task_group_incomplete_tasks, cancel_task, CONCURRENCY, ) @@ -39,10 +39,9 @@ def cancel_all_action(parameters, graph_config, input, task_group_id, task_id, t cancel_task(task_id, use_proxy=True) own_task_id = os.environ.get('TASK_ID', '') + to_cancel = [t for t in list_task_group_incomplete_tasks(task_group_id) if t != own_task_id] + logger.info("Cancelling {} tasks".format(len(to_cancel))) with futures.ThreadPoolExecutor(CONCURRENCY) as e: - cancel_futs = [ - e.submit(do_cancel_task, t) - for t in list_task_group(task_group_id) if t != own_task_id - ] + cancel_futs = [e.submit(do_cancel_task, t) for t in to_cancel] for f in futures.as_completed(cancel_futs): f.result() diff --git a/taskcluster/taskgraph/util/taskcluster.py b/taskcluster/taskgraph/util/taskcluster.py index 9b34c13fcdf8..93e577d04b7a 100644 --- a/taskcluster/taskgraph/util/taskcluster.py +++ b/taskcluster/taskgraph/util/taskcluster.py @@ -73,7 +73,7 @@ def _do_request(url, force_get=False, **kwargs): if kwargs and not force_get: response = session.post(url, **kwargs) else: - response = session.get(url, stream=True) + response = session.get(url, stream=True, **kwargs) if response.status_code >= 400: # Consume content before raise_for_status, so that the connection can be # reused. @@ -281,8 +281,8 @@ def send_email(address, subject, content, link, use_proxy=False): }) -def list_task_group(task_group_id): - """Generate the tasks in a task group""" +def list_task_group_incomplete_tasks(task_group_id): + """Generate the incomplete tasks in a task group""" params = {} while True: url = liburls.api(get_root_url(), 'queue', 'v1', From 1b3c1d1b1da180bdd497b00c058eaa57e0e9fc18 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 15 Jan 2019 18:57:20 +0000 Subject: [PATCH 19/33] Bug 1517645 - fix lint issues in previous push; a=bustage CLOSED TREE --HG-- extra : amend_source : 302098ef24e29399b896c0f6bc379fdbcfce6f64 --- taskcluster/taskgraph/create.py | 3 --- taskcluster/taskgraph/util/taskcluster.py | 1 - 2 files changed, 4 deletions(-) diff --git a/taskcluster/taskgraph/create.py b/taskcluster/taskgraph/create.py index b4648477af86..3dc4ba9aae3a 100644 --- a/taskcluster/taskgraph/create.py +++ b/taskcluster/taskgraph/create.py @@ -5,8 +5,6 @@ from __future__ import absolute_import, print_function, unicode_literals import concurrent.futures as futures -import requests -import requests.adapters import json import os import sys @@ -26,7 +24,6 @@ testing = False def create_tasks(taskgraph, label_to_taskid, params, decision_task_id=None): taskid_to_label = {t: l for l, t in label_to_taskid.iteritems()} - decision_task_id = decision_task_id or os.environ.get('TASK_ID') # when running as an actual decision task, we use the decision task's diff --git a/taskcluster/taskgraph/util/taskcluster.py b/taskcluster/taskgraph/util/taskcluster.py index 93e577d04b7a..4212a30bab3e 100644 --- a/taskcluster/taskgraph/util/taskcluster.py +++ b/taskcluster/taskgraph/util/taskcluster.py @@ -15,7 +15,6 @@ import logging import taskcluster_urls as liburls from mozbuild.util import memoize from requests.packages.urllib3.util.retry import Retry -from requests.adapters import HTTPAdapter from taskgraph.task import Task logger = logging.getLogger(__name__) From 55bd92ed93e94cf43fa4a163be904f7bfe8d829e Mon Sep 17 00:00:00 2001 From: Jorg K Date: Tue, 15 Jan 2019 08:39:00 +0200 Subject: [PATCH 20/33] Bug 1519653 - move initialisation of richlist properties to constructor. r=paolo --- toolkit/content/widgets/richlistbox.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/toolkit/content/widgets/richlistbox.js b/toolkit/content/widgets/richlistbox.js index 15361a375c9c..f4376cb93772 100644 --- a/toolkit/content/widgets/richlistbox.js +++ b/toolkit/content/widgets/richlistbox.js @@ -10,6 +10,16 @@ MozElements.RichListBox = class RichListBox extends MozElements.BaseControl { constructor() { super(); + this.selectedItems = new ChromeNodeList(); + this._currentIndex = null; + this._lastKeyTime = 0; + this._incrementalString = ""; + this._suppressOnSelect = false; + this._userSelecting = false; + this._selectTimeout = null; + this._currentItem = null; + this._selectionStart = null; + this.addEventListener("keypress", event => { if (event.altKey || event.metaKey) { return; @@ -135,17 +145,6 @@ MozElements.RichListBox = class RichListBox extends MozElements.BaseControl { } this.setAttribute("allowevents", "true"); - - this.selectedItems = new ChromeNodeList(); - this._currentIndex = null; - this._lastKeyTime = 0; - this._incrementalString = ""; - this._suppressOnSelect = false; - this._userSelecting = false; - this._selectTimeout = null; - this._currentItem = null; - this._selectionStart = null; - this._refreshSelection(); } From ffe2b2ffaffa0a57774082e4c2067ce7597b6ab9 Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Tue, 15 Jan 2019 22:12:40 +0200 Subject: [PATCH 21/33] Bug 1437051 - Follow-up for disable browser_extension_controlled.js on win ccov DONTBUILD r=me --HG-- extra : rebase_source : fa18ecd48590491836034b84817e71918188c00a --- browser/components/preferences/in-content/tests/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/preferences/in-content/tests/browser.ini b/browser/components/preferences/in-content/tests/browser.ini index 3f23c7875e97..543726257fe6 100644 --- a/browser/components/preferences/in-content/tests/browser.ini +++ b/browser/components/preferences/in-content/tests/browser.ini @@ -54,7 +54,7 @@ skip-if = true || !healthreport # Bug 1185403 for the "true" [browser_homepages_filter_aboutpreferences.js] [browser_homepages_use_bookmark.js] [browser_extension_controlled.js] -skipif = ccov && os == 'win' # bug 1437051 +skip-if = ccov && os == 'win' # bug 1437051 [browser_languages_subdialog.js] [browser_browser_languages_subdialog.js] [browser_layersacceleration.js] From 6172be8a5a05be0b490623976ebc7c91feef8488 Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Tue, 15 Jan 2019 23:07:50 +0200 Subject: [PATCH 22/33] Backed out changeset a0b8e8c08c8d (bug 1517837) for devtools failures at devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js --HG-- rename : devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_record.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js rename : devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js => devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js rename : devtools/client/webreplay/mochitest/examples/doc_rr_basic.html => devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html rename : devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html => devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html rename : devtools/client/webreplay/mochitest/examples/doc_rr_error.html => devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html rename : devtools/client/webreplay/mochitest/examples/doc_rr_logs.html => devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html rename : devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html => devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html --- .eslintignore | 1 - .../debugger/new/test/mochitest/browser.ini | 38 ++++ .../new/test/mochitest/browser_dbg-console.js | 2 +- .../browser_dbg_rr_breakpoints-01.js | 12 +- .../browser_dbg_rr_breakpoints-02.js | 15 +- .../browser_dbg_rr_breakpoints-03.js | 16 +- .../browser_dbg_rr_breakpoints-04.js | 14 +- .../browser_dbg_rr_breakpoints-05.js | 14 +- .../browser_dbg_rr_console_warp-01.js | 30 ++- .../browser_dbg_rr_console_warp-02.js | 48 +++++ .../test}/mochitest/browser_dbg_rr_record.js | 14 +- .../mochitest/browser_dbg_rr_recovery-01.js | 27 +-- .../mochitest/browser_dbg_rr_replay-01.js | 24 +-- .../mochitest/browser_dbg_rr_replay-02.js | 21 +- .../mochitest/browser_dbg_rr_replay-03.js | 22 +- .../mochitest/browser_dbg_rr_stepping-01.js | 16 +- .../mochitest/browser_dbg_rr_stepping-02.js | 16 +- .../mochitest/browser_dbg_rr_stepping-03.js | 18 +- .../mochitest/browser_dbg_rr_stepping-04.js | 16 +- .../mochitest/examples/doc_rr_basic.html | 0 .../mochitest/examples/doc_rr_continuous.html | 0 .../mochitest/examples/doc_rr_error.html | 0 .../test}/mochitest/examples/doc_rr_logs.html | 0 .../mochitest/examples/doc_rr_recovery.html | 0 .../debugger/new/test/mochitest/head.js | 200 ++++++++++++++++++ .../debugger/new/test/mochitest/helpers.js | 72 ------- .../client/webreplay/mochitest/browser.ini | 38 ---- .../browser_dbg_rr_console_warp-02.js | 33 --- devtools/client/webreplay/mochitest/head.js | 144 ------------- devtools/client/webreplay/moz.build | 2 - 30 files changed, 411 insertions(+), 442 deletions(-) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_breakpoints-01.js (92%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_breakpoints-02.js (82%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_breakpoints-03.js (77%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_breakpoints-04.js (86%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_breakpoints-05.js (84%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_console_warp-01.js (57%) create mode 100644 devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_record.js (69%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_recovery-01.js (53%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_replay-01.js (65%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_replay-02.js (74%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_replay-03.js (63%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_stepping-01.js (76%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_stepping-02.js (75%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_stepping-03.js (69%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/browser_dbg_rr_stepping-04.js (82%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/examples/doc_rr_basic.html (100%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/examples/doc_rr_continuous.html (100%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/examples/doc_rr_error.html (100%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/examples/doc_rr_logs.html (100%) rename devtools/client/{webreplay => debugger/new/test}/mochitest/examples/doc_rr_recovery.html (100%) delete mode 100644 devtools/client/webreplay/mochitest/browser.ini delete mode 100644 devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js delete mode 100644 devtools/client/webreplay/mochitest/head.js diff --git a/.eslintignore b/.eslintignore index e7999a5eafee..35dfd77cb57a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -106,7 +106,6 @@ devtools/client/shared/webpack/shims/test/test_clipboard.html devtools/shared/qrcode/tests/mochitest/test_decode.html devtools/shared/tests/mochitest/*.html devtools/shared/webconsole/test/test_*.html -devtools/client/webreplay/mochitest/examples/*.html # Soon to be removed, the new/ directory is explicitly excluded below due to # also being an imported repository. diff --git a/devtools/client/debugger/new/test/mochitest/browser.ini b/devtools/client/debugger/new/test/mochitest/browser.ini index a28bb68efefc..497d58d9ab09 100644 --- a/devtools/client/debugger/new/test/mochitest/browser.ini +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -656,6 +656,11 @@ support-files = examples/simple-worker.js examples/doc-event-handler.html examples/doc-eval-throw.html + examples/doc_rr_basic.html + examples/doc_rr_continuous.html + examples/doc_rr_logs.html + examples/doc_rr_recovery.html + examples/doc_rr_error.html [browser_dbg-asm.js] [browser_dbg-async-stepping.js] @@ -770,3 +775,36 @@ skip-if = true [browser_dbg-windowless-workers.js] [browser_dbg-event-handler.js] [browser_dbg-eval-throw.js] +[browser_dbg_rr_breakpoints-01.js] +skip-if = true # bug 1513057 os != "mac" || debug || !nightly_build +[browser_dbg_rr_breakpoints-02.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_breakpoints-03.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_breakpoints-04.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_breakpoints-05.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_record.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_stepping-01.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_stepping-02.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_stepping-03.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_stepping-04.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_recovery-01.js] +skip-if = true # See bug 1481009 +[browser_dbg_rr_replay-01.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_replay-02.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_replay-03.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_console_warp-01.js] +skip-if = true # os != "mac" || debug || !nightly_build +[browser_dbg_rr_console_warp-02.js] +skip-if = true # os != "mac" || debug || !nightly_build + diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js index cc5d2acd52f7..965b36b84d55 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js @@ -5,7 +5,7 @@ add_task(async function() { await selectSource(dbg, "switching-01"); // open the console - await getDebuggerSplitConsole(dbg); + await getSplitConsole(dbg); ok(dbg.toolbox.splitConsole, "Split console is shown."); // close the console diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js similarity index 92% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js index cb908c88211c..d734dff0e8af 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js @@ -2,14 +2,11 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test basic breakpoint functionality in web replay. -add_task(async function() { +async function test() { + waitForExplicitFinish(); + const dbg = await attachRecordingDebugger( "doc_rr_basic.html", { waitForRecording: true } @@ -41,4 +38,5 @@ add_task(async function() { await toolbox.closeToolbox(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js similarity index 82% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js index f753cf75a081..96f80555784e 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js @@ -2,16 +2,12 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test unhandled divergence while evaluating at a breakpoint with Web Replay. -add_task(async function() { - const dbg = await attachRecordingDebugger("doc_rr_basic.html", - { waitForRecording: true }); +async function test() { + waitForExplicitFinish(); + + const dbg = await attachRecordingDebugger("doc_rr_basic.html", { waitForRecording: true }); const {threadClient, tab, toolbox} = dbg; await setBreakpoint(threadClient, "doc_rr_basic.html", 21); @@ -25,4 +21,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js similarity index 77% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js index ba7786c86c8a..9209fef2295f 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js @@ -2,14 +2,11 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test some issues when stepping around after hitting a breakpoint while recording. -add_task(async function() { +async function test() { + waitForExplicitFinish(); + const dbg = await attachRecordingDebugger("doc_rr_continuous.html"); const {threadClient, tab, toolbox} = dbg; @@ -17,9 +14,7 @@ add_task(async function() { await setBreakpoint(threadClient, "doc_rr_continuous.html", 19); await resumeToLine(threadClient, 19); await reverseStepOverToLine(threadClient, 18); - await checkEvaluateInTopFrame(threadClient, - "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", - undefined); + await checkEvaluateInTopFrame(threadClient, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined); await stepInToLine(threadClient, 22); await setBreakpoint(threadClient, "doc_rr_continuous.html", 24); await resumeToLine(threadClient, 24); @@ -28,4 +23,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js similarity index 86% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js index 50996d653cc6..2a5626f97ae2 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js @@ -2,21 +2,18 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test navigating back to earlier breakpoints while recording, then resuming // recording. -add_task(async function() { +async function test() { + waitForExplicitFinish(); + const dbg = await attachRecordingDebugger("doc_rr_continuous.html"); const {threadClient, tab, toolbox} = dbg; await setBreakpoint(threadClient, "doc_rr_continuous.html", 14); await resumeToLine(threadClient, 14); - const value = await evaluateInTopFrame(threadClient, "number"); + let value = await evaluateInTopFrame(threadClient, "number"); await resumeToLine(threadClient, 14); await checkEvaluateInTopFrame(threadClient, "number", value + 1); await rewindToLine(threadClient, 14); @@ -32,4 +29,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js similarity index 84% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js index 8e14957b73af..09640a1af8c6 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js @@ -2,17 +2,14 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test hitting breakpoints when rewinding past the point where the breakpoint // script was created. -add_task(async function() { +async function test() { + waitForExplicitFinish(); + const dbg = await attachRecordingDebugger( - "doc_rr_basic.html", + "doc_rr_basic.html", { waitForRecording: true } ); @@ -29,4 +26,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js similarity index 57% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js index fd3452b90303..2dbce382cfe0 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js @@ -2,24 +2,37 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ -"use strict"; +// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js, +// since this test straddles both the web console and the debugger. I couldn't +// figure out how to load that script directly here. +function waitForThreadEvents(threadClient, eventName) { + info(`Waiting for thread event '${eventName}' to fire.`); + + return new Promise(function(resolve, reject) { + threadClient.addListener(eventName, function onEvent(eventName, ...args) { + info(`Thread event '${eventName}' fired.`); + threadClient.removeListener(eventName, onEvent); + resolve.apply(resolve, args); + }); + }); +} -// To disable all Web Replay tests, see browser.ini // Test basic console time warping functionality in web replay. -add_task(async function() { +async function test() { + waitForExplicitFinish(); + const dbg = await attachRecordingDebugger( - "doc_rr_error.html", + "doc_rr_error.html", { waitForRecording: true } ); const {tab, toolbox, threadClient} = dbg; - const console = await getDebuggerSplitConsole(dbg); + const console = await getSplitConsole(dbg); const hud = console.hud; - await warpToMessage(hud, dbg, "Number 5"); + await warpToMessage(hud, threadClient, "Number 5"); await threadClient.interrupt(); await checkEvaluateInTopFrame(threadClient, "number", 5); @@ -36,4 +49,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js new file mode 100644 index 000000000000..6502156c75b4 --- /dev/null +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + +// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js, +// since this test straddles both the web console and the debugger. I couldn't +// figure out how to load that script directly here. +function waitForThreadEvents(threadClient, eventName) { + info(`Waiting for thread event '${eventName}' to fire.`); + + return new Promise(function(resolve, reject) { + threadClient.addListener(eventName, function onEvent(eventName, ...args) { + info(`Thread event '${eventName}' fired.`); + threadClient.removeListener(eventName, onEvent); + resolve.apply(resolve, args); + }); + }); +} + + +// Test basic console time warping functionality in web replay. +async function test() { + waitForExplicitFinish(); + + const dbg = await attachRecordingDebugger( + "doc_rr_logs.html", + { waitForRecording: true } + ); + + const {tab, toolbox, threadClient} = dbg; + const console = await getSplitConsole(dbg); + const hud = console.hud; + + let message = await warpToMessage(hud, threadClient, "number: 1"); + ok(!message.classList.contains("paused-before"), "paused before message is not shown"); + + await stepOverToLine(threadClient, 18); + await reverseStepOverToLine(threadClient, 17); + + message = findMessage(hud, "number: 1") + ok(message.classList.contains("paused-before"), "paused before message is shown"); + + await toolbox.destroy(); + await gBrowser.removeTab(tab); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js similarity index 69% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_record.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js index 4f3358419172..dd6042cc69b1 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js @@ -2,15 +2,12 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test basic recording of a tab without any debugging. -add_task(async function() { - const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + var recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); @@ -18,4 +15,5 @@ add_task(async function() { await gBrowser.removeTab(recordingTab); ok(true, "Finished"); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js similarity index 53% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js index 8d2c751c72f7..b2b6bf82a5c9 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js @@ -2,32 +2,27 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test basic recovery of crashed child processes in web replay. -add_task(async function() { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const toolbox = await attachDebugger(tab), client = toolbox.threadClient; + let toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_recovery.html", 21); await rewindToLine(client, 21); - await checkEvaluateInTopFrame(client, - "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", - undefined); + await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", undefined); await stepOverToLine(client, 22); await stepOverToLine(client, 23); - await checkEvaluateInTopFrame(client, - "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " + - "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", - undefined); + await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " + + "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", undefined); + await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js similarity index 65% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js index d864f188d3ea..3f9eec2a81b1 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js @@ -2,32 +2,27 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Basic test for saving a recording and then replaying it in a new tab. -add_task(async function() { - const recordingFile = newRecordingFile(); - const recordingTab = BrowserTestUtils.addTab(gBrowser, null, - { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let recordingFile = newRecordingFile(); + let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); - const replayingTab = BrowserTestUtils.addTab(gBrowser, null, - { replayExecution: recordingFile }); + let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); - const toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient; + let toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -40,4 +35,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(recordingTab); await gBrowser.removeTab(replayingTab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js similarity index 74% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js index a529ca119d8a..57edaa8aa728 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js @@ -2,18 +2,13 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test ending a recording at a breakpoint and then separately replaying to the end. -add_task(async function() { +async function test() { waitForExplicitFinish(); - const recordingFile = newRecordingFile(); - const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + let recordingFile = newRecordingFile(); + let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current"); @@ -23,9 +18,9 @@ add_task(async function() { await resumeToLine(client, 14); await resumeToLine(client, 14); await reverseStepOverToLine(client, 13); - const lastNumberValue = await evaluateInTopFrame(client, "number"); + let lastNumberValue = await evaluateInTopFrame(client, "number"); - const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); @@ -33,8 +28,7 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(recordingTab); - const replayingTab = BrowserTestUtils.addTab(gBrowser, null, - { replayExecution: recordingFile }); + let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); @@ -51,4 +45,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(replayingTab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js similarity index 63% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js index f0311420c195..a004ff61af4b 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js @@ -2,30 +2,25 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ -"use strict"; +// Test for saving a recording and then replaying it in a new tab, with rewinding disabled. +async function test() { + waitForExplicitFinish(); -// To disable all Web Replay tests, see browser.ini - -// Test for saving a recording and then replaying it in a new tab, -// with rewinding disabled. -add_task(async function() { await pushPref("devtools.recordreplay.enableRewinding", false); - const recordingFile = newRecordingFile(); - const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + let recordingFile = newRecordingFile(); + let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = recordingTab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; + let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent; ok(tabParent, "Found recording tab parent"); ok(tabParent.saveRecording(recordingFile), "Saved recording"); await once(Services.ppmm, "SaveRecordingFinished"); - const replayingTab = BrowserTestUtils.addTab(gBrowser, null, - { replayExecution: recordingFile }); + let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile }); gBrowser.selectedTab = replayingTab; await once(Services.ppmm, "HitRecordingEndpoint"); @@ -33,4 +28,5 @@ add_task(async function() { await gBrowser.removeTab(recordingTab); await gBrowser.removeTab(replayingTab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js similarity index 76% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js index 105b4f2e8bc9..fb4dc2083ba9 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js @@ -2,20 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test basic step-over/back functionality in web replay. -add_task(async function() { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const toolbox = await attachDebugger(tab), client = toolbox.threadClient; + let toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -28,4 +25,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js similarity index 75% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js index 9c32b8a6112f..a16cac6b5ef3 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js @@ -2,20 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test fixes for some simple stepping bugs. -add_task(async function() { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const toolbox = await attachDebugger(tab), client = toolbox.threadClient; + let toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 22); await rewindToLine(client, 22); @@ -29,4 +26,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js similarity index 69% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js index 8fed55cd67df..2bc1c1de0dae 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js @@ -2,23 +2,20 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Test stepping back while recording, then resuming recording. -add_task(async function() { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current"); - const toolbox = await attachDebugger(tab), client = toolbox.threadClient; + let toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_continuous.html", 13); await resumeToLine(client, 13); - const value = await evaluateInTopFrame(client, "number"); + let value = await evaluateInTopFrame(client, "number"); await reverseStepOverToLine(client, 12); await checkEvaluateInTopFrame(client, "number", value - 1); await resumeToLine(client, 13); @@ -27,4 +24,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js similarity index 82% rename from devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js index 6c209dc388af..b5173fd099a2 100644 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js @@ -2,20 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini // Stepping past the beginning or end of a frame should act like a step-out. -add_task(async function() { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); +async function test() { + waitForExplicitFinish(); + + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); gBrowser.selectedTab = tab; openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current"); await once(Services.ppmm, "RecordingFinished"); - const toolbox = await attachDebugger(tab), client = toolbox.threadClient; + let toolbox = await attachDebugger(tab), client = toolbox.threadClient; await client.interrupt(); await setBreakpoint(client, "doc_rr_basic.html", 21); await rewindToLine(client, 21); @@ -41,4 +38,5 @@ add_task(async function() { await toolbox.destroy(); await gBrowser.removeTab(tab); -}); + finish(); +} diff --git a/devtools/client/webreplay/mochitest/examples/doc_rr_basic.html b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html similarity index 100% rename from devtools/client/webreplay/mochitest/examples/doc_rr_basic.html rename to devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html diff --git a/devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html similarity index 100% rename from devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html rename to devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html diff --git a/devtools/client/webreplay/mochitest/examples/doc_rr_error.html b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html similarity index 100% rename from devtools/client/webreplay/mochitest/examples/doc_rr_error.html rename to devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html diff --git a/devtools/client/webreplay/mochitest/examples/doc_rr_logs.html b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html similarity index 100% rename from devtools/client/webreplay/mochitest/examples/doc_rr_logs.html rename to devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html diff --git a/devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html similarity index 100% rename from devtools/client/webreplay/mochitest/examples/doc_rr_recovery.html rename to devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js index 4ea3838d6668..7b694374d9db 100644 --- a/devtools/client/debugger/new/test/mochitest/head.js +++ b/devtools/client/debugger/new/test/mochitest/head.js @@ -47,6 +47,16 @@ const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/"; +async function waitUntilPredicate(predicate) { + let result; + await waitUntil(() => { + result = predicate(); + return result; + }) + + return result; +} + // NOTE: still experimental, the screenshots might not be exactly correct async function takeScreenshot(dbg) { let canvas = dbg.win.document.createElementNS( @@ -60,3 +70,193 @@ async function takeScreenshot(dbg) { await waitForTime(1000); dump(`[SCREENSHOT] ${canvas.toDataURL()}\n`); } + +// Attach a debugger to a tab, returning a promise that resolves with the +// debugger's toolbox. +async function attachDebugger(tab) { + let target = await TargetFactory.forTab(tab); + let toolbox = await gDevTools.showToolbox(target, "jsdebugger"); + return toolbox; +} + +async function attachRecordingDebugger(url, { waitForRecording } = { waitForRecording: false }) { + let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); + gBrowser.selectedTab = tab; + openTrustedLinkIn(EXAMPLE_URL + url, "current"); + + if (waitForRecording) { + await once(Services.ppmm, "RecordingFinished"); + } + const toolbox = await attachDebugger(tab); + const dbg = createDebuggerContext(toolbox) + const threadClient = dbg.toolbox.threadClient; + + await threadClient.interrupt(); + return {...dbg, tab, threadClient}; +} + + +// Return a promise with a reference to jsterm, opening the split +// console if necessary. This cleans up the split console pref so +// it won't pollute other tests. +async function getSplitConsole(dbg) { + const { toolbox, win } = dbg; + + if (!win) { + win = toolbox.win; + } + + if (!toolbox.splitConsole) { + pressKey(dbg, "Escape"); + } + + await toolbox.openSplitConsole(); + return toolbox.getPanel("webconsole"); +} + + +// Return a promise that resolves when a breakpoint has been set. +async function setBreakpoint(threadClient, expectedFile, lineno) { + let {sources} = await threadClient.getSources(); + ok(sources.length == 1, "Got one source"); + ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile); + let sourceClient = threadClient.source(sources[0]); + await sourceClient.setBreakpoint({ line: lineno }); +} + +function resumeThenPauseAtLineFunctionFactory(method) { + return async function(threadClient, lineno) { + threadClient[method](); + await threadClient.addOneTimeListener("paused", async function(event, packet) { + let {frames} = await threadClient.getFrames(0, 1); + let frameLine = frames[0] ? frames[0].where.line : undefined; + ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno); + }); + }; +} + +// Define various methods that resume a thread in a specific way and ensure it +// pauses at a specified line. +var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind"); +var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume"); +var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver"); +var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver"); +var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn"); +var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn"); +var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut"); +var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut"); + +// Return a promise that resolves with the result of a thread evaluating a +// string in the topmost frame. +async function evaluateInTopFrame(threadClient, text) { + let {frames} = await threadClient.getFrames(0, 1); + ok(frames.length == 1, "Got one frame"); + let response = await threadClient.eval(frames[0].actor, text); + ok(response.type == "resumed", "Got resume response from eval"); + let rval; + await threadClient.addOneTimeListener("paused", function(event, packet) { + ok(packet.type == "paused" && + packet.why.type == "clientEvaluated" && + "return" in packet.why.frameFinished, "Eval returned a value"); + rval = packet.why.frameFinished["return"]; + }); + return (rval.type == "undefined") ? undefined : rval; +} + +// Return a promise that resolves when a thread evaluates a string in the +// topmost frame, ensuring the result matches the expected value. +async function checkEvaluateInTopFrame(threadClient, text, expected) { + let rval = await evaluateInTopFrame(threadClient, text); + ok(rval == expected, "Eval returned " + expected); +} + +// Return a promise that resolves when a thread evaluates a string in the +// topmost frame, with the result throwing an exception. +async function checkEvaluateInTopFrameThrows(threadClient, text) { + let {frames} = await threadClient.getFrames(0, 1); + ok(frames.length == 1, "Got one frame"); + let response = await threadClient.eval(frames[0].actor, text); + ok(response.type == "resumed", "Got resume response from eval"); + await threadClient.addOneTimeListener("paused", function(event, packet) { + ok(packet.type == "paused" && + packet.why.type == "clientEvaluated" && + "throw" in packet.why.frameFinished, "Eval threw an exception"); + }); +} + +// Return a pathname that can be used for a new recording file. +function newRecordingFile() { + ChromeUtils.import("resource://gre/modules/osfile.jsm", this); + return OS.Path.join(OS.Constants.Path.tmpDir, + "MochitestRecording" + Math.round(Math.random() * 1000000000)); +} + + +async function warpToMessage(hud, threadClient, text) { + let messages = await waitForMessages(hud, text); + ok(messages.length == 1, "Found one message"); + let message = messages.pop(); + + let menuPopup = await openConsoleContextMenu(hud, message); + console.log(`.>> menu`, menuPopup); + + + let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp"); + ok(timeWarpItem, "Time warp menu item is available"); + + timeWarpItem.click(); + + await Promise.all([ + hideConsoleContextMenu(hud), + once(Services.ppmm, "TimeWarpFinished"), + waitForThreadEvents(threadClient, 'paused') + ]); + + messages = findMessages(hud, "", ".paused"); + ok(messages.length == 1, "Found one paused message"); + + return message; +} + + +function findMessage(hud, text, selector = ".message") { + return findMessages(hud, text, selector)[0] +} + +function findMessages(hud, text, selector = ".message") { + const messages = hud.ui.outputNode.querySelectorAll(selector); + const elements = Array.prototype.filter.call( + messages, + (el) => el.textContent.includes(text) + ); + + if (elements.length == 0) { + return null; + } + + return elements; +} + +function waitForMessages(hud, text, selector = ".message") { + return waitUntilPredicate(() => findMessages(hud, text, selector)) +} + +async function openConsoleContextMenu(hud, element) { + const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open"); + synthesizeContextMenuEvent(element); + await onConsoleMenuOpened; + const doc = hud.ui.consoleOutput.owner.chromeWindow.document; + return doc.getElementById("webconsole-menu"); +} + +function hideConsoleContextMenu(hud) { + const doc = hud.ui.consoleOutput.owner.chromeWindow.document; + const popup = doc.getElementById("webconsole-menu"); + if (!popup) { + return Promise.resolve(); + } + + const onPopupHidden = once(popup, "popuphidden"); + popup.hidePopup(); + return onPopupHidden; +} diff --git a/devtools/client/debugger/new/test/mochitest/helpers.js b/devtools/client/debugger/new/test/mochitest/helpers.js index d50820ccb66f..412552adbea5 100644 --- a/devtools/client/debugger/new/test/mochitest/helpers.js +++ b/devtools/client/debugger/new/test/mochitest/helpers.js @@ -1462,75 +1462,3 @@ async function editExpression(dbg, input) { pressKey(dbg, "Enter"); await evaluated; } - -async function waitUntilPredicate(predicate) { - let result; - await waitUntil(() => { - result = predicate(); - return result; - }) - - return result; -} - -// Return a promise with a reference to jsterm, opening the split -// console if necessary. This cleans up the split console pref so -// it won't pollute other tests. -async function getDebuggerSplitConsole(dbg) { - const { toolbox, win } = dbg; - - if (!win) { - win = toolbox.win; - } - - if (!toolbox.splitConsole) { - pressKey(dbg, "Escape"); - } - - await toolbox.openSplitConsole(); - return toolbox.getPanel("webconsole"); -} - -async function openConsoleContextMenu(hud, element) { - const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open"); - synthesizeContextMenuEvent(element); - await onConsoleMenuOpened; - const doc = hud.ui.consoleOutput.owner.chromeWindow.document; - return doc.getElementById("webconsole-menu"); -} - -function hideConsoleContextMenu(hud) { - const doc = hud.ui.consoleOutput.owner.chromeWindow.document; - const popup = doc.getElementById("webconsole-menu"); - if (!popup) { - return Promise.resolve(); - } - - const onPopupHidden = once(popup, "popuphidden"); - popup.hidePopup(); - return onPopupHidden; -} - -// Return a promise that resolves with the result of a thread evaluating a -// string in the topmost frame. -async function evaluateInTopFrame(threadClient, text) { - const {frames} = await threadClient.getFrames(0, 1); - ok(frames.length == 1, "Got one frame"); - const response = await threadClient.eval(frames[0].actor, text); - ok(response.type == "resumed", "Got resume response from eval"); - let rval; - await threadClient.addOneTimeListener("paused", function(event, packet) { - ok(packet.type == "paused" && - packet.why.type == "clientEvaluated" && - "return" in packet.why.frameFinished, "Eval returned a value"); - rval = packet.why.frameFinished.return; - }); - return (rval.type == "undefined") ? undefined : rval; -} - -// Return a promise that resolves when a thread evaluates a string in the -// topmost frame, ensuring the result matches the expected value. -async function checkEvaluateInTopFrame(threadClient, text, expected) { - const rval = await evaluateInTopFrame(threadClient, text); - ok(rval == expected, "Eval returned " + expected); -} diff --git a/devtools/client/webreplay/mochitest/browser.ini b/devtools/client/webreplay/mochitest/browser.ini deleted file mode 100644 index 92e99ea8e16f..000000000000 --- a/devtools/client/webreplay/mochitest/browser.ini +++ /dev/null @@ -1,38 +0,0 @@ -[DEFAULT] -tags = devtools -subsuite = devtools - -# Feel free to set this to true if an impending change breaks Web Replay and -# fixing it would be annoying or difficult. This will avoid running all tests -# that use Web Replay; we don't want this experimental feature to impede -# development in the rest of Gecko. -# -# Please file a bug against the 'Core > Web Replay' component if you do so, -# so that the problem can be fixed and tests reenabled. -skip-if = os != "mac" || debug || !nightly_build - -support-files = - head.js - examples/doc_rr_basic.html - examples/doc_rr_continuous.html - examples/doc_rr_logs.html - examples/doc_rr_recovery.html - examples/doc_rr_error.html - -[browser_dbg_rr_breakpoints-01.js] -[browser_dbg_rr_breakpoints-02.js] -[browser_dbg_rr_breakpoints-03.js] -[browser_dbg_rr_breakpoints-04.js] -[browser_dbg_rr_breakpoints-05.js] -[browser_dbg_rr_record.js] -[browser_dbg_rr_stepping-01.js] -[browser_dbg_rr_stepping-02.js] -[browser_dbg_rr_stepping-03.js] -[browser_dbg_rr_stepping-04.js] -[browser_dbg_rr_recovery-01.js] -skip-if = true # See bug 1481009 -[browser_dbg_rr_replay-01.js] -[browser_dbg_rr_replay-02.js] -[browser_dbg_rr_replay-03.js] -[browser_dbg_rr_console_warp-01.js] -[browser_dbg_rr_console_warp-02.js] diff --git a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js deleted file mode 100644 index 2f52a5ce3293..000000000000 --- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -// To disable all Web Replay tests, see browser.ini - -// Test basic console time warping functionality in web replay. -add_task(async function() { - const dbg = await attachRecordingDebugger( - "doc_rr_logs.html", - { waitForRecording: true } - ); - - const {tab, toolbox, threadClient} = dbg; - const console = await getDebuggerSplitConsole(dbg); - const hud = console.hud; - - let message = await warpToMessage(hud, dbg, "number: 1"); - ok(!message.classList.contains("paused-before"), "paused before message is not shown"); - - await stepOverToLine(threadClient, 18); - await reverseStepOverToLine(threadClient, 17); - - message = findMessage(hud, "number: 1"); - ok(message.classList.contains("paused-before"), "paused before message is shown"); - - await toolbox.destroy(); - await gBrowser.removeTab(tab); -}); diff --git a/devtools/client/webreplay/mochitest/head.js b/devtools/client/webreplay/mochitest/head.js deleted file mode 100644 index d7fe650ce90d..000000000000 --- a/devtools/client/webreplay/mochitest/head.js +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable no-undef */ - -"use strict"; - -Services.scriptloader.loadSubScript( - "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", - this -); - -Services.scriptloader.loadSubScript( - "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js", - this -); - -const EXAMPLE_URL = - "http://example.com/browser/devtools/client/webreplay/mochitest/examples/"; - -// Attach a debugger to a tab, returning a promise that resolves with the -// debugger's toolbox. -async function attachDebugger(tab) { - const target = await TargetFactory.forTab(tab); - const toolbox = await gDevTools.showToolbox(target, "jsdebugger"); - return toolbox; -} - -async function attachRecordingDebugger(url, - { waitForRecording } = { waitForRecording: false }) { - const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" }); - gBrowser.selectedTab = tab; - openTrustedLinkIn(EXAMPLE_URL + url, "current"); - - if (waitForRecording) { - await once(Services.ppmm, "RecordingFinished"); - } - const toolbox = await attachDebugger(tab); - const dbg = createDebuggerContext(toolbox); - const threadClient = dbg.toolbox.threadClient; - - await threadClient.interrupt(); - return {...dbg, tab, threadClient}; -} - -// Return a promise that resolves when a breakpoint has been set. -async function setBreakpoint(threadClient, expectedFile, lineno) { - const {sources} = await threadClient.getSources(); - ok(sources.length == 1, "Got one source"); - ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile); - const sourceClient = threadClient.source(sources[0]); - await sourceClient.setBreakpoint({ line: lineno }); -} - -function resumeThenPauseAtLineFunctionFactory(method) { - return async function(threadClient, lineno) { - threadClient[method](); - await threadClient.addOneTimeListener("paused", async function(event, packet) { - const {frames} = await threadClient.getFrames(0, 1); - const frameLine = frames[0] ? frames[0].where.line : undefined; - ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno); - }); - }; -} - -// Define various methods that resume a thread in a specific way and ensure it -// pauses at a specified line. -var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind"); -var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume"); -var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver"); -var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver"); -var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn"); -var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn"); -var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut"); -var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut"); - -// Return a promise that resolves when a thread evaluates a string in the -// topmost frame, with the result throwing an exception. -async function checkEvaluateInTopFrameThrows(threadClient, text) { - const {frames} = await threadClient.getFrames(0, 1); - ok(frames.length == 1, "Got one frame"); - const response = await threadClient.eval(frames[0].actor, text); - ok(response.type == "resumed", "Got resume response from eval"); - await threadClient.addOneTimeListener("paused", function(event, packet) { - ok(packet.type == "paused" && - packet.why.type == "clientEvaluated" && - "throw" in packet.why.frameFinished, "Eval threw an exception"); - }); -} - -// Return a pathname that can be used for a new recording file. -function newRecordingFile() { - ChromeUtils.import("resource://gre/modules/osfile.jsm", this); - return OS.Path.join(OS.Constants.Path.tmpDir, - "MochitestRecording" + Math.round(Math.random() * 1000000000)); -} - -function findMessage(hud, text, selector = ".message") { - return findMessages(hud, text, selector)[0]; -} - -function findMessages(hud, text, selector = ".message") { - const messages = hud.ui.outputNode.querySelectorAll(selector); - const elements = Array.prototype.filter.call( - messages, - (el) => el.textContent.includes(text) - ); - - if (elements.length == 0) { - return null; - } - - return elements; -} - -function waitForMessages(hud, text, selector = ".message") { - return waitUntilPredicate(() => findMessages(hud, text, selector)); -} - -async function warpToMessage(hud, threadClient, text) { - let messages = await waitForMessages(hud, text); - ok(messages.length == 1, "Found one message"); - const message = messages.pop(); - - const menuPopup = await openConsoleContextMenu(hud, message); - console.log(`.>> menu`, menuPopup); - - const timeWarpItem = menuPopup.querySelector("#console-menu-time-warp"); - ok(timeWarpItem, "Time warp menu item is available"); - - timeWarpItem.click(); - - await Promise.all([ - hideConsoleContextMenu(hud), - once(Services.ppmm, "TimeWarpFinished"), - waitForThreadEvents(threadClient, "paused"), - ]); - - messages = findMessages(hud, "", ".paused"); - ok(messages.length == 1, "Found one paused message"); - - return message; -} diff --git a/devtools/client/webreplay/moz.build b/devtools/client/webreplay/moz.build index b176495deef8..5560adb742f0 100644 --- a/devtools/client/webreplay/moz.build +++ b/devtools/client/webreplay/moz.build @@ -14,5 +14,3 @@ DevToolsModules( with Files('**'): BUG_COMPONENT = ('Core', 'Web Replay') - -BROWSER_CHROME_MANIFESTS += [ 'mochitest/browser.ini' ] From 2d4a38b6779bc6a998363cb407b0f271a57b0f46 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 15 Jan 2019 15:21:17 -0600 Subject: [PATCH 23/33] Bug 1515623 - Make wasm::HasCachingSupport(cx) accurately represent testing flags (r=lth) --- js/src/wasm/WasmCompile.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 7284fb5cae7c..40cb0732a57a 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -413,7 +413,6 @@ void CompilerEnvironment::computeParameters(Decoder& d, gcFeatureOptIn == HasGcTypes::True; bool argBaselineEnabled = args_->baselineEnabled || gcEnabled; bool argIonEnabled = args_->ionEnabled && !gcEnabled; - bool argTestTiering = args_->testTiering && !gcEnabled; bool argDebugEnabled = args_->debugEnabled; uint32_t codeSectionSize = 0; @@ -424,11 +423,9 @@ void CompilerEnvironment::computeParameters(Decoder& d, } // Attempt to default to ion if baseline is disabled. - bool baselineEnabled = - BaselineCanCompile() && (argBaselineEnabled || argTestTiering); + bool baselineEnabled = BaselineCanCompile() && argBaselineEnabled; bool debugEnabled = BaselineCanCompile() && argDebugEnabled; - bool ionEnabled = - IonCanCompile() && (argIonEnabled || !baselineEnabled || argTestTiering); + bool ionEnabled = IonCanCompile() && (argIonEnabled || !baselineEnabled); #ifdef ENABLE_WASM_CRANELIFT bool forceCranelift = args_->forceCranelift; #endif @@ -437,7 +434,7 @@ void CompilerEnvironment::computeParameters(Decoder& d, MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled); if (baselineEnabled && ionEnabled && !debugEnabled && CanUseExtraThreads() && - (TieringBeneficial(codeSectionSize) || argTestTiering)) { + (TieringBeneficial(codeSectionSize) || args_->testTiering)) { mode_ = CompileMode::Tier1; tier_ = Tier::Baseline; } else { From 6d9401a9e6bfc05eacea3f7a536919386d9e270a Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Fri, 4 Jan 2019 09:28:49 -0500 Subject: [PATCH 24/33] Bug 1453747 - Use rounded dest rect in simplified image decode size calculations for WebRender. r=mstange We are using the unrounded dest rect to calculate the image decode size in ComputeImageContainerDrawingParameters, while passing the rounded dest rect to WebRender. This mismatch causes images to be decoded to one size and display at another, cause some visual distortions. Using the correct rect seems to allow us to remove the extra snapping logic added to work around this. At this time, how we snap is different between WebRender and non-WebRender in general. This patch will likely morph again once we bring the two models closer together. Differential Revision: https://phabricator.services.mozilla.com/D15739 --- gfx/layers/wr/StackingContextHelper.h | 4 -- layout/base/nsLayoutUtils.cpp | 51 +++++------------------ layout/generic/nsBulletFrame.cpp | 4 +- layout/generic/nsImageFrame.cpp | 8 +++- layout/painting/nsCSSRenderingBorders.cpp | 3 +- layout/painting/nsImageRenderer.cpp | 6 ++- layout/xul/nsImageBoxFrame.cpp | 4 +- layout/xul/reftest/reftest.list | 2 +- 8 files changed, 29 insertions(+), 53 deletions(-) diff --git a/gfx/layers/wr/StackingContextHelper.h b/gfx/layers/wr/StackingContextHelper.h index cd7615f1b11d..79e6120073e9 100644 --- a/gfx/layers/wr/StackingContextHelper.h +++ b/gfx/layers/wr/StackingContextHelper.h @@ -55,10 +55,6 @@ class MOZ_RAII StackingContextHelper { // Export the inherited scale gfx::Size GetInheritedScale() const { return mScale; } - const gfx::Matrix& GetInheritedTransform() const { - return mInheritedTransform; - } - const gfx::Matrix& GetSnappingSurfaceTransform() const { return mSnappingSurfaceTransform; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 487ba30bf6cb..6b723fdb3a57 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -6361,8 +6361,6 @@ static SnappedImageDrawingParameters ComputeSnappedImageDrawingParameters( // Snap even if we have a scale in the context. But don't snap if // we have something that's not translation+scale, or if the scale flips in // the X or Y direction, because snapped image drawing can't handle that yet. - // Any changes to this algorithm will need to be reflected in - // ComputeImageContainerDrawingParameters. if (!currentMatrix.HasNonAxisAlignedTransform() && currentMatrix._11 > 0.0 && currentMatrix._22 > 0.0 && aCtx->UserToDevicePixelSnapped(fill, true) && aCtx->UserToDevicePixelSnapped(dest, true)) { @@ -6752,46 +6750,17 @@ static ImgDrawResult DrawImageInternal( } } - // Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters. - // Any changes to the algorithm here will need to be reflected there. - bool snapped = false; - gfxSize gfxLayerSize; - const gfx::Matrix& itm = aSc.GetInheritedTransform(); - if (!itm.HasNonAxisAlignedTransform() && itm._11 > 0.0 && itm._22 > 0.0) { - gfxRect rect(gfxPoint(aDestRect.X(), aDestRect.Y()), - gfxSize(aDestRect.Width(), aDestRect.Height())); + // Compute our size in layer pixels. We may need to revisit this for Android + // because mobile websites are rarely displayed at a 1:1 + // LayoutPixel:ScreenPixel ratio and the snapping here may be insufficient. + const LayerIntSize layerSize = + RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width, + aDestRect.Height() * scaleFactors.height)); - gfxPoint p1 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopLeft()))); - gfxPoint p2 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopRight()))); - gfxPoint p3 = ThebesPoint(itm.TransformPoint(ToPoint(rect.BottomRight()))); - - if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) { - p1.Round(); - p3.Round(); - - rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y))); - rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(), - std::max(p1.y, p3.y) - rect.Y())); - - // An empty size is unacceptable so we ensure our suggested size is at - // least 1 pixel wide/tall. - gfxLayerSize = - gfxSize(std::max(rect.Width(), 1.0), std::max(rect.Height(), 1.0)); - snapped = true; - } - } - - if (!snapped) { - // Compute our size in layer pixels. - const LayerIntSize layerSize = - RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width, - aDestRect.Height() * scaleFactors.height)); - - // An empty size is unacceptable so we ensure our suggested size is at least - // 1 pixel wide/tall. - gfxLayerSize = - gfxSize(std::max(layerSize.width, 1), std::max(layerSize.height, 1)); - } + // An empty size is unacceptable so we ensure our suggested size is at least + // 1 pixel wide/tall. + gfxSize gfxLayerSize = + gfxSize(std::max(layerSize.width, 1), std::max(layerSize.height, 1)); return aImage->OptimalImageSizeForDest( gfxLayerSize, imgIContainer::FRAME_CURRENT, samplingFilter, aFlags); diff --git a/layout/generic/nsBulletFrame.cpp b/layout/generic/nsBulletFrame.cpp index 57251c7217d9..bb511e3efb68 100644 --- a/layout/generic/nsBulletFrame.cpp +++ b/layout/generic/nsBulletFrame.cpp @@ -432,6 +432,8 @@ ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage( aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel); + destRect.Round(); + Maybe svgContext; gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( @@ -454,7 +456,7 @@ ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage( return drawResult; } - wr::LayoutRect dest = wr::ToRoundedLayoutRect(destRect); + wr::LayoutRect dest = wr::ToLayoutRect(destRect); aBuilder.PushImage(dest, dest, !aItem->BackfaceIsHidden(), rendering, key.value()); diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 9d5f170bf930..db028cabbad7 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1618,8 +1618,10 @@ ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer( size); const int32_t factor = PresContext()->AppUnitsPerDevPixel(); - const LayoutDeviceRect destRect( + LayoutDeviceRect destRect( LayoutDeviceRect::FromAppUnits(dest, factor)); + destRect.Round(); + Maybe svgContext; IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( @@ -1896,8 +1898,10 @@ bool nsDisplayImage::CreateWebRenderCommands( } const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel(); - const LayoutDeviceRect destRect( + LayoutDeviceRect destRect( LayoutDeviceRect::FromAppUnits(GetDestRect(), factor)); + destRect.Round(); + Maybe svgContext; IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( mImage, mFrame, destRect, aSc, flags, svgContext); diff --git a/layout/painting/nsCSSRenderingBorders.cpp b/layout/painting/nsCSSRenderingBorders.cpp index eb55dc60e572..528fe5e9617a 100644 --- a/layout/painting/nsCSSRenderingBorders.cpp +++ b/layout/painting/nsCSSRenderingBorders.cpp @@ -3553,7 +3553,8 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands( LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mArea, appUnitsPerDevPixel); - wr::LayoutRect dest = wr::ToRoundedLayoutRect(destRect); + destRect.Round(); + wr::LayoutRect dest = wr::ToLayoutRect(destRect); wr::LayoutRect clip = dest; if (!mClip.IsEmpty()) { diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index fde899948ee9..c4e4865a6f6b 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -567,6 +567,9 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems( mForFrame->PresContext()->AppUnitsPerDevPixel(); LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(aDest, appUnitsPerDevPixel); + auto stretchSize = wr::ToLayoutSize(destRect.Size()); + destRect.Round(); + gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( mImageContainer, mForFrame, destRect, aSc, containerFlags, @@ -600,8 +603,7 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems( appUnitsPerDevPixel); wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect); - wr::LayoutRect roundedDest = wr::ToRoundedLayoutRect(destRect); - auto stretchSize = wr::ToLayoutSize(destRect.Size()); + wr::LayoutRect roundedDest = wr::ToLayoutRect(destRect); // WebRender special cases situations where stretchSize == fillSize to // infer that it shouldn't use repeat sampling. This makes sure diff --git a/layout/xul/nsImageBoxFrame.cpp b/layout/xul/nsImageBoxFrame.cpp index d7e5d6f824ac..dcdbabf43739 100644 --- a/layout/xul/nsImageBoxFrame.cpp +++ b/layout/xul/nsImageBoxFrame.cpp @@ -398,6 +398,8 @@ ImgDrawResult nsImageBoxFrame::CreateWebRenderCommands( const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest, appUnitsPerDevPixel); + fillRect.Round(); + Maybe svgContext; gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( @@ -420,7 +422,7 @@ ImgDrawResult nsImageBoxFrame::CreateWebRenderCommands( if (key.isNothing()) { return result; } - wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect); + wr::LayoutRect fill = wr::ToLayoutRect(fillRect); LayoutDeviceSize gapSize(0, 0); aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), diff --git a/layout/xul/reftest/reftest.list b/layout/xul/reftest/reftest.list index ca8b3b6b9b63..4c518abcff94 100644 --- a/layout/xul/reftest/reftest.list +++ b/layout/xul/reftest/reftest.list @@ -1,7 +1,7 @@ fails-if(Android) == textbox-multiline-noresize.xul textbox-multiline-ref.xul # reference is blank on Android (due to no native theme support?) != textbox-multiline-resize.xul textbox-multiline-ref.xul == popup-explicit-size.xul popup-explicit-size-ref.xul -random-if(Android) fuzzy-if(webrender,128-128,168-168) == image-size.xul image-size-ref.xul +random-if(Android) == image-size.xul image-size-ref.xul == image-scaling-min-height-1.xul image-scaling-min-height-1-ref.xul == textbox-text-transform.xul textbox-text-transform-ref.xul From cd62fff2d8af311fbc5b30012bcf08c47693d363 Mon Sep 17 00:00:00 2001 From: Cristina Coroiu Date: Thu, 10 Jan 2019 14:19:00 +0200 Subject: [PATCH 25/33] Bug 1495002 - disable timeouts.py on win ccov for frequent failures. r=whimboo --- .../meta/webdriver/tests/new_session/timeouts.py.ini | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testing/web-platform/meta/webdriver/tests/new_session/timeouts.py.ini diff --git a/testing/web-platform/meta/webdriver/tests/new_session/timeouts.py.ini b/testing/web-platform/meta/webdriver/tests/new_session/timeouts.py.ini new file mode 100644 index 000000000000..a155e47fb9ad --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/new_session/timeouts.py.ini @@ -0,0 +1,2 @@ +[timeouts.py] + disabled: if ccov and (os == "win") and (bits == 64) and (version == "10.0.15063"): https://bugzilla.mozilla.org/show_bug.cgi?id=1495002 From 9da6d02ee2a9e62972ae730eb99183bdad641f05 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 8 Jan 2019 15:25:49 +0000 Subject: [PATCH 26/33] Bug 1501410 - Part 1 - Optimize the variables containing the global state. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D15944 --HG-- extra : rebase_source : dfe1baba6b1c0731d5fb12cbe4152067e997008c --- .../aboutconfig/content/aboutconfig.js | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index 53034bc59cff..6eb74e00efa2 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -6,7 +6,22 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/Preferences.jsm"); let gDefaultBranch = Services.prefs.getDefaultBranch(""); -let gPrefArray; + +/** + * Maps the name of each preference that exists in the back-end to its PrefRow + * object. This is as an optimization to avoid querying the preferences service + * each time the list is filtered. + */ +let gExistingPrefs = new Map(); + +/** + * Maps each row element currently in the table to its PrefRow object. + */ +let gElementToPrefMap = new WeakMap(); + +/** + * Reference to the PrefRow currently being edited, if any. + */ let gPrefInEdit = null; class PrefRow { @@ -16,8 +31,8 @@ class PrefRow { this.editing = false; this.element = document.createElement("tr"); - this.element.setAttribute("aria-label", this.name); this._setupElement(); + gElementToPrefMap.set(this.element, this); } refreshValue() { @@ -159,10 +174,6 @@ class PrefRow { } } -function getPrefName(prefRow) { - return prefRow.getAttribute("aria-label"); -} - document.addEventListener("DOMContentLoaded", () => { if (!Preferences.get("browser.aboutConfig.showWarning")) { loadPrefs(); @@ -187,9 +198,8 @@ function loadPrefs() { prefs.id = "prefs"; document.body.appendChild(prefs); - gPrefArray = Services.prefs.getChildList("").map(name => new PrefRow(name)); - - gPrefArray.sort((a, b) => a.name > b.name); + gExistingPrefs = new Map(Services.prefs.getChildList("") + .map(name => [name, new PrefRow(name)])); search.addEventListener("keypress", e => { if (e.key == "Enter") { @@ -202,12 +212,10 @@ function loadPrefs() { return; } let prefRow = event.target.closest("tr"); - let prefName = getPrefName(prefRow); - let pref = gPrefArray.find(p => p.name == prefName); + let pref = gElementToPrefMap.get(prefRow); let button = event.target.closest("button"); if (button.classList.contains("button-reset")) { - // Reset pref and update gPrefArray. - Services.prefs.clearUserPref(prefName); + Services.prefs.clearUserPref(pref.name); pref.refreshValue(); pref.refreshElement(); pref.editButton.focus(); @@ -220,8 +228,7 @@ function loadPrefs() { addNewPref(prefRow.firstChild.innerHTML, button.classList.contains("add-Number") ? 0 : "").edit(); } else if (button.classList.contains("button-toggle")) { - // Toggle the pref and update gPrefArray. - Services.prefs.setBoolPref(prefName, !pref.value); + Services.prefs.setBoolPref(pref.name, !pref.value); pref.refreshValue(); pref.refreshElement(); } else if (button.classList.contains("button-edit")) { @@ -229,8 +236,8 @@ function loadPrefs() { } else if (button.classList.contains("button-save")) { pref.save(); } else { - Services.prefs.clearUserPref(prefName); - gPrefArray.splice(gPrefArray.findIndex(p => p.name == prefName), 1); + Services.prefs.clearUserPref(pref.name); + gExistingPrefs.delete(pref.name); prefRow.remove(); } }); @@ -245,10 +252,15 @@ function filterPrefs() { let substring = document.getElementById("search").value.trim(); document.getElementById("prefs").textContent = ""; - if (substring && !gPrefArray.some(pref => pref.name == substring)) { + let prefArray = [...gExistingPrefs.values()]; + if (substring) { + prefArray = prefArray.filter(pref => pref.name.includes(substring)); + } + prefArray.sort((a, b) => a.name > b.name); + if (substring && !gExistingPrefs.has(substring)) { document.getElementById("prefs").appendChild(createNewPrefFragment(substring)); } - let fragment = createPrefsFragment(gPrefArray.filter(pref => pref.name.includes(substring))); + let fragment = createPrefsFragment(prefArray); document.getElementById("prefs").appendChild(fragment); } @@ -313,8 +325,7 @@ function prefHasDefaultValue(name) { function addNewPref(name, value) { Preferences.set(name, value); let pref = new PrefRow(name); - gPrefArray.push(pref); - gPrefArray.sort((a, b) => a.name > b.name); + gExistingPrefs.set(name, pref); filterPrefs(); return pref; } From 1c6a8a588a97ceefcd17361b0bad39243ee46dc4 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 15 Jan 2019 23:48:11 +0000 Subject: [PATCH 27/33] Bug 1501410 - Part 2 - Add support for recovering deleted preferences. r=bgrins This updates the user interface for adding new preferences to be closer to the mockup, and makes deleted preferences more similar to rows that are not added yet. This will later make it simpler to react to external changes in the underlying data. Differential Revision: https://phabricator.services.mozilla.com/D15945 --HG-- extra : rebase_source : dbd4bc08ea1a75ae534dce327103f854ef84f8dc --- .../aboutconfig/content/aboutconfig.css | 8 +- .../aboutconfig/content/aboutconfig.js | 152 +++++++++--------- .../aboutconfig/content/aboutconfig.notftl | 2 +- .../aboutconfig/test/browser/browser_edit.js | 89 ++++++---- .../aboutconfig/test/browser/head.js | 4 +- 5 files changed, 149 insertions(+), 106 deletions(-) diff --git a/browser/components/aboutconfig/content/aboutconfig.css b/browser/components/aboutconfig/content/aboutconfig.css index f6742d759a56..c15c8715c267 100644 --- a/browser/components/aboutconfig/content/aboutconfig.css +++ b/browser/components/aboutconfig/content/aboutconfig.css @@ -63,11 +63,17 @@ padding-inline-start: 30px; } +#prefs > tr.deleted > td.cell-name { + font-weight: bold; + opacity: 0.4; +} + .cell-value { word-break: break-all; } -td.cell-value > form > input { +td.cell-value > form > input[type="text"], +td.cell-value > form > input[type="number"] { -moz-appearance: textfield; width: 100%; box-sizing: border-box; diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index 6eb74e00efa2..e171f6d177d4 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -27,6 +27,7 @@ let gPrefInEdit = null; class PrefRow { constructor(name) { this.name = name; + this.value = true; this.refreshValue(); this.editing = false; @@ -36,11 +37,15 @@ class PrefRow { } refreshValue() { + this.hasDefaultValue = prefHasDefaultValue(this.name); this.hasUserValue = Services.prefs.prefHasUserValue(this.name); - this.hasDefaultValue = this.hasUserValue ? prefHasDefaultValue(this.name) - : true; this.isLocked = Services.prefs.prefIsLocked(this.name); + // If this preference has been deleted, we keep its last known value. + if (!this.exists) { + return; + } + try { // This can throw for locked preferences without a default value. this.value = Preferences.get(this.name); @@ -57,6 +62,14 @@ class PrefRow { } } + get type() { + return this.value.constructor.name; + } + + get exists() { + return this.hasDefaultValue || this.hasUserValue; + } + _setupElement() { this.element.textContent = ""; let nameCell = document.createElement("td"); @@ -88,9 +101,10 @@ class PrefRow { refreshElement() { this.element.classList.toggle("has-user-value", !!this.hasUserValue); this.element.classList.toggle("locked", !!this.isLocked); - if (!this.editing) { + this.element.classList.toggle("deleted", !this.exists); + if (this.exists && !this.editing) { this.valueCell.textContent = this.value; - if (this.value.constructor.name == "Boolean") { + if (this.type == "Boolean") { document.l10n.setAttributes(this.editButton, "about-config-pref-toggle"); this.editButton.className = "button-toggle"; } else { @@ -106,20 +120,52 @@ class PrefRow { let form = document.createElement("form"); form.addEventListener("submit", event => event.preventDefault()); form.id = "form-edit"; - this.inputField = document.createElement("input"); - this.inputField.value = this.value; - if (this.value.constructor.name == "Number") { - this.inputField.type = "number"; - this.inputField.required = true; - this.inputField.min = -2147483648; - this.inputField.max = 2147483647; + if (this.editing) { + this.inputField = document.createElement("input"); + this.inputField.value = this.value; + if (this.type == "Number") { + this.inputField.type = "number"; + this.inputField.required = true; + this.inputField.min = -2147483648; + this.inputField.max = 2147483647; + } else { + this.inputField.type = "text"; + } + form.appendChild(this.inputField); + document.l10n.setAttributes(this.editButton, "about-config-pref-save"); + this.editButton.className = "primary button-save"; } else { - this.inputField.type = "text"; + delete this.inputField; + for (let type of ["Boolean", "Number", "String"]) { + let radio = document.createElement("input"); + radio.type = "radio"; + radio.name = "type"; + radio.value = type; + radio.checked = this.type == type; + form.appendChild(radio); + let radioLabel = document.createElement("span"); + radioLabel.textContent = type; + form.appendChild(radioLabel); + } + form.addEventListener("click", event => { + if (event.target.name != "type") { + return; + } + let type = event.target.value; + if (this.type != type) { + if (type == "Boolean") { + this.value = true; + } else if (type == "Number") { + this.value = 0; + } else { + this.value = ""; + } + } + }); + document.l10n.setAttributes(this.editButton, "about-config-pref-add"); + this.editButton.className = "button-add"; } - form.appendChild(this.inputField); this.valueCell.appendChild(form); - document.l10n.setAttributes(this.editButton, "about-config-pref-save"); - this.editButton.className = "primary button-save"; this.editButton.setAttribute("form", "form-edit"); } this.editButton.disabled = this.isLocked; @@ -154,7 +200,7 @@ class PrefRow { } save() { - if (this.value.constructor.name == "Number") { + if (this.type == "Number") { if (!this.inputField.reportValidity()) { return; } @@ -219,14 +265,13 @@ function loadPrefs() { pref.refreshValue(); pref.refreshElement(); pref.editButton.focus(); - } else if (button.classList.contains("add-true")) { - addNewPref(prefRow.firstChild.innerHTML, true); - } else if (button.classList.contains("add-false")) { - addNewPref(prefRow.firstChild.innerHTML, false); - } else if (button.classList.contains("add-Number") || - button.classList.contains("add-String")) { - addNewPref(prefRow.firstChild.innerHTML, - button.classList.contains("add-Number") ? 0 : "").edit(); + } else if (button.classList.contains("button-add")) { + Preferences.set(pref.name, pref.value); + pref.refreshValue(); + pref.refreshElement(); + if (pref.type != "Boolean") { + pref.edit(); + } } else if (button.classList.contains("button-toggle")) { Services.prefs.setBoolPref(pref.name, !pref.value); pref.refreshValue(); @@ -236,9 +281,11 @@ function loadPrefs() { } else if (button.classList.contains("button-save")) { pref.save(); } else { + pref.editing = false; Services.prefs.clearUserPref(pref.name); gExistingPrefs.delete(pref.name); - prefRow.remove(); + pref.refreshValue(); + pref.refreshElement(); } }); @@ -251,58 +298,21 @@ function filterPrefs() { } let substring = document.getElementById("search").value.trim(); - document.getElementById("prefs").textContent = ""; let prefArray = [...gExistingPrefs.values()]; if (substring) { prefArray = prefArray.filter(pref => pref.name.includes(substring)); } prefArray.sort((a, b) => a.name > b.name); - if (substring && !gExistingPrefs.has(substring)) { - document.getElementById("prefs").appendChild(createNewPrefFragment(substring)); - } - let fragment = createPrefsFragment(prefArray); - document.getElementById("prefs").appendChild(fragment); -} - -function createPrefsFragment(prefArray) { + let prefsElement = document.getElementById("prefs"); let fragment = document.createDocumentFragment(); for (let pref of prefArray) { fragment.appendChild(pref.element); } - return fragment; -} - -function createNewPrefFragment(name) { - let fragment = document.createDocumentFragment(); - let row = document.createElement("tr"); - row.classList.add("has-user-value"); - row.setAttribute("aria-label", name); - let nameCell = document.createElement("td"); - nameCell.className = "cell-name"; - nameCell.append(name); - row.appendChild(nameCell); - - let valueCell = document.createElement("td"); - valueCell.classList.add("cell-value"); - let guideText = document.createElement("span"); - document.l10n.setAttributes(guideText, "about-config-pref-add"); - valueCell.appendChild(guideText); - for (let item of ["true", "false", "Number", "String"]) { - let optionBtn = document.createElement("button"); - optionBtn.textContent = item; - optionBtn.classList.add("add-" + item); - valueCell.appendChild(optionBtn); + if (substring && !gExistingPrefs.has(substring)) { + fragment.appendChild((new PrefRow(substring)).element); } - row.appendChild(valueCell); - - let editCell = document.createElement("td"); - row.appendChild(editCell); - - let buttonCell = document.createElement("td"); - row.appendChild(buttonCell); - - fragment.appendChild(row); - return fragment; + prefsElement.textContent = ""; + prefsElement.appendChild(fragment); } function prefHasDefaultValue(name) { @@ -321,11 +331,3 @@ function prefHasDefaultValue(name) { } catch (ex) {} return false; } - -function addNewPref(name, value) { - Preferences.set(name, value); - let pref = new PrefRow(name); - gExistingPrefs.set(name, pref); - filterPrefs(); - return pref; -} diff --git a/browser/components/aboutconfig/content/aboutconfig.notftl b/browser/components/aboutconfig/content/aboutconfig.notftl index fd9a01553537..c502be2059f2 100644 --- a/browser/components/aboutconfig/content/aboutconfig.notftl +++ b/browser/components/aboutconfig/content/aboutconfig.notftl @@ -13,7 +13,7 @@ about-config-title = about:config about-config-search = .placeholder = Search -about-config-pref-add = Add as: +about-config-pref-add = Add about-config-pref-toggle = Toggle about-config-pref-edit = Edit about-config-pref-save = Save diff --git a/browser/components/aboutconfig/test/browser/browser_edit.js b/browser/components/aboutconfig/test/browser/browser_edit.js index b5d47582b4d0..d8c354c37ca9 100644 --- a/browser/components/aboutconfig/test/browser/browser_edit.js +++ b/browser/components/aboutconfig/test/browser/browser_edit.js @@ -18,36 +18,74 @@ add_task(async function setup() { }); add_task(async function test_add_user_pref() { - await AboutConfigTest.withNewTab(async function() { - Assert.ok(!Services.prefs.getChildList("").find(pref => pref == "testPref")); + const PREF_NEW = "test.aboutconfig.new"; + Assert.equal(Services.prefs.getPrefType(PREF_NEW), + Ci.nsIPrefBranch.PREF_INVALID); - for (let [buttonSelector, expectedValue] of [ - [".add-true", true], - [".add-false", false], - [".add-Number", 0], - [".add-String", ""], + await AboutConfigTest.withNewTab(async function() { + // The row for a new preference appears when searching for its name. + Assert.ok(!this.getRow(PREF_NEW)); + this.search(PREF_NEW); + let row = this.getRow(PREF_NEW); + Assert.ok(row.hasClass("deleted")); + + for (let [radioIndex, expectedValue, expectedEditingMode] of [ + [0, true, false], + [1, 0, true], + [2, "", true], ]) { - this.search("testPref"); - this.document.querySelector("#prefs button" + buttonSelector).click(); - Assert.ok(Services.prefs.getChildList("").find(pref => pref == "testPref")); - Assert.ok(Preferences.get("testPref") === expectedValue); - this.document.querySelector("#prefs button[data-l10n-id='about-config-pref-delete']").click(); + // Adding the preference should set the default for the data type. + row.element.querySelectorAll("input")[radioIndex].click(); + row.editColumnButton.click(); + Assert.ok(!row.hasClass("deleted")); + Assert.ok(Preferences.get(PREF_NEW) === expectedValue); + + // Number and String preferences should be in edit mode. + Assert.equal(!!row.valueInput, expectedEditingMode); + + // Repeat the search to verify that the preference remains. + this.search(PREF_NEW); + row = this.getRow(PREF_NEW); + Assert.ok(!row.hasClass("deleted")); + Assert.ok(!row.valueInput); + + // Reset the preference, then continue by adding a different type. + row.resetColumnButton.click(); + Assert.equal(Services.prefs.getPrefType(PREF_NEW), + Ci.nsIPrefBranch.PREF_INVALID); } }); }); add_task(async function test_delete_user_pref() { - Services.prefs.setBoolPref("userAddedPref", true); - await AboutConfigTest.withNewTab(async function() { - let row = this.getRow("userAddedPref"); - row.resetColumnButton.click(); - Assert.ok(!this.getRow("userAddedPref")); - Assert.ok(!Services.prefs.getChildList("").includes("userAddedPref")); + const PREF_NEW = "test.aboutconfig.new"; - // Search for nothing to test gPrefArray - this.search(); - Assert.ok(!this.getRow("userAddedPref")); - }); + for (let [radioIndex, testValue] of [ + [0, false], + [1, -1], + [2, "value"], + ]) { + Preferences.set(PREF_NEW, testValue); + await AboutConfigTest.withNewTab(async function() { + // Deleting the preference should keep the row. + let row = this.getRow(PREF_NEW); + row.resetColumnButton.click(); + Assert.ok(row.hasClass("deleted")); + Assert.equal(Services.prefs.getPrefType(PREF_NEW), + Ci.nsIPrefBranch.PREF_INVALID); + + // Re-adding the preference should keep the same value. + Assert.ok(row.element.querySelectorAll("input")[radioIndex].checked); + row.editColumnButton.click(); + Assert.ok(!row.hasClass("deleted")); + Assert.ok(Preferences.get(PREF_NEW) === testValue); + + // Searching again after deleting should remove the row. + row.resetColumnButton.click(); + this.search(); + Assert.ok(!this.getRow(PREF_NEW)); + }); + } }); add_task(async function test_reset_user_pref() { @@ -148,11 +186,8 @@ add_task(async function test_modify() { row.editColumnButton.click(); Assert.equal(row.valueInput.value, Preferences.get(prefName)); row.resetColumnButton.click(); - if (willDelete) { - Assert.ok(!this.getRow(prefName)); - } else { - Assert.ok(!row.hasClass("has-user-value")); - } + Assert.ok(!row.hasClass("has-user-value")); + Assert.equal(row.hasClass("deleted"), willDelete); } }); }); diff --git a/browser/components/aboutconfig/test/browser/head.js b/browser/components/aboutconfig/test/browser/head.js index 5de3315b8ad1..f3a43c2840eb 100644 --- a/browser/components/aboutconfig/test/browser/head.js +++ b/browser/components/aboutconfig/test/browser/head.js @@ -42,8 +42,8 @@ class AboutConfigRowTest { } /** - * This is normally "edit" or "toggle" based on the preference type, or "save" - * when the row is in edit mode. + * This is normally "edit" or "toggle" based on the preference type, "save" + * when the row is in edit mode, or "add" when the preference does not exist. */ get editColumnButton() { return this.querySelector("td.cell-edit > button"); From 9074b09f096a2f3b39d5862a135792b1a6fd24aa Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 8 Jan 2019 15:26:11 +0000 Subject: [PATCH 28/33] Bug 1501410 - Part 3 - Detect changes in preferences. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D15946 --HG-- extra : rebase_source : ccaf6bfb2142a0e8cc35d8917c368d524f88648c --- .../aboutconfig/content/aboutconfig.js | 84 +++++++---- .../aboutconfig/test/browser/browser.ini | 7 +- .../aboutconfig/test/browser/browser_edit.js | 3 - .../test/browser/browser_observe.js | 140 ++++++++++++++++++ .../aboutconfig/test/browser/head.js | 3 + 5 files changed, 204 insertions(+), 33 deletions(-) create mode 100644 browser/components/aboutconfig/test/browser/browser_observe.js diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index e171f6d177d4..2706051f104f 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -8,11 +8,12 @@ ChromeUtils.import("resource://gre/modules/Preferences.jsm"); let gDefaultBranch = Services.prefs.getDefaultBranch(""); /** - * Maps the name of each preference that exists in the back-end to its PrefRow - * object. This is as an optimization to avoid querying the preferences service - * each time the list is filtered. + * Maps the name of each preference in the back-end to its PrefRow object, + * separating the preferences that actually exist. This is as an optimization to + * avoid querying the preferences service each time the list is filtered. */ let gExistingPrefs = new Map(); +let gDeletedPrefs = new Map(); /** * Maps each row element currently in the table to its PrefRow object. @@ -24,6 +25,11 @@ let gElementToPrefMap = new WeakMap(); */ let gPrefInEdit = null; +/** + * Substring that should be contained in the preference name. + */ +let gFilterString = null; + class PrefRow { constructor(name) { this.name = name; @@ -43,8 +49,12 @@ class PrefRow { // If this preference has been deleted, we keep its last known value. if (!this.exists) { + gExistingPrefs.delete(this.name); + gDeletedPrefs.set(this.name, this); return; } + gExistingPrefs.set(this.name, this); + gDeletedPrefs.delete(this.name); try { // This can throw for locked preferences without a default value. @@ -70,6 +80,10 @@ class PrefRow { return this.hasDefaultValue || this.hasUserValue; } + get matchesFilter() { + return !gFilterString || this.name.includes(gFilterString); + } + _setupElement() { this.element.textContent = ""; let nameCell = document.createElement("td"); @@ -220,6 +234,24 @@ class PrefRow { } } +let gPrefObserver = { + observe(subject, topic, data) { + let pref = gExistingPrefs.get(data) || gDeletedPrefs.get(data); + if (pref) { + pref.refreshValue(); + if (!pref.editing) { + pref.refreshElement(); + } + return; + } + + let newPref = new PrefRow(data); + if (newPref.matchesFilter) { + document.getElementById("prefs").appendChild(newPref.element); + } + }, +}; + document.addEventListener("DOMContentLoaded", () => { if (!Preferences.get("browser.aboutConfig.showWarning")) { loadPrefs(); @@ -244,8 +276,9 @@ function loadPrefs() { prefs.id = "prefs"; document.body.appendChild(prefs); - gExistingPrefs = new Map(Services.prefs.getChildList("") - .map(name => [name, new PrefRow(name)])); + for (let name of Services.prefs.getChildList("")) { + new PrefRow(name); + } search.addEventListener("keypress", e => { if (e.key == "Enter") { @@ -257,61 +290,58 @@ function loadPrefs() { if (event.target.localName != "button") { return; } - let prefRow = event.target.closest("tr"); - let pref = gElementToPrefMap.get(prefRow); + let pref = gElementToPrefMap.get(event.target.closest("tr")); let button = event.target.closest("button"); - if (button.classList.contains("button-reset")) { - Services.prefs.clearUserPref(pref.name); - pref.refreshValue(); - pref.refreshElement(); - pref.editButton.focus(); - } else if (button.classList.contains("button-add")) { + if (button.classList.contains("button-add")) { Preferences.set(pref.name, pref.value); - pref.refreshValue(); - pref.refreshElement(); if (pref.type != "Boolean") { pref.edit(); } } else if (button.classList.contains("button-toggle")) { Services.prefs.setBoolPref(pref.name, !pref.value); - pref.refreshValue(); - pref.refreshElement(); } else if (button.classList.contains("button-edit")) { pref.edit(); } else if (button.classList.contains("button-save")) { pref.save(); } else { + // This is "button-reset" or "button-delete". pref.editing = false; Services.prefs.clearUserPref(pref.name); - gExistingPrefs.delete(pref.name); - pref.refreshValue(); - pref.refreshElement(); + pref.editButton.focus(); } }); filterPrefs(); + + Services.prefs.addObserver("", gPrefObserver); + window.addEventListener("unload", () => { + Services.prefs.removeObserver("", gPrefObserver); + }, { once: true }); } function filterPrefs() { if (gPrefInEdit) { gPrefInEdit.endEdit(); } + gDeletedPrefs.clear(); - let substring = document.getElementById("search").value.trim(); + let searchName = document.getElementById("search").value.trim(); + gFilterString = searchName; let prefArray = [...gExistingPrefs.values()]; - if (substring) { - prefArray = prefArray.filter(pref => pref.name.includes(substring)); + if (gFilterString) { + prefArray = prefArray.filter(pref => pref.matchesFilter); } prefArray.sort((a, b) => a.name > b.name); + if (searchName && !gExistingPrefs.has(searchName)) { + prefArray.push(new PrefRow(searchName)); + } + let prefsElement = document.getElementById("prefs"); + prefsElement.textContent = ""; let fragment = document.createDocumentFragment(); for (let pref of prefArray) { fragment.appendChild(pref.element); } - if (substring && !gExistingPrefs.has(substring)) { - fragment.appendChild((new PrefRow(substring)).element); - } - prefsElement.textContent = ""; prefsElement.appendChild(fragment); } diff --git a/browser/components/aboutconfig/test/browser/browser.ini b/browser/components/aboutconfig/test/browser/browser.ini index 49073ee0ba86..4204bcd58e98 100644 --- a/browser/components/aboutconfig/test/browser/browser.ini +++ b/browser/components/aboutconfig/test/browser/browser.ini @@ -3,10 +3,11 @@ support-files = head.js [browser_basic.js] -[browser_warning.js] [browser_edit.js] skip-if = debug # Bug 1507747 +[browser_locked.js] +[browser_observe.js] +skip-if = debug # Bug 1507747 [browser_search.js] skip-if = debug # Bug 1507747 -[browser_locked.js] - +[browser_warning.js] diff --git a/browser/components/aboutconfig/test/browser/browser_edit.js b/browser/components/aboutconfig/test/browser/browser_edit.js index d8c354c37ca9..177296095972 100644 --- a/browser/components/aboutconfig/test/browser/browser_edit.js +++ b/browser/components/aboutconfig/test/browser/browser_edit.js @@ -18,7 +18,6 @@ add_task(async function setup() { }); add_task(async function test_add_user_pref() { - const PREF_NEW = "test.aboutconfig.new"; Assert.equal(Services.prefs.getPrefType(PREF_NEW), Ci.nsIPrefBranch.PREF_INVALID); @@ -58,8 +57,6 @@ add_task(async function test_add_user_pref() { }); add_task(async function test_delete_user_pref() { - const PREF_NEW = "test.aboutconfig.new"; - for (let [radioIndex, testValue] of [ [0, false], [1, -1], diff --git a/browser/components/aboutconfig/test/browser/browser_observe.js b/browser/components/aboutconfig/test/browser/browser_observe.js new file mode 100644 index 000000000000..adba609d499d --- /dev/null +++ b/browser/components/aboutconfig/test/browser/browser_observe.js @@ -0,0 +1,140 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["test.aboutconfig.modify.boolean", true], + ["test.aboutconfig.modify.number", 1337], + ["test.aboutconfig.modify.string", "the answer to the life the universe and everything"], + ], + }); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref(PREF_BOOLEAN_DEFAULT_TRUE); + Services.prefs.clearUserPref(PREF_NUMBER_DEFAULT_ZERO); + Services.prefs.clearUserPref(PREF_STRING_DEFAULT_EMPTY); + }); +}); + +add_task(async function test_observe_add_user_pref() { + Assert.equal(Services.prefs.getPrefType(PREF_NEW), + Ci.nsIPrefBranch.PREF_INVALID); + + await AboutConfigTest.withNewTab(async function() { + for (let value of [false, true, "", "value", 0, -10]) { + // A row should be added when a new preference is added. + Assert.ok(!this.getRow(PREF_NEW)); + Preferences.set(PREF_NEW, value); + let row = this.getRow(PREF_NEW); + Assert.equal(row.value, "" + value); + + // The row should stay when the preference is removed. + Preferences.reset(PREF_NEW); + Assert.ok(row.hasClass("deleted")); + + // Re-adding the preference from the interface should restore its value. + row.editColumnButton.click(); + if (value.constructor.name != "Boolean") { + row.editColumnButton.click(); + } + Assert.equal(row.value, "" + value); + Assert.ok(Preferences.get(PREF_NEW) === value); + + // Searching again after deleting should remove the row. + Preferences.reset(PREF_NEW); + this.search(); + Assert.ok(!this.getRow(PREF_NEW)); + + // Searching for the preference name should give the ability to add it. + Preferences.reset(PREF_NEW); + this.search(PREF_NEW); + row = this.getRow(PREF_NEW); + Assert.ok(row.hasClass("deleted")); + + // The row for adding should be reused if the new preference is added. + Preferences.set(PREF_NEW, value); + Assert.equal(row.value, "" + value); + + // If a new preference does not match the filter it is not displayed. + Preferences.reset(PREF_NEW); + this.search(PREF_NEW + ".extra"); + Assert.ok(!this.getRow(PREF_NEW)); + Preferences.set(PREF_NEW, value); + Assert.ok(!this.getRow(PREF_NEW)); + + // Resetting the filter should display the new preference. + this.search(""); + Assert.equal(this.getRow(PREF_NEW).value, "" + value); + + // Reset the preference, then continue by adding a different value. + Preferences.reset(PREF_NEW); + this.search(""); + } + }); +}); + +add_task(async function test_observe_delete_user_pref() { + for (let value of [true, "value", -10]) { + Preferences.set(PREF_NEW, value); + await AboutConfigTest.withNewTab(async function() { + // Deleting the preference should keep the row. + let row = this.getRow(PREF_NEW); + Preferences.reset(PREF_NEW); + Assert.ok(row.hasClass("deleted")); + + // Searching again should remove the row. + this.search(); + Assert.ok(!this.getRow(PREF_NEW)); + }); + } +}); + +add_task(async function test_observe_reset_user_pref() { + await SpecialPowers.pushPrefEnv({ + "set": [ + [PREF_BOOLEAN_DEFAULT_TRUE, false], + ], + }); + + await AboutConfigTest.withNewTab(async function() { + let row = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE); + Preferences.reset(PREF_BOOLEAN_DEFAULT_TRUE); + Assert.ok(!row.hasClass("has-user-value")); + Assert.equal(row.value, "true"); + }); +}); + +add_task(async function test_observe_modify() { + await AboutConfigTest.withNewTab(async function() { + for (let [name, value] of [ + ["test.aboutconfig.modify.boolean", false], + ["test.aboutconfig.modify.number", -10], + ["test.aboutconfig.modify.string", "value"], + [PREF_BOOLEAN_DEFAULT_TRUE, false], + [PREF_NUMBER_DEFAULT_ZERO, 1], + [PREF_STRING_DEFAULT_EMPTY, "string"], + ]) { + let row = this.getRow(name); + Assert.notEqual(row.value, "" + value); + Preferences.set(name, value); + Assert.equal(row.value, "" + value); + + if (value.constructor.name == "Boolean") { + continue; + } + + // Changing the value or removing while editing should not take effect. + row.editColumnButton.click(); + row.valueInput.value = "42"; + Preferences.reset(name); + Assert.equal(row.element, this.getRow(name).element); + Assert.equal(row.valueInput.value, "42"); + + // Saving should store the value even if the preference was modified. + row.editColumnButton.click(); + Assert.equal(row.value, "42"); + Assert.equal(Preferences.get(name), "42"); + } + }); +}); diff --git a/browser/components/aboutconfig/test/browser/head.js b/browser/components/aboutconfig/test/browser/head.js index f3a43c2840eb..3a6c4f658d0a 100644 --- a/browser/components/aboutconfig/test/browser/head.js +++ b/browser/components/aboutconfig/test/browser/head.js @@ -17,6 +17,9 @@ const PREF_STRING_DEFAULT_NOTEMPTY = "accessibility.typeaheadfind.soundURL"; const PREF_STRING_DEFAULT_NOTEMPTY_VALUE = "beep"; const PREF_STRING_LOCALIZED_MISSING = "gecko.handlerService.schemes.irc.1.name"; +// Other preference names used in tests. +const PREF_NEW = "test.aboutconfig.new"; + class AboutConfigRowTest { constructor(element) { this.element = element; From eb32faadeedc85786ac0183e4302ddb8b436ea4b Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 8 Jan 2019 15:08:04 +0000 Subject: [PATCH 29/33] Bug 1501410 - Part 4 - Make the filter case insensitive. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D15947 --HG-- extra : rebase_source : b3a2788b0ac8444b2942aa6aeeacd4a5c954ec90 --- browser/components/aboutconfig/content/aboutconfig.js | 6 +++--- .../aboutconfig/test/browser/browser_search.js | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index 2706051f104f..3382fe1738ae 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -26,7 +26,7 @@ let gElementToPrefMap = new WeakMap(); let gPrefInEdit = null; /** - * Substring that should be contained in the preference name. + * Lowercase substring that should be contained in the preference name. */ let gFilterString = null; @@ -81,7 +81,7 @@ class PrefRow { } get matchesFilter() { - return !gFilterString || this.name.includes(gFilterString); + return !gFilterString || this.name.toLowerCase().includes(gFilterString); } _setupElement() { @@ -326,7 +326,7 @@ function filterPrefs() { gDeletedPrefs.clear(); let searchName = document.getElementById("search").value.trim(); - gFilterString = searchName; + gFilterString = searchName.toLowerCase(); let prefArray = [...gExistingPrefs.values()]; if (gFilterString) { prefArray = prefArray.filter(pref => pref.matchesFilter); diff --git a/browser/components/aboutconfig/test/browser/browser_search.js b/browser/components/aboutconfig/test/browser/browser_search.js index 043390d4c63b..7e5a944f2d16 100644 --- a/browser/components/aboutconfig/test/browser/browser_search.js +++ b/browser/components/aboutconfig/test/browser/browser_search.js @@ -25,7 +25,7 @@ add_task(async function test_search() { // Filter a subset of preferences. The "browser.download." branch is // chosen because it is very unlikely that its preferences would be // modified by other code during the execution of this test. - this.search("wser.down "); + this.search("Wser.down "); let filteredPrefArray = prefArray.filter(pref => pref.includes("wser.down")); @@ -49,8 +49,13 @@ add_task(async function test_search() { this.search("aJunkValueasdf"); Assert.equal(this.rows.length, 1); - // Test added preferences search returns 2 preferences. + // Two preferences match this filter, and one of those matches exactly. this.search("test.aboutconfig.a"); Assert.equal(this.rows.length, 2); + + // When searching case insensitively, there is an additional row to add a + // new preference with the same name but a different case. + this.search("TEST.aboutconfig.a"); + Assert.equal(this.rows.length, 3); }); }); From 8df9d6843c6fdae75867a2f5a54f3115b145e064 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 15 Jan 2019 14:21:07 +0000 Subject: [PATCH 30/33] Bug 1501411 - Make sure that copying preference values from the new "about:config" page preserves whitespace. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D16056 --HG-- extra : rebase_source : de02625fed68f99e5daa6b3a9e783f9b985897eb --- .../aboutconfig/content/aboutconfig.css | 1 + .../aboutconfig/content/aboutconfig.js | 7 +- .../aboutconfig/test/browser/browser.ini | 3 + .../test/browser/browser_clipboard.js | 92 +++++++++++++++++++ .../aboutconfig/test/browser/head.js | 16 +++- .../mochitest/tests/SimpleTest/SimpleTest.js | 2 +- 6 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 browser/components/aboutconfig/test/browser/browser_clipboard.js diff --git a/browser/components/aboutconfig/content/aboutconfig.css b/browser/components/aboutconfig/content/aboutconfig.css index c15c8715c267..491b91b97433 100644 --- a/browser/components/aboutconfig/content/aboutconfig.css +++ b/browser/components/aboutconfig/content/aboutconfig.css @@ -69,6 +69,7 @@ } .cell-value { + white-space: pre-wrap; word-break: break-all; } diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index 3382fe1738ae..b8f32ecc5664 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -117,7 +117,12 @@ class PrefRow { this.element.classList.toggle("locked", !!this.isLocked); this.element.classList.toggle("deleted", !this.exists); if (this.exists && !this.editing) { - this.valueCell.textContent = this.value; + // We need to place the text inside a "span" element to ensure that the + // text copied to the clipboard includes all whitespace. + let span = document.createElement("span"); + span.textContent = this.value; + this.valueCell.textContent = ""; + this.valueCell.append(span); if (this.type == "Boolean") { document.l10n.setAttributes(this.editButton, "about-config-pref-toggle"); this.editButton.className = "button-toggle"; diff --git a/browser/components/aboutconfig/test/browser/browser.ini b/browser/components/aboutconfig/test/browser/browser.ini index 4204bcd58e98..3e0020c576da 100644 --- a/browser/components/aboutconfig/test/browser/browser.ini +++ b/browser/components/aboutconfig/test/browser/browser.ini @@ -3,6 +3,9 @@ support-files = head.js [browser_basic.js] +[browser_clipboard.js] +skip-if = debug # Bug 1507747 +subsuite = clipboard [browser_edit.js] skip-if = debug # Bug 1507747 [browser_locked.js] diff --git a/browser/components/aboutconfig/test/browser/browser_clipboard.js b/browser/components/aboutconfig/test/browser/browser_clipboard.js new file mode 100644 index 000000000000..7b04235b0362 --- /dev/null +++ b/browser/components/aboutconfig/test/browser/browser_clipboard.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["test.aboutconfig.copy.false", false], + ["test.aboutconfig.copy.number", 10], + ["test.aboutconfig.copy.spaces.1", " "], + ["test.aboutconfig.copy.spaces.2", " "], + ["test.aboutconfig.copy.spaces.3", " "], + ["test.aboutconfig.copy.string", "010.5"], + ], + }); +}); + +add_task(async function test_copy() { + await AboutConfigTest.withNewTab(async function() { + for (let [name, expectedString] of [ + [PREF_BOOLEAN_DEFAULT_TRUE, "true"], + [PREF_BOOLEAN_USERVALUE_TRUE, "true"], + [PREF_STRING_DEFAULT_EMPTY, ""], + ["test.aboutconfig.copy.false", "false"], + ["test.aboutconfig.copy.number", "10"], + ["test.aboutconfig.copy.spaces.1", " "], + ["test.aboutconfig.copy.spaces.2", " "], + ["test.aboutconfig.copy.spaces.3", " "], + ["test.aboutconfig.copy.string", "010.5"], + ]) { + // Limit the number of preferences shown so all the rows are visible. + this.search(name); + let row = this.getRow(name); + + // Triple click at any location in the name cell should select the name. + await BrowserTestUtils.synthesizeMouseAtCenter(row.nameCell, + { clickCount: 3 }, + this.browser); + Assert.ok(row.nameCell.contains(this.window.getSelection().anchorNode)); + await SimpleTest.promiseClipboardChange(name, async () => { + await BrowserTestUtils.synthesizeKey("c", { accelKey: true }, + this.browser); + }); + + // Triple click at any location in the value cell should select the value. + await BrowserTestUtils.synthesizeMouseAtCenter(row.valueCell, + { clickCount: 3 }, + this.browser); + let selection = this.window.getSelection(); + Assert.ok(row.valueCell.contains(selection.anchorNode)); + + // The selection is never collapsed because of the element, and + // this makes sure that an empty string can be copied. + Assert.ok(!selection.isCollapsed); + await SimpleTest.promiseClipboardChange(expectedString, async () => { + await BrowserTestUtils.synthesizeKey("c", { accelKey: true }, + this.browser); + }); + } + }); +}); + +add_task(async function test_copy_multiple() { + await AboutConfigTest.withNewTab(async function() { + // Lines are separated by a single LF character on all platforms. + let expectedString = "test.aboutconfig.copy.false\tfalse\t\n" + + "test.aboutconfig.copy.number\t10\t\n" + + "test.aboutconfig.copy.spaces.1\t \t\n" + + "test.aboutconfig.copy.spaces.2\t \t\n" + + "test.aboutconfig.copy.spaces.3\t \t\n" + + "test.aboutconfig.copy.string\t010.5"; + + this.search("test.aboutconfig.copy."); + let startRow = this.getRow("test.aboutconfig.copy.false"); + let endRow = this.getRow("test.aboutconfig.copy.string"); + let { width, height } = endRow.valueCell.getBoundingClientRect(); + + // Drag from the top left of the first row to the bottom right of the last. + await BrowserTestUtils.synthesizeMouse(startRow.nameCell, 1, 1, + { type: "mousedown" }, this.browser); + await BrowserTestUtils.synthesizeMouse(endRow.valueCell, + width - 1, height - 1, + { type: "mousemove" }, this.browser); + await BrowserTestUtils.synthesizeMouse(endRow.valueCell, + width - 1, height - 1, + { type: "mouseup" }, this.browser); + + await SimpleTest.promiseClipboardChange(expectedString, async () => { + await BrowserTestUtils.synthesizeKey("c", { accelKey: true }, + this.browser); + }); + }); +}); diff --git a/browser/components/aboutconfig/test/browser/head.js b/browser/components/aboutconfig/test/browser/head.js index 3a6c4f658d0a..5525f6f78489 100644 --- a/browser/components/aboutconfig/test/browser/head.js +++ b/browser/components/aboutconfig/test/browser/head.js @@ -29,19 +29,27 @@ class AboutConfigRowTest { return this.element.querySelector(selector); } + get nameCell() { + return this.querySelector("td"); + } + get name() { - return this.querySelector("td").textContent; + return this.nameCell.textContent; + } + + get valueCell() { + return this.querySelector("td.cell-value"); } get value() { - return this.querySelector("td.cell-value").textContent; + return this.valueCell.textContent; } /** * Text input field when the row is in edit mode. */ get valueInput() { - return this.querySelector("td.cell-value input"); + return this.valueCell.querySelector("input"); } /** @@ -77,7 +85,9 @@ class AboutConfigTest { } constructor(browser) { + this.browser = browser; this.document = browser.contentDocument; + this.window = browser.contentWindow; } async setupNewTab(options) { diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 2da5e4b0c736..86835c481857 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -990,7 +990,7 @@ SimpleTest.promiseClipboardChange = async function(aExpectedStringOrValidatorFn, let maxPolls = aTimeout ? aTimeout / 100 : 50; async function putAndVerify(operationFn, validatorFn, flavor) { - operationFn(); + await operationFn(); let data; for (let i = 0; i < maxPolls; i++) { From 64e2c3597bd80ba49a762483a87fffe15f229d5c Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 15 Jan 2019 14:22:16 +0000 Subject: [PATCH 31/33] Bug 1501417 - Part 1 - Use the search string set in previous sessions. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D16309 --HG-- extra : rebase_source : c64b56a9013bdb6145b2c71c0d00400e53019cb1 --- .../aboutconfig/content/aboutconfig.html | 2 +- .../aboutconfig/content/aboutconfig.js | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/browser/components/aboutconfig/content/aboutconfig.html b/browser/components/aboutconfig/content/aboutconfig.html index 23b9c31424d6..3155c9cd4926 100644 --- a/browser/components/aboutconfig/content/aboutconfig.html +++ b/browser/components/aboutconfig/content/aboutconfig.html @@ -35,7 +35,7 @@
-
diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index b8f32ecc5664..fbe9ebb11035 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -257,15 +257,19 @@ let gPrefObserver = { }, }; -document.addEventListener("DOMContentLoaded", () => { - if (!Preferences.get("browser.aboutConfig.showWarning")) { - loadPrefs(); - } -}, { once: true }); +if (!Preferences.get("browser.aboutConfig.showWarning")) { + // When showing the filtered preferences directly, remove the warning elements + // immediately to prevent flickering, but wait to filter the preferences until + // the value of the textbox has been restored from previous sessions. + document.addEventListener("DOMContentLoaded", loadPrefs, { once: true }); + window.addEventListener("load", filterPrefs, { once: true }); +} -function alterWarningState() { +function onWarningButtonClick() { Services.prefs.setBoolPref("browser.aboutConfig.showWarning", document.getElementById("showWarningNextTime").checked); + loadPrefs(); + filterPrefs(); } function loadPrefs() { @@ -316,8 +320,6 @@ function loadPrefs() { } }); - filterPrefs(); - Services.prefs.addObserver("", gPrefObserver); window.addEventListener("unload", () => { Services.prefs.removeObserver("", gPrefObserver); From 347205d643789e61c03f042fb833b3be5732213a Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 15 Jan 2019 14:37:30 +0000 Subject: [PATCH 32/33] Bug 1501417 - Part 2 - Don't wait for the Enter key in the new "about:config" search field. r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D16310 --HG-- extra : rebase_source : ac093624c1d7a02e56da4e567d6d33f502325a6e --- .../aboutconfig/content/aboutconfig.js | 21 ++++++++++++-- .../test/browser/browser_search.js | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index fbe9ebb11035..7e6398609c33 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -2,10 +2,14 @@ * 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.import("resource://gre/modules/DeferredTask.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/Preferences.jsm"); +const SEARCH_TIMEOUT_MS = 500; + let gDefaultBranch = Services.prefs.getDefaultBranch(""); +let gFilterPrefsTask = new DeferredTask(() => filterPrefs(), SEARCH_TIMEOUT_MS); /** * Maps the name of each preference in the back-end to its PrefRow object, @@ -289,12 +293,23 @@ function loadPrefs() { new PrefRow(name); } - search.addEventListener("keypress", e => { - if (e.key == "Enter") { - filterPrefs(); + search.addEventListener("keypress", event => { + switch (event.key) { + case "Escape": + search.value = ""; + // Fall through. + case "Enter": + gFilterPrefsTask.disarm(); + filterPrefs(); } }); + search.addEventListener("input", () => { + // We call "disarm" to restart the timer at every input. + gFilterPrefsTask.disarm(); + gFilterPrefsTask.arm(); + }); + prefs.addEventListener("click", event => { if (event.target.localName != "button") { return; diff --git a/browser/components/aboutconfig/test/browser/browser_search.js b/browser/components/aboutconfig/test/browser/browser_search.js index 7e5a944f2d16..aa8d11f1440b 100644 --- a/browser/components/aboutconfig/test/browser/browser_search.js +++ b/browser/components/aboutconfig/test/browser/browser_search.js @@ -59,3 +59,31 @@ add_task(async function test_search() { Assert.equal(this.rows.length, 3); }); }); + +add_task(async function test_search_delayed() { + await AboutConfigTest.withNewTab(async function() { + let prefs = this.document.getElementById("prefs"); + + // Prepare the table and the search field for the test. + this.search("test.aboutconfig.a"); + Assert.equal(this.rows.length, 2); + + // The table is updated in a single microtask, so we don't need to wait for + // specific mutations, we can just continue when the children are updated. + let prefsTableChanged = new Promise(resolve => { + let observer = new MutationObserver(() => { + observer.disconnect(); + resolve(); + }); + observer.observe(prefs, { childList: true }); + }); + + // Add a character and test that the table is not updated immediately. + EventUtils.synthesizeKey("b"); + Assert.equal(this.rows.length, 2); + + // The table will eventually be updated after a delay. + await prefsTableChanged; + Assert.equal(this.rows.length, 1); + }); +}); From 994713f90586d390375ea4419b270d2156c6400b Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Tue, 15 Jan 2019 17:25:17 -0600 Subject: [PATCH 33/33] Bug 1519541 - Clamp negative portions of relative scroll anchoring bounding rect. r=dholbert The scroll anchoring bounding rect of a node can be influenced by absolutely positioned descendants with very negative offsets. This can cause undesired scroll adjustments, and has been seen on the web in Gmail. The spec needs to be amended to say what to do here. Chrome currently will clamp the vertical offset. This commit implements a stop-gap to clamp the negative portions to fix this issue, while we do more research and spec-work. Differential Revision: https://phabricator.services.mozilla.com/D16625 --HG-- extra : rebase_source : 882ac29fca602ae398ffa74bf5747a8eeb4e9329 extra : amend_source : b6537c3626c5bae60285a4e55399b69ad52206b4 extra : source : ecc18f11431e2da2676962c82962932b4465fb38 --- layout/generic/ScrollAnchorContainer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp index 2a8cb1f55c2b..819acaa0cdcc 100644 --- a/layout/generic/ScrollAnchorContainer.cpp +++ b/layout/generic/ScrollAnchorContainer.cpp @@ -102,6 +102,14 @@ static nsRect FindScrollAnchoringBoundingRect(const nsIFrame* aScrollFrame, } nsRect localRect = aCandidate->GetScrollableOverflowRectRelativeToSelf(); + + // XXX this isn't correct with non-vertical-tb writing-mode, see bug 1520344 + if (localRect.X() < 0) { + localRect.SetBoxX(0, localRect.XMost()); + } + if (localRect.Y() < 0) { + localRect.SetBoxY(0, localRect.YMost()); + } nsRect transformed = nsLayoutUtils::TransformFrameRectToAncestor( aCandidate, localRect, aScrollFrame); return transformed;