From 19726944c5cb2180786e28694429e51c6aa29377 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:36 +0900 Subject: [PATCH 01/78] Bug 1397422 - Part 1: Allow autoscrolling on XUL elements. r=jaws --- toolkit/content/browser-content.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index 63116f09cc2e..88124f5ca4fe 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -75,6 +75,14 @@ var ClickEventHandler = { return false; }, + isScrollableElement(aNode) { + if (aNode instanceof content.HTMLElement) { + return !(aNode instanceof content.HTMLSelectElement) || aNode.multiple; + } + + return aNode instanceof content.XULElement; + }, + findNearestScrollableElement(aNode) { // this is a list of overflow property values that allow scrolling const scrollingAllowed = ["scroll", "auto"]; @@ -84,10 +92,9 @@ var ClickEventHandler = { for (this._scrollable = aNode; this._scrollable; this._scrollable = this._scrollable.parentNode) { // do not use overflow based autoscroll for and - // Elements or non-html elements such as svg or Document nodes + // Elements or non-html/non-xul elements such as svg or Document nodes // also make sure to skip select elements that are not multiline - if (!(this._scrollable instanceof content.HTMLElement) || - ((this._scrollable instanceof content.HTMLSelectElement) && !this._scrollable.multiple)) { + if (!this.isScrollableElement(this._scrollable)) { continue; } From 9edef3186ba9fb72499508b8fd55b76eb5361dbc Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:36 +0900 Subject: [PATCH 02/78] Bug 1397422 - Part 2: Allow autoscrolling on XBL elements. r=jaws --- toolkit/content/browser-content.js | 61 ++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index 88124f5ca4fe..ea7827d57524 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -83,43 +83,82 @@ var ClickEventHandler = { return aNode instanceof content.XULElement; }, + getXBLNodes(parent, array) { + let anonNodes = content.document.getAnonymousNodes(parent); + let nodes = Array.from(anonNodes || parent.childNodes || []); + for (let node of nodes) { + if (node.nodeName == "children") { + return true; + } + if (this.getXBLNodes(node, array)) { + array.push(node); + return true; + } + } + return false; + }, + + * parentNodeIterator(aNode) { + while (aNode) { + yield aNode; + + let parent = aNode.parentNode; + if (parent && parent instanceof content.XULElement) { + let anonNodes = content.document.getAnonymousNodes(parent); + if (anonNodes && !Array.from(anonNodes).includes(aNode)) { + // XBL elements are skipped by parentNode property. + // Yield elements between parent and here. + let nodes = []; + this.getXBLNodes(parent, nodes); + for (let node of nodes) { + yield node; + } + } + } + + aNode = parent; + } + }, + findNearestScrollableElement(aNode) { // this is a list of overflow property values that allow scrolling const scrollingAllowed = ["scroll", "auto"]; // go upward in the DOM and find any parent element that has a overflow // area and can therefore be scrolled - for (this._scrollable = aNode; this._scrollable; - this._scrollable = this._scrollable.parentNode) { + this._scrollable = null; + for (let node of this.parentNodeIterator(aNode)) { // do not use overflow based autoscroll for and // Elements or non-html/non-xul elements such as svg or Document nodes // also make sure to skip select elements that are not multiline - if (!this.isScrollableElement(this._scrollable)) { + if (!this.isScrollableElement(node)) { continue; } - var overflowx = this._scrollable.ownerGlobal - .getComputedStyle(this._scrollable) + var overflowx = node.ownerGlobal + .getComputedStyle(node) .getPropertyValue("overflow-x"); - var overflowy = this._scrollable.ownerGlobal - .getComputedStyle(this._scrollable) + var overflowy = node.ownerGlobal + .getComputedStyle(node) .getPropertyValue("overflow-y"); // we already discarded non-multiline selects so allow vertical // scroll for multiline ones directly without checking for a // overflow property - var scrollVert = this._scrollable.scrollTopMax && - (this._scrollable instanceof content.HTMLSelectElement || + var scrollVert = node.scrollTopMax && + (node instanceof content.HTMLSelectElement || scrollingAllowed.indexOf(overflowy) >= 0); // do not allow horizontal scrolling for select elements, it leads // to visual artifacts and is not the expected behavior anyway - if (!(this._scrollable instanceof content.HTMLSelectElement) && - this._scrollable.scrollLeftMin != this._scrollable.scrollLeftMax && + if (!(node instanceof content.HTMLSelectElement) && + node.scrollLeftMin != node.scrollLeftMax && scrollingAllowed.indexOf(overflowx) >= 0) { this._scrolldir = scrollVert ? "NSEW" : "EW"; + this._scrollable = node; break; } else if (scrollVert) { this._scrolldir = "NS"; + this._scrollable = node; break; } } From 8d886455b3ad4c53305e6478b37660cb979b8627 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 6 Dec 2017 04:59:18 +0100 Subject: [PATCH 03/78] Bug 1423281: Store the userdata for freeing our memory on the longer living snapshot. r=dvander MozReview-Commit-ID: 91tVpJC7gAe --- gfx/2d/SourceSurfaceCapture.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/gfx/2d/SourceSurfaceCapture.cpp b/gfx/2d/SourceSurfaceCapture.cpp index 0b1f629e5ef3..39a683d59b0a 100644 --- a/gfx/2d/SourceSurfaceCapture.cpp +++ b/gfx/2d/SourceSurfaceCapture.cpp @@ -94,6 +94,7 @@ RefPtr SourceSurfaceCapture::ResolveImpl(BackendType aBackendType) { RefPtr dt; + uint8_t* data = nullptr; if (!mSurfaceAllocationSize) { if (aBackendType == mRefDT->GetBackendType()) { dt = mRefDT->CreateSimilarDrawTarget(mSize, mFormat); @@ -101,7 +102,7 @@ SourceSurfaceCapture::ResolveImpl(BackendType aBackendType) dt = Factory::CreateDrawTarget(aBackendType, mSize, mFormat); } } else { - uint8_t* data = static_cast(calloc(1, mSurfaceAllocationSize)); + data = static_cast(calloc(1, mSurfaceAllocationSize)); if (!data) { return nullptr; } @@ -113,9 +114,12 @@ SourceSurfaceCapture::ResolveImpl(BackendType aBackendType) free(data); return nullptr; } - dt->AddUserData(reinterpret_cast(dt.get()), data, free); } + if (!dt) { + // Make sure we haven't allocated and aren't leaking something, the code right + // anove here should have guaranteed that. + MOZ_ASSERT(!data); return nullptr; } @@ -128,11 +132,19 @@ SourceSurfaceCapture::ResolveImpl(BackendType aBackendType) DrawingCommand* cmd = iter.Get(); cmd->ExecuteOnDT(dt, nullptr); } + + RefPtr surf; if (!mShouldResolveToLuminance) { - return dt->Snapshot(); + surf = dt->Snapshot(); } else { - return dt->IntoLuminanceSource(mLuminanceType, mOpacity); + surf = dt->IntoLuminanceSource(mLuminanceType, mOpacity); } + + if (data) { + surf->AddUserData(reinterpret_cast(dt.get()), data, free); + } + + return surf.forget(); } already_AddRefed From e83999bf32069ba8de25708ade39bd370a172cb4 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 6 Dec 2017 04:59:19 +0100 Subject: [PATCH 04/78] Bug 1416862: Reverse DrawTargetSkia snapshot ownership model r=dvander MozReview-Commit-ID: 3hpeYteEPlA --- gfx/2d/DrawTargetSkia.cpp | 29 +++++++++++++++++++---------- gfx/2d/DrawTargetSkia.h | 5 ++--- gfx/2d/SourceSurfaceSkia.cpp | 16 +--------------- gfx/2d/SourceSurfaceSkia.h | 14 ++++++++++---- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 36c80e02fb56..1436179a20fb 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -278,7 +278,7 @@ GetSkImageForSurface(SourceSurface* aSurface, const Rect* aBounds = nullptr, con DrawTargetSkia::DrawTargetSkia() : mSnapshot(nullptr) - , mSnapshotLock{make_shared("DrawTargetSkia::mSnapshotLock")} + , mSnapshotLock{"DrawTargetSkia::mSnapshotLock"} #ifdef MOZ_WIDGET_COCOA , mCG(nullptr) , mColorSpace(nullptr) @@ -291,6 +291,12 @@ DrawTargetSkia::DrawTargetSkia() DrawTargetSkia::~DrawTargetSkia() { + if (mSnapshot) { + MutexAutoLock lock(mSnapshotLock); + // We're going to go away, hand our SkSurface to the SourceSurface. + mSnapshot->GiveSurface(mSurface); + } + #ifdef MOZ_WIDGET_COCOA if (mCG) { CGContextRelease(mCG); @@ -309,7 +315,7 @@ DrawTargetSkia::Snapshot() { // Without this lock, this could cause us to get out a snapshot and race with // Snapshot::~Snapshot() actually destroying itself. - MutexAutoLock lock(*mSnapshotLock); + MutexAutoLock lock(mSnapshotLock); RefPtr snapshot = mSnapshot; if (mSurface && !snapshot) { snapshot = new SourceSurfaceSkia(); @@ -322,7 +328,7 @@ DrawTargetSkia::Snapshot() } else { image = mSurface->makeImageSnapshot(); } - if (!snapshot->InitFromImage(image, mFormat, this, mSnapshotLock)) { + if (!snapshot->InitFromImage(image, mFormat, this)) { return nullptr; } mSnapshot = snapshot; @@ -2226,8 +2232,17 @@ DrawTargetSkia::CreateFilter(FilterType aType) void DrawTargetSkia::MarkChanged() { - MutexAutoLock lock(*mSnapshotLock); + // I'm not entirely certain whether this lock is needed, as multiple threads + // should never modify the DrawTarget at the same time anyway, but this seems + // like the safest. + MutexAutoLock lock(mSnapshotLock); if (mSnapshot) { + if (mSnapshot->hasOneRef()) { + // No owners outside of this DrawTarget's own reference. Just dump it. + mSnapshot = nullptr; + return; + } + mSnapshot->DrawTargetWillChange(); mSnapshot = nullptr; @@ -2238,11 +2253,5 @@ DrawTargetSkia::MarkChanged() } } -void -DrawTargetSkia::SnapshotDestroyed() -{ - mSnapshot = nullptr; -} - } // namespace gfx } // namespace mozilla diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h index 812120cb427b..b63d5fece745 100644 --- a/gfx/2d/DrawTargetSkia.h +++ b/gfx/2d/DrawTargetSkia.h @@ -160,7 +160,6 @@ public: private: friend class SourceSurfaceSkia; - void SnapshotDestroyed(); void MarkChanged(); @@ -205,8 +204,8 @@ private: IntSize mSize; sk_sp mSurface; SkCanvas* mCanvas; - SourceSurfaceSkia* mSnapshot; - std::shared_ptr mSnapshotLock; + RefPtr mSnapshot; + Mutex mSnapshotLock; #ifdef MOZ_WIDGET_COCOA friend class BorrowedCGContext; diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp index 737f1992b247..2c2e59d43669 100644 --- a/gfx/2d/SourceSurfaceSkia.cpp +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -26,13 +26,6 @@ SourceSurfaceSkia::SourceSurfaceSkia() SourceSurfaceSkia::~SourceSurfaceSkia() { - if (mSnapshotLock) { - MutexAutoLock lock{*mSnapshotLock}; - if (mDrawTarget) { - mDrawTarget->SnapshotDestroyed(); - mDrawTarget = nullptr; - } - } } IntSize @@ -109,8 +102,7 @@ SourceSurfaceSkia::InitFromData(unsigned char* aData, bool SourceSurfaceSkia::InitFromImage(const sk_sp& aImage, SurfaceFormat aFormat, - DrawTargetSkia* aOwner, - shared_ptr aSnapshotLock) + DrawTargetSkia* aOwner) { if (!aImage) { return false; @@ -143,8 +135,6 @@ SourceSurfaceSkia::InitFromImage(const sk_sp& aImage, mImage = aImage; if (aOwner) { - MOZ_ASSERT(aSnapshotLock); - mSnapshotLock = move(aSnapshotLock); mDrawTarget = aOwner; } @@ -194,10 +184,6 @@ SourceSurfaceSkia::Unmap() void SourceSurfaceSkia::DrawTargetWillChange() { - // In this case synchronisation on destroy should be guaranteed! - MOZ_ASSERT(mSnapshotLock); - mSnapshotLock->AssertCurrentThreadOwns(); - MutexAutoLock lock(mChangeMutex); if (mDrawTarget) { // Raster snapshots do not use Skia's internal copy-on-write mechanism, diff --git a/gfx/2d/SourceSurfaceSkia.h b/gfx/2d/SourceSurfaceSkia.h index f6b04ce47c3c..b33490fc73bb 100644 --- a/gfx/2d/SourceSurfaceSkia.h +++ b/gfx/2d/SourceSurfaceSkia.h @@ -31,6 +31,12 @@ public: virtual IntSize GetSize() const; virtual SurfaceFormat GetFormat() const; + // This is only ever called by the DT destructor, which can only ever happen + // from one place at a time. Therefore it doesn't need to hold the ChangeMutex + // as mSurface is never read to directly and is just there to keep the object + // alive, which itself is refcounted in a thread-safe manner. + void GiveSurface(sk_sp &aSurface) { mSurface = aSurface; mDrawTarget = nullptr; } + sk_sp GetImage(); bool InitFromData(unsigned char* aData, @@ -40,8 +46,7 @@ public: bool InitFromImage(const sk_sp& aImage, SurfaceFormat aFormat = SurfaceFormat::UNKNOWN, - DrawTargetSkia* aOwner = nullptr, - std::shared_ptr aSnapshotLock = std::shared_ptr{}); + DrawTargetSkia* aOwner = nullptr); virtual uint8_t* GetData(); @@ -60,11 +65,12 @@ private: void DrawTargetWillChange(); sk_sp mImage; + // This keeps a surface alive if needed because its DrawTarget has gone away. + sk_sp mSurface; SurfaceFormat mFormat; IntSize mSize; int32_t mStride; - RefPtr mDrawTarget; - std::shared_ptr mSnapshotLock; + DrawTargetSkia* mDrawTarget; Mutex mChangeMutex; }; From 755fb5d4ae97e8314dc227525b914ac4acdb76e5 Mon Sep 17 00:00:00 2001 From: Bevis Tseng Date: Tue, 5 Dec 2017 14:52:05 +0800 Subject: [PATCH 05/78] Bug 1418933 - Fix failure of test_contentViewer_overrideDPPX.html relying on non-comformant Promise handling. r=jryans --- .../general/test_contentViewer_overrideDPPX.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html b/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html index 1532b69a8b95..2a65ac6e5376 100644 --- a/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html +++ b/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html @@ -238,7 +238,7 @@ const gTests = { let mql = window.matchMedia(`(resolution: ${dppx}dppx)`); mql.addListener(function listener() { - ok("MediaQueryList's listener invoked.") + ok(true, "MediaQueryList's listener invoked.") mql.removeListener(listener); resolve(); }); @@ -247,7 +247,7 @@ const gTests = { let mql = frameWindow.matchMedia(`(resolution: ${dppx}dppx)`); mql.addListener(function listener() { - ok("frame's MediaQueryList's listener invoked.") + ok(true, "frame's MediaQueryList's listener invoked.") mql.removeListener(listener); resolve(); }); @@ -270,7 +270,7 @@ const gTests = { let mql = window.matchMedia(`(resolution: ${dppx}dppx)`); mql.addListener(function listener() { - ok("MediaQueryList's listener for dppx invoked."); + ok(true, "MediaQueryList's listener for dppx invoked."); mql.removeListener(listener); overridden = true; resolve(); @@ -289,14 +289,14 @@ const gTests = { }) ]; + setOverrideDPPX(dppx); + setFullZoom(zoom); + promises[0] .then(() => setOverrideDPPX(0)) .then(promises[1]) .then(() => setFullZoom(originalZoom)) .then(done, e => {throw e}); - - setOverrideDPPX(dppx); - setFullZoom(zoom); }, "test OverrideDPPX is kept on document navigation": (done) => { assertValuesAreInitial(); From 206c236dd95077c288eec6785644c69b02975363 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 6 Dec 2017 01:02:48 -0800 Subject: [PATCH 06/78] Add a Talos test for flooding the main thread with SVG rasterization. (bug 1419306 part 1, r=jmaher) --- testing/talos/talos.json | 2 +- testing/talos/talos/test.py | 23 +++ .../tests/gfx/benchmarks/rasterflood_svg.html | 156 ++++++++++++++++++ .../talos/tests/gfx/rasterflood_svg.manifest | 1 + 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html create mode 100644 testing/talos/talos/tests/gfx/rasterflood_svg.manifest diff --git a/testing/talos/talos.json b/testing/talos/talos.json index 6fb1b9fc563b..5a2c164e2d9a 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -47,7 +47,7 @@ "talos_options": ["--disable-stylo"] }, "g4-e10s": { - "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate"] + "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg"] }, "g4-stylo-disabled-e10s": { "tests": ["basic_compositor_video", "glvideo"], diff --git a/testing/talos/talos/test.py b/testing/talos/talos/test.py index d21ea4dce955..40a29bb18331 100644 --- a/testing/talos/talos/test.py +++ b/testing/talos/talos/test.py @@ -963,3 +963,26 @@ class displaylist_mutate(PageloaderTest): 'docshell.event_starvation_delay_hint': 1, 'dom.send_after_paint_to_content': False} unit = 'ms' + + +@register_test() +class rasterflood_svg(PageloaderTest): + """ + Test modifying single items in a large display list. Measure transaction speed + to the compositor. + """ + tpmanifest = '${talos}/tests/gfx/rasterflood_svg.manifest' + tpcycles = 1 + tppagecycles = 10 + tploadnocache = True + tpmozafterpaint = False + tpchrome = False + gecko_profile_interval = 2 + gecko_profile_entries = 2000000 + win_counters = w7_counters = linux_counters = mac_counters = None + filters = filter.ignore_first.prepare(1) + filter.median.prepare() + """ASAP mode""" + preferences = {'layout.frame_rate': 0, + 'docshell.event_starvation_delay_hint': 1, + 'dom.send_after_paint_to_content': False} + unit = 'ms' diff --git a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html new file mode 100644 index 000000000000..7ac47cdf764c --- /dev/null +++ b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html @@ -0,0 +1,156 @@ + + + + Paint-In-Time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/talos/talos/tests/gfx/rasterflood_svg.manifest b/testing/talos/talos/tests/gfx/rasterflood_svg.manifest new file mode 100644 index 000000000000..9398300b8619 --- /dev/null +++ b/testing/talos/talos/tests/gfx/rasterflood_svg.manifest @@ -0,0 +1 @@ +% http://localhost/tests/gfx/benchmarks/rasterflood_svg.html From 17d9d94075a6b5e34231e5f08e0529e0d13d3a15 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 6 Dec 2017 01:02:48 -0800 Subject: [PATCH 07/78] Add a Talos test for flooding the main thread with gradient rasterization. (bug 1419306 part 2, r=jmaher) --- testing/talos/talos.json | 2 +- testing/talos/talos/test.py | 23 +++ .../gfx/benchmarks/rasterflood_gradient.html | 132 ++++++++++++++++++ .../tests/gfx/rasterflood_gradient.manifest | 1 + 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html create mode 100644 testing/talos/talos/tests/gfx/rasterflood_gradient.manifest diff --git a/testing/talos/talos.json b/testing/talos/talos.json index 5a2c164e2d9a..914c66d37587 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -47,7 +47,7 @@ "talos_options": ["--disable-stylo"] }, "g4-e10s": { - "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg"] + "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg", "rasterflood_gradient"] }, "g4-stylo-disabled-e10s": { "tests": ["basic_compositor_video", "glvideo"], diff --git a/testing/talos/talos/test.py b/testing/talos/talos/test.py index 40a29bb18331..9bb34922893e 100644 --- a/testing/talos/talos/test.py +++ b/testing/talos/talos/test.py @@ -986,3 +986,26 @@ class rasterflood_svg(PageloaderTest): 'docshell.event_starvation_delay_hint': 1, 'dom.send_after_paint_to_content': False} unit = 'ms' + + +@register_test() +class rasterflood_gradient(PageloaderTest): + """ + Test expensive rasterization while the main thread is busy. + """ + tpmanifest = '${talos}/tests/gfx/rasterflood_gradient.manifest' + tpcycles = 1 + tppagecycles = 10 + tploadnocache = True + tpmozafterpaint = False + tpchrome = False + gecko_profile_interval = 2 + gecko_profile_entries = 2000000 + win_counters = w7_counters = linux_counters = mac_counters = None + filters = filter.ignore_first.prepare(1) + filter.median.prepare() + """ASAP mode""" + preferences = {'layout.frame_rate': 0, + 'docshell.event_starvation_delay_hint': 1, + 'dom.send_after_paint_to_content': False} + lower_is_better = False + unit = 'score' diff --git a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html new file mode 100644 index 000000000000..778c8b475cfc --- /dev/null +++ b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html @@ -0,0 +1,132 @@ + + + + + Paint-In-Time 2 + + + +
+
+ + + diff --git a/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest b/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest new file mode 100644 index 000000000000..2351c5653f76 --- /dev/null +++ b/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest @@ -0,0 +1 @@ +% http://localhost/tests/gfx/benchmarks/rasterflood_gradient.html From daa1d89a83d1d6282caeec967b6eb87c7c3f6e5c Mon Sep 17 00:00:00 2001 From: Tiberius Oros Date: Wed, 6 Dec 2017 12:06:55 +0200 Subject: [PATCH 08/78] Backed out 2 changesets (bug 1419306)for eslint failures on /builds/worker/checkouts/gecko/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html:32:1 r=backout Backed out changeset 4948ce7f26a8 (bug 1419306) Backed out changeset 350a5151f44f (bug 1419306) --- testing/talos/talos.json | 2 +- testing/talos/talos/test.py | 46 ------ .../gfx/benchmarks/rasterflood_gradient.html | 132 --------------- .../tests/gfx/benchmarks/rasterflood_svg.html | 156 ------------------ .../tests/gfx/rasterflood_gradient.manifest | 1 - .../talos/tests/gfx/rasterflood_svg.manifest | 1 - 6 files changed, 1 insertion(+), 337 deletions(-) delete mode 100644 testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html delete mode 100644 testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html delete mode 100644 testing/talos/talos/tests/gfx/rasterflood_gradient.manifest delete mode 100644 testing/talos/talos/tests/gfx/rasterflood_svg.manifest diff --git a/testing/talos/talos.json b/testing/talos/talos.json index 914c66d37587..6fb1b9fc563b 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -47,7 +47,7 @@ "talos_options": ["--disable-stylo"] }, "g4-e10s": { - "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg", "rasterflood_gradient"] + "tests": ["basic_compositor_video", "glvideo", "displaylist_mutate"] }, "g4-stylo-disabled-e10s": { "tests": ["basic_compositor_video", "glvideo"], diff --git a/testing/talos/talos/test.py b/testing/talos/talos/test.py index 9bb34922893e..d21ea4dce955 100644 --- a/testing/talos/talos/test.py +++ b/testing/talos/talos/test.py @@ -963,49 +963,3 @@ class displaylist_mutate(PageloaderTest): 'docshell.event_starvation_delay_hint': 1, 'dom.send_after_paint_to_content': False} unit = 'ms' - - -@register_test() -class rasterflood_svg(PageloaderTest): - """ - Test modifying single items in a large display list. Measure transaction speed - to the compositor. - """ - tpmanifest = '${talos}/tests/gfx/rasterflood_svg.manifest' - tpcycles = 1 - tppagecycles = 10 - tploadnocache = True - tpmozafterpaint = False - tpchrome = False - gecko_profile_interval = 2 - gecko_profile_entries = 2000000 - win_counters = w7_counters = linux_counters = mac_counters = None - filters = filter.ignore_first.prepare(1) + filter.median.prepare() - """ASAP mode""" - preferences = {'layout.frame_rate': 0, - 'docshell.event_starvation_delay_hint': 1, - 'dom.send_after_paint_to_content': False} - unit = 'ms' - - -@register_test() -class rasterflood_gradient(PageloaderTest): - """ - Test expensive rasterization while the main thread is busy. - """ - tpmanifest = '${talos}/tests/gfx/rasterflood_gradient.manifest' - tpcycles = 1 - tppagecycles = 10 - tploadnocache = True - tpmozafterpaint = False - tpchrome = False - gecko_profile_interval = 2 - gecko_profile_entries = 2000000 - win_counters = w7_counters = linux_counters = mac_counters = None - filters = filter.ignore_first.prepare(1) + filter.median.prepare() - """ASAP mode""" - preferences = {'layout.frame_rate': 0, - 'docshell.event_starvation_delay_hint': 1, - 'dom.send_after_paint_to_content': False} - lower_is_better = False - unit = 'score' diff --git a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html deleted file mode 100644 index 778c8b475cfc..000000000000 --- a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_gradient.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - Paint-In-Time 2 - - - -
-
- - - diff --git a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html b/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html deleted file mode 100644 index 7ac47cdf764c..000000000000 --- a/testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - Paint-In-Time - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest b/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest deleted file mode 100644 index 2351c5653f76..000000000000 --- a/testing/talos/talos/tests/gfx/rasterflood_gradient.manifest +++ /dev/null @@ -1 +0,0 @@ -% http://localhost/tests/gfx/benchmarks/rasterflood_gradient.html diff --git a/testing/talos/talos/tests/gfx/rasterflood_svg.manifest b/testing/talos/talos/tests/gfx/rasterflood_svg.manifest deleted file mode 100644 index 9398300b8619..000000000000 --- a/testing/talos/talos/tests/gfx/rasterflood_svg.manifest +++ /dev/null @@ -1 +0,0 @@ -% http://localhost/tests/gfx/benchmarks/rasterflood_svg.html From b02fcd6df02136367f20016d3161603a1b0126bb Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Tue, 7 Nov 2017 15:58:31 +0000 Subject: [PATCH 09/78] Bug 1180145, part 1 - Add a 'reportResourceTiming' member to the nsITimedChannel interface. r=mayhemer MozReview-Commit-ID: 3poTmHWKs4K --- netwerk/base/nsITimedChannel.idl | 3 +++ netwerk/protocol/http/HttpBaseChannel.cpp | 13 +++++++++++++ netwerk/protocol/http/HttpBaseChannel.h | 1 + netwerk/protocol/http/NullHttpChannel.cpp | 10 ++++++++++ 4 files changed, 27 insertions(+) diff --git a/netwerk/base/nsITimedChannel.idl b/netwerk/base/nsITimedChannel.idl index ff0e221d67d2..343bf4f54ac6 100644 --- a/netwerk/base/nsITimedChannel.idl +++ b/netwerk/base/nsITimedChannel.idl @@ -97,4 +97,7 @@ interface nsITimedChannel : nsISupports { readonly attribute PRTime cacheReadEndTime; readonly attribute PRTime redirectStartTime; readonly attribute PRTime redirectEndTime; + + // If this attribute is false, this resource MUST NOT be reported in resource timing. + [noscript] attribute boolean reportResourceTiming; }; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 2b35c0214542..3580ad97a174 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -175,6 +175,7 @@ HttpBaseChannel::HttpBaseChannel() , mChannelIsForDownload(false) , mTracingEnabled(true) , mTimingEnabled(false) + , mReportTiming(true) , mAllowSpdy(true) , mAllowAltSvc(true) , mBeConservative(false) @@ -4158,6 +4159,18 @@ HttpBaseChannel::GetPerformance() return docPerformance; } +NS_IMETHODIMP +HttpBaseChannel::SetReportResourceTiming(bool enabled) { + mReportTiming = enabled; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::GetReportResourceTiming(bool* _retval) { + *_retval = mReportTiming; + return NS_OK; +} + nsIURI* HttpBaseChannel::GetReferringPage() { diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 9e652a745c92..a3c6da5ac43a 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -560,6 +560,7 @@ protected: uint32_t mTracingEnabled : 1; // True if timing collection is enabled uint32_t mTimingEnabled : 1; + uint32_t mReportTiming : 1; uint32_t mAllowSpdy : 1; uint32_t mAllowAltSvc : 1; uint32_t mBeConservative : 1; diff --git a/netwerk/protocol/http/NullHttpChannel.cpp b/netwerk/protocol/http/NullHttpChannel.cpp index 4ec242b03ac6..fbce6f45e049 100644 --- a/netwerk/protocol/http/NullHttpChannel.cpp +++ b/netwerk/protocol/http/NullHttpChannel.cpp @@ -879,6 +879,16 @@ NullHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage) return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +NullHttpChannel::SetReportResourceTiming(bool enabled) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +NullHttpChannel::GetReportResourceTiming(bool* _retval) { + return NS_ERROR_NOT_IMPLEMENTED; +} + #define IMPL_TIMING_ATTR(name) \ NS_IMETHODIMP \ NullHttpChannel::Get##name##Time(PRTime* _retval) { \ From abe1a2a6c45ac3fbcdab94ce3950b32180251e12 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Wed, 8 Nov 2017 16:59:31 +0000 Subject: [PATCH 10/78] Bug 1180145, part 2 - Taint style sheet sub-resources of no-cors resources against resource timing reporting. r=heycam MozReview-Commit-ID: 9B2qtp3FvjU --- layout/style/Loader.cpp | 47 ++++++++++++++++++++++++++++++++++++ layout/style/SheetLoadData.h | 12 +++++++++ 2 files changed, 59 insertions(+) diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 894d070625fb..2791910a1e9d 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -167,6 +167,8 @@ SheetLoadData::SheetLoadData(Loader* aLoader, , mWasAlternate(aIsAlternate) , mUseSystemPrincipal(false) , mSheetAlreadyComplete(false) + , mIsCrossOriginNoCORS(false) + , mBlockResourceTiming(false) , mOwningElement(aOwningElement) , mObserver(aObserver) , mLoaderPrincipal(aLoaderPrincipal) @@ -199,6 +201,8 @@ SheetLoadData::SheetLoadData(Loader* aLoader, , mWasAlternate(false) , mUseSystemPrincipal(false) , mSheetAlreadyComplete(false) + , mIsCrossOriginNoCORS(false) + , mBlockResourceTiming(false) , mOwningElement(nullptr) , mObserver(aObserver) , mLoaderPrincipal(aLoaderPrincipal) @@ -241,6 +245,8 @@ SheetLoadData::SheetLoadData(Loader* aLoader, , mWasAlternate(false) , mUseSystemPrincipal(aUseSystemPrincipal) , mSheetAlreadyComplete(false) + , mIsCrossOriginNoCORS(false) + , mBlockResourceTiming(false) , mOwningElement(nullptr) , mObserver(aObserver) , mLoaderPrincipal(aLoaderPrincipal) @@ -717,6 +723,14 @@ SheetLoadData::VerifySheetReadyToParse(nsresult aStatus, mSheet->SetPrincipal(principal); + if (mLoaderPrincipal && mSheet->GetCORSMode() == CORS_NONE) { + bool subsumed; + result = mLoaderPrincipal->Subsumes(principal, &subsumed); + if (NS_FAILED(result) || !subsumed) { + mIsCrossOriginNoCORS = true; + } + } + // If it's an HTTP channel, we want to make sure this is not an // error document we got. nsCOMPtr httpChannel(do_QueryInterface(aChannel)); @@ -1556,6 +1570,39 @@ Loader::LoadSheet(SheetLoadData* aLoadData, if (timedChannel) { if (aLoadData->mParentData) { timedChannel->SetInitiatorType(NS_LITERAL_STRING("css")); + + // This is a child sheet load. + // + // The resource timing of the sub-resources that a document loads + // should normally be reported to the document. One exception is any + // sub-resources of any cross-origin resources that are loaded. We + // don't mind reporting timing data for a direct child cross-origin + // resource since the resource that linked to it (and hence potentially + // anything in that parent origin) is aware that the cross-origin + // resources is to be loaded. However, we do not want to report + // timings for any sub-resources that a cross-origin resource may load + // since that obviously leaks information about what the cross-origin + // resource loads, which is bad. + // + // In addition to checking whether we're an immediate child resource of + // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set + // to true on our parent), we also check our parent to see whether it + // itself is a sub-resource of a cross-origin resource by checking + // mBlockResourceTiming. If that is set then we too are such a + // sub-resource and so we set the flag on ourself too to propagate it + // on down. + // + if (aLoadData->mParentData->mIsCrossOriginNoCORS || + aLoadData->mParentData->mBlockResourceTiming) { + // Set a flag so any other stylesheet triggered by this one will + // not be reported + aLoadData->mBlockResourceTiming = true; + + // Mark the channel so PerformanceMainThread::AddEntry will not + // report the resource. + timedChannel->SetReportResourceTiming(false); + } + } else { timedChannel->SetInitiatorType(NS_LITERAL_STRING("link")); } diff --git a/layout/style/SheetLoadData.h b/layout/style/SheetLoadData.h index 019de72d7107..1fb301b38e77 100644 --- a/layout/style/SheetLoadData.h +++ b/layout/style/SheetLoadData.h @@ -149,6 +149,18 @@ public: // async observer notification for an already-complete sheet. bool mSheetAlreadyComplete : 1; + // If true, the sheet is being loaded cross-origin without CORS permissions. + // This is completely normal and CORS isn't needed for such loads. This + // flag is simply useful in determining whether to set mBlockResourceTiming + // for a child sheet. + bool mIsCrossOriginNoCORS : 1; + + // If this flag is true, LoadSheet will call SetReportResourceTiming(false) + // on the timedChannel. This is to mark resources that are loaded by a + // cross-origin stylesheet with a no-cors policy. + // https://www.w3.org/TR/resource-timing/#processing-model + bool mBlockResourceTiming : 1; + // This is the element that imported the sheet. Needed to get the // charset set on it and to fire load/error events. nsCOMPtr mOwningElement; From 25f221f47d01eea20f86adc023c79e906ea3655e Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Wed, 8 Nov 2017 18:13:46 +0000 Subject: [PATCH 11/78] Bug 1180145, part 3 - Block resource timing reporting for channels that are tainted. r=jwatt MozReview-Commit-ID: FdlbJ2YYMaG --- dom/performance/PerformanceMainThread.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index 636a8af39787..1d6b64d7f21d 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -141,6 +141,17 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel, originalURI->GetSpec(name); NS_ConvertUTF8toUTF16 entryName(name); + bool reportTiming = true; + timedChannel->GetReportResourceTiming(&reportTiming); + + if (!reportTiming) { +#ifdef DEBUG_jwatt + NS_WARNING( + nsPrintfCString("Not reporting CORS resource: %s", name.get()).get()); +#endif + return; + } + // The nsITimedChannel argument will be used to gather all the timings. // The nsIHttpChannel argument will be used to check if any cross-origin // redirects occurred. From 22a6ced9792d86d60df9862051e3ef2590e00c4b Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Thu, 9 Nov 2017 13:12:45 +0000 Subject: [PATCH 12/78] Bug 1180145, part 4 - Tests for blocking of resource timing for sub-resources of no-cors resources. r=jwatt MozReview-Commit-ID: GQRhvsME5gv --- dom/tests/mochitest/general/cssA.css | 0 dom/tests/mochitest/general/cssB.css | 2 + dom/tests/mochitest/general/cssC.css | 0 dom/tests/mochitest/general/emptyCssFile2.css | 1 + .../general/file_resource_timing_nocors.html | 191 ++++++++++++++++++ dom/tests/mochitest/general/generateCss.sjs | 42 ++++ .../general/importsSameAndCrossOrigin.css | 3 + dom/tests/mochitest/general/mochitest.ini | 5 + .../general/resource_timing_main_test.html | 21 +- .../general/resource_timing_nocors.html | 88 ++++++++ .../general/test_resource_timing_nocors.html | 37 ++++ 11 files changed, 379 insertions(+), 11 deletions(-) create mode 100644 dom/tests/mochitest/general/cssA.css create mode 100644 dom/tests/mochitest/general/cssB.css create mode 100644 dom/tests/mochitest/general/cssC.css create mode 100644 dom/tests/mochitest/general/emptyCssFile2.css create mode 100644 dom/tests/mochitest/general/file_resource_timing_nocors.html create mode 100644 dom/tests/mochitest/general/generateCss.sjs create mode 100644 dom/tests/mochitest/general/importsSameAndCrossOrigin.css create mode 100644 dom/tests/mochitest/general/resource_timing_nocors.html create mode 100644 dom/tests/mochitest/general/test_resource_timing_nocors.html diff --git a/dom/tests/mochitest/general/cssA.css b/dom/tests/mochitest/general/cssA.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dom/tests/mochitest/general/cssB.css b/dom/tests/mochitest/general/cssB.css new file mode 100644 index 000000000000..8d44ebd00f26 --- /dev/null +++ b/dom/tests/mochitest/general/cssB.css @@ -0,0 +1,2 @@ +@import 'cssC.css'; +@import url('http://example.org/tests/dom/tests/mochitest/general/cssC.css'); diff --git a/dom/tests/mochitest/general/cssC.css b/dom/tests/mochitest/general/cssC.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dom/tests/mochitest/general/emptyCssFile2.css b/dom/tests/mochitest/general/emptyCssFile2.css new file mode 100644 index 000000000000..64f97ae68746 --- /dev/null +++ b/dom/tests/mochitest/general/emptyCssFile2.css @@ -0,0 +1 @@ +@import url('http://example.org/tests/dom/tests/mochitest/general/cross.css'); \ No newline at end of file diff --git a/dom/tests/mochitest/general/file_resource_timing_nocors.html b/dom/tests/mochitest/general/file_resource_timing_nocors.html new file mode 100644 index 000000000000..491df5d325fb --- /dev/null +++ b/dom/tests/mochitest/general/file_resource_timing_nocors.html @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bug #1180145 - Resource Timing NO-CORS CSS + +

+
+
+ +
BLUE
+
RED
+
Font
+
CURSOR
+
+
+ + diff --git a/dom/tests/mochitest/general/generateCss.sjs b/dom/tests/mochitest/general/generateCss.sjs new file mode 100644 index 000000000000..d72ae1c07e32 --- /dev/null +++ b/dom/tests/mochitest/general/generateCss.sjs @@ -0,0 +1,42 @@ +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/css", false); + response.write(gResponses[request.queryString]); +} + +let gResponses = { + // 1 + "A": "@import 'generateCss.sjs?B';", + "B": "", + + // 2 + "C": "@import 'generateCss.sjs?D';", + "D": "", + + // 3 + "E": "@import 'generateCss.sjs?F';", + "F": "", + + // 4 + "G": "@import 'generateCss.sjs?H'; @import 'http://example.org/tests/dom/tests/mochitest/general/generateCss.sjs?K';", + "H": "@import 'http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?I';", + "I": "@import 'generateCss.sjs?J", + "J": "", + "K": "@import 'generateCss.sjs?L';", + "L": "@import 'generateCss.sjs?M", + "M": "", + + // 5 + "N": ".c1 { background-image: -moz-image-rect(url('/image/test/mochitest/blue.png'), 0, 0, 200, 200);}", + + // 6 + "O": ".c2 { background-image: url('/image/test/mochitest/red.png');}", + + // 7 + "P": "@font-face { font-family: Ahem; src: url('/tests/dom/base/test/Ahem.ttf'); } .c3 { font-family: Ahem; font-size: 20px; }", + + // 8 + "Q": ".c4 { cursor: url('/image/test/mochitest/over.png') 2 2, auto; } ", + + // 9 + "R": "#image { mask: url('/tests/dom/base/test/file_use_counter_svg_fill_pattern_data.svg'); }", +}; diff --git a/dom/tests/mochitest/general/importsSameAndCrossOrigin.css b/dom/tests/mochitest/general/importsSameAndCrossOrigin.css new file mode 100644 index 000000000000..69e239c063b0 --- /dev/null +++ b/dom/tests/mochitest/general/importsSameAndCrossOrigin.css @@ -0,0 +1,3 @@ +@import 'emptyCssFile2.css'; +@import url('http://example.org/tests/dom/tests/mochitest/general/emptyCssFile2.css'); + diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 546142795d2c..9ccfa024333d 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -9,6 +9,8 @@ support-files = file_interfaces.xml file_moving_nodeList.html file_moving_xhr.html + file_resource_timing_nocors.html + generateCss.sjs historyframes.html start_historyframe.html url1_historyframe.html @@ -56,7 +58,9 @@ support-files = !/image/test/mochitest/damon.jpg !/image/test/mochitest/over.png !/image/test/mochitest/red.png + !/dom/base/test/Ahem.ttf !/dom/base/test/file_empty.html + !/dom/base/test/file_use_counter_svg_fill_pattern_data.svg file_focusrings.html [test_497898.html] @@ -135,5 +139,6 @@ skip-if = toolkit == 'android' # bug 1230232 - Mouse doesn't select in the same [test_WebKitCSSMatrix.html] [test_windowedhistoryframes.html] [test_windowProperties.html] +[test_resource_timing_nocors.html] [test_resizeby.html] skip-if = toolkit == 'android' # Window sizes cannot be controled on android. diff --git a/dom/tests/mochitest/general/resource_timing_main_test.html b/dom/tests/mochitest/general/resource_timing_main_test.html index 864b02984e6d..d03e7d891c47 100644 --- a/dom/tests/mochitest/general/resource_timing_main_test.html +++ b/dom/tests/mochitest/general/resource_timing_main_test.html @@ -66,6 +66,13 @@ function isnot(received, notExpected, message) { var bufferFullCounter = 0; const expectedBufferFullEvents = 1; +var allResources = { + "http://mochi.test:8888/tests/SimpleTest/test.css": "link", + "http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img", + "http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object", + "http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed", + "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"}; + window.onload = function() { ok(!!window.performance, "Performance object should exist"); ok(!!window.performance.getEntries, "Performance.getEntries() should exist"); @@ -76,8 +83,7 @@ window.onload = function() { bufferFullCounter += 1; } - // Here, we should have 5 entries (1 css, 3 png, 1 html) since the image was loaded. - is(window.performance.getEntriesByType("resource").length, 5, "Performance.getEntriesByType() returned wrong number of entries."); + is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "Performance.getEntriesByType() returned wrong number of entries."); checkStringify(window.performance.getEntriesByType("resource")[0]); @@ -106,11 +112,11 @@ window.onload = function() { checkEntries(window.performance.getEntriesByType("resource")); window.performance.setResourceTimingBufferSize(1); - is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " + + is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "No entries should be " + "removed when setResourceTimingBufferSize is called."); window.performance.setResourceTimingBufferSize(4); - is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " + + is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "No entries should be " + "removed when setResourceTimingBufferSize is called."); window.performance.setResourceTimingBufferSize(1); @@ -183,13 +189,6 @@ function checkEntries(anEntryList) { // Check that the entries have the expected initiator type. We can't check // the order (the order might depend on the platform the tests are running). - allResources = { - "http://mochi.test:8888/tests/SimpleTest/test.css" : "link", - "http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img", - "http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object", - "http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed", - "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"}; - for (resourceName in allResources) { // Check that we have a resource with the specific name. namedEntries = window.performance.getEntriesByName(resourceName); diff --git a/dom/tests/mochitest/general/resource_timing_nocors.html b/dom/tests/mochitest/general/resource_timing_nocors.html new file mode 100644 index 000000000000..39b34950fcea --- /dev/null +++ b/dom/tests/mochitest/general/resource_timing_nocors.html @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + Bug #1180145 - Resource Timing NO-CORS CSS + +

+
+
+ + diff --git a/dom/tests/mochitest/general/test_resource_timing_nocors.html b/dom/tests/mochitest/general/test_resource_timing_nocors.html new file mode 100644 index 000000000000..b825b25d3da2 --- /dev/null +++ b/dom/tests/mochitest/general/test_resource_timing_nocors.html @@ -0,0 +1,37 @@ + + + + + + + + + + + +
+
+
+ + + From c2d6bb704edeccbd27ab4461734600920e9716b9 Mon Sep 17 00:00:00 2001 From: Ekanan Ketunuti Date: Tue, 5 Dec 2017 08:41:22 +0700 Subject: [PATCH 13/78] Bug 1422346 - Add words to the en-US dictionary. r=jet --- .../spellcheck/locales/en-US/hunspell/en-US.dic | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic index 8e865b83a205..ee2bfc607af4 100644 --- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic +++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic @@ -1,4 +1,4 @@ -52456 +52460 0/nm 0th/pt 1/n1 @@ -15888,6 +15888,7 @@ ban/SM banal/Y banality/SM banana/SM +banc/S band's band/ESGD bandage/DSMG @@ -20903,7 +20904,7 @@ copycatted copycatting copyist/MS copyleft -copyright/GSMD +copyright/GSMDB copywriter/MS coquetry/SM coquette/DSMG @@ -21703,7 +21704,7 @@ curability/M curacao curacy/SM curare/M -curate/DSMGV +curate/DSMGVN curative/MS curator/KMS curatorial @@ -22832,6 +22833,7 @@ differential/SM differentiate/DSGN differentiated/U differentiation/M +differentiator/S difficult/Y difficulty/SM diffidence/M @@ -24168,7 +24170,7 @@ effuse/DSGNVX effusion/M effusive/YP effusiveness/M -egad +egad/S egalitarian/SM egalitarianism/M egg/GSMD @@ -32383,6 +32385,7 @@ krone/RM kronor kronur krypton/M +kryptonite króna/M krónur kt @@ -51417,6 +51420,7 @@ webisode/MS weblog/MS webmaster/SM webmistress/MS +webpage/SM website/SM wed/AS wedded/A From 15353262e8f3d7182adf25871e1ff4be36406d04 Mon Sep 17 00:00:00 2001 From: Johann Hofmann Date: Tue, 13 Jun 2017 12:10:39 +0200 Subject: [PATCH 14/78] Bug 1366357 - Use origin instead of documentURI for WebRTC permission requests. r=florian MozReview-Commit-ID: IkccA65Ma3a --HG-- extra : rebase_source : 9c1f2c895949b1dae617b0c2a1039a5494cd8b2a --- .../popupNotifications/browser_displayURI.js | 77 +++++++++++++++---- browser/modules/ContentWebRTC.jsm | 1 + browser/modules/webrtcUI.jsm | 8 +- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/browser/base/content/test/popupNotifications/browser_displayURI.js b/browser/base/content/test/popupNotifications/browser_displayURI.js index 10a8199dab54..df2bfb54fe25 100644 --- a/browser/base/content/test/popupNotifications/browser_displayURI.js +++ b/browser/base/content/test/popupNotifications/browser_displayURI.js @@ -1,19 +1,11 @@ /* - * Make sure that the origin is shown for ContentPermissionPrompt - * consumers e.g. geolocation. -*/ + * Make sure that the correct origin is shown for permission prompts. + */ -add_task(async function test_displayURI() { - await BrowserTestUtils.withNewTab({ - gBrowser, - url: "https://test1.example.com/", - }, async function(browser) { +async function check(contentTask) { + await BrowserTestUtils.withNewTab("https://test1.example.com/", async function(browser) { let popupShownPromise = waitForNotificationPanel(); - await ContentTask.spawn(browser, null, async function() { - content.navigator.geolocation.getCurrentPosition(function(pos) { - // Do nothing - }); - }); + await ContentTask.spawn(browser, null, contentTask); let panel = await popupShownPromise; let notification = panel.children[0]; let body = document.getAnonymousElementByAttribute(notification, @@ -21,4 +13,63 @@ add_task(async function test_displayURI() { "popup-notification-body"); ok(body.innerHTML.includes("example.com"), "Check that at least the eTLD+1 is present in the markup"); }); + + let channel = NetUtil.newChannel({ + uri: getRootDirectory(gTestPath), + loadUsingSystemPrincipal: true, + }); + channel = channel.QueryInterface(Ci.nsIFileChannel); + + return BrowserTestUtils.withNewTab(channel.file.path, async function(browser) { + let popupShownPromise = waitForNotificationPanel(); + await ContentTask.spawn(browser, null, contentTask); + let panel = await popupShownPromise; + let notification = panel.children[0]; + let body = document.getAnonymousElementByAttribute(notification, + "class", + "popup-notification-body"); + if (notification.id == "geolocation-notification") { + ok(body.innerHTML.includes("local file"), `file:// URIs should be displayed as local file.`); + } else { + ok(body.innerHTML.includes("Unknown origin"), "file:// URIs should be displayed as unknown origin."); + } + }); +} + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({set: [ + ["media.navigator.permission.fake", true], + ["media.navigator.permission.force", true], + ]}); }); + +add_task(async function test_displayURI_geo() { + await check(async function() { + content.navigator.geolocation.getCurrentPosition(() => {}); + }); +}); + +add_task(async function test_displayURI_camera() { + await check(async function() { + content.navigator.mediaDevices.getUserMedia({video: true, fake: true}); + }); +}); + +add_task(async function test_displayURI_geo_blob() { + await check(async function() { + let text = ""; + let blob = new Blob([text], {type: "text/html"}); + let url = content.URL.createObjectURL(blob); + content.location.href = url; + }); +}); + +add_task(async function test_displayURI_camera_blob() { + await check(async function() { + let text = ""; + let blob = new Blob([text], {type: "text/html"}); + let url = content.URL.createObjectURL(blob); + content.location.href = url; + }); +}); + diff --git a/browser/modules/ContentWebRTC.jsm b/browser/modules/ContentWebRTC.jsm index f717f6abbc0a..1cbe0832cba1 100644 --- a/browser/modules/ContentWebRTC.jsm +++ b/browser/modules/ContentWebRTC.jsm @@ -216,6 +216,7 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec let request = { callID: aCallID, windowID: aWindowID, + origin: aContentWindow.origin, documentURI: aContentWindow.document.documentURI, secure: aSecure, requestTypes, diff --git a/browser/modules/webrtcUI.jsm b/browser/modules/webrtcUI.jsm index 6bc97eb7305c..3efdf946352b 100644 --- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -368,7 +368,13 @@ function prompt(aBrowser, aRequest) { aBrowser.dispatchEvent(new aBrowser.ownerGlobal .CustomEvent("PermissionStateChange")); - let uri = Services.io.newURI(aRequest.documentURI); + let uri; + try { + // This fails for principals that serialize to "null", e.g. file URIs. + uri = Services.io.newURI(aRequest.origin); + } catch (e) { + uri = Services.io.newURI(aRequest.documentURI); + } let host = getHost(uri); let chromeDoc = aBrowser.ownerDocument; let stringBundle = chromeDoc.defaultView.gNavigatorBundle; From feb949d1a87bf874a3abc852cefb697427072b14 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 6 Dec 2017 12:36:50 +0100 Subject: [PATCH 15/78] Bug 752790 - Allow content to overflow into the padding area in the block-axis (but not in the inline-axis). r=dholbert MozReview-Commit-ID: FnPW6mKxWE6 --- layout/style/res/forms.css | 2 +- .../meta/css/css-overflow/input-scrollable-region-001.html.ini | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 testing/web-platform/meta/css/css-overflow/input-scrollable-region-001.html.ini diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index c4e7ff49be31..f9e64dd6d296 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -107,7 +107,7 @@ input { text-indent: 0; -moz-user-select: text; text-shadow: none; - overflow-clip-box: content-box; + overflow-clip-box: padding-box content-box; } input > .anonymous-div, diff --git a/testing/web-platform/meta/css/css-overflow/input-scrollable-region-001.html.ini b/testing/web-platform/meta/css/css-overflow/input-scrollable-region-001.html.ini deleted file mode 100644 index 238870ff466b..000000000000 --- a/testing/web-platform/meta/css/css-overflow/input-scrollable-region-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[input-scrollable-region-001.html] - expected: FAIL From 1f138f81040284cfff0a8c3252a70f50afc0c11a Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 6 Dec 2017 12:39:57 +0100 Subject: [PATCH 16/78] Bug 1419475 - Disable mochitest-webgl tests as they are retried too often, until the infrastructure problem is fixed. r=jmaher --HG-- extra : rebase_source : 7c074403a32d1c8d38af97708fc3602e2fdce516 --- taskcluster/ci/test/mochitest.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/taskcluster/ci/test/mochitest.yml b/taskcluster/ci/test/mochitest.yml index f32e423ddac5..aa65cc4265a4 100644 --- a/taskcluster/ci/test/mochitest.yml +++ b/taskcluster/ci/test/mochitest.yml @@ -342,6 +342,10 @@ mochitest-webgl: description: "Mochitest webgl run" suite: mochitest/mochitest-gl treeherder-symbol: tc-M(gl) + run-on-projects: + by-test-platform: + windows10-64-ccov/.*: [] # Do not run on Windows ccov, see bug 1419475. + default: built-projects virtualization: virtual-with-gpu chunks: by-test-platform: From e8c489d641cf76ff6d1acfaaefe5159788028c0f Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Mon, 4 Dec 2017 10:30:01 +0100 Subject: [PATCH 17/78] Bug 1422698 - Make Windows coverage build and tests tier 2. r=jmaher --HG-- extra : rebase_source : ae4fca676818d913f73a56f9bbe64fdacfc8ecd3 --- taskcluster/ci/build/windows.yml | 2 +- taskcluster/taskgraph/transforms/tests.py | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/taskcluster/ci/build/windows.yml b/taskcluster/ci/build/windows.yml index 23555bf47076..d52dbd490d5c 100755 --- a/taskcluster/ci/build/windows.yml +++ b/taskcluster/ci/build/windows.yml @@ -478,7 +478,7 @@ win64-ccov/debug: treeherder: platform: windows2012-64/ccov symbol: tc(B) - tier: 3 + tier: 2 worker-type: aws-provisioner-v1/gecko-{level}-b-win2012 worker: max-run-time: 7200 diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py index d7bafad6ca25..ab412bbb5bca 100644 --- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -573,11 +573,6 @@ def set_tier(config, tests): else: test['tier'] = 2 - # Temporarily set windows10-64-ccov/debug tests as tier 3, until we get the tests - # consistently green. - if test['test-platform'] == 'windows10-64-ccov/debug': - test['tier'] = 3 - yield test From d2271cd3a89131588bd927424f7c8bc5288b268f Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Wed, 6 Dec 2017 12:57:28 +0100 Subject: [PATCH 18/78] Bug 1417431 - secureConnectionStart should be 0 for pages with HTTP scheme. r=bkelly --- dom/performance/PerformanceTiming.cpp | 27 ++++++++++++++++--- dom/performance/PerformanceTiming.h | 2 ++ .../tests/test_performance_user_timing.js | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp index 08a0ae314fbf..384a420e6166 100644 --- a/dom/performance/PerformanceTiming.cpp +++ b/dom/performance/PerformanceTiming.cpp @@ -52,6 +52,23 @@ PerformanceTiming::PerformanceTiming(Performance* aPerformance, mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck; } + mSecureConnection = false; + nsCOMPtr uri; + if (aHttpChannel) { + aHttpChannel->GetURI(getter_AddRefs(uri)); + } else { + nsCOMPtr httpChannel = do_QueryInterface(aChannel); + if (httpChannel) { + httpChannel->GetURI(getter_AddRefs(uri)); + } + } + + if (uri) { + nsresult rv = uri->SchemeIs("https", &mSecureConnection); + if (NS_FAILED(rv)) { + mSecureConnection = false; + } + } InitializeTimingInfo(aChannel); // Non-null aHttpChannel implies that this PerformanceTiming object is being @@ -118,7 +135,8 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) mConnectStart = *clampTime; } - if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < *clampTime) { + if (mSecureConnection && !mSecureConnectionStart.IsNull() && + mSecureConnectionStart < *clampTime) { mSecureConnectionStart = *clampTime; } @@ -369,8 +387,11 @@ PerformanceTiming::SecureConnectionStartHighRes() nsContentUtils::ShouldResistFingerprinting()) { return mZeroTime; } - return mSecureConnectionStart.IsNull() ? mZeroTime - : TimeStampToDOMHighRes(mSecureConnectionStart); + return !mSecureConnection + ? 0 // We use 0 here, because mZeroTime is sometimes set to the navigation + // start time. + : (mSecureConnectionStart.IsNull() ? mZeroTime + : TimeStampToDOMHighRes(mSecureConnectionStart)); } DOMTimeMilliSec diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h index 11d836c59d35..e904e20e2cd9 100644 --- a/dom/performance/PerformanceTiming.h +++ b/dom/performance/PerformanceTiming.h @@ -305,6 +305,8 @@ private: // redirectEnd attributes. It is false if there were no redirects, or if // any of the responses didn't pass the timing-allow-check bool mReportCrossOriginRedirect; + + bool mSecureConnection; }; } // namespace dom diff --git a/dom/performance/tests/test_performance_user_timing.js b/dom/performance/tests/test_performance_user_timing.js index 34a9e811f17c..d5419e6ee5e9 100644 --- a/dom/performance/tests/test_performance_user_timing.js +++ b/dom/performance/tests/test_performance_user_timing.js @@ -266,7 +266,7 @@ var steps = [ performance.measure("test", n); ok(true, "Measure created from reserved name as starting time: " + n); } catch (e) { - ok(["redirectStart", "redirectEnd", "unloadEventStart", "unloadEventEnd", "loadEventEnd"].indexOf(n) >= 0, + ok(["redirectStart", "redirectEnd", "unloadEventStart", "unloadEventEnd", "loadEventEnd", "secureConnectionStart"].indexOf(n) >= 0, "Measure created from reserved name as starting time: " + n + " and threw expected error"); } }; From a26fd9163d429b05820ec6b4066e90ab7158711c Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Wed, 6 Dec 2017 13:01:15 +0100 Subject: [PATCH 19/78] Bug 1423146 - Change a pref so that an auth prompt requested by an image resource loaded from cross-originis not allowed. r=ckerschb --- 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 e6b7f1c8d019..46ee9c4488b4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2188,7 +2188,7 @@ pref("network.auth.subresource-http-auth-allow", 2); // false - it is not allowed. // If network.auth.subresource-http-auth-allow has values 0 or 1 this pref does not // have any effect. -pref("network.auth.subresource-img-cross-origin-http-auth-allow", true); +pref("network.auth.subresource-img-cross-origin-http-auth-allow", false); // This preference controls whether to allow sending default credentials (SSO) to // NTLM/Negotiate servers allowed in the "trusted uri" list when navigating them From f39232154e51ca82cb25c93213d1fa4a18f61d2c Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 6 Dec 2017 13:41:55 +0100 Subject: [PATCH 20/78] Bug 1419475 - Add another space before comment in yaml file to avoid linting failure. r=me --- taskcluster/ci/test/mochitest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taskcluster/ci/test/mochitest.yml b/taskcluster/ci/test/mochitest.yml index aa65cc4265a4..4d5254bd7129 100644 --- a/taskcluster/ci/test/mochitest.yml +++ b/taskcluster/ci/test/mochitest.yml @@ -344,7 +344,7 @@ mochitest-webgl: treeherder-symbol: tc-M(gl) run-on-projects: by-test-platform: - windows10-64-ccov/.*: [] # Do not run on Windows ccov, see bug 1419475. + windows10-64-ccov/.*: [] # Do not run on Windows ccov, see bug 1419475. default: built-projects virtualization: virtual-with-gpu chunks: From 6faa3298a56035bdccc076cb06f34814ff812f78 Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Wed, 6 Dec 2017 14:13:32 +0100 Subject: [PATCH 21/78] Bug 1423522 - We should not block http-authentication prompts for proxies. r=ckerschb --- netwerk/protocol/http/nsHttpChannelAuthProvider.cpp | 9 +++++++-- netwerk/protocol/http/nsHttpChannelAuthProvider.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index 92abed30355a..26bd6752525a 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -910,7 +910,7 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // sub-resources, or always allowed for sub-resources. // For more details look at the bug 647010. // BlockPrompt will set mCrossOrigin parameter as well. - if (BlockPrompt()) { + if (BlockPrompt(proxyAuth)) { LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge: " "Prompt is blocked [this=%p pref=%d img-pref=%d]\n", this, sAuthAllowPref, sImgCrossOriginAuthAllowPref)); @@ -962,7 +962,7 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, } bool -nsHttpChannelAuthProvider::BlockPrompt() +nsHttpChannelAuthProvider::BlockPrompt(bool proxyAuth) { // Verify that it's ok to prompt for credentials here, per spec // http://xhr.spec.whatwg.org/#the-send%28%29-method @@ -976,6 +976,11 @@ nsHttpChannelAuthProvider::BlockPrompt() return true; } + if (proxyAuth) { + // Do not block auth-dialog if this is a proxy authentication. + return false; + } + nsCOMPtr chan = do_QueryInterface(mAuthChannel); nsCOMPtr loadInfo; chan->GetLoadInfo(getter_AddRefs(loadInfo)); diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.h b/netwerk/protocol/http/nsHttpChannelAuthProvider.h index 4cf61c5a5881..7dcbede0a386 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h @@ -122,7 +122,7 @@ private: // for all sub-resources, blocked for cross-origin sub-resources, or // always allowed for sub-resources. // For more details look at the bug 647010. - bool BlockPrompt(); + bool BlockPrompt(bool proxyAuth); // Store credentials to the cache when appropriate aFlags are set. MOZ_MUST_USE nsresult UpdateCache(nsIHttpAuthenticator *aAuth, From 7820c6ec0d2cd87a610731ec07343631abf8f614 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Wed, 6 Dec 2017 14:17:19 +0100 Subject: [PATCH 22/78] Bug 1423139 - Don't attach a SetArrayLength stub for JSOP_INITELEM. r=evilpie --- js/src/jit-test/tests/cacheir/bug1423139.js | 15 +++++++++++++++ js/src/jit/CacheIR.cpp | 7 +++++-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/cacheir/bug1423139.js diff --git a/js/src/jit-test/tests/cacheir/bug1423139.js b/js/src/jit-test/tests/cacheir/bug1423139.js new file mode 100644 index 000000000000..31ad297a4941 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1423139.js @@ -0,0 +1,15 @@ +var c = 0; +for (var i = 0; i < 5; i++) { + try { + Object.defineProperty([], "length", { + configurable: true, + enumerable: true, + writable: true, + value: 0 + }); + } catch (e) { + assertEq(e instanceof TypeError, true); + c++; + } +} +assertEq(c, i); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 1235c144a6c8..14f57136ad31 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -2852,9 +2852,9 @@ SetPropIRGenerator::tryAttachStub() return true; if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId)) return true; - if (tryAttachSetArrayLength(obj, objId, id, rhsValId)) - return true; if (IsPropertySetOp(JSOp(*pc_))) { + if (tryAttachSetArrayLength(obj, objId, id, rhsValId)) + return true; if (tryAttachSetter(obj, objId, id, rhsValId)) return true; if (tryAttachWindowProxy(obj, objId, id, rhsValId)) @@ -3287,6 +3287,9 @@ bool SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId) { + // Don't attach an array length stub for ops like JSOP_INITELEM. + MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_))); + if (!obj->is() || !JSID_IS_ATOM(id, cx_->names().length) || !obj->as().lengthIsWritable()) From 1648b3e19138fe4ad481202a59d7f12beea46175 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Wed, 6 Dec 2017 14:18:21 +0100 Subject: [PATCH 23/78] Bug 1416727 - Fix some problems with Baseline's Call IC. r=djvj --- js/src/jit-test/tests/baseline/bug1416727.js | 16 +++++++++ js/src/jit/BaselineIC.cpp | 38 +++++++++++--------- js/src/jit/BaselineIC.h | 9 ----- 3 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 js/src/jit-test/tests/baseline/bug1416727.js diff --git a/js/src/jit-test/tests/baseline/bug1416727.js b/js/src/jit-test/tests/baseline/bug1416727.js new file mode 100644 index 000000000000..547afd1e151e --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1416727.js @@ -0,0 +1,16 @@ +// |jit-test| error: too much recursion +g = newGlobal() +g.parent = this +g.eval("new Debugger(parent).onExceptionUnwind = function () {}") +function test() { + function f(n) { + if (n != 0) { + f(n - 1); + } + try { + test(); + } finally {} + } + f(100); +} +test(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 317491842e7c..21ce1b582d06 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -2227,7 +2227,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb return true; } - if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) { + if (stub->state().mode() == ICState::Mode::Megamorphic) { // Create a Call_AnyScripted stub. JitSpew(JitSpew_BaselineIC, " Generating Call_AnyScripted stub (cons=%s, spread=%s)", constructing ? "yes" : "no", isSpread ? "yes" : "no"); @@ -2335,9 +2335,9 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb return true; } - if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) { + if (stub->state().mode() == ICState::Mode::Megamorphic) { JitSpew(JitSpew_BaselineIC, - " Too many Call_Native stubs. TODO: add Call_AnyNative!"); + " Megamorphic Call_Native stubs. TODO: add Call_AnyNative!"); return true; } @@ -2518,18 +2518,16 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo()); } } - if (!handled) - stub->state().trackNotAttached(); - } - // Try attaching a regular call stub, but only if the CacheIR attempt didn't add - // any stubs. - if (!handled) { - bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc); - if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, - createSingleton, &handled)) - { - return false; + // Try attaching a regular call stub, but only if the CacheIR attempt didn't add + // any stubs. + if (!handled) { + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc); + if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, + createSingleton, &handled)) + { + return false; + } } } @@ -2573,7 +2571,12 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint if (!stub->addMonitorStubForValue(cx, frame, types, res)) return false; - if (!handled) { + // Try to transition again in case we called this IC recursively. + if (stub->state().maybeTransition()) + stub->discardStubs(cx); + canAttachStub = stub->state().canAttachStub(); + + if (!handled && canAttachStub) { // If 'callee' is a potential Call_ConstStringSplit, try to attach an // optimized ConstStringSplit stub. Note that vp[0] now holds the return value // instead of the callee, so we pass the callee as well. @@ -2581,8 +2584,11 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint return false; } - if (!handled) + if (!handled) { stub->noteUnoptimizableCall(); + if (canAttachStub) + stub->state().trackNotAttached(); + } return true; } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index b7bcf0e0ae53..1777d9579c7f 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -732,8 +732,6 @@ class ICCall_Fallback : public ICMonitoredFallbackStub static const unsigned UNOPTIMIZABLE_CALL_FLAG = 0x1; static const uint32_t MAX_OPTIMIZED_STUBS = 16; - static const uint32_t MAX_SCRIPTED_STUBS = 7; - static const uint32_t MAX_NATIVE_STUBS = 7; private: explicit ICCall_Fallback(JitCode* stubCode) @@ -748,16 +746,9 @@ class ICCall_Fallback : public ICMonitoredFallbackStub return extra_ & UNOPTIMIZABLE_CALL_FLAG; } - unsigned scriptedStubCount() const { - return numStubsWithKind(Call_Scripted); - } bool scriptedStubsAreGeneralized() const { return hasStub(Call_AnyScripted); } - - unsigned nativeStubCount() const { - return numStubsWithKind(Call_Native); - } bool nativeStubsAreGeneralized() const { // Return hasStub(Call_AnyNative) after Call_AnyNative stub is added. return false; From 15fe8a65ea6cac9ca2a0833090dcca25bc8cc370 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 17:17:20 +0100 Subject: [PATCH 24/78] Bug 1397793 - Delete old-deprecated VoEExternalMedia. r=dminor MozReview-Commit-ID: C2189mFvWiY --HG-- extra : rebase_source : 9182b1c9ddc8548cb0687b63bf850d94627dd505 extra : histedit_source : e115740ff0e51c5af4bdc596e9833728d91a6ed7 --- .../src/media-conduit/AudioConduit.cpp | 56 ++-- .../src/peerconnection/TransceiverImpl.cpp | 5 +- .../voice_engine/include/voe_external_media.h | 53 ---- .../auto_test/standard/external_media_test.cc | 37 --- .../voice_engine/voe_external_media_impl.cc | 264 ------------------ .../voice_engine/voe_external_media_impl.h | 25 -- 6 files changed, 25 insertions(+), 415 deletions(-) diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp index 56d9179a7f8f..36b6b80ec8bc 100644 --- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp @@ -74,13 +74,6 @@ WebrtcAudioConduit::~WebrtcAudioConduit() delete codec; } - // The first one of a pair to be deleted shuts down media for both - if(mPtrVoEXmedia) - { - mPtrVoEXmedia->SetExternalRecordingStatus(false); - mPtrVoEXmedia->SetExternalPlayoutStatus(false); - } - //Deal with the transport if(mPtrVoENetwork) { @@ -357,8 +350,9 @@ MediaConduitErrorCode WebrtcAudioConduit::Init() return kMediaConduitSessionNotInited; } - // init the engine with our audio device layer - if(mPtrVoEBase->Init() == -1) + // Init the engine with a fake audio device (we're using cubeb for audio input + // and output anyways). + if(mPtrVoEBase->Init(mFakeAudioDevice.get()) == -1) { CSFLogError(LOGTAG, "%s VoiceEngine Base Not Initialized", __FUNCTION__); return kMediaConduitSessionNotInited; @@ -423,21 +417,7 @@ MediaConduitErrorCode WebrtcAudioConduit::Init() return kMediaConduitTransportRegistrationFail; } - if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1) - { - CSFLogError(LOGTAG, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__, - mPtrVoEBase->LastError()); - return kMediaConduitExternalPlayoutError; - } - - if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1) - { - CSFLogError(LOGTAG, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__, - mPtrVoEBase->LastError()); - return kMediaConduitExternalRecordingError; - } - - CSFLogDebug(LOGTAG , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this); + CSFLogDebug(LOGTAG, "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this); return kMediaConduitNoError; } @@ -717,7 +697,7 @@ WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[], } capture_delay = mCaptureDelay; - //Insert the samples + // Insert the samples mPtrVoEBase->audio_transport()->PushCaptureData(mChannel, audio_data, sizeof(audio_data[0])*8, // bits samplingFreqHz, @@ -729,9 +709,9 @@ WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[], MediaConduitErrorCode WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], - int32_t samplingFreqHz, - int32_t capture_delay, - int& lengthSamples) + int32_t samplingFreqHz, + int32_t capture_delay, + int& lengthSamples) { CSFLogDebug(LOGTAG, "%s ", __FUNCTION__); @@ -772,11 +752,9 @@ WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], lengthSamples = 0; //output paramter - if(mPtrVoEXmedia->ExternalPlayoutGetData( speechData, - samplingFreqHz, - capture_delay, - lengthSamples) == -1) - { + if (mPtrVoEXmedia->GetAudioFrame(mChannel, + samplingFreqHz, + &mAudioFrame) != 0) { int error = mPtrVoEBase->LastError(); CSFLogError(LOGTAG, "%s Getting audio data Failed %d", __FUNCTION__, error); if(error == VE_RUNTIME_PLAY_ERROR) @@ -786,6 +764,11 @@ WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], return kMediaConduitUnknownError; } + // XXX Annoying, have to copy to our buffers -- refactor? + lengthSamples = mAudioFrame.samples_per_channel_ * mAudioFrame.num_channels_; + PodCopy(speechData, mAudioFrame.data_, + lengthSamples); + // Not #ifdef DEBUG or on a log module so we can use it for about:webrtc/etc mSamples += lengthSamples; if (mSamples >= mLastSyncLog + samplingFreqHz) { @@ -964,6 +947,13 @@ WebrtcAudioConduit::StartReceiving() return kMediaConduitUnknownError; } + // we can't call GetAudioFrame() if we don't enable "external" mixing + if(mPtrVoEXmedia->SetExternalMixing(mChannel, true) == -1) + { + CSFLogError(LOGTAG, "%s SetExternalMixing Failed", __FUNCTION__); + return kMediaConduitPlayoutError; + } + if(mPtrVoEBase->StartPlayout(mChannel) == -1) { CSFLogError(LOGTAG, "%s Starting playout Failed", __FUNCTION__); diff --git a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp index e4ccb2e26023..8e8f112f065f 100644 --- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp @@ -1030,8 +1030,7 @@ static void StartTrack(MediaStream* aSource, segment_(aSegment) {} void Run() override { - TrackRate track_rate = segment_->GetType() == MediaSegment::AUDIO ? - WEBRTC_DEFAULT_SAMPLE_RATE : mStream->GraphRate(); + TrackRate track_rate = mStream->GraphRate(); StreamTime current_end = mStream->GetTracksEnd(); MOZ_MTLOG(ML_DEBUG, "current_end = " << current_end); TrackTicks current_ticks = @@ -1053,7 +1052,7 @@ static void StartTrack(MediaStream* aSource, MOZ_MTLOG(ML_DEBUG, "Calling AddAudioTrack"); mStream->AsSourceStream()->AddAudioTrack( kAudioTrack, - WEBRTC_DEFAULT_SAMPLE_RATE, + track_rate, 0, static_cast(segment_.forget())); } else { diff --git a/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h b/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h index 331223fed0a2..bcb12b2fa32d 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h +++ b/media/webrtc/trunk/webrtc/voice_engine/include/voe_external_media.h @@ -8,27 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ -// In some cases it is desirable to use an audio source or sink which may -// not be available to the VoiceEngine, such as a DV camera. This sub-API -// contains functions that allow for the use of such external recording -// sources and playout sinks. It also describes how recorded data, or data -// to be played out, can be modified outside the VoiceEngine. -// -// Usage example, omitting error checking: -// -// using namespace webrtc; -// VoiceEngine* voe = VoiceEngine::Create(); -// VoEBase* base = VoEBase::GetInterface(voe); -// VoEMediaProcess media = VoEMediaProcess::GetInterface(voe); -// base->Init(); -// ... -// media->SetExternalRecordingStatus(true); -// ... -// base->Terminate(); -// base->Release(); -// media->Release(); -// VoiceEngine::Delete(voe); -// #ifndef WEBRTC_VOICE_ENGINE_VOE_EXTERNAL_MEDIA_H #define WEBRTC_VOICE_ENGINE_VOE_EXTERNAL_MEDIA_H @@ -85,38 +64,6 @@ class WEBRTC_DLLEXPORT VoEExternalMedia { // media for the specified |channel| and |type|. virtual int DeRegisterExternalMediaProcessing(int channel, ProcessingTypes type) = 0; - - // Toogles state of external recording. - virtual int SetExternalRecordingStatus(bool enable) = 0; - - // Toogles state of external playout. - virtual int SetExternalPlayoutStatus(bool enable) = 0; - - // This function accepts externally recorded audio. During transmission, - // this method should be called at as regular an interval as possible - // with frames of corresponding size. - virtual int ExternalRecordingInsertData( - const int16_t speechData10ms[], int lengthSamples, - int samplingFreqHz, int current_delay_ms) = 0; - - - // This function inserts audio written to the OS audio drivers for use - // as the far-end signal for AEC processing. The length of the block - // must be 160, 320, 441 or 480 samples (for 16000, 32000, 44100 or - // 48000 kHz sampling rates respectively). - virtual int ExternalPlayoutData( - int16_t speechData10ms[], int samplingFreqHz, int num_channels, - int& lengthSamples) = 0; - - // This function gets audio for an external playout sink. - // During transmission, this function should be called every ~10 ms - // to obtain a new 10 ms frame of audio. The length of the block will - // be 160, 320, 441 or 480 samples (for 16000, 32000, 44100 or - // 48000 kHz sampling rates respectively). - virtual int ExternalPlayoutGetData( - int16_t speechData10ms[], int samplingFreqHz, - int current_delay_ms, int& lengthSamples) = 0; - // Pulls an audio frame from the specified |channel| for external mixing. // If the |desired_sample_rate_hz| is 0, the signal will be returned with // its native frequency, otherwise it will be resampled. Valid frequencies diff --git a/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/external_media_test.cc b/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/external_media_test.cc index 5ea2c17bfc5f..4534e128b3a7 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/external_media_test.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/external_media_test.cc @@ -29,43 +29,6 @@ class ExternalMediaTest : public AfterStreamingFixture { } }; -TEST_F(ExternalMediaTest, ManualCanRecordAndPlaybackUsingExternalPlayout) { - SwitchToManualMicrophone(); - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); - EXPECT_EQ(0, voe_xmedia_->SetExternalPlayoutStatus(true)); - EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - - TEST_LOG("Recording data for 2 seconds starting now: please speak.\n"); - int16_t recording[32000]; - for (int i = 0; i < 200; i++) { - int sample_length = 0; - EXPECT_EQ(0, voe_xmedia_->ExternalPlayoutGetData( - &(recording[i * 160]), 16000, 100, sample_length)); - EXPECT_EQ(160, sample_length); - Sleep(10); - } - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); - EXPECT_EQ(0, voe_xmedia_->SetExternalPlayoutStatus(false)); - EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); - EXPECT_EQ(0, voe_xmedia_->SetExternalRecordingStatus(true)); - EXPECT_EQ(0, voe_base_->StartSend(channel_)); - - TEST_LOG("Playing back recording, you should hear what you said earlier.\n"); - for (int i = 0; i < 200; i++) { - EXPECT_EQ(0, voe_xmedia_->ExternalRecordingInsertData( - &(recording[i * 160]), 160, 16000, 20)); - Sleep(10); - } - - EXPECT_EQ(0, voe_base_->StopSend(channel_)); - EXPECT_EQ(0, voe_xmedia_->SetExternalRecordingStatus(false)); -} - TEST_F(ExternalMediaTest, ManualRegisterExternalMediaProcessingOnAllChannelsAffectsPlayout) { TEST_LOG("Enabling external media processing: audio should be affected.\n"); diff --git a/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc b/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc index ece976ce3527..3351af6fd03f 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc @@ -31,9 +31,6 @@ VoEExternalMedia* VoEExternalMedia::GetInterface(VoiceEngine* voiceEngine) { VoEExternalMediaImpl::VoEExternalMediaImpl(voe::SharedData* shared) : -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - playout_delay_ms_(0), -#endif shared_(shared) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(shared_->instance_id(), -1), "VoEExternalMediaImpl() - ctor"); @@ -116,267 +113,6 @@ int VoEExternalMediaImpl::DeRegisterExternalMediaProcessing( return -1; } -int VoEExternalMediaImpl::SetExternalRecordingStatus(bool enable) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(shared_->instance_id(), -1), - "SetExternalRecordingStatus(enable=%d)", enable); -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - if (shared_->audio_device() && shared_->audio_device()->Recording()) - { - shared_->SetLastError(VE_ALREADY_SENDING, kTraceError, - "SetExternalRecordingStatus() cannot set state while sending"); - return -1; - } - shared_->set_ext_recording(enable); - return 0; -#else - shared_->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetExternalRecordingStatus() external recording is not supported"); - return -1; -#endif -} - -int VoEExternalMediaImpl::ExternalRecordingInsertData( - const int16_t speechData10ms[], - int lengthSamples, - int samplingFreqHz, - int current_delay_ms) -{ - WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(shared_->instance_id(), -1), - "ExternalRecordingInsertData(speechData10ms=0x%x," - " lengthSamples=%u, samplingFreqHz=%d, current_delay_ms=%d)", - &speechData10ms[0], lengthSamples, samplingFreqHz, - current_delay_ms); -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - if (!shared_->statistics().Initialized()) - { - shared_->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - if (!shared_->ext_recording()) - { - shared_->SetLastError(VE_INVALID_OPERATION, kTraceError, - "ExternalRecordingInsertData() external recording is not enabled"); - return -1; - } - if (shared_->NumOfSendingChannels() == 0) - { - shared_->SetLastError(VE_ALREADY_SENDING, kTraceError, - "SetExternalRecordingStatus() no channel is sending"); - return -1; - } - if ((16000 != samplingFreqHz) && (32000 != samplingFreqHz) && - (48000 != samplingFreqHz) && (44100 != samplingFreqHz)) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetExternalRecordingStatus() invalid sample rate"); - return -1; - } - if ((0 == lengthSamples) || - ((lengthSamples % (samplingFreqHz / 100)) != 0)) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetExternalRecordingStatus() invalid buffer size"); - return -1; - } - if (current_delay_ms < 0) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetExternalRecordingStatus() invalid delay)"); - return -1; - } - - uint16_t blockSize = samplingFreqHz / 100; - // We know the number of samples for 10ms of audio, so we can derive the - // number of channels here: - uint32_t channels = lengthSamples * 100 / samplingFreqHz; - uint32_t nBlocks = lengthSamples / blockSize / channels; - int16_t totalDelayMS = 0; - uint16_t playoutDelayMS = 0; - - for (uint32_t i = 0; i < nBlocks; i++) - { - if (!shared_->ext_playout()) - { - // Use real playout delay if external playout is not enabled. - if (shared_->audio_device()->PlayoutDelay(&playoutDelayMS) != 0) { - shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning, - "PlayoutDelay() unable to get the playout delay"); - } - totalDelayMS = current_delay_ms + playoutDelayMS; - } - else - { - // Use stored delay value given the last call - // to ExternalPlayoutGetData. - totalDelayMS = current_delay_ms + playout_delay_ms_; - // Compensate for block sizes larger than 10ms - totalDelayMS -= (int16_t)(i*10); - if (totalDelayMS < 0) - totalDelayMS = 0; - } - shared_->transmit_mixer()->PrepareDemux( - (const int8_t*)(&speechData10ms[i*blockSize]), - blockSize, - channels, - samplingFreqHz, - totalDelayMS, - 0, - 0, - false); // Typing detection not supported - - shared_->transmit_mixer()->DemuxAndMix(); - shared_->transmit_mixer()->EncodeAndSend(); - } - return 0; -#else - shared_->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "ExternalRecordingInsertData() external recording is not supported"); - return -1; -#endif -} - -int VoEExternalMediaImpl::SetExternalPlayoutStatus(bool enable) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(shared_->instance_id(), -1), - "SetExternalPlayoutStatus(enable=%d)", enable); -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - if (shared_->audio_device() && shared_->audio_device()->Playing()) - { - shared_->SetLastError(VE_ALREADY_SENDING, kTraceError, - "SetExternalPlayoutStatus() cannot set state while playing"); - return -1; - } - shared_->set_ext_playout(enable); - return 0; -#else - shared_->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "SetExternalPlayoutStatus() external playout is not supported"); - return -1; -#endif -} - -// This inserts a copy of the raw audio sent to the output drivers to use -// as the "far end" signal for the AEC. Currently only 10ms chunks are -// supported unfortunately. Since we have to rechunk to 10ms to call this, -// thre isn't much gained by allowing N*10ms here; external code can loop -// if needed. -int VoEExternalMediaImpl::ExternalPlayoutData( - int16_t speechData10ms[], - int samplingFreqHz, - int num_channels, - int& lengthSamples) -{ - WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(shared_->instance_id(), -1), - "ExternalPlayoutData(speechData10ms=0x%x," - " lengthSamples=%u, samplingFreqHz=%d)", - &speechData10ms[0], lengthSamples, samplingFreqHz); - -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - if (!shared_->statistics().Initialized()) - { - shared_->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - // FIX(jesup) - check if this is enabled? - if (shared_->NumOfSendingChannels() == 0) - { - shared_->SetLastError(VE_ALREADY_SENDING, kTraceError, - "SetExternalRecordingStatus() no channel is sending"); - return -1; - } - if ((16000 != samplingFreqHz) && (32000 != samplingFreqHz) && - (48000 != samplingFreqHz) && (44100 != samplingFreqHz)) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetExternalRecordingStatus() invalid sample rate"); - return -1; - } - - // Far-end data is inserted without going through neteq/etc. - // Only supports 10ms chunks; AnalyzeReverseStream() enforces that - // lower down. - AudioFrame audioFrame; - audioFrame.UpdateFrame(-1, 0xFFFFFFFF, - speechData10ms, - lengthSamples, - samplingFreqHz, - AudioFrame::kNormalSpeech, - AudioFrame::kVadUnknown, - num_channels); - - shared_->output_mixer()->APMAnalyzeReverseStream(audioFrame); -#endif - return 0; -} - -int VoEExternalMediaImpl::ExternalPlayoutGetData( - int16_t speechData10ms[], - int samplingFreqHz, - int current_delay_ms, - int& lengthSamples) -{ - WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(shared_->instance_id(), -1), - "ExternalPlayoutGetData(speechData10ms=0x%x, samplingFreqHz=%d" - ", current_delay_ms=%d)", &speechData10ms[0], samplingFreqHz, - current_delay_ms); -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - if (!shared_->statistics().Initialized()) - { - shared_->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - if (!shared_->ext_playout()) - { - shared_->SetLastError(VE_INVALID_OPERATION, kTraceError, - "ExternalPlayoutGetData() external playout is not enabled"); - return -1; - } - if ((16000 != samplingFreqHz) && (32000 != samplingFreqHz) && - (48000 != samplingFreqHz) && (44100 != samplingFreqHz)) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "ExternalPlayoutGetData() invalid sample rate"); - return -1; - } - if (current_delay_ms < 0) - { - shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "ExternalPlayoutGetData() invalid delay)"); - return -1; - } - - AudioFrame audioFrame; - - uint32_t channels = shared_->output_mixer()->GetOutputChannelCount(); - // If we have not received any data yet, consider it's mono since it's the - // most common case. - if (channels == 0) { - channels = 1; - } - - // Retrieve mixed output at the specified rate - shared_->output_mixer()->MixActiveChannels(); - shared_->output_mixer()->DoOperationsOnCombinedSignal(true); - shared_->output_mixer()->GetMixedAudio(samplingFreqHz, channels, &audioFrame); - - // Deliver audio (PCM) samples to the external sink - memcpy(speechData10ms, - audioFrame.data_, - sizeof(int16_t)*audioFrame.samples_per_channel_*channels); - lengthSamples = audioFrame.samples_per_channel_ * channels; - - // Store current playout delay (to be used by ExternalRecordingInsertData). - playout_delay_ms_ = current_delay_ms; - - return 0; -#else - shared_->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, - "ExternalPlayoutGetData() external playout is not supported"); - return -1; -#endif -} - int VoEExternalMediaImpl::GetAudioFrame(int channel, int desired_sample_rate_hz, AudioFrame* frame) { if (!shared_->statistics().Initialized()) { diff --git a/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.h b/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.h index da0228589540..22e963b7a8cb 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.h +++ b/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.h @@ -26,28 +26,6 @@ class VoEExternalMediaImpl : public VoEExternalMedia { int DeRegisterExternalMediaProcessing(int channel, ProcessingTypes type) override; - virtual int SetExternalRecordingStatus(bool enable) override; - - virtual int SetExternalPlayoutStatus(bool enable) override; - - virtual int ExternalRecordingInsertData( - const int16_t speechData10ms[], - int lengthSamples, - int samplingFreqHz, - int current_delay_ms) override; - - // Insertion of far-end data as actually played out to the OS audio driver - virtual int ExternalPlayoutData( - int16_t speechData10ms[], - int samplingFreqHz, - int num_channels, - int& lengthSamples) override; - - virtual int ExternalPlayoutGetData(int16_t speechData10ms[], - int samplingFreqHz, - int current_delay_ms, - int& lengthSamples) override; - int GetAudioFrame(int channel, int desired_sample_rate_hz, AudioFrame* frame) override; @@ -59,9 +37,6 @@ class VoEExternalMediaImpl : public VoEExternalMedia { ~VoEExternalMediaImpl() override; private: -#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT - int playout_delay_ms_; -#endif voe::SharedData* shared_; }; From 43c980e20dee9480375dc871738819ef9765ebb6 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 17:24:26 +0100 Subject: [PATCH 25/78] Bug 1397793 - Use the MSG rate in MediaPipeline/PeerConnectionImpl. r=pehrsons We used to fix the rate, arbitrarily, to 32kHz. Because the graph is almost never running at 32kHz (more like 44.1kHz or 48kHz), and the codec would often not be at 32kHz, this meant multiple resampling: - Once here, in MediaPipeline, to bring to 32kHz - Once when getting inserted in the MSG (so that the audio was brought back to MSG rate) - Maybe once in cubeb (depending on the platform) This always removes the second resampling: the track is now at the correct rate, as far as the MSG is concerned. Additionally, if the MSG is running at 48kHz, more resampling are saved, because it's one of the native webrtc.org rates. MozReview-Commit-ID: DBWcwuWxUpu --HG-- extra : rebase_source : 588d188f63237f1ce2cb0f2b290d54797d2d22e8 extra : histedit_source : 51733a22f6019140f7a309038a2ff524fbb564a4 --- .../src/mediapipeline/MediaPipeline.cpp | 17 ++++++++++------- .../signaling/src/mediapipeline/MediaPipeline.h | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index d593f67d434e..74c9bef696cf 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -63,7 +63,7 @@ // Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at // 48KHz) #define AUDIO_SAMPLE_BUFFER_MAX_BYTES 480*2*2 -static_assert((WEBRTC_DEFAULT_SAMPLE_RATE/100)*sizeof(uint16_t) * 2 +static_assert((WEBRTC_MAX_SAMPLE_RATE/100)*sizeof(uint16_t) * 2 <= AUDIO_SAMPLE_BUFFER_MAX_BYTES, "AUDIO_SAMPLE_BUFFER_MAX_BYTES is not large enough"); @@ -2038,8 +2038,11 @@ public: return; } + TrackRate rate = graph->GraphRate(); + uint32_t samples_per_10ms = rate/100; + // This comparison is done in total time to avoid accumulated roundoff errors. - while (source_->TicksToTimeRoundDown(WEBRTC_DEFAULT_SAMPLE_RATE, + while (source_->TicksToTimeRoundDown(rate, played_ticks_) < desired_time) { int16_t scratch_buffer[AUDIO_SAMPLE_BUFFER_MAX_BYTES / sizeof(int16_t)]; @@ -2049,7 +2052,7 @@ public: MediaConduitErrorCode err = static_cast(conduit_.get())->GetAudioFrame( scratch_buffer, - WEBRTC_DEFAULT_SAMPLE_RATE, + rate, 0, // TODO(ekr@rtfm.com): better estimate of "capture" (really playout) delay samples_length); @@ -2059,11 +2062,11 @@ public: err, played_ticks_, desired_time, source_->StreamTimeToSeconds(desired_time)); // if this is not enough we'll loop and provide more - samples_length = WEBRTC_DEFAULT_SAMPLE_RATE/100; + samples_length = samples_per_10ms; PodArrayZero(scratch_buffer); } - MOZ_ASSERT(samples_length * sizeof(uint16_t) < AUDIO_SAMPLE_BUFFER_MAX_BYTES); + MOZ_ASSERT(samples_length * sizeof(uint16_t) <= AUDIO_SAMPLE_BUFFER_MAX_BYTES); CSFLogDebug(LOGTAG, "Audio conduit returned buffer of length %u", samples_length); @@ -2074,7 +2077,7 @@ public: // We derive the number of channels of the stream from the number of samples // the AudioConduit gives us, considering it gives us packets of 10ms and we // know the rate. - uint32_t channelCount = samples_length / (WEBRTC_DEFAULT_SAMPLE_RATE / 100); + uint32_t channelCount = samples_length / samples_per_10ms; AutoTArray channels; AutoTArray outputChannels; size_t frames = samples_length / channelCount; @@ -2101,7 +2104,7 @@ public: if (source_->AppendToTrack(track_id_, &segment)) { played_ticks_ += frames; if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) { - if (played_ticks_ > last_log_ + WEBRTC_DEFAULT_SAMPLE_RATE) { // ~ 1 second + if (played_ticks_ > last_log_ + graph->GraphRate()) { // ~ 1 second MOZ_LOG(AudioLogModule(), LogLevel::Debug, ("%p: Inserting %zu samples into track %d, total = %" PRIu64, (void*) this, frames, track_id_, played_ticks_)); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index 6242e6cd68b9..e7b0f1313b8c 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -28,6 +28,7 @@ // Should come from MediaEngine.h, but that's a pain to include here // because of the MOZILLA_EXTERNAL_LINKAGE stuff. #define WEBRTC_DEFAULT_SAMPLE_RATE 32000 +#define WEBRTC_MAX_SAMPLE_RATE 48000 class nsIPrincipal; From 42b91c9e6147f4cdc4c4a0dc1c8c10d589d57f5f Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 17:33:42 +0100 Subject: [PATCH 26/78] Bug 1397793 - Move away from VoEExternalMedia "external" API in AudioConduit.cpp. r=dminor This forces us to do a copy. It's not the end of the world but could be avoided. The number of channels received is now explicit (via `AudioFrame::num_channels_`), instead of being guessed based on the number of samples (considering we're always dealing with 10ms of audio, and we know the rate). It's still coupled a bit with audio devices, but we cheat, and use a "fake audio device", which isn't going to touch actual OS APIs. MozReview-Commit-ID: 1Tfajkv1HQR --HG-- extra : rebase_source : f9ed6f1beeb3745dc17c4e6264808d1918e8906c extra : histedit_source : 4338aea961b861462caa79afab66ebaea06e40b2 --- media/webrtc/signaling/src/media-conduit/AudioConduit.h | 5 +++++ media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp | 2 +- media/webrtc/signaling/src/mediapipeline/MediaPipeline.h | 1 - .../signaling/src/peerconnection/PeerConnectionImpl.cpp | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.h b/media/webrtc/signaling/src/media-conduit/AudioConduit.h index a7af9f9ed943..e2104417c169 100644 --- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h @@ -17,6 +17,7 @@ // Audio Engine Includes #include "webrtc/common_types.h" +#include "webrtc/modules/audio_device/include/fake_audio_device.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_volume_control.h" #include "webrtc/voice_engine/include/voe_codec.h" @@ -174,6 +175,7 @@ public: explicit WebrtcAudioConduit(): mVoiceEngine(nullptr), + mFakeAudioDevice(new webrtc::FakeAudioDeviceModule()), mTransportMonitor("WebrtcAudioConduit"), mTransmitterTransport(nullptr), mReceiverTransport(nullptr), @@ -297,6 +299,7 @@ private: void DumpCodecDB() const; webrtc::VoiceEngine* mVoiceEngine; + UniquePtr mFakeAudioDevice; mozilla::ReentrantMonitor mTransportMonitor; RefPtr mTransmitterTransport; RefPtr mReceiverTransport; @@ -334,6 +337,8 @@ private: uint32_t mLastTimestamp; + webrtc::AudioFrame mAudioFrame; // for output pulls + uint32_t mSamples; uint32_t mLastSyncLog; diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index 74c9bef696cf..7e68c41b016f 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -2104,7 +2104,7 @@ public: if (source_->AppendToTrack(track_id_, &segment)) { played_ticks_ += frames; if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) { - if (played_ticks_ > last_log_ + graph->GraphRate()) { // ~ 1 second + if (played_ticks_ > last_log_ + rate) { // ~ 1 second MOZ_LOG(AudioLogModule(), LogLevel::Debug, ("%p: Inserting %zu samples into track %d, total = %" PRIu64, (void*) this, frames, track_id_, played_ticks_)); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index e7b0f1313b8c..25837a9961d0 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -27,7 +27,6 @@ // Should come from MediaEngine.h, but that's a pain to include here // because of the MOZILLA_EXTERNAL_LINKAGE stuff. -#define WEBRTC_DEFAULT_SAMPLE_RATE 32000 #define WEBRTC_MAX_SAMPLE_RATE 48000 class nsIPrincipal; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 06727d6038eb..b2a100447e5c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -2269,6 +2269,7 @@ PeerConnectionImpl::InsertAudioLevelForContributingSource( break; } } + return NS_OK; } From f73796a286be8516f73a175b5e9d71238cee6ace Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 17:40:19 +0100 Subject: [PATCH 27/78] Bug 1397793 - Revert Mozilla changes to OutputMixer r=jesup The MSG provides the reverse stream, and feed it directly to the APM. MozReview-Commit-ID: A6DO407CJkp --HG-- extra : rebase_source : 65515c02928ed56d57ddd2facd586125df7f09ec extra : histedit_source : fc61533566deca6023cb749acda96b5772661ebc --- .../trunk/webrtc/voice_engine/output_mixer.cc | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc b/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc index 81db50729c47..61babd2750b3 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc @@ -247,11 +247,6 @@ OutputMixer::GetOutputVolumePan(float& left, float& right) return 0; } -int OutputMixer::GetOutputChannelCount() -{ - return _audioFrame.num_channels_; -} - int OutputMixer::StartRecordingPlayout(const char* fileName, const CodecInst* codecInst) { @@ -477,7 +472,11 @@ OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm) // --- Far-end Voice Quality Enhancement (AudioProcessing Module) if (feed_data_to_apm) { - APMAnalyzeReverseStream(_audioFrame); + if (_audioProcessingModulePtr->ProcessReverseStream(&_audioFrame) != 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), + "AudioProcessingModule::ProcessReverseStream() => error"); + RTC_NOTREACHED(); + } } // --- External media processing @@ -504,28 +503,5 @@ OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm) return 0; } - -// Brought back by Mozilla so we can insert the reverse stream -void OutputMixer::APMAnalyzeReverseStream(AudioFrame &audioFrame) { - // Convert 44100Hz to 32000Hz since Processing doesn't support 44100 - // directly. - // XXX Bug 1367510 -- convert to 48000? Or modify Processing to - // support 44100 directly? (that's probably the best) - AudioFrame *frame = &audioFrame; - AudioFrame tempframe; - if (frame->sample_rate_hz_ == AudioProcessing::NativeRate::kSampleRate44_1kHz) { - tempframe.num_channels_ = 1; - tempframe.sample_rate_hz_ = AudioProcessing::NativeRate::kSampleRate32kHz; - RemixAndResample(audioFrame, &audioproc_resampler_, &tempframe); - frame = &tempframe; - } - - if (_audioProcessingModulePtr->ProcessReverseStream(frame) != 0) { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1), - "AudioProcessingModule::ProcessReverseStream() => error"); - RTC_DCHECK(false); - } -} - } // namespace voe } // namespace webrtc From 36e1dc3f90569dcb4046f6f3829a662fab0cf1e6 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 17:43:10 +0100 Subject: [PATCH 28/78] Bug 1397793 - Move MediaEngineDefault to use the MSG rate instead of something hard-coded. r=pehrsons This is "just" for testing, but is cleaner, and skips some resampling, and is in line with the other patches, to converge with always using MSG rate when we can. MozReview-Commit-ID: CBQHEDQWJE3 --HG-- extra : rebase_source : a65c4df357a6f56306b63b92416697f01699358f extra : histedit_source : ae589d7cf7bc3895a0f4b5b496b60846bddf7d1a --- dom/media/webrtc/MediaEngineDefault.cpp | 16 ++++++++++------ dom/media/webrtc/MediaEngineDefault.h | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dom/media/webrtc/MediaEngineDefault.cpp b/dom/media/webrtc/MediaEngineDefault.cpp index ea5996e92e23..060d63ad430e 100644 --- a/dom/media/webrtc/MediaEngineDefault.cpp +++ b/dom/media/webrtc/MediaEngineDefault.cpp @@ -26,7 +26,6 @@ #include "YuvStamper.h" #endif -#define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE #define DEFAULT_AUDIO_TIMER_MS 10 namespace mozilla { @@ -332,6 +331,7 @@ NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource) MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource() : MediaEngineAudioSource(kReleased) , mLastNotify(0) + , mFreq(1000) {} MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource() @@ -382,10 +382,8 @@ MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConst return NS_ERROR_FAILURE; } + mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000; mState = kAllocated; - // generate sine wave (default 1KHz) - mSineGenerator = new SineWaveGenerator(AUDIO_RATE, - static_cast(aPrefs.mFreq ? aPrefs.mFreq : 1000)); *aOutHandle = nullptr; return NS_OK; } @@ -409,9 +407,15 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID, return NS_ERROR_FAILURE; } + + if (!mSineGenerator) { + // generate sine wave (default 1KHz) + mSineGenerator = new SineWaveGenerator(aStream->GraphRate(), mFreq); + } + // AddTrack will take ownership of segment AudioSegment* segment = new AudioSegment(); - aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); + aStream->AddAudioTrack(aID, mRate, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); // Remember TrackID so we can finish later mTrackID = aID; @@ -467,7 +471,7 @@ MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph, MOZ_ASSERT(aID == mTrackID); AudioSegment segment; // avoid accumulating rounding errors - TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime); + TrackTicks desired = aSource->TimeToTicksRoundUp(mRate, aDesiredTime); TrackTicks delta = desired - mLastNotify; mLastNotify += delta; AppendToSegment(segment, delta, aPrincipalHandle); diff --git a/dom/media/webrtc/MediaEngineDefault.h b/dom/media/webrtc/MediaEngineDefault.h index 629b0c675874..7834f68c1299 100644 --- a/dom/media/webrtc/MediaEngineDefault.h +++ b/dom/media/webrtc/MediaEngineDefault.h @@ -188,8 +188,10 @@ protected: TrackID mTrackID; TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread) + TrackRate mRate; // ditto + uint32_t mFreq; // ditto - // Created on Allocate, then accessed from NotifyPull (MSG thread) + // Created on Start, then accessed from NotifyPull (MSG thread) nsAutoPtr mSineGenerator; }; From 0ade31e233d5d80c3596db454eca8e921ac4a0b1 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 18:11:48 +0100 Subject: [PATCH 29/78] Bug 1397793 - Remove VoEExternalMedia usage in MediaEngineWebRTCAudio and MediaEngineWebRTC. r=pehrsons This needs the next patches to build fine, but is split out for the review. A side effect of this patch is to break non-duplex, making the whole init/cleanup phase much simpler. MozReview-Commit-ID: Caqc8v7CWwZ --HG-- extra : rebase_source : 6e7d501ef99f3ea5d755a610238b8f260194bba0 extra : histedit_source : 298c7e95a2bd40e8f9ce014e06faad159fca513e --- dom/media/webrtc/MediaEngine.h | 2 - dom/media/webrtc/MediaEngineWebRTC.cpp | 58 +----- dom/media/webrtc/MediaEngineWebRTC.h | 113 +----------- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 192 +------------------- 4 files changed, 18 insertions(+), 347 deletions(-) diff --git a/dom/media/webrtc/MediaEngine.h b/dom/media/webrtc/MediaEngine.h index 0c57c361a1fd..a5fc53a35af0 100644 --- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -53,8 +53,6 @@ public: static const int DEFAULT_169_VIDEO_WIDTH = 1280; static const int DEFAULT_169_VIDEO_HEIGHT = 720; - static const int DEFAULT_SAMPLE_RATE = 32000; - // This allows using whatever rate the graph is using for the // MediaStreamTrack. This is useful for microphone data, we know it's already // at the correct rate for insertion in the MSG. diff --git a/dom/media/webrtc/MediaEngineWebRTC.cpp b/dom/media/webrtc/MediaEngineWebRTC.cpp index a318a7062ad5..731188f66adc 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -107,7 +107,6 @@ void AudioInputCubeb::UpdateDeviceList() MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) : mMutex("mozilla::MediaEngineWebRTC"), - mVoiceEngine(nullptr), mAudioInput(nullptr), mFullDuplex(aPrefs.mFullDuplex), mDelayAgnostic(aPrefs.mDelayAgnostic), @@ -280,43 +279,11 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource, return; } -#ifdef MOZ_WIDGET_ANDROID - JavaVM* jvm = mozilla::jni::GetVM(); - jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); - - if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) { - LOG(("VoiceEngine:SetAndroidObjects Failed")); - return; - } -#endif - - if (!mVoiceEngine) { - mVoiceEngine = webrtc::VoiceEngine::Create(); - if (!mVoiceEngine) { + if (!mAudioInput) { + if (!SupportsDuplex()) { return; } - } - - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return; - } - - // Always re-init the voice engine, since if we close the last use we - // DeInitEngine() and Terminate(), which shuts down Process() - but means - // we have to Init() again before using it. Init() when already inited is - // just a no-op, so call always. - if (ptrVoEBase->Init() < 0) { - return; - } - - if (!mAudioInput) { - if (SupportsDuplex()) { - // The platform_supports_full_duplex. - mAudioInput = new mozilla::AudioInputCubeb(mVoiceEngine); - } else { - mAudioInput = new mozilla::AudioInputWebRTC(mVoiceEngine); - } + mAudioInput = new mozilla::AudioInputCubeb(); } int nDevices = 0; @@ -344,7 +311,6 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource, if (uniqueId[0] == '\0') { // Mac and Linux don't set uniqueId! - MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check } @@ -354,16 +320,7 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource, // We've already seen this device, just append. aASources->AppendElement(aSource.get()); } else { - AudioInput* audioinput = mAudioInput; - if (SupportsDuplex()) { - // The platform_supports_full_duplex. - - // For cubeb, it has state (the selected ID) - // XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this - // XXX Small window where the device list/index could change! - audioinput = new mozilla::AudioInputCubeb(mVoiceEngine, i); - } - aSource = new MediaEngineWebRTCMicrophoneSource(mVoiceEngine, audioinput, + aSource = new MediaEngineWebRTCMicrophoneSource(new mozilla::AudioInputCubeb(i), i, deviceName, uniqueId, mDelayAgnostic, mExtendedFilter); mAudioSources.Put(uuid, aSource); // Hashtable takes ownership. @@ -401,13 +358,6 @@ MediaEngineWebRTC::Shutdown() mVideoSources.Clear(); mAudioSources.Clear(); - if (mVoiceEngine) { - mVoiceEngine->SetTraceCallback(nullptr); - webrtc::VoiceEngine::Delete(mVoiceEngine); - } - - mVoiceEngine = nullptr; - mozilla::camera::Shutdown(); AudioInputCubeb::CleanupGlobalData(); } diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 3958255158e5..962077c0a5fa 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -141,7 +141,7 @@ protected: class AudioInput { public: - explicit AudioInput(webrtc::VoiceEngine* aVoiceEngine) : mVoiceEngine(aVoiceEngine) {}; + AudioInput() = default; // Threadsafe because it's referenced from an MicrophoneSource, which can // had references to it on other threads. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInput) @@ -160,15 +160,13 @@ public: protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~AudioInput() {} - - webrtc::VoiceEngine* mVoiceEngine; }; class AudioInputCubeb final : public AudioInput { public: - explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) : - AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0) + explicit AudioInputCubeb(int aIndex = 0) : + AudioInput(), mSelectedDevice(aIndex), mInUseCount(0) { if (!mDeviceIndexes) { mDeviceIndexes = new nsTArray; @@ -314,14 +312,7 @@ public: MOZ_ASSERT(mDevices.count > 0); #endif - if (mInUseCount == 0) { - ScopedCustomReleasePtr ptrVoEXMedia; - ptrVoEXMedia = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine); - if (ptrVoEXMedia) { - ptrVoEXMedia->SetExternalRecordingStatus(true); - } - mAnyInUse = true; - } + mAnyInUse = true; mInUseCount++; // Always tell the stream we're using it for input aStream->OpenAudioInput(mSelectedDevice, aListener); @@ -369,77 +360,6 @@ private: static uint32_t sUserChannelCount; }; -class AudioInputWebRTC final : public AudioInput -{ -public: - explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {} - - int GetNumOfRecordingDevices(int& aDevices) - { - ScopedCustomReleasePtr ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - aDevices = ptrVoEBase->audio_device_module()->RecordingDevices(); - return 0; - } - - int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128], - char aStrGuidUTF8[128]) - { - ScopedCustomReleasePtr ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->RecordingDeviceName(aIndex, - aStrNameUTF8, - aStrGuidUTF8); - } - - int GetRecordingDeviceStatus(bool& aIsAvailable) - { - ScopedCustomReleasePtr ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->RecordingIsAvailable(&aIsAvailable); - } - - void GetChannelCount(uint32_t& aChannels) - { - aChannels = 1; // default to mono - } - - int GetMaxAvailableChannels(uint32_t& aChannels) - { - aChannels = 1; - return 0; - } - - void SetUserChannelCount(uint32_t aChannels) - {} - - void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {} - void StopRecording(SourceMediaStream *aStream) {} - - int SetRecordingDevice(int aIndex) - { - ScopedCustomReleasePtr ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->SetRecordingDevice(aIndex); - } - -protected: - // Protected destructor, to discourage deletion outside of Release(): - ~AudioInputWebRTC() {} -}; - class WebRTCAudioDataListener : public AudioDataListener { protected: @@ -490,13 +410,11 @@ private: RefPtr mAudioSource; }; -class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource, - public webrtc::VoEMediaProcess +class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource { typedef MediaEngineAudioSource Super; public: - MediaEngineWebRTCMicrophoneSource(webrtc::VoiceEngine* aVoiceEnginePtr, - mozilla::AudioInput* aAudioInput, + MediaEngineWebRTCMicrophoneSource(mozilla::AudioInput* aAudioInput, int aIndex, const char* name, const char* uuid, @@ -550,11 +468,6 @@ public: const nsTArray& aConstraintSets, const nsString& aDeviceId) const override; - // VoEMediaProcess. - virtual void Process(int channel, webrtc::ProcessingTypes type, - int16_t audio10ms[], size_t length, - int samplingFreq, bool isStereo) override; - void Shutdown() override; NS_DECL_THREADSAFE_ISUPPORTS @@ -576,9 +489,6 @@ private: // These allocate/configure and release the channel bool AllocChannel(); void FreeChannel(); - // These start/stop VoEBase and associated interfaces - bool InitEngine(); - void DeInitEngine(); // This is true when all processing is disabled, we can skip // packetization, resampling and other processing passes. @@ -596,18 +506,12 @@ private: TrackRate aRate, uint32_t aChannels); - webrtc::VoiceEngine* mVoiceEngine; RefPtr mAudioInput; RefPtr mListener; RefPtr mAudioOutputObserver; - // Note: shared across all microphone sources - we don't want to Terminate() - // the VoEBase until there are no active captures + // Note: shared across all microphone sources static int sChannelsOpen; - static ScopedCustomReleasePtr mVoEBase; - static ScopedCustomReleasePtr mVoERender; - static ScopedCustomReleasePtr mVoENetwork; - static ScopedCustomReleasePtr mVoEProcessing; // accessed from the GraphDriver thread except for deletion @@ -623,7 +527,6 @@ private: nsTArray mPrincipalHandles; // Maps to mSources. int mCapIndex; - int mChannel; bool mDelayAgnostic; bool mExtendedFilter; MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID; @@ -638,7 +541,6 @@ private: NullTransport *mNullTransport; - nsTArray mInputBuffer; // mSkipProcessing is true if none of the processing passes are enabled, // because of prefs or constraints. This allows simply copying the audio into // the MSG, skipping resampling and the whole webrtc.org code. @@ -676,7 +578,6 @@ private: // gUM runnables can e.g. Enumerate from multiple threads Mutex mMutex; - webrtc::VoiceEngine* mVoiceEngine; RefPtr mAudioInput; bool mFullDuplex; bool mDelayAgnostic; diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 21349dc3e7d6..27666814e927 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -53,10 +53,6 @@ NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource) NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource) int MediaEngineWebRTCMicrophoneSource::sChannelsOpen = 0; -ScopedCustomReleasePtr MediaEngineWebRTCMicrophoneSource::mVoEBase; -ScopedCustomReleasePtr MediaEngineWebRTCMicrophoneSource::mVoERender; -ScopedCustomReleasePtr MediaEngineWebRTCMicrophoneSource::mVoENetwork; -ScopedCustomReleasePtr MediaEngineWebRTCMicrophoneSource::mVoEProcessing; AudioOutputObserver::AudioOutputObserver() : mPlayoutFreq(0) @@ -187,7 +183,6 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame } MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( - webrtc::VoiceEngine* aVoiceEnginePtr, mozilla::AudioInput* aAudioInput, int aIndex, const char* name, @@ -195,11 +190,9 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( bool aDelayAgnostic, bool aExtendedFilter) : MediaEngineAudioSource(kReleased) - , mVoiceEngine(aVoiceEnginePtr) , mAudioInput(aAudioInput) , mMonitor("WebRTCMic.Monitor") , mCapIndex(aIndex) - , mChannel(-1) , mDelayAgnostic(aDelayAgnostic) , mExtendedFilter(aExtendedFilter) , mTrackID(TRACK_NONE) @@ -207,11 +200,9 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE) , mTotalFrames(0) , mLastLogFrames(0) - , mNullTransport(nullptr) , mSkipProcessing(false) , mInputDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100) { - MOZ_ASSERT(aVoiceEnginePtr); MOZ_ASSERT(aAudioInput); mDeviceName.Assign(NS_ConvertUTF8toUTF16(name)); mDeviceUUID.Assign(uuid); @@ -318,12 +309,7 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( switch (mState) { case kReleased: MOZ_ASSERT(aHandle); - if (sChannelsOpen == 0) { - if (!InitEngine()) { - LOG(("Audio engine is not initalized")); - return NS_ERROR_FAILURE; - } - } else { + if (sChannelsOpen != 0) { // Until we fix (or wallpaper) support for multiple mic input // (Bug 1238038) fail allocation for a second device return NS_ERROR_FAILURE; @@ -518,20 +504,8 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, mAudioOutputObserver->Clear(); } - if (mVoEBase->StartReceive(mChannel)) { - return NS_ERROR_FAILURE; - } - - // Must be *before* StartSend() so it will notice we selected external input (full_duplex) mAudioInput->StartRecording(aStream, mListener); - if (mVoEBase->StartSend(mChannel)) { - return NS_ERROR_FAILURE; - } - - // Attach external media processor, so this::Process will be called. - mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this); - return NS_OK; } @@ -560,9 +534,6 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID) if (mState != kStarted) { return NS_ERROR_FAILURE; } - if (!mVoEBase) { - return NS_ERROR_FAILURE; - } mState = kStopped; } @@ -574,14 +545,6 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID) mAudioInput->StopRecording(aSource); - mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel); - - if (mVoEBase->StopSend(mChannel)) { - return NS_ERROR_FAILURE; - } - if (mVoEBase->StopReceive(mChannel)) { - return NS_ERROR_FAILURE; - } return NS_OK; } @@ -775,130 +738,27 @@ MediaEngineWebRTCMicrophoneSource::DeviceChanged() { ResetProcessingIfNeeded(Ns); } -bool -MediaEngineWebRTCMicrophoneSource::InitEngine() -{ - MOZ_ASSERT(!mVoEBase); - mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - - mVoEBase->Init(); - webrtc::Config config; - config.Set(new webrtc::ExtendedFilter(mExtendedFilter)); - config.Set(new webrtc::DelayAgnostic(mDelayAgnostic)); - mVoEBase->audio_processing()->SetExtraOptions(config); - - mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine); - if (mVoERender) { - mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine); - if (mVoENetwork) { - mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine); - if (mVoEProcessing) { - mNullTransport = new NullTransport(); - return true; - } - } - } - DeInitEngine(); - return false; -} - -// This shuts down the engine when no channel is open -void -MediaEngineWebRTCMicrophoneSource::DeInitEngine() -{ - if (mVoEBase) { - mVoEBase->Terminate(); - delete mNullTransport; - mNullTransport = nullptr; - - mVoEProcessing = nullptr; - mVoENetwork = nullptr; - mVoERender = nullptr; - mVoEBase = nullptr; - } -} - -// This shuts down the engine when no channel is open. // mState records if a channel is allocated (slightly redundantly to mChannel) void MediaEngineWebRTCMicrophoneSource::FreeChannel() { if (mState != kReleased) { - if (mChannel != -1) { - MOZ_ASSERT(mVoENetwork && mVoEBase); - if (mVoENetwork) { - mVoENetwork->DeRegisterExternalTransport(mChannel); - } - if (mVoEBase) { - mVoEBase->DeleteChannel(mChannel); - } - mChannel = -1; - } mState = kReleased; MOZ_ASSERT(sChannelsOpen > 0); - if (--sChannelsOpen == 0) { - DeInitEngine(); - } + --sChannelsOpen; } } bool MediaEngineWebRTCMicrophoneSource::AllocChannel() { - MOZ_ASSERT(mVoEBase); + mSampleFrequency = MediaEngine::USE_GRAPH_RATE; + LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency)); - mChannel = mVoEBase->CreateChannel(); - if (mChannel >= 0) { - if (!mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) { - mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE; - LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency)); - - // Check for availability. - if (!mAudioInput->SetRecordingDevice(mCapIndex)) { - // Because of the permission mechanism of B2G, we need to skip the status - // check here. - bool avail = false; - mAudioInput->GetRecordingDeviceStatus(avail); - if (!avail) { - if (sChannelsOpen == 0) { - DeInitEngine(); - } - return false; - } - - // Set "codec" to PCM, 32kHz on device's channels - ScopedCustomReleasePtr ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine)); - if (ptrVoECodec) { - webrtc::CodecInst codec; - strcpy(codec.plname, ENCODING); - codec.channels = CHANNELS; - uint32_t maxChannels = 0; - if (mAudioInput->GetMaxAvailableChannels(maxChannels) == 0) { - MOZ_ASSERT(maxChannels); - codec.channels = std::min(maxChannels, MAX_CHANNELS); - } - MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000); - codec.rate = SAMPLE_RATE(mSampleFrequency); - codec.plfreq = mSampleFrequency; - codec.pacsize = SAMPLE_LENGTH(mSampleFrequency); - codec.pltype = 0; // Default payload type - - if (!ptrVoECodec->SetSendCodec(mChannel, codec)) { - mState = kAllocated; - sChannelsOpen++; - return true; - } - } - } - } - } - mVoEBase->DeleteChannel(mChannel); - mChannel = -1; - if (sChannelsOpen == 0) { - DeInitEngine(); - } - return false; + mState = kAllocated; + sChannelsOpen++; + return true; } void @@ -940,44 +800,6 @@ MediaEngineWebRTCMicrophoneSource::Shutdown() mAudioInput = nullptr; } -typedef int16_t sample; - -void -MediaEngineWebRTCMicrophoneSource::Process(int channel, - webrtc::ProcessingTypes type, - sample *audio10ms, size_t length, - int samplingFreq, bool isStereo) -{ - MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode."); - // On initial capture, throw away all far-end data except the most recent sample - // since it's already irrelevant and we want to keep avoid confusing the AEC far-end - // input code with "old" audio. - if (!mStarted) { - mStarted = true; - while (mAudioOutputObserver->Size() > 1) { - free(mAudioOutputObserver->Pop()); // only call if size() > 0 - } - } - - while (mAudioOutputObserver->Size() > 0) { - FarEndAudioChunk *buffer = mAudioOutputObserver->Pop(); // only call if size() > 0 - if (buffer) { - int length = buffer->mSamples; - int res = mVoERender->ExternalPlayoutData(buffer->mData, - mAudioOutputObserver->PlayoutFrequency(), - mAudioOutputObserver->PlayoutChannels(), - length); - free(buffer); - if (res == -1) { - return; - } - } - } - - uint32_t channels = isStereo ? 2 : 1; - InsertInGraph(audio10ms, length, channels); -} - void MediaEngineWebRTCAudioCaptureSource::GetName(nsAString &aName) const { From b76152fc034285da9840475333aa2e3d101ce707 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 31 Oct 2017 18:25:41 +0100 Subject: [PATCH 30/78] Bug 1397793 - Move to APM - Part 1 - UpdateSingleSource. r=pehrsons This part is about setting on/off audio processing feature. It's long, but it's mostly mechanichal changes, from the old API to the new one. This also covers reseting the processing in case of device changes (with macros). MozReview-Commit-ID: EI2TxHRicEr --HG-- extra : rebase_source : 7044c2d1695cdf0d6a69b4faa19349e3261ef204 extra : histedit_source : f5ac61e7b90ab4d5280623095c443529fb36cde5%2C5c969f1833bdc425842f945a5a8a4702ca13cd56 --- dom/media/webrtc/MediaEngineWebRTC.h | 8 +- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 251 ++++++++++++++------ modules/libpref/init/all.js | 4 +- 3 files changed, 185 insertions(+), 78 deletions(-) diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 962077c0a5fa..0333bea7f37d 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -513,9 +513,10 @@ private: // Note: shared across all microphone sources static int sChannelsOpen; + const UniquePtr mAudioProcessing; // accessed from the GraphDriver thread except for deletion - nsAutoPtr> mPacketizer; + nsAutoPtr> mPacketizer; ScopedCustomReleasePtr mVoERenderListener; // mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and @@ -539,8 +540,6 @@ private: uint64_t mTotalFrames; uint64_t mLastLogFrames; - NullTransport *mNullTransport; - // mSkipProcessing is true if none of the processing passes are enabled, // because of prefs or constraints. This allows simply copying the audio into // the MSG, skipping resampling and the whole webrtc.org code. @@ -549,7 +548,8 @@ private: // To only update microphone when needed, we keep track of previous settings. MediaEnginePrefs mLastPrefs; - AlignedShortBuffer mInputDownmixBuffer; + AlignedFloatBuffer mInputBuffer; + AlignedFloatBuffer mInputDownmixBuffer; }; class MediaEngineWebRTC : public MediaEngine diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 27666814e927..99edb51bc011 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -16,6 +16,11 @@ #undef FF #endif #include "webrtc/modules/audio_device/opensl/single_rw_fifo.h" +#include "webrtc/voice_engine/voice_engine_defines.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/common_audio/include/audio_util.h" + +using namespace webrtc; #define CHANNELS 1 #define ENCODING "L16" @@ -63,7 +68,7 @@ AudioOutputObserver::AudioOutputObserver() , mDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100) { // Buffers of 10ms chunks - mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10); + mPlayoutFifo = new SingleRwFifo(MAX_AEC_FIFO_DEPTH/10); } AudioOutputObserver::~AudioOutputObserver() @@ -191,6 +196,7 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( bool aExtendedFilter) : MediaEngineAudioSource(kReleased) , mAudioInput(aAudioInput) + , mAudioProcessing(AudioProcessing::Create()) , mMonitor("WebRTCMic.Monitor") , mCapIndex(aIndex) , mDelayAgnostic(aDelayAgnostic) @@ -266,6 +272,140 @@ bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b) return !memcmp(&a, &b, sizeof(MediaEnginePrefs)); }; +// This does an early return in case of error. +#define HANDLE_APM_ERROR(fn) \ +do { \ + int rv = fn; \ + if (rv != AudioProcessing::kNoError) { \ + MOZ_ASSERT_UNREACHABLE("APM error in " #fn); \ + return; \ + } \ +} while(0); + +void MediaEngineWebRTCMicrophoneSource::UpdateAECSettingsIfNeeded(bool aEnable, EcModes aMode) +{ + using webrtc::EcModes; + + EchoCancellation::SuppressionLevel level; + + switch(aMode) { + case EcModes::kEcUnchanged: + level = mAudioProcessing->echo_cancellation()->suppression_level(); + break; + case EcModes::kEcConference: + level = EchoCancellation::kHighSuppression; + break; + case EcModes::kEcDefault: + level = EchoCancellation::kModerateSuppression; + break; + case EcModes::kEcAec: + level = EchoCancellation::kModerateSuppression; + break; + case EcModes::kEcAecm: + // No suppression level to set for the mobile echo canceller + break; + default: + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Bad EcMode value")); + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the echo cancelation mode."); + // fall back to something sensible in release + level = EchoCancellation::kModerateSuppression; + break; + } + + // AECm and AEC are mutually exclusive. + if (aMode == EcModes::kEcAecm) { + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(false)); + HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(aEnable)); + } else { + HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(false)); + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(aEnable)); + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->set_suppression_level(level)); + } +} + +void +MediaEngineWebRTCMicrophoneSource::UpdateAGCSettingsIfNeeded(bool aEnable, AgcModes aMode) +{ +#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID) + if (aMode == kAgcAdaptiveAnalog) { + MOZ_LOG(GetMediaManagerLog(), + LogLevel::Error, + ("Invalid AGC mode kAgcAdaptiveAnalog on mobile")); + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the auto gain, on mobile."); + aMode = kAgcDefault; + } +#endif + GainControl::Mode mode = kDefaultAgcMode; + + switch (aMode) { + case AgcModes::kAgcDefault: + mode = kDefaultAgcMode; + break; + case AgcModes::kAgcUnchanged: + mode = mAudioProcessing->gain_control()->mode(); + break; + case AgcModes::kAgcFixedDigital: + mode = GainControl::Mode::kFixedDigital; + break; + case AgcModes::kAgcAdaptiveAnalog: + mode = GainControl::Mode::kAdaptiveAnalog; + break; + case AgcModes::kAgcAdaptiveDigital: + mode = GainControl::Mode::kAdaptiveDigital; + break; + default: + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the auto gain."); + // This is a good fallback, it works regardless of the platform. + mode = GainControl::Mode::kAdaptiveDigital; + break; + } + + HANDLE_APM_ERROR(mAudioProcessing->gain_control()->set_mode(mode)); + HANDLE_APM_ERROR(mAudioProcessing->gain_control()->Enable(aEnable)); +} + +void +MediaEngineWebRTCMicrophoneSource::UpdateNSSettingsIfNeeded(bool aEnable, NsModes aMode) +{ + NoiseSuppression::Level nsLevel; + + switch (aMode) { + case NsModes::kNsDefault: + nsLevel = kDefaultNsMode; + break; + case NsModes::kNsUnchanged: + nsLevel = mAudioProcessing->noise_suppression()->level(); + break; + case NsModes::kNsConference: + nsLevel = NoiseSuppression::kHigh; + break; + case NsModes::kNsLowSuppression: + nsLevel = NoiseSuppression::kLow; + break; + case NsModes::kNsModerateSuppression: + nsLevel = NoiseSuppression::kModerate; + break; + case NsModes::kNsHighSuppression: + nsLevel = NoiseSuppression::kHigh; + break; + case NsModes::kNsVeryHighSuppression: + nsLevel = NoiseSuppression::kVeryHigh; + break; + default: + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the noise suppression."); + // Pick something sensible as a faillback in release. + nsLevel = NoiseSuppression::kModerate; + } + HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->set_level(nsLevel)); + HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->Enable(aEnable)); +} + +#undef HANDLE_APM_ERROR + nsresult MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( const AllocationHandle* aHandle, @@ -314,24 +454,12 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( // (Bug 1238038) fail allocation for a second device return NS_ERROR_FAILURE; } - if (mAudioInput->SetRecordingDevice(mCapIndex)) { - return NS_ERROR_FAILURE; - } mAudioInput->SetUserChannelCount(prefs.mChannels); if (!AllocChannel()) { FreeChannel(); LOG(("Audio device is not initalized")); return NS_ERROR_FAILURE; } - LOG(("Audio device %d allocated", mCapIndex)); - { - // Update with the actual applied channelCount in order - // to store it in settings. - uint32_t channelCount = 0; - mAudioInput->GetChannelCount(channelCount); - MOZ_ASSERT(channelCount > 0); - prefs.mChannels = channelCount; - } break; case kStarted: @@ -341,19 +469,19 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( if (prefs.mChannels != mLastPrefs.mChannels) { MOZ_ASSERT(mSources.Length() > 0); + // If the channel count changed, tell the MSG to open a new driver with + // the correct channel count. auto& source = mSources.LastElement(); mAudioInput->SetUserChannelCount(prefs.mChannels); // Get validated number of channel uint32_t channelCount = 0; mAudioInput->GetChannelCount(channelCount); MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0); - // Check if new validated channels is the same as previous - if (static_cast(mLastPrefs.mChannels) != channelCount && + if (mLastPrefs.mChannels != prefs.mChannels && !source->OpenNewAudioCallbackDriver(mListener)) { + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Could not open a new AudioCallbackDriver for input")); return NS_ERROR_FAILURE; } - // Update settings - prefs.mChannels = channelCount; } if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) { @@ -372,46 +500,22 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( } if (sChannelsOpen > 0) { - int error; + UpdateAGCSettingsIfNeeded(prefs.mAgcOn, static_cast(prefs.mAgc)); + UpdateNSSettingsIfNeeded(prefs.mNoiseOn, static_cast(prefs.mNoise)); + UpdateAECSettingsIfNeeded(prefs.mAecOn, static_cast(prefs.mAec)); - error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec); - if (error) { - LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error)); - // Overhead of capturing all the time is very low (<0.1% of an audio only call) - if (prefs.mAecOn) { - error = mVoEProcessing->SetEcMetricsStatus(true); - if (error) { - LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error)); - } - } - } - error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc); - if (error) { - LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error)); - } - error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise); - if (error) { - LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error)); - } + webrtc::Config config; + config.Set(new webrtc::ExtendedFilter(mExtendedFilter)); + config.Set(new webrtc::DelayAgnostic(mDelayAgnostic)); + mAudioProcessing->SetExtraOptions(config); } - // we don't allow switching from non-fast-path to fast-path on the fly yet - if (mState != kStarted) { - mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn); - if (mSkipProcessing) { - mSampleFrequency = MediaEngine::USE_GRAPH_RATE; - } else { - // make sure we route a copy of the mixed audio output of this MSG to the - // AEC - if (!mAudioOutputObserver) { - mAudioOutputObserver = new AudioOutputObserver(); - } - } - } SetLastPrefs(prefs); return NS_OK; } +#undef HANDLE_APM_ERROR + void MediaEngineWebRTCMicrophoneSource::SetLastPrefs( const MediaEnginePrefs& aPrefs) @@ -579,13 +683,25 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, TrackRate aRate, uint32_t aChannels) { - // This will call Process() with data coming out of the AEC/NS/AGC/etc chain + MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode."); + size_t offset = 0; + if (!mPacketizer || mPacketizer->PacketSize() != aRate/100u || mPacketizer->Channels() != aChannels) { // It's ok to drop the audio still in the packetizer here. mPacketizer = - new AudioPacketizer(aRate/100, aChannels); + new AudioPacketizer(aRate/100, aChannels); + } + + // On initial capture, throw away all far-end data except the most recent sample + // since it's already irrelevant and we want to keep avoid confusing the AEC far-end + // input code with "old" audio. + if (!mStarted) { + mStarted = true; + while (mAudioOutputObserver->Size() > 1) { + free(mAudioOutputObserver->Pop()); // only call if size() > 0 + } } mPacketizer->Input(aBuffer, static_cast(aFrames)); @@ -704,38 +820,31 @@ MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph, #define ResetProcessingIfNeeded(_processing) \ do { \ - webrtc::_processing##Modes mode; \ - int rv = mVoEProcessing->Get##_processing##Status(enabled, mode); \ - if (rv) { \ - NS_WARNING("Could not get the status of the " \ - #_processing " on device change."); \ - return; \ - } \ + bool enabled = mAudioProcessing->_processing()->is_enabled(); \ \ if (enabled) { \ - rv = mVoEProcessing->Set##_processing##Status(!enabled); \ + int rv = mAudioProcessing->_processing()->Enable(!enabled); \ + if (rv) { \ + NS_WARNING("Could not reset the status of the " \ + #_processing " on device change."); \ + return; \ + } \ + rv = mAudioProcessing->_processing()->Enable(enabled); \ if (rv) { \ NS_WARNING("Could not reset the status of the " \ #_processing " on device change."); \ return; \ } \ \ - rv = mVoEProcessing->Set##_processing##Status(enabled); \ - if (rv) { \ - NS_WARNING("Could not reset the status of the " \ - #_processing " on device change."); \ - return; \ - } \ } \ } while(0) void MediaEngineWebRTCMicrophoneSource::DeviceChanged() { // Reset some processing - bool enabled; - ResetProcessingIfNeeded(Agc); - ResetProcessingIfNeeded(Ec); - ResetProcessingIfNeeded(Ns); + ResetProcessingIfNeeded(gain_control); + ResetProcessingIfNeeded(echo_cancellation); + ResetProcessingIfNeeded(noise_suppression); } // mState records if a channel is allocated (slightly redundantly to mChannel) @@ -796,8 +905,6 @@ MediaEngineWebRTCMicrophoneSource::Shutdown() Deallocate(mRegisteredHandles[0].get()); } MOZ_ASSERT(mState == kReleased); - - mAudioInput = nullptr; } void diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 46ee9c4488b4..7c6b29137bee 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -496,7 +496,7 @@ pref("media.peerconnection.ice.no_host", false); pref("media.peerconnection.ice.default_address_only", false); pref("media.peerconnection.ice.proxy_only", false); -// These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h +// These values (aec, agc, and noise) are from media/webrtc/trunk/webrtc/common_types.h // kXxxUnchanged = 0, kXxxDefault = 1, and higher values are specific to each // setting (for Xxx = Ec, Agc, or Ns). Defaults are all set to kXxxDefault here. pref("media.peerconnection.turn.disable", false); @@ -510,7 +510,7 @@ pref("media.getusermedia.noise_enabled", true); pref("media.getusermedia.aec_extended_filter", true); pref("media.getusermedia.noise", 1); pref("media.getusermedia.agc_enabled", false); -pref("media.getusermedia.agc", 1); +pref("media.getusermedia.agc", 3); // kAgcAdaptiveDigital // capture_delay: Adjustments for OS-specific input delay (lower bound) // playout_delay: Adjustments for OS-specific AudioStream+cubeb+output delay (lower bound) // full_duplex: enable cubeb full-duplex capture/playback From 20a31e686f269082a5b475f2d93306b41ecb8ad2 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Dec 2017 13:34:14 +0100 Subject: [PATCH 31/78] Bug 1397793 - Move to APM - Part 2 - Actual processing. r=pehrsons This also is long, but simple. First, we switch to floats everywhere. This allows to work with any rate, is more flexible with channel layout, and is a stable API (see audio_processing.h in webrtc.org). Then, 10ms worth of audio (already at the graph rate) are poped from the lock-free queue (fed on the other end by the MSG mixer), and does the following: - Down mixing to stereo (if needed) - De-interleaving into planar buffer - Prepare input and output config - Actually make the API call - Free the data Now, first, we should use a ring buffer, and not have to free any data. Then we also should not use a lock-free queue, and synchronously process the reverse-stream, but this is enough code already. Then, the actual mic data processing: - Pop a packet from the packetizer (that gives us 10ms worth of audio, note that we switch from int16_t to float, i.e. we don't do this conversion anymore). - We convert to planar buffers, deinterleaving - Prepare input and output config - Allocate a SharedBuffer of the right size - Process the data with the processing algorithm selected in UpdateSingleSource - Append to the a MediaSegment, and append to the right MediaStreamTrack for the correct SourceMediaStream (the data is already planar and all well). MozReview-Commit-ID: 2IjgHP0GAmw --HG-- extra : rebase_source : d2245037e8ee7145af7eef528dcee50817b69d83 extra : histedit_source : 79443c35b82d3bc8833d140dd5afc882b85b4c12 --- dom/media/webrtc/AudioOutputObserver.h | 6 +- dom/media/webrtc/MediaEngineWebRTC.h | 5 +- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 217 +++++++++++++++++--- 3 files changed, 196 insertions(+), 32 deletions(-) diff --git a/dom/media/webrtc/AudioOutputObserver.h b/dom/media/webrtc/AudioOutputObserver.h index 156ef3a2129b..b69cb898fd88 100644 --- a/dom/media/webrtc/AudioOutputObserver.h +++ b/dom/media/webrtc/AudioOutputObserver.h @@ -17,13 +17,13 @@ class SingleRwFifo; namespace mozilla { typedef struct FarEndAudioChunk_ { - uint16_t mSamples; + size_t mSamples; bool mOverrun; - int16_t mData[1]; // variable-length + AudioDataValue mData[1]; // variable-length } FarEndAudioChunk; // This class is used to packetize and send the mixed audio from an MSG, in -// int16, to the AEC module of WebRTC.org. +// float, to the AEC module of WebRTC.org. class AudioOutputObserver { public: diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 0333bea7f37d..72c1067ddf60 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -516,7 +516,7 @@ private: const UniquePtr mAudioProcessing; // accessed from the GraphDriver thread except for deletion - nsAutoPtr> mPacketizer; + nsAutoPtr> mPacketizer; ScopedCustomReleasePtr mVoERenderListener; // mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and @@ -549,7 +549,8 @@ private: MediaEnginePrefs mLastPrefs; AlignedFloatBuffer mInputBuffer; - AlignedFloatBuffer mInputDownmixBuffer; + AlignedFloatBuffer mDeinterleavedBuffer; + AlignedAudioBuffer mInputDownmixBuffer; }; class MediaEngineWebRTC : public MediaEngine diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 99edb51bc011..eb9e8c2ac0db 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -22,13 +22,6 @@ using namespace webrtc; -#define CHANNELS 1 -#define ENCODING "L16" -#define DEFAULT_PORT 5555 - -#define SAMPLE_RATE(freq) ((freq)*2*8) // bps, 16-bit samples -#define SAMPLE_LENGTH(freq) (((freq)*10)/1000) - // These are restrictions from the webrtc.org code #define MAX_CHANNELS 2 #define MAX_SAMPLING_FREQ 48000 // Hz - multiple of 100 @@ -144,7 +137,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame while (aFrames) { if (!mSaved) { mSaved = (FarEndAudioChunk *) moz_xmalloc(sizeof(FarEndAudioChunk) + - (mChunkSize * channels - 1)*sizeof(int16_t)); + (mChunkSize * channels - 1)*sizeof(AudioDataValue)); mSaved->mSamples = mChunkSize; mSaved->mOverrun = aOverran; aOverran = false; @@ -154,7 +147,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame to_copy = aFrames; } - int16_t* dest = &(mSaved->mData[mSamplesSaved * channels]); + AudioDataValue* dest = &(mSaved->mData[mSamplesSaved * channels]); if (aChannels > MAX_CHANNELS) { AudioConverter converter(AudioConfig(aChannels, 0), AudioConfig(channels, 0)); converter.Process(mDownmixBuffer, aBuffer, to_copy); @@ -165,7 +158,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame #ifdef LOG_FAREND_INSERTION if (fp) { - fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(int16_t), fp); + fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(AudioDataValue), fp); } #endif aFrames -= to_copy; @@ -203,7 +196,7 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( , mExtendedFilter(aExtendedFilter) , mTrackID(TRACK_NONE) , mStarted(false) - , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE) + , mSampleFrequency(MediaEngine::USE_GRAPH_RATE) , mTotalFrames(0) , mLastLogFrames(0) , mSkipProcessing(false) @@ -454,12 +447,24 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( // (Bug 1238038) fail allocation for a second device return NS_ERROR_FAILURE; } + if (mAudioInput->SetRecordingDevice(mCapIndex)) { + return NS_ERROR_FAILURE; + } mAudioInput->SetUserChannelCount(prefs.mChannels); if (!AllocChannel()) { FreeChannel(); LOG(("Audio device is not initalized")); return NS_ERROR_FAILURE; } + LOG(("Audio device %d allocated", mCapIndex)); + { + // Update with the actual applied channelCount in order + // to store it in settings. + uint32_t channelCount = 0; + mAudioInput->GetChannelCount(channelCount); + MOZ_ASSERT(channelCount > 0); + prefs.mChannels = channelCount; + } break; case kStarted: @@ -676,6 +681,7 @@ MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph, } } +// Only called if we're not in passthrough mode void MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, @@ -691,7 +697,7 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, mPacketizer->Channels() != aChannels) { // It's ok to drop the audio still in the packetizer here. mPacketizer = - new AudioPacketizer(aRate/100, aChannels); + new AudioPacketizer(aRate/100, aChannels); } // On initial capture, throw away all far-end data except the most recent sample @@ -704,6 +710,110 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, } } + // Feed the far-end audio data (speakers) to the feedback input of the AEC. + while (mAudioOutputObserver->Size() > 0) { + // Bug 1414837: This will call `free()`, and we should remove it. + // Pop gives ownership. + UniquePtr buffer(mAudioOutputObserver->Pop()); // only call if size() > 0 + if (!buffer) { + continue; + } + AudioDataValue* packetDataPointer = buffer->mData; + AutoTArray deinterleavedPacketDataChannelPointers; + AudioDataValue* interleavedFarend = nullptr; + uint32_t channelCountFarend = 0; + uint32_t framesPerPacketFarend = 0; + + // Downmix from aChannels to MAX_CHANNELS if needed + if (mAudioOutputObserver->PlayoutChannels() > MAX_CHANNELS) { + AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_DEFAULT), + AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_DEFAULT)); + framesPerPacketFarend = + buffer->mSamples; + framesPerPacketFarend = + converter.Process(mInputDownmixBuffer, + packetDataPointer, + framesPerPacketFarend); + interleavedFarend = mInputDownmixBuffer.Data(); + channelCountFarend = MAX_CHANNELS; + deinterleavedPacketDataChannelPointers.SetLength(MAX_CHANNELS); + } else { + uint32_t outputChannels = mAudioOutputObserver->PlayoutChannels(); + interleavedFarend = packetDataPointer; + channelCountFarend = outputChannels; + framesPerPacketFarend = buffer->mSamples; + deinterleavedPacketDataChannelPointers.SetLength(outputChannels); + } + + MOZ_ASSERT(interleavedFarend && + (channelCountFarend == 1 || channelCountFarend == 2) && + framesPerPacketFarend); + + offset = 0; + for (size_t i = 0; i < deinterleavedPacketDataChannelPointers.Length(); ++i) { + deinterleavedPacketDataChannelPointers[i] = packetDataPointer + offset; + offset += framesPerPacketFarend; + } + + // deinterleave back into the FarEndAudioChunk buffer to save an alloc. + // There is enough room because either there is the same number of + // channels/frames or we've just downmixed. + Deinterleave(interleavedFarend, + framesPerPacketFarend, + channelCountFarend, + deinterleavedPacketDataChannelPointers.Elements()); + + // Having the same config for input and output means we potentially save + // some CPU. We won't need the output here, the API forces us to set a + // valid pointer with enough space. + StreamConfig inputConfig(mAudioOutputObserver->PlayoutFrequency(), + channelCountFarend, + false /* we don't use typing detection*/); + StreamConfig outputConfig = inputConfig; + + // Prepare a channel pointers array, with enough storage for the + // frames. + // + // If this is a platform that uses s16 for audio input and output, + // convert to floats, the APM API we use only accepts floats. + + float* inputData = nullptr; +#ifdef MOZ_SAMPLE_TYPE_S16 + // Convert to floats, use mInputBuffer for this. + size_t sampleCount = framesPerPacketFarend * channelCountFarend; + if (mInputBuffer.Length() < sampleCount) { + mInputBuffer.SetLength(sampleCount); + } + ConvertAudioSamples(buffer->mData, mInputBuffer.Data(), sampleCount); + inputData = mInputBuffer.Data(); +#else // MOZ_SAMPLE_TYPE_F32 + inputData = buffer->mData; +#endif + + AutoTArray channelsPointers; + channelsPointers.SetLength(channelCountFarend); + offset = 0; + for (size_t i = 0; i < channelsPointers.Length(); ++i) { + channelsPointers[i] = inputData + offset; + offset += framesPerPacketFarend; + } + + // Passing the same pointers here saves a copy inside this function. + int err = + mAudioProcessing->ProcessReverseStream(channelsPointers.Elements(), + inputConfig, + outputConfig, + channelsPointers.Elements()); + + if (err) { + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, + ("error in audio ProcessReverseStream(): %d", err)); + return; + } + } + + // Packetize our input data into 10ms chunks, deinterleave into planar channel + // buffers, process, and append to the right MediaStreamTrack. mPacketizer->Input(aBuffer, static_cast(aFrames)); while (mPacketizer->PacketsAvailable()) { @@ -711,19 +821,72 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, mPacketizer->Channels(); if (mInputBuffer.Length() < samplesPerPacket) { mInputBuffer.SetLength(samplesPerPacket); + mDeinterleavedBuffer.SetLength(samplesPerPacket); } - int16_t* packet = mInputBuffer.Elements(); + float* packet = mInputBuffer.Data(); mPacketizer->Output(packet); - if (aChannels > MAX_CHANNELS) { - AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_S16), - AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_S16)); - converter.Process(mInputDownmixBuffer, packet, mPacketizer->PacketSize()); - mVoERender->ExternalRecordingInsertData(mInputDownmixBuffer.Data(), - mPacketizer->PacketSize() * MAX_CHANNELS, - aRate, 0); - } else { - mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0); + // Deinterleave the input data + // Prepare an array pointing to deinterleaved channels. + AutoTArray deinterleavedPacketizedInputDataChannelPointers; + deinterleavedPacketizedInputDataChannelPointers.SetLength(aChannels); + offset = 0; + for (size_t i = 0; i < deinterleavedPacketizedInputDataChannelPointers.Length(); ++i) { + deinterleavedPacketizedInputDataChannelPointers[i] = mDeinterleavedBuffer.Data() + offset; + offset += aFrames; + } + + // Deinterleave to mInputBuffer, pointed to by inputBufferChannelPointers. + Deinterleave(packet, mPacketizer->PacketSize(), aChannels, + deinterleavedPacketizedInputDataChannelPointers.Elements()); + + StreamConfig inputConfig(aRate, + aChannels, + false /* we don't use typing detection*/); + StreamConfig outputConfig = inputConfig; + + // Bug 1404965: Get the right delay here, it saves some work down the line. + mAudioProcessing->set_stream_delay_ms(0); + + // Bug 1414837: find a way to not allocate here. + RefPtr buffer = + SharedBuffer::Create(mPacketizer->PacketSize() * aChannels * sizeof(float)); + AudioSegment segment; + + // Prepare channel pointers to the SharedBuffer created above. + AutoTArray processedOutputChannelPointers; + AutoTArray processedOutputChannelPointersConst; + processedOutputChannelPointers.SetLength(aChannels); + processedOutputChannelPointersConst.SetLength(aChannels); + + offset = 0; + for (size_t i = 0; i < processedOutputChannelPointers.Length(); ++i) { + processedOutputChannelPointers[i] = static_cast(buffer->Data()) + offset; + processedOutputChannelPointersConst[i] = static_cast(buffer->Data()) + offset; + offset += aFrames; + } + + mAudioProcessing->ProcessStream(deinterleavedPacketizedInputDataChannelPointers.Elements(), + inputConfig, + outputConfig, + processedOutputChannelPointers.Elements()); + MonitorAutoLock lock(mMonitor); + if (mState != kStarted) + return; + + for (size_t i = 0; i < mSources.Length(); ++i) { + if (!mSources[i]) { // why ?! + continue; + } + + // We already have planar audio data of the right format. Insert into the + // MSG. + MOZ_ASSERT(processedOutputChannelPointers.Length() == aChannels); + segment.AppendFrames(buffer.forget(), + processedOutputChannelPointersConst, + mPacketizer->PacketSize(), + mPrincipalHandles[i]); + mSources[i]->AppendToTrack(mTrackID, &segment); } } } @@ -750,7 +913,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer, } size_t len = mSources.Length(); - for (size_t i = 0; i < len; i++) { + for (size_t i = 0; i < len; ++i) { if (!mSources[i]) { continue; } @@ -766,7 +929,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer, MOZ_ASSERT(aChannels >= 1 && aChannels <= 8, "Support up to 8 channels"); - nsAutoPtr segment(new AudioSegment()); + AudioSegment segment; RefPtr buffer = SharedBuffer::Create(aFrames * aChannels * sizeof(T)); AutoTArray channels; @@ -792,11 +955,11 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer, } MOZ_ASSERT(aChannels == channels.Length()); - segment->AppendFrames(buffer.forget(), channels, aFrames, + segment.AppendFrames(buffer.forget(), channels, aFrames, mPrincipalHandles[i]); - segment->GetStartTime(insertTime); + segment.GetStartTime(insertTime); - mSources[i]->AppendToTrack(mTrackID, segment); + mSources[i]->AppendToTrack(mTrackID, &segment); } } From 965ccaadaa7518600196118df16e7be829a0c060 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 23 Nov 2017 15:53:25 +0100 Subject: [PATCH 32/78] Bug 1397793 - Allow switching processing on/off dynamically. r=pehrsons MozReview-Commit-ID: G0NJRkKEVeM --HG-- extra : rebase_source : 16547d0ace0d0d7e791ef60249f94fe3060944b4 extra : histedit_source : 78db64f41f799dec503c36a809cb05713cbd45e4 --- dom/media/webrtc/MediaEngineWebRTC.h | 4 ++-- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 72c1067ddf60..f27805bd0b9e 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -508,12 +508,12 @@ private: RefPtr mAudioInput; RefPtr mListener; - RefPtr mAudioOutputObserver; // Note: shared across all microphone sources static int sChannelsOpen; const UniquePtr mAudioProcessing; + const RefPtr mAudioOutputObserver; // accessed from the GraphDriver thread except for deletion nsAutoPtr> mPacketizer; @@ -543,7 +543,7 @@ private: // mSkipProcessing is true if none of the processing passes are enabled, // because of prefs or constraints. This allows simply copying the audio into // the MSG, skipping resampling and the whole webrtc.org code. - bool mSkipProcessing; + std::atomic_bool mSkipProcessing; // To only update microphone when needed, we keep track of previous settings. MediaEnginePrefs mLastPrefs; diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index eb9e8c2ac0db..edf944f1dfaa 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -588,7 +588,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) { mSampleFrequency = aStream->GraphRate(); } - aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); + aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED); // XXX Make this based on the pref. aStream->RegisterForAudioMixing(); @@ -609,9 +609,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream, // Make sure logger starts before capture AsyncLatencyLogger::Get(true); - if (mAudioOutputObserver) { - mAudioOutputObserver->Clear(); - } + mAudioOutputObserver->Clear(); mAudioInput->StartRecording(aStream, mListener); @@ -675,7 +673,7 @@ MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph, TrackRate aRate, uint32_t aChannels) { - if (mAudioOutputObserver) { + if (!PassThrough()) { mAudioOutputObserver->InsertFarEnd(aBuffer, aFrames, false, aRate, aChannels); } @@ -714,7 +712,7 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, while (mAudioOutputObserver->Size() > 0) { // Bug 1414837: This will call `free()`, and we should remove it. // Pop gives ownership. - UniquePtr buffer(mAudioOutputObserver->Pop()); // only call if size() > 0 + nsAutoPtr buffer(mAudioOutputObserver->Pop()); // only call if size() > 0 if (!buffer) { continue; } From 58fc6f439a929e23af1b7fc02acfebc492dc526e Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 7 Nov 2017 14:28:06 +0100 Subject: [PATCH 33/78] Bug 1397793 - Refactor the code that sets the processing modes. r=pehrsons MozReview-Commit-ID: IUgAfHawFIz --HG-- extra : rebase_source : f80414c0e9048ee5eb9754a0161844b5607addb8 extra : histedit_source : 71375da254afb9764c812ed9ba92689d828c74db --- dom/media/webrtc/MediaEngineWebRTC.h | 5 +++++ dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index f27805bd0b9e..6f7a38d1f90d 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -484,6 +484,11 @@ private: const nsString& aDeviceId, const char** aOutBadConstraint) override; + + void UpdateAECSettingsIfNeeded(bool aEnable, webrtc::EcModes aMode); + void UpdateAGCSettingsIfNeeded(bool aEnable, webrtc::AgcModes aMode); + void UpdateNSSettingsIfNeeded(bool aEnable, webrtc::NsModes aMode); + void SetLastPrefs(const MediaEnginePrefs& aPrefs); // These allocate/configure and release the channel diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index edf944f1dfaa..53d3bdb777c7 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -190,6 +190,7 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( : MediaEngineAudioSource(kReleased) , mAudioInput(aAudioInput) , mAudioProcessing(AudioProcessing::Create()) + , mAudioOutputObserver(new AudioOutputObserver()) , mMonitor("WebRTCMic.Monitor") , mCapIndex(aIndex) , mDelayAgnostic(aDelayAgnostic) @@ -514,7 +515,6 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( config.Set(new webrtc::DelayAgnostic(mDelayAgnostic)); mAudioProcessing->SetExtraOptions(config); } - SetLastPrefs(prefs); return NS_OK; } From de142c0970d329b6ea6b86924be58a2d2f9fc4af Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 8 Nov 2017 14:10:45 +0100 Subject: [PATCH 34/78] Bug 1397793 - Use a ControlMessage to switch between passthrough and processing mode for microphone input. r=pehsrons MozReview-Commit-ID: EIvQKZf7oGq --HG-- extra : rebase_source : cf84d44df1fadd262283946641d7ce67f779b7d2 extra : histedit_source : 71388af2bab6cbfb3d6a4c700b2419899811e680 --- dom/media/webrtc/MediaEngineWebRTC.h | 19 +++++++----- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 32 ++++++++++++++++++--- dom/media/webrtc/moz.build | 3 +- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 6f7a38d1f90d..b6476c2157b5 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -494,12 +494,6 @@ private: // These allocate/configure and release the channel bool AllocChannel(); void FreeChannel(); - - // This is true when all processing is disabled, we can skip - // packetization, resampling and other processing passes. - bool PassThrough() { - return mSkipProcessing; - } template void InsertInGraph(const T* aBuffer, size_t aFrames, @@ -511,6 +505,16 @@ private: TrackRate aRate, uint32_t aChannels); + + // This is true when all processing is disabled, we can skip + // packetization, resampling and other processing passes. + bool PassThrough() { + return mSkipProcessing; + } + void SetPassThrough(bool aPassThrough) { + mSkipProcessing = aPassThrough; + } + RefPtr mAudioInput; RefPtr mListener; @@ -548,7 +552,8 @@ private: // mSkipProcessing is true if none of the processing passes are enabled, // because of prefs or constraints. This allows simply copying the audio into // the MSG, skipping resampling and the whole webrtc.org code. - std::atomic_bool mSkipProcessing; + // This is read and written to only on the MSG thread. + bool mSkipProcessing; // To only update microphone when needed, we keep track of previous settings. MediaEnginePrefs mLastPrefs; diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 53d3bdb777c7..01eda416779e 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -10,6 +10,7 @@ #include "mtransport/runnable_utils.h" #include "nsAutoPtr.h" #include "AudioConverter.h" +#include "MediaStreamGraphImpl.h" // scoped_ptr.h uses FF #ifdef FF @@ -522,8 +523,7 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( #undef HANDLE_APM_ERROR void -MediaEngineWebRTCMicrophoneSource::SetLastPrefs( - const MediaEnginePrefs& aPrefs) +MediaEngineWebRTCMicrophoneSource::SetLastPrefs(const MediaEnginePrefs& aPrefs) { mLastPrefs = aPrefs; @@ -534,11 +534,35 @@ MediaEngineWebRTCMicrophoneSource::SetLastPrefs( that->mSettings->mAutoGainControl.Value() = aPrefs.mAgcOn; that->mSettings->mNoiseSuppression.Value() = aPrefs.mNoiseOn; that->mSettings->mChannelCount.Value() = aPrefs.mChannels; + + class Message : public ControlMessage { + public: + Message(MediaEngineWebRTCMicrophoneSource* aSource, + bool aPassThrough) + : ControlMessage(nullptr) + , mMicrophoneSource(aSource) + , mPassThrough(aPassThrough) + {} + + void Run() override + { + mMicrophoneSource->SetPassThrough(mPassThrough); + } + + protected: + RefPtr mMicrophoneSource; + bool mPassThrough; + }; + + bool passThrough = !(aPrefs.mAecOn || aPrefs.mAgcOn || aPrefs.mNoiseOn); + if (!that->mSources.IsEmpty()) { + that->mSources[0]->GraphImpl()->AppendMessage(MakeUnique(that, passThrough)); + } + return NS_OK; })); } - nsresult MediaEngineWebRTCMicrophoneSource::Deallocate(AllocationHandle* aHandle) { @@ -675,7 +699,7 @@ MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph, { if (!PassThrough()) { mAudioOutputObserver->InsertFarEnd(aBuffer, aFrames, false, - aRate, aChannels); + aRate, aChannels); } } diff --git a/dom/media/webrtc/moz.build b/dom/media/webrtc/moz.build index 56fc75b4519c..02805f06a59e 100644 --- a/dom/media/webrtc/moz.build +++ b/dom/media/webrtc/moz.build @@ -39,11 +39,12 @@ if CONFIG['MOZ_WEBRTC']: 'MediaEngineWebRTC.cpp', ] LOCAL_INCLUDES += [ + '..', '/dom/base', '/media/libyuv/libyuv/include', '/media/webrtc/signaling/src/common', '/media/webrtc/signaling/src/common/browser_logging', - '/media/webrtc/trunk', + '/media/webrtc/trunk' ] XPIDL_SOURCES += [ From 75105f79ae60b288ff59e9361d68203ed5313f08 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 21 Nov 2017 20:53:51 +0100 Subject: [PATCH 35/78] Bug 1397793 - Make the assertions in the rate conversion functions in StreamTracks.h be fatal. r=pehrsons MozReview-Commit-ID: 8nLask8nqV4 --HG-- extra : rebase_source : a9724f42fbf33a08a08b720f42d25bda0b6623f1 extra : histedit_source : 0a7ad7125b482b9de2013f787d86a661e554c240 --- dom/media/StreamTracks.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dom/media/StreamTracks.h b/dom/media/StreamTracks.h index 86357f5e4ab9..640db71ba658 100644 --- a/dom/media/StreamTracks.h +++ b/dom/media/StreamTracks.h @@ -16,17 +16,17 @@ inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) { - NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); - NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); - NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); + MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); + MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); + MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); return (aTicks * aOutRate) / aInRate; } inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) { - NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); - NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); - NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); + MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); + MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); + MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); return (aTicks * aOutRate + aInRate - 1) / aInRate; } From 1c13882dc4cd1a732b3b86d2b40cf5462a0bfa21 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 8 Nov 2017 15:16:06 +0100 Subject: [PATCH 36/78] Bug 1397793 - Remove mRate from MediaEngineDefault and use GraphRate(). r=pehrsons MozReview-Commit-ID: D9x3wICAMNA --HG-- extra : rebase_source : 6f863eccdf5044e1838a555f45b88f465503c1fc extra : histedit_source : 347fb57ff58138b8c949b8a2efb26b17e0a8c47d --- dom/media/webrtc/MediaEngineDefault.cpp | 4 ++-- dom/media/webrtc/MediaEngineDefault.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dom/media/webrtc/MediaEngineDefault.cpp b/dom/media/webrtc/MediaEngineDefault.cpp index 060d63ad430e..96a60cee5c14 100644 --- a/dom/media/webrtc/MediaEngineDefault.cpp +++ b/dom/media/webrtc/MediaEngineDefault.cpp @@ -415,7 +415,7 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID, // AddTrack will take ownership of segment AudioSegment* segment = new AudioSegment(); - aStream->AddAudioTrack(aID, mRate, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); + aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED); // Remember TrackID so we can finish later mTrackID = aID; @@ -471,7 +471,7 @@ MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph, MOZ_ASSERT(aID == mTrackID); AudioSegment segment; // avoid accumulating rounding errors - TrackTicks desired = aSource->TimeToTicksRoundUp(mRate, aDesiredTime); + TrackTicks desired = aSource->TimeToTicksRoundUp(aGraph->GraphRate(), aDesiredTime); TrackTicks delta = desired - mLastNotify; mLastNotify += delta; AppendToSegment(segment, delta, aPrincipalHandle); diff --git a/dom/media/webrtc/MediaEngineDefault.h b/dom/media/webrtc/MediaEngineDefault.h index 7834f68c1299..ceff38ae3807 100644 --- a/dom/media/webrtc/MediaEngineDefault.h +++ b/dom/media/webrtc/MediaEngineDefault.h @@ -188,7 +188,6 @@ protected: TrackID mTrackID; TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread) - TrackRate mRate; // ditto uint32_t mFreq; // ditto // Created on Start, then accessed from NotifyPull (MSG thread) From 93fa5b007c2d678c6278f295ecf6cf4ed96312ce Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Nov 2017 19:22:00 +0100 Subject: [PATCH 37/78] Bug 1397793 - Add asserts for AudioChunk invariants. r=pehrsons MozReview-Commit-ID: CFqyMPMMHyA --HG-- extra : rebase_source : 6648d283e6da1cc3ad98616063e62542b9e7346d extra : histedit_source : 8937ab1b4567fabc3c98802eefede9f00e9684a5 --- dom/media/AudioSegment.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/dom/media/AudioSegment.h b/dom/media/AudioSegment.h index c10d2c912db4..562781373426 100644 --- a/dom/media/AudioSegment.h +++ b/dom/media/AudioSegment.h @@ -187,7 +187,9 @@ struct AudioChunk { } return true; } - bool IsNull() const { return mBuffer == nullptr; } + bool IsNull() const { + return mBuffer == nullptr; + } void SetNull(StreamTime aDuration) { mBuffer = nullptr; @@ -333,13 +335,15 @@ public: void ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate); - void AppendFrames(already_AddRefed aBuffer, const nsTArray& aChannelData, int32_t aDuration, const PrincipalHandle& aPrincipalHandle) { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; + + MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?"); + for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) { chunk->mChannelData.AppendElement(aChannelData[channel]); } @@ -355,6 +359,9 @@ public: { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; + + MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?"); + for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) { chunk->mChannelData.AppendElement(aChannelData[channel]); } @@ -363,6 +370,7 @@ public: chunk->mTimeStamp = TimeStamp::Now(); #endif chunk->mPrincipalHandle = aPrincipalHandle; + } // Consumes aChunk, and returns a pointer to the persistent copy of aChunk // in the segment. @@ -371,6 +379,9 @@ public: AudioChunk* chunk = AppendChunk(aChunk->mDuration); chunk->mBuffer = aChunk->mBuffer.forget(); chunk->mChannelData.SwapElements(aChunk->mChannelData); + + MOZ_ASSERT(chunk->mBuffer || aChunk->mChannelData.IsEmpty(), "Appending invalid data ?"); + chunk->mVolume = aChunk->mVolume; chunk->mBufferFormat = aChunk->mBufferFormat; #ifdef MOZILLA_INTERNAL_API From b585f4301aebd6cf1bf1d6b2ff272c1ab7284304 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 28 Nov 2017 18:43:11 +0100 Subject: [PATCH 38/78] Bug 1397793 - Share SharedBuffer accross SourceMediaStream. r=pehrsons MozReview-Commit-ID: 4PEjwiA6PR8 --HG-- extra : rebase_source : 265d30d8bf83103c50bb3cb84b263bdb87b8494e extra : histedit_source : 1ba9523a366310d5784a671e2be03a7a71e790b3 --- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 01eda416779e..919d90c1d52b 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -904,7 +904,8 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, // We already have planar audio data of the right format. Insert into the // MSG. MOZ_ASSERT(processedOutputChannelPointers.Length() == aChannels); - segment.AppendFrames(buffer.forget(), + RefPtr other = buffer; + segment.AppendFrames(other.forget(), processedOutputChannelPointersConst, mPacketizer->PacketSize(), mPrincipalHandles[i]); From 2f0ae5124fefdd90067ac20948a29ae8c41fc7ef Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Dec 2017 18:16:22 +0100 Subject: [PATCH 39/78] Bug 1397793 - Don't use the AEC on a 440Hz tone when testing that audio is flowing. r=pehrsons This brings in a lot of noise and makes the test fail. MozReview-Commit-ID: 70EGM1q1J24 --HG-- extra : rebase_source : f19c191747f2e63303406a43a79454e4ea1ed428 extra : histedit_source : f27d34204de924ab30b5718b240ac3d23677991c --- .../mochitest/test_getUserMedia_mediaElementCapture_audio.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html b/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html index 8e1dab466b75..3b9e00896cf0 100644 --- a/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html @@ -16,7 +16,7 @@ createHTML({ var audioContext; var gUMAudioElement; var analyser; -runTest(() => getUserMedia({audio: true}) +runTest(() => getUserMedia({audio: { echoCancellation: false }}) .then(stream => { gUMAudioElement = createMediaElement("audio", "gUMAudio"); gUMAudioElement.srcObject = stream; From b5c3324cd13dbe75fbe0efdabbda0f217b344c8e Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Dec 2017 15:34:20 +0100 Subject: [PATCH 40/78] Bug 1423228 - Prevent using non-fake devices when testing screen-sharing. r=jib MozReview-Commit-ID: LX2fbVk0VHG --HG-- extra : rebase_source : a940b255ffdcc23c5c46c798b3706c9359c7c2aa extra : source : a75a3b7f9c6ee53b9e80684daca7425d20d01929 extra : histedit_source : c8bdb3e45db60e44fdf8f3e5eba8f2805affc49a --- browser/base/content/test/webrtc/get_user_media.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/base/content/test/webrtc/get_user_media.html b/browser/base/content/test/webrtc/get_user_media.html index 57fed46a5b19..fc3230dc32e8 100644 --- a/browser/base/content/test/webrtc/get_user_media.html +++ b/browser/base/content/test/webrtc/get_user_media.html @@ -32,7 +32,8 @@ function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) { mozMediaSource: aShare, mediaSource: aShare }; - } else if (useFakeStreams) { + } + if (useFakeStreams) { opts.fake = true; } From 3fe7d161c40312f6df159200c8f5eb9c005dc9d3 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 13 Nov 2017 20:13:45 +0000 Subject: [PATCH 41/78] Bug 1417251, part 1 - Fix race in layout/reftests/css-ui/caret-color-01-ref.html. r=me MozReview-Commit-ID: FwUxjshEkWU --- .../reftests/css-ui/caret-color-01-ref.html | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/layout/reftests/css-ui/caret-color-01-ref.html b/layout/reftests/css-ui/caret-color-01-ref.html index b516c9262430..2092e62c77d9 100644 --- a/layout/reftests/css-ui/caret-color-01-ref.html +++ b/layout/reftests/css-ui/caret-color-01-ref.html @@ -1,5 +1,19 @@ - + +
- + From 3e61847b92507def7146b5b47ef5e7e788d64e1d Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Tue, 14 Nov 2017 15:58:30 +0000 Subject: [PATCH 42/78] Bug 1417251, part 2 - Make the reftest harness support loading of consecutive URIs differing only by hash. r=dbaron,bz MozReview-Commit-ID: 6VVJrtX1KZa --- docshell/base/nsDocShell.cpp | 3 +++ layout/tools/reftest/reftest-content.js | 31 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index fcbb6f0ee68e..cc0c5c156de6 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -10531,6 +10531,9 @@ nsDocShell::InternalLoad(nsIURI* aURI, (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0; mURIResultedInDocument = false; // reset the clock... + // Note that there is code that relies on this check to stop us entering the + // `doShortCircuitedLoad` block below for certain load types. (For example, + // reftest-content.js uses LOAD_FLAGS_BYPASS_CACHE for this purpose.) if (aLoadType == LOAD_NORMAL || aLoadType == LOAD_STOP_CONTENT || LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) || diff --git a/layout/tools/reftest/reftest-content.js b/layout/tools/reftest/reftest-content.js index b357629d6b51..147c0d28b6de 100644 --- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -1055,9 +1055,38 @@ function DoAssertionCheck() SendAssertionCount(numAsserts); } +function URIsEqualIgnoringHash(uri1, uri2) +{ + if (!uri2) { + return false; + } + let hashIndex1 = uri1.indexOf("#"); + if (hashIndex1 > -1) { + uri1 = uri1.substr(0, hashIndex1); + } + let hashIndex2 = uri2.indexOf("#"); + if (hashIndex2 > -1) { + uri2 = uri2.substr(0, hashIndex2); + } + return uri1 == uri2; +} + function LoadURI(uri) { - var flags = webNavigation().LOAD_FLAGS_NONE; + let flags = CI.nsIWebNavigation.LOAD_FLAGS_NONE; + + if (URIsEqualIgnoringHash(uri, gCurrentURL)) { + // In this case the new URI would normally just cause an anchor scroll + // so we use LOAD_FLAGS_BYPASS_CACHE to force a reload so we'll get a + // 'load' event. (The code that handles new URIs once they're ready is + // triggered by a 'load' event, so we'll time out if we don't get one). + // + // Note that we avoid using this flag in general since we don't want + // to unnecessarily reload and reprocess common font files, images, + // etc. for every test/reference (that may slow down reftest runs). + flags = CI.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; + } + webNavigation().loadURI(uri, flags, null, null, null); } From 421d6dfd164a7d2fc5558386d39b4f49bc64c1ed Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Thu, 16 Nov 2017 11:59:32 +0000 Subject: [PATCH 43/78] Bug 1417251, part 3 - Tweak the fuzz on four reftests. r=me MozReview-Commit-ID: 1zGRbndck9G --- layout/reftests/backgrounds/reftest.list | 2 +- layout/reftests/bugs/reftest.list | 2 +- layout/reftests/css-break/reftest.list | 2 +- layout/reftests/text-overflow/reftest.list | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/reftests/backgrounds/reftest.list b/layout/reftests/backgrounds/reftest.list index 085b27a44ece..804fc06a9263 100644 --- a/layout/reftests/backgrounds/reftest.list +++ b/layout/reftests/backgrounds/reftest.list @@ -169,7 +169,7 @@ fuzzy(50,500) fuzzy-if(skiaContent,51,320) fails-if(webrender) == attachment-loc # The next three tests are fuzzy due to bug 1128229. fuzzy(16,69) fuzzy-if(skiaContent,95,2206) == attachment-local-clipping-image-4.html attachment-local-clipping-image-4-ref.html fuzzy(16,69) fuzzy-if(skiaContent,95,2206) == attachment-local-clipping-image-5.html attachment-local-clipping-image-4-ref.html -fuzzy(80,500) fuzzy-if(skiaContent,109,908) fails-if(webrender) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html +fuzzy(80,500) fuzzy-if(skiaContent,118,929) fails-if(webrender) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html fuzzy-if(skiaContent,1,8) fuzzy-if(webrender,1,84) == background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html == background-repeat-large-area.html background-repeat-large-area-ref.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 1f8e551776c2..3750ba61f4cc 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -598,7 +598,7 @@ fails-if(Android&&!asyncPan) != 367247-l-hidden.html 367247-l-scroll.html == 367612-1e.html 367612-1-ref.html == 367612-1f.html 367612-1-ref.html != 367612-1g.html 367612-1-ref.html -fuzzy-if(skiaContent,1,28) fuzzy-if(winWidget||gtkWidget,32,34) == 368020-1.html 368020-1-ref.html +fuzzy(32,34) fuzzy-if(cocoaWidget,32,100) == 368020-1.html 368020-1-ref.html == 368020-2.html 368020-2-ref.html == 368020-3.html 368020-3-ref.html pref(layout.css.box-decoration-break.enabled,true) == 368020-5.html 368020-5-ref.html diff --git a/layout/reftests/css-break/reftest.list b/layout/reftests/css-break/reftest.list index 97b1fedbb7a2..bfdc1820ca3a 100644 --- a/layout/reftests/css-break/reftest.list +++ b/layout/reftests/css-break/reftest.list @@ -2,7 +2,7 @@ default-preferences pref(layout.css.box-decoration-break.enabled,true) == box-decoration-break-1.html box-decoration-break-1-ref.html fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,6-6,8490-8490) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html -fuzzy(45,460) fuzzy-if(skiaContent,57,439) fuzzy-if(Android,57,1330) fuzzy-if(styloVsGecko,45,1410) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543 +fuzzy(60,692) fuzzy-if(Android||cocoaWidget||winWidget,77,1764) fuzzy-if(styloVsGecko,45,1410) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543 random-if(!gtkWidget) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html == box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html diff --git a/layout/reftests/text-overflow/reftest.list b/layout/reftests/text-overflow/reftest.list index f1738d124185..bd5ebab79beb 100644 --- a/layout/reftests/text-overflow/reftest.list +++ b/layout/reftests/text-overflow/reftest.list @@ -22,7 +22,7 @@ skip-if(Android) fuzzy-if(skiaContent,1,5) == clipped-elements.html clipped-elem fuzzy-if(gtkWidget,10,32) == two-value-syntax.html two-value-syntax-ref.html == single-value.html single-value-ref.html fuzzy-if(gtkWidget,10,2) == atomic-under-marker.html atomic-under-marker-ref.html -fuzzy(1,2616) skip-if(Android) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) fails-if(gtkWidget) == xulscroll.html xulscroll-ref.html # gtkWidget:bug 1309107, bug 1328771 +fuzzy(1,2616) skip-if(Android) fuzzy-if(asyncPan&&!layersGPUAccelerated,255,12352) fuzzy-if(asyncPan&&layersGPUAccelerated,255,3771) fails-if(gtkWidget) == xulscroll.html xulscroll-ref.html # gtkWidget:bug 1309107, bug 1328771 == combobox-zoom.html combobox-zoom-ref.html == dynamic-change-1.html dynamic-change-1-ref.html == float-edges-1-ref.html float-edges-1-ref.html From 5e6b9c139d42bf8522e4b513d57e4777ebea0c59 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Thu, 16 Nov 2017 12:36:08 +0000 Subject: [PATCH 44/78] Bug 1417251, part 4 - Remove skip-if(styloVsGecko) for various reftests. r=bz MozReview-Commit-ID: AaI2hdIbn9D --- layout/reftests/bugs/reftest.list | 4 ++-- layout/reftests/scrolling/reftest.list | 4 ++-- layout/reftests/svg/smil/reftest.list | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 3750ba61f4cc..cb76110a9292 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -305,7 +305,7 @@ fuzzy-if(Android,3,50) fuzzy-if(skiaContent,1,133) == 273681-1.html 273681-1-ref == 283686-2.html 283686-2-ref.html == 283686-3.html about:blank == 289384-1.xhtml 289384-ref.xhtml -random-if(d2d) fuzzy-if(Android,8,1439) skip-if(styloVsGecko) HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 578114 for the d2d failures, bug 1354406 +random-if(d2d) fuzzy-if(Android,8,1439) HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 578114 for the d2d failures == 290129-1.html 290129-1-ref.html == 291078-1.html 291078-1-ref.html == 291078-2.html 291078-2-ref.html @@ -1473,7 +1473,7 @@ random == 536061.html 536061-ref.html # fixedpoint division in blur code makes t == 539323-3.html 539323-3-ref.html == 539880-1.html 539880-1-ref.html == 539880-1-dynamic.html 539880-1-ref.html -fuzzy-if(Android,12,1000) skip-if(styloVsGecko) == 539949-1.html#test2 539949-1-ref.html#test2 # bug 1354406 +fuzzy-if(Android,12,1000) == 539949-1.html#test2 539949-1-ref.html#test2 == 541382-1.html 541382-1-ref.html random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)||!haveTestPlugin) HTTP == 541406-1.html 541406-1-ref.html needs-focus != 542116-1.html 542116-1-ref.html diff --git a/layout/reftests/scrolling/reftest.list b/layout/reftests/scrolling/reftest.list index 585b974fae65..b961563136fd 100644 --- a/layout/reftests/scrolling/reftest.list +++ b/layout/reftests/scrolling/reftest.list @@ -1,4 +1,4 @@ -skip-if(styloVsGecko) HTTP == deferred-anchor.xhtml#d deferred-anchor-ref.xhtml#d # bug 1354406 +HTTP == deferred-anchor.xhtml#d deferred-anchor-ref.xhtml#d fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,23) == deferred-anchor2.xhtml deferred-anchor-ref.xhtml#d # bug 1182632 HTTP == fixed-1.html fixed-1.html?ref fuzzy-if(skiaContent,1,32200) HTTP == fixed-table-1.html fixed-table-1.html?ref @@ -22,7 +22,7 @@ pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.pr pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-10.html scroll-behavior-10.html?ref pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-textarea.html scroll-behavior-textarea.html?ref HTTP == simple-1.html simple-1.html?ref -skip-if(styloVsGecko) HTTP == subpixel-1.html#d subpixel-1-ref.html#d # bug 1354406 +HTTP == subpixel-1.html#d subpixel-1-ref.html#d fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref fuzzy-if(Android,4,120) HTTP == text-2.html?up text-2.html?ref fuzzy-if(d2d,1,4) fuzzy-if(webrender,0-1,0-42) HTTP == transformed-1.html transformed-1.html?ref diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index 8a32bbc2abee..34452bf2f095 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -142,7 +142,7 @@ random-if(webrender) == anim-marker-orient-02.svg lime.svg fuzzy-if(Android,4,1) == anim-svg-viewBox-01.svg lime.svg == anim-svg-viewBox-02.svg lime.svg == anim-svg-viewBox-03.svg lime.svg -skip-if(styloVsGecko) == anim-view-01.svg#view lime.svg # bug 1354406 +== anim-view-01.svg#view lime.svg # animate some preserveAspectRatio attributes == anim-feImage-preserveAspectRatio-01.svg lime.svg From eabc47f6c9cbe235499093dc669ef5290c906585 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 27 Nov 2017 11:42:15 +0000 Subject: [PATCH 45/78] Bug 1415068 - Include SVG text elements in elementsFromPoint. r=heycam MozReview-Commit-ID: FvMzOF6JJM1 --- dom/base/nsDocument.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index da8afd030a2d..b23d02a774e4 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3776,7 +3776,11 @@ nsDocument::ElementsFromPointHelper(float aX, float aY, // If this helper is called via ElementsFromPoint, we need to make sure // our frame is an element. Otherwise return whatever the top frame is // even if it isn't the top-painted element. - if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) { + // SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so + // if 'node' is a child of such an element then we need to manually defer + // to the parent here. + if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) && + !nsSVGUtils::IsInSVGTextSubtree(outFrames[i])) { continue; } node = node->GetParent(); From 67dfd1dd0040d45b288c7eecd1c4a7b3edbf6b1e Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Thu, 30 Nov 2017 13:43:15 +0000 Subject: [PATCH 46/78] Bug 1422979, part 1 - Increase bmpsuite reftest fuzz for HiDPI. r=njn --- .../test/reftest/bmp/bmpsuite/b/reftest.list | 4 ++-- .../test/reftest/bmp/bmpsuite/g/reftest.list | 24 +++++++++---------- .../test/reftest/bmp/bmpsuite/q/reftest.list | 10 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/image/test/reftest/bmp/bmpsuite/b/reftest.list b/image/test/reftest/bmp/bmpsuite/b/reftest.list index 2d8ef5c75732..074492713f70 100644 --- a/image/test/reftest/bmp/bmpsuite/b/reftest.list +++ b/image/test/reftest/bmp/bmpsuite/b/reftest.list @@ -10,7 +10,7 @@ # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # "Header incorrectly indicates that the bitmap is several GB in size." # [We accept it. So does Chromium.] -== badbitssize.bmp pal1.png +fuzzy(1,926) == badbitssize.bmp pal1.png # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 @@ -72,7 +72,7 @@ # allowed." # [We accept it. Chromium rejects it. Accepting seems better given that we can # decode it perfectly well.] -fuzzy(1,899) == rletopdown.bmp pal8.png +fuzzy(1,996) == rletopdown.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # "A file that has been truncated in the middle of the bitmap." diff --git a/image/test/reftest/bmp/bmpsuite/g/reftest.list b/image/test/reftest/bmp/bmpsuite/g/reftest.list index 9715b1ac82a2..bd4fb547e79d 100644 --- a/image/test/reftest/bmp/bmpsuite/g/reftest.list +++ b/image/test/reftest/bmp/bmpsuite/g/reftest.list @@ -5,12 +5,12 @@ # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # "1 bit/pixel paletted image, in which black is the first color in the # palette." -== pal1.bmp pal1.png +fuzzy(1,926) == pal1.bmp pal1.png # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # "1 bit/pixel paletted image, in which white is the first color in the # palette." -== pal1wb.bmp pal1.png +fuzzy(1,926) == pal1wb.bmp pal1.png # BMP: bihsize=40, 127 x 64, bpp=1, compression=0, colors=2 # "1 bit/pixel paletted image, with colors other than black and white." @@ -26,16 +26,16 @@ # BMP: bihsize=40, 127 x 64, bpp=8, compression=0, colors=252 # "Our standard paletted image, with 252 palette colors, and 8 bits/pixel." -fuzzy(1,899) == pal8.bmp pal8.png +fuzzy(1,996) == pal8.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=8, compression=0, colors=0 # "Every field that can be set to 0 is set to 0: pixels/meter=0; colors used=0 # (meaning the default 256); size-of-image=0." -fuzzy(1,899) == pal8-0.bmp pal8.png +fuzzy(1,996) == pal8-0.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=8, compression=1, colors=252 # "8-bit image that uses RLE compression." -fuzzy(1,899) == pal8rle.bmp pal8.png +fuzzy(1,996) == pal8rle.bmp pal8.png # BMP: bihsize=40, 126 x 63, bpp=8, compression=0, colors=252 # BMP: bihsize=40, 125 x 62, bpp=8, compression=0, colors=252 @@ -43,34 +43,34 @@ fuzzy(1,899) == pal8rle.bmp pal8.png # "Images with different widths and heights. In BMP format, rows are padded to # a multiple of four bytes, so we test all four possibilities." fuzzy(1,889) == pal8w126.bmp pal8w126.png -fuzzy(1,879) == pal8w125.bmp pal8w125.png -fuzzy(1,869) == pal8w124.bmp pal8w124.png +fuzzy(1,974) == pal8w125.bmp pal8w125.png +fuzzy(1,870) == pal8w124.bmp pal8w124.png # BMP: bihsize=40, 127 x -64, bpp=8, compression=0, colors=252 # "BMP images are normally stored from the bottom up, but there is a way to # store them from the top down." -fuzzy(1,899) == pal8topdown.bmp pal8.png +fuzzy(1,996) == pal8topdown.bmp pal8.png # BMP: bihsize=40, 127 x 32, bpp=8, compression=0, colors=252 # "An image with non-square pixels: the X pixels/meter is twice the Y # pixels/meter. Image editors can be expected to leave the image 'squashed'; # image viewers should consider stretching it to its correct proportions." # [We leave it squashed, as does Chromium.] -fuzzy(1,473) == pal8nonsquare.bmp pal8nonsquare-e.png +fuzzy(1,1462) == pal8nonsquare.bmp pal8nonsquare-e.png # BMP: bihsize=12, 127 x 64, bpp=8, compression=0, colors=0 # "An OS/2-style bitmap." -fuzzy(1,899) == pal8os2.bmp pal8.png +fuzzy(1,996) == pal8os2.bmp pal8.png # BMP: bihsize=108, 127 x 64, bpp=8, compression=0, colors=252 # "A v4 bitmap. I’m not sure that the gamma and chromaticity values in this # file are sensible, because I can’t find any detailed documentation of them." -fuzzy(1,899) == pal8v4.bmp pal8.png +fuzzy(1,996) == pal8v4.bmp pal8.png # BMP: bihsize=124, 127 x 64, bpp=8, compression=0, colors=252 # "A v5 bitmap. Version 5 has additional colorspace options over v4, so it is # easier to create, and ought to be more portable." -fuzzy(1,899) == pal8v5.bmp pal8.png +fuzzy(1,996) == pal8v5.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=16, compression=0, colors=0 # "A 16-bit image with the default color format: 5 bits each for red, green, and diff --git a/image/test/reftest/bmp/bmpsuite/q/reftest.list b/image/test/reftest/bmp/bmpsuite/q/reftest.list index 80da580cc8f8..f915c41cae66 100644 --- a/image/test/reftest/bmp/bmpsuite/q/reftest.list +++ b/image/test/reftest/bmp/bmpsuite/q/reftest.list @@ -7,7 +7,7 @@ # documentation says that 1-bpp images have a palette size of 2 (not 'up to # 2'), but it would be silly for a viewer not to support a size of 1." # [We accept it. So does Chromium.] -== pal1p1.bmp pal1p1.png +fuzzy(1,926) == pal1p1.bmp pal1p1.png # BMP: bihsize=40, 127 x 64, bpp=2, compression=0, colors=4 # "A paletted image with 2 bits/pixel. Usually only 1, 4, and 8 are allowed, @@ -32,14 +32,14 @@ # "A file with some unused bytes between the palette and the image. This is # probably valid, but I’m not 100% sure." # [We accept it. So does Chromium.] -fuzzy(1,899) == pal8offs.bmp pal8.png +fuzzy(1,996) == pal8offs.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=8, compression=0, colors=300 # "An 8-bit image with 300 palette colors. This may be invalid, because the # documentation could be interpreted to imply that 8-bit images aren’t allowed # to have more than 256 colors." # [We accept it. So does Chromium.] -fuzzy(1,899) == pal8oversizepal.bmp pal8.png +fuzzy(1,996) == pal8oversizepal.bmp pal8.png # BMP: bihsize=12, 127 x 64, bpp=8, compression=0, colors=0 # "An OS/2v1 with a less-than-full-sized palette. Probably not valid, but such @@ -52,12 +52,12 @@ fuzzy(1,899) == pal8oversizepal.bmp pal8.png # BMP: bihsize=64, 127 x 64, bpp=8, compression=0, colors=252 # "My attempt to make an OS/2v2 bitmap." # [We accept it. So does Chromium.] -fuzzy(1,899) == pal8os2v2.bmp pal8.png +fuzzy(1,996) == pal8os2v2.bmp pal8.png # BMP: bihsize=16, 127 x 64, bpp=8, compression=0, colors=0 # "An OS/2v2 bitmap whose header has only 16 bytes, instead of the full 64." # [We accept it. So does Chromium.] -fuzzy(1,899) == pal8os2v2-16.bmp pal8.png +fuzzy(1,996) == pal8os2v2-16.bmp pal8.png # BMP: bihsize=40, 127 x 64, bpp=16, compression=3, colors=0 # "An unusual and silly 16-bit image, with 2 red bits, 3 green bits, and 1 blue From 074c9f99da9d0a4b8774ebebd3b3bbbb7d081e32 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 1 Dec 2017 23:45:17 +0000 Subject: [PATCH 47/78] Bug 1422979, part 2 - Update the bmpsuite test suite to version 2.5. r=njn --- .../test/reftest/bmp/bmpsuite/README.mozilla | 1 + image/test/reftest/bmp/bmpsuite/b/badrle4.bmp | Bin 0 -> 4326 bytes image/test/reftest/bmp/bmpsuite/b/badrle4.png | Bin 0 -> 245 bytes .../reftest/bmp/bmpsuite/b/badrle4bis.bmp | Bin 0 -> 4326 bytes .../reftest/bmp/bmpsuite/b/badrle4bis.png | Bin 0 -> 880 bytes .../reftest/bmp/bmpsuite/b/badrle4ter.bmp | Bin 0 -> 4326 bytes .../reftest/bmp/bmpsuite/b/badrle4ter.png | Bin 0 -> 883 bytes .../test/reftest/bmp/bmpsuite/b/badrlebis.bmp | Bin 0 -> 9212 bytes .../test/reftest/bmp/bmpsuite/b/badrlebis.png | Bin 0 -> 1626 bytes .../test/reftest/bmp/bmpsuite/b/badrleter.bmp | Bin 0 -> 9212 bytes .../test/reftest/bmp/bmpsuite/b/badrleter.png | Bin 0 -> 1628 bytes .../test/reftest/bmp/bmpsuite/b/reftest.list | 26 ++++ .../test/reftest/bmp/bmpsuite/b/rgb16-880.bmp | Bin 0 -> 16450 bytes .../test/reftest/bmp/bmpsuite/b/rgb16-880.png | Bin 0 -> 1029 bytes image/test/reftest/bmp/bmpsuite/g/pal4gs.bmp | Bin 0 -> 4198 bytes image/test/reftest/bmp/bmpsuite/g/pal4gs.png | Bin 0 -> 2016 bytes image/test/reftest/bmp/bmpsuite/g/pal8gs.bmp | Bin 0 -> 9254 bytes image/test/reftest/bmp/bmpsuite/g/pal8gs.png | Bin 0 -> 9441 bytes image/test/reftest/bmp/bmpsuite/g/pal8v4.bmp | Bin 9322 -> 9322 bytes .../test/reftest/bmp/bmpsuite/g/reftest.list | 16 +++ .../reftest/bmp/bmpsuite/g/rgb16bfdef.bmp | Bin 0 -> 16450 bytes .../reftest/bmp/bmpsuite/g/rgb32bfdef.bmp | Bin 0 -> 32578 bytes .../test/reftest/bmp/bmpsuite/q/pal1huff.bmp | Bin 0 -> 2151 bytes .../test/reftest/bmp/bmpsuite/q/pal2color.bmp | Bin 0 -> 2118 bytes .../reftest/bmp/bmpsuite/q/pal4rlecut.bmp | Bin 0 -> 3610 bytes .../reftest/bmp/bmpsuite/q/pal4rlecut.png | Bin 0 -> 1918 bytes .../reftest/bmp/bmpsuite/q/pal8os2-hs.bmp | Bin 0 -> 8986 bytes .../reftest/bmp/bmpsuite/q/pal8os2-sz.bmp | Bin 0 -> 8986 bytes .../reftest/bmp/bmpsuite/q/pal8os2v2-40sz.bmp | Bin 0 -> 9254 bytes .../reftest/bmp/bmpsuite/q/pal8os2v2-sz.bmp | Bin 0 -> 9278 bytes .../reftest/bmp/bmpsuite/q/pal8rlecut.bmp | Bin 0 -> 7980 bytes .../reftest/bmp/bmpsuite/q/pal8rlecut.png | Bin 0 -> 3524 bytes .../test/reftest/bmp/bmpsuite/q/reftest.list | 111 ++++++++++++++++++ .../reftest/bmp/bmpsuite/q/rgb16-3103.bmp | Bin 0 -> 16450 bytes .../reftest/bmp/bmpsuite/q/rgb16-3103.png | Bin 0 -> 3347 bytes image/test/reftest/bmp/bmpsuite/q/rgb16.png | Bin 0 -> 1177 bytes .../reftest/bmp/bmpsuite/q/rgb16faketrns.bmp | Bin 0 -> 16438 bytes .../reftest/bmp/bmpsuite/q/rgb24prof2.bmp | Bin 0 -> 25254 bytes .../reftest/bmp/bmpsuite/q/rgb24prof2.png | Bin 0 -> 19650 bytes .../reftest/bmp/bmpsuite/q/rgb32-7187.bmp | Bin 0 -> 32578 bytes .../reftest/bmp/bmpsuite/q/rgb32-7187.png | Bin 0 -> 2136 bytes .../reftest/bmp/bmpsuite/q/rgb32-xbgr.bmp | Bin 0 -> 32650 bytes .../test/reftest/bmp/bmpsuite/q/rgb32h52.bmp | Bin 0 -> 32578 bytes .../reftest/bmp/bmpsuite/q/rgba16-1924.bmp | Bin 0 -> 16522 bytes .../reftest/bmp/bmpsuite/q/rgba16-1924.png | Bin 0 -> 2811 bytes .../reftest/bmp/bmpsuite/q/rgba16-5551.bmp | Bin 0 -> 16522 bytes .../reftest/bmp/bmpsuite/q/rgba16-5551.png | Bin 0 -> 1226 bytes .../reftest/bmp/bmpsuite/q/rgba32-1010102.bmp | Bin 0 -> 32650 bytes .../reftest/bmp/bmpsuite/q/rgba32-1010102.png | Bin 0 -> 1253 bytes .../reftest/bmp/bmpsuite/q/rgba32-61754.bmp | Bin 0 -> 32650 bytes .../reftest/bmp/bmpsuite/q/rgba32-61754.png | Bin 0 -> 2483 bytes .../reftest/bmp/bmpsuite/q/rgba32-81284.bmp | Bin 0 -> 32650 bytes .../reftest/bmp/bmpsuite/q/rgba32-81284.png | Bin 0 -> 2182 bytes .../test/reftest/bmp/bmpsuite/q/rgba32h56.bmp | Bin 0 -> 32582 bytes image/test/reftest/bmp/bmpsuite/reftest.list | 1 + image/test/reftest/bmp/bmpsuite/x/ba-bm.bmp | Bin 0 -> 9000 bytes .../test/reftest/bmp/bmpsuite/x/reftest.list | 10 ++ .../test/reftest/bmp/bmpsuite/x/wrapper.html | 28 +++++ 58 files changed, 193 insertions(+) create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4.png create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4bis.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4bis.png create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4ter.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrle4ter.png create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrlebis.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrlebis.png create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrleter.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/badrleter.png create mode 100644 image/test/reftest/bmp/bmpsuite/b/rgb16-880.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/b/rgb16-880.png create mode 100644 image/test/reftest/bmp/bmpsuite/g/pal4gs.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/g/pal4gs.png create mode 100644 image/test/reftest/bmp/bmpsuite/g/pal8gs.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/g/pal8gs.png create mode 100644 image/test/reftest/bmp/bmpsuite/g/rgb16bfdef.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/g/rgb32bfdef.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal1huff.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal2color.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal4rlecut.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal4rlecut.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8os2-hs.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8os2-sz.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8os2v2-40sz.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8os2v2-sz.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8rlecut.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/pal8rlecut.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb16-3103.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb16-3103.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb16.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb16faketrns.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb24prof2.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb24prof2.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb32-7187.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb32-7187.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb32-xbgr.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgb32h52.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba16-1924.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba16-1924.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba16-5551.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba16-5551.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-61754.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-61754.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-81284.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32-81284.png create mode 100644 image/test/reftest/bmp/bmpsuite/q/rgba32h56.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/x/ba-bm.bmp create mode 100644 image/test/reftest/bmp/bmpsuite/x/reftest.list create mode 100644 image/test/reftest/bmp/bmpsuite/x/wrapper.html diff --git a/image/test/reftest/bmp/bmpsuite/README.mozilla b/image/test/reftest/bmp/bmpsuite/README.mozilla index 87d185e897fb..f59a494c263d 100644 --- a/image/test/reftest/bmp/bmpsuite/README.mozilla +++ b/image/test/reftest/bmp/bmpsuite/README.mozilla @@ -20,6 +20,7 @@ There are three sub-directories. - g/: for "good" images. - q/: for "questionable" images. - b/: for "bad" images. +- x/: for images that arguably may not truly be in "BMP format". Each file listed in a reftest.list file is annotated with the following lines. diff --git a/image/test/reftest/bmp/bmpsuite/b/badrle4.bmp b/image/test/reftest/bmp/bmpsuite/b/badrle4.bmp new file mode 100644 index 0000000000000000000000000000000000000000..632787a730fb44640ed6ee6c427495db6ed95433 GIT binary patch literal 4326 zcmb_e&rc&q7JhNt)oq&rmz%M%GYQy>v{94;Rs)hvOFPCsMYGaK$Tuz#5eLL!=VTE@ zN_-09gyloz(Hz{}hJAOiaD$iKmUudDqFl1Wy(6y>h2>VEaU?|tu8 zZ~pVQ6a{~K`27t1JM_osA5oMdxPFUuGpYIm-1FZpeIHzT6b!$|75{X5t1kGx#UrC15qdR=b11Dv8!%i^Sn@zEe%sm7R1DNC|lYL`04$qmv@-^C^A zE!j%VEl}pLB~3YRqODSLor=4Ayz{u_V1T_Bytg$V<8)qPZ`mRvK*jSlYln7tu}**Yc6N82(nM=JD+>FEqe+X?l$&_# z&!RhyERX)D=suLPis(9}PUnn_4JzNoH#l>qFISdloYjgj9o@SFQ@=hrET9C6%ZJ+GBjr)W9EKFn3NvJfEy%qt5R9UY?LC~Hd&~+MoE1uqg#y$#Psjk*1-WpQedDNuGebJA8YQI*b^dFtNM&EjPb$-u zL~%H$-ik}h9ikPo(x;@F*Ja3^DIY+6dT_3VycLj*Rj1^^8#oimL~!iFw=gz3{1`?& zH;R3r!E6M%h4W5#QR9LhIlimD3~~!~OnD(>pRL9t$WKPA%Rqa`9H=S>-RXsRl{B{( zx78a86%G0C_o&JXx_cdQHOw2ZmBxvz z8f(og6siSqR&{%N=qWV(v$bZ~O2a@_K^Pe0Fkgf`2^k&E=+bEi&uB7kli2@Ctt;=t z3OA*PVy#*gt5Q}tIUlEThNTKMpq%&n zSw?`dF3W0HFzVL5-AmBIyT_z;%-+rZUL3qp925^j{!vF~rQJWt=GgC`s)n40&qbwe zR5TCj^@Ph_!)fLAYB@wds>fjlafTyL1ApPyi0h&Dq(z9D;&@Wc!TDf%KqELOZ0$s3 zNn>UNTM3v9GhvE9Fc|QMY@3xPie%m;(I=6WS-eIuri;%rdW6-G9KPPwX;uoG)u`K) zehWsjd6~}<0-CSb6AiD;q@z`V&j+{W(L-b}&PmfHqd${Hu5|F}r7FlB9nIO=&xtkC zyr-tZmTcoI%k5!)ZxM)$G?itwRkIw{h-yJ!npQ|1Q2oy0_&$owF&UaN+CnQH8Kt+~1!-#wbH{2BY(?NZe%B zgr~oq$wu2ondi<)T&65a21Kq1RQ_R2Vtlh}X^-O;Z*!GmWT;GmdRRfa8#%)UOT~0C zy*yuEt*?Grd29RcGixNgYmdY={iB&}W)E`Ra<}}P*Y?ZEkBPoAiO~HJ!u%g2q*$?t zs$4`A>-4~_pZhod8O^g7TOJwL6l*rI-i)?I7wg?C{}St{E_^O%os~p@dRQO+IIhU} zQTFIA#XPqM_w^!G(#y*LUkCgr{`T$|Fvb}S`w6z+hh|`( z#D#x>Q387;c#l5x<q9nc@MSBwp1O6X^ye;LC^$c$0Y3{arT70UKtqxLJI;`Ev8TmwZB+e->|$4hv7` bwkhNP+B|4}11G+HPRSQk^!v!3HFaC$7i_QhPjI978JRyuED5*I>ZI;%IMc zZ_diQpkdL16Ryi7VzVPUIy|<8)-gO%w!?w`-8;qaP;#ED)XB9G?ao*s!AYYr1xIjooiQ3ET$f5-kC?Dx9bKY(PC)hPkns}f?W37>?Z;^;TUyHqM{18| z*687O-a^Zb8c_mP6Ku8953Q1D9kj?e7>?KK+ptWFwsos9W=qPFbW3ijeUw}${p4L- zlG&22^vnXyAGD+;XDzf9O0H3HXP0*#w;T+x_Xh86_sKl5B=(jqGDB26UbDB!+#%gs zHcOf2K}jF!i%r7_;z1Y(63wv%mJA(oPo*mZ=N=W6zc zP#1vO!_L8xu}8;?N5&d`9&GRIc%_Nvddmv?ht;IT+mu^a z^;_zWmF2ttN!>Xqt5DY~wcDp;u2cE?9!@D;S;ZT*8s1o=&n_l$x}NJD{a%e1?c0iy z=p!5qi*zq-=CzjaDAszdG>^0uyLpQ+=L(NI>2CMGQ&-rUHSSX!RMstRS79DyYk%63-ek6FyPw`ukRDptT($hd;lkma zJpF^MwSgm7fOZ^lemm%&(wx*#6TGg8-0)t%ekzY}r+drb^NTfS&Dq(t^W4hA1{0IH zIS(mHr7q{3I6OF{X^Bjf6tlO@9Y#ThETmbjgo+liu1V5d((%6MNg}O9ngn;TRu=h* zCo*NJnvTZTD1{vH{3DUq%BoYW9AO{kDqHynQ5?)G3%ENxK*dqk>{4hA^GID|C&xU* zZ1(MwAr}F-_&I#*W32is`;eDvRa%C$xI=4RWbI2irl&k6Xfk^hx^%b_sKFBgDa0!d zN8VXu@3y}|oeuqya14ctB?^92e(3UD5=u$+H1h90cL+zu{iTF@YtC-Ia;r_|z4DLJ zvR|^%?)k}#_Qsyx;c-q?N}Gdf%p|+4n1rwI5Wxl$7Af)jIUR)?hutXR;y4YI4$Ou< z!G^6+Yf|*?F)L(vR;Xw!6cCNGgbeU~kPBDYH&z{-#ii$~yOi`1oqrieq_Uru4=T4S ziQ;fZtx8DSAEFhq(xaprH)X_KMt+3o6XCfQ@m4@KR=rXHZ{Un}L|6phB5h3gF;qM^ z(msqqmval}o$yiP!ajw?!&AQvqSzW3u{q;_PRJgQ{f%>#pG_IY9x?|}wSc%2g?N>8 zzZ-Yd3WW+0R*5Va63*~i^*mz7RX(cR(d0tJk@|K}NJb3Xw%R?0)0MrrdmV8#%p0(k z!HKLI$C_0rR14s&>UTxtDPs61d)0PS3A-;f+{{gVzvC^c?XTG9G=UC~q`5Yg4QpGEJ#!h+xPyMCq>*P6hqv zq27*KHKm@DvT~eNK1L&^jCj{%(xKRH`(R(rJ zm=!HR^ah5{Uc+hS_DVTIKdQ%3262WXPeXs<*NE$3?4@mjYKr5@Xb#T@D*&2joD+_A zY-CAiW}dN?kjW?$Zt({OL;hUGVxdUpeKL9^vNFqgWH4QX^Nb!~HX?_wcjI(k8V<*v za47Q@jAZjNpCg=}`HC~quq>6)tHuh_x)#tZvKQy1<&)V4 z=T?nCyiH|w*DUkc85tKJ+ms54TsBbohc$`uO|Qy;b&KQkK_NqB3ZsW3NcdBy*x+F? zQ_L*Q)>rB)Usc{ZgZFc*B!a7ej4S$6GuzDW=Q`z1`8m(+m60D4eR&e0{~?6=Uq?tr zv52Z%go<^V_UmVZ>%l2m?8Rn4<`o&uCgz*EYjiN*xg1{X# zj2~r>Ku`vL7w(H9RWeIU0AC0E$AkBcRn@jux1519c#EB>oZa6C1s#y^Vz7r2G1-`e z|AWp&eZ#Dicy$KAgSm!rE(WQl{)5>e@#d1p5M7bZ)4!oJJ=~ql5!~YphW!NF?;&Pj zpTzm#9Cr!qk>EXgh_3-H-hyUV7~J>8!bag`^=0*ye-@ktgX_j-WAmI_z-@5VeEP%F zWB1s7<6j1s!QcXC^JMEHN3lE96|d@NWDYFey>X5^MhI2Ko6aSk@ReENitZzlHDBPX z(??w}iX>;3zF5kC_4x5JnP;Rsr$&PktBtJ&p3gN$Y=vV@lDYkF_w&zRyukP+#i3ut zbYLmq4!fs{2Y8ZvRaZ>N1|{Lkhzod=eAD?wHp(FzRvvZG literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/b/badrle4bis.png b/image/test/reftest/bmp/bmpsuite/b/badrle4bis.png new file mode 100644 index 0000000000000000000000000000000000000000..7d0d95871a310e70441f0cc8c3766883cc62705e GIT binary patch literal 880 zcmeAS@N?(olHy`uVBq!ia0vp^^+4>v!3HFaC$7k5U|=@)ba4!+nDchV!=kqe94)ma z_vidxd|V_vz~{>3op;Z@DOiy!^d};7R*;v|-^kj(j2efQbIez&>H46FpYcEc^=p0y zK|9wUA`gRG{0T7XqvPutz-FF1EFN>Bbc)W0+MhrFbSlli)AcItPu2DCJ;5c;GbUN- z^KMvOyqC>X#nWZepCgOTo#%gld-dzhdY`0ie^ysIyr?)i<9eTYF{_+E!^GLrGA!}) z(piH3&&g|<^5A>Zb;*POS@nJ6OdHI6W}QrRmAn6w&I-(7rv)qL*hCskK|?n(%L^yJh>u^+DI+}Wf1zWX(Z ze(j!YKkMh+zj0gSv?5CVre`Pzg`!@uo-)2bNTh(yz-@4z@2hPkd-&4=jF=xildz=MX&#KiATsd?6_52ggtg|d9QSOn%%KbbRRmDl%azBWkaN_fs<@Sm;X>Y3>)f8GB*&;Ha$77xeg(Xt_)b7Cy-G3`I`^TmV9 z#`Pzw?c)Bsz`Q&=y^jCGT?^yS<$IssFL@)KZa0U)_IUn`pA&@>o}bzObzc8yq e#>H<(>ZeqmEc|+HNe3_|F?hQAxvX<>JxZ_9 z!=1c^wlHi&3Cx<{S=+sERuYY)1{p_#@!IANW~N2knrMvKlCmV-l3Qvulk23PzKcsT z8?uo}&(qvdLt1jiLR+Ea8Wnf>V<9Nw=2G zl4c8wI~lgciwjAqQn)rFO-Rxkne05Heo)$9+U zE&#QMm4l|SPU}JM^t8ky+#1|oA3Y79sFs39#u|Md?CkD(rLpFE%L@C4)uhEq$}PP0 zTk4LK<+J}u-B~HCP}eK9T4!XgQ~Bl|PAOek#S^s}o>-&LF2`}Yp6fM#uf~h^q@pBx zu!CWd?q$rp)({>|HC`*tBW=ZA-Xh#{g~y$AxAWhrD{Rdg^%$GVn$R}R+KLiV1mb0Q zt%l*j*?)(#^KkZq6}qRdi${mJKdi69Jj~YKv?aaqY}NKYy{8~Ow6M5p`T67d<2!l! z2U}|a$3g+xvE%$!&^x19si7u#9TU0Xy?*^vHgTqV%i!~iHD}G)-Lv!D%7ca}#&dHH zQj|&^&N*>>bWGC{nJ8(>-Z6I>1sSrCVYL#TMhfwY z<6(5xD7u|*P^ZJVBmzUBVu^wuRUEo}mxNMMT@B;A&mHW@xWAN8Z_U}uS0>tc-mCa1 zBYPzq?OvSBXm=Fp9Uf;@rL@^pBPKay#Ux^V2L~HWSfs@7=X7W{w%yQiaqI?42WG<_ zW5ZUcH7UCHm=!YIE0h}x1-NmBkO96AauF)W#;Sv}xb%E=mXbc)`InI+)%+RxpfXuW z6o+Z`RzlkT0IiUfE+y5tDI@N(@*}uUg!@{=TLIZv^-2MvK`>e~hL;dqq>YI<28u5X zwGTb$#*kas?}U#U7xq~5uKSwEEz~g;g^*pI^=~ocC!;lG=sjc(Ts05hi9)J5bo4y!~n7!twoyXtz#j;mr+xuePXh$HpwppXn1wr#cg6n0mR;_h+C)gW)cRu((5 zYV0+uP^cEbS=H}|C{lR%CwtX)l!t+=0vUMrLB0ri5-~a$F{S5*&uB94kl6oP-B-~^ z6>h?BsR56OP~nYOiG$Y=OLQIaiq#|Mk*=cA=uJLSdm9Q)5KM(YF z)LSuios^Z6oQg3TF=fQNCX)_L?L{##`}9dxX7P!&Vpr@+S>fb-oX8orD%?OH$!<5t z2ykx7vRW04`kTS-C1_zD4QV$yy7VvU5Daxtnhp3z9ZgH8dy-q=xWlR%aUMN4B^|S( z1#oX*_#8EYR$;G{BlN?19Ayw^IPx@%7jX@_9(XTf6I4?ikGnbCAFKdqo^eh%+KG`P zotb&YvxH1WnJ~d07!3JCwxy+oBANHe=#t3E9PfDs)5ZH4J;2?F96sLl>6|nicB618 z`xcDk@-n|b*gf+VXRP71R93GVD@f~FKr_f*?30#HW;c~XuC!)kMpckIK1w^22An1$NL4y4M{tbKu`?wQRoy2+1fG zQI(5Oag(O~&GY_E|BNh-Vk;o?nv8=3+&|D=qmBFRtNtbKqq^|EptqJ2A?iVW_;p>8 z@x$y9FlFF(5WXlf zpTtG~0%r-Vk>EAD@K>J}C!pCC2KRljuu*tfeOZ0wp9kkb|E9iG-@4!yaO+7y2w!^Ulk_(659_F9Fc!BXNibKDO z>0qXSJLWx8JRp+ft4+m(tWy%P47orw$v5p^WTPCiVHJxT#g`i|H_kiB$E5eq;&lX3 ezj%_~rn&yV4h|2#MG)UTr{oJN_WSoTI`$tdv3Jn` literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/b/badrle4ter.png b/image/test/reftest/bmp/bmpsuite/b/badrle4ter.png new file mode 100644 index 0000000000000000000000000000000000000000..230bb60f478ba8dd2625d53ddcc3c16d386218d3 GIT binary patch literal 883 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̚t zc%(1#TZzy8I&s-s5-(o;w0SoYXkT5T{V0ixcGTmZX#Z-{SDW^s{iF;cztneW;x5|n z=I-)!(f(tJ_NOE++EGth-0y4mlm3u;N&E{U2cmIX(ZKb*dL_3^c*I6gP`BKrczr6o)EmIB4T?&8P5`;ydmeKd8^ z%?OmE+bF+_a&+aEg4BQd?bJoHpZEJ8MfpH98i}q(`8H>#;~YJF^yvA~ix;uq!+sCt zm=DHY1JQSqacM&A7xTe*y>$Qn)cqf^;o;+}j~_q1_Vnqqxfd_6{}uMX!iEgeS4?m+ z{^YHm*Z_l=zuf*p18Dz>1{8b%Vc7@c#77!H`%4-yZ(QJ=T;M{1|5y z+T}Rk00x&p=bJq z+WZRLuV3-i_e6^cMbX)euX`YTnw z@2h?LPBQdKRetH@rAv1ixL+ggi}w$Iq;wA-{{+F~7f)Y2gW!dJrD~V(U()s`Kk-rf J-sFGq`5&B0v?2fi literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/b/badrlebis.png b/image/test/reftest/bmp/bmpsuite/b/badrlebis.png new file mode 100644 index 0000000000000000000000000000000000000000..f0094a1156e71ed95ddfc9ef0041ff9a660f3aa3 GIT binary patch literal 1626 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 b{3!&_^-EQ|g#VJZKlzQ1+IJ@ZgU|l}4h*!M literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/b/badrleter.png b/image/test/reftest/bmp/bmpsuite/b/badrleter.png new file mode 100644 index 0000000000000000000000000000000000000000..359bc12e12293a0e060e1ea9d0e8c8b58de9ac2d GIT binary patch literal 1628 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/g/pal4gs.bmp b/image/test/reftest/bmp/bmpsuite/g/pal4gs.bmp new file mode 100644 index 0000000000000000000000000000000000000000..813268ca77f3542b3442fec85f892a17f819b5d1 GIT binary patch literal 4198 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/g/pal8v4.bmp b/image/test/reftest/bmp/bmpsuite/g/pal8v4.bmp index 7064be31541f5669f61a5c49214a31fca7971087..34ebb8030c5f579b9f2a5fdcf95e506fa9e65cf3 100644 GIT binary patch delta 22 YcmaFm@ycUD9=EYE69W`)to*1909MZiOV diff --git a/image/test/reftest/bmp/bmpsuite/g/reftest.list b/image/test/reftest/bmp/bmpsuite/g/reftest.list index bd4fb547e79d..505b69010d39 100644 --- a/image/test/reftest/bmp/bmpsuite/g/reftest.list +++ b/image/test/reftest/bmp/bmpsuite/g/reftest.list @@ -20,6 +20,10 @@ fuzzy(1,926) == pal1wb.bmp pal1.png # "Paletted image with 12 palette colors, and 4 bits/pixel." == pal4.bmp pal4.png +# BMP: bihsize=40, 127 x 64, bpp=4, compression=0, colors=12 +# "Paletted image with 12 grayscale palette colors, and 4 bits/pixel." +== pal4gs.bmp pal4gs.png + # BMP: bihsize=40, 127 x 64, bpp=4, compression=2, colors=12 # "4-bit image that uses RLE compression." == pal4rle.bmp pal4.png @@ -33,6 +37,10 @@ fuzzy(1,996) == pal8.bmp pal8.png # (meaning the default 256); size-of-image=0." fuzzy(1,996) == pal8-0.bmp pal8.png +# BMP: bihsize=40, 127 x 64, bpp=8, compression=0, colors=252 +# "An 8-bit image with a palette of 252 grayscale colors." +== pal8gs.bmp pal8gs.png + # BMP: bihsize=40, 127 x 64, bpp=8, compression=1, colors=252 # "8-bit image that uses RLE compression." fuzzy(1,996) == pal8rle.bmp pal8.png @@ -78,6 +86,10 @@ fuzzy(1,996) == pal8v5.bmp pal8.png # pure white: (255,255,255), not (248,248,248)." fuzzy(1,1296) == rgb16.bmp rgb16.png +# BMP: bihsize=40, 127 x 64, bpp=16, compression=3, colors=0 +# "Same format as rgb16.bmp, but with a BITFIELDS segment." +fuzzy(1,1296) == rgb16bfdef.bmp rgb16.png + # BMP: bihsize=40, 127 x 64, bpp=16, compression=3, colors=0 # "A 16-bit image with a BITFIELDS segment indicating 5 red, 6 green, and 5 blue # bits. This is a standard 16-bit format, even supported by old versions of @@ -104,6 +116,10 @@ fuzzy(1,1516) == rgb16.bmp rgb16.png # The unused bits are set to 0." == rgb32.bmp rgb24.png +# BMP: bihsize=40, 127 x 64, bpp=32, compression=3, colors=0 +# "Same format as rgb32.bmp, but with a BITFIELDS segment." +== rgb32bfdef.bmp rgb24.png + # BMP: bihsize=40, 127 x 64, bpp=32, compression=3, colors=0 # "A 32-bit image with a BITFIELDS segment. As usual, there are 8 bits per color # channel, and 8 unused bits. But the color channels are in an unusual order, diff --git a/image/test/reftest/bmp/bmpsuite/g/rgb16bfdef.bmp b/image/test/reftest/bmp/bmpsuite/g/rgb16bfdef.bmp new file mode 100644 index 0000000000000000000000000000000000000000..30fe8bb8d6c920dda30b65f074ed9ef85945a610 GIT binary patch literal 16450 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal1huff.bmp b/image/test/reftest/bmp/bmpsuite/q/pal1huff.bmp new file mode 100644 index 0000000000000000000000000000000000000000..790a4836978c9e42be17c2c902762d0ca3426536 GIT binary patch literal 2151 zcmah~UuauZ7(Yq>BUbH)17k-m(*sND*RRpt##Fq4YS z)ZIR`iJ%Wd3ijfIeGu(@ak5NOQWB7{g)K_a+2t-WVx0AsDhBgVzwexT^QYW8FL3#9Do*VFMieep~k6GNj#R}1Z9xMYapN6Y{(32Db=1oZZwF7&f?bU6aM!6 zqzVilGwv)UzGpPna{Pa~Ud9$<`3cW(Byrn5H65DoP043I}fh6F`gx*t{Bz=y}&bWbPJu zav>T;xoCJO0af61oyx!vK@uDTkmWcoL;QqR4Uk(oRv#-5VXCd}3XD2BV$n*9~3xHg3*Ts$W2> zEz@ZzyA<|G^ZgK43Vk<Y#7gOqB1L-T9!IN4bRQ; zQ_OdD408kErI9D4cC%k z`M5#A>-F_S<9TV#%26*mqHod-@`20nu+17+$14`D*1-GjH>`m)JFC`48O6N4B`Xnc zEcW*D9-YG2%Me(jPL9;c;kdxziBu$4zqW#RDeeY{hbD~~gL}oGS^~8gF{??T-er@@ zQBNPtA+xtFYN46LB}nyc*7G$HaTplBj?gJcOogf6S1wQC9O{L^=pB?3c zkt0nVJk@BEyaU~&+NS9f)&oa;EQXW7Ur$qMg~LV5bV>`#G@|TPAut<)KB%ZIA^_@h o^dfp8cFPZS{#G#8_tF>s118I9KmY&$ literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal2color.bmp b/image/test/reftest/bmp/bmpsuite/q/pal2color.bmp new file mode 100644 index 0000000000000000000000000000000000000000..27fe7276bc7c58cbaf5e99f84f9adbe19ed707d2 GIT binary patch literal 2118 zcmZ{k!D`z|6oyBM33cuT*<4&`_l4p`=>rI^xKX+*q@WS?t_w?C{1Ug27wDn_$*a2v zCLTi`z_W;Bzl|4)_6b%0e@2qsI6bmtjmN*f^Pf4Ie?ESAAxwP0^)1f-aQZkMafB-Z z^MCze&yM}2zE+~bT~*=g3}mE~#;`AClyID=#jUgviq82cpWYOr69l-+1+S{Omm@hs zd=`TLZ}c(|LULTS`XEU2>9!Dl5XjW%bLflI#&^S?Q17(-$us;;!)HF=6P}OqJ$?`R zxaRAYPi#Id3cu$2!FhVXH~GW3j3x*E=@mKdP(~Njth>$s_%3lz|2O_IFW&sZXZ^a+U#{T)Zwwy0<^dVfkHF{r3G{A> z{5fA8MG0bzu0#IhAK&Hf3(pg%KOXPiSv~Mg{mR4@B|ahNkMC-}q<*u`^KbBfygpmm z`r~Pg407@}Q8f03u6rs z)3NIh{dyamfp7Brg%5w?aftdmTt5ulnr~h@wP8id*MGh%g5bTWUu747`J?@9e=4C= zuP;pf9#4`P;!5(RQ+&K|)X^4tAA1Jk)jt0#e;KM%rJTV<$ib1ZKN+)pQ|yJC|>JbkQ UM*keYvI+dC?E6sM&uv`)1mB9@&;S4c literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal4rlecut.bmp b/image/test/reftest/bmp/bmpsuite/q/pal4rlecut.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2f32d1d7add527b6d6d80c4629aadd24f2437c9d GIT binary patch literal 3610 zcmb_e&ubf35FYPp-%3`jYP7Ls$!TM$ftHd(mbDF%7aB!Al@X+S^gx2FHJ-vOJH{Unk%+sA8$~lVv zT6p~$<7bT97;}^&1Mf!*L{3%@Oyj>ZdNQ8H(y9c>VL>YL=h?m1b?1fZ& zuT9q8XuY}hkgRRG?m07-M{EwW%N@7lb`Rz^$vi$vGudr!=X1-nwAW^buR5?bn%|)6 zc1up4SP2dUfhA{q1G0}D#@XB^J4QiSH})(zGE8PWUsx6_vfgkfXi8IfG#YrE?|K>! zGtI;27?C_{dY56U08@aIqXTP`HlxAuu`4TLZLw#2kK!E-H@aha1?@Y+}a-effSGMxwwcG+mFfKu&eYC2b{Rdxu^Ix3JsW~cXQ*;1b2OvYTSznNU6C0ELYpplHcoYl4h z=R{^_=cV}?EI1rY*OJlt;zEXK)xlxf%FFx9`{%0YbI<5P_N5AP zBZ>*T(cm>LvVn>XdN%k9o;`cS2bh_`E%5ivhQHx&x4g1gZDi*vX9?81ZV#1DYJYDZ zPs!p+Uhl>7Xs)O)Am5rJnl&U3Y zN7W@>9HKP1)gCb0-$Mt`-aI$_A+{EE2lGTM6TT#5%@<-lLYKFMmW1z* zXKpMbk<+-s)9AsPZDGTkV#Cv@4OV?bV^u0p)>VRj!w?iAAc;US2xih8gnE|c5K8-L zqAu+v&)4d+b+4Jq>2GD&3rCm~*ZVZD*KJPpRNyO6l8Wz)gq;d%sS&slqCqf7IlhG0 zk__00LubwA(m2Dho(fVYPM%bVjuel%gY!zl@r}H`tz8Fj*dnv-r(hg4SfUan8#dto<28)TTIwJ|2}4Aq;~L9z|E&kwBuAN zjSARlgguo+3QfNB);(Wq5;a!`;o^Oyf5lG|?nWy%2c3A2!D*k=&X4-Jjy}1=XY`XD zF@^{=-h`Dj7}Qw6AY~hIWFrpIzy|AtE^NLTWz5&H&FRncTz^{BXD2H*Cwv+_?^CK3 z`_^&MN`VXb0kY=Ry?RZ@FLg7cZ4sN~^9uL-MTrb^n``=1Fcoe^+wTEa__5eKkmz#% z#&0y{SLb5HE4o^a{r=NpN#c%sT*76tFU7uHHzMdSvO;8R1wreu*J=s+ad%8=Kv&|d z0^?5XM2K`w6zdyLtsnx<6$&I$JOs)*6bg8PQeL7H%XbBpbbD zqKKStyv(eV8Qm>tB-svrkAyv}x4uC9c7bZ#Xg0mD86HNbr-OT*w#bY6QU9bzb`eZ= zx?L~qB8TtaKkOlMOCca61CdOsBkDmt=BuOhJ+h;F(cPR+OBw}!hoNENX?1633Ws1y zu-Jkn`kSoN#dRxE&nLY>P%Nx-B)Ld;q+3Y*=n!$XCF3fbdkr_M)k3v!+SG#_?jGv;$B4d$;age^*7eCf{dcDhu~C|>V+FO0BsB4z8}82zpm}U`U8LH51*6T zrCRavV*oq^#NUTIDA;Kei2p;bs^%Sgi_{-)PskcB-nZVV;pMra)z-9jF~`jXFB$RD he;a!t^=|kMv&^pfYvz#DpM5A{NY`focy)m=_Ya3`k@o-q literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal4rlecut.png b/image/test/reftest/bmp/bmpsuite/q/pal4rlecut.png new file mode 100644 index 0000000000000000000000000000000000000000..767f5a0ad7316ff03cec324ab653c8eb8a5e415f GIT binary patch literal 1918 zcmV-^2Z8vBP)lp34j;VNukZoKg@iO@nikJe7KAsF>1!s z*z2&!Uwxlu_$*%WmlPSyZ#?e#GRD(*BY$3jZ(w|nJ(dHF4aS4TbP~gOy%(A@@Je3* zz7eyAparGtiFjzSJLpJh8u7ybrN;A^=dU$i1M`D5-iNd|UM2hhd@ z70KTF@=%}auw51$>wvXY!}OU+)K` zKwWm|d|vaV-(bA-IA?wa;5!q518qzI$CqoqcfXV8P2e*P(IRgpO#A&g=6j5PH6K7T zzsVRi<7q5AEXnxpvp~+`CB~5ka2XHC^8J<1`3z{`yLpV48X}oLG|&ZJFu%c8JQ_m8 zN^?ove`dfd4|?P`>aBcvJ&^9lYJK*8+m3w($ZNha0Tw&nngFY#{0`%-3E%=hV|+pW z7-{|!K4Sj5=8Fl?0fS;|wp~n?`$5+>)$(^7w>3Y*fNJn5z+%1G`7!c6<~JU*hN5vh zoR_TUiI@UC5|nYfHbt53$5aDZk*W@V3aGtfH~|>&!vbJOUhrUEv8HdwiunTYlIQ&T zBB??d%m?fIvgWIMnG@k$Ub4=Wo-S550o+ln6I?f4D7p`4tylAn!(}`v8hc&Fo~Fdi zzXQI$4_V*1hF^yg{3+wvJsizHmzS)sH*t5)Q=mtJGH%yqVvU!vtNC9s^7n-n6p5{G z1IMnNYrg;fiRQEY=;!=yIDVdiZ)>)+mF zt6Li&#Sr%zrvboceDI!Y0=WB`G5+JTd3_)A8}TViYW|+70;Q%y)$40()#j$^doxzd z@4n4^f#w$!NAAPBc1056H4~bDFSjtiAFTPZvs^ Pu6k@DF}5^M4*5XP?NQ?2^*A|K0QEPh$dnQu8_Zvay2o%=>62h7$q-hX={{dzybSSpCJ(731|s zY)Y~`%{~-YFy5O06U|4SxMKPW^OFrS4AR=K*V20u4bmsk#|H4)*7bd*{*!oYSR~DU znPbKY#`kyzz6GESXbJO2BQVw%YCg$-IspVDAB_{8zcB%LBSfHWnh8kAMl$LJ^Z)MQ zGF~!Dc6^V==-$x$d%&~%{65W>vDEC@`NuTh(->FtD-O3oO+XCb`Pg5WGA&-~`!#=t z&)ENG!x7+Cff>M>@yyqQuYiAE^W$qM;PwO{%p@o;CzDlRzqYuOly9vc@1iq zWc;i7@YV9bm?Yzg=1(So(EM`X1Lm7~Y-oO14{vYvns3zmeq6zLZvuQYpEiUvtB^P2 z0W@bgix)J1Isc!h`3^p*giX&G&j8N)mFIlnO-)<$A^6kv1<*?4=U!?4^FGn}RzWhI z#Q?YZ{){AaP^tF}{_=qyz`c-7jAjwb=X}X<^C^Iu$1rDosqJoOgY$Zm;Nw#M>YL7< zr#k^qz0SsK&do?p>?^LHH-=pxOZeWnSYPbk28aegyEYU(x(i&-t5-{fU|{CP2ja1Dc4TX*)I6Apu1q3mA0!6~ z=1!rKhZ8|3(PSkL=01pFC`S+~J|xmQIaSB~pu*7lP$~$D4>`H#;-6L@*8Tj}+LKKF zd3w3O#hyL0ch>%WKi}_KvntF%#RtJ0B5JC#!^r&Z3VoK?9<<(w)gRnV$nRKco3qzX=z zl`3mhHmYn@IZ|cEvz61z8Re{Uk#c;Xx7NyNWwna9rxQviv`!eEusRXxgwtuI(^{vE zPFtOhblT~h(mAbjM(3=~MLOqnLFt0l1)~dA7b0D7x~z0r>$1^htILrtJMENqT05hi z)h^Nw9bjvtj8WF82w5hfOhTK4F$rrDkx4j{Rwk`Y+L*L8>Byv=$tjc5CTC2}np|XZ z&J>g>Xj3qzU`-)11!v02l(i`vQ?{lYnX)rZ8K;di##!Sc)aw6*ETrk%|xo6|ODY|h$TWOL3Ilr3mmFt%WAA+iN$%gUCu zEgM_5wj9~Avrbv3tuxkH>muuLpRjeLOr&h2BEpCgDoW@mVWNbM5>b?JQCdZ59i>f_ zwoy8Y(k{xWD5s;GiE=i|MN!U01r-%^R4`G&MujLUxTvh6vX06oD%+?WMP(N`6*(O_ z6FD2XC~{5~0%>3gy12&ZU)0YnL`IZCyHYY3Fjv<+RHgm$NPx zxtwza;!-j?p>o;wn{idy(-`n~=0{-p&4-t@U&StWml_Q_YcP=_SUcK-i5`J+2{_oFw z{6+lIUP|z#y;R^=^}@eF_{9PE-`nc(SLGkXlyLAJRIic<=tMv_0!HBf0sfoV{}KW9 zC``iN0e=VW2M}-t0as}M3j!Mb{Dy`U0$LEz3jZhYw_*PQ;g9L_oWrrm0pYLCKk$eA z31C_NfgcBiKIX5^Kk$eA3E&O+58}XJ2L2@cN%%YAKWomK5%_POzxn+?;eQD~_f-;p z2J{U08PGG}_w(UT!M}IY-c7CWw{2~E?^E~>2!A}F&yfA%p%9>a{+%KJGiT%cB>>^~ z0kHlF;Bx}_dVu^1;9DO6>#vddl7A}Xzj<$*zXTxsS+cG^@_Y`*AZOY)RPlF{zwqP0 zk2hm}??BA&9fehdgyQ6V7?g!GZWJ{0P{d+}^ya^H4YT zpFJ~jegylyfEO=SeLVt_$wV^Q@p(rF_74nvJuqSDeU`y&Q4=&HvB*oBu`rOHz+L;Hs*rQ8f+lCp&j_c6J}??uLJa z0B*v6^W}?|;Mdo~pX^9>_=11n$^i5f{EdyOaXb9U)cRDaWkX8~{H+Ag27lWD_$$P6 zUYz~lU?qP9`1OwfpZ^H!U-CzQv~NZL?UNY<40H?(T#E%fLV&j)0e<}>z~|q}`j`9> zAi0MlWG}f_9q`vQ)Cga5GyK%qvm?|^4EX2GmoFFikucAX141A88@D%x`S4SF_qI}P z81QM^fdj(7cr1!ykp9UUv9B}P+1%ZEw)<@N$k`u8ZX$s82v{fd_07o)qod=r4Ct>7 zT)P7Ol}GrWY?Sy?$y9SoYH!Qlme#$Ww6-CD_6S&Y<%gXh{Nzt{3V$c~@c%FZK2^;h z=P&Uu&cBiTsZ=xkDe&R{q!oM${%lO24PQT5&VJ;1mHgLWe-i>S2*@H}6#g3sxJCQl zJij;)_%j_D&ksHP;=rR{+Ohv20x}55BH%duUm~E7_9qeWhU~?GrTL$&%0KY0t4Bam zMy37ffz!S{0)Bba2!2)m1Tde!=U+YjLHGyZZ-PIC{tW#2?tJ$s{I^DK{qR5V|K{^w z;1Br+e&{{_LHIkv{8Ud%Ps?%m`&#=x`3w9f;eX>$HEU{W8k)#2L;eKNohN?+xak96 z{lUKu`ep=Xa3BNyX#`v&fNR3vjsXY#{8WknT6*9ofHogM$X~LLkG$kB*@r{;--5p> z=FgIU%1$)z925&?o#u7$Er%2|c-U_Sp}K0p!0X&L07^?;Ooa z{_KG8q~K@#iXSwo@L#+3RQTIN{&LIbFK<|YeF*URXXEw1^qlAV;1K=SytSr@N~Pcz zjrmdP){PbVd&p({?O(JXtnjn`r2p~beHZ}$$x8k!WiJNMziCa=TdAf@QzpglGX#w0 zZ;aj=jro7uxb9;a&Dmy5@cct@{!ih53je|OgI|PO*-V`O@je9nrSBvH-mv=zV}A0d zGQuwoz<*;jo%@iNBd?kK>80Y_)mNKlfXQGCJ6cRxX1VWlD}jxxz8VvOa4Q7 zRg3)^0bK~_L4bw72>24pw2!^bRQRuZ{(kH~74x&q!YueFu>X?qOYTb#$vzxQ z^AG$B`NO{v0nLoq|DmCw(B!(<|5Wf-$)5no-;*ymFi##FfM3I};qQVUrCAIA?F#>r z^^g7Y{62rB+D}#S$I=$fQh}Z)dndvf>lw2CYuNyr{JY4X0JuClTAa_n`bm<07W@kT z_3KaNHU^vuj_@ygcvC$~-p}VQYhaNB@V{Nf-$VYwj|1|gB}IF2C|>{MAG%fX=U148 zYH>}t#Q_#!oV_?8{0L~$O}kTFnI8F*ydAZpxBdQK-k?=C3O8)cf|F6 zbbNySKf(ULbP_3EwuRp!B0ln*^F{Knt*w2#R#Uro!%qzjS?YE%%=?}8TQ+|D@jgbl zVu$#TJ9(rWPv`n$0rF{(?{k0I+KI7=iT|3obm>z3jkHqsa*h7Fmi@20bk}b7f6q{l z9b)y{*#F->z2`p`o?YRWhS%qw_Vac$Y z0uCdf7Xf4N&mf=_p0s==#r&Qg{>lDt`Y+4ha$$oE7S8;QdheJ{yp#?hJUzcxMvLhQs7_m{UeMj>-$UgGjrGH z<|g|u_h0_zJ0uDJw0C0ZG(U@#zc7ltFUnB0b>zQ;{11~q0rU*RPXH@@|4RE~o}cWc zgntqPE}vo%vi_%h{wuU!F?(4L@V^89j+nof{O9@QkxT2?1q5)>mT3PR0h9fIga7iW zt6ZEKoLc(#;d5U8Rq==SAkcdUgdYKVhu*XMa93|nFZ`@tTVnq&uVa+wx1K)cN5HAe zr-)#1iuJd=;9Rxh{44xb`4^wves%lR@7uQQm$C1J4CooMI^cgUBhDZGQ&;~={`u*A zF|41lI}`UN?p=af#!A`y{Ojs!>)wHX$Kl+2=nL)_+}H-8*$Vs6Bh& z?;XZ~F{*?C@V^p%1niS>0)DYy2KO1y=bleaPX2B3GWFee@J~%+K#>ab@7=o>C-3Qb zzw|8m*RlV1u>be4|9jd0!>AuC6`z&J|J5JJf0F%wnf$*a|0(jHW`40WcBe%C_ui0w zoIeH#|6$=52jHI>^Z3HQO=>@NV*d$W7pT$&ai>&z{#^LUpSmji;sE?J#V}v^g?Oc- z$o1loT8sTV5pV zKS01k1UyE->{N03A4PFb=EE=fOZJlc{2{BvA8JYdfj{I=09E`rFwc(z6zx5Kb^d`r zjKzCm+E7 z0RD&YKbd+mH4Fc<;uU^lu|B%1f?;Vf@5a*A8%MaN953fGP{^{x2 z;w<);M8Lh*F?XO}c#VUo(;&t z|IQJ0zdch~NuKKclL>fE_;>JA1xzr!z?52QZx^_8!SzV1@5?{feC{rf-Pe?a~EEBw@x zC$rQu40!&G`%CD>jRpFmmHodq-v1xP`~O_L|L@5D{{#8okN5wt@&11j@Be3E{jZw6 zxFP)HPaP5dBjCgT_i^y4YW}Z2Tfo12QTTDd^OHaIQ1~B$5C1=A!Kb*tTJ}YnbI>3A z5pV4)}_FllnuP$Dsy%!(>c>59X5COD*IQw|^3GHWndjv@K M;ox@x#7hqKzck|>>;M1& literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal8os2-sz.bmp b/image/test/reftest/bmp/bmpsuite/q/pal8os2-sz.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7f1455d5ef11558afb819dbaeef84f3166ee5d06 GIT binary patch literal 8986 zcmbuDPiT}^zsFBZO!`M%h=RNp7rosyvvG3NjF!gBl@x@OF3faMvoI4qnMBYoBrg=q zokAsxH-gSYlanmWU5H^Q6@-e5L|P|r)$v)VFtjd81wnCQVFdR zMkTCDL@MD_TB)>FX`|9sr6ZMgDyLLVtDI3et8$UbIaO4us8!LZqE*F66`iUmRne+q zRK==Fq$-YQE2ot+%30+i<@lhq(pnjXVc22wM`qFwl*Evw6i&7bK2&N%~_j^Y|h!DvPEr+#ulwDMz-i|McInB6=N&b zRw7$*)+y_>b;detU1S~Z6Sj_&iIk00L>N&*MF|}xOq8%uB8n0&N~7gbbL(NV=j6&qEesNy20BBvu~B4;BP zMb61WAPp=*7dKf73FQ*nC5%g0mxx@#xwLX=?b61jtxHEP?OaZ|oOU_ma@OS{mvgSD zTv5BCaYgHjkt;e^QLdt0#kh)fmB>}384HcHu^evSJS_Y8wO(zfcI`TJ$e1(c%$f7l zPe1%by?F7$|4qWz=hWAqf9>mAUqAo+GxzIN1GRqrh7FrGZQitb^H%Ep_ui*I{P4rf zn~Uk&)%N;b+YfC&w96c#y*Yp8hx0!n;J<%*fdKZRjKt?Mq&)(@mGSM*GU(r+8tT_K zY-rf9e$xipZ`!)~y{+#f;NRZ=5CPfdY$n?|cjPnq&P9jEs~7%5!Y>ZM|HFBYzldMj zO9{TTmkRuK@ds{vJs{DhP5)QtD>Qxc}oe1bgz%cwj!haL{Um~C$g-Q53 z;P0UQ00OQc;0o=3ML?sU-_VdkKnns|;r|5wHtatj{4sr=b2t_`ApF(&2mX*h0W8Zu z@Z*5c$Nbg#2mX*h0lXpqK^z#&z@LOa34bU2XU$nN4FApZH-Gpi{4e3>zDmN+fSv(A z19}Ghem?vu`1fwwyQvlawykaNeG2~p;g1LO8M0qI6atjbzcb{2=4_n51R(rA0MX{WUUQ@=t~QH}8$}mjHx6OV-s#p3mVJ1NFD z9fCf8oc0_uD*w!Bb~5;hg6j9Ei`tkAUsT?ajM74|QYz*)zlE zhq2!ac=1xz*CQaAOeB*XpLcX%|G>c40|QsSy>bNszy1n80veJH&FfPeTCjib=GLvP z*zW~=cwh zIF~;H{Q5_L&wrTpFZm-t+BYMB_Q?za208`?uEhc#A;8;@0Kfhb;PY=~{Y(A`kle!& zvX|Ve4)|*tYJ{)38Gh>Q*ltZ0_zn+kLis`0S6vHxWR41gsPK`sQSY(a~{Q2K3hku3drt z$|L+wHcEV{WU9F(wYOz&OY7cGTH6pndjzby^21ILe)6X}g})Pg_SGihOeJ2XFu}1O8#rGzX<^u1Y{8~0{;yJ+@k&Oo?jdY z{F#o7=Z7ACap2Lf?bv@10T~2j5pW#-FA>m3`;!QGL-yjp()`a>$HEU{W8k)#2L;eKNohN?+xak96{lUKu z`ep=Xa3BNyX#`v&fNR3vjsXY#{8WknT6*9ofHogM$X~LLkG$kB*@r{;--5p>=FgIU z%lBhvf}Bd z5g@meqCEoI8zuj6C7bFAZ~hPYuUS*G=B=h=Q&Xmyf2TXMdH6?e4Bxo5Qh&NC&oBL1 z0`RxDH@1HPzg)>=Q{l~*tzY&LK%ejzFhKGz2tB!T_SqN20P&6QGJ>)X}_AlBG&hfMUr2p~beHZ}$$+`Sj%3chhf76<#w^B`+rc8?8X9yU{-x#?y z67&DQaoxuo%@iNBd?kK>8OJ{HHzrNnoBo6NG$u+~a$G$zQUU+~*I-CI3QR)ndO! zKo87aSlXglD$w&}??gCbJww)iEgL|Se;4@^0GCHbO7r6bbOrs zKhFNYbP_3EwuRp!B0ln*^Cj}Ht*w2#R#Uro!%r0ombzUE^ZuaymW>~OypIvC*aH7? zCy$il>1=;2Kt2ufeeN$?J3cx-{$JylE?tVhkygrHuF+rDvj26L?%K`%?^XjEczyP1e}7nf@{wOU;ct$QBY^gOmoCLugkDwvErnbL^m)l21BAaz z_`AS|zhvj~|6>dMTcF=Z`@k0XrQh_d@COG1|6ur=Q42l=e>{uF=2tpY9ro`)z+nXR zB48B$X#|wRla{ZfnBUXGKhgh9|7H1GE==-!&TCw0*7EFE%AWuh<-h1jz6pyXG?|{B zo}Cr`Z{i3R@?Sn{WqQ6!>S}kuzX$%q@DKG2^^C$_4*W~Le}qwGeSgV*diMJ4>_q?N z{>$Hdk0jxr@=h$B=4Y|;7ee}(odW-sdj{&(Qt5%c$w|2)4ua%mmAfB-JqGVPxuV50v!_%ENj%EigS z$)$fEKIi3M6@PdS0=;)Y_z|FY=smj+clGx4!q4ipW%mE_I!1YZ>*-^D1f05jiUgwOfKR=Z(h4nLf zXZ+syy-QHbSSfp-e_dT|-8=B_IK2Dt;oh#^A^1n9?R2?(yIg+t$Ez*dwrrvOKH48U zb^`nh7*K}a2Y~+*{S)88fBEX)u3ja8si{J#DtL4uQxLuC< zy#q17cOd5X4lLyF`6mZi)9^EYyd3xoo}O=w;6aJA&%Y4Xe_dVOJ9Rs#J$vBq9m0T7 zs*C~fzY=}~?2~ZA9dY1g_ z*#A4&|9jZ~z3l%X)Q^@+&&uTg>QCf9!T!HY{@;`TB>7JN*Qzrj=Z^%B*9|MH{ zu<(lm@K29=eBs|FwVyh%|Aem#RQZCqQ!YP$F8t(AT@`+D0RHJxm@oW7ywXwPda0n+ zV*gGA96`WQ1dR2L4*h*p)R)Vz5kU7X`?q2Ke%Aj90yu&B7g+yg>@Po`y^sA55bzKI zj}b64S(^GsN!*k9@Js%Zz2rWB$SU!NT9SX@5BU>76+aHl^Wy+Td(U5;f8Y=K6M*NZ z^@;;I!oL&#o$w!l|6K36-ZA*^jNXCnb@?^?&=Uas2!Q^Cw;uttN5K7w2k<|D{~`QO zCZ9~s!2hiDEX)`FK#$qtfFBEI;a`;h(7(uE;(t^AQ_J%g39<$j&LMk#=m`J2Rs2WE zU-)sLe8=;Dub~l|09QwV*k+4*ytGcmqoy<*YoQ?s)!ManYu|-`=aD@} zjvPIF^ys;vu`vR;LjZS#-v_YY3pnu=mOtx%QT}uMw4dkq4y=+t>%S`hX;07Z%9y_D4FB49 z*Y2b~`UrmN+_^F84hB5G!!MZ+q(1ZYm9LAw?ozMsa{vDQ`@h_OK>hX`{M3^tGt@H- zc>aw0OX$Uo1^SYe{l7Ne{~yKs|6IKP@5uiD6Zzkd_y2G4{(lnh|7T(SubREMA^hY| z9TEN`;KTp-G4QEs{;xh;z`uJ@_;JAVlRx!P_#c7~|37BHr?|gb_9dEg&>#B|a0CIg zKQeZ7>>TaKe0v0V`w4*dUckk#E?%U)7a#$6`w{RE0knTO^LXY7?Pq*@1W5Mb;CBJU HOAhtFBGewo literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal8os2v2-40sz.bmp b/image/test/reftest/bmp/bmpsuite/q/pal8os2v2-40sz.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d1e66b615c97cdb1b0941487658fa89e2397c9d2 GIT binary patch literal 9254 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 TP<~mJ2SBpdi|+*(=Pm0$>%kyo literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal8os2v2-sz.bmp b/image/test/reftest/bmp/bmpsuite/q/pal8os2v2-sz.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6fe566e3fd9385b895dc95310dc42b920fcf1ebe GIT binary patch literal 9278 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 Nll3QGs>B9m{5Mp_Pk;ab literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/pal8rlecut.png b/image/test/reftest/bmp/bmpsuite/q/pal8rlecut.png new file mode 100644 index 0000000000000000000000000000000000000000..dfceeb568b859124f008cce601a3b7d04ab305eb GIT binary patch literal 3524 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb16-3103.png b/image/test/reftest/bmp/bmpsuite/q/rgb16-3103.png new file mode 100644 index 0000000000000000000000000000000000000000..79ba23c83445d28eb1463a2b56c9a89f2642ffe6 GIT binary patch literal 3347 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb24prof2.bmp b/image/test/reftest/bmp/bmpsuite/q/rgb24prof2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f9f61b8ee31168effb8c3b9469708b69ff7cbcc0 GIT binary patch literal 25254 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb24prof2.png b/image/test/reftest/bmp/bmpsuite/q/rgb24prof2.png new file mode 100644 index 0000000000000000000000000000000000000000..b65ccc529572f4e8fab91dfb7c26aa0171633476 GIT binary patch literal 19650 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 A)Bpeg literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb32-7187.bmp b/image/test/reftest/bmp/bmpsuite/q/rgb32-7187.bmp new file mode 100644 index 0000000000000000000000000000000000000000..887ba52c917a48aa17b7ca1e40d00cc01d947cb5 GIT binary patch literal 32578 zcmcKD4_wptqTm16n|86IUF@Y@=&%bNo`o|!L&dYG_*lVW4G8i_tU<8_i!D+{kun-- zhD$MZ6seaYtyFRu1%(~iAv>~?RaUZ=m8@kYYqOHy>vQ|z{yOi` z$IiLu`gpwVyPzcbe4k#9QG*F736+Y!{+7W1)scAf30kDkD*l2M@A%7q_(S4N`EC9{ zFt|v5s6tt#R;W}Fikg}giX-X-MSXpS;+ST)Vrf&UA|j$nv8<(D5f#;iA8}5-=ZLoK|CI%g1c8J)FBFmMy*h2Lpe&ALa}nGLa{bN zp-5P!P$Weu6dB7EitH5%Mc!(KV(%J-V&8g&Vn64u+Ne;}BqDz!?XiclY@S)n|Uv8DqX9Hs7g?@)F&yTS~3)Eo!JLg4CEc?9NT+fjd$OH9{+wtg6g1R zKy#Q!cvLaGrs+UNQp^2&{$A|w#r|IG@5TOJ?C-_?UhMD1{$A|w z#r|IG@5TOJ?C<61_a4xy6jfT4>X25YuF;(5ezzw5p^J zw5p6Ytt$JpR+ZPORqZ{mRqgB1s`mG4RaFC8Rn4GQRX?m%9lxPfogCAuPTkb1&P-@k z-M6%=D^pt4wP~$t%%@dN%xYCreywUIpjFKUwW|4hxB&NHe-HK+>@C<^u(x1;5BB$9 ze-HNeV1Ezx7VIt9Td=oae-HNeV1Ezx_h4_q-h#aadkgj!?C-(;9_;VI{vPZt*juo- z2>agNXHUyISkXtaGpwjn=3qr=$Fd z82iQ8FXrfr74d3?GM@X7SF1zf)f#oYS{oX#)`i8ZS1yfLuZ@UTCoGFsCq>1pGnU7z zvsc8c^H#^J_pXUo?^_?Q-k%V!uG$!{u1Shl*Qds-k7vZIPi~7>pURF`pV<|!?#_!> zU)d9{zE&8o9xIMlPn5>1r^@5iGZpdbxypF;JfCyK=RK7Y`zq|Ku&=_t3VS8?O6--` zE3sE%Uxj@Y_Ep$dVXwqqiM&Apar6+*9)f)c_957ZU>|~g z2=*b^hhQIqeF*j;*oR;rf_(_~A=p2F{R7xPfc*p5KY;xM*gt^%1K2-+{R7xPfc*p5 zKY;xM*gwG0A2^V!QB>t>REKgk>Y7}Q=18taTc4}Z9n00MY|7QFJ&~(PXvx(ieUPik zXv@`PpU&0fb>?dJp3l|n>&eyZ@6FXz4diNS26Hv_!?~K{H*z&6$8t5NZsuyvOyp|1 zZ{=#POyz2>P3LOHe7Ty5*<8((KUXso$kogRb2an#aRKhb{yyxj*jur;VsFL%KJ4$q z{yyyQ!~Q<(t=La_Ezkz*x!f!ec0cJ{e9S5vA1Gx z74`!Ic^$HkSkW0-Co6Ku&a$F&vh%E{OLl=3b<28KkyCb&6ZsDwGyJ>07kw5Q|o$wrI7X7OgJKqFuSvqFozd(IzajXp^EW+KlBE zZT1R_HgB~>yLXL6yKlWkyFbCAt=eeO)+AZ9^{E!^@eGUhV0WAHx0+_A2aE*sHKt zVXwme5cY?#KZN}u>{ZySuvcNP!u}BUhp<0{{UPiRVXwkog}n-U750a)KZN}uE^UhVnr8Z-K?lb=43?|WtUjdW#Z_e*oR^tihU^dq1cCF zABue__MzB^VjqfqDE6V)hhiU!eJJ)1WB)Mr4`cr@_77wKF!m2)|1kCsWB)Mr4`cr@ z_77wKF!m2~^oI{L>l9VZI@O_Oow}x3r#aHB)7CfZbjO-?E1Q~iYfm)m5?Y#dNgp)p zGTNGT*{7Rzd7aI=z2}>C`+Ayn`+J*pRRhhsn!#pW{cyAH_>E@W$+2eLshiEZGZW3a z?pw{eD^tz7YtzlTF<-N8VzyZ~_6`_XA8F(M8!MR&-g$(KXm>u-9O( z!Cr&C273+m8tgULYp~Z~ufbk}y#{*?_8RQNun)sN4Er$b!>|v-J`DRX?8C4R!#)iA zFzmyy55qo;qlYQ_Vid~07?r9oMjg@@qfz(8XhZvAbYXolE0^}gtc~c4Nm$kwlN8k# zld-%nCVNF+Oy26gn7wQIV)m`?i`k#h7gM#dFQz7`FQz`VFXnhgU(CsEeKDu9`(n=Q z>Wk^l>x;Rvr!VGOVPDKxabL_tX|~g2=*b^hhSfWeGT?C*w&9(K*?9R@5cCz>2zMJ*>znyU2T*?GO5?rPwI8YCiN?uCiQDiOzIO_ zCiO`lOzJb*CiU5;C-r%qllr~qC-wV!CiVM!C-qeWllq#$NqznBr2hDgN&U&ON&Tst zlln6glltykllm)Dllp7Zlln2=q<&&{Qa|ON)XxMa^>e{V{rrPmfCsUE5c^tw?$=^p zi+wHj4`Tly_77tJAodSpUyFS$_O;m8V*eoa4`Tly_77rTi+wHjwb<8UUyJ>N*guH< zgV;ZaeJ%F2*w+gC;o&`JWSy+YAv?>8&dJWRqAuA5R@5!)VMR{aMOJi4c9|9R$~d|f zd;SMELW{i?doA`_?6ufyvDad+#a@fO7JDuBTI{vhYq5{OJ_7p)>?5#`z&--|2<#)U zkH9_x`v~kKu#dn#0{aM#9>Fj2d6F5gQt^xZ5M{hZt&G=(D&uuw%J`K_mGNsMl<^76 zl<`SX%J_`s%J}RR%J{t1%J{u&l=1u4E93VkDC4U(D&uRCl=1bc%J}0M%J`Gpl<}vs zmGNhGDdW5Il<`;gDC4gcD&xnBmGKj$%J`{rW&BKqGJdX789&eOCn(g|tFb@w!{=Zk=RFKABlY=_L0~}VjqcpB=(WmM`9m|eI)jg*hgX?iTyI{mtnsQ`(@ZK!+sg| z%dlUD{W9#AVZRLfW!Nvnei`=5IQp^!kp@Loq(OBk(x9%1G-!@Q8npG12Hmkp!^);e z!`c&(hJ=<#L(&J4hK#mIL-y%NLtba3Vek1!!@izK!~Wh#L)Ac}p=L1BP(K`LIDRA2 zaB?itaO!5H;mky&q5D>(;mTB`;o5YhVayk4n3#<;O!*@XGl59MTrkow{}318A?zQ* zz7G33e(u-RXXxs%e+c`Buzv{qhp>ML`#S9Fu&=|u4*Q3&e+c`Buzv{qI_&GPufx6$ z`#S6&!u}!bAHx12?CY?v!@f?~-?;HYr_8~M&dScQqVuvYR&+tu&5C+tPF8eLc8L{T zmi4ltJ{d>XVXwnphrJGa9rilxb=d2$*I}>2UWdI7dmZ*V>~+}du#dt%3i~MRqp**{ zJ_`FN?4z)c!afT7DD0!KkHS6*`zVecrASOyC==6Fs>F14NMgE1otUl-O-$E?C8n=j znwY*eA~8K-Sz>xpRAPF@^2GG)6^ZG2s}s}ru1QSaw>~j_e?nq<)yBm1nxw?^`qaeq z;~9zRC$}Z0pUO^5KeH<_y*n>4{mP!i^lOEQ>0`x-=@X@i=~Ly2=`$6H>2sBd>GS*^ zq9PRgQ0(imum9omo~|DIQ0zmo55+zd`%vubv9HIz9{YOiL$MFVJ{0> zdhF}555+zd`%vsdv9HIz9{YMOK<9t@PkTiU*;!U}PIjIZb;&NUqHb9aD{{&%vZ70} z%dDtZ*2jvj5J!*3J{tRI?4z-d#y%SRXzZi0kH$V4`)KT=v5&?+8vAJMqp@F({c`M= zW4|2x<=8LBemVBbv0sk;a_pC5za0DJ*e}O^IY(c9Am6B{$~USG5s5Z(P}wZ(MsK-$=~H`Wa1 z8|#PjjmK}~8&8hq8&BQLH=dcuH+J93H(r^_H(s00H;(!8jT5u^#wmZkaVC&&oD1d~ z=O5t$Jc9ir*f(I`fPDi$_ZzT(1p7y@e+2tSuzv*m2J9QKZ@|6*`$w>U1p7y@e+2sm z>>IFez`gF&a$F&vh%E{OLl=3b<28KkyCb& z6}2h4E8bD$6z0WeGK+7*vDYM z0{a!%ufTo<_A9Vof&B{XS75&a`xV%)z zYi>=FHMc(1ntMFMntO7aHTP7uHTTReYi@U*HTTLMYwoo|YwlRFHFu)anmbi)&7G;R z=FU}GbLTZ&0Dd!1rNRCk?BBuu9qix1UW2^`dkyv)>^0cGgZ(?$zk~fd*lV!YV6VYm zgZ(?$zk~fd*uR7QJJ@Tm*I=)~UW5HR*uR7QJ6r(AfBw%eiq6W;v7+;`E>?6w*3F7~ zWKLFeQFe(HU6%E-qCVLbR@6@%Jr?^|>|?Qy#Xc7MSnOl5kHtO~`&jH_v5&<*7W-K2 zW3i9Lel_;1v0sh-YV22IzZ(11*ssQZHTJ8qUyc20>{nyI8vE58ef0r*fuhP@pgLqP zP}kTCG)L?O+Io9|?wGw`Ws|*N?FoB9LW{j1=>vN~Mw`7L`?S3vuhU+z_q@GeUyr?D zf3LluYQSDlGiWcUAGQ}9zhN&pIc6_7b<#^5kug6}Gy&ii#_Im8~*z2*^ zW3R_vkNq0#*I>T}`!(3F!F~<)Yp`E~{Tl4oV7~_YHQ2Agehv0(IQklet3;u6m8ev% z5_O2HM5A_ zZdng2a>_2UqD!*Ntf*Jk$BM4V`dQHcar8Ls#<*t{d(-zbM*BGye37J z*Q7e+HK}X7Ce0DANn7tV>5h3#E1SHgwI{r$gch$U=>xARqs?o|KJ7K-b$U&E&wEY# zdc3Cny#YZ~);O%tHrZP-7C z{bSfahW%sM+pxD`Z^Pb(y$$=vuzw8u$FP44dmHvP>}|q+V&dPNlbvTpU9t>ACG-J_VL)qV;_%wJofR}$73JQ(c={=i$bZg z@aKpY{y@p1QS%%7p(=|mOl4WQRApHkp|T__Q(2OtRF;h8DogeXl_hVr%CdKj%Cc|0 z%CbK}WvSYzveYE0EcK}>%kd1A<>WS%nha8!%^d)U8+{d?HIhy8olYq8g2uf<-A{d?HIhy8n8fOG%#zrG|oFY97O7i8V6 zs7K~xMHgk4SkYx!FDvSkU13H2vH@1)B98tf_D^E}B=%2Y|0MQLV*e!ePh$Th_D^E} zB=%2Y|0MQLV*e!ePhy{leIoXW*e7D2hQzNM?aGNr4&Hm$23 z^XaN5W_8t5eqHrUKvz8%)K$+v&INcJ`^T|w#=aT*X6&1>e;oVAv40%<$FYAL`)2H$ zv2Vt{8T-eve;oVAv40%Z^phE`)2GP$Nq8bAIJW2?3=M~#=cqD-@5hEd07`L zx*+RjMLjYnE4nDV#ELGo3P)6{U+=;VZRCcP1tY3 zeiQbau-}CJChRw1zX|(I*c-4nU~jJ&;tol0e>Q->JpG-^YgHq=n33p3QMTxzIW8)2wRSZ1i>*6T8s8|t!G80zv?8|wD1 zG1To_Z>Za!V5qCwXsD}6GStXKbx zMcuL^Eb-8T-xHZ^nKz z_M5TajQwWpH)FpU`_0&I#y$o66zo&5Pr*I~`xNX`uus80h5Puke(Nk~+HCQ#5g7c6L;{|O)P6YPJ2 zy&Zcy_IB*=bUSwJ?bzF~zvGX=-j2N;dpq_&!Tu-M{{;J=U~k9Xj=f#jPfhLXl3id$ z-Lf85pN4%J_G#FsW1o(FI`-+UveLD8(*r(s=8vR}0zrMP8e?oP0)yC@P znxyLH`qb*?JK7gkH;)xpH&2vSH&2yUH_ud5H_ug8H_r=X9rilx-^c!a?BBnO{rlLz zkNx|1v40=?_pyH;`}eWeVXwnphrJH__pyH;`}eW$`tSe!Wzhv$H!JFqIa$#~*(FwV zS=P&n`eavFQNL_}6}e;|v!biS(YIp175lB&Z^eEq_FJ*viv3pXw_?8)`>oh-#eOUH zTe07Y{Z{O=u+PFi3;QhWv#`&?J`4LS?6Ve(IqIL~+rsUM?QLzX8fa~;8EkE>A8u{^ z8~cSjTKKWkt*v9e*4ByH*48P1YwJv)wRJAo+B*MJe!x$$|0(vZ*tcTe`Xl?>Z>JUe zR_t4UWdFB*4EC+qw_@Ll{ZFy~DfU0b{-@ZtV&95=D<9bP#TPGMkae@79+{IBU6fs7 zMVDp0tf)_Rg%$P723V0x_Ax8ED&y$evEPpUcI>xfza9JS*l)*vJNDbL-;Vuu?6+gT z9sBLrZ^wQ+_D1ZD*c-7oVsFIWh`kYeBlgCH>%VY~{v5k-n_@Q(b<`vcb=0R0b==Xu zaHwOfc&KBdbf{yhe5hllVyI)Ta;RfInvajhJ{tQ|*q_4w)LraPVSftyQ+KgHh5ae) zPho!w`)KT=v5&?+8v9e&pThnW7vjSI_#gjP)Gg~_MNZj8R&+^rnHBZQ`dHBwSwAZp zkhxgV$Fi%eXplJiPV9GLzZ3hN*zd%CC-ytB---QB>~~_n6Z@Um@5Fv5_B*lPiG2?C zIoRi5pM!l4_Bq(+V4s71j_?uR7R-Nm>reZ?3%4P5;T8s_yJ`lfyXuFhyY6T|J>50t zo9>#Jo$i|QPj}4(rn}~X(_QmF<3@gl{m-y($G#o=_Pf}(W8aQ_`(5nYv2Vw|9s73d ze}?_fu>TqMKf}Hq`*!TxIm3mSnSa|Y>tRJs*+o`#Np_hP^~(BK(G^)gD;kiwSkcF_ ztE^~H#?ha}{#opw#r|3BpT+)J?4QN{S?r(1{#opw#r|3BpT+)J?4QN{S?qJM&&56$ z`&{gEvCqXm7yDf7a~F&j+!o&cQ}_1U*%;DSlN8cdpBmD4NBhE%zOmwvzKPP1zNzw% zzL|=UzPZYfzWEp)L=5&Z*nf!qhuD937yA#f{}B5R?_&QU_8(&ZA@(0)AA@}i_A%JU zVE-ZZA7cL@2kHKw|ML}5kIc!6F3K*kqRX;gR@5iE!ixH31FXm;`!!I zVgDTV&td-@_RnGe9QMy){~Y$uVgDTV&td-@_RnGe9QMy){~Y%D*ym%Pk9|J&`Pk=U zpO1Y$_W29Ox0~^H{(QMBMx+Z!^b0m65TOU27I~F~(vMG9K?TP52gqG+b zUV8EWf4lv52BL>*2BU}Shogt?Xg?i2H0FyQnwX6qn({{v%><%{=7P~f^DDX8mDsPu zz61LX>^tsa-+_Gx_8oVz@4&tT`wr|ouwRM&O6*r+zY_Zn>^rdU5cXex{YsC_$%-z@ zF0rD^vR+oyC%eLm`eg&G$R+!j6(OB$bu|I?T8SKy8#r_QTXRtqW7yC2VpTYhN z_Ghq<#Xc7MSnOl5KZE@l?9Xrkdj8k{+Ang-F0!IavdgTfSJuahuE_dX(SXdwiawTI zWkrLsAy#yaIQom&zli;d*uRMVi`c)2{fpSYi2aM$zli;d*uRMVi`c)2{fpSYht~*vVzOt!k zeC>&%@r0J5@uUxm#_wo9P&8gMSTtTgTr_@1`{|>YQpcVO?p-fCDPO3v{CpGHYNo{EDq%N#>a^=$6$+Z!+lL^aeC-3?>e`D=rO;YV-eQNFG9qkKi zC&!9wCnrj4C#TA5Cub^ZC+8|_C+GD%I6d}y?9X9;4*PR=u|J3XIqc8f#r_=j=deGA z{W|e(IW$a(Z{$=c6#{OmOU&j7r>|e(IW$a(Zz6|>^ z?8~q(!@dmrGVIH+FT=jAUY<68)*~hHts%(%I4au&tqU$mq-z(U^g8eJlzk>ZM*uR4PE7-q+{VUkN zg8eJlzk>ZM*uR4PE7-q+y$O30_9pC2*qg97VQ<3TguQ9u8r<%-@c#eTz5V0YkIe2* z7@4iwI5Jz4G%{PCIx>4l`@)ggvEq^0iPDkTsq&H8ncMF(4*PEGyRq-Si+wlt-Pm{k zJ=iZirpy25|NOgNSsyF9BI{>G12PvY`dD_A6%ERUSkX1vbyhS?+~2F%zl!~<*uRSX ztJuGa{j1o&iv6qDzl!~<*uRSXtJuGa{j1o&ioF?oGxlce&Dfi4Eh4~!@j^B?WcW#F`q9mG3yIVE!@V!<5+mxiM)5}J{p;Ajj{WP{zrJvu+pqJ&`~P3}_K)w0390Cf2{||r6LNSkCgkXFOvoMW zr(;6g3-@#T?cDx$-(BqcugOTV&i%HTKomS7Tp|eKq#g*jHm;jeRxt)!0{KUyXe=_SM)|V_%K^ z8`!^r{TtZ7f&ClUzk&T5*uR1O8w+PyIN!qCf6Cr|8wu&52REjN9!^RRJ(`*xdPn<( z`?~$(|Je8UW8aT`|DV}jJdWER+mrX+`&y!GBP)7Jwuu!PWJ#tZ1_=g%zdBwy>f!8TVa>J%4c~q7M5y?CY?v!@dstI_&GPufx6$`#S9Fu&=|u z4*NRn-@^Va?BBxvE$rXI{w?g^!u~Dn-|?bnvK-%`{?E>hM-E>_h=E(xiN3{%%d zMugTyMupWyu3B0bxh|qEa>KH^$mFQH$jm$4&c?dP!%20KM^oz}?`VJfeOJ^)`YY=q z`HMpeg@NaIce?G{Kkh2_SFyi(7yGN&U&a0^_E)htU~j!Gq9p0Su!iyEK6ZUsj@AsC{32ma=#7OH(=j@eFOFl*f(I`fPDk@ z4cIqe-++As_6^uKVBdg!1NLuY|2Fn-WB)exZ)5*9_HSeVHui7d>1-VR?E@Xri>f-J z7a!_~UQ*K$9d@K6I-*xH9 zqp^pRMq`hrj>i5i``hhGM`OL^qp`k<(O7@wXlzjYg|ZavQ?S3zbG+Lh>+K(Z9sBFp zU%!j}b?mQWe;xbl*r#Bhf_)11DcE1f{yO&8x$%Yj6MOr8ZG7N?*PoJYVnqg75-UoU zZDvI&vQ$>IMV7{j(q$PecWJ}khP@4Y8}>HrZP?qew_$I?-iEymdmHvP>}}ZFu(x6V z3+#V^{V%Zp1@^zd{ukK)0{dTJ|BHq5-F~)(_y4hb`|S+O#vLA_@O4;W^&Ik6*YSu?z1T!F~k$k-OM0{MbKs z7w&K2{+HPQ68m3b|4ZzDxp0=-&$jUX zKV}zhL+rxGZ46C3oD`aPG&MBwZ`m*Wn8MIRcX4QSB54A#a|{& z$37kV-(vq;?0TeIzry}k3)lb0=li?<_rh(89f(aiJQ$mDbT~HU|FU2BF;lTA*QaAs+`iZp z&unap*B_hW3&f`QgRv<=@r#H|>@%?+#eNj~QS3+gJK75$FCOc{e~)56iv8#xu>ZTB z>BsjY?r+my{^frV8DvSUC|S0d6{W~hS-->-J_N~~rV&95=EB39}w_^Wm?0=2@ud)9%_P@sd*Vz9W`(I=K>xJv~=eRbv zU!R1mtb-e~vJNL@WgSh;%K97oh5O9Q%Ie>fm36%^E6ZJ+mE|eT%JP^rdUz`g_f4(vOy@4&tT`wr|ou_5W(BkVuI{v+%^!u})dKl*cAk00O0>W2KnH4XWt>l^Yb5*qRkZfwXuoYatiG_@iB zZ|t)h@;i4m>b!UuyDXR+rcrWI$gKg(;f&hnb9v%DtjEU(Er%WJaE@|vu(eEvRrz)`fQ%2BlV zkfUfxjiV^+h@&W?-cb~F%u%$e$x*cKgrjIfi=!y{14mJ2o1?y{&bciF0??y_|e z?y?Qb+-1p8?y}6~?y?;#+-190yUPmKxXVh{yUQvP++_zhy2}nHxyz2Gy33j}++{7> z++}Uq?y}BZ?y{adcUk`)ciHtqcbU7`UFIoumwC(GWxfh`nZMFq7R=!SXNKu%cAi7FLudOJ_wHvP@RARhGqy zwh>2nV(-M>iMA@MeHwPe-V4W?esj|e zzqw`1Z*IHkH+N3>%{{mL=Kd+a`TDfq?DqN1o>{-y>-U>|0l(QF^qYgvZ~>md{u%5& z*n6<|VDG{H8SJ0I{u%6_!TuTSJ=lA&_h9eA{u%6_!TuTSpTXXPy$5>__8#m#*gu2) zGuS_a{WI8mu=il^5%&M|pT4FZO-d_hH|MeINFGIiKu4 z?0MOLMIZKk*!N-IhkYORec1P5-^bDW6dLOyrN+8grLisv(OARO8f!$T#u^o-v94OG zv962IST`)wSd*hP*39J^>khu!@$S_cYvCG=wRFA4T9Kf!9^9z09!}C&kEUv@O&J<% z%QlU*En8#l+@-PhiBe>#tZ0jj7fk+@C|#DpiZW%q zsP3;sS+Z@c=xO5U1K1B>KY;xJ_5;`tU_XHU0QLje4`4rl{Q&j@*biVofc*gWF6>>{ zyRdg*@50`Ny$gF6_AcyQ*t@WIVei7;g}wM=EY|^j?V>7u?cziF+9fsm+OQ+~+K766 zZPYP+?W!hy?Ya~C+6^uG+T;)PwV7@D+8w9$wYxj@wT0*PwWU4!+KOI%?ZE+k?cqUv z?a^U_gZ*RQYj1@yK4puRS^ zn+vcT``y@2Vn2!fB=(co@5X*N_Pep)js0%yC$XQzeiHjh>~~|o8~feZ@5X)-`$_C4 zv7f|#68qiQ@5X*N_Pep4#C{U{Nn!ub{@Gs{WJ#<6(Q#C{O_LF@;yAH;qT`$6mnu^+^K5c@&w2eBW-eh~X1?1!)) z!hQ(*A?$~+AHseJ`yuRyuph#H2>T)Ihp-oalrf|*HY`#a8y2gK4NF3d4Pk0yLqw>t zAu7z+uxhEXVO@l=VZ$vn9p(oGS(7(snaJ|sj;4U^ccuI{8-g0Auufo{iuQWCU z#S2IBvCqf;GweUZ{xj@9!#*GTeC+eF&&NI=`_Hid4ExWp{|x(l?DMhD$37qX&#?at z`_Hid4ExWp&&NI=`+V&4vHuMF&#?cD3t(8b>@87}ESVK;mZh+wRM{3*lqO4OMH#Y8 zRX^y4s>x(qcfw@b&|=dpht`{%LuV(-P?EA0RIKYuGpmduJa%TicTs%#4@N|U9tq6}FkE7~f{ zVny3zPqU)!GLC)&`y1Hb!2SmIH?Y5f{SEAIV1EPq8`$5#{s#6pu)l%*4eW1VKZ^Y* z_M_O3Vn2%gDE6b+k77TH{V4XM*pFgAiv1||qa1xy(P&?!Y_u;{HQJYiG}^<|jrNGp zMtf9PqkYxVM*F&mM*D_kjrQcIMtkP+M*EHxjrQHE8|{T_8ttX)8|@VdjrM~Z8|{aa z8tq3@8|_UQjrNvpjrO+eMtkS3Mte_QqrHDmqy2hequpKHX!n#h+P&qCc3(xK-Cx;g z=Y>HEMFI8&*nf`w=h%Oa{pZ*hU|)cJ0rmyh7hwN6_Mc<_Irg7pUx0l9_667%VE;Mx zpJV?y_Mc<_IratE7hqq2eF65LWB)n!pK}5Da->H^$uhpk#Zgg;ER_{)k@1BPj*8M{ z8LTK%#*5{Tin3(eSkcomUQm5hWF(G0hW!}!W7v;jKZgAn_G8$OVLyib81`e>k6}NC z{TTLR*pFfF#@>y+8+$kQZtUIIyRmm;@5bJZy&HQs_HOLm*twP&_bhWpPb+xzM>}v0v=xXn|)z#iV)zyA|x~tvo>uUGR zcC~x`UG2U=SGzyh)gCP50u*9ji2XG7)7VdAKaG7M_J!CNVqb`TA@&DXp1b3 z6{X8ESW%{ID=W&9ZDU1G%eJ#3ql}~9#QrAsH?hBo{Y~s|Vt*6+o7msP{wDS}vA>D^ zP3&)Ce-rzg*pFjBj{P|H$FU#BejNL8?8R?wj4Q?+i?OYFbI{!8q? z#J&joBJ7K>FT(yy?7zhROYFbI{!8qOurI>C2>T-Jzr_Aa?7!p!Brjk7wrH~~g%zdB zwy>f!Svo7qkY%!>t+Fguv`zLjE7~qIvZ8F_=o8paU_XKV1ojiyPhdZR{RH+C*iT?T zf&B#b6WC8+KY{%O_8#m#*n6<|VDG`+gS`iP5B47HJ=lA&_h9eA-h;h|qk9enoQtXg z&c%lU&LuSgXV{T|Gon7=j5-!@u4)Q6*PRGBH?#zt$sYuqnQZ~*j?)3>?#_U-@O;2o z+7obA^ah*<2LjH+g8}E!;efO0M!?xJ7I3!R3^+R{0?wXW0cZbIz z>GcPkzCgg~4+fmUVlF^2_Qlxyu=io_!`_E|G4{pS7h_+HeKGbv?0wk#u=inKjD0co z#n=~P@5A1Qy$^dI_CD;3u`mAN^PZ;|dmr{b?0v%iU;fLtH_K93QL1bUD@v24v!V=H zCM()1%VI^_WKXlA?J^@P%9e5TTiD;i{ucJPu)l@~CRz3;RjzC$XQzeiHjh>?g6G#C{U{N$e-FpTvF=`$_C4u@_HqCKchXMapp3VpX_n zNl3UWOdalu2n}~dg@wCTEe&_AiwJjZSQhR|jtX~WE)RF@SP|~py*k`gxF+0Hx<1@h zkr3`WxG~&yI4Rt9G&S7Slo9S~*%t0<%MN#S?h1GHCKne&5CvqN1wue3i~PSr?8*GehT|3?5D7w!hQ<- zDeR}PpTd3$`zh?Fu=ir`#omj(7ke-EUhKWtd$IRo@5SDWy%&3aUv0vRz4&FW_dwjp zqN=!&#fRcXmejZHvZ^U=WZj9lkqs?zBgr4cjbygPjqErbH?q4k zZlv&h+(>Cp+(<=l+{nR!xRJwyaU(~E<3^fp#ErC!#f`Mxj2r2kh#Tp-6*tm96*qEy zI&Q@6iyQIG#*KLWaU;G!+=xFIHxex60+eFUzXqqC#eNq1S?p)AFU7tT`%>&nu`k7b z7W-N3XR)8fz7+dX>`Srd%f&X$Vn2)hEcUb5&thMSeJS>(KYZTv&SF1{{j9KGwdz=k zER_{)k)^SsbXf)~%9L$oMOm_KtmtXkc2;DRWwW9kGLHTQ_FrKC1@>QH{{{A6VE+a7 zUts?Q_FrKC1@>QH{{{A6VE+a7Utm9t{WSK|*iU0Wjr}zC)7VdAKaKq~_S4u;V?T}k zH1^_`I@5|A_abGEd$B6Vy(A>Z9j4B4M}+3Mqr!6BtCr@t*G1&GH!RC>Cr9PDGnePM zcdW>9?_QnbE?kr2E?uAFu1LsnAKaMZKAe=}KAM{2Zpz4Uw`|LCw`J$JJ9p){d-8JJ z{d;oU*9&vp?&2J`r!>dyE$5GOD{|ca${cr4yil|Z`!ejm!TuZUzrp?+?8~q(!@dmr zGVIH+{|5VSu>S`8Z?G@Jz6|>^?8~tK2K#TY{|5VSu>S`8GVIH+FT=i!pZnin{|)xv zZ~;KJ0zi`>^+6 z@5A1Qz4)Iy-vP5{QI*-V_>kGNq{i$CJ7V@k)SEp~$IPBpO=i!!6K2nb7PBY$1G6Wy z&FtB6+U(igY4#MJH+xEZ%$|x~v*+M|*>iZ%>^VAY_B7ovds@cKp0=B2Pv?Z$({s!0 z>7O!tu1}jiZlBrXnKgU7{I2u33s8=IIre_+`B&exe(e3&mt$X!eL42! z*q3AP$KH>N#{O&UzsCM+?7znTYwW+qeir*#>}Ro`#eNq1S?p)ApT&L_`&sN~v7g0$ z7W-N3#izVkh0VK2Y4a{t*}O|aY~C=n%^MMF^G1c)ysMVlyz3%t-VMua-sC8oH*>ko zyJLmTyL+|GTe!yNEnRQ(RwUTG2RGWhhm&mHqp3D;Q-;mkvd!jg%eHwtciFr>c{XqV z9-H@iq0Q?qws}3JHm|qb=Ji$ZTRfFEZ&18Q*o3_a`){%T7W;3p{}y`__9pC2*qg97 zVgD`m-(vqQ_TOS}!rp|v340Uv-(vqQ_TOUvE%x7HZ^GV$y$O30_TTbz|6A<8aM>9Pz~lquWFin3(eSkcq6?X1Ws%VtG8WII_=4srB3?B}qb!+s9?Iqc`K zpTm9*`#J3Au%E+z4*NOm=dho{ehzy-_I~XB*!!{fWADe_kG&szKlXm?{n-1l_hawJ zUi`H8A8`5>RXKf&4>^5HYMj2XBTipLz0((U%;{Uz8t2<`VI~_eTN5~zN5oVU(*ezuVu{XYrE<6bxt^aJ-3{`{wb&L z`n1#M_Bnl?S*OqIck+r{C-?941uM7!71&o`AHY6+ZBlx4G`9kQLQC`ZQ8zr+4J?7zeQJM6#1{yXfy!~Q$$zr+4J z?7zeQJM6#1{yXfy!~Q$$1K0;u>bun%A#z&?O|0DE!tfWqTn zr1ba~t33WCAs&C2+T)K1_4uR0JpNTnJ^pnO9{+}A9)EI_$Dg^}%-EZ;{~r7AvHu?X@3A*yZ^qt?y%~Em_TOXwJ@(&Y|2_6* z?9JGlu{UG?J@(&Y|2_8KWB)z&X6((_o3S@z|2_8K^K<`uF2I)c>wh6ilclqw3|S^C z+A7OpMcZUgv!d-XBP+_5?O;VaWjU;97jg7??B}tc$9^9BdFVjsjlheS=f@*_^HnC_s9G5`8?l`=jZkK{PlU?adEbT?uP9K004@#N4Rb8 z$=hWmC9=IP2F&~bF!m(!3`7d@=f1mg+qf&z{u~wn#P|Lv5K#0$5da|HkO=EDaSxUr z`dyZ_+24_zzGHoPXk3jFqUL(~*yE|cuRrPpe}V1Fv%0*?scZo8(y2MEpD9BRbts$2 z?w7Ah&2iC%A`SJXbQPUXi5RN8iDV0yLtm|~iKa1l86k=JS_|#ZqpF({X)@TWS)96$yy(jk(&|Vcg<&%dQHY0t`3s*ugjWa zk^o}ZXI)L?=m9G62T{Y?o>;)%AQ!c2#k-7Hu5f7;{*@_LI8+Gd>la)2S_%?xOvflr* zx7!9EmVonuhR@Hqy56X2&N&}R;6#Nb6bx9I@G%D@vTk>gAI5;6%+)|_@8(sdhk2tF za@Q0@=4$+QdPcmffR;uN+@luftu^7}Tfcr2Tdk2s#nD$i?xzj{k&@1pNA`gM!c4`? zK>*XRg-j@3Sho0L4lo)9=QQ&z_lheT;3LtE zS#RxoUcbGKj~6C%^Ljt^e`@gJCGaup{tJE-N@DntHBkITghMfiJttGYa&oY~{xO@! zJly9s>E+$TFjQi;GEX#L>Yyczka6;2UJ|v*Tg87!=H`@jLoaq37_p>9H)fQ3Pr)2} zN!0v!eW&93MtpEPM^J|i&dhk&H6Ej`G;U(ZT9@j`@NR}VBn-ptJHkME120sr9<$p}&;WX^dn?eX&V2?vZy^7wm3*Q24I=C;~Ih=az$(x~((bM6VQ0ONE9Dj}<>_wH*#(!JNic z7kgja6^1S)T|X@)YEAET?3d&3Q(4eb>v7ha8!URopoNM-sl+QqTBjzak#C53#t$?! zjrE6Hx5k7c^@x)AM;AzN%2WSXll?JDA-7UjPP7N$a?Y@WTMW^ZEE;}Qf~5sbn$db# z`2E7#9zB8^9{Jt9Icyz%b>hK1dO`1&&kjX8TvM&?;EBRM2-_Q zZ%XJ|Uy;-#9 zG4ONJF=KSv_GQ&$eop!I(F0xgX`jB-(BKh`;LPP&&MA+f;J7NTY{GSMXPdMXG<$f) zKrcqyt?Fnvrw7FJYfq!St&p$`;1ytM7Dy$TgSw1uKO&0KZeGSJ;;P8}!8OxMw|@XAkD(a#uxn<+ouky%-|esLgW(wN4x!2LlPps_-(@RCqQ0 zg(nd|8;cEzCT!L7o`Bp?_^LEQ0dbIyW)^oGnBNHG^rbxpTeoTzIT%!F>};F>0T zvKqM*taW6Cpob+Zt2X13$0*yBGt&`9wPc4tM(_(>CUqeBcRKQX!z29}s7)ru(W{mTWeVIBJzmbk5M%wxHTeYw0Ox zVSFk6aXG#R&Sj#7?9noI`YFIdQ4X8PHPXo==#LdcM|8Nou#gjboh>V}v>=bp2!dFX z<9{9Uo*eIHfFs#xV9cm<7JTV_6tIcIB^3v~7o&e=UX9sLu*2=fjET_`j=m3puzt>v ztOf4E@wx49T(*r0)%CT#@+WB3T2OV5Ur4l=O+H&U2W+U~RQRs1@H+-7er9(WJ^GX! zQWy(p{|p;-ovZ+VOyO;K!lk`R8S#)#2z%|>z1>c(lNtZ>^*Bn{fAyEPLPv=#N#F`68b8Bk!cqc#jYT*}DVl0-9QOQ%*5;O!X+ literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb32-xbgr.bmp b/image/test/reftest/bmp/bmpsuite/q/rgb32-xbgr.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c6c05e1480ccc9f14d0d2a58e569e239a99abbc6 GIT binary patch literal 32650 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 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgb32h52.bmp b/image/test/reftest/bmp/bmpsuite/q/rgb32h52.bmp new file mode 100644 index 0000000000000000000000000000000000000000..db6e4538ef84f48baf706603bd919223d372444b GIT binary patch literal 32578 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 B3Bv#Y literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba16-1924.bmp b/image/test/reftest/bmp/bmpsuite/q/rgba16-1924.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6564098a4de1ad870b5b42ed58f4b884eb14e9d5 GIT binary patch literal 16522 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 H+0p2KhP!s3 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba16-1924.png b/image/test/reftest/bmp/bmpsuite/q/rgba16-1924.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc182f1537f47fdf6559e7b36fb36e72cd1160a GIT binary patch literal 2811 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 P`QMs;yZE=NclrMh0b0l| literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba16-5551.png b/image/test/reftest/bmp/bmpsuite/q/rgba16-5551.png new file mode 100644 index 0000000000000000000000000000000000000000..613126a8ab53826564504b1cc1c3fbad54ddf18c GIT binary patch literal 1226 zcmV;*1U37KP)Px#26R$RQvkpKzyQFFc)Pj)000SaNLh0L01FcU01FcV0GgZ_000DCNkl2wMJ0CmW2wqXCx&;elMYGF=46YSp^sxjSa|NaGH4yflSV2kws3?cSSG61n} zk^zW)lMJYZrd0hs3+Q7&4Ut?D*I1rG6*vInKIalz9Ds3O(#KNQ0K~pY1|arLG61n} zk^zW)lMKLo-y{PN`z9HHaldl4rGNp5eMtvPF#xeI8R(@LpmLw5bAFzq9b>@0;HzNn zU@=H{_BuTPSS%KmRhrK`0C08nEy4FYl|_xmW91I0vHjroP3Hyx$c$t-wbQwu-B-Z= za_K1KhwHikFdknkbb$Ij&L9XX%xbk-0N^`5oze8@rE~c>dlVA*@Zjjs&~=9j9dN(^ zR2@ou<~d$MVl;H&T;!~p(fjn|>{?lYj$3K2J5+TM*0sMMAw}qi1woKED;hfQov-dz zrP5wrdOQG^(@@Pt_#gyObtrRHmc;1ypY~2utL}V%ea$qztd0o{83V=`i_(dVBX1~7 z^ys~}b-UU0r$tXwE&EF$0Jd7KX1}?$BNy@zYe6)z_DX>W%w3Vfp9i zUU##nr6fk^i!6K8KT5~_kB=YIr=m)+jztHhR9_+Xn`+&-0~*6deCX?Im~Z<1t%MC@ ze?FXZVn6MG`mi1?z*wp2t7G5uJbez(8~0ty;;D7xhNELz%77pUqHBQVT4@PF&2e0V zTM_2-{kY)>CB+g~bd_L0Lc+-j6~w;65>l7}y09;lctnO9(GkzdZ}hF{HMb0iOE^BJ zT5Iwcx2iumyJLMLcZS3^WmD&@$@<7YiX5;t%Hbi&7op%+ZcR@J4MQ~!FkkC#gZRXs z5JEU-eUi_H-`KA5=MT}RVZ0GSISkF~0P}G_9>jg$FVCX>9pUI3(Y8GT-f7hvA#~UD z#j&36?Fp^1WVG2#+`?LSvP`5g-ozV^#+>puqm4C@N?K;KQE{#7c{a~paWw|$-QQArID%|`&@g))fk{R?oat@ z-QURo6>m@ML%yFZd_p1KhZ5nUe7fGZC-$M%l}W(=OVj0meR-x~YkJzHdl~n&IH0D_ zj#Vm}E>R`0U{+NP)jMEcus>)IY_Q9LXgKFl>;RS6Z$Ce$i^XM!>t8Z51>Uvqcuq^S zv$J;M?=NDz1?@yZYnB1{9H&VKV6CgxbpXbF`S@1-8h~-% oBm)rpCK-U(H^~6RzDWlB1x8||Z!x<_E&u=k07*qoM6N<$f{=$uf&c&j literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.bmp b/image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1a918cebf5caf486faafaf3488ef4be712b09aab GIT binary patch literal 32650 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 INi+2S0m0w)Jpcdz literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.png b/image/test/reftest/bmp/bmpsuite/q/rgba32-1010102.png new file mode 100644 index 0000000000000000000000000000000000000000..a472fbc799e338cd8b355b982f1b62ec5ff8049e GIT binary patch literal 1253 zcmZ{kX;2b)0LA~{ks3;+wrOUWWrsA4p_QwqAt8YWi;2EMTlj2cTSlm zI4!j{G6K1!P6W6yXn9dy7%E6vCSDMp0lMzTo!y=Jz4zwLyw9(I5E*7~=V}K4us@7N z6PGi4Ss^yo%Z|MDgt{D%bYd6^@H*T^my2y0HZC22m5zT40?I4a0bp5p7=1A2ymF!< zDBS6wQ}faZ#O!g-){IWdikipHuDkVZq=yAoUa?qT6~t?Jl8_@s6N(Gg-duMaWudWK zBe%ojzX+rbxY14e+fP?1go7;6ds^ILdf7m8N5_LXhVhi@r7+$DVk-qOi`So$M41=) zT}>nYP-xQ|&%BBm<%B45y=25D$G4}srNs{1!Ud@@EuV_vA-J;gv#++%{~I~fM5gpF z7Q>nBDEYrf!Kpe!Xehh+MwENr{%$QTBRwW-oV4f;h76xau$p}jvOwdN6%Tq-N-zhA z0lN?gwvQZ6P>rZ9Ky`a{ZF{>Z;;di&<{b4I=QU+Jnwnbyb77&rpiiG*M6>o&!wR&D zyzD^!Tb)y;C@t|sn#x|x*wv7La7ezT_4CnN5?lJHojT6Wdk@dZ%5nDrlPaU;XtIq* zGhP?PA@LnuReCa@L>Tu#Eo`}!bt&=w8Nb>HD>E-e7y|<45*z0#W<7hnCzEu(yoFB4 zqrt8xU$tg#pRu9JYRNda+G_rof}TezyC&JP71w$pTY38KkIBdPLP8vhVM*B(wPAQ; zFwT8wCO7pgg0e&j%_pAeZ;VmUOU}1eUxiyuUGIK%a$DOXQSwWB@cuMrDdVbt|9J(5 zqp=MxqB8cG<_*5Jp1<8Ibtk=gC*<(%z~LLIOvMI)m`GBwMk#8z>C|JF-rmITLMG2S zd@Zqj+oV5t$dNv+qdr%@&n2(kjJ?KR`F5NeN50;h1<5ZY%&xZ{X9e6ny}1#=U+}04 zT?4AqkEA9G(}g@AX1pYp*GA!u=*RVch(8wz{4YNp^~s*4SzPS8xbD8IKn@bkBynJk zJNw9r4O7bei{XXEqr1bO z@oB6_FHI7IsR2nB1*&N%uTdCzU~5SY+hx17ejsL+^F^9FXt2?Cvd*>prUf&>S(?DgE=EkW*gIrt2MXySmyefMVs1CX zj03ie-ijl($mdXM_ZUI*aGlM&Ni`O8-Y4j+m{^H52b{{p*ebmIU3 literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba32-61754.bmp b/image/test/reftest/bmp/bmpsuite/q/rgba32-61754.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d5936fd20ba66c6523e0ddd6b010c5dbd40c2a6c GIT binary patch literal 32650 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 G_5TBf8bMkB literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba32-81284.png b/image/test/reftest/bmp/bmpsuite/q/rgba32-81284.png new file mode 100644 index 0000000000000000000000000000000000000000..c958cd36fcbc198065cf24138d7c42cd3fbf01d7 GIT binary patch literal 2182 zcma);dpy(oAIHBg4M|EU;~1fwSj=5QE@3LEQ1nA53q@qvTs9kpjy5K8Ye{q=r+Qrz8~<#vO20{|cgcX7P9 zqxhXUwGXuO28c!K?ug`Fd$@=69%)HrP5if|%pDi+y8s}2;M<4+f94$m0BK#gqn$@| z9ykA7u&T3SE7@MQA<;h10gff~+25YkbQ71e8F+E?9n93_eKI@UK|I;#-0zD+m5&f5 z)ohtc-v>wB9`2zlo|P{}8D2?M}qyZ=0gCO6pbXA1XudgprE0MGmdgH zc`$oMYq^+_jSsqJ?0CY+H8!9EfeuUtd{+dv3#`GRBoRs85(w;@odjDwhH&bvbX(NnT2(t;4OXv04Lx1Yeh&I6!Q;kk!8j+MyGt??rzRr%< z?=(_@=zPyZt@i%tg1g_}-;XY-kOP3FLu!1CW`5sD9gTvULN3w?ccQu87KfF(^_jQD-4GXUGHegjdOiw5?g=JJJ43|ia{wDzB~U9?f6Oe z7F|{e%{a22*6)T}5M*K3Nb1&`t!;mM@mRIKw$rPoMn*>H4P-=c&ur;~RR8c<*<;EJ z3j{i_sOtYqNmCP*+BBw*M-fE=UMA`5W;vCq?-obFTT>{Mf?6?+`FXvl6(5RH`s)fP zsUPly%B!xXf`*3yGjn`>UER(62F0V`Xx=29es|`dj`v4#k2YUYoydxg2yBhJWWbGd zWV3Oha$v|pT`$`!{_J!s?<~Vl(h58@JRBRj-S{PD<0BQa$U3OfYzIH``9o?#!ZW)9 zk-JvDdTq8__N8K1h~A5E04Lg5z}cQM%N3r;t+Jq{fc%<|ov?b{Go0du*%k*6Yqf$Fw8s4}%*%-U=`?mmduYGb2&g zbKc^%)=Q?7YsQske1lG2f@g%if_nwkT051%a5;;nZ$~_Q_TE0|IVKT&3oNWFmC+!J z-s+JD^XeQW>i!kR?|atQG^OfifYb4CD4VJtP5m;N$+N^mY_q_zf4XL6W?r`J3I~d# z=Mr?q+xSsr-4A19yM{i0-arLEH*uRvzWh9h(|WxUw&p=sK?p%Nf?YPE9uem&Mvcwg z4@ay}9L3}D%oZW&uN66K7Nm5=xKUg_HQySu7B<1~$jricnbh-o(|Bd1x`g819EZ>; zzZltQ;?QWGY`0AEO#JERMYB}~WOD2q!t-Pgvo>C(y*s46| z{ZguT7Cy*Z7t#CVxF~NrGmw+sEpB3do)C&A%jBd~Kgr$le9WI{d4MCdiUAte{=I*J zKlByGj4d_T#hocKw6u)z%m5JHU&0U^Ok>&jz|*I$r`g9mKXq=Oh<5+AS15dAVJm-4 z1S{G=amPv^K^Tm172OWCYiNx1^mf@`nN8O+Zo6xCSzO!1Uk%NA+J{;%6g1l6+D^w0l9o6Ct z9!ZO96BK;B;&SFpl}}v|^&|pY5V7dMSl3=+Di~kT$qelrc(s=?-8iX;+_|L*3_&?^ zc|58?tvtJTq+$KgEnd5zs)fTaC+ob_>H52s6f%BZI~J3Vhqp$0jql?0m*XfC-W*kx z3cuf-?vu-!p9aV;EWD`Taj~D!J7qbFMm85bKTH_7CAQF$Y#ImU6(a(O{<#7K78lJB z`1&%NW46_RQ_@BOGQa36NpzT9Ej}jcx^5eNXWF4aZ=IN?w_Z8X;W{X#r^m7;9gtJA zLNB5(U!HTdxIoKrYckpkh{y6My|c;$El@X~aG0?CCJ(L2p*-`$#dca7hQt#|%0@o` zF)8@U^W#`|fzxkH)yUkjW0WXEFa)v|hm!b>Dg!KMS(P8iDP^zx!w{o^dcfmlw$(Qw zw|KA+pn?Y*=c@cau-akU|7?sM7@cA`%U1o=p8?;Q=EV_7DM0gUMdx^;osR?nKkerD J(*D}L{{r9pO6ULp literal 0 HcmV?d00001 diff --git a/image/test/reftest/bmp/bmpsuite/q/rgba32h56.bmp b/image/test/reftest/bmp/bmpsuite/q/rgba32h56.bmp new file mode 100644 index 0000000000000000000000000000000000000000..343baa3300f331aaeabeb40d8fcd2f232393e815 GIT binary patch literal 32582 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 Q2$1Z + + +Image reftest wrapper + + + + + + + + + From e0301dc51a0363e80a060600841b82b45b323925 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Mon, 4 Dec 2017 18:54:12 +0000 Subject: [PATCH 48/78] Bug 1422915 - Send remote-active after socket has ceased listening. r=whimboo The remote-active system notification is currently sent before the TCP socket has stopped listening. It is marginally safer to send it as the last action. MozReview-Commit-ID: KhB6TMvyCPv --- testing/marionette/server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/marionette/server.js b/testing/marionette/server.js index af447b833bf4..22b347330018 100644 --- a/testing/marionette/server.js +++ b/testing/marionette/server.js @@ -385,11 +385,10 @@ server.TCPListener = class { } this.alteredPrefs.clear(); - Services.obs.notifyObservers(this, NOTIFY_RUNNING); - // Shutdown server socket, and no longer listen for new connections this.acceptConnections = false; + Services.obs.notifyObservers(this, NOTIFY_RUNNING); this.alive = false; } From ad7ff3a791ee39284b66db9ff003905b12c3ccc2 Mon Sep 17 00:00:00 2001 From: David Major Date: Wed, 6 Dec 2017 09:36:00 -0500 Subject: [PATCH 49/78] Bug 1423287 - Simplify NtCurrentTeb() handling. r=njn --- js/src/jsnativestack.cpp | 24 ------------------------ memory/replace/dmd/DMD.cpp | 17 +---------------- tools/profiler/core/platform-win32.cpp | 25 +------------------------ 3 files changed, 2 insertions(+), 64 deletions(-) diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 40b680231726..e918fe92ed45 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -31,32 +31,8 @@ void* js::GetNativeStackBaseImpl() { -# if defined(_M_IX86) && defined(_MSC_VER) - /* - * offset 0x18 from the FS segment register gives a pointer to - * the thread information block for the current thread - */ - NT_TIB* pTib; - __asm { - MOV EAX, FS:[18h] - MOV pTib, EAX - } - return static_cast(pTib->StackBase); - -# elif defined(_M_X64) - PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); - return reinterpret_cast(pTib->StackBase); - -# elif defined(_M_ARM) PNT_TIB pTib = reinterpret_cast(NtCurrentTeb()); return static_cast(pTib->StackBase); - -# elif defined(_WIN32) && defined(__GNUC__) - NT_TIB* pTib; - asm ("movl %%fs:0x18, %0\n" : "=r" (pTib)); - return static_cast(pTib->StackBase); - -# endif } #elif defined(SOLARIS) diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp index 4a9b8ba80e92..151712d3c9e4 100644 --- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -764,22 +764,7 @@ StackTrace::Get(Thread* aT) RtlCaptureContext(&context); void** fp = reinterpret_cast(context.Ebp); - // Offset 0x18 from the FS segment register gives a pointer to the thread - // information block for the current thread. -#if defined(_MSC_VER) - NT_TIB* pTib; - __asm { - MOV EAX, FS:[18h] - MOV pTib, EAX - } -#elif defined(__GNUC__) - NT_TIB* pTib; - asm ( "movl %%fs:0x18, %0\n" - : "=r" (pTib) - ); -#else -# error "unknown compiler" -#endif + PNT_TIB pTib = reinterpret_cast(NtCurrentTeb()); void* stackEnd = static_cast(pTib->StackBase); FramePointerStackWalk(StackWalkCallback, /* skipFrames = */ 0, MaxFrames, &tmp, fp, stackEnd); diff --git a/tools/profiler/core/platform-win32.cpp b/tools/profiler/core/platform-win32.cpp index 0836b42a1762..f4466a894282 100644 --- a/tools/profiler/core/platform-win32.cpp +++ b/tools/profiler/core/platform-win32.cpp @@ -51,31 +51,8 @@ Thread::GetCurrentId() void* GetStackTop(void* aGuess) { -#if defined(GP_ARCH_x86) - // Offset 0x18 from the FS segment register gives a pointer to the thread - // information block for the current thread. - NT_TIB* pTib; -#if defined(_MSC_VER) - __asm { - MOV EAX, FS:[18h] - MOV pTib, EAX - } -#elif defined(__GNUC__) - asm ( "movl %%fs:0x18, %0\n" - : "=r" (pTib) - ); -#else -#error "unimplemented" -#endif - return static_cast(pTib->StackBase); - -#elif defined(GP_ARCH_amd64) - PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); + PNT_TIB pTib = reinterpret_cast(NtCurrentTeb()); return reinterpret_cast(pTib->StackBase); - -#else -#error "unimplemented" -#endif } static void From f7df533e97f9e0ee83111f999918bdb08f738662 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:37 +0900 Subject: [PATCH 50/78] Bug 1228841 - Part 0: Remove remaining conditional catch consumers in dom/. r=billm --- .../mochitest/test_peerConnection_transceivers.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dom/media/tests/mochitest/test_peerConnection_transceivers.html b/dom/media/tests/mochitest/test_peerConnection_transceivers.html index 53c4f861d140..aabf15879ee6 100644 --- a/dom/media/tests/mochitest/test_peerConnection_transceivers.html +++ b/dom/media/tests/mochitest/test_peerConnection_transceivers.html @@ -423,11 +423,12 @@ pc.addTransceiver("foo"); ok(false, 'addTransceiver("foo") throws'); } - catch (e if e instanceof TypeError) { - ok(true, 'addTransceiver("foo") throws a TypeError'); - } catch (e) { - ok(false, 'addTransceiver("foo") throws a TypeError'); + if (e instanceof TypeError) { + ok(true, 'addTransceiver("foo") throws a TypeError'); + } else { + ok(false, 'addTransceiver("foo") throws a TypeError'); + } } hasProps(pc.getTransceivers(), []); From d464aa8fa72f6450f4de6459c2f8ceeb53cf54cf Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:39 +0900 Subject: [PATCH 51/78] Bug 1228841 - Part 1: Remove conditional catch consumers in js/. r=evilpie --- js/src/jit-test/lib/nightly-only.js | 5 +++- js/src/jit-test/lib/syntax.js | 8 ------- .../baseline/classConstructor-AnyScripted.js | 5 +++- .../jit-test/tests/basic/bug593663-regexp.js | 4 +++- .../jit-test/tests/debug/Frame-onStep-12.js | 13 ---------- js/src/jit-test/tests/gc/bug-1259490.js | 6 ++++- js/src/jit-test/tests/ion/bug1293542.js | 6 ++++- js/src/jit-test/tests/ion/bug799185-4.js | 5 +++- js/src/jit-test/tests/ion/bug799185-5.js | 13 +++++++--- js/src/jit-test/tests/ion/bug799185-7.js | 11 ++------- js/src/jit-test/tests/ion/throw.js | 7 +++--- js/src/jit-test/tests/parser/arrow-rest.js | 4 ---- .../tests/ecma_3_1/Object/regress-444787.js | 15 ++++++------ .../arrow-not-as-end-of-statement.js | 16 ------------- js/src/tests/ecma_6/Class/newTargetEval.js | 10 ++++++-- js/src/tests/ecma_6/Class/outerBinding.js | 5 +++- .../tests/ecma_6/Class/superCallThisInit.js | 5 +++- .../destructuring-pattern-parenthesized.js | 5 ++-- .../tests/js1_5/extensions/regress-104077.js | 24 ++++++++++--------- .../tests/js1_7/extensions/regress-350312.js | 4 +++- js/src/tests/js1_8_5/reflect-parse/Match.js | 8 +++++-- .../js1_8_5/reflect-parse/PatternAsserts.js | 4 +++- .../reflect-parse/builderExceptions.js | 4 +++- 23 files changed, 95 insertions(+), 92 deletions(-) diff --git a/js/src/jit-test/lib/nightly-only.js b/js/src/jit-test/lib/nightly-only.js index ef57765e79e5..471a2d7431f8 100644 --- a/js/src/jit-test/lib/nightly-only.js +++ b/js/src/jit-test/lib/nightly-only.js @@ -11,7 +11,10 @@ function nightlyOnly(error, f) { try { f(); throw new Error("use of feature expected to fail on release and beta, but succeeded; please update test"); - } catch (e if e instanceof error) { + } catch (e) { + if (!(e instanceof error)) { + throw e; + } // All is well. } } else { diff --git a/js/src/jit-test/lib/syntax.js b/js/src/jit-test/lib/syntax.js index 130b0b169d5b..81d14df7d9fc 100644 --- a/js/src/jit-test/lib/syntax.js +++ b/js/src/jit-test/lib/syntax.js @@ -184,14 +184,6 @@ function test_syntax(postfixes, check_error, ignore_opts) { test("try {} catch (e) {} finally { "); test("try {} catch (e) {} finally {} "); - test("try {} catch (e if "); - test("try {} catch (e if e "); - test("try {} catch (e if e instanceof "); - test("try {} catch (e if e instanceof x "); - test("try {} catch (e if e instanceof x) "); - test("try {} catch (e if e instanceof x) { "); - test("try {} catch (e if e instanceof x) {} "); - // ---- Declarations ---- // var diff --git a/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js b/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js index 8acb052c7469..763a8bb7d9fa 100644 --- a/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js +++ b/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js @@ -18,4 +18,7 @@ for (let i = 0; i < 11; i++) try { test(foo); throw new Error("Invoking a class constructor without new must throw"); -} catch (e if e instanceof TypeError) { } +} catch (e) { + if (!(e instanceof TypeError)) + throw e; +} diff --git a/js/src/jit-test/tests/basic/bug593663-regexp.js b/js/src/jit-test/tests/basic/bug593663-regexp.js index 10133c30032f..012a7fb4cd72 100644 --- a/js/src/jit-test/tests/basic/bug593663-regexp.js +++ b/js/src/jit-test/tests/basic/bug593663-regexp.js @@ -7,7 +7,9 @@ function isPatternSyntaxError(pattern) { try { new RegExp(pattern); return false; - } catch (e if e instanceof SyntaxError) { + } catch (e) { + if (!(e instanceof SyntaxError)) + throw e; return true; } } diff --git a/js/src/jit-test/tests/debug/Frame-onStep-12.js b/js/src/jit-test/tests/debug/Frame-onStep-12.js index 4db38b313606..f3a95bf8c53a 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-12.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-12.js @@ -87,19 +87,6 @@ testOne("testCatchFinally", nothing(); // +8 `, "1689"); -// The same but without a finally clause. This relies on a -// SpiderMonkey extension, because otherwise there's no way to see -// extra instructions at the end of a catch. -testOne("testCatch", - `try { - throw new TypeError(); - } catch (e if e instanceof TypeError) { - ${bitOfCode} - } catch (e) { // +6 - } // +7 - nothing(); // +8 - `, "189"); - // Test the instruction at the end of a "finally" clause. testOne("testFinally", `try { diff --git a/js/src/jit-test/tests/gc/bug-1259490.js b/js/src/jit-test/tests/gc/bug-1259490.js index 063b4236e18f..24868120440b 100644 --- a/js/src/jit-test/tests/gc/bug-1259490.js +++ b/js/src/jit-test/tests/gc/bug-1259490.js @@ -1,4 +1,8 @@ -try { eval("3 ** 4") } catch (e if e instanceof SyntaxError) { quit(); }; +try { eval("3 ** 4") } catch (e) { + if (!(e instanceof SyntaxError)) + throw e; + quit(); +} eval(` gczeal(8); diff --git a/js/src/jit-test/tests/ion/bug1293542.js b/js/src/jit-test/tests/ion/bug1293542.js index 3f59d7b61be4..38c27125e459 100644 --- a/js/src/jit-test/tests/ion/bug1293542.js +++ b/js/src/jit-test/tests/ion/bug1293542.js @@ -1,5 +1,9 @@ -try { eval("3 ** 4") } catch (e if e instanceof SyntaxError) { quit(); }; +try { eval("3 ** 4") } catch (e) { + if (!(e instanceof SyntaxError)) + throw e; + quit(); +} var f = new Function("x", "return (x ** (1 / ~4294967297)) && x"); for (var i = 0; i < 2; ++i) { diff --git a/js/src/jit-test/tests/ion/bug799185-4.js b/js/src/jit-test/tests/ion/bug799185-4.js index a369964390f1..964e5c9f20ce 100644 --- a/js/src/jit-test/tests/ion/bug799185-4.js +++ b/js/src/jit-test/tests/ion/bug799185-4.js @@ -1,7 +1,10 @@ function foo(aObject) { try { } - catch (ex if (ex.name == "TypeError")) { } + catch (ex) { + if (ex.name != "TypeError") + throw ex; + } try { Object.getPrototypeOf(aObject); } catch (ex) { } } diff --git a/js/src/jit-test/tests/ion/bug799185-5.js b/js/src/jit-test/tests/ion/bug799185-5.js index 9d3ec897778e..ba5435981865 100644 --- a/js/src/jit-test/tests/ion/bug799185-5.js +++ b/js/src/jit-test/tests/ion/bug799185-5.js @@ -5,13 +5,20 @@ function foo(aObject) if (!aObject) return; } - catch (ex if (ex.name == "TypeError")) { } + catch (ex) { + if (ex.name != "TypeError") + throw ex; + } finally { } undefined.x; } - catch (ex if (ex.name == "TypeError")) { } - catch (ex if (ex.name == "TypeError")) { } + catch (ex) { + if (ex.name != "TypeError") + throw ex; + if (ex.name != "TypeError") + throw ex; + } finally { } } diff --git a/js/src/jit-test/tests/ion/bug799185-7.js b/js/src/jit-test/tests/ion/bug799185-7.js index 51174e21c484..86cc0c8afc94 100644 --- a/js/src/jit-test/tests/ion/bug799185-7.js +++ b/js/src/jit-test/tests/ion/bug799185-7.js @@ -7,16 +7,9 @@ try {} catch (x) { } } -try {} catch (x if y) { - try {} catch (x if y) { - try {} catch (x if y) { - } - } -} - while (false) { - try {} catch ({x,y} if x) { - try {} catch ({a,b,c,d} if a) { + try {} catch ({x,y}) { + try {} catch ({a,b,c,d}) { if (b) break; if (c) continue; } diff --git a/js/src/jit-test/tests/ion/throw.js b/js/src/jit-test/tests/ion/throw.js index 808345f882ae..e81558194b98 100644 --- a/js/src/jit-test/tests/ion/throw.js +++ b/js/src/jit-test/tests/ion/throw.js @@ -100,10 +100,11 @@ function test5() { for (var i=0; i<40; i++) { try { throw i; - } catch (e if e % 2) { - res += e; } catch (e) { - res += e * 3; + if (e % 2) + res += e; + else + res += e * 3; } } return res; diff --git a/js/src/jit-test/tests/parser/arrow-rest.js b/js/src/jit-test/tests/parser/arrow-rest.js index 193efa43a1f8..c4ba373c0783 100644 --- a/js/src/jit-test/tests/parser/arrow-rest.js +++ b/js/src/jit-test/tests/parser/arrow-rest.js @@ -135,10 +135,6 @@ testThrow(` throw ...a) => `, 6); -testThrow(` -try {} catch (x if ...a) => -`, 19); - // class testThrow(` diff --git a/js/src/tests/ecma_3_1/Object/regress-444787.js b/js/src/tests/ecma_3_1/Object/regress-444787.js index 06b811987a1c..08e1496860d4 100644 --- a/js/src/tests/ecma_3_1/Object/regress-444787.js +++ b/js/src/tests/ecma_3_1/Object/regress-444787.js @@ -53,14 +53,13 @@ function test() actual = Object.getPrototypeOf(instance); reportCompare(expect, actual, summary + ': new ' + type.name); } - catch(ex if ex instanceof TypeError) - { - print('Ignore ' + ex); - } - catch(ex) - { - actual = ex + ''; - reportCompare(expect, actual, summary + ': new ' + type.name); + catch(ex) { + if (ex instanceof TypeError) { + print('Ignore ' + ex); + } else { + actual = ex + ''; + reportCompare(expect, actual, summary + ': new ' + type.name); + } } } diff --git a/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js b/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js index 1cafd7788da2..53926d65da46 100644 --- a/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js +++ b/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js @@ -72,22 +72,6 @@ switch (1) break; } -try -{ - // Catch guards are non-standard, so ignore a syntax error. - eval(`try - { - } - catch (x if a => {}) - { - }`); -} -catch (e) -{ - assertEq(e instanceof SyntaxError, true, - "should only have thrown SyntaxError, instead got " + e); -} - assertEq(0[a => {}], undefined); class Y {}; diff --git a/js/src/tests/ecma_6/Class/newTargetEval.js b/js/src/tests/ecma_6/Class/newTargetEval.js index ff639e4a662a..f7581c5ab8bb 100644 --- a/js/src/tests/ecma_6/Class/newTargetEval.js +++ b/js/src/tests/ecma_6/Class/newTargetEval.js @@ -2,7 +2,10 @@ try { eval('new.target'); assertEq(false, true); -} catch (e if e instanceof SyntaxError) { } +} catch (e) { + if (!(e instanceof SyntaxError)) + throw e; +} // new.target is invalid inside eval inside top-level arrow functions assertThrowsInstanceOf(() => eval('new.target'), SyntaxError); @@ -12,7 +15,10 @@ let ieval = eval; try { (function () { return ieval('new.target'); })(); assertEq(false, true); -} catch (e if e instanceof SyntaxError) { } +} catch (e) { + if (!(e instanceof SyntaxError)) + throw e; +} function assertNewTarget(expected) { assertEq(eval('new.target'), expected); diff --git a/js/src/tests/ecma_6/Class/outerBinding.js b/js/src/tests/ecma_6/Class/outerBinding.js index e0f88a494e55..19ffcb3e4aea 100644 --- a/js/src/tests/ecma_6/Class/outerBinding.js +++ b/js/src/tests/ecma_6/Class/outerBinding.js @@ -28,7 +28,10 @@ evaluate("const globalConstant = 0; var earlyError = true;"); try { evaluate("earlyError = false; class globalConstant { constructor() { } }"); -} catch (e if e instanceof SyntaxError) { } +} catch (e) { + if (!(e instanceof SyntaxError)) + throw e; +} assertEq(earlyError, true); function strictEvalShadows() { diff --git a/js/src/tests/ecma_6/Class/superCallThisInit.js b/js/src/tests/ecma_6/Class/superCallThisInit.js index 4154b36119d5..935f6b69ffee 100644 --- a/js/src/tests/ecma_6/Class/superCallThisInit.js +++ b/js/src/tests/ecma_6/Class/superCallThisInit.js @@ -7,7 +7,10 @@ class testInitialize extends base { try { this; throw new Error(); - } catch (e if e instanceof ReferenceError) { } + } catch (e) { + if (!(e instanceof ReferenceError)) + throw e; + } super(); assertEq(this.prop, 42); } diff --git a/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js b/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js index ca495c88684b..ef49fecf593a 100644 --- a/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js +++ b/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js @@ -96,8 +96,9 @@ function classesEnabled() new Function("class B { constructor() { } }; class D extends B { constructor() { super(); } }"); return true; } - catch (e if e instanceof SyntaxError) - { + catch (e) { + if (!(e instanceof SyntaxError)) + throw e; return false; } } diff --git a/js/src/tests/js1_5/extensions/regress-104077.js b/js/src/tests/js1_5/extensions/regress-104077.js index 91bf4e0704a1..189f0ce90354 100644 --- a/js/src/tests/js1_5/extensions/regress-104077.js +++ b/js/src/tests/js1_5/extensions/regress-104077.js @@ -70,8 +70,9 @@ function addValues_3(obj) sum +=1; print("In finally block of addValues_3() function: sum = " + sum); } - catch (e if e == 42) - { + catch (e) { + if (e != 42) + throw e; sum +=1; print('In finally catch block of addValues_3() function: sum = ' + sum + ', e = ' + e); } @@ -132,15 +133,16 @@ function addValues_4(obj) sum += 1; print("In finally block of addValues_4() function: sum = " + sum); } - catch (e if e == 42) - { - sum += 1; - print("In 1st finally catch block of addValues_4() function: sum = " + sum + ", e = " + e); - } - catch (e if e == 43) - { - sum += 1; - print("In 2nd finally catch block of addValues_4() function: sum = " + sum + ", e = " + e); + catch (e) { + if (e == 42) { + sum += 1; + print("In 1st finally catch block of addValues_4() function: sum = " + sum + ", e = " + e); + } else if (e == 43) { + sum += 1; + print("In 2nd finally catch block of addValues_4() function: sum = " + sum + ", e = " + e); + } else { + throw e; + } } finally { diff --git a/js/src/tests/js1_7/extensions/regress-350312.js b/js/src/tests/js1_7/extensions/regress-350312.js index 8f266aa9e5c1..c10d84901827 100644 --- a/js/src/tests/js1_7/extensions/regress-350312.js +++ b/js/src/tests/js1_7/extensions/regress-350312.js @@ -24,7 +24,9 @@ function test() { try { yield iter; - } catch (e if e == null) { + } catch (e) { + if (e != null) + throw e; actual += 'CATCH,'; print("CATCH"); } finally { diff --git a/js/src/tests/js1_8_5/reflect-parse/Match.js b/js/src/tests/js1_8_5/reflect-parse/Match.js index ae01292a0c7e..15abc0a564db 100644 --- a/js/src/tests/js1_8_5/reflect-parse/Match.js +++ b/js/src/tests/js1_8_5/reflect-parse/Match.js @@ -22,7 +22,9 @@ var Match = try { return this.match(act); } - catch (e if e instanceof MatchError) { + catch (e) { + if (!(e instanceof MatchError)) + throw e; return false; } }, @@ -31,7 +33,9 @@ var Match = try { return this.match(act); } - catch (e if e instanceof MatchError) { + catch (e) { + if (!(e instanceof MatchError)) + throw e; throw new Error((message || "failed match") + ": " + e.message); } }, diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js b/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js index 8fd5840b6231..6b645f912468 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js @@ -86,7 +86,9 @@ function assertDecl(src, patt) { function assertError(src, errorType) { try { Reflect.parse(src); - } catch (expected if expected instanceof errorType) { + } catch (expected) { + if (!(expected instanceof errorType)) + throw expected; return; } throw new Error("expected " + errorType.name + " for " + uneval(src)); diff --git a/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js b/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js index e8430dbfa41e..d22ab473fe59 100644 --- a/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js +++ b/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js @@ -3,7 +3,9 @@ var thrown = false; try { Reflect.parse("42", { builder: { program: function() { throw "expected" } } }); -} catch (e if e === "expected") { +} catch (e) { + if (e !== "expected") + throw e; thrown = true; } if (!thrown) From 9c49a02b67bb2cab8bf51c44e1d7d06a8510f6f4 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:40 +0900 Subject: [PATCH 52/78] Bug 1228841 - Part 2: Remove testcases specific to conditional catch in js/. r=evilpie --- js/src/jit-test/tests/baseline/bug843811-1.js | 11 -- js/src/jit-test/tests/baseline/bug843811-2.js | 10 -- js/src/jit-test/tests/baseline/bug843811-3.js | 11 -- js/src/jit-test/tests/basic/bug640078.js | 4 - js/src/jit-test/tests/basic/bug787309.js | 7 -- js/src/jit-test/tests/basic/bug787848.js | 8 -- js/src/jit-test/tests/ion/bug799185-1.js | 14 --- js/src/jit-test/tests/jaeger/bug553781-2.js | 13 -- js/src/jit-test/tests/jaeger/bug553781.js | 11 -- .../ecma_5/extensions/iterator-in-catch.js | 20 ---- .../js1_5/extensions/catchguard-001-n.js | 38 ------ .../tests/js1_5/extensions/catchguard-001.js | 44 ------- .../tests/js1_5/extensions/catchguard-002.js | 39 ------ .../tests/js1_5/extensions/catchguard-003.js | 54 --------- .../js1_5/extensions/regress-346494-01.js | 86 ------------- .../tests/js1_5/extensions/regress-346494.js | 79 ------------ .../js1_5/extensions/regress-350312-02.js | 109 ----------------- .../js1_5/extensions/regress-350312-03.js | 113 ------------------ .../js1_5/extensions/regress-351102-01.js | 36 ------ .../js1_5/extensions/regress-351102-02.js | 43 ------- .../js1_5/extensions/regress-351102-06.js | 31 ----- .../tests/js1_5/extensions/regress-374589.js | 31 ----- .../js1_7/extensions/regress-351102-03.js | 41 ------- .../js1_7/extensions/regress-351102-04.js | 32 ----- .../js1_7/extensions/regress-351102-05.js | 31 ----- .../js1_7/extensions/regress-351102-07.js | 40 ------- js/src/tests/js1_7/regress/regress-375695.js | 24 ---- .../js1_8_5/extensions/regress-677589.js | 10 -- 28 files changed, 990 deletions(-) delete mode 100644 js/src/jit-test/tests/baseline/bug843811-1.js delete mode 100644 js/src/jit-test/tests/baseline/bug843811-2.js delete mode 100644 js/src/jit-test/tests/baseline/bug843811-3.js delete mode 100644 js/src/jit-test/tests/basic/bug640078.js delete mode 100644 js/src/jit-test/tests/basic/bug787309.js delete mode 100644 js/src/jit-test/tests/basic/bug787848.js delete mode 100644 js/src/jit-test/tests/ion/bug799185-1.js delete mode 100644 js/src/jit-test/tests/jaeger/bug553781-2.js delete mode 100644 js/src/jit-test/tests/jaeger/bug553781.js delete mode 100644 js/src/tests/ecma_5/extensions/iterator-in-catch.js delete mode 100644 js/src/tests/js1_5/extensions/catchguard-001-n.js delete mode 100644 js/src/tests/js1_5/extensions/catchguard-001.js delete mode 100644 js/src/tests/js1_5/extensions/catchguard-002.js delete mode 100644 js/src/tests/js1_5/extensions/catchguard-003.js delete mode 100644 js/src/tests/js1_5/extensions/regress-346494-01.js delete mode 100644 js/src/tests/js1_5/extensions/regress-346494.js delete mode 100644 js/src/tests/js1_5/extensions/regress-350312-02.js delete mode 100644 js/src/tests/js1_5/extensions/regress-350312-03.js delete mode 100644 js/src/tests/js1_5/extensions/regress-351102-01.js delete mode 100644 js/src/tests/js1_5/extensions/regress-351102-02.js delete mode 100644 js/src/tests/js1_5/extensions/regress-351102-06.js delete mode 100644 js/src/tests/js1_5/extensions/regress-374589.js delete mode 100644 js/src/tests/js1_7/extensions/regress-351102-03.js delete mode 100644 js/src/tests/js1_7/extensions/regress-351102-04.js delete mode 100644 js/src/tests/js1_7/extensions/regress-351102-05.js delete mode 100644 js/src/tests/js1_7/extensions/regress-351102-07.js delete mode 100644 js/src/tests/js1_7/regress/regress-375695.js delete mode 100644 js/src/tests/js1_8_5/extensions/regress-677589.js diff --git a/js/src/jit-test/tests/baseline/bug843811-1.js b/js/src/jit-test/tests/baseline/bug843811-1.js deleted file mode 100644 index 2fe9289e5060..000000000000 --- a/js/src/jit-test/tests/baseline/bug843811-1.js +++ /dev/null @@ -1,11 +0,0 @@ -// |jit-test| error: uncaught exception: -evalcx("\ - try {\ - throw\"\"\ - } catch (\ - x if (function(){\ - x\ - })()\ - ) {}\ -", newGlobal("")) - diff --git a/js/src/jit-test/tests/baseline/bug843811-2.js b/js/src/jit-test/tests/baseline/bug843811-2.js deleted file mode 100644 index 390e19a3eae7..000000000000 --- a/js/src/jit-test/tests/baseline/bug843811-2.js +++ /dev/null @@ -1,10 +0,0 @@ -// |jit-test| error: uncaught exception: -eval("\ - try {\ - throw\"\"\ - } catch (\ - x if (function(){\ - x\ - })()\ - ) {}\ -") diff --git a/js/src/jit-test/tests/baseline/bug843811-3.js b/js/src/jit-test/tests/baseline/bug843811-3.js deleted file mode 100644 index 697613e5aad7..000000000000 --- a/js/src/jit-test/tests/baseline/bug843811-3.js +++ /dev/null @@ -1,11 +0,0 @@ -// |jit-test| error: uncaught exception: -Function("\ - try {\ - throw\"\"\ - } catch (\ - x if (function(){\ - x\ - })()\ - ) {}\ -")() - diff --git a/js/src/jit-test/tests/basic/bug640078.js b/js/src/jit-test/tests/basic/bug640078.js deleted file mode 100644 index 27e5470815b9..000000000000 --- a/js/src/jit-test/tests/basic/bug640078.js +++ /dev/null @@ -1,4 +0,0 @@ -eval("\ - try{}\ - catch(w if(function(){})){4067286856}\ -") diff --git a/js/src/jit-test/tests/basic/bug787309.js b/js/src/jit-test/tests/basic/bug787309.js deleted file mode 100644 index 51129c88dfd1..000000000000 --- a/js/src/jit-test/tests/basic/bug787309.js +++ /dev/null @@ -1,7 +0,0 @@ -// |jit-test| error: TypeError -try { - h -} catch (x -if gc()) {} finally { - this.z.z -} diff --git a/js/src/jit-test/tests/basic/bug787848.js b/js/src/jit-test/tests/basic/bug787848.js deleted file mode 100644 index 2972a4c8c167..000000000000 --- a/js/src/jit-test/tests/basic/bug787848.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error: TypeError -try { - i -} -catch (x if (function() {})()) {} -catch (d) { - this.z.z -} diff --git a/js/src/jit-test/tests/ion/bug799185-1.js b/js/src/jit-test/tests/ion/bug799185-1.js deleted file mode 100644 index 3584bed2f463..000000000000 --- a/js/src/jit-test/tests/ion/bug799185-1.js +++ /dev/null @@ -1,14 +0,0 @@ -options('strict') -f = (function() { - for (var z = 0; z < 9; ++z) { - x = z - } - try { - i - } catch (x if null) { - let e - } catch (l) { - x.m - } -}) -for (a in f()) {} diff --git a/js/src/jit-test/tests/jaeger/bug553781-2.js b/js/src/jit-test/tests/jaeger/bug553781-2.js deleted file mode 100644 index b10c5c8919c1..000000000000 --- a/js/src/jit-test/tests/jaeger/bug553781-2.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - do { - try { - return - } - catch(x if (c)) { - return - } (x) - } while (x) -})() - -/* Don't assert. */ - diff --git a/js/src/jit-test/tests/jaeger/bug553781.js b/js/src/jit-test/tests/jaeger/bug553781.js deleted file mode 100644 index 4c9b674321f8..000000000000 --- a/js/src/jit-test/tests/jaeger/bug553781.js +++ /dev/null @@ -1,11 +0,0 @@ -(function () { - try { - return - } catch (x if i) { - return - } - for (z in []); -})() - -/* Don't assert */ - diff --git a/js/src/tests/ecma_5/extensions/iterator-in-catch.js b/js/src/tests/ecma_5/extensions/iterator-in-catch.js deleted file mode 100644 index 390b585e936a..000000000000 --- a/js/src/tests/ecma_5/extensions/iterator-in-catch.js +++ /dev/null @@ -1,20 +0,0 @@ -//Bug 350712 - -function iterator () { - for (var i in []); -} - -try { - try { - throw 5; - } - catch(error if iterator()) { - assertEq(false, true); - } -} -catch(error) { - assertEq(error, 5); -} - -if (typeof reportCompare === "function") - reportCompare(true, true); diff --git a/js/src/tests/js1_5/extensions/catchguard-001-n.js b/js/src/tests/js1_5/extensions/catchguard-001-n.js deleted file mode 100644 index 3f8a403e8574..000000000000 --- a/js/src/tests/js1_5/extensions/catchguard-001-n.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -DESCRIPTION = " the non-guarded catch should HAVE to appear last"; - -test(); - -function test() -{ - var EXCEPTION_DATA = "String exception"; - var e; - - printStatus ("Catchguard syntax negative test."); - - try - { - throw EXCEPTION_DATA; - } - catch (e) /* the non-guarded catch should HAVE to appear last */ - { - - } - catch (e if true) - { - - } - catch (e if false) - { - - } - - reportCompare('PASS', 'FAIL', - "Illegally constructed catchguard should have thrown " + - "an exception."); -} diff --git a/js/src/tests/js1_5/extensions/catchguard-001.js b/js/src/tests/js1_5/extensions/catchguard-001.js deleted file mode 100644 index 80d81d0a6024..000000000000 --- a/js/src/tests/js1_5/extensions/catchguard-001.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -test(); - -function test() -{ - var EXCEPTION_DATA = "String exception"; - var e = "foo"; - var caught = false; - - printStatus ("Basic catchguard test."); - - try - { - throw EXCEPTION_DATA; - } - catch (e if true) - { - caught = true; - e = "this change should not propagate outside of this scope"; - } - catch (e if false) - { - reportCompare('PASS', 'FAIL', "Catch block (e if false) should not have executed."); - } - catch (e) - { - reportCompare('PASS', 'FAIL', "Catch block (e) should not have executed."); - } - - if (!caught) - reportCompare('PASS', 'FAIL', "Exception was never caught."); - - if (e != "foo") - reportCompare('PASS', 'FAIL', "Exception data modified inside catch() scope should " + - "not be visible in the function scope (e = '" + - e + "'.)"); - - reportCompare('PASS', 'PASS', ''); -} diff --git a/js/src/tests/js1_5/extensions/catchguard-002.js b/js/src/tests/js1_5/extensions/catchguard-002.js deleted file mode 100644 index 308c16006a71..000000000000 --- a/js/src/tests/js1_5/extensions/catchguard-002.js +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -test(); - -function test() -{ - var EXCEPTION_DATA = "String exception"; - var e; - var caught = false; - - printStatus ("Basic catchguard test."); - - try - { - throw EXCEPTION_DATA; - } - catch (e if true) - { - caught = true; - } - catch (e if true) - { - reportCompare('PASS', 'FAIL', - "Second (e if true) catch block should not have executed."); - } - catch (e) - { - reportCompare('PASS', 'FAIL', "Catch block (e) should not have executed."); - } - - if (!caught) - reportCompare('PASS', 'FAIL', "Exception was never caught."); - - reportCompare('PASS', 'PASS', 'Basic catchguard test'); -} diff --git a/js/src/tests/js1_5/extensions/catchguard-003.js b/js/src/tests/js1_5/extensions/catchguard-003.js deleted file mode 100644 index de7689985e53..000000000000 --- a/js/src/tests/js1_5/extensions/catchguard-003.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -test(); - -function test() -{ - var EXCEPTION_DATA = "String exception"; - var e = "foo", x = "foo"; - var caught = false; - - printStatus ("Catchguard 'Common Scope' test."); - - try - { - throw EXCEPTION_DATA; - } - catch (e if ((x = 1) && false)) - { - reportCompare('PASS', 'FAIL', - "Catch block (e if ((x = 1) && false) should not " + - "have executed."); - } - catch (e if (x == 1)) - { - caught = true; - } - catch (e) - { - reportCompare('PASS', 'FAIL', - "Same scope should be used across all catchguards."); - } - - if (!caught) - reportCompare('PASS', 'FAIL', - "Exception was never caught."); - - if (e != "foo") - reportCompare('PASS', 'FAIL', - "Exception data modified inside catch() scope should " + - "not be visible in the function scope (e ='" + - e + "'.)"); - - if (x != 1) - reportCompare('PASS', 'FAIL', - "Data modified in 'catchguard expression' should " + - "be visible in the function scope (x = '" + - x + "'.)"); - - reportCompare('PASS', 'PASS', 'Catchguard Common Scope test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-346494-01.js b/js/src/tests/js1_5/extensions/regress-346494-01.js deleted file mode 100644 index 92168a1b4e94..000000000000 --- a/js/src/tests/js1_5/extensions/regress-346494-01.js +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 346494; -var summary = 'various try...catch tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var pfx = "(function (x) {try {throw x}", - cg1 = " catch (e if e === 42) {var v = 'catch guard 1 ' + e; actual += v + ','; print(v);}" - cg2 = " catch (e if e === 43) {var v = 'catch guard 2 ' + e; actual += v + ','; print(v);}" - cat = " catch (e) {var v = 'catch all ' + e; actual += v + ','; print(v);}" - fin = " finally{var v = 'fin'; actual += v + ','; print(v)}", - end = "})"; - - var exphash = { - pfx: "(function (y) { var result = ''; y = y + ',';", - cg1: "result += (y === '42,') ? ('catch guard 1 ' + y):'';", - cg2: "result += (y === '43,') ? ('catch guard 2 ' + y):'';", - cat: "result += /catch guard/.test(result) ? '': ('catch all ' + y);", - fin: "result += 'fin,';", - end: "return result;})" - }; - - var src = [ - pfx + fin + end, - pfx + cat + end, - pfx + cat + fin + end, - pfx + cg1 + end, - pfx + cg1 + fin + end, - pfx + cg1 + cat + end, - pfx + cg1 + cat + fin + end, - pfx + cg1 + cg2 + end, - pfx + cg1 + cg2 + fin + end, - pfx + cg1 + cg2 + cat + end, - pfx + cg1 + cg2 + cat + fin + end, - ]; - - var expsrc = [ - exphash.pfx + exphash.fin + exphash.end, - exphash.pfx + exphash.cat + exphash.end, - exphash.pfx + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1 + exphash.end, - exphash.pfx + exphash.cg1 + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.cat + exphash.fin + exphash.end, - ]; - - for (var i in src) { - print("\n=== " + src[i]); - var f = eval(src[i]); - print(src[i]); - var exp = eval(expsrc[i]); - // dis(f); - print('decompiling: ' + f); - - actual = ''; - try { expect = exp(42); f(42) } catch (e) { print('tried f(42), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(43); f(43) } catch (e) { print('tried f(43), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(44); f(44) } catch (e) { print('tried f(44), caught ' + e) } - reportCompare(expect, actual, summary); - } -} diff --git a/js/src/tests/js1_5/extensions/regress-346494.js b/js/src/tests/js1_5/extensions/regress-346494.js deleted file mode 100644 index 3a8c66898c62..000000000000 --- a/js/src/tests/js1_5/extensions/regress-346494.js +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 346494; -var summary = 'try-catch-finally scope'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - function g() - { - try - { - throw "foo"; - } - catch(e if e == "bar") - { - } - catch(e if e == "baz") - { - } - finally - { - } - } - - expect = "foo"; - try - { - g(); - actual = 'No Exception'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary); - - function h() - { - try - { - throw "foo"; - } - catch(e if e == "bar") - { - } - catch(e) - { - } - finally - { - } - } - - expect = "No Exception"; - try - { - h(); - actual = 'No Exception'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary); -} diff --git a/js/src/tests/js1_5/extensions/regress-350312-02.js b/js/src/tests/js1_5/extensions/regress-350312-02.js deleted file mode 100644 index e80deb9ac67a..000000000000 --- a/js/src/tests/js1_5/extensions/regress-350312-02.js +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 350312; -var summary = 'Accessing wrong stack slot with nested catch/finally'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - function createPrint(obj) - { - return new Function("actual += " + obj + " + ','; " + - "print(" + obj + ");"); - } - - function createThrow(obj) - { - return new Function("throw " + obj + "; "); - } - - - function f(a, b, c) - { - try { - a(); - } catch (e if e == null) { - b(); - } finally { - c(); - } - } - - print('test 1'); - expect = 'a,c,'; - actual = ''; - try - { - f(createPrint("'a'"), createPrint("'b'"), createPrint("'c'")); - } - catch(ex) - { - actual += 'caught ' + ex; - } - reportCompare(expect, actual, summary + ': 1'); - - print('test 2'); - expect = 'c,caught a'; - actual = ''; - try - { - f(createThrow("'a'"), createPrint("'b'"), createPrint("'c'")); - } - catch(ex) - { - actual += 'caught ' + ex; - } - reportCompare(expect, actual, summary + ': 2'); - - print('test 3'); - expect = 'b,c,'; - actual = ''; - try - { - f(createThrow("null"), createPrint("'b'"), createPrint("'c'")); - } - catch(ex) - { - actual += 'caught ' + ex; - } - reportCompare(expect, actual, summary + ': 3'); - - print('test 4'); - expect = 'a,c,'; - actual = ''; - try - { - f(createPrint("'a'"), createThrow("'b'"), createPrint("'c'")); - } - catch(ex) - { - actual += 'caught ' + ex; - } - reportCompare(expect, actual, summary + ': 4'); - - print('test 5'); - expect = 'c,caught b'; - actual = ''; - try - { - f(createThrow("null"), createThrow("'b'"), createPrint("'c'")); - } - catch(ex) - { - actual += 'caught ' + ex; - } - reportCompare(expect, actual, summary + ': 5'); -} diff --git a/js/src/tests/js1_5/extensions/regress-350312-03.js b/js/src/tests/js1_5/extensions/regress-350312-03.js deleted file mode 100644 index ed9661071303..000000000000 --- a/js/src/tests/js1_5/extensions/regress-350312-03.js +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 350312; -var summary = 'Accessing wrong stack slot with nested catch/finally'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var pfx = "(function (x) {try {if (x > 41) throw x}", - cg1a = " catch (e if e === 42) {var v = 'catch guard 1 ' + e; actual += v + ',';print(v);}" - cg1b = " catch (e if e === 42) {var v = 'catch guard 1 + throw ' + e; actual += v + ',';print(v); throw e;}" - cg2 = " catch (e if e === 43) {var v = 'catch guard 2 ' + e; actual += v + ',';print(v)}" - cat = " catch (e) {var v = 'catch all ' + e; print(v); if (e == 44) throw e}" - fin = " finally{var v = 'fin'; actual += v + ',';print(v)}", - end = "})"; - - var exphash = { - pfx: "(function (y) { var result = ''; y = y + ',';", - cg1a: " result += (y === '42,') ? ('catch guard 1 ' + y):'';", - cg1b: " result += (y === '42,') ? ('catch guard 1 + throw ' + y):'';", - cg2: " result += (y === '43,') ? ('catch guard 2 ' + y):'';", - cat: " result += (y > 41) ? ('catch all ' + y):'';", - fin: " result += 'fin,';", - end: "return result;})" - }; - - var src = [ - pfx + fin + end, - pfx + cat + end, - pfx + cat + fin + end, - pfx + cg1a + end, - pfx + cg1a + fin + end, - pfx + cg1a + cat + end, - pfx + cg1a + cat + fin + end, - pfx + cg1a + cg2 + end, - pfx + cg1a + cg2 + fin + end, - pfx + cg1a + cg2 + cat + end, - pfx + cg1a + cg2 + cat + fin + end, - pfx + cg1b + end, - pfx + cg1b + fin + end, - pfx + cg1b + cat + end, - pfx + cg1b + cat + fin + end, - pfx + cg1b + cg2 + end, - pfx + cg1b + cg2 + fin + end, - pfx + cg1b + cg2 + cat + end, - pfx + cg1b + cg2 + cat + fin + end, - ]; - - var expsrc = [ - exphash.pfx + exphash.fin + exphash.end, - exphash.pfx + exphash.cat + exphash.end, - exphash.pfx + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1a + exphash.end, - exphash.pfx + exphash.cg1a + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1b + exphash.end, - exphash.pfx + exphash.cg1b + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cat + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.fin + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.cat + exphash.end, - exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.cat + exphash.fin + exphash.end, - ]; - - for (var i in src) { - print("\n=== " + i + ": " + src[i]); - var f = eval(src[i]); - var exp = eval(expsrc[i]); - // dis(f); - print('decompiling: ' + f); - //print('decompiling exp: ' + exp); - - actual = ''; - try { expect = exp(41); f(41) } catch (e) { print('tried f(41), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(42); f(42) } catch (e) { print('tried f(42), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(43); f(43) } catch (e) { print('tried f(43), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(44); f(44) } catch (e) { print('tried f(44), caught ' + e) } - reportCompare(expect, actual, summary); - - actual = ''; - try { expect = exp(45); f(45) } catch (e) { print('tried f(44), caught ' + e) } - reportCompare(expect, actual, summary); - - } -} diff --git a/js/src/tests/js1_5/extensions/regress-351102-01.js b/js/src/tests/js1_5/extensions/regress-351102-01.js deleted file mode 100644 index 5dcff2bcdf8c..000000000000 --- a/js/src/tests/js1_5/extensions/regress-351102-01.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - - f = function () { - try { - throw new Error('bad'); - } catch (e if (e = null, gc(), false)) { - } catch (e) { - // e is dangling now - } - }; - - f(); - - reportCompare(expect, actual, summary + ': 1'); -} diff --git a/js/src/tests/js1_5/extensions/regress-351102-02.js b/js/src/tests/js1_5/extensions/regress-351102-02.js deleted file mode 100644 index 7e4ffcff7aab..000000000000 --- a/js/src/tests/js1_5/extensions/regress-351102-02.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - f = function () - { - var a = null; - try { - a(); - } catch (e) { - } - return false; - }; - - try { - throw 1; - } catch (e if f()) { - } catch (e if e == 1) { - print("GOOD"); - } catch (e) { - print("BAD: "+e); - } - - reportCompare(expect, actual, summary + ': 2'); -} diff --git a/js/src/tests/js1_5/extensions/regress-351102-06.js b/js/src/tests/js1_5/extensions/regress-351102-06.js deleted file mode 100644 index fe014feea0e1..000000000000 --- a/js/src/tests/js1_5/extensions/regress-351102-06.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - try - { - try { null.a } catch(e if (e = null, gc())) { } - } - catch(ex) - { - } - reportCompare(expect, actual, summary + ': 6'); -} diff --git a/js/src/tests/js1_5/extensions/regress-374589.js b/js/src/tests/js1_5/extensions/regress-374589.js deleted file mode 100644 index dfb7bd0745da..000000000000 --- a/js/src/tests/js1_5/extensions/regress-374589.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 374589; -var summary = 'Do not assert decompiling try { } catch(x if true) { } ' + - 'catch(y) { } finally { this.a.b; }'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f = function () { - try { } catch(x if true) { } catch(y) { } finally { this.a.b; } }; - - expect = 'function () {\n\ - try { } catch(x if true) { } catch(y) { } finally { this.a.b; } }'; - - actual = f + ''; - compareSource(expect, actual, summary); -} diff --git a/js/src/tests/js1_7/extensions/regress-351102-03.js b/js/src/tests/js1_7/extensions/regress-351102-03.js deleted file mode 100644 index 4500a6b05ddb..000000000000 --- a/js/src/tests/js1_7/extensions/regress-351102-03.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - f = function() - { - try - { - d.d.d; - } - catch({} if gc()) - { - } - catch(y) - { - } - }; - - f(); - f(); - - reportCompare(expect, actual, summary + ': 3'); -} diff --git a/js/src/tests/js1_7/extensions/regress-351102-04.js b/js/src/tests/js1_7/extensions/regress-351102-04.js deleted file mode 100644 index 5afabf3caf1d..000000000000 --- a/js/src/tests/js1_7/extensions/regress-351102-04.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - try - { - try { foo() } catch([] if gc()) { } - } - catch(ex) - { - } - reportCompare(expect, actual, summary + ': 4'); -} - diff --git a/js/src/tests/js1_7/extensions/regress-351102-05.js b/js/src/tests/js1_7/extensions/regress-351102-05.js deleted file mode 100644 index 8d566abc9a9d..000000000000 --- a/js/src/tests/js1_7/extensions/regress-351102-05.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - try - { - try { d.d.d } catch([] if gc()) { } - } - catch(ex) - { - } - reportCompare(expect, actual, summary + ': 5'); -} diff --git a/js/src/tests/js1_7/extensions/regress-351102-07.js b/js/src/tests/js1_7/extensions/regress-351102-07.js deleted file mode 100644 index 130b8a69d033..000000000000 --- a/js/src/tests/js1_7/extensions/regress-351102-07.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351102; -var summary = 'try/catch-guard/finally GC issues'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - var f; - var obj = { get a() { - try { - throw 1; - } catch (e) { - } - return false; - }}; - - try { - throw obj; - } catch ({a: a} if a) { - throw "Unreachable"; - } catch (e) { - if (e !== obj) - throw "Unexpected exception: "+uneval(e); - } - reportCompare(expect, actual, summary + ': 7'); -} diff --git a/js/src/tests/js1_7/regress/regress-375695.js b/js/src/tests/js1_7/regress/regress-375695.js deleted file mode 100644 index 5f4459d51ee8..000000000000 --- a/js/src/tests/js1_7/regress/regress-375695.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 375695; -var summary = 'Do not assert: !fp->blockChain || OBJ_GET_PARENT(cx, obj) == fp->blockChain'; -var actual = ''; -var expect = ''; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - printBugNumber(BUGNUMBER); - printStatus (summary); - - try { try { throw 1 } catch([] if false) { } } catch(ex) {} - - reportCompare(expect, actual, summary); -} diff --git a/js/src/tests/js1_8_5/extensions/regress-677589.js b/js/src/tests/js1_8_5/extensions/regress-677589.js deleted file mode 100644 index 0bdb2e79ff37..000000000000 --- a/js/src/tests/js1_8_5/extensions/regress-677589.js +++ /dev/null @@ -1,10 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell) -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -try { - clone(null); // don't crash -} catch (exc if exc instanceof TypeError) { -} - -reportCompare(0, 0, 'ok'); From 319d250ae37e4bc4bc3e752294a29918fa0694c0 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Wed, 6 Dec 2017 23:44:17 +0900 Subject: [PATCH 53/78] Bug 1228841 - Part 3: Remove conditional catch handling in devtools. r=jdescottes --- devtools/shared/Parser.jsm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devtools/shared/Parser.jsm b/devtools/shared/Parser.jsm index 4bd635383a1e..7c4bd2ddd222 100644 --- a/devtools/shared/Parser.jsm +++ b/devtools/shared/Parser.jsm @@ -1134,7 +1134,6 @@ var SyntaxTreeVisitor = { * type: "TryStatement"; * block: BlockStatement; * handler: CatchClause | null; - * guardedHandlers: [ CatchClause ]; * finalizer: BlockStatement | null; * } */ @@ -1156,9 +1155,6 @@ var SyntaxTreeVisitor = { if (node.handler) { this[node.handler.type](node.handler, node, callbacks); } - for (let guardedHandler of node.guardedHandlers) { - this[guardedHandler.type](guardedHandler, node, callbacks); - } if (node.finalizer) { this[node.finalizer.type](node.finalizer, node, callbacks); } From 1bb01cfa44249d2f82d26002e927ac02827cc759 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 5 Dec 2017 09:42:40 +0900 Subject: [PATCH 54/78] Bug 1228841 - Part 4: Remove conditional catch and fix Reflect.parse tests for conditional catch. r=jwalden,evilpie --- js/src/builtin/ReflectParse.cpp | 64 +++---- js/src/frontend/BytecodeEmitter.cpp | 152 +++-------------- js/src/frontend/FoldConstants.cpp | 40 ++--- js/src/frontend/FullParseHandler.h | 39 ++--- js/src/frontend/NameFunctions.cpp | 32 +--- js/src/frontend/ParseNode.cpp | 17 +- js/src/frontend/ParseNode.h | 16 +- js/src/frontend/Parser.cpp | 159 +++++++----------- js/src/frontend/SyntaxParseHandler.h | 11 +- js/src/jsversion.h | 1 - .../js1_8_5/reflect-parse/PatternBuilders.js | 8 +- .../js1_8_5/reflect-parse/alternateBuilder.js | 10 +- .../js1_8_5/reflect-parse/basicBuilder.js | 8 +- .../tests/js1_8_5/reflect-parse/statements.js | 25 +-- 14 files changed, 177 insertions(+), 405 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 53b8dfb537b9..1f209ada34ca 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -475,7 +475,7 @@ class NodeBuilder MOZ_MUST_USE bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst); - MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos, + MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue body, TokenPos* pos, MutableHandleValue dst); MOZ_MUST_USE bool prototypeMutation(HandleValue val, TokenPos* pos, MutableHandleValue dst); @@ -527,7 +527,7 @@ class NodeBuilder MOZ_MUST_USE bool switchStatement(HandleValue disc, NodeVector& elts, bool lexical, TokenPos* pos, MutableHandleValue dst); - MOZ_MUST_USE bool tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded, + MOZ_MUST_USE bool tryStatement(HandleValue body, HandleValue handler, HandleValue finally, TokenPos* pos, MutableHandleValue dst); MOZ_MUST_USE bool debuggerStatement(TokenPos* pos, MutableHandleValue dst); @@ -954,21 +954,16 @@ NodeBuilder::switchStatement(HandleValue disc, NodeVector& elts, bool lexical, T } bool -NodeBuilder::tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded, +NodeBuilder::tryStatement(HandleValue body, HandleValue handler, HandleValue finally, TokenPos* pos, MutableHandleValue dst) { - RootedValue guardedHandlers(cx); - if (!newArray(guarded, &guardedHandlers)) - return false; - RootedValue cb(cx, callbacks[AST_TRY_STMT]); if (!cb.isNull()) - return callback(cb, body, guardedHandlers, unguarded, opt(finally), pos, dst); + return callback(cb, body, handler, opt(finally), pos, dst); return newNode(AST_TRY_STMT, pos, "block", body, - "guardedHandlers", guardedHandlers, - "handler", unguarded, + "handler", handler, "finalizer", finally, dst); } @@ -1451,16 +1446,15 @@ NodeBuilder::switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, Mutab } bool -NodeBuilder::catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos, +NodeBuilder::catchClause(HandleValue var, HandleValue body, TokenPos* pos, MutableHandleValue dst) { RootedValue cb(cx, callbacks[AST_CATCH]); if (!cb.isNull()) - return callback(cb, opt(var), opt(guard), body, pos, dst); + return callback(cb, opt(var), body, pos, dst); return newNode(AST_CATCH, pos, "param", var, - "guard", guard, "body", body, dst); } @@ -1680,7 +1674,7 @@ class ASTSerializer bool switchStatement(ParseNode* pn, MutableHandleValue dst); bool switchCase(ParseNode* pn, MutableHandleValue dst); bool tryStatement(ParseNode* pn, MutableHandleValue dst); - bool catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst); + bool catchClause(ParseNode* pn, MutableHandleValue dst); bool optExpression(ParseNode* pn, MutableHandleValue dst) { if (!pn) { @@ -2173,23 +2167,19 @@ ASTSerializer::switchStatement(ParseNode* pn, MutableHandleValue dst) } bool -ASTSerializer::catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst) +ASTSerializer::catchClause(ParseNode* pn, MutableHandleValue dst) { - MOZ_ASSERT_IF(pn->pn_kid1, pn->pn_pos.encloses(pn->pn_kid1->pn_pos)); - MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos)); - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos)); + MOZ_ASSERT(pn->isKind(PNK_CATCH)); + MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); - RootedValue var(cx), guard(cx), body(cx); + RootedValue var(cx), body(cx); - if (!optPattern(pn->pn_kid1, &var) || - !optExpression(pn->pn_kid2, &guard)) { + if (!optPattern(pn->pn_left, &var)) return false; - } - *isGuarded = !guard.isMagic(JS_SERIALIZE_NO_NODE); - - return statement(pn->pn_kid3, &body) && - builder.catchClause(var, guard, body, &pn->pn_pos, dst); + return statement(pn->pn_right, &body) && + builder.catchClause(var, body, &pn->pn_pos, dst); } bool @@ -2203,28 +2193,16 @@ ASTSerializer::tryStatement(ParseNode* pn, MutableHandleValue dst) if (!statement(pn->pn_kid1, &body)) return false; - NodeVector guarded(cx); - RootedValue unguarded(cx, NullValue()); - - if (ParseNode* catchList = pn->pn_kid2) { - if (!guarded.reserve(catchList->pn_count)) + RootedValue handler(cx, NullValue()); + if (ParseNode* catchScope = pn->pn_kid2) { + MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); + if (!catchClause(catchScope->scopeBody(), &handler)) return false; - - for (ParseNode* next = catchList->pn_head; next; next = next->pn_next) { - RootedValue clause(cx); - bool isGuarded; - if (!catchClause(next->pn_expr, &isGuarded, &clause)) - return false; - if (isGuarded) - guarded.infallibleAppend(clause); - else - unguarded = clause; - } } RootedValue finally(cx); return optStatement(pn->pn_kid3, &finally) && - builder.tryStatement(body, guarded, unguarded, finally, &pn->pn_pos, dst); + builder.tryStatement(body, handler, finally, &pn->pn_pos, dst); } bool diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 9222f5ce9e23..7d9f4270d0e0 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -301,9 +301,6 @@ class TryFinallyControl : public BytecodeEmitter::NestableControl // The subroutine when emitting a finally block. JumpList gosubs; - // Offset of the last catch guard, if any. - JumpList guardJump; - TryFinallyControl(BytecodeEmitter* bce, StatementKind kind) : NestableControl(bce, kind), emittingSubroutine_(false) @@ -1524,7 +1521,6 @@ class MOZ_STACK_CLASS TryEmitter // DontUseControl is used by yield* and the internal try-catch around // IteratorClose. These internal uses must: // * have only one catch block - // * have no catch guard // * have JSOP_GOTO at the end of catch-block // * have no non-local-jump // * don't use finally block for normal completion of try-block and @@ -1634,14 +1630,9 @@ class MOZ_STACK_CLASS TryEmitter public: bool emitCatch() { - if (state_ == Try) { - if (!emitTryEnd()) - return false; - } else { - MOZ_ASSERT(state_ == Catch); - if (!emitCatchEnd(true)) - return false; - } + MOZ_ASSERT(state_ == Try); + if (!emitTryEnd()) + return false; MOZ_ASSERT(bce_->stackDepth == depth_); @@ -1672,28 +1663,10 @@ class MOZ_STACK_CLASS TryEmitter if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs)) return false; MOZ_ASSERT(bce_->stackDepth == depth_); - } - // Jump over the remaining catch blocks. This will get fixed - // up to jump to after catch/finally. - if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_)) - return false; - - // If this catch block had a guard clause, patch the guard jump to - // come here. - if (controlInfo_->guardJump.offset != -1) { - if (!bce_->emitJumpTargetAndPatch(controlInfo_->guardJump)) + // Jump over the finally block. + if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_)) return false; - controlInfo_->guardJump.offset = -1; - - // If this catch block is the last one, rethrow, delegating - // execution of any finally block to the exception handler. - if (!hasNext) { - if (!bce_->emit1(JSOP_EXCEPTION)) - return false; - if (!bce_->emit1(JSOP_THROW)) - return false; - } } return true; @@ -3222,7 +3195,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) return true; case PNK_STATEMENTLIST: - case PNK_CATCHLIST: // Strict equality operations and logical operators are well-behaved and // perform no conversions. case PNK_OR: @@ -3411,9 +3383,9 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) return false; if (*answer) return true; - if (ParseNode* catchList = pn->pn_kid2) { - MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST)); - if (!checkSideEffects(catchList, answer)) + if (ParseNode* catchScope = pn->pn_kid2) { + MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); + if (!checkSideEffects(catchScope, answer)) return false; if (*answer) return true; @@ -3425,20 +3397,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) return true; case PNK_CATCH: - MOZ_ASSERT(pn->isArity(PN_TERNARY)); - if (ParseNode* name = pn->pn_kid1) { + MOZ_ASSERT(pn->isArity(PN_BINARY)); + if (ParseNode* name = pn->pn_left) { if (!checkSideEffects(name, answer)) return false; if (*answer) return true; } - if (ParseNode* cond = pn->pn_kid2) { - if (!checkSideEffects(cond, answer)) - return false; - if (*answer) - return true; - } - return checkSideEffects(pn->pn_kid3, answer); + return checkSideEffects(pn->pn_right, answer); case PNK_SWITCH: MOZ_ASSERT(pn->isArity(PN_BINARY)); @@ -6615,20 +6581,13 @@ bool BytecodeEmitter::emitCatch(ParseNode* pn) { // We must be nested under a try-finally statement. - TryFinallyControl& controlInfo = innermostNestableControl->as(); + MOZ_ASSERT(innermostNestableControl->is()); /* Pick up the pending exception and bind it to the catch variable. */ if (!emit1(JSOP_EXCEPTION)) return false; - /* - * Dup the exception object if there is a guard for rethrowing to use - * it later when rethrowing or in other catches. - */ - if (pn->pn_kid2 && !emit1(JSOP_DUP)) - return false; - - ParseNode* pn2 = pn->pn_kid1; + ParseNode* pn2 = pn->pn_left; if (!pn2) { // Catch parameter was omitted; just discard the exception. if (!emit1(JSOP_POP)) @@ -6655,47 +6614,8 @@ BytecodeEmitter::emitCatch(ParseNode* pn) } } - // If there is a guard expression, emit it and arrange to jump to the next - // catch block if the guard expression is false. - if (pn->pn_kid2) { - if (!emitTree(pn->pn_kid2)) - return false; - - // If the guard expression is false, fall through, pop the block scope, - // and jump to the next catch block. Otherwise jump over that code and - // pop the dupped exception. - JumpList guardCheck; - if (!emitJump(JSOP_IFNE, &guardCheck)) - return false; - - { - NonLocalExitControl nle(this, NonLocalExitControl::Throw); - - // Move exception back to cx->exception to prepare for - // the next catch. - if (!emit1(JSOP_THROWING)) - return false; - - // Leave the scope for this catch block. - if (!nle.prepareForNonLocalJump(&controlInfo)) - return false; - - // Jump to the next handler added by emitTry. - if (!emitJump(JSOP_GOTO, &controlInfo.guardJump)) - return false; - } - - // Back to normal control flow. - if (!emitJumpTargetAndPatch(guardCheck)) - return false; - - // Pop duplicated exception object as we no longer need it. - if (!emit1(JSOP_POP)) - return false; - } - /* Emit the catch body. */ - return emitTree(pn->pn_kid3); + return emitTree(pn->pn_right); } // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the @@ -6703,11 +6623,11 @@ BytecodeEmitter::emitCatch(ParseNode* pn) MOZ_NEVER_INLINE bool BytecodeEmitter::emitTry(ParseNode* pn) { - ParseNode* catchList = pn->pn_kid2; + ParseNode* catchScope = pn->pn_kid2; ParseNode* finallyNode = pn->pn_kid3; TryEmitter::Kind kind; - if (catchList) { + if (catchScope) { if (finallyNode) kind = TryEmitter::TryCatchFinally; else @@ -6725,43 +6645,25 @@ BytecodeEmitter::emitTry(ParseNode* pn) return false; // If this try has a catch block, emit it. - if (catchList) { - MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST)); - + if (catchScope) { // The emitted code for a catch block looks like: // // [pushlexicalenv] only if any local aliased // exception - // if there is a catchguard: - // dup // setlocal 0; pop assign or possibly destructure exception - // if there is a catchguard: - // < catchguard code > - // ifne POST - // debugleaveblock - // [poplexicalenv] only if any local aliased - // throwing pop exception to cx->exception - // goto - // POST: pop // < catch block contents > // debugleaveblock // [poplexicalenv] only if any local aliased - // goto non-local; finally applies - // - // If there's no catch block without a catchguard, the last points to rethrow code. This code will [gosub] to the finally - // code if appropriate, and is also used for the catch-all trynote for - // capturing exceptions thrown from catch{} blocks. - // - for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) { - if (!tryCatch.emitCatch()) - return false; + // if there is a finally block: + // gosub + // goto + if (!tryCatch.emitCatch()) + return false; - // Emit the lexical scope and catch body. - MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE)); - if (!emitTree(pn3)) - return false; - } + // Emit the lexical scope and catch body. + MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); + if (!emitTree(catchScope)) + return false; } // Emit the finally handler, if there is one. @@ -6896,7 +6798,7 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn) EmitterScope emitterScope(this); ScopeKind kind; if (body->isKind(PNK_CATCH)) - kind = (!body->pn_kid1 || body->pn_kid1->isKind(PNK_NAME)) + kind = (!body->pn_left || body->pn_left->isKind(PNK_NAME)) ? ScopeKind::SimpleCatch : ScopeKind::Catch; else diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 804fb45df1e7..c8505cff558a 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -188,22 +188,17 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) if (*result) return true; - if (ParseNode* catchList = node->pn_kid2) { - for (ParseNode* lexicalScope = catchList->pn_head; - lexicalScope; - lexicalScope = lexicalScope->pn_next) - { - MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE)); + if (ParseNode* catchScope = node->pn_kid2) { + MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); - ParseNode* catchNode = lexicalScope->pn_expr; - MOZ_ASSERT(catchNode->isKind(PNK_CATCH)); + ParseNode* catchNode = catchScope->pn_expr; + MOZ_ASSERT(catchNode->isKind(PNK_CATCH)); - ParseNode* catchStatements = catchNode->pn_kid3; - if (!ContainsHoistedDeclaration(cx, catchStatements, result)) - return false; - if (*result) - return true; - } + ParseNode* catchStatements = catchNode->pn_right; + if (!ContainsHoistedDeclaration(cx, catchStatements, result)) + return false; + if (*result) + return true; } if (ParseNode* finallyBlock = node->pn_kid3) @@ -370,7 +365,6 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) case PNK_NEW: case PNK_GENERATOR: case PNK_PARAMSBODY: - case PNK_CATCHLIST: case PNK_CATCH: case PNK_FORIN: case PNK_FOROF: @@ -1206,8 +1200,8 @@ FoldTry(JSContext* cx, ParseNode* node, Parser& pars if (!Fold(cx, &statements, parser)) return false; - if (ParseNode*& catchList = node->pn_kid2) { - if (!Fold(cx, &catchList, parser)) + if (ParseNode*& catchScope = node->pn_kid2) { + if (!Fold(cx, &catchScope, parser)) return false; } @@ -1223,19 +1217,14 @@ static bool FoldCatch(JSContext* cx, ParseNode* node, Parser& parser) { MOZ_ASSERT(node->isKind(PNK_CATCH)); - MOZ_ASSERT(node->isArity(PN_TERNARY)); + MOZ_ASSERT(node->isArity(PN_BINARY)); - if (ParseNode*& declPattern = node->pn_kid1) { + if (ParseNode*& declPattern = node->pn_left) { if (!Fold(cx, &declPattern, parser)) return false; } - if (ParseNode*& cond = node->pn_kid2) { - if (!FoldCondition(cx, &cond, parser)) - return false; - } - - if (ParseNode*& statements = node->pn_kid3) { + if (ParseNode*& statements = node->pn_right) { if (!Fold(cx, &statements, parser)) return false; } @@ -1724,7 +1713,6 @@ Fold(JSContext* cx, ParseNode** pnp, Parser& parser) case PNK_OBJECT: case PNK_STATEMENTLIST: case PNK_CLASSMETHODLIST: - case PNK_CATCHLIST: case PNK_TEMPLATE_STRING_LIST: case PNK_VAR: case PNK_CONST: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index fb50650e0462..0da78e11a0f7 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -592,10 +592,10 @@ class FullParseHandler return new_(PNK_THROW, pos, expr); } - ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchList, + ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchScope, ParseNode* finallyBlock) { - TokenPos pos(begin, (finallyBlock ? finallyBlock : catchList)->pn_pos.end); - return new_(PNK_TRY, body, catchList, finallyBlock, pos); + TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end); + return new_(PNK_TRY, body, catchScope, finallyBlock, pos); } ParseNode* newDebuggerStatement(const TokenPos& pos) { @@ -610,9 +610,19 @@ class FullParseHandler return new_(lhs, index, lhs->pn_pos.begin, end); } - inline MOZ_MUST_USE bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope, - ParseNode* catchName, ParseNode* catchGuard, - ParseNode* catchBody); + bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) { + ParseNode* catchpn; + if (catchName) { + catchpn = new_(PNK_CATCH, JSOP_NOP, catchName, catchBody); + } else { + catchpn = new_(PNK_CATCH, JSOP_NOP, catchBody->pn_pos, catchName, + catchBody); + } + if (!catchpn) + return false; + lexicalScope->setScopeBody(catchpn); + return true; + } inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* pn); @@ -769,10 +779,6 @@ class FullParseHandler return decl->pn_head; } - ParseNode* newCatchList(const TokenPos& pos) { - return new_(PNK_CATCHLIST, JSOP_NOP, pos); - } - ParseNode* newCommaExpressionList(ParseNode* kid) { return new_(PNK_COMMA, JSOP_NOP, kid); } @@ -852,19 +858,6 @@ class FullParseHandler } }; -inline bool -FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope, - ParseNode* catchName, ParseNode* catchGuard, - ParseNode* catchBody) -{ - ParseNode* catchpn = new_(PNK_CATCH, catchName, catchGuard, catchBody); - if (!catchpn) - return false; - catchList->append(lexicalScope); - lexicalScope->setScopeBody(catchpn); - return true; -} - inline bool FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue) diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 19f958ae2690..76f4ff621ad5 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -638,9 +638,11 @@ class NameResolver if (!resolve(cur->pn_kid1, prefix)) return false; MOZ_ASSERT(cur->pn_kid2 || cur->pn_kid3); - if (ParseNode* catchList = cur->pn_kid2) { - MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST)); - if (!resolve(catchList, prefix)) + if (ParseNode* catchScope = cur->pn_kid2) { + MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); + MOZ_ASSERT(catchScope->scopeBody()->isKind(PNK_CATCH)); + MOZ_ASSERT(catchScope->scopeBody()->isArity(PN_BINARY)); + if (!resolve(catchScope->scopeBody(), prefix)) return false; } if (ParseNode* finallyBlock = cur->pn_kid3) { @@ -654,16 +656,12 @@ class NameResolver // contain any expression. The catch statements, of course, may // contain arbitrary expressions. case PNK_CATCH: - MOZ_ASSERT(cur->isArity(PN_TERNARY)); - if (cur->pn_kid1) { - if (!resolve(cur->pn_kid1, prefix)) + MOZ_ASSERT(cur->isArity(PN_BINARY)); + if (cur->pn_left) { + if (!resolve(cur->pn_left, prefix)) return false; } - if (cur->pn_kid2) { - if (!resolve(cur->pn_kid2, prefix)) - return false; - } - if (!resolve(cur->pn_kid3, prefix)) + if (!resolve(cur->pn_right, prefix)) return false; break; @@ -760,18 +758,6 @@ class NameResolver break; } - case PNK_CATCHLIST: { - MOZ_ASSERT(cur->isArity(PN_LIST)); - for (ParseNode* catchNode = cur->pn_head; catchNode; catchNode = catchNode->pn_next) { - MOZ_ASSERT(catchNode->isKind(PNK_LEXICALSCOPE)); - MOZ_ASSERT(catchNode->scopeBody()->isKind(PNK_CATCH)); - MOZ_ASSERT(catchNode->scopeBody()->isArity(PN_TERNARY)); - if (!resolve(catchNode->scopeBody(), prefix)) - return false; - } - break; - } - case PNK_DOT: MOZ_ASSERT(cur->isArity(PN_NAME)); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 1aee9adf9d62..72c5a770767c 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -407,17 +407,13 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushResult::Recyclable; } - // A catch node has first kid as catch-variable pattern, the second kid - // as catch condition (which, if non-null, records the || in - // SpiderMonkey's |catch (e if )| extension), and third kid as the - // statements in the catch block. + // A catch node has left node as catch-variable pattern (or null if + // omitted) and right node as the statements in the catch block. case PNK_CATCH: { - MOZ_ASSERT(pn->isArity(PN_TERNARY)); - if (pn->pn_kid1) - stack->push(pn->pn_kid1); - if (pn->pn_kid2) - stack->push(pn->pn_kid2); - stack->push(pn->pn_kid3); + MOZ_ASSERT(pn->isArity(PN_BINARY)); + if (pn->pn_left) + stack->push(pn->pn_left); + stack->push(pn->pn_right); return PushResult::Recyclable; } @@ -459,7 +455,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_VAR: case PNK_CONST: case PNK_LET: - case PNK_CATCHLIST: case PNK_STATEMENTLIST: case PNK_IMPORT_SPEC_LIST: case PNK_EXPORT_SPEC_LIST: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 92bf5620691e..d068f5580f4d 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -79,7 +79,6 @@ class ObjectBox; F(DELETEEXPR) \ F(TRY) \ F(CATCH) \ - F(CATCHLIST) \ F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ @@ -260,15 +259,14 @@ IsTypeofKind(ParseNodeKind kind) * pn_kid3: update expr after second ';' or nullptr * PNK_THROW unary pn_kid: exception * PNK_TRY ternary pn_kid1: try block - * pn_kid2: null or PNK_CATCHLIST list + * pn_kid2: null or PNK_LEXICALSCOPE for catch-block + * with pn_expr pointing to a PNK_CATCH node * pn_kid3: null or finally block - * PNK_CATCHLIST list pn_head: list of PNK_LEXICALSCOPE nodes, one per - * catch-block, each with pn_expr pointing - * to a PNK_CATCH node - * PNK_CATCH ternary pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node - * (PNK_ARRAY or PNK_OBJECT if destructuring) - * pn_kid2: null or the catch guard expression - * pn_kid3: catch block statements + * PNK_CATCH binary pn_left: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch + * var node + * (PNK_ARRAY or PNK_OBJECT if destructuring), + * or null if optional catch binding + * pn_right: catch block statements * PNK_BREAK name pn_atom: label or null * PNK_CONTINUE name pn_atom: label or null * PNK_WITH binary pn_left: head expr; pn_right: body; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 61346328c26e..5b257ce06dc5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6764,10 +6764,9 @@ Parser::tryStatement(YieldHandling yieldHandling) * kid2 is the catch node list or null * kid3 is the finally statement * - * catch nodes are ternary. - * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC) - * kid2 is the catch guard or null if no guard - * kid3 is the catch block + * catch nodes are binary. + * left is the catch-name/pattern or null + * right is the catch block * * catch lvalue nodes are either: * a single identifier @@ -6800,118 +6799,80 @@ Parser::tryStatement(YieldHandling yieldHandling) JSMSG_CURLY_OPENED, openedPos)); } - bool hasUnconditionalCatch = false; - Node catchList = null(); + Node catchScope = null(); TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_CATCH) { - catchList = handler.newCatchList(pos()); - if (!catchList) + /* + * Create a lexical scope node around the whole catch clause, + * including the head. + */ + ParseContext::Statement stmt(pc, StatementKind::Catch); + ParseContext::Scope scope(this); + if (!scope.init(pc)) return null(); - do { - Node pnblock; + /* + * Legal catch forms are: + * catch (lhs) { + * catch { + * where lhs is a name or a destructuring left-hand side. + */ + bool omittedBinding; + if (!tokenStream.matchToken(&omittedBinding, TOK_LC)) + return null(); - /* Check for another catch after unconditional catch. */ - if (hasUnconditionalCatch) { - error(JSMSG_CATCH_AFTER_GENERAL); + Node catchName; + if (omittedBinding) { + catchName = null(); + } else { + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); + + if (!tokenStream.getToken(&tt)) return null(); - } - - /* - * Create a lexical scope node around the whole catch clause, - * including the head. - */ - ParseContext::Statement stmt(pc, StatementKind::Catch); - ParseContext::Scope scope(this); - if (!scope.init(pc)) - return null(); - - /* - * Legal catch forms are: - * catch (lhs) { - * catch (lhs if ) { - * catch { - * where lhs is a name or a destructuring left-hand side. - * The second is legal only #ifdef JS_HAS_CATCH_GUARD. - */ - bool omittedBinding; - if (!tokenStream.matchToken(&omittedBinding, TOK_LC)) - return null(); - - Node catchName; - Node catchGuard = null(); - - if (omittedBinding) { - catchName = null(); - } else { - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); - - if (!tokenStream.getToken(&tt)) + switch (tt) { + case TOK_LB: + case TOK_LC: + catchName = destructuringDeclaration(DeclarationKind::CatchParameter, + yieldHandling, tt); + if (!catchName) return null(); - switch (tt) { - case TOK_LB: - case TOK_LC: - catchName = destructuringDeclaration(DeclarationKind::CatchParameter, - yieldHandling, tt); - if (!catchName) - return null(); - break; + break; - default: { - if (!TokenKindIsPossibleIdentifierName(tt)) { - error(JSMSG_CATCH_IDENTIFIER); - return null(); - } - - catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter, - yieldHandling); - if (!catchName) - return null(); - break; - } + default: { + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_CATCH_IDENTIFIER); + return null(); } -#if JS_HAS_CATCH_GUARD - /* - * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') - * to avoid conflicting with the JS2/ECMAv4 type annotation - * catchguard syntax. - */ - bool matched; - if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand)) + catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter, + yieldHandling); + if (!catchName) return null(); - if (matched) { - catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited); - if (!catchGuard) - return null(); - } -#endif - MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH); - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); + break; + } } - Node catchBody = catchBlockStatement(yieldHandling, scope); - if (!catchBody) - return null(); + MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH); - if (!catchGuard) - hasUnconditionalCatch = true; + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); + } - pnblock = finishLexicalScope(scope, catchBody); - if (!pnblock) - return null(); + Node catchBody = catchBlockStatement(yieldHandling, scope); + if (!catchBody) + return null(); - if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody)) - return null(); - handler.setEndPosition(catchList, pos().end); - handler.setEndPosition(pnblock, pos().end); + catchScope = finishLexicalScope(scope, catchBody); + if (!catchScope) + return null(); - if (!tokenStream.getToken(&tt, TokenStream::Operand)) - return null(); - } while (tt == TOK_CATCH); + if (!handler.setupCatchScope(catchScope, catchName, catchBody)) + return null(); + handler.setEndPosition(catchScope, pos().end); + + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); } Node finallyBlock = null(); @@ -6940,12 +6901,12 @@ Parser::tryStatement(YieldHandling yieldHandling) } else { tokenStream.ungetToken(); } - if (!catchList && !finallyBlock) { + if (!catchScope && !finallyBlock) { error(JSMSG_CATCH_OR_FINALLY); return null(); } - return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock); + return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock); } template diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 06c332e7d4f8..84eb6d266c0f 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -313,7 +313,7 @@ class SyntaxParseHandler } Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; } - Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) { + Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) { return NodeGeneric; } Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; } @@ -325,8 +325,9 @@ class SyntaxParseHandler Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; } - MOZ_MUST_USE bool addCatchBlock(Node catchList, Node letBlock, Node catchName, - Node catchGuard, Node catchBody) { return true; } + MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) { + return true; + } MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; } @@ -406,10 +407,6 @@ class SyntaxParseHandler // This method should only be called from parsers using FullParseHandler. Node singleBindingFromDeclaration(Node decl) = delete; - Node newCatchList(const TokenPos& pos) { - return NodeGeneric; - } - Node newCommaExpressionList(Node kid) { return NodeGeneric; } diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 4ea1b82b928e..82343696adca 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -12,7 +12,6 @@ */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ #ifndef NIGHTLY_BUILD diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js index c6bfcd79862a..2e0e76801065 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js @@ -120,11 +120,11 @@ function caseClause(test, stmts) { function defaultClause(stmts) { return Pattern({ type: "SwitchCase", test: null, consequent: stmts }); } -function catchClause(id, guard, body) { - return Pattern({ type: "CatchClause", param: id, guard: guard, body: body }); +function catchClause(id, body) { + return Pattern({ type: "CatchClause", param: id, body: body }); } -function tryStmt(body, guarded, unguarded, fin) { - return Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handler: unguarded, finalizer: fin }); +function tryStmt(body, handler, fin) { + return Pattern({ type: "TryStatement", block: body, handler: handler, finalizer: fin }); } function superProp(id) { diff --git a/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js b/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js index 6123505d77b1..4d869a7f7e11 100644 --- a/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js +++ b/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js @@ -174,10 +174,8 @@ return { returnStatement: function(expr) { return expr ? ["ReturnStmt", {}, expr] : ["ReturnStmt", {}]; }, - tryStatement: function(body, catches, fin) { - if (catches.length > 1) - throw new SyntaxError("multiple catch clauses not supported"); - var node = ["TryStmt", body, catches[0] || ["Empty"]]; + tryStatement: function(body, handler, fin) { + var node = ["TryStmt", body, handler]; if (fin) node.push(fin); return node; @@ -194,9 +192,7 @@ return { stmts.unshift("DefaultCase", {}); return stmts; }, - catchClause: function(param, guard, body) { - if (guard) - throw new SyntaxError("catch guards not supported"); + catchClause: function(param, body) { param[0] = "IdPatt"; return ["CatchClause", {}, param, body]; }, diff --git a/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js b/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js index f7abcfe19b2d..294136410177 100644 --- a/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js +++ b/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js @@ -42,12 +42,8 @@ assertGlobalExpr("this", 14, { thisExpression: () => 14 }); assertGlobalExpr("(function*() { yield 42 })", genFunExpr("es6", null, [], blockStmt([exprStmt(19)])), { yieldExpression: () => 19 }); assertGlobalStmt("switch (x) { case y: }", switchStmt(ident("x"), [1]), { switchCase: () => 1 }); -assertGlobalStmt("try { } catch (e) { }", 2, { tryStatement: (b, g, u, f) => u, catchClause: () => 2 }); -assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", [2, 2], { tryStatement: (b, g, u, f) => g, catchClause: () => 2 }); -assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [], 2, null), { catchClause: () => 2 }); -assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", - tryStmt(blockStmt([]), [2, 2], null, null), - { catchClause: () => 2 }); +assertGlobalStmt("try { } catch (e) { }", 2, { tryStatement: (b, h, f) => h, catchClause: () => 2 }); +assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), 2, null), { catchClause: () => 2 }); assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: () => 1 }); assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: () => 2 }); diff --git a/js/src/tests/js1_8_5/reflect-parse/statements.js b/js/src/tests/js1_8_5/reflect-parse/statements.js index 5544fe78914d..36a8b3ecea8e 100644 --- a/js/src/tests/js1_8_5/reflect-parse/statements.js +++ b/js/src/tests/js1_8_5/reflect-parse/statements.js @@ -52,40 +52,23 @@ assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; case caseClause(lit(42), [ exprStmt(lit(42)) ]) ])); assertStmt("try { } catch (e) { }", tryStmt(blockStmt([]), - [], - catchClause(ident("e"), null, blockStmt([])), + catchClause(ident("e"), blockStmt([])), null)); assertStmt("try { } catch (e) { } finally { }", tryStmt(blockStmt([]), - [], - catchClause(ident("e"), null, blockStmt([])), + catchClause(ident("e"), blockStmt([])), blockStmt([]))); assertStmt("try { } finally { }", tryStmt(blockStmt([]), - [], null, blockStmt([]))); -assertStmt("try { } catch (e if foo) { } catch (e if bar) { } finally { }", - tryStmt(blockStmt([]), - [ catchClause(ident("e"), ident("foo"), blockStmt([])), - catchClause(ident("e"), ident("bar"), blockStmt([])) ], - null, - blockStmt([]))); -assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } finally { }", - tryStmt(blockStmt([]), - [ catchClause(ident("e"), ident("foo"), blockStmt([])), - catchClause(ident("e"), ident("bar"), blockStmt([])) ], - catchClause(ident("e"), null, blockStmt([])), - blockStmt([]))); assertStmt("try { } catch { }", tryStmt(blockStmt([]), - [], - catchClause(null, null, blockStmt([])), + catchClause(null, blockStmt([])), null)); assertStmt("try { } catch { } finally { }", tryStmt(blockStmt([]), - [], - catchClause(null, null, blockStmt([])), + catchClause(null, blockStmt([])), blockStmt([]))); From 73138b170fe98d59104ea55a929151cb40bd8be0 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 6 Dec 2017 14:54:58 +0000 Subject: [PATCH 55/78] Bug 1420420 - Update module implementation to match latest spec regarding handling of instantiation errors r=anba r=baku r=jgraham --- dom/script/ModuleLoadRequest.cpp | 4 +- dom/script/ModuleScript.cpp | 43 +-- dom/script/ModuleScript.h | 13 +- dom/script/ScriptLoader.cpp | 79 +++-- dom/script/ScriptLoader.h | 1 + js/src/builtin/Module.js | 294 +++++++++--------- js/src/builtin/ModuleObject.cpp | 39 ++- js/src/builtin/ModuleObject.h | 13 +- js/src/builtin/SelfHostingDefines.h | 14 +- js/src/builtin/TestingFunctions.cpp | 4 +- js/src/jit-test/tests/modules/bug-1284486.js | 12 +- .../jit-test/tests/modules/bug-1420420-2.js | 18 ++ .../jit-test/tests/modules/bug-1420420-3.js | 8 + .../jit-test/tests/modules/bug-1420420-4.js | 15 + js/src/jit-test/tests/modules/bug-1420420.js | 18 ++ js/src/js.msg | 1 - js/src/jsapi.cpp | 14 - js/src/jsapi.h | 6 - js/src/vm/EnvironmentObject.cpp | 4 +- js/src/vm/SelfHosting.cpp | 1 - .../module/instantiation-error-1.html.ini | 3 + .../module/instantiation-error-2.html.ini | 3 + .../module/instantiation-error-3.html.ini | 4 +- .../module/instantiation-error-4.html.ini | 3 + .../module/instantiation-error-5.html.ini | 3 + 25 files changed, 337 insertions(+), 280 deletions(-) create mode 100644 js/src/jit-test/tests/modules/bug-1420420-2.js create mode 100644 js/src/jit-test/tests/modules/bug-1420420-3.js create mode 100644 js/src/jit-test/tests/modules/bug-1420420-4.js create mode 100644 js/src/jit-test/tests/modules/bug-1420420.js create mode 100644 testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html.ini create mode 100644 testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html.ini create mode 100644 testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html.ini create mode 100644 testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html.ini diff --git a/dom/script/ModuleLoadRequest.cpp b/dom/script/ModuleLoadRequest.cpp index af85028c4db7..8ce2541b4518 100644 --- a/dom/script/ModuleLoadRequest.cpp +++ b/dom/script/ModuleLoadRequest.cpp @@ -90,7 +90,7 @@ ModuleLoadRequest::ModuleLoaded() LOG(("ScriptLoadRequest (%p): Module loaded", this)); mModuleScript = mLoader->GetFetchedModule(mURI); - if (!mModuleScript || mModuleScript->IsErrored()) { + if (!mModuleScript || mModuleScript->HasParseError()) { ModuleErrored(); return; } @@ -104,7 +104,7 @@ ModuleLoadRequest::ModuleErrored() LOG(("ScriptLoadRequest (%p): Module errored", this)); mLoader->CheckModuleDependenciesLoaded(this); - MOZ_ASSERT(!mModuleScript || mModuleScript->IsErrored()); + MOZ_ASSERT(!mModuleScript || mModuleScript->HasParseError()); CancelImports(); SetReady(); diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp index a8e018343670..73cc90446c63 100644 --- a/dom/script/ModuleScript.cpp +++ b/dom/script/ModuleScript.cpp @@ -22,7 +22,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) tmp->UnlinkModuleRecord(); - tmp->mError.setUndefined(); + tmp->mParseError.setUndefined(); + tmp->mErrorToRethrow.setUndefined(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript) @@ -31,7 +32,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mError) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript) @@ -44,7 +46,8 @@ ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL) MOZ_ASSERT(mLoader); MOZ_ASSERT(mBaseURL); MOZ_ASSERT(!mModuleRecord); - MOZ_ASSERT(mError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); } void @@ -70,7 +73,8 @@ void ModuleScript::SetModuleRecord(JS::Handle aModuleRecord) { MOZ_ASSERT(!mModuleRecord); - MOZ_ASSERT(mError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); mModuleRecord = aModuleRecord; @@ -81,37 +85,24 @@ ModuleScript::SetModuleRecord(JS::Handle aModuleRecord) } void -ModuleScript::SetPreInstantiationError(const JS::Value& aError) +ModuleScript::SetParseError(const JS::Value& aError) { MOZ_ASSERT(!aError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); UnlinkModuleRecord(); - mError = aError; - + mParseError = aError; HoldJSObjects(this); } -bool -ModuleScript::IsErrored() const +void +ModuleScript::SetErrorToRethrow(const JS::Value& aError) { - if (!mModuleRecord) { - MOZ_ASSERT(!mError.isUndefined()); - return true; - } + MOZ_ASSERT(!aError.isUndefined()); + MOZ_ASSERT(!HasErrorToRethrow()); - return JS::IsModuleErrored(mModuleRecord); -} - -JS::Value -ModuleScript::Error() const -{ - MOZ_ASSERT(IsErrored()); - - if (!mModuleRecord) { - return mError; - } - - return JS::GetModuleError(mModuleRecord); + mErrorToRethrow = aError; } } // dom namespace diff --git a/dom/script/ModuleScript.h b/dom/script/ModuleScript.h index 3831a1f4e507..553c6394e174 100644 --- a/dom/script/ModuleScript.h +++ b/dom/script/ModuleScript.h @@ -23,7 +23,8 @@ class ModuleScript final : public nsISupports RefPtr mLoader; nsCOMPtr mBaseURL; JS::Heap mModuleRecord; - JS::Heap mError; + JS::Heap mParseError; + JS::Heap mErrorToRethrow; ~ModuleScript(); @@ -35,14 +36,16 @@ public: nsIURI* aBaseURL); void SetModuleRecord(JS::Handle aModuleRecord); - void SetPreInstantiationError(const JS::Value& aError); + void SetParseError(const JS::Value& aError); + void SetErrorToRethrow(const JS::Value& aError); ScriptLoader* Loader() const { return mLoader; } JSObject* ModuleRecord() const { return mModuleRecord; } nsIURI* BaseURL() const { return mBaseURL; } - - bool IsErrored() const; - JS::Value Error() const; + JS::Value ParseError() const { return mParseError; } + JS::Value ErrorToRethrow() const { return mErrorToRethrow; } + bool HasParseError() const { return !mParseError.isUndefined(); } + bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); } void UnlinkModuleRecord(); }; diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index b99fa74f9884..490743dad678 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -444,7 +444,7 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) return rv; } - if (!aRequest->mModuleScript->IsErrored()) { + if (!aRequest->mModuleScript->HasParseError()) { StartFetchingModuleDependencies(aRequest); } @@ -518,7 +518,7 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) return NS_ERROR_FAILURE; } - moduleScript->SetPreInstantiationError(error); + moduleScript->SetParseError(error); aRequest->ModuleErrored(); return NS_OK; } @@ -530,8 +530,6 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) nsCOMArray urls; rv = ResolveRequestedModules(aRequest, urls); if (NS_FAILED(rv)) { - // ResolveRequestedModules sets pre-instanitation error on failure. - MOZ_ASSERT(moduleScript->IsErrored()); aRequest->ModuleErrored(); return NS_OK; } @@ -574,7 +572,7 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, return NS_ERROR_OUT_OF_MEMORY; } - aScript->SetPreInstantiationError(error); + aScript->SetParseError(error); return NS_OK; } @@ -675,7 +673,6 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray& aUrls) } // Let url be the result of resolving a module specifier given module script and requested. - ModuleScript* ms = aRequest->mModuleScript; nsCOMPtr uri = ResolveModuleSpecifier(ms, specifier); if (!uri) { uint32_t lineNumber = 0; @@ -702,7 +699,7 @@ void ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) { MOZ_ASSERT(aRequest->mModuleScript); - MOZ_ASSERT(!aRequest->mModuleScript->IsErrored()); + MOZ_ASSERT(!aRequest->mModuleScript->HasParseError()); MOZ_ASSERT(!aRequest->IsReadyToRun()); LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest)); @@ -814,7 +811,7 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) ModuleScript* ms = script->Loader()->GetFetchedModule(uri); MOZ_ASSERT(ms, "Resolved module not found in module map"); - MOZ_ASSERT(!ms->IsErrored()); + MOZ_ASSERT(!ms->HasParseError()); *vp = JS::ObjectValue(*ms->ModuleRecord()); return true; @@ -844,22 +841,20 @@ ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) LOG(("ScriptLoadRequest (%p): Check dependencies loaded", aRequest)); RefPtr moduleScript = aRequest->mModuleScript; - if (moduleScript && !moduleScript->IsErrored()) { - for (auto childRequest : aRequest->mImports) { - ModuleScript* childScript = childRequest->mModuleScript; - if (!childScript) { - aRequest->mModuleScript = nullptr; - LOG(("ScriptLoadRequest (%p): %p failed (load error)", aRequest, childScript)); - return; - } else if (childScript->IsErrored()) { - moduleScript->SetPreInstantiationError(childScript->Error()); - LOG(("ScriptLoadRequest (%p): %p failed (error)", aRequest, childScript)); - return; - } + if (!moduleScript || moduleScript->HasParseError()) { + return; + } + + for (auto childRequest : aRequest->mImports) { + ModuleScript* childScript = childRequest->mModuleScript; + if (!childScript) { + aRequest->mModuleScript = nullptr; + LOG(("ScriptLoadRequest (%p): %p failed (load error)", aRequest, childRequest.get())); + return; } } - LOG(("ScriptLoadRequest (%p): all ok", aRequest)); + LOG(("ScriptLoadRequest (%p): all ok", aRequest)); } void @@ -867,7 +862,7 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) { if (aRequest->IsTopLevel()) { ModuleScript* moduleScript = aRequest->mModuleScript; - if (moduleScript && !moduleScript->IsErrored()) { + if (moduleScript && !moduleScript->HasErrorToRethrow()) { if (!InstantiateModuleTree(aRequest)) { aRequest->mModuleScript = nullptr; } @@ -881,6 +876,28 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) } } +JS::Value +ScriptLoader::FindFirstParseError(ModuleLoadRequest* aRequest) +{ + MOZ_ASSERT(aRequest); + + ModuleScript* moduleScript = aRequest->mModuleScript; + MOZ_ASSERT(moduleScript); + + if (moduleScript->HasParseError()) { + return moduleScript->ParseError(); + } + + for (ModuleLoadRequest* childRequest : aRequest->mImports) { + JS::Value error = FindFirstParseError(childRequest); + if (!error.isUndefined()) { + return error; + } + } + + return JS::UndefinedValue(); +} + bool ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) { @@ -893,6 +910,14 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) ModuleScript* moduleScript = aRequest->mModuleScript; MOZ_ASSERT(moduleScript); + + JS::Value parseError = FindFirstParseError(aRequest); + if (!parseError.isUndefined()) { + LOG(("ScriptLoadRequest (%p): found parse error", aRequest)); + moduleScript->SetErrorToRethrow(parseError); + return true; + } + MOZ_ASSERT(moduleScript->ModuleRecord()); nsAutoMicroTask mt; @@ -915,7 +940,7 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) return false; } MOZ_ASSERT(!exception.isUndefined()); - // Ignore the exception. It will be recorded in the module record. + moduleScript->SetErrorToRethrow(exception); } return true; @@ -1582,7 +1607,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) } else { AddDeferRequest(request); } - if (!modReq->mModuleScript->IsErrored()) { + if (!modReq->mModuleScript->HasParseError()) { StartFetchingModuleDependencies(modReq); } return false; @@ -2210,9 +2235,9 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) MOZ_ASSERT(!request->mOffThreadToken); ModuleScript* moduleScript = request->mModuleScript; - if (moduleScript->IsErrored()) { - LOG(("ScriptLoadRequest (%p): module is errored", aRequest)); - JS::Rooted error(cx, moduleScript->Error()); + if (moduleScript->HasErrorToRethrow()) { + LOG(("ScriptLoadRequest (%p): module has error to rethrow", aRequest)); + JS::Rooted error(cx, moduleScript->ErrorToRethrow()); JS_SetPendingException(cx, error); return NS_OK; // An error is reported by AutoEntryScript. } diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index 133b472604d5..8ffb3bae8e97 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -488,6 +488,7 @@ private: void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest); void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest); bool InstantiateModuleTree(ModuleLoadRequest* aRequest); + JS::Value FindFirstParseError(ModuleLoadRequest* aRequest); void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest); RefPtr diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js index 4e365f501781..d19ab03b7edd 100644 --- a/js/src/builtin/Module.js +++ b/js/src/builtin/Module.js @@ -68,10 +68,14 @@ function ModuleGetExportedNames(exportStarSet = []) function ModuleSetStatus(module, newStatus) { - assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED, + assert(newStatus >= MODULE_STATUS_UNINSTANTIATED && + newStatus <= MODULE_STATUS_EVALUATED_ERROR, "Bad new module status in ModuleSetStatus"); - if (newStatus !== MODULE_STATUS_ERRORED) - assert(newStatus > module.status, "New module status inconsistent with current status"); + + assert((module.status <= MODULE_STATUS_INSTANTIATING && + newStatus === MODULE_STATUS_UNINSTANTIATED) || + newStatus > module.status, + "New module status inconsistent with current status"); UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus); } @@ -81,20 +85,15 @@ function ModuleSetStatus(module, newStatus) // Returns an object describing the location of the resolved export or // indicating a failure. // -// On success this returns: { resolved: true, module, bindingName } +// On success this returns a resolved binding record: { module, bindingName } // -// There are three failure cases: +// There are two failure cases: // -// - The resolution failure can be blamed on a particular module. -// Returns: { resolved: false, module, ambiguous: false } +// - If no definition was found or the request is found to be circular, *null* +// is returned. // -// - No culprit can be determined and the resolution failure was due to star -// export ambiguity. -// Returns: { resolved: false, module: null, ambiguous: true } -// -// - No culprit can be determined and the resolution failure was not due to -// star export ambiguity. -// Returns: { resolved: false, module: null, ambiguous: false } +// - If the request is found to be ambiguous, the string `"ambiguous"` is +// returned. // function ModuleResolveExport(exportName, resolveSet = []) { @@ -107,53 +106,47 @@ function ModuleResolveExport(exportName, resolveSet = []) let module = this; // Step 2 - assert(module.status !== MODULE_STATUS_ERRORED, "Bad module state in ResolveExport"); - - // Step 3 for (let i = 0; i < resolveSet.length; i++) { let r = resolveSet[i]; if (r.module === module && r.exportName === exportName) { // This is a circular import request. - return {resolved: false, module: null, ambiguous: false}; + return null; } } - // Step 4 + // Step 3 _DefineDataProperty(resolveSet, resolveSet.length, {module, exportName}); - // Step 5 + // Step 4 let localExportEntries = module.localExportEntries; for (let i = 0; i < localExportEntries.length; i++) { let e = localExportEntries[i]; if (exportName === e.exportName) - return {resolved: true, module, bindingName: e.localName}; + return {module, bindingName: e.localName}; } - // Step 6 + // Step 5 let indirectExportEntries = module.indirectExportEntries; for (let i = 0; i < indirectExportEntries.length; i++) { let e = indirectExportEntries[i]; if (exportName === e.exportName) { let importedModule = CallModuleResolveHook(module, e.moduleRequest, MODULE_STATUS_UNINSTANTIATED); - let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName, - resolveSet); - if (!resolution.resolved && !resolution.module) - resolution.module = module; - return resolution; + return callFunction(importedModule.resolveExport, importedModule, e.importName, + resolveSet); } } - // Step 7 + // Step 6 if (exportName === "default") { // A default export cannot be provided by an export *. - return {resolved: false, module: null, ambiguous: false}; + return null; } - // Step 8 + // Step 7 let starResolution = null; - // Step 9 + // Step 8 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; @@ -161,26 +154,30 @@ function ModuleResolveExport(exportName, resolveSet = []) MODULE_STATUS_UNINSTANTIATED); let resolution = callFunction(importedModule.resolveExport, importedModule, exportName, resolveSet); - if (!resolution.resolved && (resolution.module || resolution.ambiguous)) + if (resolution === "ambiguous") return resolution; - if (resolution.resolved) { + if (resolution !== null) { if (starResolution === null) { starResolution = resolution; } else { if (resolution.module !== starResolution.module || resolution.bindingName !== starResolution.bindingName) { - return {resolved: false, module: null, ambiguous: true}; + return "ambiguous"; } } } } - // Step 10 - if (starResolution !== null) - return starResolution; + // Step 9 + return starResolution; +} - return {resolved: false, module: null, ambiguous: false}; +function IsResolvedBinding(resolution) +{ + assert(resolution === "ambiguous" || typeof resolution === "object", + "Bad module resolution result"); + return typeof resolution === "object" && resolution !== null; } // 15.2.1.18 GetModuleNamespace(module) @@ -189,12 +186,12 @@ function GetModuleNamespace(module) // Step 1 assert(IsModule(module), "GetModuleNamespace called with non-module"); - // Step 2 + // Steps 2-3 assert(module.status !== MODULE_STATUS_UNINSTANTIATED && - module.status !== MODULE_STATUS_ERRORED, + module.status !== MODULE_STATUS_EVALUATED_ERROR, "Bad module state in GetModuleNamespace"); - // Step 3 + // Step 4 let namespace = module.namespace; // Step 3 @@ -204,7 +201,7 @@ function GetModuleNamespace(module) for (let i = 0; i < exportedNames.length; i++) { let name = exportedNames[i]; let resolution = callFunction(module.resolveExport, module, name); - if (resolution.resolved) + if (IsResolvedBinding(resolution)) _DefineDataProperty(unambiguousNames, unambiguousNames.length, name); } namespace = ModuleNamespaceCreate(module, unambiguousNames); @@ -226,7 +223,7 @@ function ModuleNamespaceCreate(module, exports) for (let i = 0; i < exports.length; i++) { let name = exports[i]; let binding = callFunction(module.resolveExport, module, name); - assert(binding.resolved, "Failed to resolve binding"); + assert(IsResolvedBinding(binding), "Failed to resolve binding"); AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); } @@ -237,11 +234,6 @@ function GetModuleEnvironment(module) { assert(IsModule(module), "Non-module passed to GetModuleEnvironment"); - // Check for a previous failed attempt to instantiate this module. This can - // only happen due to a bug in the module loader. - if (module.status === MODULE_STATUS_ERRORED) - ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status); - assert(module.status >= MODULE_STATUS_INSTANTIATING, "Attempt to access module environement before instantation"); @@ -252,19 +244,6 @@ function GetModuleEnvironment(module) return env; } -function RecordModuleError(module, error) -{ - // Set the module's status to 'errored' to indicate a failed module - // instantiation and record the exception. The environment slot is also - // reset to 'undefined'. - - assert(IsObject(module) && IsModule(module), "Non-module passed to RecordModuleError"); - - ModuleSetStatus(module, MODULE_STATUS_ERRORED); - UnsafeSetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT, error); - UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined); -} - function CountArrayValues(array, value) { let count = 0; @@ -284,6 +263,16 @@ function ArrayContains(array, value) return false; } +function HandleModuleInstantiationFailure(module) +{ + // Reset the module to the "uninstantiated" state. Don't reset the + // environment slot as the environment object will be required by any + // possible future instantiation attempt. + ModuleSetStatus(module, MODULE_STATUS_UNINSTANTIATED); + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, undefined); + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, undefined); +} + // 15.2.1.16.4 ModuleInstantiate() function ModuleInstantiate() { @@ -305,37 +294,29 @@ function ModuleInstantiate() // Steps 4-5 try { - InnerModuleDeclarationInstantiation(module, stack, 0); + InnerModuleInstantiation(module, stack, 0); } catch (error) { for (let i = 0; i < stack.length; i++) { let m = stack[i]; - - assert(m.status === MODULE_STATUS_INSTANTIATING || - m.status === MODULE_STATUS_ERRORED, - "Bad module status after failed instantiation"); - - RecordModuleError(m, error); + assert(m.status === MODULE_STATUS_INSTANTIATING, + "Expected instantiating status during failed instantiation"); + HandleModuleInstantiationFailure(m); } - if (stack.length === 0 && - typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined") - { - // This can happen due to OOM when appending to the stack or - // over-recursion errors. - RecordModuleError(module, error); - } + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0) + HandleModuleInstantiationFailure(module); - assert(module.status === MODULE_STATUS_ERRORED, - "Bad module status after failed instantiation"); - assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error), - "Module has different error set after failed instantiation"); + assert(module.status === MODULE_STATUS_UNINSTANTIATED, + "Expected uninstantiated status after failed instantiation"); throw error; } // Step 6 - assert(module.status == MODULE_STATUS_INSTANTIATED || - module.status == MODULE_STATUS_EVALUATED, + assert(module.status === MODULE_STATUS_INSTANTIATED || + module.status === MODULE_STATUS_EVALUATED || + module.status === MODULE_STATUS_EVALUATED_ERROR, "Bad module status after successful instantiation"); // Step 7 @@ -347,8 +328,8 @@ function ModuleInstantiate() } _SetCanonicalName(ModuleInstantiate, "ModuleInstantiate"); -// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index) -function InnerModuleDeclarationInstantiation(module, stack, index) +// 15.2.1.16.4.1 InnerModuleInstantiation(module, stack, index) +function InnerModuleInstantiation(module, stack, index) { // Step 1 // TODO: Support module records other than source text module records. @@ -356,42 +337,40 @@ function InnerModuleDeclarationInstantiation(module, stack, index) // Step 2 if (module.status === MODULE_STATUS_INSTANTIATING || module.status === MODULE_STATUS_INSTANTIATED || - module.status === MODULE_STATUS_EVALUATED) + module.status === MODULE_STATUS_EVALUATED || + module.status === MODULE_STATUS_EVALUATED_ERROR) { return index; } // Step 3 - if (module.status === MODULE_STATUS_ERRORED) - throw module.error; - - // Step 4 assert(module.status === MODULE_STATUS_UNINSTANTIATED, "Bad module status in ModuleDeclarationInstantiation"); - // Steps 5 + // Steps 4 ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING); - // Step 6-8 + // Step 5-7 UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index); UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index); index++; - // Step 9 + // Step 8 _DefineDataProperty(stack, stack.length, module); - // Step 10 + // Step 9 let requestedModules = module.requestedModules; for (let i = 0; i < requestedModules.length; i++) { let required = requestedModules[i].moduleSpecifier; - let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED); + let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED); - index = InnerModuleDeclarationInstantiation(requiredModule, stack, index); + index = InnerModuleInstantiation(requiredModule, stack, index); assert(requiredModule.status === MODULE_STATUS_INSTANTIATING || requiredModule.status === MODULE_STATUS_INSTANTIATED || - requiredModule.status === MODULE_STATUS_EVALUATED, - "Bad required module status after InnerModuleDeclarationInstantiation"); + requiredModule.status === MODULE_STATUS_EVALUATED || + requiredModule.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad required module status after InnerModuleInstantiation"); assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) === ArrayContains(stack, requiredModule), @@ -407,16 +386,16 @@ function InnerModuleDeclarationInstantiation(module, stack, index) } } - // Step 11 + // Step 10 ModuleDeclarationEnvironmentSetup(module); - // Steps 12-13 + // Steps 11-12 assert(CountArrayValues(stack, module) === 1, "Current module should appear exactly once in the stack"); assert(module.dfsAncestorIndex <= module.dfsIndex, "Bad DFS ancestor index"); - // Step 14 + // Step 13 if (module.dfsAncestorIndex === module.dfsIndex) { let requiredModule; do { @@ -437,11 +416,9 @@ function ModuleDeclarationEnvironmentSetup(module) for (let i = 0; i < indirectExportEntries.length; i++) { let e = indirectExportEntries[i]; let resolution = callFunction(module.resolveExport, module, e.exportName); - assert(resolution.resolved || resolution.module, - "Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup"); - if (!resolution.resolved) { - return ResolutionError(resolution, "indirectExport", e.exportName, - e.lineNumber, e.columnNumber); + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "indirectExport", e.exportName, + e.lineNumber, e.columnNumber); } } @@ -461,12 +438,9 @@ function ModuleDeclarationEnvironmentSetup(module) } else { let resolution = callFunction(importedModule.resolveExport, importedModule, imp.importName); - if (!resolution.resolved && !resolution.module) - resolution.module = module; - - if (!resolution.resolved) { - return ResolutionError(resolution, "import", imp.importName, - imp.lineNumber, imp.columnNumber); + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "import", imp.importName, + imp.lineNumber, imp.columnNumber); } CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName); @@ -476,38 +450,58 @@ function ModuleDeclarationEnvironmentSetup(module) InstantiateModuleFunctionDeclarations(module); } -// 15.2.1.16.4.3 ResolutionError(module) -function ResolutionError(resolution, kind, name, line, column) +function ThrowResolutionError(module, resolution, kind, name, line, column) { - let module = resolution.module; - assert(module !== null, - "Null module passed to ResolutionError"); - - assert(module.status === MODULE_STATUS_UNINSTANTIATED || - module.status === MODULE_STATUS_INSTANTIATING, - "Unexpected module status in ResolutionError"); + assert(module.status === MODULE_STATUS_INSTANTIATING, + "Unexpected module status in ThrowResolutionError"); assert(kind === "import" || kind === "indirectExport", - "Unexpected kind in ResolutionError"); + "Unexpected kind in ThrowResolutionError"); assert(line > 0, "Line number should be present for all imports and indirect exports"); + let ambiguous = resolution === "ambiguous"; + let errorNumber; - if (kind === "import") { - errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT - : JSMSG_MISSING_IMPORT; - } else { - errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT - : JSMSG_MISSING_INDIRECT_EXPORT; - } + if (kind === "import") + errorNumber = ambiguous ? JSMSG_AMBIGUOUS_IMPORT : JSMSG_MISSING_IMPORT; + else + errorNumber = ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT : JSMSG_MISSING_INDIRECT_EXPORT; let message = GetErrorMessage(errorNumber) + ": " + name; let error = CreateModuleSyntaxError(module, line, column, message); - RecordModuleError(module, error); throw error; } +function GetModuleEvaluationError(module) +{ + assert(IsObject(module) && IsModule(module), + "Non-module passed to GetModuleEvaluationError"); + assert(module.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad module status in GetModuleEvaluationError"); + return UnsafeGetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT); +} + +function RecordModuleEvaluationError(module, error) +{ + // Set the module's EvaluationError slot to indicate a failed module + // evaluation. + + assert(IsObject(module) && IsModule(module), + "Non-module passed to RecordModuleEvaluationError"); + + if (module.status === MODULE_STATUS_EVALUATED_ERROR) { + // It would be nice to assert that |error| is the same as the one we + // previously recorded, but that's not always true in the case of out of + // memory and over-recursion errors. + return; + } + + ModuleSetStatus(module, MODULE_STATUS_EVALUATED_ERROR); + UnsafeSetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT, error); +} + // 15.2.1.16.5 ModuleEvaluate() function ModuleEvaluate() { @@ -518,9 +512,9 @@ function ModuleEvaluate() let module = this; // Step 2 - if (module.status !== MODULE_STATUS_ERRORED && - module.status !== MODULE_STATUS_INSTANTIATED && - module.status !== MODULE_STATUS_EVALUATED) + if (module.status !== MODULE_STATUS_INSTANTIATED && + module.status !== MODULE_STATUS_EVALUATED && + module.status !== MODULE_STATUS_EVALUATED_ERROR) { ThrowInternalError(JSMSG_BAD_MODULE_STATUS); } @@ -534,30 +528,22 @@ function ModuleEvaluate() } catch (error) { for (let i = 0; i < stack.length; i++) { let m = stack[i]; - assert(m.status === MODULE_STATUS_EVALUATING, "Bad module status after failed evaluation"); - - RecordModuleError(m, error); + RecordModuleEvaluationError(m, error); } - if (stack.length === 0 && - typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined") - { - // This can happen due to OOM when appending to the stack or - // over-recursion errors. - RecordModuleError(module, error); - } + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0) + RecordModuleEvaluationError(module, error); - assert(module.status === MODULE_STATUS_ERRORED, + assert(module.status === MODULE_STATUS_EVALUATED_ERROR, "Bad module status after failed evaluation"); - assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error), - "Module has different error set after failed evaluation"); throw error; } - assert(module.status == MODULE_STATUS_EVALUATED, + assert(module.status === MODULE_STATUS_EVALUATED, "Bad module status after successful evaluation"); assert(stack.length === 0, "Stack should be empty after successful evaluation"); @@ -573,19 +559,19 @@ function InnerModuleEvaluation(module, stack, index) // TODO: Support module records other than source text module records. // Step 2 - if (module.status === MODULE_STATUS_EVALUATING || - module.status === MODULE_STATUS_EVALUATED) - { + if (module.status === MODULE_STATUS_EVALUATED_ERROR) + throw GetModuleEvaluationError(module); + + if (module.status === MODULE_STATUS_EVALUATED) return index; - } // Step 3 - if (module.status === MODULE_STATUS_ERRORED) - throw module.error; + if (module.status === MODULE_STATUS_EVALUATING) + return index; // Step 4 assert(module.status === MODULE_STATUS_INSTANTIATED, - "Bad module status in ModuleEvaluation"); + "Bad module status in InnerModuleEvaluation"); // Step 5 ModuleSetStatus(module, MODULE_STATUS_EVALUATING); @@ -607,8 +593,8 @@ function InnerModuleEvaluation(module, stack, index) index = InnerModuleEvaluation(requiredModule, stack, index); - assert(requiredModule.status == MODULE_STATUS_EVALUATING || - requiredModule.status == MODULE_STATUS_EVALUATED, + assert(requiredModule.status === MODULE_STATUS_EVALUATING || + requiredModule.status === MODULE_STATUS_EVALUATED, "Bad module status after InnerModuleEvaluation"); assert((requiredModule.status === MODULE_STATUS_EVALUATING) === diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 02b88edf7b24..df3294d53199 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -22,10 +22,10 @@ using namespace js; using namespace js::frontend; -static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED && - MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING && +static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING && MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED && - MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED, + MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED && + MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR, "Module statuses are ordered incorrectly"); template @@ -342,12 +342,12 @@ IndirectBindingMap::trace(JSTracer* trc) } bool -IndirectBindingMap::putNew(JSContext* cx, HandleId name, - HandleModuleEnvironmentObject environment, HandleId localName) +IndirectBindingMap::put(JSContext* cx, HandleId name, + HandleModuleEnvironmentObject environment, HandleId localName) { RootedShape shape(cx, environment->lookup(cx, localName)); MOZ_ASSERT(shape); - if (!map_.putNew(name, Binding(environment, shape))) { + if (!map_.put(name, Binding(environment, shape))) { ReportOutOfMemory(cx); return false; } @@ -428,7 +428,7 @@ ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName, RootedModuleEnvironmentObject environment(cx, &targetModule->initialEnvironment()); RootedId exportedNameId(cx, AtomToId(exportedName)); RootedId localNameId(cx, AtomToId(localName)); - return bindings().putNew(cx, exportedNameId, environment, localNameId); + return bindings().put(cx, exportedNameId, environment, localNameId); } const char ModuleNamespaceObject::ProxyHandler::family = 0; @@ -799,7 +799,7 @@ ModuleObject::initialEnvironment() const ModuleEnvironmentObject* ModuleObject::environment() const { - MOZ_ASSERT(status() != MODULE_STATUS_ERRORED); + MOZ_ASSERT(!hadEvaluationError()); // According to the spec the environment record is created during // instantiation, but we create it earlier than that. @@ -845,7 +845,7 @@ void ModuleObject::init(HandleScript script) { initReservedSlot(ScriptSlot, PrivateValue(script)); - initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED)); + initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED)); } void @@ -961,7 +961,8 @@ ModuleObject::script() const static inline void AssertValidModuleStatus(ModuleStatus status) { - MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED); + MOZ_ASSERT(status >= MODULE_STATUS_UNINSTANTIATED && + status <= MODULE_STATUS_EVALUATED_ERROR); } ModuleStatus @@ -972,11 +973,17 @@ ModuleObject::status() const return status; } -Value -ModuleObject::error() const +bool +ModuleObject::hadEvaluationError() const { - MOZ_ASSERT(status() == MODULE_STATUS_ERRORED); - return getReservedSlot(ErrorSlot); + return status() == MODULE_STATUS_EVALUATED_ERROR; +} + +Value +ModuleObject::evaluationError() const +{ + MOZ_ASSERT(hadEvaluationError()); + return getReservedSlot(EvaluationErrorSlot); } Value @@ -1137,7 +1144,7 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self) DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot) -DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot) +DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot) @@ -1152,7 +1159,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle global) static const JSPropertySpec protoAccessors[] = { JS_PSG("namespace", ModuleObject_namespace_Getter, 0), JS_PSG("status", ModuleObject_statusGetter, 0), - JS_PSG("error", ModuleObject_errorGetter, 0), + JS_PSG("evaluationError", ModuleObject_evaluationErrorGetter, 0), JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0), JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0), JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0), diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index c9eb83f82716..54f435a80434 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -133,8 +133,8 @@ class IndirectBindingMap void trace(JSTracer* trc); - bool putNew(JSContext* cx, HandleId name, - HandleModuleEnvironmentObject environment, HandleId localName); + bool put(JSContext* cx, HandleId name, + HandleModuleEnvironmentObject environment, HandleId localName); size_t count() const { return map_.count(); @@ -254,7 +254,7 @@ class ModuleObject : public NativeObject EnvironmentSlot, NamespaceSlot, StatusSlot, - ErrorSlot, + EvaluationErrorSlot, HostDefinedSlot, RequestedModulesSlot, ImportEntriesSlot, @@ -272,8 +272,8 @@ class ModuleObject : public NativeObject "EnvironmentSlot must match self-hosting define"); static_assert(StatusSlot == MODULE_OBJECT_STATUS_SLOT, "StatusSlot must match self-hosting define"); - static_assert(ErrorSlot == MODULE_OBJECT_ERROR_SLOT, - "ErrorSlot must match self-hosting define"); + static_assert(EvaluationErrorSlot == MODULE_OBJECT_EVALUATION_ERROR_SLOT, + "EvaluationErrorSlot must match self-hosting define"); static_assert(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT, "DFSIndexSlot must match self-hosting define"); static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, @@ -303,7 +303,8 @@ class ModuleObject : public NativeObject ModuleEnvironmentObject* environment() const; ModuleNamespaceObject* namespace_(); ModuleStatus status() const; - Value error() const; + bool hadEvaluationError() const; + Value evaluationError() const; Value hostDefinedField() const; ArrayObject& requestedModules() const; ArrayObject& importEntries() const; diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index 23e66e17c60f..5ca1b7514d77 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -95,16 +95,16 @@ #define MODULE_OBJECT_ENVIRONMENT_SLOT 1 #define MODULE_OBJECT_STATUS_SLOT 3 -#define MODULE_OBJECT_ERROR_SLOT 4 +#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4 #define MODULE_OBJECT_DFS_INDEX_SLOT 13 #define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 14 -#define MODULE_STATUS_ERRORED 0 -#define MODULE_STATUS_UNINSTANTIATED 1 -#define MODULE_STATUS_INSTANTIATING 2 -#define MODULE_STATUS_INSTANTIATED 3 -#define MODULE_STATUS_EVALUATING 4 -#define MODULE_STATUS_EVALUATED 5 +#define MODULE_STATUS_UNINSTANTIATED 0 +#define MODULE_STATUS_INSTANTIATING 1 +#define MODULE_STATUS_INSTANTIATED 2 +#define MODULE_STATUS_EVALUATING 3 +#define MODULE_STATUS_EVALUATED 4 +#define MODULE_STATUS_EVALUATED_ERROR 5 #define STRING_GENERICS_CHAR_AT 0 #define STRING_GENERICS_CHAR_CODE_AT 1 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index d04bd8dc3099..213fb61379df 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -4322,7 +4322,7 @@ GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) } RootedModuleObject module(cx, &args[0].toObject().as()); - if (module->status() == MODULE_STATUS_ERRORED) { + if (module->hadEvaluationError()) { JS_ReportErrorASCII(cx, "Module environment unavailable"); return false; } @@ -4365,7 +4365,7 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) } RootedModuleObject module(cx, &args[0].toObject().as()); - if (module->status() == MODULE_STATUS_ERRORED) { + if (module->hadEvaluationError()) { JS_ReportErrorASCII(cx, "Module environment unavailable"); return false; } diff --git a/js/src/jit-test/tests/modules/bug-1284486.js b/js/src/jit-test/tests/modules/bug-1284486.js index 13a13b64507c..61c7cafaa2fe 100644 --- a/js/src/jit-test/tests/modules/bug-1284486.js +++ b/js/src/jit-test/tests/modules/bug-1284486.js @@ -1,9 +1,8 @@ -// This tests that attempting to perform ModuleDeclarationInstantation a second -// time after a failure re-throws the same error. +// This tests that module instantiation can succeed when executed a second +// time after a failure. // // The first attempt fails becuase module 'a' is not available. The second -// attempt fails because of the previous failure (it would otherwise succeed as -// 'a' is now available). +// attempt succeeds as 'a' is now available. // // This test exercises the path where the previously instantiated module is // encountered as an import. @@ -28,12 +27,9 @@ let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;"); let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;"); threw = false; -let e2; try { d.declarationInstantiation(); } catch (exc) { threw = true; - e2 = exc; } -assertEq(threw, true); -assertEq(e1, e2); +assertEq(threw, false); diff --git a/js/src/jit-test/tests/modules/bug-1420420-2.js b/js/src/jit-test/tests/modules/bug-1420420-2.js new file mode 100644 index 000000000000..e7de4eff4c5b --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-2.js @@ -0,0 +1,18 @@ +// Test re-instantiation module after failure. + +load(libdir + "asserts.js"); +load(libdir + "dummyModuleResolveHook.js"); + +moduleRepo["good"] = parseModule(`export let x`); + +moduleRepo["y1"] = parseModule(`export let y`); +moduleRepo["y2"] = parseModule(`export let y`); +moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`); + +moduleRepo["a"] = parseModule(`import* as ns from "good"; import {y} from "bad";`); + +let b = moduleRepo["b"] = parseModule(`import "a";`); +let c = moduleRepo["c"] = parseModule(`import "a";`); + +assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError); +assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError); diff --git a/js/src/jit-test/tests/modules/bug-1420420-3.js b/js/src/jit-test/tests/modules/bug-1420420-3.js new file mode 100644 index 000000000000..5fec62d0caf5 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-3.js @@ -0,0 +1,8 @@ +if (!('stackTest' in this)) + quit(); + +let a = parseModule(`throw new Error`); +a.declarationInstantiation(); +stackTest(function() { + a.evaluation(); +}); diff --git a/js/src/jit-test/tests/modules/bug-1420420-4.js b/js/src/jit-test/tests/modules/bug-1420420-4.js new file mode 100644 index 000000000000..f6ae8f2f6922 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420-4.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); +load(libdir + "dummyModuleResolveHook.js"); + +moduleRepo["a"] = parseModule(`throw undefined`); + +let b = moduleRepo["b"] = parseModule(`import "a";`); +let c = moduleRepo["c"] = parseModule(`import "a";`); + +b.declarationInstantiation(); +c.declarationInstantiation(); + +let count = 0; +try { b.evaluation() } catch (e) { count++; } +try { c.evaluation() } catch (e) { count++; } +assertEq(count, 2); diff --git a/js/src/jit-test/tests/modules/bug-1420420.js b/js/src/jit-test/tests/modules/bug-1420420.js new file mode 100644 index 000000000000..b1eda3aead1f --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1420420.js @@ -0,0 +1,18 @@ +// Test re-instantiation module after failure. + +load(libdir + "asserts.js"); +load(libdir + "dummyModuleResolveHook.js"); + +moduleRepo["good"] = parseModule(`export let x`); + +moduleRepo["y1"] = parseModule(`export let y`); +moduleRepo["y2"] = parseModule(`export let y`); +moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`); + +moduleRepo["a"] = parseModule(`import {x} from "good"; import {y} from "bad";`); + +let b = moduleRepo["b"] = parseModule(`import "a";`); +let c = moduleRepo["c"] = parseModule(`import "a";`); + +assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError); +assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError); diff --git a/js/src/js.msg b/js/src/js.msg index 79f4501f6ee2..8f9d20df4ff3 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -583,7 +583,6 @@ MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found") MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import") MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace") MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found") -MSG_DEF(JSMSG_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure") MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status") // Promise diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2fb4319540c3..fc42197e6e19 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4948,20 +4948,6 @@ JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value, *columnNumber = requested.columnNumber(); } -JS_PUBLIC_API(bool) -JS::IsModuleErrored(JSObject* moduleArg) -{ - AssertHeapIsIdle(); - return moduleArg->as().status() == MODULE_STATUS_ERRORED; -} - -JS_PUBLIC_API(JS::Value) -JS::GetModuleError(JSObject* moduleArg) -{ - AssertHeapIsIdle(); - return moduleArg->as().error(); -} - JS_PUBLIC_API(JSObject*) JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 61bb10c9fb00..c5dc9c3e5bb9 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4267,12 +4267,6 @@ extern JS_PUBLIC_API(void) GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject, uint32_t* lineNumber, uint32_t* columnNumber); -extern JS_PUBLIC_API(bool) -IsModuleErrored(JSObject* moduleRecord); - -extern JS_PUBLIC_API(JS::Value) -GetModuleError(JSObject* moduleRecord); - } /* namespace JS */ extern JS_PUBLIC_API(bool) diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index aa978e9ce15c..d82dd51040ef 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -493,10 +493,8 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam RootedId importNameId(cx, AtomToId(importName)); RootedId localNameId(cx, AtomToId(localName)); RootedModuleEnvironmentObject env(cx, &module->initialEnvironment()); - if (!importBindings().putNew(cx, importNameId, env, localNameId)) { - ReportOutOfMemory(cx); + if (!importBindings().put(cx, importNameId, env, localNameId)) return false; - } return true; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index e1c94b99125c..1fc279c21ddd 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2072,7 +2072,6 @@ intrinsic_CreateNamespaceBinding(JSContext* cx, unsigned argc, Value* vp) // the slot directly. RootedShape shape(cx, environment->lookup(cx, name)); MOZ_ASSERT(shape); - MOZ_ASSERT(environment->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)); environment->setSlot(shape->slot(), args[2]); args.rval().setUndefined(); return true; diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html.ini new file mode 100644 index 000000000000..e39049137658 --- /dev/null +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html.ini @@ -0,0 +1,3 @@ +[instantiation-error-1.html] + [Test that missing exports lead to SyntaxError events on window and load events on script, and that exceptions are remembered] + expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html.ini new file mode 100644 index 000000000000..0bc9a7349e74 --- /dev/null +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html.ini @@ -0,0 +1,3 @@ +[instantiation-error-2.html] + [Test that missing exports lead to SyntaxError events on window and load events on script, and that exceptions are remembered] + expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html.ini index 61dd20cde0d7..e96a3908704f 100644 --- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html.ini +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html.ini @@ -1,3 +1,3 @@ [instantiation-error-3.html] - type: testharness - disabled: true + [Test that unresolvable cycles lead to SyntaxError events on window and load events on script, and that exceptions are remembered] + expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html.ini new file mode 100644 index 000000000000..55ed20ec543e --- /dev/null +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html.ini @@ -0,0 +1,3 @@ +[instantiation-error-4.html] + [Test that loading a graph in which a module is already errored results in that module's error.] + expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html.ini new file mode 100644 index 000000000000..90715158806c --- /dev/null +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html.ini @@ -0,0 +1,3 @@ +[instantiation-error-5.html] + [Test that loading a graph in which a module is already errored results in that module's error.] + expected: FAIL From 74b31155f721a86dd5c0e7247ff7c6663ffb16d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 5 Dec 2017 18:05:51 +0100 Subject: [PATCH 56/78] Bug 1423167: Move most attribute-related methods from nsIContent to Element. r=bz MozReview-Commit-ID: 6WXqNiODttD --- accessible/base/ARIAMap.cpp | 8 +- accessible/base/ARIAMap.h | 10 +- accessible/base/nsAccessibilityService.cpp | 28 +-- accessible/generic/ARIAGridAccessible.cpp | 16 +- accessible/generic/Accessible.cpp | 22 +- accessible/html/HTMLSelectAccessible.cpp | 10 +- accessible/xul/XULSliderAccessible.cpp | 20 +- accessible/xul/XULSliderAccessible.h | 4 +- dom/base/DocumentFragment.h | 27 --- dom/base/Element.h | 113 ++++++++-- dom/base/FragmentOrElement.cpp | 18 +- dom/base/FragmentOrElement.h | 2 - dom/base/nsContentCreatorFunctions.h | 2 +- dom/base/nsContentUtils.cpp | 4 +- dom/base/nsDocument.cpp | 6 +- dom/base/nsGenericDOMDataNode.cpp | 55 +---- dom/base/nsGenericDOMDataNode.h | 16 -- dom/base/nsHTMLContentSerializer.cpp | 47 ++--- dom/base/nsHTMLContentSerializer.h | 5 +- dom/base/nsIContent.h | 100 +-------- dom/base/nsINode.cpp | 13 +- dom/base/nsIdentifierMapEntry.h | 2 +- dom/base/nsObjectLoadingContent.cpp | 14 +- dom/base/nsTreeSanitizer.cpp | 10 +- dom/base/nsTreeSanitizer.h | 6 +- dom/base/nsXHTMLContentSerializer.cpp | 34 +-- dom/base/nsXHTMLContentSerializer.h | 24 +-- dom/base/nsXMLContentSerializer.cpp | 50 ++--- dom/base/nsXMLContentSerializer.h | 16 +- dom/svg/SVGAElement.h | 2 +- dom/svg/SVGStyleElement.h | 2 +- dom/xbl/nsXBLBinding.cpp | 10 +- dom/xbl/nsXBLBinding.h | 6 +- dom/xbl/nsXBLContentSink.cpp | 10 +- dom/xbl/nsXBLContentSink.h | 3 +- dom/xbl/nsXBLPrototypeBinding.cpp | 100 ++++----- dom/xbl/nsXBLPrototypeBinding.h | 28 +-- dom/xbl/nsXBLService.cpp | 32 ++- dom/xbl/nsXBLService.h | 6 +- dom/xml/nsXMLContentSink.cpp | 4 +- dom/xml/nsXMLContentSink.h | 2 +- dom/xml/nsXMLPrettyPrinter.cpp | 14 +- dom/xslt/xpath/txMozillaXPathTreeWalker.cpp | 33 +-- dom/xslt/xslt/txMozillaTextOutput.cpp | 21 +- dom/xslt/xslt/txMozillaTextOutput.h | 8 +- dom/xslt/xslt/txMozillaXMLOutput.cpp | 7 +- dom/xslt/xslt/txMozillaXMLOutput.h | 2 +- dom/xul/XULDocument.cpp | 125 ++++++----- dom/xul/XULDocument.h | 16 +- dom/xul/nsIXULDocument.h | 9 +- dom/xul/nsXULElement.cpp | 2 +- dom/xul/nsXULElement.h | 2 +- dom/xul/templates/nsIXULTemplateBuilder.idl | 3 +- dom/xul/templates/nsXULContentBuilder.cpp | 194 +++++++++--------- dom/xul/templates/nsXULContentUtils.cpp | 8 +- dom/xul/templates/nsXULContentUtils.h | 8 +- dom/xul/templates/nsXULSortService.cpp | 39 ++-- dom/xul/templates/nsXULSortService.h | 6 +- dom/xul/templates/nsXULTemplateBuilder.cpp | 77 +++---- dom/xul/templates/nsXULTemplateBuilder.h | 16 +- .../nsXULTemplateQueryProcessorRDF.cpp | 6 +- .../nsXULTemplateQueryProcessorRDF.h | 10 +- dom/xul/templates/nsXULTreeBuilder.cpp | 16 +- dom/xul/templates/nsXULTreeBuilder.h | 6 +- editor/libeditor/EditorBase.cpp | 4 +- editor/libeditor/HTMLEditor.h | 2 +- editor/libeditor/HTMLStyleEditor.cpp | 18 +- editor/libeditor/TextEditRules.cpp | 9 +- layout/base/nsCSSFrameConstructor.cpp | 2 +- layout/base/nsLayoutUtils.cpp | 22 +- layout/base/nsLayoutUtils.h | 10 +- layout/forms/nsButtonFrameRenderer.cpp | 9 +- layout/forms/nsComboboxControlFrame.h | 2 +- layout/forms/nsDateTimeControlFrame.h | 2 +- layout/forms/nsFileControlFrame.h | 4 +- layout/forms/nsHTMLButtonControlFrame.cpp | 4 +- layout/generic/ScrollbarActivity.cpp | 17 +- layout/generic/ScrollbarActivity.h | 10 +- layout/generic/nsFrameSetFrame.cpp | 6 +- layout/generic/nsGfxScrollFrame.cpp | 51 ++--- layout/generic/nsGfxScrollFrame.h | 20 +- layout/generic/nsVideoFrame.cpp | 6 +- layout/generic/nsVideoFrame.h | 6 +- layout/mathml/nsMathMLmactionFrame.cpp | 3 +- layout/xul/PopupBoxObject.cpp | 15 +- layout/xul/nsDeckFrame.cpp | 2 +- layout/xul/nsIRootBox.h | 12 +- layout/xul/nsMenuFrame.cpp | 41 ++-- layout/xul/nsMenuPopupFrame.cpp | 6 +- layout/xul/nsProgressMeterFrame.cpp | 8 +- layout/xul/nsResizerFrame.cpp | 9 +- layout/xul/nsRootBoxFrame.cpp | 10 +- layout/xul/nsScrollbarButtonFrame.cpp | 11 +- layout/xul/nsScrollbarFrame.cpp | 2 +- layout/xul/nsSliderFrame.cpp | 23 +-- layout/xul/nsSplitterFrame.cpp | 50 +++-- layout/xul/nsXULPopupManager.cpp | 19 +- layout/xul/nsXULPopupManager.h | 4 +- layout/xul/nsXULTooltipListener.cpp | 11 +- layout/xul/tree/nsTreeBodyFrame.cpp | 4 +- layout/xul/tree/nsTreeBodyFrame.h | 4 +- layout/xul/tree/nsTreeColumns.cpp | 7 +- layout/xul/tree/nsTreeContentView.cpp | 12 +- layout/xul/tree/nsTreeContentView.h | 2 +- layout/xul/tree/nsTreeUtils.cpp | 14 +- layout/xul/tree/nsTreeUtils.h | 9 +- parser/html/nsHtml5TreeOperation.cpp | 36 ++-- widget/cocoa/nsMenuBarX.h | 11 +- widget/cocoa/nsMenuBarX.mm | 21 +- widget/cocoa/nsMenuGroupOwnerX.h | 2 +- widget/cocoa/nsMenuGroupOwnerX.mm | 2 +- widget/cocoa/nsMenuItemX.h | 21 +- widget/cocoa/nsMenuItemX.mm | 50 ++--- widget/cocoa/nsMenuX.mm | 22 +- widget/cocoa/nsStandaloneNativeMenu.mm | 2 +- widget/nsINativeMenuService.h | 7 +- xpfe/appshell/nsWebShellWindow.cpp | 2 +- 117 files changed, 1103 insertions(+), 1070 deletions(-) diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp index 27af8a28e7ba..993a598ccf55 100644 --- a/accessible/base/ARIAMap.cpp +++ b/accessible/base/ARIAMap.cpp @@ -1386,7 +1386,7 @@ bool AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue) { while (mAttrIdx < mAttrCount) { - const nsAttrName* attr = mContent->GetAttrNameAt(mAttrIdx); + const nsAttrName* attr = mElement->GetAttrNameAt(mAttrIdx); mAttrIdx++; if (attr->NamespaceEquals(kNameSpaceID_None)) { nsAtom* attrAtom = attr->Atom(); @@ -1399,17 +1399,17 @@ AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue) continue; // No need to handle exposing as obj attribute here if ((attrFlags & ATTR_VALTOKEN) && - !nsAccUtils::HasDefinedARIAToken(mContent, attrAtom)) + !nsAccUtils::HasDefinedARIAToken(mElement, attrAtom)) continue; // only expose token based attributes if they are defined if ((attrFlags & ATTR_BYPASSOBJ_IF_FALSE) && - mContent->AttrValueIs(kNameSpaceID_None, attrAtom, + mElement->AttrValueIs(kNameSpaceID_None, attrAtom, nsGkAtoms::_false, eCaseMatters)) { continue; // only expose token based attribute if value is not 'false'. } nsAutoString value; - if (mContent->GetAttr(kNameSpaceID_None, attrAtom, value)) { + if (mElement->GetAttr(kNameSpaceID_None, attrAtom, value)) { aAttrName.Assign(Substring(attrStr, 5)); aAttrValue.Assign(value); return true; diff --git a/accessible/base/ARIAMap.h b/accessible/base/ARIAMap.h index f5e0b699466c..003e41d21919 100644 --- a/accessible/base/ARIAMap.h +++ b/accessible/base/ARIAMap.h @@ -14,6 +14,7 @@ #include "nsAtom.h" #include "nsIContent.h" +#include "mozilla/dom/Element.h" class nsINode; @@ -288,10 +289,11 @@ bool HasDefinedARIAHidden(nsIContent* aContent); class AttrIterator { public: - explicit AttrIterator(nsIContent* aContent) : - mContent(aContent), mAttrIdx(0) + explicit AttrIterator(nsIContent* aContent) + : mElement(aContent->IsElement() ? aContent->AsElement() : nullptr) + , mAttrIdx(0) { - mAttrCount = mContent->GetAttrCount(); + mAttrCount = mElement ? mElement->GetAttrCount() : 0; } bool Next(nsAString& aAttrName, nsAString& aAttrValue); @@ -301,7 +303,7 @@ private: AttrIterator(const AttrIterator&) = delete; AttrIterator& operator= (const AttrIterator&) = delete; - nsIContent* mContent; + dom::Element* mElement; uint32_t mAttrIdx; uint32_t mAttrCount; }; diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp index 75374e3251f8..530b22f00dbd 100644 --- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -114,20 +114,22 @@ MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument) if (aContent->GetPrimaryFrame()->IsFocusable()) return true; - uint32_t attrCount = aContent->GetAttrCount(); - for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) { - const nsAttrName* attr = aContent->GetAttrNameAt(attrIdx); - if (attr->NamespaceEquals(kNameSpaceID_None)) { - nsAtom* attrAtom = attr->Atom(); - nsDependentAtomString attrStr(attrAtom); - if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-"))) - continue; // not ARIA + if (aContent->IsElement()) { + uint32_t attrCount = aContent->AsElement()->GetAttrCount(); + for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) { + const nsAttrName* attr = aContent->AsElement()->GetAttrNameAt(attrIdx); + if (attr->NamespaceEquals(kNameSpaceID_None)) { + nsAtom* attrAtom = attr->Atom(); + nsDependentAtomString attrStr(attrAtom); + if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-"))) + continue; // not ARIA - // A global state or a property and in case of token defined. - uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom); - if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) || - nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) { - return true; + // A global state or a property and in case of token defined. + uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom); + if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) || + nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) { + return true; + } } } } diff --git a/accessible/generic/ARIAGridAccessible.cpp b/accessible/generic/ARIAGridAccessible.cpp index 48de9bbf07a0..03435f9d7cb2 100644 --- a/accessible/generic/ARIAGridAccessible.cpp +++ b/accessible/generic/ARIAGridAccessible.cpp @@ -465,16 +465,18 @@ ARIAGridAccessible::SetARIASelected(Accessible* aAccessible, if (IsARIARole(nsGkAtoms::table)) return NS_OK; - nsIContent *content = aAccessible->GetContent(); + nsIContent* content = aAccessible->GetContent(); NS_ENSURE_STATE(content); nsresult rv = NS_OK; - if (aIsSelected) - rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, - NS_LITERAL_STRING("true"), aNotify); - else - rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, - NS_LITERAL_STRING("false"), aNotify); + if (content->IsElement()) { + if (aIsSelected) + rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, + NS_LITERAL_STRING("true"), aNotify); + else + rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, + NS_LITERAL_STRING("false"), aNotify); + } NS_ENSURE_SUCCESS(rv, rv); diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index fd7c547d2a27..e6dcd77de28d 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -704,12 +704,14 @@ Accessible::SetSelected(bool aSelect) Accessible* select = nsAccUtils::GetSelectableContainer(this, State()); if (select) { if (select->State() & states::MULTISELECTABLE) { - if (ARIARoleMap()) { + if (mContent->IsElement() && ARIARoleMap()) { if (aSelect) { - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, - NS_LITERAL_STRING("true"), true); + mContent->AsElement()->SetAttr(kNameSpaceID_None, + nsGkAtoms::aria_selected, + NS_LITERAL_STRING("true"), true); } else { - mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true); + mContent->AsElement()->UnsetAttr(kNameSpaceID_None, + nsGkAtoms::aria_selected, true); } } return; @@ -1416,8 +1418,12 @@ Accessible::SetCurValue(double aValue) nsAutoString strValue; strValue.AppendFloat(aValue); + if (!mContent->IsElement()) + return true; + return NS_SUCCEEDED( - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true)); + mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, + strValue, true)); } role @@ -2570,8 +2576,10 @@ Accessible::SetCurrentItem(Accessible* aItem) if (id) { nsAutoString idStr; id->ToString(idStr); - mContent->SetAttr(kNameSpaceID_None, - nsGkAtoms::aria_activedescendant, idStr, true); + mContent->AsElement()->SetAttr(kNameSpaceID_None, + nsGkAtoms::aria_activedescendant, + idStr, + true); } } diff --git a/accessible/html/HTMLSelectAccessible.cpp b/accessible/html/HTMLSelectAccessible.cpp index 4be3077fc177..1bb57f37d6bc 100644 --- a/accessible/html/HTMLSelectAccessible.cpp +++ b/accessible/html/HTMLSelectAccessible.cpp @@ -110,9 +110,13 @@ HTMLSelectListAccessible::CurrentItem() void HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem) { - aItem->GetContent()->SetAttr(kNameSpaceID_None, - nsGkAtoms::selected, NS_LITERAL_STRING("true"), - true); + if (!aItem->GetContent()->IsElement()) + return; + + aItem->GetContent()->AsElement()->SetAttr(kNameSpaceID_None, + nsGkAtoms::selected, + NS_LITERAL_STRING("true"), + true); } bool diff --git a/accessible/xul/XULSliderAccessible.cpp b/accessible/xul/XULSliderAccessible.cpp index eb8032105b0b..ba46dde1fe65 100644 --- a/accessible/xul/XULSliderAccessible.cpp +++ b/accessible/xul/XULSliderAccessible.cpp @@ -13,7 +13,8 @@ #include "mozilla/dom/Element.h" #include "mozilla/FloatingPoint.h" -using namespace mozilla::a11y; +namespace mozilla { +namespace a11y { //////////////////////////////////////////////////////////////////////////////// // XULSliderAccessible @@ -40,7 +41,7 @@ XULSliderAccessible::NativeInteractiveState() const if (NativelyUnavailable()) return states::UNAVAILABLE; - nsIContent* sliderElm = GetSliderElement(); + dom::Element* sliderElm = GetSliderElement(); if (sliderElm) { nsIFrame* frame = sliderElm->GetPrimaryFrame(); if (frame && frame->IsFocusable()) @@ -83,7 +84,7 @@ XULSliderAccessible::DoAction(uint8_t aIndex) if (aIndex != 0) return false; - nsIContent* sliderElm = GetSliderElement(); + dom::Element* sliderElm = GetSliderElement(); if (sliderElm) DoCommand(sliderElm); @@ -129,17 +130,17 @@ XULSliderAccessible::SetCurValue(double aValue) // Utils -nsIContent* +dom::Element* XULSliderAccessible::GetSliderElement() const { - if (!mSliderNode) { + if (!mSliderElement) { // XXX: we depend on anonymous content. - mSliderNode = mContent->OwnerDoc()-> + mSliderElement = mContent->OwnerDoc()-> GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, NS_LITERAL_STRING("slider")); } - return mSliderNode; + return mSliderElement; } nsresult @@ -163,8 +164,7 @@ XULSliderAccessible::SetSliderAttr(nsAtom* aName, const nsAString& aValue) if (IsDefunct()) return NS_ERROR_FAILURE; - nsIContent* sliderElm = GetSliderElement(); - if (sliderElm) + if (dom::Element* sliderElm = GetSliderElement()) sliderElm->SetAttr(kNameSpaceID_None, aName, aValue, true); return NS_OK; @@ -212,3 +212,5 @@ XULThumbAccessible::NativeRole() return roles::INDICATOR; } +} +} diff --git a/accessible/xul/XULSliderAccessible.h b/accessible/xul/XULSliderAccessible.h index 2559998f53a1..b00e3eaf8279 100644 --- a/accessible/xul/XULSliderAccessible.h +++ b/accessible/xul/XULSliderAccessible.h @@ -43,7 +43,7 @@ protected: /** * Return anonymous slider element. */ - nsIContent* GetSliderElement() const; + dom::Element* GetSliderElement() const; nsresult GetSliderAttr(nsAtom *aName, nsAString& aValue) const; nsresult SetSliderAttr(nsAtom *aName, const nsAString& aValue); @@ -52,7 +52,7 @@ protected: bool SetSliderAttr(nsAtom *aName, double aValue); private: - mutable nsCOMPtr mSliderNode; + mutable RefPtr mSliderElement; }; diff --git a/dom/base/DocumentFragment.h b/dom/base/DocumentFragment.h index 541c1c3b42cf..18b609407199 100644 --- a/dom/base/DocumentFragment.h +++ b/dom/base/DocumentFragment.h @@ -69,33 +69,6 @@ public: virtual JSObject* WrapNode(JSContext *aCx, JS::Handle aGivenProto) override; - // nsIContent - using nsIContent::SetAttr; - virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, - nsAtom* aPrefix, const nsAString& aValue, - nsIPrincipal* aSubjectPrincipal, - bool aNotify) override - { - return NS_OK; - } - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute, - bool aNotify) override - { - return NS_OK; - } - virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const override - { - return nullptr; - } - virtual BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const override - { - return BorrowedAttrInfo(nullptr, nullptr); - } - virtual uint32_t GetAttrCount() const override - { - return 0; - } - virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual nsIDOMNode* AsDOMNode() override { return this; } diff --git a/dom/base/Element.h b/dom/base/Element.h index 9ce3f441addf..fd933840faa5 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -430,6 +430,11 @@ public: virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute, int32_t aModType) const; + NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) + { + return NS_OK; + } + inline Directionality GetDirectionality() const { if (HasFlag(NODE_HAS_DIRECTION_RTL)) { return eDir_RTL; @@ -697,8 +702,6 @@ public: already_AddRefed GetExistingAttrNameFromQName(const nsAString& aStr) const; - using nsIContent::SetAttr; - /** * Helper for SetAttr/SetParsedAttr. This method will return true if aNotify * is true or there are mutation listeners that must be triggered, the @@ -758,9 +761,6 @@ public: */ nsresult SetSingleClassFromParser(nsAtom* aSingleClassName); - virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, - const nsAString& aValue, nsIPrincipal* aSubjectPrincipal, - bool aNotify) override; // aParsedValue receives the old value of the attribute. That's useful if // either the input or output value of aParsedValue is StoresOwnData. nsresult SetParsedAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, @@ -777,19 +777,97 @@ public: inline bool AttrValueIs(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aValue, nsCaseTreatment aCaseSensitive) const; - virtual int32_t FindAttrValueIn(int32_t aNameSpaceID, - nsAtom* aName, - AttrValuesArray* aValues, - nsCaseTreatment aCaseSensitive) const override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute, - bool aNotify) override; + int32_t FindAttrValueIn(int32_t aNameSpaceID, + nsAtom* aName, + AttrValuesArray* aValues, + nsCaseTreatment aCaseSensitive) const override; - virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const final override + /** + * Set attribute values. All attribute values are assumed to have a + * canonical string representation that can be used for these + * methods. The SetAttr method is assumed to perform a translation + * of the canonical form into the underlying content specific + * form. + * + * @param aNameSpaceID the namespace of the attribute + * @param aName the name of the attribute + * @param aValue the value to set + * @param aNotify specifies how whether or not the document should be + * notified of the attribute change. + */ + nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, + const nsAString& aValue, bool aNotify) + { + return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); + } + nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, + const nsAString& aValue, bool aNotify) + { + return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify); + } + nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue, + nsIPrincipal* aTriggeringPrincipal, bool aNotify) + { + return SetAttr(aNameSpaceID, aName, nullptr, aValue, aTriggeringPrincipal, aNotify); + } + + /** + * Set attribute values. All attribute values are assumed to have a + * canonical String representation that can be used for these + * methods. The SetAttr method is assumed to perform a translation + * of the canonical form into the underlying content specific + * form. + * + * @param aNameSpaceID the namespace of the attribute + * @param aName the name of the attribute + * @param aPrefix the prefix of the attribute + * @param aValue the value to set + * @param aMaybeScriptedPrincipal the principal of the scripted caller responsible + * for setting the attribute, or null if no scripted caller can be + * determined. A null value here does not guarantee that there is no + * scripted caller, but a non-null value does guarantee that a scripted + * caller with the given principal is directly responsible for the + * attribute change. + * @param aNotify specifies how whether or not the document should be + * notified of the attribute change. + */ + virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, + nsAtom* aPrefix, const nsAString& aValue, + nsIPrincipal* aMaybeScriptedPrincipal, + bool aNotify); + + /** + * Remove an attribute so that it is no longer explicitly specified. + * + * @param aNameSpaceID the namespace id of the attribute + * @param aAttr the name of the attribute to unset + * @param aNotify specifies whether or not the document should be + * notified of the attribute change + */ + virtual nsresult UnsetAttr(int32_t aNameSpaceID, + nsAtom* aAttribute, + bool aNotify); + + /** + * Get the namespace / name / prefix of a given attribute. + * + * @param aIndex the index of the attribute name + * @returns The name at the given index, or null if the index is + * out-of-bounds. + * @note The document returned by NodeInfo()->GetDocument() (if one is + * present) is *not* necessarily the owner document of the element. + * @note The pointer returned by this function is only valid until the + * next call of either GetAttrNameAt or SetAttr on the element. + */ + const nsAttrName* GetAttrNameAt(uint32_t aIndex) const { return mAttrsAndChildren.GetSafeAttrNameAt(aIndex); } - virtual BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const final override + /** + * Gets the attribute info (name and value) for this element at a given index. + */ + BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const { if (aIndex >= mAttrsAndChildren.AttrCount()) { return BorrowedAttrInfo(nullptr, nullptr); @@ -798,7 +876,12 @@ public: return mAttrsAndChildren.AttrInfoAt(aIndex); } - virtual uint32_t GetAttrCount() const final override + /** + * Get the number of all specified attributes. + * + * @return the number of attributes + */ + uint32_t GetAttrCount() const { return mAttrsAndChildren.AttrCount(); } @@ -1466,7 +1549,7 @@ public: void SetAttr(nsAtom* aAttr, const nsAString& aValue, nsIPrincipal& aTriggeringPrincipal, ErrorResult& aError) { - aError = nsIContent::SetAttr(kNameSpaceID_None, aAttr, aValue, &aTriggeringPrincipal, true); + aError = SetAttr(kNameSpaceID_None, aAttr, aValue, &aTriggeringPrincipal, true); } /** diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 09a8255fc8fc..eb0d690613f7 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -391,7 +391,8 @@ nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix, // declaration that declares aNamespacePrefix. const nsIContent* content = this; do { - if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) + if (content->IsElement() && + content->AsElement()->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) return NS_OK; } while ((content = content->GetParent())); return NS_ERROR_FAILURE; @@ -401,11 +402,14 @@ nsAtom* nsIContent::GetLang() const { for (const auto* content = this; content; content = content->GetParent()) { - if (!content->GetAttrCount() || !content->IsElement()) { + if (!content->IsElement()) { continue; } auto* element = content->AsElement(); + if (!element->GetAttrCount()) { + continue; + } // xml:lang has precedence over lang on HTML elements (see // XHTML1 section C.7). @@ -1179,12 +1183,6 @@ nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) return false; } -NS_IMETHODIMP -FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) -{ - return NS_OK; -} - bool FragmentOrElement::IsLink(nsIURI** aURI) const { @@ -2241,8 +2239,8 @@ FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst, const nsAttrValue* value = mAttrsAndChildren.AttrAt(i); nsAutoString valStr; value->ToString(valStr); - rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(), - name->GetPrefix(), valStr, false); + rv = aDst->AsElement()->SetAttr(name->NamespaceID(), name->LocalName(), + name->GetPrefix(), valStr, false); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index a5b0d6c98e0c..0571313a83ac 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -165,8 +165,6 @@ public: virtual void DestroyContent() override; virtual void SaveSubtreeState() override; - NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; - nsIHTMLCollection* Children(); uint32_t ChildElementCount() { diff --git a/dom/base/nsContentCreatorFunctions.h b/dom/base/nsContentCreatorFunctions.h index 61f08fcfd714..0ec77a92e51c 100644 --- a/dom/base/nsContentCreatorFunctions.h +++ b/dom/base/nsContentCreatorFunctions.h @@ -63,7 +63,7 @@ NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::FromParser aFromParser); void -NS_TrustedNewXULElement(nsIContent** aResult, +NS_TrustedNewXULElement(mozilla::dom::Element** aResult, already_AddRefed&& aNodeInfo); #endif diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index c98029620a11..13153eaa3eed 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5058,13 +5058,13 @@ nsContentUtils::CreateContextualFragment(nsINode* aContextNode, tagName = content->NodeInfo()->QualifiedName(); // see if we need to add xmlns declarations - uint32_t count = content->GetAttrCount(); + uint32_t count = content->AsElement()->GetAttrCount(); bool setDefaultNamespace = false; if (count > 0) { uint32_t index; for (index = 0; index < count; index++) { - const BorrowedAttrInfo info = content->GetAttrInfoAt(index); + const BorrowedAttrInfo info = content->AsElement()->GetAttrInfoAt(index); const nsAttrName* name = info.mName; if (name->NamespaceEquals(kNameSpaceID_XMLNS)) { info.mValue->ToString(uriStr); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index b23d02a774e4..c8a6905e3ef4 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -421,10 +421,10 @@ nsIdentifierMapEntry::GetImageIdElement() } void -nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray* aElements) +nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray* aElements) { - for (size_t i = 0; i < mIdContentList.Length(); ++i) { - aElements->AppendObject(mIdContentList[i]); + for (Element* element : mIdContentList) { + aElements->AppendObject(element); } } diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index a170eaf23fd4..8450c7a1152b 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -637,40 +637,6 @@ nsGenericDOMDataNode::GetChildren(uint32_t aFilter) return nullptr; } -nsresult -nsGenericDOMDataNode::SetAttr(int32_t aNameSpaceID, nsAtom* aAttr, - nsAtom* aPrefix, const nsAString& aValue, - nsIPrincipal* aContentPrincipal, - bool aNotify) -{ - return NS_OK; -} - -nsresult -nsGenericDOMDataNode::UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttr, - bool aNotify) -{ - return NS_OK; -} - -const nsAttrName* -nsGenericDOMDataNode::GetAttrNameAt(uint32_t aIndex) const -{ - return nullptr; -} - -BorrowedAttrInfo -nsGenericDOMDataNode::GetAttrInfoAt(uint32_t aIndex) const -{ - return BorrowedAttrInfo(nullptr, nullptr); -} - -uint32_t -nsGenericDOMDataNode::GetAttrCount() const -{ - return 0; -} - uint32_t nsGenericDOMDataNode::GetChildCount() const { @@ -683,6 +649,7 @@ nsGenericDOMDataNode::GetChildAt(uint32_t aIndex) const return nullptr; } + int32_t nsGenericDOMDataNode::IndexOf(const nsINode* aPossibleChild) const { @@ -1123,26 +1090,6 @@ nsGenericDOMDataNode::GetCurrentValueAtom() return NS_Atomize(val); } -NS_IMETHODIMP -nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker) -{ - return NS_OK; -} - -NS_IMETHODIMP_(bool) -nsGenericDOMDataNode::IsAttributeMapped(const nsAtom* aAttribute) const -{ - return false; -} - -nsChangeHint -nsGenericDOMDataNode::GetAttributeChangeHint(const nsAtom* aAttribute, - int32_t aModType) const -{ - NS_NOTREACHED("Shouldn't be calling this!"); - return nsChangeHint(0); -} - void nsGenericDOMDataNode::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index ac4af74735cd..cb5b1bbc3298 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -133,17 +133,6 @@ public: virtual already_AddRefed GetChildren(uint32_t aFilter) override; - - using nsIContent::SetAttr; - virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aAttribute, - nsAtom* aPrefix, const nsAString& aValue, - nsIPrincipal* aSubjectPrincipal, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute, - bool aNotify) override; - virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const override; - virtual mozilla::dom::BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const override; - virtual uint32_t GetAttrCount() const override; virtual const nsTextFragment *GetText() override; virtual uint32_t TextLength() const override; virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength, @@ -184,11 +173,6 @@ public: virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual bool IsLink(nsIURI** aURI) const override; - NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; - NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const; - virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute, - int32_t aModType) const; - virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override { diff --git a/dom/base/nsHTMLContentSerializer.cpp b/dom/base/nsHTMLContentSerializer.cpp index 290852c67bb5..aa89e9abe0ba 100644 --- a/dom/base/nsHTMLContentSerializer.cpp +++ b/dom/base/nsHTMLContentSerializer.cpp @@ -64,15 +64,15 @@ nsHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument, } bool -nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, +nsHTMLContentSerializer::SerializeHTMLAttributes(Element* aElement, + Element* aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsAtom* aTagName, int32_t aNamespace, nsAString& aStr) { - int32_t count = aContent->GetAttrCount(); + int32_t count = aElement->GetAttrCount(); if (!count) return true; @@ -81,7 +81,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, NS_NAMED_LITERAL_STRING(_mozStr, "_moz"); for (int32_t index = 0; index < count; index++) { - const nsAttrName* name = aContent->GetAttrNameAt(index); + const nsAttrName* name = aElement->GetAttrNameAt(index); int32_t namespaceID = name->NamespaceID(); nsAtom* attrName = name->LocalName(); @@ -91,7 +91,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) { continue; } - aContent->GetAttr(namespaceID, attrName, valueStr); + aElement->GetAttr(namespaceID, attrName, valueStr); // // Filter out special case of
or
, @@ -109,7 +109,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, // This is handled separately in SerializeLIValueAttribute() continue; } - bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); + bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr); if (((attrName == nsGkAtoms::href && (namespaceID == kNameSpaceID_None || @@ -120,7 +120,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, // Would be nice to handle OBJECT tags, but that gets more complicated // since we have to search the tag list for CODEBASE as well. For now, // just leave them relative. - nsCOMPtr uri = aContent->GetBaseURI(); + nsCOMPtr uri = aElement->GetBaseURI(); if (uri) { nsAutoString absURI; rv = NS_MakeAbsoluteURI(absURI, valueStr, uri); @@ -137,7 +137,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent, // If we're serializing a , // use the proper value, rather than what's in the document. nsAutoString header; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); if (header.LowerCaseEqualsLiteral("content-type")) { valueStr = NS_LITERAL_STRING("text/html; charset=") + NS_ConvertASCIItoUTF16(mCharset); @@ -173,22 +173,20 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement, { NS_ENSURE_ARG(aElement); - nsIContent* content = aElement; - bool forceFormat = false; nsresult rv = NS_OK; - if (!CheckElementStart(content, forceFormat, aStr, rv)) { + if (!CheckElementStart(aElement, forceFormat, aStr, rv)) { // When we go to AppendElementEnd for this element, we're going to // MaybeLeaveFromPreContent(). So make sure to MaybeEnterInPreContent() // now, so our PreLevel() doesn't get confused. - MaybeEnterInPreContent(content); + MaybeEnterInPreContent(aElement); return rv; } NS_ENSURE_SUCCESS(rv, rv); - nsAtom *name = content->NodeInfo()->NameAtom(); - int32_t ns = content->GetNameSpaceID(); + nsAtom *name = aElement->NodeInfo()->NameAtom(); + int32_t ns = aElement->GetNameSpaceID(); bool lineBreakBeforeOpen = LineBreakBeforeOpen(ns, name); @@ -224,7 +222,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement, NS_ENSURE_TRUE(AppendToString(nsDependentAtomString(name), aStr), NS_ERROR_OUT_OF_MEMORY); - MaybeEnterInPreContent(content); + MaybeEnterInPreContent(aElement); // for block elements, we increase the indentation if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) @@ -264,7 +262,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement, // Even LI passed above have to go through this // for serializing attributes other than "value". nsAutoString dummyPrefix; - NS_ENSURE_TRUE(SerializeHTMLAttributes(content, + NS_ENSURE_TRUE(SerializeHTMLAttributes(aElement, aOriginalElement, dummyPrefix, EmptyString(), @@ -287,21 +285,18 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement, NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); } - NS_ENSURE_TRUE(AfterElementStart(content, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); + NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); return NS_OK; } NS_IMETHODIMP -nsHTMLContentSerializer::AppendElementEnd(Element* aElement, - nsAString& aStr) +nsHTMLContentSerializer::AppendElementEnd(Element* aElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); - nsIContent* content = aElement; - - nsAtom *name = content->NodeInfo()->NameAtom(); - int32_t ns = content->GetNameSpaceID(); + nsAtom *name = aElement->NodeInfo()->NameAtom(); + int32_t ns = aElement->GetNameSpaceID(); if (ns == kNameSpaceID_XHTML && (name == nsGkAtoms::script || @@ -312,7 +307,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement, } bool forceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) && - content->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); + aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { DecrIndentation(name); @@ -344,7 +339,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement, if (!isContainer) { // Keep this in sync with the cleanup at the end of this method. MOZ_ASSERT(name != nsGkAtoms::body); - MaybeLeaveFromPreContent(content); + MaybeLeaveFromPreContent(aElement); return NS_OK; } } @@ -376,7 +371,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement, NS_ENSURE_TRUE(AppendToString(kGreaterThan, aStr), NS_ERROR_OUT_OF_MEMORY); // Keep this cleanup in sync with the IsContainer() early return above. - MaybeLeaveFromPreContent(content); + MaybeLeaveFromPreContent(aElement); if ((mDoFormat || forceFormat)&& !mDoRaw && !PreLevel() && LineBreakAfterClose(ns, name)) { diff --git a/dom/base/nsHTMLContentSerializer.h b/dom/base/nsHTMLContentSerializer.h index 8c7f42578e08..9fb3f51f8e33 100644 --- a/dom/base/nsHTMLContentSerializer.h +++ b/dom/base/nsHTMLContentSerializer.h @@ -17,7 +17,6 @@ #include "nsXHTMLContentSerializer.h" #include "nsString.h" -class nsIContent; class nsAtom; class nsHTMLContentSerializer final : public nsXHTMLContentSerializer { @@ -37,8 +36,8 @@ class nsHTMLContentSerializer final : public nsXHTMLContentSerializer { protected: MOZ_MUST_USE - virtual bool SerializeHTMLAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, + virtual bool SerializeHTMLAttributes(mozilla::dom::Element* aContent, + mozilla::dom::Element* aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsAtom* aTagName, diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index 523f3d8db48e..ec6861812e78 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -15,7 +15,6 @@ // Forward declarations class nsAtom; class nsIURI; -class nsRuleWalker; class nsAttrValue; class nsAttrName; class nsTextFragment; @@ -356,60 +355,6 @@ public: mNodeInfo->NameAtom() == nsGkAtoms::mozgeneratedcontentafter; } - /** - * Set attribute values. All attribute values are assumed to have a - * canonical string representation that can be used for these - * methods. The SetAttr method is assumed to perform a translation - * of the canonical form into the underlying content specific - * form. - * - * @param aNameSpaceID the namespace of the attribute - * @param aName the name of the attribute - * @param aValue the value to set - * @param aNotify specifies how whether or not the document should be - * notified of the attribute change. - */ - nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify); - } - nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue, - nsIPrincipal* aTriggeringPrincipal, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aTriggeringPrincipal, aNotify); - } - - /** - * Set attribute values. All attribute values are assumed to have a - * canonical String representation that can be used for these - * methods. The SetAttr method is assumed to perform a translation - * of the canonical form into the underlying content specific - * form. - * - * @param aNameSpaceID the namespace of the attribute - * @param aName the name of the attribute - * @param aPrefix the prefix of the attribute - * @param aValue the value to set - * @param aMaybeScriptedPrincipal the principal of the scripted caller responsible - * for setting the attribute, or null if no scripted caller can be - * determined. A null value here does not guarantee that there is no - * scripted caller, but a non-null value does guarantee that a scripted - * caller with the given principal is directly responsible for the - * attribute change. - * @param aNotify specifies how whether or not the document should be - * notified of the attribute change. - */ - virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, - nsAtom* aPrefix, const nsAString& aValue, - nsIPrincipal* aMaybeScriptedPrincipal, - bool aNotify) = 0; - /** * Get the current value of the attribute. This returns a form that is * suitable for passing back into SetAttr. @@ -419,6 +364,8 @@ public: * @param aResult the value (may legitimately be the empty string) [OUT] * @returns true if the attribute was set (even when set to empty string) * false when not set. + * + * FIXME(emilio): Move to Element. */ bool GetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAString& aResult) const; @@ -492,43 +439,6 @@ public: return ATTR_MISSING; } - /** - * Remove an attribute so that it is no longer explicitly specified. - * - * @param aNameSpaceID the namespace id of the attribute - * @param aAttr the name of the attribute to unset - * @param aNotify specifies whether or not the document should be - * notified of the attribute change - */ - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttr, - bool aNotify) = 0; - - - /** - * Get the namespace / name / prefix of a given attribute. - * - * @param aIndex the index of the attribute name - * @returns The name at the given index, or null if the index is - * out-of-bounds. - * @note The document returned by NodeInfo()->GetDocument() (if one is - * present) is *not* necessarily the owner document of the element. - * @note The pointer returned by this function is only valid until the - * next call of either GetAttrNameAt or SetAttr on the element. - */ - virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const = 0; - - /** - * Gets the attribute info (name and value) for this content at a given index. - */ - virtual mozilla::dom::BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const = 0; - - /** - * Get the number of all specified attributes. - * - * @return the number of attributes - */ - virtual uint32_t GetAttrCount() const = 0; - /** * Get direct access (but read only) to the text in the text content. * NOTE: For elements this is *not* the concatenation of all text children, @@ -925,12 +835,6 @@ public: return nullptr; } - /** - * Walk aRuleWalker over the content style rules (presentational - * hint rules) for this content node. - */ - NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) = 0; - /** * Should be called when the node can become editable or when it can stop * being editable (for example when its contentEditable attribute changes, diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 2ec1564bc9d7..4517a3af3a2e 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -778,17 +778,22 @@ nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) // return the prefix (i.e. the attribute localName). for (nsIContent* content = element; content; content = content->GetParent()) { - uint32_t attrCount = content->GetAttrCount(); + if (!content->IsElement()) { + continue; + } + + Element* element = content->AsElement(); + uint32_t attrCount = element->GetAttrCount(); for (uint32_t i = 0; i < attrCount; ++i) { - const nsAttrName* name = content->GetAttrNameAt(i); + const nsAttrName* name = element->GetAttrNameAt(i); if (name->NamespaceEquals(kNameSpaceID_XMLNS) && - content->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(), + element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(), aNamespaceURI, eCaseMatters)) { // If the localName is "xmlns", the prefix we output should be // null. - nsAtom *localName = name->LocalName(); + nsAtom* localName = name->LocalName(); if (localName != nsGkAtoms::xmlns) { localName->ToString(aPrefix); diff --git a/dom/base/nsIdentifierMapEntry.h b/dom/base/nsIdentifierMapEntry.h index fca103e415ec..962b32474627 100644 --- a/dom/base/nsIdentifierMapEntry.h +++ b/dom/base/nsIdentifierMapEntry.h @@ -152,7 +152,7 @@ public: /** * Append all the elements with this id to aElements */ - void AppendAllIdContent(nsCOMArray* aElements); + void AppendAllIdContent(nsCOMArray* aElements); /** * This can fire ID change callbacks. * @return true if the content could be added, false if we failed due diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 1048c2f1a214..cbf39e8ab972 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -924,14 +924,14 @@ nsObjectLoadingContent::BuildParametersArray() return NS_OK; } - nsCOMPtr content = + nsCOMPtr element = do_QueryInterface(static_cast(this)); - for (uint32_t i = 0; i != content->GetAttrCount(); i += 1) { + for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) { MozPluginParameter param; - const nsAttrName* attrName = content->GetAttrNameAt(i); + const nsAttrName* attrName = element->GetAttrNameAt(i); nsAtom* atom = attrName->LocalName(); - content->GetAttr(attrName->NamespaceID(), atom, param.mValue); + element->GetAttr(attrName->NamespaceID(), atom, param.mValue); atom->ToString(param.mName); mCachedAttributes.AppendElement(param); } @@ -958,10 +958,10 @@ nsObjectLoadingContent::BuildParametersArray() // Nav 4.x would simply replace the "data" with "src". Because some plugins correctly // look for "data", lets instead copy the "data" attribute and add another entry // to the bottom of the array if there isn't already a "src" specified. - if (content->IsHTMLElement(nsGkAtoms::object) && - !content->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { + if (element->IsHTMLElement(nsGkAtoms::object) && + !element->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { MozPluginParameter param; - content->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue); + element->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue); if (!param.mValue.IsEmpty()) { param.mName = NS_LITERAL_STRING("SRC"); mCachedAttributes.AppendElement(param); diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index 36f719caec67..a631a5d7dbe4 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -1401,10 +1401,12 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot) int32_t ns = nodeInfo->NamespaceID(); if (MustPrune(ns, localName, elt)) { - RemoveAllAttributes(node); + RemoveAllAttributes(elt); nsIContent* descendant = node; while ((descendant = descendant->GetNextNode(node))) { - RemoveAllAttributes(descendant); + if (descendant->IsElement()) { + RemoveAllAttributes(descendant->AsElement()); + } } nsIContent* next = node->GetNextNonChildNode(aRoot); node->RemoveFromParent(); @@ -1450,7 +1452,7 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot) continue; } if (MustFlatten(ns, localName)) { - RemoveAllAttributes(node); + RemoveAllAttributes(elt); nsCOMPtr next = node->GetNextNode(aRoot); nsCOMPtr parent = node->GetParent(); nsCOMPtr child; // Must keep the child alive during move @@ -1505,7 +1507,7 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot) } void -nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement) +nsTreeSanitizer::RemoveAllAttributes(Element* aElement) { const nsAttrName* attrName; while ((attrName = aElement->GetAttrNameAt(0))) { diff --git a/dom/base/nsTreeSanitizer.h b/dom/base/nsTreeSanitizer.h index 2b1392a0c33c..011e5b0556c7 100644 --- a/dom/base/nsTreeSanitizer.h +++ b/dom/base/nsTreeSanitizer.h @@ -146,8 +146,8 @@ class MOZ_STACK_CLASS nsTreeSanitizer { * @return true if the attribute was removed and false otherwise */ bool SanitizeURL(mozilla::dom::Element* aElement, - int32_t aNamespace, - nsAtom* aLocalName); + int32_t aNamespace, + nsAtom* aLocalName); /** * Checks a style rule for the presence of the 'binding' CSS property and @@ -178,7 +178,7 @@ class MOZ_STACK_CLASS nsTreeSanitizer { /** * Removes all attributes from an element node. */ - void RemoveAllAttributes(nsIContent* aElement); + void RemoveAllAttributes(mozilla::dom::Element* aElement); /** * The whitelist of HTML elements. diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index bcbfa6b92674..f2fa44149d8b 100644 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -156,8 +156,8 @@ nsXHTMLContentSerializer::AppendText(nsIContent* aText, } bool -nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, +nsXHTMLContentSerializer::SerializeAttributes(Element* aElement, + Element* aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsAtom* aTagName, @@ -171,7 +171,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, nsAutoString xmlnsStr; xmlnsStr.AssignLiteral(kXMLNS); - int32_t contentNamespaceID = aContent->GetNameSpaceID(); + int32_t contentNamespaceID = aElement->GetNameSpaceID(); // this method is not called by nsHTMLContentSerializer // so we don't have to check HTML element, just XHTML @@ -185,7 +185,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, // Store its start attribute value in olState->startVal. nsAutoString start; int32_t startAttrVal = 0; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start); + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start); if (!start.IsEmpty()) { nsresult rv = NS_OK; startAttrVal = start.ToInteger(&rv); @@ -204,7 +204,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement); if (mIsFirstChildOfOL) { // If OL is parent of this LI, serialize attributes in different manner. - NS_ENSURE_TRUE(SerializeLIValueAttribute(aContent, aStr), false); + NS_ENSURE_TRUE(SerializeLIValueAttribute(aElement, aStr), false); } } } @@ -228,7 +228,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, NS_NAMED_LITERAL_STRING(_mozStr, "_moz"); - count = aContent->GetAttrCount(); + count = aElement->GetAttrCount(); // Now serialize each of the attributes // XXX Unfortunately we need a namespace manager to get @@ -239,7 +239,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, continue; } - mozilla::dom::BorrowedAttrInfo info = aContent->GetAttrInfoAt(index); + mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index); const nsAttrName* name = info.mName; int32_t namespaceID = name->NamespaceID(); @@ -287,7 +287,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, continue; } - isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); + isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr); if (namespaceID == kNameSpaceID_None && ((attrName == nsGkAtoms::href) || @@ -298,7 +298,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, // but that gets more complicated since we have to // search the tag list for CODEBASE as well. // For now, just leave them relative. - nsCOMPtr uri = aContent->GetBaseURI(); + nsCOMPtr uri = aElement->GetBaseURI(); if (uri) { nsAutoString absURI; rv = NS_MakeAbsoluteURI(absURI, valueStr, uri); @@ -314,7 +314,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, // If we're serializing a , // use the proper value, rather than what's in the document. nsAutoString header; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); if (header.LowerCaseEqualsLiteral("content-type")) { valueStr = NS_LITERAL_STRING("text/html; charset=") + NS_ConvertASCIItoUTF16(mCharset); @@ -327,7 +327,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, } } else { - isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); + isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr); } NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false); @@ -414,8 +414,8 @@ nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument, } bool -nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent, - bool & aForceFormat, +nsXHTMLContentSerializer::CheckElementStart(Element* aElement, + bool& aForceFormat, nsAString& aStr, nsresult& aResult) { @@ -425,16 +425,16 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent, // indicate that this element should be pretty printed // even if we're not in pretty printing mode aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) && - aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); + aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); - if (aContent->IsHTMLElement(nsGkAtoms::br) && + if (aElement->IsHTMLElement(nsGkAtoms::br) && (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) && PreLevel() > 0) { aResult = AppendNewLineToString(aStr) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; return false; } - if (aContent->IsHTMLElement(nsGkAtoms::body)) { + if (aElement->IsHTMLElement(nsGkAtoms::body)) { ++mInBody; } @@ -834,7 +834,7 @@ nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement) } bool -nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) { +nsXHTMLContentSerializer::HasNoChildren(nsIContent* aContent) { for (nsIContent* child = aContent->GetFirstChild(); child; diff --git a/dom/base/nsXHTMLContentSerializer.h b/dom/base/nsXHTMLContentSerializer.h index a0033c8d2ab5..6b4fee0bd03c 100644 --- a/dom/base/nsXHTMLContentSerializer.h +++ b/dom/base/nsXHTMLContentSerializer.h @@ -48,10 +48,10 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer { protected: - virtual bool CheckElementStart(nsIContent * aContent, - bool & aForceFormat, - nsAString& aStr, - nsresult& aResult) override; + virtual bool CheckElementStart(mozilla::dom::Element* aElement, + bool& aForceFormat, + nsAString& aStr, + nsresult& aResult) override; MOZ_MUST_USE virtual bool AfterElementStart(nsIContent* aContent, @@ -77,14 +77,14 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer { virtual void MaybeLeaveFromPreContent(nsIContent* aNode) override; MOZ_MUST_USE - virtual bool SerializeAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, - nsAString& aTagPrefix, - const nsAString& aTagNamespaceURI, - nsAtom* aTagName, - nsAString& aStr, - uint32_t aSkipAttr, - bool aAddNSAttr) override; + virtual bool SerializeAttributes(mozilla::dom::Element* aContent, + mozilla::dom::Element* aOriginalElement, + nsAString& aTagPrefix, + const nsAString& aTagNamespaceURI, + nsAtom* aTagName, + nsAString& aStr, + uint32_t aSkipAttr, + bool aAddNSAttr) override; bool IsFirstChildOfOL(nsIContent* aElement); diff --git a/dom/base/nsXMLContentSerializer.cpp b/dom/base/nsXMLContentSerializer.cpp index f3f93edb6d0c..f1c0c739382e 100644 --- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -712,20 +712,20 @@ nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix, } uint32_t -nsXMLContentSerializer::ScanNamespaceDeclarations(nsIContent* aContent, - nsIContent *aOriginalElement, +nsXMLContentSerializer::ScanNamespaceDeclarations(Element* aElement, + Element* aOriginalElement, const nsAString& aTagNamespaceURI) { uint32_t index, count; nsAutoString uriStr, valueStr; - count = aContent->GetAttrCount(); + count = aElement->GetAttrCount(); // First scan for namespace declarations, pushing each on the stack uint32_t skipAttr = count; for (index = 0; index < count; index++) { - const BorrowedAttrInfo info = aContent->GetAttrInfoAt(index); + const BorrowedAttrInfo info = aElement->GetAttrInfoAt(index); const nsAttrName* name = info.mName; int32_t namespaceID = name->NamespaceID(); @@ -799,8 +799,8 @@ nsXMLContentSerializer::IsJavaScript(nsIContent * aContent, nsAtom* aAttrNameAto bool -nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, +nsXMLContentSerializer::SerializeAttributes(Element* aElement, + Element* aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsAtom* aTagName, @@ -828,7 +828,7 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent, PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement); } - count = aContent->GetAttrCount(); + count = aElement->GetAttrCount(); // Now serialize each of the attributes // XXX Unfortunately we need a namespace manager to get @@ -838,7 +838,7 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent, continue; } - const nsAttrName* name = aContent->GetAttrNameAt(index); + const nsAttrName* name = aElement->GetAttrNameAt(index); int32_t namespaceID = name->NamespaceID(); nsAtom* attrName = name->LocalName(); nsAtom* attrPrefix = name->GetPrefix(); @@ -863,10 +863,10 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent, addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true); } - aContent->GetAttr(namespaceID, attrName, valueStr); + aElement->GetAttr(namespaceID, attrName, valueStr); nsDependentAtomString nameStr(attrName); - bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); + bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr); NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false); @@ -888,15 +888,13 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement, { NS_ENSURE_ARG(aElement); - nsIContent* content = aElement; - bool forceFormat = false; nsresult rv = NS_OK; - if (!CheckElementStart(content, forceFormat, aStr, rv)) { + if (!CheckElementStart(aElement, forceFormat, aStr, rv)) { // When we go to AppendElementEnd for this element, we're going to // MaybeLeaveFromPreContent(). So make sure to MaybeEnterInPreContent() // now, so our PreLevel() doesn't get confused. - MaybeEnterInPreContent(content); + MaybeEnterInPreContent(aElement); return rv; } @@ -907,11 +905,12 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement, aElement->NodeInfo()->GetName(tagLocalName); aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI); - uint32_t skipAttr = ScanNamespaceDeclarations(content, - aOriginalElement, tagNamespaceURI); + uint32_t skipAttr = + ScanNamespaceDeclarations(aElement, aOriginalElement, tagNamespaceURI); - nsAtom *name = content->NodeInfo()->NameAtom(); - bool lineBreakBeforeOpen = LineBreakBeforeOpen(content->GetNameSpaceID(), name); + nsAtom *name = aElement->NodeInfo()->NameAtom(); + bool lineBreakBeforeOpen = + LineBreakBeforeOpen(aElement->GetNameSpaceID(), name); if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { if (mColPos && lineBreakBeforeOpen) { @@ -952,25 +951,26 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement, } NS_ENSURE_TRUE(AppendToString(tagLocalName, aStr), NS_ERROR_OUT_OF_MEMORY); - MaybeEnterInPreContent(content); + MaybeEnterInPreContent(aElement); if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { NS_ENSURE_TRUE(IncrIndentation(name), NS_ERROR_OUT_OF_MEMORY); } - NS_ENSURE_TRUE(SerializeAttributes(content, aOriginalElement, tagPrefix, tagNamespaceURI, - name, aStr, skipAttr, addNSAttr), + NS_ENSURE_TRUE(SerializeAttributes(aElement, aOriginalElement, tagPrefix, + tagNamespaceURI, name, aStr, skipAttr, + addNSAttr), NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() - && LineBreakAfterOpen(content->GetNameSpaceID(), name)) { + && LineBreakAfterOpen(aElement->GetNameSpaceID(), name)) { NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); } - NS_ENSURE_TRUE(AfterElementStart(content, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); + NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); return NS_OK; } @@ -1145,8 +1145,8 @@ nsXMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument, } bool -nsXMLContentSerializer::CheckElementStart(nsIContent * aContent, - bool & aForceFormat, +nsXMLContentSerializer::CheckElementStart(Element*, + bool& aForceFormat, nsAString& aStr, nsresult& aResult) { diff --git a/dom/base/nsXMLContentSerializer.h b/dom/base/nsXMLContentSerializer.h index d20dd2b48101..62b47ba62eef 100644 --- a/dom/base/nsXMLContentSerializer.h +++ b/dom/base/nsXMLContentSerializer.h @@ -209,13 +209,13 @@ class nsXMLContentSerializer : public nsIContentSerializer { */ void GenerateNewPrefix(nsAString& aPrefix); - uint32_t ScanNamespaceDeclarations(nsIContent* aContent, - nsIContent *aOriginalElement, + uint32_t ScanNamespaceDeclarations(mozilla::dom::Element* aContent, + mozilla::dom::Element* aOriginalElement, const nsAString& aTagNamespaceURI); MOZ_MUST_USE - virtual bool SerializeAttributes(nsIContent* aContent, - nsIContent *aOriginalElement, + virtual bool SerializeAttributes(mozilla::dom::Element* aContent, + mozilla::dom::Element* aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsAtom* aTagName, @@ -243,10 +243,10 @@ class nsXMLContentSerializer : public nsIContentSerializer { * by setting aForceFormat to true. * @return boolean true if the element can be output */ - virtual bool CheckElementStart(nsIContent * aContent, - bool & aForceFormat, - nsAString& aStr, - nsresult& aResult); + virtual bool CheckElementStart(Element* aElement, + bool & aForceFormat, + nsAString& aStr, + nsresult& aResult); /** * This method is responsible for appending the '>' at the end of the start diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h index 4ba0a0588fd1..548e49d164c0 100644 --- a/dom/svg/SVGAElement.h +++ b/dom/svg/SVGAElement.h @@ -56,7 +56,7 @@ public: virtual void GetLinkTarget(nsAString& aTarget) override; virtual already_AddRefed GetHrefURI() const override; virtual EventStates IntrinsicState() const override; - using nsIContent::SetAttr; + using Element::SetAttr; virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, const nsAString& aValue, nsIPrincipal* aSubjectPrincipal, diff --git a/dom/svg/SVGStyleElement.h b/dom/svg/SVGStyleElement.h index e3495966ef3f..b25c56c789fe 100644 --- a/dom/svg/SVGStyleElement.h +++ b/dom/svg/SVGStyleElement.h @@ -44,7 +44,7 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - using nsIContent::SetAttr; + using Element::SetAttr; virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix, const nsAString& aValue, nsIPrincipal* aSubjectPrincipal, diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 858bd425188e..a8b975c34687 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -262,7 +262,7 @@ nsXBLBinding::UnbindAnonymousContent(nsIDocument* aDocument, } void -nsXBLBinding::SetBoundElement(nsIContent* aElement) +nsXBLBinding::SetBoundElement(Element* aElement) { mBoundElement = aElement; if (mNextBinding) @@ -301,7 +301,7 @@ nsXBLBinding::GenerateAnonymousContent() "Someone forgot a script blocker"); // Fetch the content element for this binding. - nsIContent* content = + Element* content = mPrototypeBinding->GetImmediateChild(nsGkAtoms::content); if (!content) { @@ -417,8 +417,12 @@ nsXBLBinding::GenerateAnonymousContent() } // Conserve space by wiping the attributes off the clone. + // + // FIXME(emilio): It'd be nice to make `mContent` a `RefPtr`, but + // as of right now it can also be a ShadowRoot (we don't enter in this + // codepath though). Move Shadow DOM outside XBL and then fix that. if (mContent) - mContent->UnsetAttr(namespaceID, name, false); + mContent->AsElement()->UnsetAttr(namespaceID, name, false); } } diff --git a/dom/xbl/nsXBLBinding.h b/dom/xbl/nsXBLBinding.h index 253fac3bc660..ad67effb46aa 100644 --- a/dom/xbl/nsXBLBinding.h +++ b/dom/xbl/nsXBLBinding.h @@ -64,8 +64,8 @@ public: nsXBLBinding* GetBaseBinding() const { return mNextBinding; } void SetBaseBinding(nsXBLBinding *aBinding); - nsIContent* GetBoundElement() { return mBoundElement; } - void SetBoundElement(nsIContent *aElement); + mozilla::dom::Element* GetBoundElement() { return mBoundElement; } + void SetBoundElement(mozilla::dom::Element* aElement); /* * Does a lookup for a method or attribute provided by one of the bindings' @@ -173,7 +173,7 @@ protected: nsCOMPtr mContent; // Strong. Our anonymous content stays around with us. RefPtr mNextBinding; // Strong. The derived binding owns the base class bindings. - nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it. + mozilla::dom::Element* mBoundElement; // [WEAK] We have a reference, but we don't own it. // The elements that we found in our when we // processed this binding. The default insertion point has no includes diff --git a/dom/xbl/nsXBLContentSink.cpp b/dom/xbl/nsXBLContentSink.cpp index 36f4db8e8f8d..2a11ecf4c569 100644 --- a/dom/xbl/nsXBLContentSink.cpp +++ b/dom/xbl/nsXBLContentSink.cpp @@ -535,7 +535,8 @@ nsXBLContentSink::OnOpenContainer(const char16_t **aAtts, nsresult nsXBLContentSink::ConstructBinding(uint32_t aLineNumber) { - nsCOMPtr binding = GetCurrentContent(); + // This is only called from HandleStartElement, so it'd better be an element. + RefPtr binding = GetCurrentContent()->AsElement(); binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID); NS_ConvertUTF16toUTF8 cid(mCurrentBindingID); @@ -880,13 +881,12 @@ nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, } nsresult -nsXBLContentSink::AddAttributes(const char16_t** aAtts, - nsIContent* aContent) +nsXBLContentSink::AddAttributes(const char16_t** aAtts, Element* aElement) { - if (aContent->IsXULElement()) + if (aElement->IsXULElement()) return NS_OK; // Nothing to do, since the proto already has the attrs. - return nsXMLContentSink::AddAttributes(aAtts, aContent); + return nsXMLContentSink::AddAttributes(aAtts, aElement); } #ifdef MOZ_XUL diff --git a/dom/xbl/nsXBLContentSink.h b/dom/xbl/nsXBLContentSink.h index 2d0b3e3ca99b..762e171b1994 100644 --- a/dom/xbl/nsXBLContentSink.h +++ b/dom/xbl/nsXBLContentSink.h @@ -92,8 +92,7 @@ protected: nsIContent** aResult, bool* aAppendContent, mozilla::dom::FromParser aFromParser) override; - nsresult AddAttributes(const char16_t** aAtts, - nsIContent* aContent) override; + nsresult AddAttributes(const char16_t** aAtts, Element* aElement) override; #ifdef MOZ_XUL nsresult AddAttributesToXULPrototype(const char16_t **aAtts, diff --git a/dom/xbl/nsXBLPrototypeBinding.cpp b/dom/xbl/nsXBLPrototypeBinding.cpp index db99c049372c..06a666592fae 100644 --- a/dom/xbl/nsXBLPrototypeBinding.cpp +++ b/dom/xbl/nsXBLPrototypeBinding.cpp @@ -62,8 +62,8 @@ using namespace mozilla::dom; class nsXBLAttributeEntry { public: nsXBLAttributeEntry(nsAtom* aSrcAtom, nsAtom* aDstAtom, - int32_t aDstNameSpace, nsIContent* aContent) - : mElement(aContent), + int32_t aDstNameSpace, Element* aElement) + : mElement(aElement), mSrcAttribute(aSrcAtom), mDstAttribute(aDstAtom), mDstNameSpace(aDstNameSpace), @@ -77,13 +77,13 @@ public: nsAtom* GetDstAttribute() { return mDstAttribute; } int32_t GetDstNameSpace() { return mDstNameSpace; } - nsIContent* GetElement() { return mElement; } + Element* GetElement() { return mElement; } nsXBLAttributeEntry* GetNext() { return mNext; } void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; } protected: - nsIContent* mElement; + Element* mElement; RefPtr mSrcAttribute; RefPtr mDstAttribute; @@ -113,7 +113,7 @@ nsXBLPrototypeBinding::nsXBLPrototypeBinding() nsresult nsXBLPrototypeBinding::Init(const nsACString& aID, nsXBLDocumentInfo* aInfo, - nsIContent* aElement, + Element* aElement, bool aFirstBinding) { nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI)); @@ -180,8 +180,7 @@ nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, void *aClosure) c void nsXBLPrototypeBinding::Initialize() { - nsIContent* content = GetImmediateChild(nsGkAtoms::content); - if (content) { + if (Element* content = GetImmediateChild(nsGkAtoms::content)) { ConstructAttributeTable(content); } } @@ -207,7 +206,7 @@ nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding) } void -nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement) +nsXBLPrototypeBinding::SetBindingElement(Element* aElement) { mBinding = aElement; if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle, @@ -323,7 +322,7 @@ void nsXBLPrototypeBinding::AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID, bool aRemoveFlag, - nsIContent* aChangedElement, + Element* aChangedElement, nsIContent* aAnonymousContent, bool aNotify) { @@ -339,13 +338,12 @@ nsXBLPrototypeBinding::AttributeChanged(nsAtom* aAttribute, return; // Iterate over the elements in the array. - nsCOMPtr content = GetImmediateChild(nsGkAtoms::content); + RefPtr content = GetImmediateChild(nsGkAtoms::content); while (xblAttr) { - nsIContent* element = xblAttr->GetElement(); + Element* element = xblAttr->GetElement(); - nsCOMPtr realElement = LocateInstance(aChangedElement, content, - aAnonymousContent, - element); + RefPtr realElement = + LocateInstance(aChangedElement, content, aAnonymousContent, element); if (realElement) { // Hold a strong reference here so that the atom doesn't go away during @@ -436,14 +434,14 @@ nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const // Internal helpers /////////////////////////////////////////////////////////////////////// -nsIContent* +Element* nsXBLPrototypeBinding::GetImmediateChild(nsAtom* aTag) { for (nsIContent* child = mBinding->GetFirstChild(); child; child = child->GetNextSibling()) { if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) { - return child; + return child->AsElement(); } } @@ -461,18 +459,18 @@ nsXBLPrototypeBinding::InitClass(const nsString& aClassName, aClassName, this, aClassObject, aNew); } -nsIContent* -nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement, +Element* +nsXBLPrototypeBinding::LocateInstance(Element* aBoundElement, nsIContent* aTemplRoot, nsIContent* aCopyRoot, - nsIContent* aTemplChild) + Element* aTemplChild) { // XXX We will get in trouble if the binding instantiation deviates from the template // in the prototype. if (aTemplChild == aTemplRoot || !aTemplChild) return nullptr; - nsIContent* templParent = aTemplChild->GetParent(); + Element* templParent = aTemplChild->GetParentElement(); // We may be disconnected from our parent during cycle collection. if (!templParent) @@ -485,11 +483,17 @@ nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement, if (!copyParent) return nullptr; - return copyParent->GetChildAt(templParent->IndexOf(aTemplChild)); + nsIContent* child = copyParent->GetChildAt(templParent->IndexOf(aTemplChild)); + if (child && child->IsElement()) { + return child->AsElement(); + } + return nullptr; } void -nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent) +nsXBLPrototypeBinding::SetInitialAttributes( + Element* aBoundElement, + nsIContent* aAnonymousContent) { if (!mAttributeTable) { return; @@ -528,9 +532,9 @@ nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIConten while (curr) { nsAtom* dst = curr->GetDstAttribute(); int32_t dstNs = curr->GetDstNameSpace(); - nsIContent* element = curr->GetElement(); + Element* element = curr->GetElement(); - nsIContent* realElement = + Element* realElement = LocateInstance(aBoundElement, content, aAnonymousContent, element); @@ -595,7 +599,7 @@ nsXBLPrototypeBinding::EnsureAttributeTable() void nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* aSourceTag, int32_t aDestNamespaceID, nsAtom* aDestTag, - nsIContent* aContent) + Element* aElement) { InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID); if (!attributesNS) { @@ -604,7 +608,7 @@ nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* a } nsXBLAttributeEntry* xblAttr = - new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aContent); + new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aElement); nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag); if (!entry) { @@ -617,7 +621,7 @@ nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* a } void -nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement) +nsXBLPrototypeBinding::ConstructAttributeTable(Element* aElement) { // Don't add entries for elements, since those will get // removed from the DOM when we construct the insertion point table. @@ -692,7 +696,9 @@ nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement) for (nsIContent* child = aElement->GetFirstChild(); child; child = child->GetNextSibling()) { - ConstructAttributeTable(child); + if (child->IsElement()) { + ConstructAttributeTable(child->AsElement()); + } } } @@ -1195,12 +1201,11 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, if (namespaceID == XBLBinding_Serialize_NoContent) return NS_OK; - nsCOMPtr content; - // If this is a text type, just read the string and return. if (namespaceID == XBLBinding_Serialize_TextNode || namespaceID == XBLBinding_Serialize_CDATANode || namespaceID == XBLBinding_Serialize_CommentNode) { + nsCOMPtr content; switch (namespaceID) { case XBLBinding_Serialize_TextNode: content = new nsTextNode(aNim); @@ -1244,6 +1249,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, rv = aStream->Read32(&attrCount); NS_ENSURE_SUCCESS(rv, rv); + RefPtr element; // Create XUL prototype elements, or regular elements for other namespaces. // This needs to match the code in nsXBLContentSink::CreateElement. #ifdef MOZ_XUL @@ -1293,17 +1299,12 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, NS_ENSURE_SUCCESS(rv, rv); } - nsCOMPtr result; nsresult rv = - nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(result)); + nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); - content = result; - } - else { + } else { #endif - nsCOMPtr element; NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER); - content = element; for (uint32_t i = 0; i < attrCount; i++) { rv = ReadNamespace(aStream, namespaceID); @@ -1322,7 +1323,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, prefixAtom = NS_Atomize(prefix); RefPtr nameAtom = NS_Atomize(name); - content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false); + element->SetAttr(namespaceID, nameAtom, prefixAtom, val, false); } #ifdef MOZ_XUL @@ -1348,7 +1349,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, RefPtr destAtom = NS_Atomize(destAttribute); EnsureAttributeTable(); - AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content); + AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, element); rv = ReadNamespace(aStream, srcNamespaceID); NS_ENSURE_SUCCESS(rv, rv); @@ -1365,11 +1366,11 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, // Child may be null if this was a comment for example and can just be ignored. if (child) { - content->AppendChildTo(child, false); + element->AppendChildTo(child, false); } } - content.swap(*aContent); + element.forget(aContent); return NS_OK; } @@ -1405,21 +1406,22 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, } // Otherwise, this is an element. + Element* element = aNode->AsElement(); // Write the namespace id followed by the tag name - rv = WriteNamespace(aStream, aNode->GetNameSpaceID()); + rv = WriteNamespace(aStream, element->GetNameSpaceID()); NS_ENSURE_SUCCESS(rv, rv); nsAutoString prefixStr; - aNode->NodeInfo()->GetPrefix(prefixStr); + element->NodeInfo()->GetPrefix(prefixStr); rv = aStream->WriteWStringZ(prefixStr.get()); NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->NodeInfo()->NameAtom()).get()); + rv = aStream->WriteWStringZ(nsDependentAtomString(element->NodeInfo()->NameAtom()).get()); NS_ENSURE_SUCCESS(rv, rv); // Write attributes - uint32_t count = aNode->GetAttrCount(); + uint32_t count = element->GetAttrCount(); rv = aStream->Write32(count); NS_ENSURE_SUCCESS(rv, rv); @@ -1428,7 +1430,7 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, // Write out the namespace id, the namespace prefix, the local tag name, // and the value, in that order. - const BorrowedAttrInfo attrInfo = aNode->GetAttrInfoAt(i); + const BorrowedAttrInfo attrInfo = element->GetAttrInfoAt(i); const nsAttrName* name = attrInfo.mName; // XXXndeakin don't write out xbl:inherits? @@ -1462,7 +1464,7 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, nsXBLAttributeEntry* entry = iter2.UserData(); do { - if (entry->GetElement() == aNode) { + if (entry->GetElement() == element) { WriteNamespace(aStream, srcNamespace); aStream->WriteWStringZ( nsDependentAtomString(entry->GetSrcAttribute()).get()); @@ -1480,12 +1482,12 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, NS_ENSURE_SUCCESS(rv, rv); // Finally, write out the child nodes. - count = aNode->GetChildCount(); + count = element->GetChildCount(); rv = aStream->Write32(count); NS_ENSURE_SUCCESS(rv, rv); for (i = 0; i < count; i++) { - rv = WriteContentNode(aStream, aNode->GetChildAt(i)); + rv = WriteContentNode(aStream, element->GetChildAt(i)); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/xbl/nsXBLPrototypeBinding.h b/dom/xbl/nsXBLPrototypeBinding.h index 9ace95b8f2f1..e5b2357b2799 100644 --- a/dom/xbl/nsXBLPrototypeBinding.h +++ b/dom/xbl/nsXBLPrototypeBinding.h @@ -41,8 +41,8 @@ class nsXBLPrototypeBinding final : public: MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsXBLPrototypeBinding) - nsIContent* GetBindingElement() const { return mBinding; } - void SetBindingElement(nsIContent* aElement); + mozilla::dom::Element* GetBindingElement() const { return mBinding; } + void SetBindingElement(mozilla::dom::Element* aElement); nsIURI* BindingURI() const { return mBindingURI; } nsIURI* AlternateBindingURI() const { return mAlternateBindingURI; } @@ -111,7 +111,8 @@ public: bool HasImplementation() const { return mImplementation != nullptr; } void AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID, - bool aRemoveFlag, nsIContent* aChangedElement, + bool aRemoveFlag, + mozilla::dom::Element* aChangedElement, nsIContent* aAnonymousContent, bool aNotify); void SetBasePrototype(nsXBLPrototypeBinding* aBinding); @@ -120,7 +121,8 @@ public: nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; } bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); } - void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent); + void SetInitialAttributes(mozilla::dom::Element* aBoundElement, + nsIContent* aAnonymousContent); void AppendStyleSheet(mozilla::StyleSheet* aSheet); void RemoveStyleSheet(mozilla::StyleSheet* aSheet); @@ -246,7 +248,7 @@ public: // binding's handlers, properties, etc are all set. nsresult Init(const nsACString& aRef, nsXBLDocumentInfo* aInfo, - nsIContent* aElement, + mozilla::dom::Element* aElement, bool aFirstBinding = false); void Traverse(nsCycleCollectionTraversalCallback &cb) const; @@ -259,11 +261,11 @@ public: * GetImmediateChild locates the immediate child of our binding element which * has the localname given by aTag and is in the XBL namespace. */ - nsIContent* GetImmediateChild(nsAtom* aTag); - nsIContent* LocateInstance(nsIContent* aBoundElt, - nsIContent* aTemplRoot, - nsIContent* aCopyRoot, - nsIContent* aTemplChild); + mozilla::dom::Element* GetImmediateChild(nsAtom* aTag); + mozilla::dom::Element* LocateInstance(mozilla::dom::Element* aBoundElt, + nsIContent* aTemplRoot, + nsIContent* aCopyRoot, + mozilla::dom::Element* aTemplChild); bool ChromeOnlyContent() { return mChromeOnlyContent; } bool BindToUntrustedContent() { return mBindToUntrustedContent; } @@ -276,8 +278,8 @@ protected: // Ad an entry to the attribute table void AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* aSourceTag, int32_t aDestNamespaceID, nsAtom* aDestTag, - nsIContent* aContent); - void ConstructAttributeTable(nsIContent* aElement); + mozilla::dom::Element* aContent); + void ConstructAttributeTable(mozilla::dom::Element* aElement); void CreateKeyHandlers(); private: @@ -287,7 +289,7 @@ private: protected: nsCOMPtr mBindingURI; nsCOMPtr mAlternateBindingURI; // Alternate id-less URI that is only non-null on the first binding. - nsCOMPtr mBinding; // Strong. We own a ref to our content element in the binding doc. + RefPtr mBinding; // Strong. We own a ref to our content element in the binding doc. nsAutoPtr mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers. // the url of the base binding diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index 720a8af346b5..e93c78113a5a 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -479,7 +479,7 @@ private: // This function loads a particular XBL file and installs all of the bindings // onto the element. nsresult -nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, +nsXBLService::LoadBindings(Element* aElement, nsIURI* aURL, nsIPrincipal* aOriginPrincipal, nsXBLBinding** aBinding, bool* aResolveStyle) { @@ -488,20 +488,20 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, *aBinding = nullptr; *aResolveStyle = false; - AutoEnsureSubtreeStyled subtreeStyled(aContent->AsElement()); + AutoEnsureSubtreeStyled subtreeStyled(aElement); if (MOZ_UNLIKELY(!aURL)) { return NS_OK; } // Easy case: The binding was already loaded. - nsXBLBinding* binding = aContent->GetXBLBinding(); + nsXBLBinding* binding = aElement->GetXBLBinding(); if (binding && !binding->MarkedForDeath() && binding->PrototypeBinding()->CompareBindingURI(aURL)) { return NS_OK; } - nsCOMPtr document = aContent->OwnerDoc(); + nsCOMPtr document = aElement->OwnerDoc(); nsAutoCString urlspec; nsresult rv; @@ -518,13 +518,13 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, } if (binding) { - FlushStyleBindings(aContent); + FlushStyleBindings(aElement); binding = nullptr; } bool ready; RefPtr newBinding; - if (NS_FAILED(rv = GetBinding(aContent, aURL, false, aOriginPrincipal, + if (NS_FAILED(rv = GetBinding(aElement, aURL, false, aOriginPrincipal, &ready, getter_AddRefs(newBinding)))) { return rv; } @@ -537,21 +537,21 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, return NS_OK; } - if (::IsAncestorBinding(document, aURL, aContent)) { + if (::IsAncestorBinding(document, aURL, aElement)) { return NS_ERROR_ILLEGAL_VALUE; } - AutoStyleElement styleElement(aContent->AsElement(), aResolveStyle); + AutoStyleElement styleElement(aElement, aResolveStyle); // We loaded a style binding. It goes on the end. // Install the binding on the content node. - aContent->SetXBLBinding(newBinding); + aElement->SetXBLBinding(newBinding); { nsAutoScriptBlocker scriptBlocker; // Set the binding's bound element. - newBinding->SetBoundElement(aContent); + newBinding->SetBoundElement(aElement); // Tell the binding to build the anonymous content. newBinding->GenerateAnonymousContent(); @@ -572,20 +572,18 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, return NS_OK; } -nsresult -nsXBLService::FlushStyleBindings(nsIContent* aContent) +void +nsXBLService::FlushStyleBindings(Element* aElement) { - nsCOMPtr document = aContent->OwnerDoc(); + nsCOMPtr document = aElement->OwnerDoc(); - nsXBLBinding *binding = aContent->GetXBLBinding(); + nsXBLBinding* binding = aElement->GetXBLBinding(); if (binding) { // Clear out the script references. binding->ChangeDocument(document, nullptr); - aContent->SetXBLBinding(nullptr); // Flush old style bindings + aElement->SetXBLBinding(nullptr); // Flush old style bindings } - - return NS_OK; } // diff --git a/dom/xbl/nsXBLService.h b/dom/xbl/nsXBLService.h index a18740a89f27..f113f07d11e3 100644 --- a/dom/xbl/nsXBLService.h +++ b/dom/xbl/nsXBLService.h @@ -46,7 +46,7 @@ class nsXBLService final : public nsSupportsWeakReference // This function loads a particular XBL file and installs all of the bindings // onto the element. aOriginPrincipal must not be null here. - nsresult LoadBindings(nsIContent* aContent, nsIURI* aURL, + nsresult LoadBindings(mozilla::dom::Element* aElement, nsIURI* aURL, nsIPrincipal* aOriginPrincipal, nsXBLBinding** aBinding, bool* aResolveStyle); @@ -72,8 +72,8 @@ private: virtual ~nsXBLService(); protected: - // This function clears out the bindings on a given content node. - nsresult FlushStyleBindings(nsIContent* aContent); + // This function clears out the bindings on a given element. + void FlushStyleBindings(mozilla::dom::Element*); // This method synchronously loads and parses an XBL file. nsresult FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument, diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 93b6f259fb0e..238f0d58fbcd 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -980,7 +980,7 @@ nsXMLContentSink::HandleStartElement(const char16_t *aName, NS_ENSURE_SUCCESS(result, result); // Set the attributes on the new content element - result = AddAttributes(aAtts, content); + result = AddAttributes(aAtts, content->AsElement()); if (NS_OK == result) { // Store the element @@ -1413,7 +1413,7 @@ nsXMLContentSink::ReportError(const char16_t* aErrorText, nsresult nsXMLContentSink::AddAttributes(const char16_t** aAtts, - nsIContent* aContent) + Element* aContent) { // Add tag attributes to the content attributes RefPtr prefix, localName; diff --git a/dom/xml/nsXMLContentSink.h b/dom/xml/nsXMLContentSink.h index 4b39c08dc829..adcac0cd0886 100644 --- a/dom/xml/nsXMLContentSink.h +++ b/dom/xml/nsXMLContentSink.h @@ -99,7 +99,7 @@ protected: // stylesheets are all done loading. virtual void MaybeStartLayout(bool aIgnorePendingSheets); - virtual nsresult AddAttributes(const char16_t** aNode, nsIContent* aContent); + virtual nsresult AddAttributes(const char16_t** aNode, Element* aElement); nsresult AddText(const char16_t* aString, int32_t aLength); virtual bool OnOpenContainer(const char16_t **aAtts, diff --git a/dom/xml/nsXMLPrettyPrinter.cpp b/dom/xml/nsXMLPrettyPrinter.cpp index 1fa2c1a43ac1..401bd28a5146 100644 --- a/dom/xml/nsXMLPrettyPrinter.cpp +++ b/dom/xml/nsXMLPrettyPrinter.cpp @@ -144,8 +144,8 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, NS_ENSURE_SUCCESS(rv, rv); // Compute the bound element. - nsCOMPtr rootCont = aDocument->GetRootElement(); - NS_ENSURE_TRUE(rootCont, NS_ERROR_UNEXPECTED); + RefPtr rootElement = aDocument->GetRootElement(); + NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED); // Grab the system principal. nsCOMPtr sysPrincipal; @@ -154,20 +154,20 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, // Destroy any existing frames before we unbind anonymous content. // Note that the shell might be Destroy'ed by now (see bug 1415541). - if (!shell->IsDestroying() && rootCont->IsElement()) { - shell->DestroyFramesForAndRestyle(rootCont->AsElement()); + if (!shell->IsDestroying()) { + shell->DestroyFramesForAndRestyle(rootElement); } // Load the bindings. RefPtr unused; bool ignored; - rv = xblService->LoadBindings(rootCont, bindingUri, sysPrincipal, + rv = xblService->LoadBindings(rootElement, bindingUri, sysPrincipal, getter_AddRefs(unused), &ignored); NS_ENSURE_SUCCESS(rv, rv); // Fire an event at the bound element to pass it |resultFragment|. RefPtr event = - NS_NewDOMCustomEvent(rootCont, nullptr, nullptr); + NS_NewDOMCustomEvent(rootElement, nullptr, nullptr); MOZ_ASSERT(event); nsCOMPtr resultFragmentVariant = new nsVariant(); rv = resultFragmentVariant->SetAsISupports(resultFragment); @@ -178,7 +178,7 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, NS_ENSURE_SUCCESS(rv, rv); event->SetTrusted(true); bool dummy; - rv = rootCont->DispatchEvent(event, &dummy); + rv = rootElement->DispatchEvent(event, &dummy); NS_ENSURE_SUCCESS(rv, rv); // Observe the document so we know when to switch to "normal" view diff --git a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp index 5811ac96f3c5..9ba666cd319a 100644 --- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp +++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp @@ -120,14 +120,19 @@ txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex) { NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs"); - uint32_t total = mPosition.Content()->GetAttrCount(); + if (!mPosition.Content()->IsElement()) { + return false; + } + + Element* element = mPosition.Content()->AsElement(); + uint32_t total = element->GetAttrCount(); if (aStartIndex >= total) { return false; } uint32_t index; for (index = aStartIndex; index < total; ++index) { - const nsAttrName* name = mPosition.Content()->GetAttrNameAt(index); + const nsAttrName* name = element->GetAttrNameAt(index); // We need to ignore XMLNS attributes. if (name->NamespaceID() != kNameSpaceID_XMLNS) { @@ -142,13 +147,15 @@ txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex) bool txXPathTreeWalker::moveToNamedAttribute(nsAtom* aLocalName, int32_t aNSID) { - if (!mPosition.isContent()) { + if (!mPosition.isContent() || !mPosition.Content()->IsElement()) { return false; } + Element* element = mPosition.Content()->AsElement(); + const nsAttrName* name; uint32_t i; - for (i = 0; (name = mPosition.Content()->GetAttrNameAt(i)); ++i) { + for (i = 0; (name = element->GetAttrNameAt(i)); ++i) { if (name->Equals(aLocalName, aNSID)) { mPosition.mIndex = i; @@ -310,8 +317,9 @@ txXPathNodeUtils::getLocalName(const txXPathNode& aNode) return nullptr; } - RefPtr localName = aNode.Content()-> - GetAttrNameAt(aNode.mIndex)->LocalName(); + // This is an attribute node, so we necessarily come from an element. + RefPtr localName = + aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->LocalName(); return localName.forget(); } @@ -329,7 +337,7 @@ txXPathNodeUtils::getPrefix(const txXPathNode& aNode) return aNode.Content()->NodeInfo()->GetPrefixAtom(); } - return aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetPrefix(); + return aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->GetPrefix(); } /* static */ @@ -362,7 +370,7 @@ txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName) return; } - aNode.Content()->GetAttrNameAt(aNode.mIndex)->LocalName()-> + aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->LocalName()-> ToString(aLocalName); // Check for html @@ -396,7 +404,7 @@ txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName) return; } - aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName); + aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName); } /* static */ @@ -411,7 +419,7 @@ txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode) return aNode.Content()->GetNameSpaceID(); } - return aNode.Content()->GetAttrNameAt(aNode.mIndex)->NamespaceID(); + return aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex)->NamespaceID(); } /* static */ @@ -441,7 +449,7 @@ void txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult) { if (aNode.isAttribute()) { - const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex); + const nsAttrName* name = aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex); if (aResult.IsEmpty()) { aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(), @@ -692,7 +700,8 @@ txXPathNativeNode::getNode(const txXPathNode& aNode) return aNode.mNode; } - const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex); + const nsAttrName* name = + aNode.Content()->AsElement()->GetAttrNameAt(aNode.mIndex); nsAutoString namespaceURI; nsContentUtils::NameSpaceManager()->GetNameSpaceURI(name->NamespaceID(), namespaceURI); diff --git a/dom/xslt/xslt/txMozillaTextOutput.cpp b/dom/xslt/xslt/txMozillaTextOutput.cpp index db65131ee8c5..c4331f7cdf94 100644 --- a/dom/xslt/xslt/txMozillaTextOutput.cpp +++ b/dom/xslt/xslt/txMozillaTextOutput.cpp @@ -20,6 +20,7 @@ #include "nsTextNode.h" #include "nsNameSpaceManager.h" +using namespace mozilla; using namespace mozilla::dom; txMozillaTextOutput::txMozillaTextOutput(nsITransformObserver* aObserver) @@ -196,7 +197,7 @@ txMozillaTextOutput::createResultDocument(nsIDocument* aSourceDocument, NS_ENSURE_SUCCESS(rv, rv); } else { - nsCOMPtr html, head, body; + RefPtr html, head, body; rv = createXHTMLElement(nsGkAtoms::html, getter_AddRefs(html)); NS_ENSURE_SUCCESS(rv, rv); @@ -212,12 +213,17 @@ txMozillaTextOutput::createResultDocument(nsIDocument* aSourceDocument, rv = html->AppendChildTo(body, false); NS_ENSURE_SUCCESS(rv, rv); - rv = createXHTMLElement(nsGkAtoms::pre, getter_AddRefs(mTextParent)); - NS_ENSURE_SUCCESS(rv, rv); + { + RefPtr textParent; + rv = createXHTMLElement(nsGkAtoms::pre, getter_AddRefs(textParent)); + NS_ENSURE_SUCCESS(rv, rv); + mTextParent = textParent.forget(); + } - rv = mTextParent->SetAttr(kNameSpaceID_None, nsGkAtoms::id, - NS_LITERAL_STRING("transformiixResult"), - false); + rv = mTextParent->AsElement()->SetAttr(kNameSpaceID_None, + nsGkAtoms::id, + NS_LITERAL_STRING("transformiixResult"), + false); NS_ENSURE_SUCCESS(rv, rv); rv = body->AppendChildTo(mTextParent, false); @@ -250,8 +256,7 @@ void txMozillaTextOutput::getOutputDocument(nsIDOMDocument** aDocument) } nsresult -txMozillaTextOutput::createXHTMLElement(nsAtom* aName, - nsIContent** aResult) +txMozillaTextOutput::createXHTMLElement(nsAtom* aName, Element** aResult) { nsCOMPtr element = mDocument->CreateHTMLElement(aName); element.forget(aResult); diff --git a/dom/xslt/xslt/txMozillaTextOutput.h b/dom/xslt/xslt/txMozillaTextOutput.h index e1c84640e65d..25eefb131634 100644 --- a/dom/xslt/xslt/txMozillaTextOutput.h +++ b/dom/xslt/xslt/txMozillaTextOutput.h @@ -16,6 +16,12 @@ class nsITransformObserver; class nsIDocument; class nsIContent; +namespace mozilla { +namespace dom { +class Element; +} +} + class txMozillaTextOutput : public txAOutputXMLEventHandler { public: @@ -30,7 +36,7 @@ public: bool aLoadedAsData); private: - nsresult createXHTMLElement(nsAtom* aName, nsIContent** aResult); + nsresult createXHTMLElement(nsAtom* aName, mozilla::dom::Element** aResult); nsCOMPtr mTextParent; nsWeakPtr mObserver; diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp index b6dac72fad0e..5f68dffd0809 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.cpp +++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp @@ -688,7 +688,7 @@ txMozillaXMLOutput::startHTMLElement(nsIContent* aElement, bool aIsHTML) } else if (aElement->IsHTMLElement(nsGkAtoms::tr) && aIsHTML && NS_PTR_TO_INT32(mTableStateStack.peek()) == TABLE) { - nsCOMPtr tbody; + RefPtr tbody; rv = createHTMLElement(nsGkAtoms::tbody, getter_AddRefs(tbody)); NS_ENSURE_SUCCESS(rv, rv); @@ -708,7 +708,7 @@ txMozillaXMLOutput::startHTMLElement(nsIContent* aElement, bool aIsHTML) mOutputFormat.mMethod == eHTMLOutput) { // Insert META tag, according to spec, 16.2, like // - nsCOMPtr meta; + RefPtr meta; rv = createHTMLElement(nsGkAtoms::meta, getter_AddRefs(meta)); NS_ENSURE_SUCCESS(rv, rv); @@ -915,8 +915,7 @@ txMozillaXMLOutput::createResultDocument(const nsAString& aName, int32_t aNsID, } nsresult -txMozillaXMLOutput::createHTMLElement(nsAtom* aName, - nsIContent** aResult) +txMozillaXMLOutput::createHTMLElement(nsAtom* aName, Element** aResult) { NS_ASSERTION(mOutputFormat.mMethod == eHTMLOutput, "need to adjust createHTMLElement"); diff --git a/dom/xslt/xslt/txMozillaXMLOutput.h b/dom/xslt/xslt/txMozillaXMLOutput.h index 7cc1beb076e9..84ff729de0f9 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.h +++ b/dom/xslt/xslt/txMozillaXMLOutput.h @@ -82,7 +82,7 @@ private: nsresult endHTMLElement(nsIContent* aElement); void processHTTPEquiv(nsAtom* aHeader, const nsString& aValue); nsresult createHTMLElement(nsAtom* aName, - nsIContent** aResult); + mozilla::dom::Element** aResult); nsresult attributeInternal(nsAtom* aPrefix, nsAtom* aLocalName, int32_t aNsID, const nsString& aValue); diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 7057d8aa9471..2372c7bc1501 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -158,7 +158,7 @@ nsRefMapEntry::GetFirstElement() } void -nsRefMapEntry::AppendAll(nsCOMArray* aElements) +nsRefMapEntry::AppendAll(nsCOMArray* aElements) { for (size_t i = 0; i < mRefContentList.Length(); ++i) { aElements->AppendObject(mRefContentList[i]); @@ -1098,7 +1098,7 @@ XULDocument::ContentRemoved(nsIDocument* aDocument, void XULDocument::GetElementsForID(const nsAString& aID, - nsCOMArray& aElements) + nsCOMArray& aElements) { aElements.Clear(); @@ -2059,7 +2059,7 @@ XULDocument::ApplyPersistentAttributes() nsresult XULDocument::ApplyPersistentAttributesInternal() { - nsCOMArray elements; + nsCOMArray elements; nsAutoCString utf8uri; nsresult rv = mDocumentURI->GetSpec(utf8uri); @@ -2107,7 +2107,7 @@ XULDocument::ApplyPersistentAttributesInternal() nsresult XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID, - nsCOMArray& aElements) + nsCOMArray& aElements) { nsAutoCString utf8uri; nsresult rv = mDocumentURI->GetSpec(utf8uri); @@ -2147,12 +2147,12 @@ XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID, uint32_t cnt = aElements.Count(); for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { - nsCOMPtr element = aElements.SafeObjectAt(i); + RefPtr element = aElements.SafeObjectAt(i); if (!element) { continue; } - rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE); + Unused << element->SetAttr(kNameSpaceID_None, attr, value, true); } } @@ -3153,7 +3153,7 @@ XULDocument::MaybeBroadcast() for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) { nsAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName; if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) { - nsCOMPtr listener = + nsCOMPtr listener = do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener); const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr; if (mDelayedAttrChangeBroadcasts[i].mSetAttr) { @@ -3610,7 +3610,7 @@ XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype, nsresult XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype, - nsIContent* aElement) + Element* aElement) { nsresult rv; @@ -3694,12 +3694,12 @@ XULDocument::CreateTemplateBuilder(Element* aElement) // Create a if one isn't there already. // XXXvarga what about attributes? - nsCOMPtr bodyContent; + RefPtr bodyContent; nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, nsGkAtoms::treechildren, getter_AddRefs(bodyContent)); - if (! bodyContent) { + if (!bodyContent) { bodyContent = document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren), nullptr, kNameSpaceID_XUL); @@ -3760,7 +3760,7 @@ XULDocument::OverlayForwardReference::Resolve() // Resolve a forward reference from an overlay element; attempt to // hook it up into the main document. nsresult rv; - nsCOMPtr target; + RefPtr target; nsIPresShell *shell = mDocument->GetShell(); bool notify = shell && shell->DidInitialize(); @@ -3818,23 +3818,23 @@ XULDocument::OverlayForwardReference::Resolve() nsresult -XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, - nsIContent* aOverlayNode, +XULDocument::OverlayForwardReference::Merge(Element* aTargetElement, + Element* aOverlayElement, bool aNotify) { // This function is given: - // aTargetNode: the node in the document whose 'id' attribute - // matches a toplevel node in our overlay. - // aOverlayNode: the node in the overlay document that matches - // a node in the actual document. - // aNotify: whether or not content manipulation methods should - // use the aNotify parameter. After the initial - // reflow (i.e. in the dynamic overlay merge case), - // we want all the content manipulation methods we - // call to notify so that frames are constructed - // etc. Otherwise do not, since that's during initial - // document construction before StartLayout has been - // called which will do everything for us. + // aTargetElement: the element in the document whose 'id' attribute + // matches a toplevel node in our overlay. + // aOverlayElement: the element in the overlay document that matches + // an element in the actual document. + // aNotify: whether or not content manipulation methods should + // use the aNotify parameter. After the initial + // reflow (i.e. in the dynamic overlay merge case), + // we want all the content manipulation methods we + // call to notify so that frames are constructed + // etc. Otherwise do not, since that's during initial + // document construction before StartLayout has been + // called which will do everything for us. // // This function merges the tree from the overlay into the tree in // the document, overwriting attributes and appending child content @@ -3846,27 +3846,27 @@ XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, // actual document. uint32_t i; const nsAttrName* name; - for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) { + for (i = 0; (name = aOverlayElement->GetAttrNameAt(i)); ++i) { // We don't want to swap IDs, they should be the same. if (name->Equals(nsGkAtoms::id)) continue; // In certain cases merging command or observes is unsafe, so don't. if (!aNotify) { - if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes, - kNameSpaceID_XUL)) + if (aTargetElement->NodeInfo()->Equals(nsGkAtoms::observes, + kNameSpaceID_XUL)) continue; if (name->Equals(nsGkAtoms::observes) && - aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes)) + aTargetElement->HasAttr(kNameSpaceID_None, nsGkAtoms::observes)) continue; if (name->Equals(nsGkAtoms::command) && - aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) && - !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key, - kNameSpaceID_XUL) && - !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem, - kNameSpaceID_XUL)) + aTargetElement->HasAttr(kNameSpaceID_None, nsGkAtoms::command) && + !aTargetElement->NodeInfo()->Equals(nsGkAtoms::key, + kNameSpaceID_XUL) && + !aTargetElement->NodeInfo()->Equals(nsGkAtoms::menuitem, + kNameSpaceID_XUL)) continue; } @@ -3875,27 +3875,23 @@ XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, nsAtom* prefix = name->GetPrefix(); nsAutoString value; - aOverlayNode->GetAttr(nameSpaceID, attr, value); + aOverlayElement->GetAttr(nameSpaceID, attr, value); // Element in the overlay has the 'removeelement' attribute set // so remove it from the actual document. - if (attr == nsGkAtoms::removeelement && - value.EqualsLiteral("true")) { - - nsCOMPtr parent = aTargetNode->GetParentNode(); + if (attr == nsGkAtoms::removeelement && value.EqualsLiteral("true")) { + nsCOMPtr parent = aTargetElement->GetParentNode(); if (!parent) return NS_ERROR_FAILURE; - rv = RemoveElement(parent, aTargetNode); + rv = RemoveElement(parent, aTargetElement); if (NS_FAILED(rv)) return rv; - return NS_OK; } - rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify); - if (!NS_FAILED(rv) && !aNotify) - rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode, - nameSpaceID, - attr, prefix, - value); + rv = aTargetElement->SetAttr(nameSpaceID, attr, prefix, value, aNotify); + if (!NS_FAILED(rv) && !aNotify) { + rv = mDocument->BroadcastAttributeChangeFromOverlay( + aTargetElement, nameSpaceID, attr, prefix, value); + } if (NS_FAILED(rv)) return rv; } @@ -3907,23 +3903,23 @@ XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, // to merge inside that subtree. If not, we just append the tree to // the parent like any other. - uint32_t childCount = aOverlayNode->GetChildCount(); + uint32_t childCount = aOverlayElement->GetChildCount(); // This must be a strong reference since it will be the only // reference to a content object during part of this loop. nsCOMPtr currContent; for (i = 0; i < childCount; ++i) { - currContent = aOverlayNode->GetFirstChild(); + currContent = aOverlayElement->GetFirstChild(); nsAtom *idAtom = currContent->GetID(); - nsIContent *elementInDocument = nullptr; + Element* elementInDocument = nullptr; if (idAtom) { nsDependentAtomString id(idAtom); if (!id.IsEmpty()) { - nsIDocument *doc = aTargetNode->GetUncomposedDoc(); + nsIDocument *doc = aTargetElement->GetUncomposedDoc(); //XXXsmaug should we use ShadowRoot::GetElementById() // if doc is null? if (!doc) return NS_ERROR_FAILURE; @@ -3938,30 +3934,31 @@ XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, // node. Otherwise, we just do an append as if the element had // no id attribute. if (elementInDocument) { - // Given two parents, aTargetNode and aOverlayNode, we want + // Given two parents, aTargetElement and aOverlayElement, we want // to call merge on currContent if we find an associated // node in the document with the same id as currContent that // also has aTargetNode as its parent. - nsIContent *elementParent = elementInDocument->GetParent(); + nsIContent* elementParent = elementInDocument->GetParent(); nsAtom *parentID = elementParent->GetID(); - if (parentID && - aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, - nsDependentAtomString(parentID), - eCaseMatters)) { + if (parentID && aTargetElement->GetID() == parentID) { // The element matches. "Go Deep!" - rv = Merge(elementInDocument, currContent, aNotify); + // + // Note that currContent is necessarily an element, because + // elementInDocument can only be non-null when currContent has a + // non-null ID. + rv = Merge(elementInDocument, currContent->AsElement(), aNotify); if (NS_FAILED(rv)) return rv; - aOverlayNode->RemoveChildAt(0, false); + aOverlayElement->RemoveChildAt(0, false); continue; } } - aOverlayNode->RemoveChildAt(0, false); + aOverlayElement->RemoveChildAt(0, false); - rv = InsertElement(aTargetNode, currContent, aNotify); + rv = InsertElement(aTargetElement, currContent, aNotify); if (NS_FAILED(rv)) return rv; } @@ -4092,7 +4089,7 @@ XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode, (bl->mAttribute != nsGkAtoms::_asterisk)) continue; - nsCOMPtr l = do_QueryReferent(bl->mListener); + nsCOMPtr l = do_QueryReferent(bl->mListener); if (l) { rv = l->SetAttr(aNameSpaceID, aAttribute, aPrefix, aValue, false); @@ -4255,8 +4252,7 @@ XULDocument::CheckBroadcasterHookup(Element* aElement, } nsresult -XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, - bool aNotify) +XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, bool aNotify) { // Insert aChild appropriately into aParent, accounting for a // 'pos' attribute set on aChild. @@ -4306,7 +4302,6 @@ XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, } if (!wasInserted) { - aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr); if (!posStr.IsEmpty()) { nsresult rv; diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h index a14e1aa8bc96..a22b24af4673 100644 --- a/dom/xul/XULDocument.h +++ b/dom/xul/XULDocument.h @@ -64,7 +64,7 @@ public: } mozilla::dom::Element* GetFirstElement(); - void AppendAll(nsCOMArray* aElements); + void AppendAll(nsCOMArray* aElements); /** * @return true if aElement was added, false if we failed due to OOM */ @@ -126,7 +126,7 @@ public: // nsIXULDocument interface virtual void GetElementsForID(const nsAString& aID, - nsCOMArray& aElements) override; + nsCOMArray& aElements) override; NS_IMETHOD AddSubtreeToDocument(nsIContent* aContent) override; NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aContent) override; @@ -262,7 +262,7 @@ protected: nsresult ApplyPersistentAttributes(); nsresult ApplyPersistentAttributesInternal(); nsresult ApplyPersistentAttributesToElements(const nsAString &aID, - nsCOMArray& aElements); + nsCOMArray& aElements); nsresult AddElementToDocumentPre(Element* aElement); @@ -440,7 +440,7 @@ protected: /** * Add attributes from the prototype to the element. */ - nsresult AddAttributes(nsXULPrototypeElement* aPrototype, nsIContent* aElement); + nsresult AddAttributes(nsXULPrototypeElement* aPrototype, Element* aElement); /** * The prototype-script of the current transcluded script that is being @@ -545,13 +545,13 @@ protected: { protected: XULDocument* mDocument; // [WEAK] - nsCOMPtr mOverlay; // [OWNER] + nsCOMPtr mOverlay; // [OWNER] bool mResolved; - nsresult Merge(nsIContent* aTargetNode, nsIContent* aOverlayNode, bool aNotify); + nsresult Merge(Element* aTargetNode, Element* aOverlayNode, bool aNotify); public: - OverlayForwardReference(XULDocument* aDocument, nsIContent* aOverlay) + OverlayForwardReference(XULDocument* aDocument, Element* aOverlay) : mDocument(aDocument), mOverlay(aOverlay), mResolved(false) {} virtual ~OverlayForwardReference(); @@ -598,6 +598,8 @@ protected: Element *aListener, const nsAString &aAttr); + // FIXME: This should probably be renamed, there's nothing guaranteeing that + // aChild is an Element as far as I can tell! static nsresult InsertElement(nsINode* aParent, nsIContent* aChild, bool aNotify); diff --git a/dom/xul/nsIXULDocument.h b/dom/xul/nsIXULDocument.h index 944c481d93f8..72fbd580f390 100644 --- a/dom/xul/nsIXULDocument.h +++ b/dom/xul/nsIXULDocument.h @@ -13,6 +13,12 @@ class nsIXULTemplateBuilder; class nsIContent; +namespace mozilla { +namespace dom { +class Element; +} +} + // 81ba4be5-6cc5-478a-9b08-b3e7ed524455 #define NS_IXULDOCUMENT_IID \ @@ -34,7 +40,8 @@ public: * or 'ref' is aID. The nsCOMArray will be truncated and filled in with * nsIContent pointers. */ - virtual void GetElementsForID(const nsAString& aID, nsCOMArray& aElements) = 0; + virtual void GetElementsForID(const nsAString& aID, + nsCOMArray& aElements) = 0; /** * Notify the XUL document that a subtree has been added diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 36f809c947d3..989c65260c3f 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -280,7 +280,7 @@ NS_NewXULElement(Element** aResult, already_AddRefed&& a } void -NS_TrustedNewXULElement(nsIContent** aResult, +NS_TrustedNewXULElement(Element** aResult, already_AddRefed&& aNodeInfo) { RefPtr ni = aNodeInfo; diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index 2c70ffaaae81..598669a3e47d 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -803,7 +803,7 @@ protected: NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo, mozilla::dom::FromParser aFromParser, const nsAString* aIs); friend void - NS_TrustedNewXULElement(nsIContent** aResult, mozilla::dom::NodeInfo *aNodeInfo); + NS_TrustedNewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo); static already_AddRefed Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo, diff --git a/dom/xul/templates/nsIXULTemplateBuilder.idl b/dom/xul/templates/nsIXULTemplateBuilder.idl index 6c03cd6fb6fd..8a0d90fc04ba 100644 --- a/dom/xul/templates/nsIXULTemplateBuilder.idl +++ b/dom/xul/templates/nsIXULTemplateBuilder.idl @@ -18,6 +18,7 @@ interface nsIDOMDataTransfer; class nsAtom; %} [ptr] native nsAtomPtr(nsAtom); +[ptr] native Element (mozilla::dom::Element); /** * A template builder, given an input source of data, a template, and a @@ -293,7 +294,7 @@ interface nsIXULTemplateBuilder : nsISupports * generated even if it is closed. If false, the element will only * generate its contents if it is open. This behaviour is used with menus. */ - [noscript] void createContents(in nsIContent aElement, + [noscript] void createContents(in Element aElement, in boolean aForceCreation); /** diff --git a/dom/xul/templates/nsXULContentBuilder.cpp b/dom/xul/templates/nsXULContentBuilder.cpp index ba24b23a2a1f..e17dfe0ceb3b 100644 --- a/dom/xul/templates/nsXULContentBuilder.cpp +++ b/dom/xul/templates/nsXULContentBuilder.cpp @@ -72,7 +72,7 @@ class nsXULContentBuilder : public nsXULTemplateBuilder { public: // nsIXULTemplateBuilder interface - NS_IMETHOD CreateContents(nsIContent* aElement, bool aForceCreation) override; + NS_IMETHOD CreateContents(Element* aElement, bool aForceCreation) override; using nsIXULTemplateBuilder::HasGeneratedContent; bool HasGeneratedContent(nsIRDFResource* aResource, @@ -101,10 +101,10 @@ protected: // Implementation methods nsresult - OpenContainer(nsIContent* aElement); + OpenContainer(Element* aElement); nsresult - CloseContainer(nsIContent* aElement); + CloseContainer(Element* aElement); /** * Build content from a template for a given result. This will be called @@ -114,27 +114,27 @@ protected: nsresult BuildContentFromTemplate(nsIContent *aTemplateNode, nsIContent *aResourceNode, - nsIContent *aRealNode, + Element* aRealElement, bool aIsUnique, bool aIsSelfReference, nsIXULTemplateResult* aChild, bool aNotify, nsTemplateMatch* aMatch, - nsIContent** aContainer, + Element** aContainer, int32_t* aNewIndexInContainer); /** * Copy the attributes from the template node to the node generated * from it, performing any substitutions. * - * @param aTemplateNode node within template - * @param aRealNode generated node to set attibutes upon + * @param aTemplateElement element within template + * @param aRealElement generated element to set attibutes upon * @param aResult result to look up variable->value bindings in * @param aNotify true to notify of DOM changes */ nsresult - CopyAttributesToElement(nsIContent* aTemplateNode, - nsIContent* aRealNode, + CopyAttributesToElement(Element* aTemplateElement, + Element* aRealElement, nsIXULTemplateResult* aResult, bool aNotify); @@ -147,9 +147,9 @@ protected: * @param aResult result to look up variable->value bindings in */ nsresult - AddPersistentAttributes(Element* aTemplateNode, + AddPersistentAttributes(Element* aTemplateElement, nsIXULTemplateResult* aResult, - nsIContent* aRealNode); + Element* aRealElement); /** * Recalculate any attributes that have variable references. This will @@ -182,7 +182,7 @@ protected: * @param aForceCreation true to force creation for closed items such as menus */ nsresult - CreateTemplateAndContainerContents(nsIContent* aElement, + CreateTemplateAndContainerContents(Element* aElement, bool aForceCreation); /** @@ -196,7 +196,7 @@ protected: * @param aNotifyAtEnd notify at the end of all DOM changes */ nsresult - CreateContainerContents(nsIContent* aElement, + CreateContainerContents(Element* aElement, nsIXULTemplateResult* aResult, bool aForceCreation, bool aNotify, @@ -212,11 +212,11 @@ protected: * @param aNewIndexInContainer index with container in which content was added */ nsresult - CreateContainerContentsForQuerySet(nsIContent* aElement, + CreateContainerContentsForQuerySet(Element* aElement, nsIXULTemplateResult* aResult, bool aNotify, nsTemplateQuerySet* aQuerySet, - nsIContent** aContainer, + Element** aContainer, int32_t* aNewIndexInContainer); /** @@ -231,11 +231,11 @@ protected: * @param aResult set to the found or created node. */ nsresult - EnsureElementHasGenericChild(nsIContent* aParent, + EnsureElementHasGenericChild(Element* aParent, int32_t aNameSpaceID, nsAtom* aTag, bool aNotify, - nsIContent** aResult); + Element** aResult); bool IsOpen(nsIContent* aElement); @@ -245,7 +245,7 @@ protected: nsresult GetElementsForResult(nsIXULTemplateResult* aResult, - nsCOMArray& aElements); + nsCOMArray& aElements); nsresult CreateElement(int32_t aNameSpaceID, @@ -264,7 +264,7 @@ protected: * @param aNotify true to notify of DOM changes */ nsresult - SetContainerAttrs(nsIContent *aElement, + SetContainerAttrs(Element* aElement, nsIXULTemplateResult* aResult, bool aIgnoreNonContainers, bool aNotify); @@ -282,7 +282,7 @@ protected: */ virtual bool GetInsertionLocations(nsIXULTemplateResult* aOldResult, - nsCOMArray** aLocations) override; + nsCOMArray** aLocations) override; /** * Remove the content associated with aOldResult which no longer matches, @@ -292,7 +292,7 @@ protected: ReplaceMatch(nsIXULTemplateResult* aOldResult, nsTemplateMatch* aNewMatch, nsTemplateRule* aNewMatchRule, - void *aContext) override; + Element* aContext) override; /** * Synchronize a result bindings with the generated content for that @@ -317,7 +317,7 @@ protected: * the result for the generated node. */ nsresult - InsertSortedNode(nsIContent* aContainer, + InsertSortedNode(Element* aContainer, nsIContent* aNode, nsIXULTemplateResult* aResult, bool aNotify); @@ -378,13 +378,13 @@ nsXULContentBuilder::Uninit(bool aIsFinal) nsresult nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, nsIContent *aResourceNode, - nsIContent *aRealNode, + Element* aRealElement, bool aIsUnique, bool aIsSelfReference, nsIXULTemplateResult* aChild, bool aNotify, nsTemplateMatch* aMatch, - nsIContent** aContainer, + Element** aContainer, int32_t* aNewIndexInContainer) { // This is the mother lode. Here is where we grovel through an @@ -401,7 +401,7 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, // not directly used here, but rather passed down to the XUL // sort service to perform container-level sort. // - // |aRealNode| is the element in the "real" content tree to which + // |aRealElement| is the element in the "real" content tree to which // the new elements will be copied. // // |aIsUnique| is set to "true" so long as content has been @@ -448,7 +448,7 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, ("Tags: [Template: %s Resource: %s Real: %s] for id %s", nsAtomCString(aTemplateNode->NodeInfo()->NameAtom()).get(), nsAtomCString(aResourceNode->NodeInfo()->NameAtom()).get(), - nsAtomCString(aRealNode->NodeInfo()->NameAtom()).get(), NS_ConvertUTF16toUTF8(id).get())); + nsAtomCString(aRealElement->NodeInfo()->NameAtom()).get(), NS_ConvertUTF16toUTF8(id).get())); } // Iterate through all of the template children, constructing @@ -456,7 +456,6 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, for (nsIContent* tmplKid = aTemplateNode->GetFirstChild(); tmplKid; tmplKid = tmplKid->GetNextSibling()) { - int32_t nameSpaceID = tmplKid->GetNameSpaceID(); // Check whether this element is the generation element. The generation @@ -511,7 +510,7 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, MOZ_ASSERT_IF(isGenerationElement, tmplKid->IsElement()); - nsAtom *tag = tmplKid->NodeInfo()->NameAtom(); + nsAtom* tag = tmplKid->NodeInfo()->NameAtom(); if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { MOZ_LOG(gXULTemplateLog, LogLevel::Debug, @@ -525,29 +524,41 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, // already existed in the content model. bool realKidAlreadyExisted = false; - nsCOMPtr realKid; + RefPtr realKid; if (isUnique) { // The content is "unique"; that is, we haven't descended // far enough into the template to hit the generation // element yet. |EnsureElementHasGenericChild()| will // conditionally create the element iff it isn't there // already. - rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); - if (NS_FAILED(rv)) - return rv; + // + // FIXME(emilio): The code below doesn't really make much sense if + // tmplKid is not an element. If it ever wasn't, we'd either find a + // child by tag, which doesn't really make sense, and event worse... + // If we didn't find it, we'd create an actual element with a text + // node tag for it. + // + // Both are completely bogus in tons of ways, so just avoid calling + // into EnsureElementHasGenericChild for non-elements, assuming the + // node is already there. + rv = NS_ELEMENT_WAS_THERE; + if (tmplKid->IsElement()) { + rv = EnsureElementHasGenericChild(aRealElement, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); + if (NS_FAILED(rv)) + return rv; + } if (rv == NS_ELEMENT_WAS_THERE) { realKidAlreadyExisted = true; - } - else { + } else { // Potentially remember the index of this element as the first // element that we've generated. Note that we remember // this -before- we recurse! if (aContainer && !*aContainer) { - *aContainer = aRealNode; + *aContainer = aRealElement; NS_ADDREF(*aContainer); - uint32_t indx = aRealNode->GetChildCount(); + uint32_t indx = aRealElement->GetChildCount(); // Since EnsureElementHasGenericChild() added us, make // sure to subtract one for our real index. @@ -566,8 +577,7 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, if (NS_FAILED(rv)) return rv; - } - else if (isGenerationElement) { + } else if (isGenerationElement) { // It's the "resource" element. Create a new element using // the namespace ID and tag from the template element. nsCOMPtr element; @@ -592,9 +602,8 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, // Set up the element's 'container' and 'empty' attributes. SetContainerAttrs(realKid, aChild, true, false); - } - else if (tag == nsGkAtoms::textnode && - nameSpaceID == kNameSpaceID_XUL) { + } else if (tag == nsGkAtoms::textnode && + nameSpaceID == kNameSpaceID_XUL) { // is replaced by text of the // actual value of the 'rdf:resource' attribute for the // given node. @@ -612,14 +621,13 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, content->SetText(value, false); - rv = aRealNode->AppendChildTo(content, aNotify); + rv = aRealElement->AppendChildTo(content, aNotify); if (NS_FAILED(rv)) return rv; // XXX Don't bother remembering text nodes as the // first element we've generated? } - } - else if (tmplKid->IsNodeOfType(nsINode::eTEXT)) { + } else if (tmplKid->IsNodeOfType(nsINode::eTEXT)) { nsCOMPtr tmplTextNode = do_QueryInterface(tmplKid); if (!tmplTextNode) { NS_ERROR("textnode not implementing nsIDOMNode??"); @@ -632,10 +640,9 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, NS_ERROR("failed to clone textnode"); return NS_ERROR_FAILURE; } - rv = aRealNode->AppendChildTo(clonedContent, aNotify); + rv = aRealElement->AppendChildTo(clonedContent, aNotify); if (NS_FAILED(rv)) return rv; - } - else { + } else { // It's just a generic element. Create it! nsCOMPtr element; rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); @@ -647,10 +654,10 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, // Potentially remember the index of this element as the // first element that we've generated. if (aContainer && !*aContainer) { - *aContainer = aRealNode; + *aContainer = aRealElement; NS_ADDREF(*aContainer); - uint32_t indx = aRealNode->GetChildCount(); + uint32_t indx = aRealElement->GetChildCount(); // Since we haven't inserted any content yet, our new // index in the container will be the current count of @@ -663,7 +670,9 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, // template to incrementally build content. mTemplateMap.Put(realKid, tmplKid); - rv = CopyAttributesToElement(tmplKid, realKid, aChild, false); + rv = CopyAttributesToElement(tmplKid->AsElement(), + realKid, aChild, + false); if (NS_FAILED(rv)) return rv; // Add any persistent attributes @@ -703,10 +712,10 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, rv = NS_ERROR_UNEXPECTED; if (isGenerationElement) - rv = InsertSortedNode(aRealNode, realKid, aChild, aNotify); + rv = InsertSortedNode(aRealElement, realKid, aChild, aNotify); if (NS_FAILED(rv)) { - rv = aRealNode->AppendChildTo(realKid, aNotify); + rv = aRealElement->AppendChildTo(realKid, aNotify); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); } } @@ -717,18 +726,18 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, } nsresult -nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, - nsIContent* aRealNode, +nsXULContentBuilder::CopyAttributesToElement(Element* aTemplateElement, + Element* aRealElement, nsIXULTemplateResult* aResult, bool aNotify) { nsresult rv; // Copy all attributes from the template to the new element - uint32_t numAttribs = aTemplateNode->GetAttrCount(); + uint32_t numAttribs = aTemplateElement->GetAttrCount(); for (uint32_t attr = 0; attr < numAttribs; attr++) { - const nsAttrName* name = aTemplateNode->GetAttrNameAt(attr); + const nsAttrName* name = aTemplateElement->GetAttrNameAt(attr); int32_t attribNameSpaceID = name->NamespaceID(); // Hold a strong reference here so that the atom doesn't go away // during UnsetAttr. @@ -737,7 +746,7 @@ nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, // XXXndeakin ignore namespaces until bug 321182 is fixed if (attribName != nsGkAtoms::id && attribName != nsGkAtoms::uri) { nsAutoString attribValue; - aTemplateNode->GetAttr(attribNameSpaceID, attribName, attribValue); + aTemplateElement->GetAttr(attribNameSpaceID, attribName, attribValue); if (!attribValue.IsEmpty()) { nsAutoString value; rv = SubstituteText(aResult, attribValue, value); @@ -747,14 +756,14 @@ nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, // if the string is empty after substitutions, remove the // attribute if (!value.IsEmpty()) { - rv = aRealNode->SetAttr(attribNameSpaceID, + rv = aRealElement->SetAttr(attribNameSpaceID, attribName, name->GetPrefix(), value, aNotify); } else { - rv = aRealNode->UnsetAttr(attribNameSpaceID, + rv = aRealElement->UnsetAttr(attribNameSpaceID, attribName, aNotify); } @@ -771,7 +780,7 @@ nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, nsresult nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, nsIXULTemplateResult* aResult, - nsIContent* aRealNode) + Element* aRealElement) { if (!mRoot) return NS_OK; @@ -837,8 +846,8 @@ nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, rv = value->GetValueConst(&valueStr); NS_ENSURE_SUCCESS(rv, rv); - rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), - false); + rv = aRealElement->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), + false); NS_ENSURE_SUCCESS(rv, rv); } @@ -847,15 +856,17 @@ nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, nsresult nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode, - nsIContent* aRealElement, + nsIContent* aRealNode, nsIXULTemplateResult* aResult) { // check all attributes on the template node; if they reference a resource, // update the equivalent attribute on the content node nsresult rv; - rv = CopyAttributesToElement(aTemplateNode, aRealElement, aResult, true); - if (NS_FAILED(rv)) - return rv; + if (aTemplateNode->IsElement() && aRealNode->IsElement()) { + rv = CopyAttributesToElement(aTemplateNode->AsElement(), aRealNode->AsElement(), aResult, true); + if (NS_FAILED(rv)) + return rv; + } uint32_t count = aTemplateNode->GetChildCount(); @@ -865,7 +876,7 @@ nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode, if (! tmplKid) break; - nsIContent *realKid = aRealElement->GetChildAt(loop); + nsIContent *realKid = aRealNode->GetChildAt(loop); if (! realKid) break; @@ -916,7 +927,7 @@ nsXULContentBuilder::RemoveMember(nsIContent* aContent) } nsresult -nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement, +nsXULContentBuilder::CreateTemplateAndContainerContents(Element* aElement, bool aForceCreation) { // Generate both 1) the template content for the current element, @@ -966,7 +977,7 @@ nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement, } nsresult -nsXULContentBuilder::CreateContainerContents(nsIContent* aElement, +nsXULContentBuilder::CreateContainerContents(Element* aElement, nsIXULTemplateResult* aResult, bool aForceCreation, bool aNotify, @@ -1021,7 +1032,7 @@ nsXULContentBuilder::CreateContainerContents(nsIContent* aElement, } int32_t newIndexInContainer = -1; - nsIContent* container = nullptr; + Element* container = nullptr; int32_t querySetCount = mQuerySets.Length(); @@ -1049,11 +1060,11 @@ nsXULContentBuilder::CreateContainerContents(nsIContent* aElement, } nsresult -nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement, +nsXULContentBuilder::CreateContainerContentsForQuerySet(Element* aElement, nsIXULTemplateResult* aResult, bool aNotify, nsTemplateQuerySet* aQuerySet, - nsIContent** aContainer, + Element** aContainer, int32_t* aNewIndexInContainer) { if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { @@ -1202,11 +1213,11 @@ nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement, } nsresult -nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent, +nsXULContentBuilder::EnsureElementHasGenericChild(Element* parent, int32_t nameSpaceID, nsAtom* tag, bool aNotify, - nsIContent** result) + Element** result) { nsresult rv; @@ -1311,7 +1322,7 @@ nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement) nsresult nsXULContentBuilder::GetElementsForResult(nsIXULTemplateResult* aResult, - nsCOMArray& aElements) + nsCOMArray& aElements) { // if the root has been removed from the document, just return // since there won't be any generated content any more @@ -1345,7 +1356,7 @@ nsXULContentBuilder::CreateElement(int32_t aNameSpaceID, } nsresult -nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement, +nsXULContentBuilder::SetContainerAttrs(Element* aElement, nsIXULTemplateResult* aResult, bool aIgnoreNonContainers, bool aNotify) @@ -1390,7 +1401,7 @@ nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement, // NS_IMETHODIMP -nsXULContentBuilder::CreateContents(nsIContent* aElement, bool aForceCreation) +nsXULContentBuilder::CreateContents(Element* aElement, bool aForceCreation) { NS_PRECONDITION(aElement != nullptr, "null ptr"); if (! aElement) @@ -1436,7 +1447,7 @@ nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource, return false; } - nsCOMArray elements; + nsCOMArray elements; xuldoc->GetElementsForID(refID, elements); uint32_t cnt = elements.Count(); @@ -1529,7 +1540,7 @@ nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode) bool nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, - nsCOMArray** aLocations) + nsCOMArray** aLocations) { *aLocations = nullptr; @@ -1542,7 +1553,7 @@ nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, if (! xuldoc) return false; - *aLocations = new nsCOMArray; + *aLocations = new nsCOMArray; NS_ENSURE_TRUE(*aLocations, false); xuldoc->GetElementsForID(ref, **aLocations); @@ -1578,14 +1589,13 @@ nsresult nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, nsTemplateMatch* aNewMatch, nsTemplateRule* aNewMatchRule, - void *aContext) + Element* aContext) { nsresult rv; - nsIContent* content = static_cast(aContext); // update the container attributes for the match - if (content) { + if (aContext) { nsAutoString ref; if (aNewMatch) rv = aNewMatch->mResult->GetBindingFor(mRefVariable, ref); @@ -1601,12 +1611,12 @@ nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, return rv; if (refResult) - SetContainerAttrs(content, refResult, false, true); + SetContainerAttrs(aContext, refResult, false, true); } } if (aOldResult) { - nsCOMArray elements; + nsCOMArray elements; rv = GetElementsForResult(aOldResult, elements); if (NS_FAILED(rv)) return rv; @@ -1618,7 +1628,7 @@ nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, nsTemplateMatch* match; if (mContentSupportMap.Get(child, &match)) { - if (content == match->GetContainer()) + if (aContext == match->GetContainer()) RemoveMember(child); } } @@ -1626,7 +1636,7 @@ nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, if (aNewMatch) { nsCOMPtr action = aNewMatchRule->GetAction(); - return BuildContentFromTemplate(action, content, content, true, + return BuildContentFromTemplate(action, aContext, aContext, true, mRefVariable == aNewMatchRule->GetMemberVariable(), aNewMatch->mResult, true, aNewMatch, nullptr, nullptr); @@ -1639,13 +1649,13 @@ nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, nsresult nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) { - nsCOMArray elements; + nsCOMArray elements; GetElementsForResult(aResult, elements); uint32_t cnt = elements.Count(); for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { - nsCOMPtr element = elements.SafeObjectAt(i); + nsCOMPtr element = elements.SafeObjectAt(i); nsTemplateMatch* match; if (! mContentSupportMap.Get(element, &match)) @@ -1671,7 +1681,7 @@ nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) // nsresult -nsXULContentBuilder::OpenContainer(nsIContent* aElement) +nsXULContentBuilder::OpenContainer(Element* aElement) { if (aElement != mRoot) { if (mFlags & eDontRecurse) @@ -1707,7 +1717,7 @@ nsXULContentBuilder::OpenContainer(nsIContent* aElement) } nsresult -nsXULContentBuilder::CloseContainer(nsIContent* aElement) +nsXULContentBuilder::CloseContainer(Element* aElement) { return NS_OK; } @@ -1793,7 +1803,7 @@ nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult, } nsresult -nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer, +nsXULContentBuilder::InsertSortedNode(Element* aContainer, nsIContent* aNode, nsIXULTemplateResult* aResult, bool aNotify) diff --git a/dom/xul/templates/nsXULContentUtils.cpp b/dom/xul/templates/nsXULContentUtils.cpp index dc0730d8ad8c..fb7e72736525 100644 --- a/dom/xul/templates/nsXULContentUtils.cpp +++ b/dom/xul/templates/nsXULContentUtils.cpp @@ -126,15 +126,15 @@ nsresult nsXULContentUtils::FindChildByTag(nsIContent* aElement, int32_t aNameSpaceID, nsAtom* aTag, - nsIContent** aResult) + Element** aResult) { for (nsIContent* child = aElement->GetFirstChild(); child; child = child->GetNextSibling()) { - if (child->NodeInfo()->Equals(aTag, aNameSpaceID)) { - NS_ADDREF(*aResult = child); - + if (child->IsElement() && + child->NodeInfo()->Equals(aTag, aNameSpaceID)) { + NS_ADDREF(*aResult = child->AsElement()); return NS_OK; } } diff --git a/dom/xul/templates/nsXULContentUtils.h b/dom/xul/templates/nsXULContentUtils.h index 367cc729065c..3cc63458c7b3 100644 --- a/dom/xul/templates/nsXULContentUtils.h +++ b/dom/xul/templates/nsXULContentUtils.h @@ -23,6 +23,12 @@ class nsIRDFLiteral; class nsIRDFService; class nsICollation; +namespace mozilla { +namespace dom { +class Element; +} +} + // errors to pass to LogTemplateError #define ERROR_TEMPLATE_INVALID_QUERYPROCESSOR \ "querytype attribute doesn't specify a valid query processor" @@ -103,7 +109,7 @@ public: FindChildByTag(nsIContent *aElement, int32_t aNameSpaceID, nsAtom* aTag, - nsIContent **aResult); + mozilla::dom::Element** aResult); static nsresult FindChildByResource(nsIContent* aElement, diff --git a/dom/xul/templates/nsXULSortService.cpp b/dom/xul/templates/nsXULSortService.cpp index 6dbe5df19903..0b77bacc3a38 100644 --- a/dom/xul/templates/nsXULSortService.cpp +++ b/dom/xul/templates/nsXULSortService.cpp @@ -28,26 +28,26 @@ NS_IMPL_ISUPPORTS(XULSortServiceImpl, nsIXULSortService) void -XULSortServiceImpl::SetSortHints(nsIContent *aNode, nsSortState* aSortState) +XULSortServiceImpl::SetSortHints(Element* aElement, nsSortState* aSortState) { // set sort and sortDirection attributes when is sort is done - aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sort, - aSortState->sort, true); + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::sort, + aSortState->sort, true); nsAutoString direction; if (aSortState->direction == nsSortState_descending) direction.AssignLiteral("descending"); else if (aSortState->direction == nsSortState_ascending) direction.AssignLiteral("ascending"); - aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, - direction, true); + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, + direction, true); // for trees, also set the sort info on the currently sorted column - if (aNode->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { + if (aElement->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { if (aSortState->sortKeys.Length() >= 1) { nsAutoString sortkey; aSortState->sortKeys[0]->ToString(sortkey); - SetSortColumnHints(aNode, sortkey, direction); + SetSortColumnHints(aElement, sortkey, direction); } } } @@ -71,17 +71,18 @@ XULSortServiceImpl::SetSortColumnHints(nsIContent *content, if (value.IsEmpty()) child->GetAttr(kNameSpaceID_None, nsGkAtoms::resource, value); if (value == sortResource) { - child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, - NS_LITERAL_STRING("true"), true); - child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, - sortDirection, true); + child->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, + NS_LITERAL_STRING("true"), true); + + child->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, + sortDirection, true); // Note: don't break out of loop; want to set/unset // attribs on ALL sort columns } else if (!value.IsEmpty()) { - child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, - true); - child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, - true); + child->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, + true); + child->AsElement()->UnsetAttr(kNameSpaceID_None, + nsGkAtoms::sortDirection, true); } } } @@ -109,7 +110,7 @@ XULSortServiceImpl::GetItemsToSort(nsIContent *aContainer, // if there is no template builder, just get the children. For trees, // get the treechildren element as use that as the parent - nsCOMPtr treechildren; + RefPtr treechildren; if (aContainer->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { nsXULContentUtils::FindChildByTag(aContainer, kNameSpaceID_XUL, @@ -323,8 +324,8 @@ XULSortServiceImpl::InvertSortInfo(nsTArray& aData, } nsresult -XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement, - nsIContent* aContainer, +XULSortServiceImpl::InitializeSortState(Element* aRootElement, + Element* aContainer, const nsAString& aSortKey, const nsAString& aSortHints, nsSortState* aSortState) @@ -467,7 +468,7 @@ XULSortServiceImpl::Sort(nsIDOMNode* aNode, const nsAString& aSortHints) { // get root content node - nsCOMPtr sortNode = do_QueryInterface(aNode); + nsCOMPtr sortNode = do_QueryInterface(aNode); if (!sortNode) return NS_ERROR_FAILURE; diff --git a/dom/xul/templates/nsXULSortService.h b/dom/xul/templates/nsXULSortService.h index 34f8143e58fd..4da6ae58c8a4 100644 --- a/dom/xul/templates/nsXULSortService.h +++ b/dom/xul/templates/nsXULSortService.h @@ -96,7 +96,7 @@ public: * Set sort and sortDirection attributes when a sort is done. */ void - SetSortHints(nsIContent *aNode, nsSortState* aSortState); + SetSortHints(mozilla::dom::Element* aElement, nsSortState* aSortState); /** * Set sortActive and sortDirection attributes on a tree column when a sort @@ -156,8 +156,8 @@ public: * @param aSortState structure filled in with sort data */ static nsresult - InitializeSortState(nsIContent* aRootElement, - nsIContent* aContainer, + InitializeSortState(mozilla::dom::Element* aRootElement, + mozilla::dom::Element* aContainer, const nsAString& aSortKey, const nsAString& aSortDirection, nsSortState* aSortState); diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp index 17bb817e38a3..b4fcd1985c6d 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.cpp +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -476,7 +476,7 @@ nsXULTemplateBuilder::Init() } NS_IMETHODIMP -nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation) +nsXULTemplateBuilder::CreateContents(Element* aElement, bool aForceCreation) { return NS_OK; } @@ -575,7 +575,7 @@ nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult, // will be false if the result applies to content that is in a closed menu // or treeitem for example. - nsAutoPtr > insertionPoints; + nsAutoPtr > insertionPoints; bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult, getter_Transfers(insertionPoints)); if (! mayReplace) @@ -632,7 +632,7 @@ nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult, // that container uint32_t count = insertionPoints->Count(); for (uint32_t t = 0; t < count; t++) { - nsCOMPtr insertionPoint = insertionPoints->SafeObjectAt(t); + nsCOMPtr insertionPoint = insertionPoints->SafeObjectAt(t); if (insertionPoint) { rv = UpdateResultInContainer(aOldResult, aNewResult, queryset, oldId, newId, insertionPoint); @@ -657,7 +657,7 @@ nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult, nsTemplateQuerySet* aQuerySet, nsIRDFResource* aOldId, nsIRDFResource* aNewId, - nsIContent* aInsertionPoint) + Element* aInsertionPoint) { // This method takes a result that no longer applies (aOldResult) and // replaces it with a new result (aNewResult). Either may be null @@ -1704,12 +1704,11 @@ nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, bool nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent) { - return aContent->NodeInfo()->Equals(nsGkAtoms::_template, - kNameSpaceID_XUL); + return aContent->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL); } nsresult -nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) +nsXULTemplateBuilder::GetTemplateRoot(Element** aResult) { NS_PRECONDITION(mRoot != nullptr, "not initialized"); if (! mRoot) @@ -1735,7 +1734,7 @@ nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) domDoc->GetElementById(templateID, getter_AddRefs(domElement)); if (domElement) { - nsCOMPtr content = do_QueryInterface(domElement); + nsCOMPtr content = do_QueryInterface(domElement); NS_ENSURE_STATE(content && !nsContentUtils::ContentIsDescendantOf(mRoot, content)); @@ -1751,7 +1750,7 @@ nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) child = child->GetNextSibling()) { if (IsTemplateElement(child)) { - NS_ADDREF(*aResult = child); + NS_ADDREF(*aResult = child->AsElement()); return NS_OK; } } @@ -1764,7 +1763,7 @@ nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) FlattenedChildIterator iter(mRoot); for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { if (IsTemplateElement(child)) { - NS_ADDREF(*aResult = child); + NS_ADDREF(*aResult = child->AsElement()); return NS_OK; } } @@ -1776,7 +1775,7 @@ nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) nsresult nsXULTemplateBuilder::CompileQueries() { - nsCOMPtr tmpl; + nsCOMPtr tmpl; GetTemplateRoot(getter_AddRefs(tmpl)); if (! tmpl) return NS_OK; @@ -1867,7 +1866,7 @@ nsXULTemplateBuilder::CompileQueries() } nsresult -nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, +nsXULTemplateBuilder::CompileTemplate(Element* aTemplate, nsTemplateQuerySet* aQuerySet, bool aIsQuerySet, int32_t* aPriority, @@ -1915,7 +1914,9 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, hasQuerySet = true; - rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate); + // Known to be a . + rv = CompileTemplate(rulenode->AsElement(), aQuerySet, true, + aPriority, aCanUseTemplate); if (NS_FAILED(rv)) return rv; } @@ -1925,7 +1926,7 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, continue; if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) { - nsCOMPtr action; + RefPtr action; nsXULContentUtils::FindChildByTag(rulenode, kNameSpaceID_XUL, nsGkAtoms::action, @@ -1959,20 +1960,22 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, } if (aQuerySet->mCompiledQuery) { - rv = CompileExtendedQuery(rulenode, action, memberVariable, + // It's an element (we test it for , plus it + // has `action` as a kid). + rv = CompileExtendedQuery(rulenode->AsElement(), + action, memberVariable, aQuerySet); if (NS_FAILED(rv)) return rv; *aCanUseTemplate = true; } - } - else { + } else { // backwards-compatible RDF template syntax where there is // an node but no node. In this case, // use the conditions as if it was the query. - nsCOMPtr conditions; + RefPtr conditions; nsXULContentUtils::FindChildByTag(rulenode, kNameSpaceID_XUL, nsGkAtoms::conditions, @@ -2006,7 +2009,10 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, return rv; if (aQuerySet->mCompiledQuery) { - rv = CompileExtendedQuery(rulenode, action, memberVariable, + // Known to be a , plus known to have + // kids. + rv = CompileExtendedQuery(rulenode->AsElement(), + action, memberVariable, aQuerySet); if (NS_FAILED(rv)) return rv; @@ -2015,8 +2021,7 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, } } } - } - else { + } else { if (hasQuery) continue; @@ -2031,21 +2036,21 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, hasQuerySet = true; - rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate); + // Known to be a . + rv = CompileSimpleQuery(rulenode->AsElement(), aQuerySet, + aCanUseTemplate); if (NS_FAILED(rv)) return rv; } hasRule = true; - } - else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) { + } else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) { if (hasQuery) continue; aQuerySet->mQueryNode = rulenode; hasQuery = true; - } - else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) { + } else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) { // the query must appear before the action if (! hasQuery) continue; @@ -2094,7 +2099,7 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, } nsresult -nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, +nsXULTemplateBuilder::CompileExtendedQuery(Element* aRuleElement, nsIContent* aActionElement, nsAtom* aMemberVariable, nsTemplateQuerySet* aQuerySet) @@ -2107,7 +2112,7 @@ nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, if (! rule) return NS_ERROR_OUT_OF_MEMORY; - nsCOMPtr conditions; + RefPtr conditions; nsXULContentUtils::FindChildByTag(aRuleElement, kNameSpaceID_XUL, nsGkAtoms::conditions, @@ -2127,7 +2132,7 @@ nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, rule->SetVars(mRefVariable, aMemberVariable); // If we've got bindings, add 'em. - nsCOMPtr bindings; + RefPtr bindings; nsXULContentUtils::FindChildByTag(aRuleElement, kNameSpaceID_XUL, nsGkAtoms::bindings, @@ -2170,7 +2175,7 @@ void nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsAtom** aTag) { // check for a tag - nsCOMPtr content; + RefPtr content; nsXULContentUtils::FindChildByTag(aQueryElement, kNameSpaceID_XUL, nsGkAtoms::content, @@ -2200,7 +2205,7 @@ nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsAtom** a } nsresult -nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement, +nsXULTemplateBuilder::CompileSimpleQuery(Element* aRuleElement, nsTemplateQuerySet* aQuerySet, bool* aCanUseTemplate) { @@ -2457,12 +2462,12 @@ nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule, nsresult nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule, - nsIContent* aElement) + Element* aElement) { // Crawl the content tree of a "simple" rule, adding a variable // assignment for any attribute whose value is "rdf:". - AutoTArray elements; + AutoTArray elements; if (elements.AppendElement(aElement) == nullptr) return NS_ERROR_OUT_OF_MEMORY; @@ -2470,7 +2475,7 @@ nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule, while (elements.Length()) { // Pop the next element off the stack uint32_t i = elements.Length() - 1; - nsIContent* element = elements[i]; + Element* element = elements[i]; elements.RemoveElementAt(i); // Iterate through its attributes, looking for substitutions @@ -2495,8 +2500,10 @@ nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule, for (nsIContent* child = element->GetLastChild(); child; child = child->GetPreviousSibling()) { + if (!child->IsElement()) + continue; - if (!elements.AppendElement(child)) + if (!elements.AppendElement(child->AsElement())) return NS_ERROR_OUT_OF_MEMORY; } } diff --git a/dom/xul/templates/nsXULTemplateBuilder.h b/dom/xul/templates/nsXULTemplateBuilder.h index 4a170f53403b..fbcd2584d71c 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.h +++ b/dom/xul/templates/nsXULTemplateBuilder.h @@ -159,7 +159,7 @@ public: nsTemplateQuerySet* aQuerySet, nsIRDFResource* aOldId, nsIRDFResource* aNewId, - nsIContent* aInsertionPoint); + Element* aInsertionPoint); nsresult ComputeContainmentProperties(); @@ -193,7 +193,7 @@ public: * Find the