From 778a486dc157913b75beb78936625022f5a93559 Mon Sep 17 00:00:00 2001 From: Paul Bone Date: Fri, 25 Aug 2017 15:03:24 +1000 Subject: [PATCH 01/51] Bug 1392511 - Report the correct information for used bytes. r=jonco Only use the promotion rate to make pre tenuring and nursery size decisions (now that it is calculated correctly and not under-estimated) if the nursery is at least 90% full. --- js/src/gc/Nursery.cpp | 70 +++++++++++++++++++++++++++++-------------- js/src/gc/Nursery.h | 20 +++++++++---- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 4c19ffb1a06d..4a4cfa063577 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -465,6 +465,22 @@ js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery) { } +inline float +js::Nursery::calcPromotionRate(bool *validForTenuring) const { + float used = float(previousGC.nurseryUsedBytes); + float capacity = float(previousGC.nurseryCapacity); + float tenured = float(previousGC.tenuredBytes); + + if (validForTenuring) { + /* + * We can only use promotion rates if they're likely to be valid, + * they're only valid if the nursury was at least 90% full. + */ + *validForTenuring = used > capacity * 0.9f; + } + return tenured / used; +} + void js::Nursery::renderProfileJSON(JSONPrinter& json) const { @@ -490,8 +506,7 @@ js::Nursery::renderProfileJSON(JSONPrinter& json) const json.property("reason", JS::gcreason::ExplainReason(previousGC.reason)); json.property("bytes_tenured", previousGC.tenuredBytes); - json.floatProperty("promotion_rate", - 100.0 * previousGC.tenuredBytes / double(previousGC.nurseryUsedBytes), 2); + json.floatProperty("promotion_rate", calcPromotionRate(nullptr), 0); json.property("nursery_bytes", previousGC.nurseryUsedBytes); json.property("new_nursery_bytes", numChunks() * ChunkSize); @@ -609,14 +624,13 @@ js::Nursery::collect(JS::gcreason::Reason reason) JS::AutoSuppressGCAnalysis nogc; TenureCountCache tenureCounts; - double promotionRate = 0; previousGC.reason = JS::gcreason::NO_REASON; if (!isEmpty()) - promotionRate = doCollection(reason, tenureCounts); + doCollection(reason, tenureCounts); // Resize the nursery. startProfile(ProfileKey::Resize); - maybeResizeNursery(reason, promotionRate); + maybeResizeNursery(reason); endProfile(ProfileKey::Resize); // If we are promoting the nursery, or exhausted the store buffer with @@ -624,16 +638,20 @@ js::Nursery::collect(JS::gcreason::Reason reason) // the nursery is full, look for object groups that are getting promoted // excessively and try to pretenure them. startProfile(ProfileKey::Pretenure); + bool validPromotionRate; + const float promotionRate = calcPromotionRate(&validPromotionRate); uint32_t pretenureCount = 0; - if (promotionRate > 0.8 || IsFullStoreBufferReason(reason)) { - JSContext* cx = TlsContext.get(); - for (auto& entry : tenureCounts.entries) { - if (entry.count >= 3000) { - ObjectGroup* group = entry.group; - if (group->canPreTenure()) { - AutoCompartment ac(cx, group); - group->setShouldPreTenure(cx); - pretenureCount++; + if (validPromotionRate) { + if (promotionRate > 0.8 || IsFullStoreBufferReason(reason)) { + JSContext* cx = TlsContext.get(); + for (auto& entry : tenureCounts.entries) { + if (entry.count >= 3000) { + ObjectGroup* group = entry.group; + if (group->canPreTenure()) { + AutoCompartment ac(cx, group); + group->setShouldPreTenure(cx); + pretenureCount++; + } } } } @@ -685,7 +703,7 @@ js::Nursery::collect(JS::gcreason::Reason reason) } } -double +void js::Nursery::doCollection(JS::gcreason::Reason reason, TenureCountCache& tenureCounts) { @@ -696,7 +714,8 @@ js::Nursery::doCollection(JS::gcreason::Reason reason, AutoDisableProxyCheck disableStrictProxyChecking; mozilla::DebugOnly oomUnsafeRegion; - size_t initialNurserySize = spaceToEnd(); + const size_t initialNurseryCapacity = spaceToEnd(); + const size_t initialNurseryUsedBytes = initialNurseryCapacity - freeSpace(); // Move objects pointed to by roots from the nursery to the major heap. TenuringTracer mover(rt, this); @@ -793,11 +812,9 @@ js::Nursery::doCollection(JS::gcreason::Reason reason, endProfile(ProfileKey::CheckHashTables); previousGC.reason = reason; - previousGC.nurseryUsedBytes = initialNurserySize; + previousGC.nurseryCapacity = initialNurseryCapacity; + previousGC.nurseryUsedBytes = initialNurseryUsedBytes; previousGC.tenuredBytes = mover.tenuredSize; - - // Calculate and return the promotion rate. - return mover.tenuredSize / double(initialNurserySize); } void @@ -923,7 +940,7 @@ js::Nursery::setStartPosition() } void -js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate) +js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason) { static const double GrowThreshold = 0.05; static const double ShrinkThreshold = 0.01; @@ -942,20 +959,27 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRat return; #endif + bool canUsePromotionRate; + const float promotionRate = calcPromotionRate(&canUsePromotionRate); + newMaxNurseryChunks = runtime()->gc.tunables.gcMaxNurseryBytes() >> ChunkShift; if (newMaxNurseryChunks != maxNurseryChunks_) { maxNurseryChunks_ = newMaxNurseryChunks; /* The configured maximum nursery size is changing */ - int extraChunks = numChunks() - newMaxNurseryChunks; + const int extraChunks = numChunks() - newMaxNurseryChunks; if (extraChunks > 0) { /* We need to shrink the nursery */ shrinkAllocableSpace(extraChunks); - previousPromotionRate_ = promotionRate; + if (canUsePromotionRate) + previousPromotionRate_ = promotionRate; return; } } + if (!canUsePromotionRate) + return; + if (promotionRate > GrowThreshold) growAllocableSpace(); else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold) diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index cd87d51c7da3..b2b7355e0af0 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -316,7 +316,7 @@ class Nursery unsigned maxNurseryChunks_; /* Promotion rate for the previous minor collection. */ - double previousPromotionRate_; + float previousPromotionRate_; /* Report minor collections taking at least this long, if enabled. */ mozilla::TimeDuration profileThreshold_; @@ -355,10 +355,20 @@ class Nursery struct { JS::gcreason::Reason reason; - uint64_t nurseryUsedBytes; - uint64_t tenuredBytes; + size_t nurseryCapacity; + size_t nurseryUsedBytes; + size_t tenuredBytes; } previousGC; + /* + * Calculate the promotion rate of the most recent minor GC. + * The valid_for_tenuring parameter is used to return whether this + * promotion rate is accurate enough (the nursery was full enough) to be + * used for tenuring and other decisions. + */ + float + calcPromotionRate(bool *validForTenuring) const; + /* * The set of externally malloced buffers potentially kept live by objects * stored in the nursery. Any external buffers that do not belong to a @@ -438,7 +448,7 @@ class Nursery /* Common internal allocator function. */ void* allocate(size_t size); - double doCollection(JS::gcreason::Reason reason, + void doCollection(JS::gcreason::Reason reason, gc::TenureCountCache& tenureCounts); /* @@ -466,7 +476,7 @@ class Nursery void sweepDictionaryModeObjects(); /* Change the allocable space provided by the nursery. */ - void maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate); + void maybeResizeNursery(JS::gcreason::Reason reason); void growAllocableSpace(); void shrinkAllocableSpace(unsigned removeNumChunks); void minimizeAllocableSpace(); From 75495471b98ed05254699f0fb82e98c1832888e9 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 30 Aug 2017 15:09:32 -0400 Subject: [PATCH 02/51] Bug 1392755 - Use a normal thread instead of LazyIdleThread for QuotaManager IO thread. r=jvarga QuotaManager is now a hip, popular subsystem used by many consumers, many of which are now latency sensitive. There is no longer any meaningful benefit to using LazyIdleThread, but there are latency downsides. Also, LazyIdleThread has some bugs at shutdown and complications for callers that use NS_GetCurrentThread(). So, begone LazyIdleThread! --HG-- extra : rebase_source : d6f3c1835bf2ef386b8bb4d24678b3610988282c --- dom/quota/ActorsParent.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index ad79c71d6c6f..982503a81094 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -40,7 +40,6 @@ #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/IntegerRange.h" #include "mozilla/Mutex.h" -#include "mozilla/LazyIdleThread.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" @@ -3593,11 +3592,10 @@ QuotaManager::Init(const nsAString& aBasePath) return rv; } - // Make a lazy thread for any IO we need (like clearing or enumerating the - // contents of storage directories). - mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, - NS_LITERAL_CSTRING("Storage I/O"), - LazyIdleThread::ManualShutdown); + rv = NS_NewNamedThread("QuotaManager IO", getter_AddRefs(mIOThread)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } // Make a timer here to avoid potential failures later. We don't actually // initialize the timer until shutdown. From cf9abbf1f4151c6588819fd04e7a89368b5dd033 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Thu, 31 Aug 2017 16:27:26 +0000 Subject: [PATCH 03/51] Bug 1390942: Recomposite areas of a container where child layers became visible. r=mattwoodrow MozReview-Commit-ID: E9qMmwvcGKU --- gfx/layers/LayerTreeInvalidation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 7cb6421b4235..fb966479a1ce 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -315,6 +315,11 @@ public: nsIntRegion& aOutRegion, NotifySubDocInvalidationFunc aCallback) { + if (mLayer->AsHostLayer() && !mLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) { + IntRect result = NewTransformedBoundsForLeaf(); + result = result.Union(OldTransformedBoundsForLeaf()); + aOutRegion = result; + } return true; } From 178ae7e0b07343c901e2c85fa6a02ac096dd0a7f Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Thu, 31 Aug 2017 16:14:37 +0200 Subject: [PATCH 04/51] Bug 1353762 - Revert RTCCertificate changes and remove unnecessary null-check r=keeler --- dom/base/SubtleCrypto.cpp | 2 +- dom/media/webrtc/RTCCertificate.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/dom/base/SubtleCrypto.cpp b/dom/base/SubtleCrypto.cpp index 410ead0554c4..56dfa8bd0bbb 100644 --- a/dom/base/SubtleCrypto.cpp +++ b/dom/base/SubtleCrypto.cpp @@ -58,7 +58,7 @@ SubtleCrypto::RecordTelemetryOnce() { RefPtr task = \ WebCryptoTask::Create ## Operation ## Task(__VA_ARGS__); \ if (!task) { \ - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); \ + aRv.Throw(NS_ERROR_NULL_POINTER); \ return nullptr; \ } \ task->DispatchWithPromise(p); \ diff --git a/dom/media/webrtc/RTCCertificate.cpp b/dom/media/webrtc/RTCCertificate.cpp index 20eb0abf8003..628e46adeb9f 100644 --- a/dom/media/webrtc/RTCCertificate.cpp +++ b/dom/media/webrtc/RTCCertificate.cpp @@ -280,10 +280,6 @@ RTCCertificate::GenerateCertificate( RefPtr task = new GenerateRTCCertificateTask(global, aGlobal.Context(), aOptions, usages, expires); - if (!task) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } task->DispatchWithPromise(p); return p.forget(); } From 8e318ae1c0cd8c50e385f96a194dceb58930b484 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Thu, 31 Aug 2017 13:25:52 +0100 Subject: [PATCH 05/51] Bug 1395469 - Check discarded ChromeWindow in staleness test. r=me HTML elements' ownerGlobal is a WindowProxy that indirects operations to the current browsing context. However, for XUL elements this global is ChromeWindow which gets reset to null when the window is discarded. For the same reason, since win indirects to the current browsing context's associated window, we need to test that the element's node document is not the active document. Finally test that its shadow-including root is a document. MozReview-Commit-ID: EYh18P8DbcN --- testing/marionette/element.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/testing/marionette/element.js b/testing/marionette/element.js index cfd64190c80f..ca07d6a0ce8a 100644 --- a/testing/marionette/element.js +++ b/testing/marionette/element.js @@ -624,8 +624,7 @@ element.generateUUID = function() { * Determines if el is stale. * * A stale element is an element no longer attached to the DOM or which - * ownerDocument is not the same as {@link WindowProxy}'s - * document. + * node document is not the active document. * * @param {Element} el * DOM element to check for staleness. @@ -636,8 +635,11 @@ element.generateUUID = function() { element.isStale = function(el) { let doc = el.ownerDocument; let win = doc.defaultView; - let sameDoc = el.ownerDocument === win.document; - return !sameDoc || !el.isConnected; + + if (!win || el.ownerDocument !== win.document) { + return true; + } + return !el.isConnected; }; /** From 9807a0448deae689348cb9e0c3a05692b9b6781b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 31 Aug 2017 01:28:20 -0400 Subject: [PATCH 06/51] Bug 1395442 - Remove nsHTMLDocument::mFormControls because it's never set; r=smaug --- dom/base/nsContentUtils.cpp | 27 ++++++++++----------------- dom/html/nsHTMLDocument.cpp | 2 -- dom/html/nsHTMLDocument.h | 6 ------ 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index b5520431cb1d..ac4263a1bcd3 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3054,22 +3054,15 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent, /* aDeep = */ true, /* aLiveList = */ false); } - RefPtr htmlFormControls = htmlDoc->GetExistingFormControls(); - if (!htmlFormControls) { - // If the document doesn't have an existing form controls content list, - // create a new one, but avoid creating a live list since we only need to - // use the list here and it doesn't need to listen to mutation events. - htmlFormControls = new nsContentList(aDocument, - nsHTMLDocument::MatchFormControls, - nullptr, nullptr, - /* aDeep = */ true, - /* aMatchAtom = */ nullptr, - /* aMatchNameSpaceId = */ kNameSpaceID_None, - /* aFuncMayDependOnAttr = */ true, - /* aLiveList = */ false); - } - - NS_ENSURE_TRUE(htmlForms && htmlFormControls, NS_ERROR_OUT_OF_MEMORY); + RefPtr htmlFormControls = + new nsContentList(aDocument, + nsHTMLDocument::MatchFormControls, + nullptr, nullptr, + /* aDeep = */ true, + /* aMatchAtom = */ nullptr, + /* aMatchNameSpaceId = */ kNameSpaceID_None, + /* aFuncMayDependOnAttr = */ true, + /* aLiveList = */ false); // If we have a form control and can calculate form information, use that // as the key - it is more reliable than just recording position in the @@ -3086,7 +3079,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent, // XXXbz We don't? Why not? I don't follow. // nsCOMPtr control(do_QueryInterface(aContent)); - if (control && htmlFormControls && htmlForms) { + if (control) { // Append the control type KeyAppendInt(control->ControlType(), aKey); diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index 310e4a455331..2ba4076a621b 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -203,7 +203,6 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsHTMLDocument, nsDocument, mAnchors, mScripts, mForms, - mFormControls, mWyciwygChannel, mMidasCommandManager) @@ -3719,7 +3718,6 @@ nsHTMLDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const // - mAnchors // - mScripts // - mForms - // - mFormControls // - mWyciwygChannel // - mMidasCommandManager } diff --git a/dom/html/nsHTMLDocument.h b/dom/html/nsHTMLDocument.h index fc0da755d5a5..65609f5c3f1d 100644 --- a/dom/html/nsHTMLDocument.h +++ b/dom/html/nsHTMLDocument.h @@ -82,11 +82,6 @@ public: return mForms; } - nsContentList* GetExistingFormControls() const - { - return mFormControls; - } - // nsIDOMDocument interface using nsDocument::CreateElement; using nsDocument::CreateElementNS; @@ -324,7 +319,6 @@ protected: RefPtr mAnchors; RefPtr mScripts; RefPtr mForms; - RefPtr mFormControls; RefPtr mAll; From 0ea6695439a3728cf15f1c59af1ec75b1940bad2 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Thu, 31 Aug 2017 13:04:24 -0400 Subject: [PATCH 07/51] Bug 1395609 - Bump the the font-size to 12px for the devtools toolbar tab label. r=bgrins --- devtools/client/themes/toolbox.css | 1 + 1 file changed, 1 insertion(+) diff --git a/devtools/client/themes/toolbox.css b/devtools/client/themes/toolbox.css index ade315af355a..9e7fcbeecf61 100644 --- a/devtools/client/themes/toolbox.css +++ b/devtools/client/themes/toolbox.css @@ -106,6 +106,7 @@ } .devtools-tab-label { + font-size: 12px; mask-image: linear-gradient(to left, transparent 0, black 6px); /* Set the end padding on the label to make sure the label gets faded out properly */ padding-inline-end: 10px; From 0735644b45573649be21ea0f73e31e5e7afc5222 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Thu, 31 Aug 2017 19:08:20 +0200 Subject: [PATCH 08/51] Bug 815077 - Make unicode ETHIOPIC WORDSPACE count as a space character. r=jfkthame MozReview-Commit-ID: DhUayZurZ00 --- intl/lwbrk/nsILineBreaker.h | 3 ++- .../reftests/text/ethiopic-wordspace-ref.html | 17 +++++++++++++++++ layout/reftests/text/ethiopic-wordspace.html | 17 +++++++++++++++++ layout/reftests/text/reftest.list | 2 ++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/text/ethiopic-wordspace-ref.html create mode 100644 layout/reftests/text/ethiopic-wordspace.html diff --git a/intl/lwbrk/nsILineBreaker.h b/intl/lwbrk/nsILineBreaker.h index e4ada94ad248..34cda30f4fed 100644 --- a/intl/lwbrk/nsILineBreaker.h +++ b/intl/lwbrk/nsILineBreaker.h @@ -55,12 +55,13 @@ NS_IsSpace(char16_t u) return u == 0x0020 || // SPACE u == 0x0009 || // CHARACTER TABULATION u == 0x000D || // CARRIAGE RETURN - u == 0x1680 || // OGHAM SPACE MARK (0x2000 <= u && u <= 0x2006) || // EN QUAD, EM QUAD, EN SPACE, // EM SPACE, THREE-PER-EM SPACE, // FOUR-PER-SPACE, SIX-PER-EM SPACE, (0x2008 <= u && u <= 0x200B) || // PUNCTUATION SPACE, THIN SPACE, // HAIR SPACE, ZERO WIDTH SPACE + u == 0x1361 || // ETHIOPIC WORDSPACE + u == 0x1680 || // OGHAM SPACE MARK u == 0x205F; // MEDIUM MATHEMATICAL SPACE } diff --git a/layout/reftests/text/ethiopic-wordspace-ref.html b/layout/reftests/text/ethiopic-wordspace-ref.html new file mode 100644 index 000000000000..39c289b5e6cc --- /dev/null +++ b/layout/reftests/text/ethiopic-wordspace-ref.html @@ -0,0 +1,17 @@ + + + + + Reference: Testcase for bug 815077 + + + +

+የሰው፡
ልጅ፡
ሁሉ፡
ሲወለድ፡
ነጻና፡
በክብርና፡
በመብትም፡
እኩልነት፡
ያለው፡
ነው።፡
የተፈጥሮ፡
ማስተዋልና፡
ሕሊና፡
ስላለው፡
አንዱ፡
ሌላውን፡
በወንድማማችነት፡
መንፈስ፡
መመልከት፡
ይገባዋል። +

+ + + diff --git a/layout/reftests/text/ethiopic-wordspace.html b/layout/reftests/text/ethiopic-wordspace.html new file mode 100644 index 000000000000..912374e34e7e --- /dev/null +++ b/layout/reftests/text/ethiopic-wordspace.html @@ -0,0 +1,17 @@ + + + + + Testcase for bug 815077 + + + +

+የሰው፡ልጅ፡ሁሉ፡ሲወለድ፡ነጻና፡በክብርና፡በመብትም፡እኩልነት፡ያለው፡ነው።፡የተፈጥሮ፡ማስተዋልና፡ሕሊና፡ስላለው፡አንዱ፡ሌላውን፡በወንድማማችነት፡መንፈስ፡መመልከት፡ይገባዋል። +

+ + + diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index 198cb900856b..0d721fa74fa7 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -354,3 +354,5 @@ HTTP(..) == space-font-1.html space-font-1-ref.html == letter-spacing-nolig-1.html letter-spacing-nolig-1-ref.html HTTP(..) == letter-spacing-nolig-2.html letter-spacing-nolig-2.html + +== ethiopic-wordspace.html ethiopic-wordspace-ref.html From dbe7586a180ea8ce7935800baf538ef036994991 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Thu, 31 Aug 2017 19:11:27 +0200 Subject: [PATCH 09/51] Bug 1380270 - Add dlopen() version of libudev-sys r=qdot,ted --- dom/webauthn/libudev-sys/Cargo.toml | 9 ++ dom/webauthn/libudev-sys/src/lib.rs | 180 ++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 dom/webauthn/libudev-sys/Cargo.toml create mode 100644 dom/webauthn/libudev-sys/src/lib.rs diff --git a/dom/webauthn/libudev-sys/Cargo.toml b/dom/webauthn/libudev-sys/Cargo.toml new file mode 100644 index 000000000000..78cf6c51214d --- /dev/null +++ b/dom/webauthn/libudev-sys/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libudev-sys" +version = "0.1.3" +authors = ["Tim Taubert "] +description = "FFI bindings to libudev" + +[dependencies] +lazy_static = "0.2" +libc = "0.2" diff --git a/dom/webauthn/libudev-sys/src/lib.rs b/dom/webauthn/libudev-sys/src/lib.rs new file mode 100644 index 000000000000..c422399d8521 --- /dev/null +++ b/dom/webauthn/libudev-sys/src/lib.rs @@ -0,0 +1,180 @@ +/* -*- Mode: rust; rust-indent-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] + +#[macro_use] +extern crate lazy_static; +extern crate libc; + +use libc::{c_void,c_int,c_char,c_ulonglong,dev_t}; +use libc::{RTLD_GLOBAL,RTLD_LAZY,RTLD_NOLOAD}; +use libc::{dlopen,dlclose,dlsym}; +use std::ffi::CString; +use std::{marker,mem,ops,ptr}; + +#[repr(C)] +pub struct udev { + __private: c_void +} + +#[repr(C)] +pub struct udev_list_entry { + __private: c_void +} + +#[repr(C)] +pub struct udev_device { + __private: c_void +} + +#[repr(C)] +pub struct udev_monitor { + __private: c_void +} + +#[repr(C)] +pub struct udev_enumerate { + __private: c_void +} + +macro_rules! ifnull { + ($a:expr, $b:expr) => { + if $a.is_null() { $b } else { $a } + } +} + +struct Library(*mut c_void); + +impl Library { + fn open(name: &'static str) -> Library { + let flags = RTLD_LAZY | RTLD_GLOBAL; + let flags_noload = flags | RTLD_NOLOAD; + let name = CString::new(name).unwrap(); + let name = name.as_ptr(); + + Library(unsafe { + ifnull!(dlopen(name, flags_noload), dlopen(name, flags)) + }) + } + + fn get(&self, name: &'static str) -> *mut c_void { + let name = CString::new(name).unwrap(); + unsafe { dlsym(self.0, name.as_ptr()) } + } +} + +impl Drop for Library { + fn drop(&mut self) { + unsafe { dlclose(self.0); } + } +} + +unsafe impl Sync for Library {} + +lazy_static! { + static ref LIBRARY: Library = { + Library::open("libudev.so.1") + }; +} + +pub struct Symbol { + ptr: *mut c_void, + pd: marker::PhantomData +} + +impl Symbol { + fn new(ptr: *mut c_void) -> Self { + let default = Self::default as *mut c_void; + Self { ptr: ifnull!(ptr, default), pd: marker::PhantomData } + } + + // This is the default symbol, used whenever dlopen() fails. + // Users of this library are expected to check whether udev_new() returns + // a nullptr, and if so they MUST NOT call any other exported functions. + extern "C" fn default() -> *mut c_void { + ptr::null_mut() + } +} + +impl ops::Deref for Symbol { + type Target = T; + + fn deref(&self) -> &T { + unsafe { mem::transmute(&self.ptr) } + } +} + +unsafe impl Sync for Symbol {} + +macro_rules! define { + ($name:ident, $type:ty) => { + lazy_static! { + pub static ref $name : Symbol<$type> = { + Symbol::new(LIBRARY.get(stringify!($name))) + }; + } + }; +} + +// udev +define!(udev_new, extern "C" fn () -> *mut udev); +define!(udev_unref, extern "C" fn (*mut udev) -> *mut udev); + +// udev_list +define!(udev_list_entry_get_next, extern "C" fn (*mut udev_list_entry) -> *mut udev_list_entry); +define!(udev_list_entry_get_name, extern "C" fn (*mut udev_list_entry) -> *const c_char); +define!(udev_list_entry_get_value, extern "C" fn (*mut udev_list_entry) -> *const c_char); + +// udev_device +define!(udev_device_ref, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_unref, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_new_from_syspath, extern "C" fn (*mut udev, *const c_char) -> *mut udev_device); +define!(udev_device_get_parent, extern "C" fn (*mut udev_device) -> *mut udev_device); +define!(udev_device_get_devpath, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_subsystem, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devtype, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_syspath, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysname, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysnum, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devnode, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_is_initialized, extern "C" fn (*mut udev_device) -> c_int); +define!(udev_device_get_properties_list_entry, extern "C" fn (*mut udev_device) -> *mut udev_list_entry); +define!(udev_device_get_property_value, extern "C" fn (*mut udev_device, *const c_char) -> *const c_char); +define!(udev_device_get_driver, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_devnum, extern "C" fn (*mut udev_device) -> dev_t); +define!(udev_device_get_action, extern "C" fn (*mut udev_device) -> *const c_char); +define!(udev_device_get_sysattr_value, extern "C" fn (*mut udev_device, *const c_char) -> *const c_char); +define!(udev_device_set_sysattr_value, extern "C" fn (*mut udev_device, *const c_char, *mut c_char) -> c_int); +define!(udev_device_get_sysattr_list_entry, extern "C" fn (*mut udev_device) -> *mut udev_list_entry); +define!(udev_device_get_seqnum, extern "C" fn (*mut udev_device) -> c_ulonglong); + +// udev_monitor +define!(udev_monitor_ref, extern "C" fn (*mut udev_monitor) -> *mut udev_monitor); +define!(udev_monitor_unref, extern "C" fn (*mut udev_monitor) -> *mut udev_monitor); +define!(udev_monitor_new_from_netlink, extern "C" fn (*mut udev, *const c_char) -> *mut udev_monitor); +define!(udev_monitor_enable_receiving, extern "C" fn (*mut udev_monitor) -> c_int); +define!(udev_monitor_get_fd, extern "C" fn (*mut udev_monitor) -> c_int); +define!(udev_monitor_receive_device, extern "C" fn (*mut udev_monitor) -> *mut udev_device); +define!(udev_monitor_filter_add_match_subsystem_devtype, extern "C" fn (*mut udev_monitor, *const c_char, *const c_char) -> c_int); +define!(udev_monitor_filter_add_match_tag, extern "C" fn (*mut udev_monitor, *const c_char) -> c_int); +define!(udev_monitor_filter_remove, extern "C" fn (*mut udev_monitor) -> c_int); + +// udev_enumerate +define!(udev_enumerate_unref, extern "C" fn (*mut udev_enumerate) -> *mut udev_enumerate); +define!(udev_enumerate_new, extern "C" fn (*mut udev) -> *mut udev_enumerate); +define!(udev_enumerate_add_match_subsystem, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_nomatch_subsystem, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_match_sysattr, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_nomatch_sysattr, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_match_property, extern "C" fn (*mut udev_enumerate, *const c_char, *const c_char) -> c_int); +define!(udev_enumerate_add_match_tag, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_match_parent, extern "C" fn (*mut udev_enumerate, *mut udev_device) -> c_int); +define!(udev_enumerate_add_match_is_initialized, extern "C" fn (*mut udev_enumerate) -> c_int); +define!(udev_enumerate_add_match_sysname, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_add_syspath, extern "C" fn (*mut udev_enumerate, *const c_char) -> c_int); +define!(udev_enumerate_scan_devices, extern "C" fn (*mut udev_enumerate) -> c_int); +define!(udev_enumerate_get_list_entry, extern "C" fn (*mut udev_enumerate) -> *mut udev_list_entry); From 51d5af29e647982dee837541ac890b88f57ab2df Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Thu, 31 Aug 2017 19:16:58 +0200 Subject: [PATCH 10/51] Backed out changeset d7c36348c05b (bug 1392511) for valgrind failure on Linux x64 opt. r=backout --- js/src/gc/Nursery.cpp | 70 ++++++++++++++----------------------------- js/src/gc/Nursery.h | 20 ++++--------- 2 files changed, 28 insertions(+), 62 deletions(-) diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 4a4cfa063577..4c19ffb1a06d 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -465,22 +465,6 @@ js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery) { } -inline float -js::Nursery::calcPromotionRate(bool *validForTenuring) const { - float used = float(previousGC.nurseryUsedBytes); - float capacity = float(previousGC.nurseryCapacity); - float tenured = float(previousGC.tenuredBytes); - - if (validForTenuring) { - /* - * We can only use promotion rates if they're likely to be valid, - * they're only valid if the nursury was at least 90% full. - */ - *validForTenuring = used > capacity * 0.9f; - } - return tenured / used; -} - void js::Nursery::renderProfileJSON(JSONPrinter& json) const { @@ -506,7 +490,8 @@ js::Nursery::renderProfileJSON(JSONPrinter& json) const json.property("reason", JS::gcreason::ExplainReason(previousGC.reason)); json.property("bytes_tenured", previousGC.tenuredBytes); - json.floatProperty("promotion_rate", calcPromotionRate(nullptr), 0); + json.floatProperty("promotion_rate", + 100.0 * previousGC.tenuredBytes / double(previousGC.nurseryUsedBytes), 2); json.property("nursery_bytes", previousGC.nurseryUsedBytes); json.property("new_nursery_bytes", numChunks() * ChunkSize); @@ -624,13 +609,14 @@ js::Nursery::collect(JS::gcreason::Reason reason) JS::AutoSuppressGCAnalysis nogc; TenureCountCache tenureCounts; + double promotionRate = 0; previousGC.reason = JS::gcreason::NO_REASON; if (!isEmpty()) - doCollection(reason, tenureCounts); + promotionRate = doCollection(reason, tenureCounts); // Resize the nursery. startProfile(ProfileKey::Resize); - maybeResizeNursery(reason); + maybeResizeNursery(reason, promotionRate); endProfile(ProfileKey::Resize); // If we are promoting the nursery, or exhausted the store buffer with @@ -638,20 +624,16 @@ js::Nursery::collect(JS::gcreason::Reason reason) // the nursery is full, look for object groups that are getting promoted // excessively and try to pretenure them. startProfile(ProfileKey::Pretenure); - bool validPromotionRate; - const float promotionRate = calcPromotionRate(&validPromotionRate); uint32_t pretenureCount = 0; - if (validPromotionRate) { - if (promotionRate > 0.8 || IsFullStoreBufferReason(reason)) { - JSContext* cx = TlsContext.get(); - for (auto& entry : tenureCounts.entries) { - if (entry.count >= 3000) { - ObjectGroup* group = entry.group; - if (group->canPreTenure()) { - AutoCompartment ac(cx, group); - group->setShouldPreTenure(cx); - pretenureCount++; - } + if (promotionRate > 0.8 || IsFullStoreBufferReason(reason)) { + JSContext* cx = TlsContext.get(); + for (auto& entry : tenureCounts.entries) { + if (entry.count >= 3000) { + ObjectGroup* group = entry.group; + if (group->canPreTenure()) { + AutoCompartment ac(cx, group); + group->setShouldPreTenure(cx); + pretenureCount++; } } } @@ -703,7 +685,7 @@ js::Nursery::collect(JS::gcreason::Reason reason) } } -void +double js::Nursery::doCollection(JS::gcreason::Reason reason, TenureCountCache& tenureCounts) { @@ -714,8 +696,7 @@ js::Nursery::doCollection(JS::gcreason::Reason reason, AutoDisableProxyCheck disableStrictProxyChecking; mozilla::DebugOnly oomUnsafeRegion; - const size_t initialNurseryCapacity = spaceToEnd(); - const size_t initialNurseryUsedBytes = initialNurseryCapacity - freeSpace(); + size_t initialNurserySize = spaceToEnd(); // Move objects pointed to by roots from the nursery to the major heap. TenuringTracer mover(rt, this); @@ -812,9 +793,11 @@ js::Nursery::doCollection(JS::gcreason::Reason reason, endProfile(ProfileKey::CheckHashTables); previousGC.reason = reason; - previousGC.nurseryCapacity = initialNurseryCapacity; - previousGC.nurseryUsedBytes = initialNurseryUsedBytes; + previousGC.nurseryUsedBytes = initialNurserySize; previousGC.tenuredBytes = mover.tenuredSize; + + // Calculate and return the promotion rate. + return mover.tenuredSize / double(initialNurserySize); } void @@ -940,7 +923,7 @@ js::Nursery::setStartPosition() } void -js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason) +js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate) { static const double GrowThreshold = 0.05; static const double ShrinkThreshold = 0.01; @@ -959,27 +942,20 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason) return; #endif - bool canUsePromotionRate; - const float promotionRate = calcPromotionRate(&canUsePromotionRate); - newMaxNurseryChunks = runtime()->gc.tunables.gcMaxNurseryBytes() >> ChunkShift; if (newMaxNurseryChunks != maxNurseryChunks_) { maxNurseryChunks_ = newMaxNurseryChunks; /* The configured maximum nursery size is changing */ - const int extraChunks = numChunks() - newMaxNurseryChunks; + int extraChunks = numChunks() - newMaxNurseryChunks; if (extraChunks > 0) { /* We need to shrink the nursery */ shrinkAllocableSpace(extraChunks); - if (canUsePromotionRate) - previousPromotionRate_ = promotionRate; + previousPromotionRate_ = promotionRate; return; } } - if (!canUsePromotionRate) - return; - if (promotionRate > GrowThreshold) growAllocableSpace(); else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold) diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index b2b7355e0af0..cd87d51c7da3 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -316,7 +316,7 @@ class Nursery unsigned maxNurseryChunks_; /* Promotion rate for the previous minor collection. */ - float previousPromotionRate_; + double previousPromotionRate_; /* Report minor collections taking at least this long, if enabled. */ mozilla::TimeDuration profileThreshold_; @@ -355,20 +355,10 @@ class Nursery struct { JS::gcreason::Reason reason; - size_t nurseryCapacity; - size_t nurseryUsedBytes; - size_t tenuredBytes; + uint64_t nurseryUsedBytes; + uint64_t tenuredBytes; } previousGC; - /* - * Calculate the promotion rate of the most recent minor GC. - * The valid_for_tenuring parameter is used to return whether this - * promotion rate is accurate enough (the nursery was full enough) to be - * used for tenuring and other decisions. - */ - float - calcPromotionRate(bool *validForTenuring) const; - /* * The set of externally malloced buffers potentially kept live by objects * stored in the nursery. Any external buffers that do not belong to a @@ -448,7 +438,7 @@ class Nursery /* Common internal allocator function. */ void* allocate(size_t size); - void doCollection(JS::gcreason::Reason reason, + double doCollection(JS::gcreason::Reason reason, gc::TenureCountCache& tenureCounts); /* @@ -476,7 +466,7 @@ class Nursery void sweepDictionaryModeObjects(); /* Change the allocable space provided by the nursery. */ - void maybeResizeNursery(JS::gcreason::Reason reason); + void maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate); void growAllocableSpace(); void shrinkAllocableSpace(unsigned removeNumChunks); void minimizeAllocableSpace(); From a78130ade824e7b51b3add917c00723bbf9ae2ba Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Thu, 31 Aug 2017 19:26:13 +0200 Subject: [PATCH 11/51] Bug 1384478 - browser/base/content/test/general/browser_save_video.js fails when we enable rcwn, r=honzab --- netwerk/protocol/http/nsHttpChannel.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index a1e46b04b2c1..78823a95295b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1724,8 +1724,12 @@ nsHttpChannel::CallOnStartRequest() // We must keep the cache entry in case of partial request. // Concurrent access is the same, we need the entry in // OnStopRequest. - if (!mCachedContentIsPartial && !mConcurrentCacheAccess) + // We also need the cache entry when racing cache with network to find + // out what is the source of the data. + if (!mCachedContentIsPartial && !mConcurrentCacheAccess && + !(mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_CACHE)) { CloseCacheEntry(false); + } } if (!mCanceled) { From 378bd857edf1178084fd0a48a1177e02c5d12481 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 31 Aug 2017 13:42:51 -0400 Subject: [PATCH 12/51] Bug 1395168 - Add tests for key synthesis; r=esawin Add tests for synthesizing keys, including test for dummy keys and test for wrong metastate for synthesized non-English keys (i.e. bug 1387889). MozReview-Commit-ID: SvddU2BHle --- .../tests/browser/robocop/robocop_input.html | 45 ++++++++++++++++ .../gecko/tests/testInputConnection.java | 54 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/mobile/android/tests/browser/robocop/robocop_input.html b/mobile/android/tests/browser/robocop/robocop_input.html index f8f4bf317231..a11bb8719214 100644 --- a/mobile/android/tests/browser/robocop/robocop_input.html +++ b/mobile/android/tests/browser/robocop/robocop_input.html @@ -26,6 +26,11 @@ setTimeout(function() { designMode.contentDocument.designMode = "on" }, 0); } + // Add key listeners to trigger dummy key synthesis. + input.addEventListener("keydown", function() {}); + textArea.addEventListener("keydown", function() {}); + contentEditable.addEventListener("keydown", function() {}); + // An input that resets the editor on every input by resetting the value property. let resetting_input = document.getElementById("resetting-input"); resetting_input.addEventListener("input", function() { @@ -64,6 +69,27 @@ } } + let key_log; + function get_key_metastate(event) { + return (event.ctrlKey ? "C" : "c") + + (event.altKey ? "A" : "a") + + (event.shiftKey ? "S" : "s") + + (event.metaKey ? "M" : "m"); + } + + function log_key(event) { + switch (event.type) { + case "keydown": + case "keypress": + case "keyup": + key_log += `${event.type}:${event.key},${get_key_metastate(event)};`; + break; + default: + key_log += "unknown;"; + break; + } + } + function get_event_target() { var editor = getEditor(); var parent = SpecialPowers.unwrap(editor.rootElement.parentElement); @@ -182,6 +208,25 @@ target.removeEventListener("selectionchange", log_event); }, + start_key_log: function() { + // Reset the log + key_log = ""; + + let target = get_event_target(); + target.addEventListener("keydown", log_key); + target.addEventListener("keypress", log_key); + target.addEventListener("keyup", log_key); + }, + + end_key_log: function() { + java.asyncCall("setKeyLog", key_log); + + let target = get_event_target(); + target.removeEventListener("keydown", log_key); + target.removeEventListener("keypress", log_key); + target.removeEventListener("keyup", log_key); + }, + test_bug1123514: function() { document.activeElement.addEventListener("input", function() { // Only works on input and textarea. diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java index ce7bebecf308..f6846ade9f2b 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java @@ -26,6 +26,7 @@ public class testInputConnection extends JavascriptBridgeTest { private static final String INITIAL_TEXT = "foo"; private String mEventsLog; + private String mKeyLog; public void testInputConnection() throws InterruptedException { GeckoHelper.blockForReady(); @@ -35,6 +36,9 @@ public class testInputConnection extends JavascriptBridgeTest { // Enable "selectionchange" events for input/textarea. mActions.setPref("dom.select_events.enabled", true, /* flush */ false); mActions.setPref("dom.select_events.textcontrols.enabled", true, /* flush */ false); + // Enable dummy key synthesis. + mActions.setPref("intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", + true, /* flush */ false); final String url = mStringHelper.ROBOCOP_INPUT_URL; NavigationHelper.enterAndLoadUrl(url); @@ -87,6 +91,14 @@ public class testInputConnection extends JavascriptBridgeTest { return mEventsLog; } + public void setKeyLog(final String log) { + mKeyLog = log; + } + + public String getKeyLog() { + return mKeyLog; + } + private class BasicInputConnectionTest extends InputConnectionTest { private final String mType; @@ -188,6 +200,34 @@ public class testInputConnection extends JavascriptBridgeTest { ic.deleteSurroundingText(6, 0); assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Test key synthesis. + getJS().syncCall("start_key_log"); + ic.setComposingText("f", 1); // Synthesizes dummy key. + assertTextAndSelectionAt("Can compose F key", ic, "f", 1); + ic.finishComposingText(); // Does not synthesize key. + assertTextAndSelectionAt("Can finish F key", ic, "f", 1); + ic.commitText("o", 1); // Synthesizes O key. + assertTextAndSelectionAt("Can commit O key", ic, "fo", 2); + ic.commitText("of", 1); // Synthesizes dummy key. + assertTextAndSelectionAt("Can commit non-key string", ic, "foof", 4); + + getJS().syncCall("end_key_log"); + if (mType.equals("designMode")) { + // designMode doesn't support dummy key synthesis. + fAssertEquals("Can synthesize keys", + "keydown:o,casm;keypress:o,casm;keyup:o,casm;", // O key + getKeyLog()); + } else { + fAssertEquals("Can synthesize keys", + "keydown:Unidentified,casm;keyup:Unidentified,casm;" + // Dummy + "keydown:o,casm;keypress:o,casm;keyup:o,casm;" + // O key + "keydown:Unidentified,casm;keyup:Unidentified,casm;", // Dummy + getKeyLog()); + } + + ic.deleteSurroundingText(4, 0); + assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Bug 1133802, duplication when setting the same composing text more than once. ic.setComposingText("foo", 1); assertTextAndSelectionAt("Can set the composing text", ic, "foo", 3); @@ -325,6 +365,20 @@ public class testInputConnection extends JavascriptBridgeTest { ic.deleteSurroundingText(0, 3); assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Bug 1387889 - Latin sharp S (U+00DF) triggers Alt+S shortcut + getJS().syncCall("start_key_log"); + ic.commitText("\u00df", 1); // Synthesizes "Latin sharp S" key without modifiers. + assertTextAndSelectionAt("Can commit Latin sharp S key", ic, "\u00df", 1); + + getJS().syncCall("end_key_log"); + fAssertEquals("Can synthesize sharp S key", + "keydown:\u00df,casm;keypress:\u00df,casm;keyup:\u00df,casm;", + getKeyLog()); + + ic.finishComposingText(); + ic.deleteSurroundingText(1, 0); + assertTextAndSelectionAt("Can clear text", ic, "", 0); + // Make sure we don't leave behind stale events for the following test. processGeckoEvents(); processInputConnectionEvents(); From 2cf88859ad0508915dce6357d49d34acf02c6a25 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 31 Aug 2017 13:42:52 -0400 Subject: [PATCH 13/51] Bug 1395170 - Fix lint warnings/errors from BasicGeckoViewPrompt; r=droeh Fix the following lint errors/warnings, * Using inlined constants on older versions Add version check for usages of Intent.EXTRA_ALLOW_MULTIPLE and Intent.EXTRA_MIME_TYPES. * Calling new methods on older versions Change usages of AlertDialog.Builder#setOnDismissListener to Dialog#setOnDismissListener instead. * Missing recycle() calls Add missing TypedArray#recycle call MozReview-Commit-ID: EwZFDKqoCjL --- .../BasicGeckoViewPrompt.java | 115 +++++++++--------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java index 11082bb1a33b..2d2cadd09bd4 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java @@ -5,6 +5,7 @@ package org.mozilla.geckoview_example; +import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -106,14 +107,9 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { final AlertDialog.Builder builder = new AlertDialog.Builder(activity) .setTitle(title) .setMessage(msg) - .setPositiveButton(android.R.string.ok, /* onClickListener */ null) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.dismiss(); - } - }); - addCheckbox(builder, /* parent */ null, callback).show(); + .setPositiveButton(android.R.string.ok, /* onClickListener */ null); + createStandardDialog(addCheckbox(builder, /* parent */ null, callback), + callback).show(); } public void promptForButton(final GeckoView view, final String title, final String msg, @@ -126,13 +122,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } final AlertDialog.Builder builder = new AlertDialog.Builder(activity) .setTitle(title) - .setMessage(msg) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.dismiss(); - } - }); + .setMessage(msg); final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override @@ -157,18 +147,20 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { if (btnMsg[BUTTON_TYPE_NEGATIVE] != null) { builder.setNegativeButton(btnMsg[BUTTON_TYPE_NEGATIVE], listener); } - addCheckbox(builder, /* parent */ null, callback).show(); + createStandardDialog(addCheckbox(builder, /* parent */ null, callback), + callback).show(); } private int getViewPadding(final AlertDialog.Builder builder) { final TypedArray attr = builder.getContext().obtainStyledAttributes( new int[] { android.R.attr.listPreferredItemPaddingLeft }); - return attr.getDimensionPixelSize(0, 1); + final int padding = attr.getDimensionPixelSize(0, 1); + attr.recycle(); + return padding; } private LinearLayout addStandardLayout(final AlertDialog.Builder builder, - final String title, final String msg, - final AlertCallback callback) { + final String title, final String msg) { final ScrollView scrollView = new ScrollView(builder.getContext()); final LinearLayout container = new LinearLayout(builder.getContext()); final int horizontalPadding = getViewPadding(builder); @@ -179,14 +171,20 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { scrollView.addView(container); builder.setTitle(title) .setMessage(msg) - .setOnDismissListener(new DialogInterface.OnDismissListener() { + .setView(scrollView); + return container; + } + + private AlertDialog createStandardDialog(final AlertDialog.Builder builder, + final AlertCallback callback) { + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(final DialogInterface dialog) { callback.dismiss(); } - }) - .setView(scrollView); - return container; + }); + return dialog; } public void promptForText(final GeckoView view, final String title, final String msg, @@ -198,7 +196,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, msg, callback); + final LinearLayout container = addStandardLayout(builder, title, msg); final EditText editText = new EditText(builder.getContext()); editText.setText(value); container.addView(editText); @@ -212,7 +210,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } }); - addCheckbox(builder, container, callback).show(); + createStandardDialog(addCheckbox(builder, container, callback), callback).show(); } public void promptForAuth(final GeckoView view, final String title, final String msg, @@ -224,7 +222,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, msg, callback); + final LinearLayout container = addStandardLayout(builder, title, msg); final int flags = options.getInt("flags"); final int level = options.getInt("level"); @@ -264,7 +262,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } } }); - addCheckbox(builder, container, callback).show(); + createStandardDialog(addCheckbox(builder, container, callback), callback).show(); } private void addChoiceItems(final int type, final ArrayAdapter list, @@ -303,7 +301,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - addStandardLayout(builder, title, msg, callback); + addStandardLayout(builder, title, msg); final ListView list = new ListView(builder.getContext()); if (type == CHOICE_TYPE_MULTIPLE) { @@ -413,7 +411,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { final AlertDialog dialog; if (type == CHOICE_TYPE_SINGLE || type == CHOICE_TYPE_MENU) { - dialog = builder.create(); + dialog = createStandardDialog(builder, callback); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View v, @@ -460,7 +458,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { callback.confirm(items.toArray(new String[items.size()])); } }); - dialog = builder.create(); + dialog = createStandardDialog(builder, callback); } else { throw new UnsupportedOperationException(); } @@ -484,7 +482,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - addStandardLayout(builder, title, /* msg */ null, callback); + addStandardLayout(builder, title, /* msg */ null); final int initial = parseColor(value, /* def */ 0); final ArrayAdapter adapter = new ArrayAdapter( @@ -537,7 +535,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { list.setAdapter(adapter); builder.setView(list); - final AlertDialog dialog = builder.create(); + final AlertDialog dialog = createStandardDialog(builder, callback); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View v, @@ -665,8 +663,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { timePicker = null; } - final LinearLayout container = addStandardLayout(builder, title, - /* msg */ null, callback); + final LinearLayout container = addStandardLayout(builder, title, /* msg */ null); container.setPadding(/* left */ 0, /* top */ 0, /* right */ 0, /* bottom */ 0); if (datePicker != null) { container.addView(datePicker); @@ -696,10 +693,11 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { }; builder.setNegativeButton(android.R.string.cancel, /* listener */ null) .setNeutralButton(R.string.clear_field, listener) - .setPositiveButton(android.R.string.ok, listener) - .show(); + .setPositiveButton(android.R.string.ok, listener); + createStandardDialog(builder, callback).show(); } + @TargetApi(19) public void promptForFile(GeckoView view, String title, int type, String[] mimeTypes, FileCallback callback) { @@ -738,10 +736,10 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { (mimeSubtype != null ? mimeSubtype : "*")); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - if (type == FILE_TYPE_MULTIPLE) { + if (Build.VERSION.SDK_INT >= 18 && type == FILE_TYPE_MULTIPLE) { intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); } - if (mimeTypes.length > 0) { + if (Build.VERSION.SDK_INT >= 19 && mimeTypes.length > 0) { intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); } @@ -799,20 +797,22 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.reject(); - } - }) .setNegativeButton(android.R.string.cancel, /* onClickListener */ null) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { callback.grant(); } - }) - .show(); + }); + + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(final DialogInterface dialog) { + callback.reject(); + } + }); + dialog.show(); } private Spinner addMediaSpinner(final Context context, final ViewGroup container, @@ -858,8 +858,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { return; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final LinearLayout container = addStandardLayout(builder, title, /* msg */ null, - /* callback */ null); + final LinearLayout container = addStandardLayout(builder, title, /* msg */ null); final Spinner videoSpinner; if (video != null) { @@ -875,13 +874,7 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { audioSpinner = null; } - builder.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - callback.reject(); - } - }) - .setNegativeButton(android.R.string.cancel, /* listener */ null) + builder.setNegativeButton(android.R.string.cancel, /* listener */ null) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override @@ -892,7 +885,15 @@ final class BasicGeckoViewPrompt implements GeckoView.PromptDelegate { ? (GeckoBundle) audioSpinner.getSelectedItem() : null; callback.grant(video, audio); } - }) - .show(); + }); + + final AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(final DialogInterface dialog) { + callback.reject(); + } + }); + dialog.show(); } } From 0c0c7c019b94b1b06d2eae9629ae5b03920af09e Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 31 Aug 2017 13:50:13 -0400 Subject: [PATCH 14/51] Bug 1313372 - Disable browser/components/extensions/test/browser/browser_ext_browserAction_popup.js on linux32 debug. r=gbrown --- browser/components/extensions/test/browser/browser-common.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index eb8f34e79f1d..b4b2f469eb50 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -41,6 +41,7 @@ skip-if = os == 'linux' [browser_ext_browserAction_pageAction_icon.js] [browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_popup.js] +skip-if = debug && (os == 'linux' && bits=32) # Bug 1313372 [browser_ext_browserAction_popup_preload.js] skip-if = (os == 'win' && !debug) # bug 1352668 [browser_ext_browserAction_popup_resize.js] From ca6c40e2a2201272d857e5edd02406131d2b21fd Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Thu, 31 Aug 2017 13:51:47 -0400 Subject: [PATCH 15/51] Bug 1395654 - Use --blue-50 for the devtools tab line color. r=bgrins DONTBUILD --- devtools/client/themes/common.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css index 626b2fe3ffa5..50c53e8cf8f7 100644 --- a/devtools/client/themes/common.css +++ b/devtools/client/themes/common.css @@ -8,7 +8,7 @@ :root { font: message-box; - --tab-line-selected-color: highlight; + --tab-line-selected-color: var(--blue-50); } :root.theme-light { From 9a69b9b3c6a45c8082a3119d6d680c11442d8daa Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 31 Aug 2017 14:09:26 -0400 Subject: [PATCH 16/51] Bug 1395237 - Update webrender to commit 81cba6b139c4c1061cab6a1c38acf2ae7f50445d. r=jrmuizel Includes Cargo.lock updates. MozReview-Commit-ID: GsEbblWnzT3 --- gfx/doc/README.webrender | 2 +- gfx/webrender/Cargo.toml | 2 +- gfx/webrender/examples/basic.rs | 6 +- gfx/webrender/examples/blob.rs | 1 + gfx/webrender/res/cs_box_shadow.glsl | 2 +- gfx/webrender/res/cs_clip_rectangle.fs.glsl | 65 ---- gfx/webrender/res/cs_clip_rectangle.glsl | 141 +++++++- gfx/webrender/res/cs_clip_rectangle.vs.glsl | 76 ---- gfx/webrender/res/ellipse.glsl | 69 ++++ gfx/webrender/res/prim_shared.glsl | 62 ---- gfx/webrender/res/ps_border_corner.fs.glsl | 85 ----- gfx/webrender/res/ps_border_corner.glsl | 370 +++++++++++++++++++- gfx/webrender/res/ps_border_corner.vs.glsl | 285 --------------- gfx/webrender/res/ps_border_edge.fs.glsl | 63 ---- gfx/webrender/res/ps_border_edge.glsl | 284 +++++++++++++++ gfx/webrender/res/ps_border_edge.vs.glsl | 223 ------------ gfx/webrender/res/ps_box_shadow.fs.glsl | 3 +- gfx/webrender/res/ps_box_shadow.vs.glsl | 2 +- gfx/webrender/src/debug_server.rs | 103 +++++- gfx/webrender/src/device.rs | 5 +- gfx/webrender/src/frame.rs | 8 +- gfx/webrender/src/frame_builder.rs | 41 ++- gfx/webrender/src/glyph_rasterizer.rs | 14 +- gfx/webrender/src/internal_types.rs | 7 +- gfx/webrender/src/platform/windows/font.rs | 18 +- gfx/webrender/src/prim_store.rs | 36 +- gfx/webrender/src/profiler.rs | 5 + gfx/webrender/src/render_backend.rs | 161 ++++++++- gfx/webrender/src/render_task.rs | 13 +- gfx/webrender/src/renderer.rs | 138 ++++---- gfx/webrender/src/resource_cache.rs | 58 ++- gfx/webrender/src/scene.rs | 9 + gfx/webrender/src/texture_cache.rs | 22 +- gfx/webrender/src/tiling.rs | 54 +-- gfx/webrender_api/Cargo.toml | 2 +- gfx/webrender_api/src/api.rs | 51 ++- gfx/webrender_api/src/display_item.rs | 6 +- gfx/webrender_api/src/display_list.rs | 45 +-- gfx/webrender_api/src/font.rs | 43 ++- gfx/webrender_api/src/image.rs | 20 +- gfx/webrender_bindings/Cargo.toml | 4 +- toolkit/library/gtest/rust/Cargo.lock | 10 +- toolkit/library/rust/Cargo.lock | 10 +- 43 files changed, 1521 insertions(+), 1103 deletions(-) delete mode 100644 gfx/webrender/res/cs_clip_rectangle.fs.glsl delete mode 100644 gfx/webrender/res/cs_clip_rectangle.vs.glsl create mode 100644 gfx/webrender/res/ellipse.glsl delete mode 100644 gfx/webrender/res/ps_border_corner.fs.glsl delete mode 100644 gfx/webrender/res/ps_border_corner.vs.glsl delete mode 100644 gfx/webrender/res/ps_border_edge.fs.glsl delete mode 100644 gfx/webrender/res/ps_border_edge.vs.glsl diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index 5a397acc6c33..bda0068902bb 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc the need to run the cargo update command in js/src as well. Hopefully this will be resolved soon. -Latest Commit: 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585 +Latest Commit: 81cba6b139c4c1061cab6a1c38acf2ae7f50445d diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 9d545c503e9f..3bcb481990b4 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender" -version = "0.49.0" +version = "0.50.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/gfx/webrender/examples/basic.rs b/gfx/webrender/examples/basic.rs index 9f1e99fa3e41..e19b62021b13 100644 --- a/gfx/webrender/examples/basic.rs +++ b/gfx/webrender/examples/basic.rs @@ -241,6 +241,9 @@ impl Example for App { let font_bytes = load_file("res/FreeSans.ttf"); resources.add_raw_font(font_key, font_bytes, 0); + let font_instance_key = api.generate_font_instance_key(); + resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None); + let text_bounds = (100, 200).by(700, 300); let glyphs = vec![ GlyphInstance { @@ -296,9 +299,8 @@ impl Example for App { builder.push_text(text_bounds, None, &glyphs, - font_key, + font_instance_key, ColorF::new(1.0, 1.0, 0.0, 1.0), - Au::from_px(32), None); } diff --git a/gfx/webrender/examples/blob.rs b/gfx/webrender/examples/blob.rs index 159e40dacdf0..2e0da7c9c9f2 100644 --- a/gfx/webrender/examples/blob.rs +++ b/gfx/webrender/examples/blob.rs @@ -210,6 +210,7 @@ impl api::BlobImageRenderer for CheckerboardRenderer { Err(api::BlobImageError::Other("Channel closed".into())) } fn delete_font(&mut self, _font: api::FontKey) { } + fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) { } } struct App { diff --git a/gfx/webrender/res/cs_box_shadow.glsl b/gfx/webrender/res/cs_box_shadow.glsl index 987e2e638150..ee458dce48a6 100644 --- a/gfx/webrender/res/cs_box_shadow.glsl +++ b/gfx/webrender/res/cs_box_shadow.glsl @@ -183,6 +183,6 @@ void main(void) { float value = color(pos, p0Rect, p1Rect, radii, sigma); value = max(value, 0.0); - oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value)); + oFragColor = dither(vec4(vInverted == 1.0 ? 1.0 - value : value)); } #endif diff --git a/gfx/webrender/res/cs_clip_rectangle.fs.glsl b/gfx/webrender/res/cs_clip_rectangle.fs.glsl deleted file mode 100644 index 3539587b994e..000000000000 --- a/gfx/webrender/res/cs_clip_rectangle.fs.glsl +++ /dev/null @@ -1,65 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -float clip_against_ellipse_if_needed(vec2 pos, - float current_distance, - vec4 ellipse_center_radius, - vec2 sign_modifier, - float afwidth) { - float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, - ellipse_center_radius.zw); - - return mix(current_distance, - ellipse_distance + afwidth, - all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); -} - -float rounded_rect(vec2 pos) { - float current_distance = 0.0; - - // Apply AA - float afwidth = 0.5 * length(fwidth(pos)); - - // Clip against each ellipse. - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TL, - vec2(1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TR, - vec2(-1.0, 1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BR, - vec2(-1.0), - afwidth); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BL, - vec2(1.0, -1.0), - afwidth); - - return smoothstep(0.0, afwidth, 1.0 - current_distance); -} - - -void main(void) { - float alpha = 1.f; - vec2 local_pos = init_transform_fs(vPos, alpha); - - float clip_alpha = rounded_rect(local_pos); - - float combined_alpha = min(alpha, clip_alpha); - - // Select alpha or inverse alpha depending on clip in/out. - float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode); - - oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0); -} diff --git a/gfx/webrender/res/cs_clip_rectangle.glsl b/gfx/webrender/res/cs_clip_rectangle.glsl index 581bf5e2ba01..00392e2aab94 100644 --- a/gfx/webrender/res/cs_clip_rectangle.glsl +++ b/gfx/webrender/res/cs_clip_rectangle.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include shared,prim_shared,clip_shared +#include shared,prim_shared,clip_shared,ellipse varying vec3 vPos; flat varying float vClipMode; @@ -10,3 +10,142 @@ flat varying vec4 vClipCenter_Radius_TL; flat varying vec4 vClipCenter_Radius_TR; flat varying vec4 vClipCenter_Radius_BL; flat varying vec4 vClipCenter_Radius_BR; + +#ifdef WR_VERTEX_SHADER +struct ClipRect { + RectWithSize rect; + vec4 mode; +}; + +ClipRect fetch_clip_rect(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); +} + +struct ClipCorner { + RectWithSize rect; + vec4 outer_inner_radius; +}; + +ClipCorner fetch_clip_corner(ivec2 address, int index) { + address += ivec2(2 + 2 * index, 0); + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); +} + +struct ClipData { + ClipRect rect; + ClipCorner top_left; + ClipCorner top_right; + ClipCorner bottom_left; + ClipCorner bottom_right; +}; + +ClipData fetch_clip(ivec2 address) { + ClipData clip; + + clip.rect = fetch_clip_rect(address); + clip.top_left = fetch_clip_corner(address, 0); + clip.top_right = fetch_clip_corner(address, 1); + clip.bottom_left = fetch_clip_corner(address, 2); + clip.bottom_right = fetch_clip_corner(address, 3); + + return clip; +} + +void main(void) { + CacheClipInstance cci = fetch_clip_item(gl_InstanceID); + ClipArea area = fetch_clip_area(cci.render_task_index); + Layer layer = fetch_layer(cci.layer_index); + ClipData clip = fetch_clip(cci.clip_data_address); + RectWithSize local_rect = clip.rect.rect; + + ClipVertexInfo vi = write_clip_tile_vertex(local_rect, + layer, + area, + cci.segment); + vPos = vi.local_pos; + + vClipMode = clip.rect.mode.x; + + RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); + + vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy, + clip.top_left.outer_inner_radius.xy); + + vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x, + clip_rect.p0.y + clip.top_right.outer_inner_radius.y, + clip.top_right.outer_inner_radius.xy); + + vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy, + clip.bottom_right.outer_inner_radius.xy); + + vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x, + clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y, + clip.bottom_left.outer_inner_radius.xy); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +float clip_against_ellipse_if_needed(vec2 pos, + float current_distance, + vec4 ellipse_center_radius, + vec2 sign_modifier, + float afwidth) { + float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, + ellipse_center_radius.zw); + + return mix(current_distance, + ellipse_distance + afwidth, + all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); +} + +float rounded_rect(vec2 pos) { + float current_distance = 0.0; + + // Apply AA + float afwidth = 0.5 * length(fwidth(pos)); + + // Clip against each ellipse. + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_TL, + vec2(1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_TR, + vec2(-1.0, 1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_BR, + vec2(-1.0), + afwidth); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + vClipCenter_Radius_BL, + vec2(1.0, -1.0), + afwidth); + + return smoothstep(0.0, afwidth, 1.0 - current_distance); +} + + +void main(void) { + float alpha = 1.f; + vec2 local_pos = init_transform_fs(vPos, alpha); + + float clip_alpha = rounded_rect(local_pos); + + float combined_alpha = min(alpha, clip_alpha); + + // Select alpha or inverse alpha depending on clip in/out. + float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode); + + oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0); +} +#endif diff --git a/gfx/webrender/res/cs_clip_rectangle.vs.glsl b/gfx/webrender/res/cs_clip_rectangle.vs.glsl deleted file mode 100644 index 6a4282ed4abc..000000000000 --- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl +++ /dev/null @@ -1,76 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -struct ClipRect { - RectWithSize rect; - vec4 mode; -}; - -ClipRect fetch_clip_rect(ivec2 address) { - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); -} - -struct ClipCorner { - RectWithSize rect; - vec4 outer_inner_radius; -}; - -ClipCorner fetch_clip_corner(ivec2 address, int index) { - address += ivec2(2 + 2 * index, 0); - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); -} - -struct ClipData { - ClipRect rect; - ClipCorner top_left; - ClipCorner top_right; - ClipCorner bottom_left; - ClipCorner bottom_right; -}; - -ClipData fetch_clip(ivec2 address) { - ClipData clip; - - clip.rect = fetch_clip_rect(address); - clip.top_left = fetch_clip_corner(address, 0); - clip.top_right = fetch_clip_corner(address, 1); - clip.bottom_left = fetch_clip_corner(address, 2); - clip.bottom_right = fetch_clip_corner(address, 3); - - return clip; -} - -void main(void) { - CacheClipInstance cci = fetch_clip_item(gl_InstanceID); - ClipArea area = fetch_clip_area(cci.render_task_index); - Layer layer = fetch_layer(cci.layer_index); - ClipData clip = fetch_clip(cci.clip_data_address); - RectWithSize local_rect = clip.rect.rect; - - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, - layer, - area, - cci.segment); - vPos = vi.local_pos; - - vClipMode = clip.rect.mode.x; - - RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); - - vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy, - clip.top_left.outer_inner_radius.xy); - - vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x, - clip_rect.p0.y + clip.top_right.outer_inner_radius.y, - clip.top_right.outer_inner_radius.xy); - - vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy, - clip.bottom_right.outer_inner_radius.xy); - - vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x, - clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y, - clip.bottom_left.outer_inner_radius.xy); -} diff --git a/gfx/webrender/res/ellipse.glsl b/gfx/webrender/res/ellipse.glsl new file mode 100644 index 000000000000..5d07669402c0 --- /dev/null +++ b/gfx/webrender/res/ellipse.glsl @@ -0,0 +1,69 @@ +/* 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/. */ + +#ifdef WR_FRAGMENT_SHADER + +// +// Signed distance to an ellipse. +// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm +// Note that this fails for exact circles. +// +float sdEllipse( vec2 p, in vec2 ab ) { + p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; } + float l = ab.y*ab.y - ab.x*ab.x; + + float m = ab.x*p.x/l; + float n = ab.y*p.y/l; + float m2 = m*m; + float n2 = n*n; + + float c = (m2 + n2 - 1.0)/3.0; + float c3 = c*c*c; + + float q = c3 + m2*n2*2.0; + float d = c3 + m2*n2; + float g = m + m*n2; + + float co; + + if( d<0.0 ) + { + float p = acos(q/c3)/3.0; + float s = cos(p); + float t = sin(p)*sqrt(3.0); + float rx = sqrt( -c*(s + t + 2.0) + m2 ); + float ry = sqrt( -c*(s - t + 2.0) + m2 ); + co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0; + } + else + { + float h = 2.0*m*n*sqrt( d ); + float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 ); + float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 ); + float rx = -s - u - c*4.0 + 2.0*m2; + float ry = (s - u)*sqrt(3.0); + float rm = sqrt( rx*rx + ry*ry ); + float p = ry/sqrt(rm-rx); + co = (p + 2.0*g/rm - m)/2.0; + } + + float si = sqrt( 1.0 - co*co ); + + vec2 r = vec2( ab.x*co, ab.y*si ); + + return length(r - p ) * sign(p.y-r.y); +} + +float distance_to_ellipse(vec2 p, vec2 radii) { + // sdEllipse fails on exact circles, so handle equal + // radii here. The branch coherency should make this + // a performance win for the circle case too. + if (radii.x == radii.y) { + return length(p) - radii.x; + } else { + return sdEllipse(p, radii); + } +} + +#endif diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index 887241ea2aef..c706130620f9 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -855,66 +855,4 @@ vec4 sample_gradient(int address, float offset, float gradient_repeat) { return dither(mix(texels[0], texels[1], fract(x))); } -// -// Signed distance to an ellipse. -// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm -// Note that this fails for exact circles. -// -float sdEllipse( vec2 p, in vec2 ab ) { - p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; } - float l = ab.y*ab.y - ab.x*ab.x; - - float m = ab.x*p.x/l; - float n = ab.y*p.y/l; - float m2 = m*m; - float n2 = n*n; - - float c = (m2 + n2 - 1.0)/3.0; - float c3 = c*c*c; - - float q = c3 + m2*n2*2.0; - float d = c3 + m2*n2; - float g = m + m*n2; - - float co; - - if( d<0.0 ) - { - float p = acos(q/c3)/3.0; - float s = cos(p); - float t = sin(p)*sqrt(3.0); - float rx = sqrt( -c*(s + t + 2.0) + m2 ); - float ry = sqrt( -c*(s - t + 2.0) + m2 ); - co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0; - } - else - { - float h = 2.0*m*n*sqrt( d ); - float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 ); - float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 ); - float rx = -s - u - c*4.0 + 2.0*m2; - float ry = (s - u)*sqrt(3.0); - float rm = sqrt( rx*rx + ry*ry ); - float p = ry/sqrt(rm-rx); - co = (p + 2.0*g/rm - m)/2.0; - } - - float si = sqrt( 1.0 - co*co ); - - vec2 r = vec2( ab.x*co, ab.y*si ); - - return length(r - p ) * sign(p.y-r.y); -} - -float distance_to_ellipse(vec2 p, vec2 radii) { - // sdEllipse fails on exact circles, so handle equal - // radii here. The branch coherency should make this - // a performance win for the circle case too. - if (radii.x == radii.y) { - return length(p) - radii.x; - } else { - return sdEllipse(p, radii); - } -} - #endif //WR_FRAGMENT_SHADER diff --git a/gfx/webrender/res/ps_border_corner.fs.glsl b/gfx/webrender/res/ps_border_corner.fs.glsl deleted file mode 100644 index 763c2b6daf3e..000000000000 --- a/gfx/webrender/res/ps_border_corner.fs.glsl +++ /dev/null @@ -1,85 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = 0.0; - vec2 local_pos = init_transform_fs(vLocalPos, alpha); -#else - vec2 local_pos = vLocalPos; -#endif - - alpha = min(alpha, do_clip()); - - // Find the appropriate distance to apply the AA smoothstep over. - vec2 fw = fwidth(local_pos); - float afwidth = length(fw); - float distance_for_color; - float color_mix_factor; - - // Only apply the clip AA if inside the clip region. This is - // necessary for correctness when the border width is greater - // than the border radius. - if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) { - vec2 p = local_pos - vClipCenter; - - // Get signed distance from the inner/outer clips. - float d0 = distance_to_ellipse(p, vRadii0.xy); - float d1 = distance_to_ellipse(p, vRadii0.zw); - float d2 = distance_to_ellipse(p, vRadii1.xy); - float d3 = distance_to_ellipse(p, vRadii1.zw); - - // SDF subtract main radii - float d_main = max(d0, 0.5 * afwidth - d1); - - // SDF subtract inner radii (double style borders) - float d_inner = max(d2 - 0.5 * afwidth, -d3); - - // Select how to combine the SDF based on border style. - float d = mix(max(d_main, -d_inner), d_main, vSDFSelect); - - // Only apply AA to fragments outside the signed distance field. - alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d)); - - // Get the groove/ridge mix factor. - color_mix_factor = smoothstep(-0.5 * afwidth, - 0.5 * afwidth, - -d2); - } else { - // Handle the case where the fragment is outside the clip - // region in a corner. This occurs when border width is - // greater than border radius. - - // Get linear distances along horizontal and vertical edges. - vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz); - vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw); - // Apply union to get the outer edge signed distance. - float da = min(d0.x, d1.x); - // Apply intersection to get the inner edge signed distance. - float db = max(-d0.y, -d1.y); - // Apply union to get both edges. - float d = min(da, db); - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Get the groove/ridge mix factor. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - color_mix_factor = mix(0.0, 1.0, da > 0.0); - } - - // Mix inner/outer color. - vec4 color0 = mix(vColor00, vColor01, color_mix_factor); - vec4 color1 = mix(vColor10, vColor11, color_mix_factor); - - // Select color based on side of line. Get distance from the - // reference line, and then apply AA along the edge. - float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos); - float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld); - vec4 color = mix(color0, color1, m); - - oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); -} diff --git a/gfx/webrender/res/ps_border_corner.glsl b/gfx/webrender/res/ps_border_corner.glsl index 70ba7b04042e..c3ce7f4794d0 100644 --- a/gfx/webrender/res/ps_border_corner.glsl +++ b/gfx/webrender/res/ps_border_corner.glsl @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include shared,prim_shared,shared_border +#include shared,prim_shared,shared_border,ellipse // Edge color transition flat varying vec4 vColor00; @@ -27,3 +27,371 @@ varying vec3 vLocalPos; #else varying vec2 vLocalPos; #endif + +#ifdef WR_VERTEX_SHADER +// Matches BorderCornerSide enum in border.rs +#define SIDE_BOTH 0 +#define SIDE_FIRST 1 +#define SIDE_SECOND 2 + +vec2 get_radii(vec2 radius, vec2 invalid) { + if (all(greaterThan(radius, vec2(0.0)))) { + return radius; + } + + return invalid; +} + +void set_radii(int style, + vec2 radii, + vec2 widths, + vec2 adjusted_widths) { + vRadii0.xy = get_radii(radii, 2.0 * widths); + vRadii0.zw = get_radii(radii - widths, -widths); + + switch (style) { + case BORDER_STYLE_RIDGE: + case BORDER_STYLE_GROOVE: + vRadii1.xy = radii - adjusted_widths; + // See comment in default branch + vRadii1.zw = vec2(-100.0); + break; + case BORDER_STYLE_DOUBLE: + vRadii1.xy = get_radii(radii - adjusted_widths, -widths); + vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); + break; + default: + // These aren't needed, so we set them to some reasonably large + // negative value so later computations will discard them. This + // avoids branches and numerical issues in the fragment shader. + vRadii1.xy = vec2(-100.0); + vRadii1.zw = vec2(-100.0); + break; + } +} + +void set_edge_line(vec2 border_width, + vec2 outer_corner, + vec2 gradient_sign) { + vec2 gradient = border_width * gradient_sign; + vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); +} + +void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { + vec4 modulate; + + switch (style) { + case BORDER_STYLE_GROOVE: + modulate = vec4(1.0 - 0.3 * delta.x, + 1.0 + 0.3 * delta.x, + 1.0 - 0.3 * delta.y, + 1.0 + 0.3 * delta.y); + + break; + case BORDER_STYLE_RIDGE: + modulate = vec4(1.0 + 0.3 * delta.x, + 1.0 - 0.3 * delta.x, + 1.0 + 0.3 * delta.y, + 1.0 - 0.3 * delta.y); + break; + default: + modulate = vec4(1.0); + break; + } + + // Optionally mask out one side of the border corner, + // depending on the instance kind. + switch (instance_kind) { + case SIDE_FIRST: + color0.a = 0.0; + break; + case SIDE_SECOND: + color1.a = 0.0; + break; + } + + vColor00 = vec4(color0.rgb * modulate.x, color0.a); + vColor01 = vec4(color0.rgb * modulate.y, color0.a); + vColor10 = vec4(color1.rgb * modulate.z, color1.a); + vColor11 = vec4(color1.rgb * modulate.w, color1.a); +} + +int select_style(int color_select, vec2 fstyle) { + ivec2 style = ivec2(fstyle); + + switch (color_select) { + case SIDE_BOTH: + { + // TODO(gw): A temporary hack! While we don't support + // border corners that have dots or dashes + // with another style, pretend they are solid + // border corners. + bool has_dots = style.x == BORDER_STYLE_DOTTED || + style.y == BORDER_STYLE_DOTTED; + bool has_dashes = style.x == BORDER_STYLE_DASHED || + style.y == BORDER_STYLE_DASHED; + if (style.x != style.y && (has_dots || has_dashes)) + return BORDER_STYLE_SOLID; + return style.x; + } + case SIDE_FIRST: + return style.x; + case SIDE_SECOND: + return style.y; + } +} + +void main(void) { + Primitive prim = load_primitive(); + Border border = fetch_border(prim.specific_prim_address); + int sub_part = prim.user_data0; + BorderCorners corners = get_border_corners(border, prim.local_rect); + + vec2 p0, p1; + + // TODO(gw): We'll need to pass through multiple styles + // once we support style transitions per corner. + int style; + vec4 edge_distances; + vec4 color0, color1; + vec2 color_delta; + + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + + switch (sub_part) { + case 0: { + p0 = corners.tl_outer; + p1 = corners.tl_inner; + color0 = border.colors[0]; + color1 = border.colors[1]; + vClipCenter = corners.tl_outer + border.radii[0].xy; + vClipSign = vec2(1.0); + style = select_style(prim.user_data1, border.style.yx); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[0].xy, + border.widths.xy, + adjusted_widths.xy); + set_edge_line(border.widths.xy, + corners.tl_outer, + vec2(1.0, 1.0)); + edge_distances = vec4(p0 + adjusted_widths.xy, + p0 + inv_adjusted_widths.xy); + color_delta = vec2(1.0); + break; + } + case 1: { + p0 = vec2(corners.tr_inner.x, corners.tr_outer.y); + p1 = vec2(corners.tr_outer.x, corners.tr_inner.y); + color0 = border.colors[1]; + color1 = border.colors[2]; + vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); + vClipSign = vec2(-1.0, 1.0); + style = select_style(prim.user_data1, border.style.zy); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[0].zw, + border.widths.zy, + adjusted_widths.zy); + set_edge_line(border.widths.zy, + corners.tr_outer, + vec2(-1.0, 1.0)); + edge_distances = vec4(p1.x - adjusted_widths.z, + p0.y + adjusted_widths.y, + p1.x - border.widths.z + adjusted_widths.z, + p0.y + inv_adjusted_widths.y); + color_delta = vec2(1.0, -1.0); + break; + } + case 2: { + p0 = corners.br_inner; + p1 = corners.br_outer; + color0 = border.colors[2]; + color1 = border.colors[3]; + vClipCenter = corners.br_outer - border.radii[1].xy; + vClipSign = vec2(-1.0, -1.0); + style = select_style(prim.user_data1, border.style.wz); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[1].xy, + border.widths.zw, + adjusted_widths.zw); + set_edge_line(border.widths.zw, + corners.br_outer, + vec2(-1.0, -1.0)); + edge_distances = vec4(p1.x - adjusted_widths.z, + p1.y - adjusted_widths.w, + p1.x - border.widths.z + adjusted_widths.z, + p1.y - border.widths.w + adjusted_widths.w); + color_delta = vec2(-1.0); + break; + } + case 3: { + p0 = vec2(corners.bl_outer.x, corners.bl_inner.y); + p1 = vec2(corners.bl_inner.x, corners.bl_outer.y); + color0 = border.colors[3]; + color1 = border.colors[0]; + vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); + vClipSign = vec2(1.0, -1.0); + style = select_style(prim.user_data1, border.style.xw); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, + border.radii[1].zw, + border.widths.xw, + adjusted_widths.xw); + set_edge_line(border.widths.xw, + corners.bl_outer, + vec2(1.0, -1.0)); + edge_distances = vec4(p0.x + adjusted_widths.x, + p1.y - adjusted_widths.w, + p0.x + inv_adjusted_widths.x, + p1.y - border.widths.w + adjusted_widths.w); + color_delta = vec2(-1.0, 1.0); + break; + } + } + + switch (style) { + case BORDER_STYLE_DOUBLE: { + vEdgeDistance = edge_distances; + vAlphaSelect = 0.0; + vSDFSelect = 0.0; + break; + } + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: + vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0); + vAlphaSelect = 1.0; + vSDFSelect = 1.0; + break; + case BORDER_STYLE_DOTTED: + // Disable normal clip radii for dotted corners, since + // all the clipping is handled by the clip mask. + vClipSign = vec2(0.0); + vEdgeDistance = vec4(0.0); + vAlphaSelect = 1.0; + vSDFSelect = 0.0; + break; + default: { + vEdgeDistance = vec4(0.0); + vAlphaSelect = 1.0; + vSDFSelect = 0.0; + break; + } + } + + write_color(color0, color1, style, color_delta, prim.user_data1); + + RectWithSize segment_rect; + segment_rect.p0 = p0; + segment_rect.size = p1 - p0; + +#ifdef WR_FEATURE_TRANSFORM + TransformVertexInfo vi = write_transform_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#else + VertexInfo vi = write_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#endif + + vLocalPos = vi.local_pos; + write_clip(vi.screen_pos, prim.clip_area); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + float alpha = 1.0; +#ifdef WR_FEATURE_TRANSFORM + alpha = 0.0; + vec2 local_pos = init_transform_fs(vLocalPos, alpha); +#else + vec2 local_pos = vLocalPos; +#endif + + alpha = min(alpha, do_clip()); + + // Find the appropriate distance to apply the AA smoothstep over. + vec2 fw = fwidth(local_pos); + float afwidth = length(fw); + float distance_for_color; + float color_mix_factor; + + // Only apply the clip AA if inside the clip region. This is + // necessary for correctness when the border width is greater + // than the border radius. + if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) { + vec2 p = local_pos - vClipCenter; + + // Get signed distance from the inner/outer clips. + float d0 = distance_to_ellipse(p, vRadii0.xy); + float d1 = distance_to_ellipse(p, vRadii0.zw); + float d2 = distance_to_ellipse(p, vRadii1.xy); + float d3 = distance_to_ellipse(p, vRadii1.zw); + + // SDF subtract main radii + float d_main = max(d0, 0.5 * afwidth - d1); + + // SDF subtract inner radii (double style borders) + float d_inner = max(d2 - 0.5 * afwidth, -d3); + + // Select how to combine the SDF based on border style. + float d = mix(max(d_main, -d_inner), d_main, vSDFSelect); + + // Only apply AA to fragments outside the signed distance field. + alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d)); + + // Get the groove/ridge mix factor. + color_mix_factor = smoothstep(-0.5 * afwidth, + 0.5 * afwidth, + -d2); + } else { + // Handle the case where the fragment is outside the clip + // region in a corner. This occurs when border width is + // greater than border radius. + + // Get linear distances along horizontal and vertical edges. + vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz); + vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw); + // Apply union to get the outer edge signed distance. + float da = min(d0.x, d1.x); + // Apply intersection to get the inner edge signed distance. + float db = max(-d0.y, -d1.y); + // Apply union to get both edges. + float d = min(da, db); + // Select fragment on/off based on signed distance. + // No AA here, since we know we're on a straight edge + // and the width is rounded to a whole CSS pixel. + alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); + + // Get the groove/ridge mix factor. + // TODO(gw): Support AA for groove/ridge border edge with transforms. + color_mix_factor = mix(0.0, 1.0, da > 0.0); + } + + // Mix inner/outer color. + vec4 color0 = mix(vColor00, vColor01, color_mix_factor); + vec4 color1 = mix(vColor10, vColor11, color_mix_factor); + + // Select color based on side of line. Get distance from the + // reference line, and then apply AA along the edge. + float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos); + float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld); + vec4 color = mix(color0, color1, m); + + oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); +} +#endif diff --git a/gfx/webrender/res/ps_border_corner.vs.glsl b/gfx/webrender/res/ps_border_corner.vs.glsl deleted file mode 100644 index bf8a961b4a26..000000000000 --- a/gfx/webrender/res/ps_border_corner.vs.glsl +++ /dev/null @@ -1,285 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Matches BorderCornerSide enum in border.rs -#define SIDE_BOTH 0 -#define SIDE_FIRST 1 -#define SIDE_SECOND 2 - -vec2 get_radii(vec2 radius, vec2 invalid) { - if (all(greaterThan(radius, vec2(0.0)))) { - return radius; - } - - return invalid; -} - -void set_radii(int style, - vec2 radii, - vec2 widths, - vec2 adjusted_widths) { - vRadii0.xy = get_radii(radii, 2.0 * widths); - vRadii0.zw = get_radii(radii - widths, -widths); - - switch (style) { - case BORDER_STYLE_RIDGE: - case BORDER_STYLE_GROOVE: - vRadii1.xy = radii - adjusted_widths; - // See comment in default branch - vRadii1.zw = vec2(-100.0); - break; - case BORDER_STYLE_DOUBLE: - vRadii1.xy = get_radii(radii - adjusted_widths, -widths); - vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); - break; - default: - // These aren't needed, so we set them to some reasonably large - // negative value so later computations will discard them. This - // avoids branches and numerical issues in the fragment shader. - vRadii1.xy = vec2(-100.0); - vRadii1.zw = vec2(-100.0); - break; - } -} - -void set_edge_line(vec2 border_width, - vec2 outer_corner, - vec2 gradient_sign) { - vec2 gradient = border_width * gradient_sign; - vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); -} - -void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { - vec4 modulate; - - switch (style) { - case BORDER_STYLE_GROOVE: - modulate = vec4(1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.y, - 1.0 + 0.3 * delta.y); - - break; - case BORDER_STYLE_RIDGE: - modulate = vec4(1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.y, - 1.0 - 0.3 * delta.y); - break; - default: - modulate = vec4(1.0); - break; - } - - // Optionally mask out one side of the border corner, - // depending on the instance kind. - switch (instance_kind) { - case SIDE_FIRST: - color0.a = 0.0; - break; - case SIDE_SECOND: - color1.a = 0.0; - break; - } - - vColor00 = vec4(color0.rgb * modulate.x, color0.a); - vColor01 = vec4(color0.rgb * modulate.y, color0.a); - vColor10 = vec4(color1.rgb * modulate.z, color1.a); - vColor11 = vec4(color1.rgb * modulate.w, color1.a); -} - -int select_style(int color_select, vec2 fstyle) { - ivec2 style = ivec2(fstyle); - - switch (color_select) { - case SIDE_BOTH: - { - // TODO(gw): A temporary hack! While we don't support - // border corners that have dots or dashes - // with another style, pretend they are solid - // border corners. - bool has_dots = style.x == BORDER_STYLE_DOTTED || - style.y == BORDER_STYLE_DOTTED; - bool has_dashes = style.x == BORDER_STYLE_DASHED || - style.y == BORDER_STYLE_DASHED; - if (style.x != style.y && (has_dots || has_dashes)) - return BORDER_STYLE_SOLID; - return style.x; - } - case SIDE_FIRST: - return style.x; - case SIDE_SECOND: - return style.y; - } -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - - vec2 p0, p1; - - // TODO(gw): We'll need to pass through multiple styles - // once we support style transitions per corner. - int style; - vec4 edge_distances; - vec4 color0, color1; - vec2 color_delta; - - // TODO(gw): Now that all border styles are supported, the switch - // statement below can be tidied up quite a bit. - - switch (sub_part) { - case 0: { - p0 = corners.tl_outer; - p1 = corners.tl_inner; - color0 = border.colors[0]; - color1 = border.colors[1]; - vClipCenter = corners.tl_outer + border.radii[0].xy; - vClipSign = vec2(1.0); - style = select_style(prim.user_data1, border.style.yx); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].xy, - border.widths.xy, - adjusted_widths.xy); - set_edge_line(border.widths.xy, - corners.tl_outer, - vec2(1.0, 1.0)); - edge_distances = vec4(p0 + adjusted_widths.xy, - p0 + inv_adjusted_widths.xy); - color_delta = vec2(1.0); - break; - } - case 1: { - p0 = vec2(corners.tr_inner.x, corners.tr_outer.y); - p1 = vec2(corners.tr_outer.x, corners.tr_inner.y); - color0 = border.colors[1]; - color1 = border.colors[2]; - vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); - vClipSign = vec2(-1.0, 1.0); - style = select_style(prim.user_data1, border.style.zy); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].zw, - border.widths.zy, - adjusted_widths.zy); - set_edge_line(border.widths.zy, - corners.tr_outer, - vec2(-1.0, 1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p0.y + adjusted_widths.y, - p1.x - border.widths.z + adjusted_widths.z, - p0.y + inv_adjusted_widths.y); - color_delta = vec2(1.0, -1.0); - break; - } - case 2: { - p0 = corners.br_inner; - p1 = corners.br_outer; - color0 = border.colors[2]; - color1 = border.colors[3]; - vClipCenter = corners.br_outer - border.radii[1].xy; - vClipSign = vec2(-1.0, -1.0); - style = select_style(prim.user_data1, border.style.wz); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].xy, - border.widths.zw, - adjusted_widths.zw); - set_edge_line(border.widths.zw, - corners.br_outer, - vec2(-1.0, -1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p1.y - adjusted_widths.w, - p1.x - border.widths.z + adjusted_widths.z, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0); - break; - } - case 3: { - p0 = vec2(corners.bl_outer.x, corners.bl_inner.y); - p1 = vec2(corners.bl_inner.x, corners.bl_outer.y); - color0 = border.colors[3]; - color1 = border.colors[0]; - vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); - vClipSign = vec2(1.0, -1.0); - style = select_style(prim.user_data1, border.style.xw); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].zw, - border.widths.xw, - adjusted_widths.xw); - set_edge_line(border.widths.xw, - corners.bl_outer, - vec2(1.0, -1.0)); - edge_distances = vec4(p0.x + adjusted_widths.x, - p1.y - adjusted_widths.w, - p0.x + inv_adjusted_widths.x, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0, 1.0); - break; - } - } - - switch (style) { - case BORDER_STYLE_DOUBLE: { - vEdgeDistance = edge_distances; - vAlphaSelect = 0.0; - vSDFSelect = 0.0; - break; - } - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0); - vAlphaSelect = 1.0; - vSDFSelect = 1.0; - break; - case BORDER_STYLE_DOTTED: - // Disable normal clip radii for dotted corners, since - // all the clipping is handled by the clip mask. - vClipSign = vec2(0.0); - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - default: { - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - } - } - - write_color(color0, color1, style, color_delta, prim.user_data1); - - RectWithSize segment_rect; - segment_rect.p0 = p0; - segment_rect.size = p1 - p0; - -#ifdef WR_FEATURE_TRANSFORM - TransformVertexInfo vi = write_transform_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} diff --git a/gfx/webrender/res/ps_border_edge.fs.glsl b/gfx/webrender/res/ps_border_edge.fs.glsl deleted file mode 100644 index d59d214e6d4b..000000000000 --- a/gfx/webrender/res/ps_border_edge.fs.glsl +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = 0.0; - vec2 local_pos = init_transform_fs(vLocalPos, alpha); -#else - vec2 local_pos = vLocalPos; -#endif - - alpha = min(alpha, do_clip()); - - // Find the appropriate distance to apply the step over. - vec2 fw = fwidth(local_pos); - float afwidth = length(fw); - - // Applies the math necessary to draw a style: double - // border. In the case of a solid border, the vertex - // shader sets interpolator values that make this have - // no effect. - - // Select the x/y coord, depending on which axis this edge is. - vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect); - - // Get signed distance from each of the inner edges. - float d0 = pos.x - vEdgeDistance.x; - float d1 = vEdgeDistance.y - pos.x; - - // SDF union to select both outer edges. - float d = min(d0, d1); - - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Mix color based on first distance. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0)); - - // Apply dashing / dotting parameters. - - // Get the main-axis position relative to closest dot or dash. - float x = mod(pos.y - vClipParams.x, vClipParams.y); - - // Calculate dash alpha (on/off) based on dash length - float dash_alpha = step(x, vClipParams.z); - - // Get the dot alpha - vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw; - float dot_distance = length(dot_relative_pos) - vClipParams.z; - float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth, - 0.5 * afwidth, - dot_distance); - - // Select between dot/dash alpha based on clip mode. - alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect)); - - oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); -} diff --git a/gfx/webrender/res/ps_border_edge.glsl b/gfx/webrender/res/ps_border_edge.glsl index 294b9422a1b7..8c72d2839880 100644 --- a/gfx/webrender/res/ps_border_edge.glsl +++ b/gfx/webrender/res/ps_border_edge.glsl @@ -17,3 +17,287 @@ varying vec3 vLocalPos; #else varying vec2 vLocalPos; #endif + +#ifdef WR_VERTEX_SHADER +void write_edge_distance(float p0, + float original_width, + float adjusted_width, + float style, + float axis_select, + float sign_adjust) { + switch (int(style)) { + case BORDER_STYLE_DOUBLE: + vEdgeDistance = vec2(p0 + adjusted_width, + p0 + original_width - adjusted_width); + break; + case BORDER_STYLE_GROOVE: + case BORDER_STYLE_RIDGE: + vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust); + break; + default: + vEdgeDistance = vec2(0.0); + break; + } + + vAxisSelect = axis_select; +} + +void write_alpha_select(float style) { + switch (int(style)) { + case BORDER_STYLE_DOUBLE: + vAlphaSelect = 0.0; + break; + default: + vAlphaSelect = 1.0; + break; + } +} + +// write_color function is duplicated to work around a Mali-T880 GPU driver program link error. +// See https://github.com/servo/webrender/issues/1403 for more info. +// TODO: convert back to a single function once the driver issues are resolved, if ever. +void write_color0(vec4 color, float style, bool flip) { + vec2 modulate; + + switch (int(style)) { + case BORDER_STYLE_GROOVE: + { + modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); + break; + } + case BORDER_STYLE_RIDGE: + { + modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); + break; + } + default: + modulate = vec2(1.0); + break; + } + + vColor0 = vec4(color.rgb * modulate.x, color.a); +} + +void write_color1(vec4 color, float style, bool flip) { + vec2 modulate; + + switch (int(style)) { + case BORDER_STYLE_GROOVE: + { + modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); + break; + } + case BORDER_STYLE_RIDGE: + { + modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); + break; + } + default: + modulate = vec2(1.0); + break; + } + + vColor1 = vec4(color.rgb * modulate.y, color.a); +} + +void write_clip_params(float style, + float border_width, + float edge_length, + float edge_offset, + float center_line) { + // x = offset + // y = dash on + off length + // z = dash length + // w = center line of edge cross-axis (for dots only) + switch (int(style)) { + case BORDER_STYLE_DASHED: { + float desired_dash_length = border_width * 3.0; + // Consider half total length since there is an equal on/off for each dash. + float dash_count = ceil(0.5 * edge_length / desired_dash_length); + float dash_length = 0.5 * edge_length / dash_count; + vClipParams = vec4(edge_offset - 0.5 * dash_length, + 2.0 * dash_length, + dash_length, + 0.0); + vClipSelect = 0.0; + break; + } + case BORDER_STYLE_DOTTED: { + float diameter = border_width; + float radius = 0.5 * diameter; + float dot_count = ceil(0.5 * edge_length / diameter); + float empty_space = edge_length - dot_count * diameter; + float distance_between_centers = diameter + empty_space / dot_count; + vClipParams = vec4(edge_offset - radius, + distance_between_centers, + radius, + center_line); + vClipSelect = 1.0; + break; + } + default: + vClipParams = vec4(1.0); + vClipSelect = 0.0; + break; + } +} + +void main(void) { + Primitive prim = load_primitive(); + Border border = fetch_border(prim.specific_prim_address); + int sub_part = prim.user_data0; + BorderCorners corners = get_border_corners(border, prim.local_rect); + vec4 color = border.colors[sub_part]; + + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + + float style; + bool color_flip; + + RectWithSize segment_rect; + switch (sub_part) { + case 0: { + segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); + segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); + write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); + style = border.style.x; + color_flip = false; + write_clip_params(border.style.x, + border.widths.x, + segment_rect.size.y, + segment_rect.p0.y, + segment_rect.p0.x + 0.5 * segment_rect.size.x); + break; + } + case 1: { + segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); + segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); + write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); + style = border.style.y; + color_flip = false; + write_clip_params(border.style.y, + border.widths.y, + segment_rect.size.x, + segment_rect.p0.x, + segment_rect.p0.y + 0.5 * segment_rect.size.y); + break; + } + case 2: { + segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); + segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); + write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); + style = border.style.z; + color_flip = true; + write_clip_params(border.style.z, + border.widths.z, + segment_rect.size.y, + segment_rect.p0.y, + segment_rect.p0.x + 0.5 * segment_rect.size.x); + break; + } + case 3: { + segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); + segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); + write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); + style = border.style.w; + color_flip = true; + write_clip_params(border.style.w, + border.widths.w, + segment_rect.size.x, + segment_rect.p0.x, + segment_rect.p0.y + 0.5 * segment_rect.size.y); + break; + } + } + + write_alpha_select(style); + write_color0(color, style, color_flip); + write_color1(color, style, color_flip); + +#ifdef WR_FEATURE_TRANSFORM + TransformVertexInfo vi = write_transform_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#else + VertexInfo vi = write_vertex(segment_rect, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + prim.local_rect); +#endif + + vLocalPos = vi.local_pos; + write_clip(vi.screen_pos, prim.clip_area); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + float alpha = 1.0; +#ifdef WR_FEATURE_TRANSFORM + alpha = 0.0; + vec2 local_pos = init_transform_fs(vLocalPos, alpha); +#else + vec2 local_pos = vLocalPos; +#endif + + alpha = min(alpha, do_clip()); + + // Find the appropriate distance to apply the step over. + vec2 fw = fwidth(local_pos); + float afwidth = length(fw); + + // Applies the math necessary to draw a style: double + // border. In the case of a solid border, the vertex + // shader sets interpolator values that make this have + // no effect. + + // Select the x/y coord, depending on which axis this edge is. + vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect); + + // Get signed distance from each of the inner edges. + float d0 = pos.x - vEdgeDistance.x; + float d1 = vEdgeDistance.y - pos.x; + + // SDF union to select both outer edges. + float d = min(d0, d1); + + // Select fragment on/off based on signed distance. + // No AA here, since we know we're on a straight edge + // and the width is rounded to a whole CSS pixel. + alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); + + // Mix color based on first distance. + // TODO(gw): Support AA for groove/ridge border edge with transforms. + vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0)); + + // Apply dashing / dotting parameters. + + // Get the main-axis position relative to closest dot or dash. + float x = mod(pos.y - vClipParams.x, vClipParams.y); + + // Calculate dash alpha (on/off) based on dash length + float dash_alpha = step(x, vClipParams.z); + + // Get the dot alpha + vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw; + float dot_distance = length(dot_relative_pos) - vClipParams.z; + float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth, + 0.5 * afwidth, + dot_distance); + + // Select between dot/dash alpha based on clip mode. + alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect)); + + oFragColor = color * vec4(1.0, 1.0, 1.0, alpha); +} +#endif diff --git a/gfx/webrender/res/ps_border_edge.vs.glsl b/gfx/webrender/res/ps_border_edge.vs.glsl deleted file mode 100644 index ef3349d36111..000000000000 --- a/gfx/webrender/res/ps_border_edge.vs.glsl +++ /dev/null @@ -1,223 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void write_edge_distance(float p0, - float original_width, - float adjusted_width, - float style, - float axis_select, - float sign_adjust) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vEdgeDistance = vec2(p0 + adjusted_width, - p0 + original_width - adjusted_width); - break; - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust); - break; - default: - vEdgeDistance = vec2(0.0); - break; - } - - vAxisSelect = axis_select; -} - -void write_alpha_select(float style) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vAlphaSelect = 0.0; - break; - default: - vAlphaSelect = 1.0; - break; - } -} - -// write_color function is duplicated to work around a Mali-T880 GPU driver program link error. -// See https://github.com/servo/webrender/issues/1403 for more info. -// TODO: convert back to a single function once the driver issues are resolved, if ever. -void write_color0(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor0 = vec4(color.rgb * modulate.x, color.a); -} - -void write_color1(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor1 = vec4(color.rgb * modulate.y, color.a); -} - -void write_clip_params(float style, - float border_width, - float edge_length, - float edge_offset, - float center_line) { - // x = offset - // y = dash on + off length - // z = dash length - // w = center line of edge cross-axis (for dots only) - switch (int(style)) { - case BORDER_STYLE_DASHED: { - float desired_dash_length = border_width * 3.0; - // Consider half total length since there is an equal on/off for each dash. - float dash_count = ceil(0.5 * edge_length / desired_dash_length); - float dash_length = 0.5 * edge_length / dash_count; - vClipParams = vec4(edge_offset - 0.5 * dash_length, - 2.0 * dash_length, - dash_length, - 0.0); - vClipSelect = 0.0; - break; - } - case BORDER_STYLE_DOTTED: { - float diameter = border_width; - float radius = 0.5 * diameter; - float dot_count = ceil(0.5 * edge_length / diameter); - float empty_space = edge_length - dot_count * diameter; - float distance_between_centers = diameter + empty_space / dot_count; - vClipParams = vec4(edge_offset - radius, - distance_between_centers, - radius, - center_line); - vClipSelect = 1.0; - break; - } - default: - vClipParams = vec4(1.0); - vClipSelect = 0.0; - break; - } -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - vec4 color = border.colors[sub_part]; - - // TODO(gw): Now that all border styles are supported, the switch - // statement below can be tidied up quite a bit. - - float style; - bool color_flip; - - RectWithSize segment_rect; - switch (sub_part) { - case 0: { - segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); - segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); - write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); - style = border.style.x; - color_flip = false; - write_clip_params(border.style.x, - border.widths.x, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x); - break; - } - case 1: { - segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); - segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); - write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); - style = border.style.y; - color_flip = false; - write_clip_params(border.style.y, - border.widths.y, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y); - break; - } - case 2: { - segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); - segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); - write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); - style = border.style.z; - color_flip = true; - write_clip_params(border.style.z, - border.widths.z, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x); - break; - } - case 3: { - segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); - segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); - write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); - style = border.style.w; - color_flip = true; - write_clip_params(border.style.w, - border.widths.w, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y); - break; - } - } - - write_alpha_select(style); - write_color0(color, style, color_flip); - write_color1(color, style, color_flip); - -#ifdef WR_FEATURE_TRANSFORM - TransformVertexInfo vi = write_transform_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} diff --git a/gfx/webrender/res/ps_box_shadow.fs.glsl b/gfx/webrender/res/ps_box_shadow.fs.glsl index 1bbaedf9eb17..26fbff4fd79e 100644 --- a/gfx/webrender/res/ps_box_shadow.fs.glsl +++ b/gfx/webrender/res/ps_box_shadow.fs.glsl @@ -18,5 +18,6 @@ void main(void) { uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv); // Modulate the box shadow by the color. - oFragColor = clip_scale * dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z))); + float mask = texture(sColor1, vec3(uv, vUv.z)).r; + oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask)); } diff --git a/gfx/webrender/res/ps_box_shadow.vs.glsl b/gfx/webrender/res/ps_box_shadow.vs.glsl index 6f4b8695a13c..7ac47a6a92ae 100644 --- a/gfx/webrender/res/ps_box_shadow.vs.glsl +++ b/gfx/webrender/res/ps_box_shadow.vs.glsl @@ -27,7 +27,7 @@ void main(void) { vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size; vMirrorPoint = 0.5 * prim.local_rect.size / patch_size; - vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0)); + vec2 texture_size = vec2(textureSize(sCacheA8, 0)); vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy; vColor = bs.color; diff --git a/gfx/webrender/src/debug_server.rs b/gfx/webrender/src/debug_server.rs index 4078f5353bf8..92d650cc39f5 100644 --- a/gfx/webrender/src/debug_server.rs +++ b/gfx/webrender/src/debug_server.rs @@ -14,8 +14,9 @@ use ws; // debug command queue. These are sent in a separate queue so // that none of these types are exposed to the RenderApi interfaces. // We can't use select!() as it's not stable... -pub enum DebugMsg { - FetchPasses(ws::Sender), +enum DebugMsg { + AddSender(ws::Sender), + RemoveSender(ws::util::Token), } // Represents a connection to a client. @@ -26,6 +27,16 @@ struct Server { } impl ws::Handler for Server { + fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { + self.debug_tx.send(DebugMsg::AddSender(self.ws.clone())).ok(); + + Ok(()) + } + + fn on_close(&mut self, _: ws::CloseCode, _: &str) { + self.debug_tx.send(DebugMsg::RemoveSender(self.ws.token())).ok(); + } + fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { match msg { ws::Message::Text(string) => { @@ -49,9 +60,10 @@ impl ws::Handler for Server { DebugCommand::EnableRenderTargetDebug(false) } "fetch_passes" => { - let msg = DebugMsg::FetchPasses(self.ws.clone()); - self.debug_tx.send(msg).unwrap(); - DebugCommand::Flush + DebugCommand::FetchPasses + } + "fetch_documents" => { + DebugCommand::FetchDocuments } msg => { println!("unknown msg {}", msg); @@ -74,7 +86,8 @@ impl ws::Handler for Server { pub struct DebugServer { join_handle: Option>, broadcaster: ws::Sender, - pub debug_rx: Receiver, + debug_rx: Receiver, + senders: Vec, } impl DebugServer { @@ -101,6 +114,42 @@ impl DebugServer { join_handle, broadcaster, debug_rx, + senders: Vec::new(), + } + } + + pub fn send(&mut self, message: String) { + // Add any new connections that have been queued. + while let Ok(msg) = self.debug_rx.try_recv() { + match msg { + DebugMsg::AddSender(sender) => { + self.senders.push(sender); + } + DebugMsg::RemoveSender(token) => { + self.senders.retain(|sender| { + sender.token() != token + }); + } + } + } + + // Broadcast the message to all senders. Keep + // track of the ones that failed, so they can + // be removed from the active sender list. + let mut disconnected_senders = Vec::new(); + + for (i, sender) in self.senders.iter().enumerate() { + if let Err(..) = sender.send(message.clone()) { + disconnected_senders.push(i); + } + } + + // Remove the broken senders from the list + // for next broadcast. Remove in reverse + // order so the indices are valid for the + // entire loop. + for i in disconnected_senders.iter().rev() { + self.senders.remove(*i); } } } @@ -190,3 +239,45 @@ struct Batch { description: String, count: usize, } + +#[derive(Serialize)] +pub struct TreeNode { + description: String, + children: Vec, +} + +impl TreeNode { + pub fn new(description: &str) -> TreeNode { + TreeNode { + description: description.to_owned(), + children: Vec::new(), + } + } + + pub fn add_child(&mut self, child: TreeNode) { + self.children.push(child); + } + + pub fn add_item(&mut self, description: &str) { + self.children.push(TreeNode::new(description)); + } +} + +#[derive(Serialize)] +pub struct DocumentList { + kind: &'static str, + root: TreeNode, +} + +impl DocumentList { + pub fn new() -> DocumentList { + DocumentList { + kind: "documents", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs index 7900623ca5dd..a3b53fcbb9ec 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device.rs @@ -1172,6 +1172,7 @@ impl Device { pub fn free_texture_storage(&mut self, texture: &mut Texture) { debug_assert!(self.inside_frame); + debug_assert_eq!(self.bound_pbo, PBOId(0)); if texture.format == ImageFormat::Invalid { return; @@ -1369,7 +1370,7 @@ impl Device { pub fn update_pbo_data(&mut self, data: &[T]) { debug_assert!(self.inside_frame); - debug_assert!(self.bound_pbo.0 != 0); + debug_assert_ne!(self.bound_pbo, PBOId(0)); gl::buffer_data(&*self.gl, gl::PIXEL_UNPACK_BUFFER, @@ -1379,7 +1380,7 @@ impl Device { pub fn orphan_pbo(&mut self, new_size: usize) { debug_assert!(self.inside_frame); - debug_assert!(self.bound_pbo.0 != 0); + debug_assert_ne!(self.bound_pbo, PBOId(0)); self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER, new_size as isize, diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index e12e30501c2e..6ca3f3cb7b64 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -95,6 +95,7 @@ impl NestedDisplayListInfo { struct FlattenContext<'a> { scene: &'a Scene, builder: &'a mut FrameBuilder, + resource_cache: &'a ResourceCache, tiled_image_map: TiledImageMap, replacements: Vec<(ClipId, ClipId)>, nested_display_list_info: Vec, @@ -104,11 +105,12 @@ struct FlattenContext<'a> { impl<'a> FlattenContext<'a> { fn new(scene: &'a Scene, builder: &'a mut FrameBuilder, - resource_cache: &ResourceCache) + resource_cache: &'a ResourceCache) -> FlattenContext<'a> { FlattenContext { scene, builder, + resource_cache, tiled_image_map: resource_cache.get_tiled_image_map(), replacements: Vec::new(), nested_display_list_info: Vec::new(), @@ -565,12 +567,12 @@ impl Frame { info.image_rendering); } SpecificDisplayItem::Text(ref text_info) => { + let instance = context.resource_cache.get_font_instance(text_info.font_key).unwrap(); context.builder.add_text(clip_and_scroll, reference_frame_relative_offset, item_rect_with_offset, &clip_with_offset, - text_info.font_key, - text_info.size, + instance, &text_info.color, item.glyphs(), item.display_list().get(item.glyphs()).count(), diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 9fcd8dea9515..bdad0d3dd64f 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -4,7 +4,8 @@ use api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize}; -use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; +use api::{ExtendMode, FontInstance, FontRenderMode}; +use api::{GlyphInstance, GlyphOptions, GradientStop}; use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize}; use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle}; use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow}; @@ -875,24 +876,37 @@ impl FrameBuilder { run_offset: LayoutVector2D, rect: LayerRect, local_clip: &LocalClip, - font_key: FontKey, - size: Au, + font: &FontInstance, color: &ColorF, glyph_range: ItemRange, glyph_count: usize, glyph_options: Option) { // Trivial early out checks - if size.0 <= 0 { + if font.size.0 <= 0 { + return + } + + // Sanity check - anything with glyphs bigger than this + // is probably going to consume too much memory to render + // efficiently anyway. This is specifically to work around + // the font_advance.html reftest, which creates a very large + // font as a crash test - the rendering is also ignored + // by the azure renderer. + if font.size >= Au::from_px(4096) { return } // TODO(gw): Use a proper algorithm to select // whether this item should be rendered with // subpixel AA! - let mut normal_render_mode = self.config.default_font_render_mode; + let mut default_render_mode = self.config.default_font_render_mode.limit_by(font.render_mode); + if let Some(options) = glyph_options { + default_render_mode = default_render_mode.limit_by(options.render_mode); + } // There are some conditions under which we can't use // subpixel text rendering, even if enabled. + let mut normal_render_mode = default_render_mode; if normal_render_mode == FontRenderMode::Subpixel { if color.a != 1.0 { normal_render_mode = FontRenderMode::Alpha; @@ -913,30 +927,32 @@ impl FrameBuilder { // Shadows never use subpixel AA, but need to respect the alpha/mono flag // for reftests. - let (shadow_render_mode, subpx_dir) = match self.config.default_font_render_mode { + let (shadow_render_mode, subpx_dir) = match default_render_mode { FontRenderMode::Subpixel | FontRenderMode::Alpha => { // TODO(gw): Expose subpixel direction in API once WR supports // vertical text runs. - (FontRenderMode::Alpha, SubpixelDirection::Horizontal) + (FontRenderMode::Alpha, font.subpx_dir) } FontRenderMode::Mono => { (FontRenderMode::Mono, SubpixelDirection::None) } }; + let prim_font = FontInstance::new(font.font_key, + font.size, + *color, + normal_render_mode, + subpx_dir, + font.platform_options); let prim = TextRunPrimitiveCpu { - font_key, - logical_font_size: size, + font: prim_font, glyph_range, glyph_count, glyph_gpu_blocks: Vec::new(), glyph_keys: Vec::new(), - glyph_options, - normal_render_mode, shadow_render_mode, offset: run_offset, color: *color, - subpx_dir, }; // Text shadows that have a blur radius of 0 need to be rendered as normal @@ -953,6 +969,7 @@ impl FrameBuilder { let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0]; if shadow_prim.shadow.blur_radius == 0.0 { let mut text_prim = prim.clone(); + text_prim.font.color = shadow_prim.shadow.color.into(); text_prim.color = shadow_prim.shadow.color; text_prim.offset += shadow_prim.shadow.offset; fast_text_shadow_prims.push(text_prim); diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs index 5cb73579b33d..0d04f3d846b5 100644 --- a/gfx/webrender/src/glyph_rasterizer.rs +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -378,14 +378,12 @@ fn raterize_200_glyphs() { let font_key = FontKey::new(IdNamespace(0), 0); glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0)); - let font = FontInstance { - font_key, - color: ColorF::new(0.0, 0.0, 0.0, 1.0).into(), - size: Au::from_px(32), - render_mode: FontRenderMode::Subpixel, - glyph_options: None, - subpx_dir: SubpixelDirection::Horizontal, - }; + let font = FontInstance::new(font_key, + Au::from_px(32), + ColorF::new(0.0, 0.0, 0.0, 1.0), + FontRenderMode::Subpixel, + SubpixelDirection::Horizontal, + None); let mut glyph_keys = Vec::with_capacity(200); for i in 0..200 { diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index 6ed9266058f2..2eaa3b1cce87 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -69,7 +69,7 @@ impl BatchTextures { BatchTextures { colors: [ SourceTexture::CacheRGBA8, - SourceTexture::Invalid, + SourceTexture::CacheA8, SourceTexture::Invalid, ] } @@ -156,8 +156,13 @@ impl RendererFrame { } } +pub enum DebugOutput { + FetchDocuments(String), +} + pub enum ResultMsg { DebugCommand(DebugCommand), + DebugOutput(DebugOutput), RefreshShader(PathBuf), NewFrame(DocumentId, RendererFrame, TextureUpdateList, BackendProfileCounters), UpdateResources { updates: TextureUpdateList, cancel_rendering: bool }, diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index d84bca9eaf52..2658918f8f60 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{FontKey, FontRenderMode, GlyphDimensions}; -use api::{FontInstance, GlyphKey, GlyphOptions, SubpixelDirection}; +use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode}; +use api::{GlyphDimensions, GlyphKey, GlyphOptions, SubpixelDirection}; use gamma_lut::{GammaLut, Color as ColorLut}; use internal_types::FastHashMap; @@ -46,9 +46,9 @@ fn dwrite_texture_type(render_mode: FontRenderMode) -> } } -fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option) -> +fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option) -> dwrote::DWRITE_MEASURING_MODE { - if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options { + if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options { return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC; } @@ -63,9 +63,9 @@ fn dwrite_render_mode(font_face: &dwrote::FontFace, render_mode: FontRenderMode, em_size: f32, measure_mode: dwrote::DWRITE_MEASURING_MODE, - options: Option) -> + options: Option) -> dwrote::DWRITE_RENDERING_MODE { - if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options { + if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options { return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC; } @@ -175,12 +175,12 @@ impl FontContext { }; let dwrite_measure_mode = dwrite_measure_mode(font.render_mode, - font.glyph_options); + font.platform_options); let dwrite_render_mode = dwrite_render_mode(face, font.render_mode, font.size.to_f32_px(), dwrite_measure_mode, - font.glyph_options); + font.platform_options); let (x_offset, y_offset) = font.get_subpx_offset(key); let transform = Some( @@ -302,7 +302,7 @@ impl FontContext { let mut pixels = analysis.create_alpha_texture(texture_type, bounds); if font.render_mode != FontRenderMode::Mono { - let lut_correction = match font.glyph_options { + let lut_correction = match font.platform_options { Some(option) => { if option.force_gdi_rendering { &self.gdi_gamma_lut diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 1aec0e36cc66..a85595be0368 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint}; -use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; +use api::{ExtendMode, FontRenderMode, GlyphInstance, GradientStop}; use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, TextShadow}; use api::{GlyphKey, LayerToWorldTransform, TileOffset, YuvColorSpace, YuvFormat}; -use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle, SubpixelDirection}; +use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle}; use app_units::Au; use border::BorderCornerInstance; use euclid::{Size2D}; @@ -504,21 +504,17 @@ pub struct TextShadowPrimitiveCpu { #[derive(Debug, Clone)] pub struct TextRunPrimitiveCpu { - pub font_key: FontKey, + pub font: FontInstance, pub offset: LayerVector2D, - pub logical_font_size: Au, pub glyph_range: ItemRange, pub glyph_count: usize, pub glyph_keys: Vec, pub glyph_gpu_blocks: Vec, - pub glyph_options: Option, - pub normal_render_mode: FontRenderMode, pub shadow_render_mode: FontRenderMode, pub color: ColorF, - pub subpx_dir: SubpixelDirection, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum TextRunMode { Normal, Shadow, @@ -531,18 +527,18 @@ impl TextRunPrimitiveCpu { display_list: &BuiltDisplayList, run_mode: TextRunMode, gpu_cache: &mut GpuCache) { - let font_size_dp = self.logical_font_size.scale_by(device_pixel_ratio); - let render_mode = match run_mode { - TextRunMode::Normal => self.normal_render_mode, - TextRunMode::Shadow => self.shadow_render_mode, - }; + let mut font = self.font.clone(); + font.size = font.size.scale_by(device_pixel_ratio); + match run_mode { + TextRunMode::Shadow => { + font.render_mode = self.shadow_render_mode; + } + TextRunMode::Normal => {} + } - let font = FontInstance::new(self.font_key, - font_size_dp, - self.color, - render_mode, - self.glyph_options, - self.subpx_dir); + if run_mode == TextRunMode::Shadow { + font.render_mode = self.shadow_render_mode; + } // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` @@ -590,7 +586,7 @@ impl TextRunPrimitiveCpu { request.push(self.color); request.push([self.offset.x, self.offset.y, - self.subpx_dir as u32 as f32, + self.font.subpx_dir as u32 as f32, 0.0]); request.extend_from_slice(&self.glyph_gpu_blocks); diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs index 865429f3208d..6d9a687688b7 100644 --- a/gfx/webrender/src/profiler.rs +++ b/gfx/webrender/src/profiler.rs @@ -131,6 +131,11 @@ impl ResourceProfileCounter { self.value += 1; self.size += size; } + + pub fn set(&mut self, count: usize, size: usize) { + self.value = count; + self.size = size; + } } impl ProfileCounter for ResourceProfileCounter { diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 3c3ed90252ff..4af1c8b3fce5 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -2,14 +2,18 @@ * 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/. */ +#[cfg(feature = "debugger")] +use debug_server; use frame::Frame; use frame_builder::FrameBuilderConfig; use gpu_cache::GpuCache; -use internal_types::{FastHashMap, ResultMsg, RendererFrame}; +use internal_types::{DebugOutput, FastHashMap, ResultMsg, RendererFrame}; use profiler::{BackendProfileCounters, ResourceProfileCounters}; use record::ApiRecordingReceiver; use resource_cache::ResourceCache; use scene::Scene; +#[cfg(feature = "debugger")] +use serde_json; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; use std::u32; @@ -19,9 +23,11 @@ use thread_profiler::register_thread_with_profiler; use rayon::ThreadPool; use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods}; use api::channel::{PayloadSender, PayloadSenderHelperMethods}; -use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint}; +use api::{ApiMsg, DebugCommand, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg}; use api::{IdNamespace, LayerPoint, RenderNotifier}; +#[cfg(feature = "debugger")] +use api::{BuiltDisplayListIter, SpecificDisplayItem}; struct Document { scene: Scene, @@ -275,6 +281,12 @@ impl RenderBackend { DocumentOp::Nop } } + DocumentMsg::RemovePipeline(pipeline_id) => { + profile_scope!("RemovePipeline"); + + doc.scene.remove_pipeline(pipeline_id); + DocumentOp::Nop + } DocumentMsg::Scroll(delta, cursor, move_phase) => { profile_scope!("Scroll"); let _timer = profile_counters.total_time.timer(); @@ -462,7 +474,15 @@ impl RenderBackend { self.notifier.lock().unwrap().as_mut().unwrap().new_frame_ready(); } ApiMsg::DebugCommand(option) => { - let msg = ResultMsg::DebugCommand(option); + let msg = match option { + DebugCommand::FetchDocuments => { + let json = self.get_docs_for_debugger(); + ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json)) + } + _ => { + ResultMsg::DebugCommand(option) + } + }; self.result_tx.send(msg).unwrap(); let notifier = self.notifier.lock(); notifier.unwrap() @@ -514,4 +534,139 @@ impl RenderBackend { let mut notifier = self.notifier.lock(); notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed); } + + + #[cfg(not(feature = "debugger"))] + fn get_docs_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn traverse_items<'a>(&self, + traversal: &mut BuiltDisplayListIter<'a>, + node: &mut debug_server::TreeNode) { + loop { + let subtraversal = { + let item = match traversal.next() { + Some(item) => item, + None => break, + }; + + match *item.item() { + display_item @ SpecificDisplayItem::PushStackingContext(..) => { + let mut subtraversal = item.sub_iter(); + let mut child_node = debug_server::TreeNode::new(&display_item.debug_string()); + self.traverse_items(&mut subtraversal, &mut child_node); + node.add_child(child_node); + Some(subtraversal) + } + SpecificDisplayItem::PopStackingContext => { + return; + } + display_item => { + node.add_item(&display_item.debug_string()); + None + } + } + }; + + // If flatten_item created a sub-traversal, we need `traversal` to have the + // same state as the completed subtraversal, so we reinitialize it here. + if let Some(subtraversal) = subtraversal { + *traversal = subtraversal; + } + } + } + + #[cfg(feature = "debugger")] + fn get_docs_for_debugger(&self) -> String { + let mut docs = debug_server::DocumentList::new(); + + for (_, doc) in &self.documents { + let mut debug_doc = debug_server::TreeNode::new("document"); + + for (_, display_list) in &doc.scene.display_lists { + let mut debug_dl = debug_server::TreeNode::new("display_list"); + self.traverse_items(&mut display_list.iter(), &mut debug_dl); + debug_doc.add_child(debug_dl); + } + + docs.add(debug_doc); + } + + serde_json::to_string(&docs).unwrap() + } +} + +#[cfg(feature = "debugger")] +trait ToDebugString { + fn debug_string(&self) -> String; +} + +#[cfg(feature = "debugger")] +impl ToDebugString for SpecificDisplayItem { + fn debug_string(&self) -> String { + match *self { + SpecificDisplayItem::Image(..) => { + String::from("image") + } + SpecificDisplayItem::YuvImage(..) => { + String::from("yuv_image") + } + SpecificDisplayItem::Text(..) => { + String::from("text") + } + SpecificDisplayItem::Rectangle(..) => { + String::from("rectangle") + } + SpecificDisplayItem::Line(..) => { + String::from("line") + } + SpecificDisplayItem::Gradient(..) => { + String::from("gradient") + } + SpecificDisplayItem::RadialGradient(..) => { + String::from("radial_gradient") + } + SpecificDisplayItem::BoxShadow(..) => { + String::from("box_shadow") + } + SpecificDisplayItem::Border(..) => { + String::from("border") + } + SpecificDisplayItem::PushStackingContext(..) => { + String::from("push_stacking_context") + } + SpecificDisplayItem::Iframe(..) => { + String::from("iframe") + } + SpecificDisplayItem::Clip(..) => { + String::from("clip") + } + SpecificDisplayItem::ScrollFrame(..) => { + String::from("scroll_frame") + } + SpecificDisplayItem::StickyFrame(..) => { + String::from("sticky_frame") + } + SpecificDisplayItem::PushNestedDisplayList => { + String::from("push_nested_display_list") + } + SpecificDisplayItem::PopNestedDisplayList => { + String::from("pop_nested_display_list") + } + SpecificDisplayItem::SetGradientStops => { + String::from("set_gradient_stops") + } + SpecificDisplayItem::PopStackingContext => { + String::from("pop_stacking_context") + } + SpecificDisplayItem::PushTextShadow(..) => { + String::from("push_text_shadow") + } + SpecificDisplayItem::PopTextShadow => { + String::from("pop_text_shadow") + } + } + } } diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 6379df3573fb..bbd2d4a6bf58 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -166,6 +166,7 @@ pub struct RenderTaskData { pub enum RenderTaskKind { Alpha(AlphaRenderTask), CachePrimitive(PrimitiveIndex), + BoxShadow(PrimitiveIndex), CacheMask(CacheMaskTask), VerticalBlur(DeviceIntLength), HorizontalBlur(DeviceIntLength), @@ -217,7 +218,7 @@ impl RenderTask { cache_key: Some(RenderTaskKey::BoxShadow(key)), children: Vec::new(), location: RenderTaskLocation::Dynamic(None, size), - kind: RenderTaskKind::CachePrimitive(prim_index), + kind: RenderTaskKind::BoxShadow(prim_index), } } @@ -344,6 +345,7 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref mut task) => task, RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -356,6 +358,7 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref task) => task, RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -396,7 +399,8 @@ impl RenderTask { ], } } - RenderTaskKind::CachePrimitive(..) => { + RenderTaskKind::CachePrimitive(..) | + RenderTaskKind::BoxShadow(..) => { let (target_rect, target_index) = self.get_target_rect(); RenderTaskData { data: [ @@ -500,7 +504,10 @@ impl RenderTask { RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color, - RenderTaskKind::CacheMask(..) => RenderTargetKind::Alpha, + + RenderTaskKind::CacheMask(..) | + RenderTaskKind::BoxShadow(..) => RenderTargetKind::Alpha, + RenderTaskKind::Alias(..) => { panic!("BUG: target_kind() called on invalidated task"); } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 5f251a646f5c..600252fe4c49 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -17,7 +17,7 @@ use api::DebugCommand; use debug_colors; use debug_render::DebugRenderer; #[cfg(feature = "debugger")] -use debug_server::{self, DebugMsg, DebugServer}; +use debug_server::{self, DebugServer}; use device::{DepthFunction, Device, FrameId, Program, Texture, VertexDescriptor, GpuMarker, GpuProfiler, PBOId}; use device::{GpuTimer, TextureFilter, VAO, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError}; use device::{ExternalTexture, get_gl_format_bgra, TextureSlot, VertexAttribute, VertexAttributeKind}; @@ -26,7 +26,7 @@ use frame_builder::FrameBuilderConfig; use gleam::gl; use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use internal_types::{FastHashMap, CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp}; -use internal_types::{TextureUpdateList, RenderTargetMode, TextureUpdateSource}; +use internal_types::{DebugOutput, TextureUpdateList, RenderTargetMode, TextureUpdateSource}; use internal_types::{BatchTextures, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture}; use profiler::{Profiler, BackendProfileCounters}; use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters}; @@ -1587,6 +1587,13 @@ impl Renderer { ResultMsg::RefreshShader(path) => { self.pending_shader_updates.push(path); } + ResultMsg::DebugOutput(output) => { + match output { + DebugOutput::FetchDocuments(string) => { + self.debug_server.send(string); + } + } + } ResultMsg::DebugCommand(command) => { self.handle_debug_command(command); } @@ -1595,72 +1602,66 @@ impl Renderer { } #[cfg(not(feature = "debugger"))] - fn update_debug_server(&self) { + fn get_passes_for_debugger(&self) -> String { // Avoid unused param warning. let _ = &self.debug_server; + String::new() } #[cfg(feature = "debugger")] - fn update_debug_server(&self) { - while let Ok(msg) = self.debug_server.debug_rx.try_recv() { - match msg { - DebugMsg::FetchPasses(sender) => { - let mut debug_passes = debug_server::PassList::new(); + fn get_passes_for_debugger(&self) -> String { + let mut debug_passes = debug_server::PassList::new(); - if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) { - for pass in &frame.passes { - let mut debug_pass = debug_server::Pass::new(); + if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) { + for pass in &frame.passes { + let mut debug_pass = debug_server::Pass::new(); - for target in &pass.alpha_targets.targets { - let mut debug_target = debug_server::Target::new("A8"); + for target in &pass.alpha_targets.targets { + let mut debug_target = debug_server::Target::new("A8"); - debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len()); - debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len()); - debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len()); - for (_, items) in target.clip_batcher.images.iter() { - debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); - } + debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len()); + debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len()); + debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len()); + for (_, items) in target.clip_batcher.images.iter() { + debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); + } + debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len()); - debug_pass.add(debug_target); - } + debug_pass.add(debug_target); + } - for target in &pass.color_targets.targets { - let mut debug_target = debug_server::Target::new("RGBA8"); + for target in &pass.color_targets.targets { + let mut debug_target = debug_server::Target::new("RGBA8"); - debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len()); - debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len()); - debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len()); - debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len()); - debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len()); + debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len()); + debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len()); + debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len()); + debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len()); - for batch in target.alpha_batcher - .batch_list - .opaque_batch_list - .batches - .iter() - .rev() { - debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len()); - } - - for batch in &target.alpha_batcher - .batch_list - .alpha_batch_list - .batches { - debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len()); - } - - debug_pass.add(debug_target); - } - - debug_passes.add(debug_pass); - } + for batch in target.alpha_batcher + .batch_list + .opaque_batch_list + .batches + .iter() + .rev() { + debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len()); } - let json = serde_json::to_string(&debug_passes).unwrap(); - sender.send(json).ok(); + for batch in &target.alpha_batcher + .batch_list + .alpha_batch_list + .batches { + debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len()); + } + + debug_pass.add(debug_target); } + + debug_passes.add(debug_pass); } } + + serde_json::to_string(&debug_passes).unwrap() } fn handle_debug_command(&mut self, command: DebugCommand) { @@ -1686,8 +1687,10 @@ impl Renderer { self.debug_flags.remove(RENDER_TARGET_DBG); } } - DebugCommand::Flush => { - self.update_debug_server(); + DebugCommand::FetchDocuments => {} + DebugCommand::FetchPasses => { + let json = self.get_passes_for_debugger(); + self.debug_server.send(json); } } } @@ -1839,7 +1842,6 @@ impl Renderer { // Ensure no PBO is bound when creating the texture storage, // or GL will attempt to read data from there. - self.device.bind_pbo(None); self.device.init_texture(texture, width, height, @@ -1882,6 +1884,9 @@ impl Renderer { layer_index, stride, 0); + + // Ensure that other texture updates won't read from this PBO. + self.device.bind_pbo(None); } TextureUpdateOp::Free => { let texture = &mut self.texture_resolver.cache_texture_map[update.id.0]; @@ -1890,9 +1895,6 @@ impl Renderer { } } } - - // Ensure that other texture updates won't read from this PBO. - self.device.bind_pbo(None); } fn draw_instanced_batch(&mut self, @@ -2164,16 +2166,6 @@ impl Renderer { } } - // Draw any box-shadow caches for this target. - if !target.box_shadow_cache_prims.is_empty() { - self.device.set_blend(false); - let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); - self.cs_box_shadow.bind(&mut self.device, projection); - self.draw_instanced_batch(&target.box_shadow_cache_prims, - VertexArrayKind::CacheBoxShadow, - &BatchTextures::no_texture()); - } - // Draw any textrun caches for this target. For now, this // is only used to cache text runs that are to be blurred // for text-shadow support. In the future it may be worth @@ -2297,6 +2289,16 @@ impl Renderer { target.used_rect()); } + // Draw any box-shadow caches for this target. + if !target.box_shadow_cache_prims.is_empty() { + self.device.set_blend(false); + let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); + self.cs_box_shadow.bind(&mut self.device, projection); + self.draw_instanced_batch(&target.box_shadow_cache_prims, + VertexArrayKind::CacheBoxShadow, + &BatchTextures::no_texture()); + } + // Draw the clip items into the tiled alpha mask. { let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP); @@ -2863,4 +2865,6 @@ impl DebugServer { pub fn new(_: MsgSender) -> DebugServer { DebugServer } + + pub fn send(&mut self, _: String) {} } diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index ef7b84ae26d6..466f0427833b 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use app_units::Au; use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; @@ -17,7 +18,9 @@ use texture_cache::{TextureCache, TextureCacheHandle}; use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest}; use api::{BlobImageResources, BlobImageData, ResourceUpdates, ResourceUpdate, AddFont}; use api::{DevicePoint, DeviceUintRect, DeviceUintSize}; -use api::{Epoch, FontInstance, FontKey, FontTemplate}; +use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate}; +use api::{FontInstanceOptions, FontInstancePlatformOptions}; +use api::{ColorF, FontRenderMode, SubpixelDirection}; use api::{GlyphDimensions, GlyphKey, IdNamespace}; use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering}; use api::{TileOffset, TileSize}; @@ -170,6 +173,7 @@ impl Into for ImageRequest { struct Resources { font_templates: FastHashMap, + font_instances: FastHashMap, image_templates: ImageTemplates, } @@ -213,6 +217,7 @@ impl ResourceCache { cached_images: ResourceClassCache::new(), resources: Resources { font_templates: FastHashMap::default(), + font_instances: FastHashMap::default(), image_templates: ImageTemplates::new(), }, cached_glyph_dimensions: FastHashMap::default(), @@ -279,6 +284,13 @@ impl ResourceCache { ResourceUpdate::DeleteFont(font) => { self.delete_font_template(font); } + ResourceUpdate::AddFontInstance(instance) => { + self.add_font_instance(instance.key, instance.font_key, instance.glyph_size, + instance.options, instance.platform_options); + } + ResourceUpdate::DeleteFontInstance(instance) => { + self.delete_font_instance(instance); + } } } } @@ -298,6 +310,40 @@ impl ResourceCache { } } + pub fn add_font_instance(&mut self, + instance_key: FontInstanceKey, + font_key: FontKey, + glyph_size: Au, + options: Option, + platform_options: Option) { + let mut render_mode = FontRenderMode::Subpixel; + let mut subpx_dir = SubpixelDirection::Horizontal; + if let Some(options) = options { + render_mode = options.render_mode; + if render_mode == FontRenderMode::Mono { + subpx_dir = SubpixelDirection::None; + } + } + let instance = FontInstance::new(font_key, + glyph_size, + ColorF::new(0.0, 0.0, 0.0, 1.0), + render_mode, + subpx_dir, + platform_options); + self.resources.font_instances.insert(instance_key, instance); + } + + pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) { + self.resources.font_instances.remove(&instance_key); + if let Some(ref mut r) = self.blob_image_renderer { + r.delete_font_instance(instance_key); + } + } + + pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<&FontInstance> { + self.resources.font_instances.get(&instance_key) + } + pub fn add_image_template(&mut self, image_key: ImageKey, descriptor: ImageDescriptor, @@ -607,13 +653,11 @@ impl ResourceCache { ); // Apply any updates of new / updated images (incl. blobs) to the texture cache. - self.update_texture_cache(gpu_cache, texture_cache_profile); - self.texture_cache.end_frame(); + self.update_texture_cache(gpu_cache); + self.texture_cache.end_frame(texture_cache_profile); } - fn update_texture_cache(&mut self, - gpu_cache: &mut GpuCache, - _texture_cache_profile: &mut TextureCacheProfileCounters) { + fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) { for request in self.pending_image_requests.drain() { let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); debug_assert!(image_template.data.uses_texture_cache()); @@ -667,7 +711,7 @@ impl ResourceCache { let (stride, offset) = if tiled_on_cpu { (image_descriptor.stride, 0) } else { - let bpp = image_descriptor.format.bytes_per_pixel().unwrap(); + let bpp = image_descriptor.format.bytes_per_pixel(); let stride = image_descriptor.compute_stride(); let offset = image_descriptor.offset + tile.y as u32 * tile_size as u32 * stride + tile.x as u32 * tile_size as u32 * bpp; diff --git a/gfx/webrender/src/scene.rs b/gfx/webrender/src/scene.rs index ea7a7f28a13c..9e7a5fb3e3bf 100644 --- a/gfx/webrender/src/scene.rs +++ b/gfx/webrender/src/scene.rs @@ -127,4 +127,13 @@ impl Scene { self.pipeline_map.insert(pipeline_id, new_pipeline); } + + pub fn remove_pipeline(&mut self, + pipeline_id: PipelineId) { + if self.root_pipeline_id == Some(pipeline_id) { + self.root_pipeline_id = None; + } + self.display_lists.remove(&pipeline_id); + self.pipeline_map.remove(&pipeline_id); + } } diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs index 4b6f3e3b8230..a204f41f109b 100644 --- a/gfx/webrender/src/texture_cache.rs +++ b/gfx/webrender/src/texture_cache.rs @@ -8,6 +8,7 @@ use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; use gpu_cache::{GpuCache, GpuCacheHandle}; use internal_types::{SourceTexture, TextureUpdate, TextureUpdateOp}; use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList, TextureUpdateSource}; +use profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use resource_cache::CacheItem; use std::cmp; use std::mem; @@ -223,8 +224,13 @@ impl TextureCache { self.frame_id = frame_id; } - pub fn end_frame(&mut self) { + pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { self.expire_old_standalone_entries(); + + self.array_a8.update_profile(&mut texture_cache_profile.pages_a8); + self.array_rg8.update_profile(&mut texture_cache_profile.pages_rg8); + self.array_rgb8.update_profile(&mut texture_cache_profile.pages_rgb8); + self.array_rgba8.update_profile(&mut texture_cache_profile.pages_rgba8); } // Request an item in the texture cache. All images that will @@ -835,6 +841,18 @@ impl TextureArray { } } + fn update_profile(&self, counter: &mut ResourceProfileCounter) { + if self.is_allocated { + let size = TEXTURE_ARRAY_LAYERS as u32 * + TEXTURE_LAYER_DIMENSIONS * + TEXTURE_LAYER_DIMENSIONS * + self.format.bytes_per_pixel(); + counter.set(TEXTURE_ARRAY_LAYERS as usize, size as usize); + } else { + counter.set(0, 0); + } + } + // Allocate space in this texture array. fn alloc(&mut self, width: u32, @@ -957,7 +975,7 @@ impl TextureUpdate { } ImageData::Raw(bytes) => { let finish = descriptor.offset + - descriptor.width * descriptor.format.bytes_per_pixel().unwrap_or(0) + + descriptor.width * descriptor.format.bytes_per_pixel() + (descriptor.height-1) * descriptor.compute_stride(); assert!(bytes.len() >= finish as usize); diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index f207ea61a4c0..5daa11623ae8 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -22,7 +22,7 @@ use std::{f32, i32, usize}; use texture_allocator::GuillotineAllocator; use util::{TransformedRect, TransformedRectKind}; use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey}; -use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstance}; +use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize}; use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect}; use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle}; use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D}; @@ -44,7 +44,7 @@ impl AlphaBatchHelpers for PrimitiveStore { match metadata.prim_kind { PrimitiveKind::TextRun => { let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0]; - match text_run_cpu.normal_render_mode { + match text_run_cpu.font.render_mode { FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color), FontRenderMode::Alpha | FontRenderMode::Mono => BlendMode::Alpha, } @@ -453,17 +453,12 @@ impl AlphaRenderItem { } PrimitiveKind::TextRun => { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let font_size_dp = text_cpu.logical_font_size.scale_by(ctx.device_pixel_ratio); // TODO(gw): avoid / recycle this allocation in the future. let mut instances = Vec::new(); - let font = FontInstance::new(text_cpu.font_key, - font_size_dp, - text_cpu.color, - text_cpu.normal_render_mode, - text_cpu.glyph_options, - text_cpu.subpx_dir); + let mut font = text_cpu.font.clone(); + font.size = font.size.scale_by(ctx.device_pixel_ratio); let texture_id = ctx.resource_cache.get_glyphs(font, &text_cpu.glyph_keys, @@ -576,8 +571,9 @@ impl AlphaRenderItem { let box_shadow = &ctx.prim_store.cpu_box_shadows[prim_metadata.cpu_prim_index.0]; let cache_task_id = prim_metadata.render_task_id.unwrap(); let cache_task_address = render_tasks.get_task_address(cache_task_id); + let textures = BatchTextures::render_target_cache(); - let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, no_textures); + let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, textures); let batch = batch_list.get_suitable_batch(&key, item_bounding_rect); for rect_index in 0..box_shadow.rects.len() { @@ -906,7 +902,6 @@ impl RenderTargetList { /// A render target represents a number of rendering operations on a surface. pub struct ColorRenderTarget { pub alpha_batcher: AlphaBatcher, - pub box_shadow_cache_prims: Vec, // List of text runs to be cached to this render target. // TODO(gw): For now, assume that these all come from // the same source texture id. This is almost @@ -933,7 +928,6 @@ impl RenderTarget for ColorRenderTarget { fn new(size: DeviceUintSize) -> ColorRenderTarget { ColorRenderTarget { alpha_batcher: AlphaBatcher::new(), - box_shadow_cache_prims: Vec::new(), text_run_cache_prims: Vec::new(), line_cache_prims: Vec::new(), text_run_textures: BatchTextures::no_texture(), @@ -993,16 +987,9 @@ impl RenderTarget for ColorRenderTarget { } RenderTaskKind::CachePrimitive(prim_index) => { let prim_metadata = ctx.prim_store.get_metadata(prim_index); - let prim_address = prim_metadata.gpu_location.as_int(gpu_cache); match prim_metadata.prim_kind { - PrimitiveKind::BoxShadow => { - self.box_shadow_cache_prims.push(BoxShadowCacheInstance { - prim_address: gpu_cache.get_address(&prim_metadata.gpu_location), - task_index: render_tasks.get_task_address(task_id), - }); - } PrimitiveKind::TextShadow => { let prim = &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0]; @@ -1026,14 +1013,9 @@ impl RenderTarget for ColorRenderTarget { // the parent text-shadow prim address as a user data field, allowing // the shader to fetch the text-shadow parameters. let text = &ctx.prim_store.cpu_text_runs[sub_metadata.cpu_prim_index.0]; - let font_size_dp = text.logical_font_size.scale_by(ctx.device_pixel_ratio); - let font = FontInstance::new(text.font_key, - font_size_dp, - text.color, - text.shadow_render_mode, - text.glyph_options, - text.subpx_dir); + let mut font = text.font.clone(); + font.size = font.size.scale_by(ctx.device_pixel_ratio); let texture_id = ctx.resource_cache.get_glyphs(font, &text.glyph_keys, @@ -1073,7 +1055,8 @@ impl RenderTarget for ColorRenderTarget { } } } - RenderTaskKind::CacheMask(..) => { + RenderTaskKind::CacheMask(..) | + RenderTaskKind::BoxShadow(..) => { panic!("Should not be added to color target!"); } RenderTaskKind::Readback(device_rect) => { @@ -1085,6 +1068,7 @@ impl RenderTarget for ColorRenderTarget { pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, + pub box_shadow_cache_prims: Vec, allocator: TextureAllocator, } @@ -1096,6 +1080,7 @@ impl RenderTarget for AlphaRenderTarget { fn new(size: DeviceUintSize) -> AlphaRenderTarget { AlphaRenderTarget { clip_batcher: ClipBatcher::new(), + box_shadow_cache_prims: Vec::new(), allocator: TextureAllocator::new(size), } } @@ -1121,6 +1106,21 @@ impl RenderTarget for AlphaRenderTarget { RenderTaskKind::Readback(..) => { panic!("Should not be added to alpha target!"); } + RenderTaskKind::BoxShadow(prim_index) => { + let prim_metadata = ctx.prim_store.get_metadata(prim_index); + + match prim_metadata.prim_kind { + PrimitiveKind::BoxShadow => { + self.box_shadow_cache_prims.push(BoxShadowCacheInstance { + prim_address: gpu_cache.get_address(&prim_metadata.gpu_location), + task_index: render_tasks.get_task_address(task_id), + }); + } + _ => { + panic!("BUG: invalid prim kind"); + } + } + } RenderTaskKind::CacheMask(ref task_info) => { let task_address = render_tasks.get_task_address(task_id); self.clip_batcher.add(task_address, diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index 42b2cd2aa98f..3222bdc68507 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 2a03faf95411..ff5c2c56f41c 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -2,14 +2,15 @@ * 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/. */ +use app_units::Au; use channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender}; use std::cell::Cell; use std::fmt; use std::marker::PhantomData; use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint}; -use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey}; +use {DeviceUintRect, DeviceUintSize, FontInstanceKey, FontKey, GlyphDimensions, GlyphKey}; use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform}; -use {FontInstance, NativeFontHandle, WorldPoint}; +use {FontInstance, FontInstanceOptions, FontInstancePlatformOptions, NativeFontHandle, WorldPoint}; pub type TileSize = u16; @@ -26,6 +27,8 @@ pub enum ResourceUpdate { DeleteImage(ImageKey), AddFont(AddFont), DeleteFont(FontKey), + AddFontInstance(AddFontInstance), + DeleteFontInstance(FontInstanceKey), } impl ResourceUpdates { @@ -71,6 +74,19 @@ impl ResourceUpdates { self.updates.push(ResourceUpdate::DeleteFont(key)); } + pub fn add_font_instance(&mut self, + key: FontInstanceKey, + font_key: FontKey, + glyph_size: Au, + options: Option, + platform_options: Option) { + self.updates.push(ResourceUpdate::AddFontInstance(AddFontInstance { key, font_key, glyph_size, options, platform_options })); + } + + pub fn delete_font_instance(&mut self, key: FontInstanceKey) { + self.updates.push(ResourceUpdate::DeleteFontInstance(key)); + } + pub fn merge(&mut self, mut other: ResourceUpdates) { self.updates.append(&mut other.updates); } @@ -102,6 +118,15 @@ pub enum AddFont { Native(FontKey, NativeFontHandle), } +#[derive(Clone, Deserialize, Serialize)] +pub struct AddFontInstance { + pub key: FontInstanceKey, + pub font_key: FontKey, + pub glyph_size: Au, + pub options: Option, + pub platform_options: Option, +} + #[derive(Clone, Deserialize, Serialize)] pub enum DocumentMsg { SetDisplayList { @@ -118,6 +143,7 @@ pub enum DocumentMsg { SetPinchZoom(ZoomFactor), SetPan(DeviceIntPoint), SetRootPipeline(PipelineId), + RemovePipeline(PipelineId), SetWindowParameters { window_size: DeviceUintSize, inner_rect: DeviceUintRect, @@ -137,6 +163,7 @@ impl fmt::Debug for DocumentMsg { DocumentMsg::SetPinchZoom(..) => "DocumentMsg::SetPinchZoom", DocumentMsg::SetPan(..) => "DocumentMsg::SetPan", DocumentMsg::SetRootPipeline(..) => "DocumentMsg::SetRootPipeline", + DocumentMsg::RemovePipeline(..) => "DocumentMsg::RemovePipeline", DocumentMsg::SetWindowParameters{..} => "DocumentMsg::SetWindowParameters", DocumentMsg::Scroll(..) => "DocumentMsg::Scroll", DocumentMsg::ScrollNodeWithId(..) => "DocumentMsg::ScrollNodeWithId", @@ -155,8 +182,10 @@ pub enum DebugCommand { EnableTextureCacheDebug(bool), // Display intermediate render targets on screen. EnableRenderTargetDebug(bool), - // Flush any pending debug commands. - Flush, + // Fetch current documents and display lists. + FetchDocuments, + // Fetch current passes and batches. + FetchPasses, } #[derive(Clone, Deserialize, Serialize)] @@ -329,6 +358,11 @@ impl RenderApi { FontKey::new(self.namespace_id, new_id) } + pub fn generate_font_instance_key(&self) -> FontInstanceKey { + let new_id = self.next_unique_id(); + FontInstanceKey::new(self.namespace_id, new_id) + } + /// Gets the dimensions for the supplied glyph keys /// /// Note: Internally, the internal texture cache doesn't store @@ -422,7 +456,7 @@ impl RenderApi { self.api_sender.send(ApiMsg::UpdateDocument(document_id, msg)).unwrap() } - /// Sets the root pipeline. + /// Sets the root pipeline. /// /// # Examples /// @@ -439,6 +473,13 @@ impl RenderApi { self.send(document_id, DocumentMsg::SetRootPipeline(pipeline_id)); } + /// Removes data associated with a pipeline from the internal data structures. + /// If the specified `pipeline_id` is for the root pipeline, the root pipeline + /// is reset back to `None`. + pub fn remove_pipeline(&self, document_id: DocumentId, pipeline_id: PipelineId) { + self.send(document_id, DocumentMsg::RemovePipeline(pipeline_id)); + } + /// Supplies a new frame to WebRender. /// /// Non-blocking, it notifies a worker process which processes the display list. diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 61be7b101b1a..ac5282421fbb 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -2,9 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use app_units::Au; use euclid::{SideOffsets2D, TypedSideOffsets2D}; -use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; +use {ColorF, FontInstanceKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding}; // NOTE: some of these structs have an "IMPLICIT" comment. @@ -139,8 +138,7 @@ pub enum LineStyle { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct TextDisplayItem { - pub font_key: FontKey, - pub size: Au, + pub font_key: FontInstanceKey, pub color: ColorF, pub glyph_options: Option, } // IMPLICIT: glyphs: Vec diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index 6e3b113a61ca..36d53f25dd34 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -2,14 +2,13 @@ * 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/. */ -use app_units::Au; use bincode; use serde::{Deserialize, Serialize, Serializer}; use serde::ser::{SerializeSeq, SerializeMap}; use time::precise_time_ns; use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem}; use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem}; -use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontKey, GlyphIndex, GlyphInstance}; +use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontInstanceKey, GlyphIndex, GlyphInstance}; use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem}; use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayoutPoint, LayoutRect, LayoutSize}; use {LayoutTransform, LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, LocalClip}; @@ -301,7 +300,7 @@ impl<'a> BuiltDisplayListIter<'a> { } impl<'a> Iterator for GlyphsIter<'a> { - type Item = (FontKey, ColorF, ItemRange); + type Item = (FontInstanceKey, ColorF, ItemRange); fn next(&mut self) -> Option { if self.data.len() == 0 { return None; } @@ -457,7 +456,7 @@ pub struct DisplayListBuilder { pub pipeline_id: PipelineId, clip_stack: Vec, // FIXME: audit whether fast hashers (FNV?) are safe here - glyphs: FastHashMap<(FontKey, ColorF), FastHashSet>, + glyphs: FastHashMap<(FontInstanceKey, ColorF), FastHashSet>, next_clip_id: u64, builder_start_time: u64, @@ -605,38 +604,28 @@ impl DisplayListBuilder { rect: LayoutRect, local_clip: Option, glyphs: &[GlyphInstance], - font_key: FontKey, + font_key: FontInstanceKey, color: ColorF, - size: Au, glyph_options: Option) { - // Sanity check - anything with glyphs bigger than this - // is probably going to consume too much memory to render - // efficiently anyway. This is specifically to work around - // the font_advance.html reftest, which creates a very large - // font as a crash test - the rendering is also ignored - // by the azure renderer. - if size < Au::from_px(4096) { - let item = SpecificDisplayItem::Text(TextDisplayItem { - color, - font_key, - size, - glyph_options, - }); + let item = SpecificDisplayItem::Text(TextDisplayItem { + color, + font_key, + glyph_options, + }); - for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) { - self.push_item(item, rect, local_clip); - self.push_iter(split_glyphs); + for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) { + self.push_item(item, rect, local_clip); + self.push_iter(split_glyphs); - // Remember that we've seen these glyphs - self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index)); - } + // Remember that we've seen these glyphs + self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index)); } } fn cache_glyphs>(&mut self, - font_key: FontKey, - color: ColorF, - glyphs: I) { + font_key: FontInstanceKey, + color: ColorF, + glyphs: I) { let mut font_glyphs = self.glyphs.entry((font_key, color)) .or_insert(FastHashSet::default()); diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 41cb6ed67355..0e6689182c7f 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -72,7 +72,7 @@ pub enum FontTemplate { Native(NativeFontHandle), } -#[repr(C)] +#[repr(u32)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)] pub enum FontRenderMode { Mono = 0, @@ -80,7 +80,7 @@ pub enum FontRenderMode { Subpixel, } -#[repr(C)] +#[repr(u32)] #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] pub enum SubpixelDirection { None = 0, @@ -115,6 +115,14 @@ impl FontRenderMode { _ => panic!("Should only be given the fractional part"), } } + + // Combine two font render modes such that the lesser amount of AA limits the AA of the result. + pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode { + match (self, other) { + (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other, + _ => self, + } + } } #[repr(u8)] @@ -137,8 +145,21 @@ impl Into for SubpixelOffset { } } +#[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct GlyphOptions { + pub render_mode: FontRenderMode, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstanceOptions { + pub render_mode: FontRenderMode, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstancePlatformOptions { // These are currently only used on windows for dwrite fonts. pub use_embedded_bitmap: bool, pub force_gdi_rendering: bool, @@ -155,8 +176,8 @@ pub struct FontInstance { pub size: Au, pub color: ColorU, pub render_mode: FontRenderMode, - pub glyph_options: Option, pub subpx_dir: SubpixelDirection, + pub platform_options: Option, } impl FontInstance { @@ -164,8 +185,8 @@ impl FontInstance { size: Au, mut color: ColorF, render_mode: FontRenderMode, - glyph_options: Option, - subpx_dir: SubpixelDirection) -> FontInstance { + subpx_dir: SubpixelDirection, + platform_options: Option) -> FontInstance { // In alpha/mono mode, the color of the font is irrelevant. // Forcing it to black in those cases saves rasterizing glyphs // of different colors when not needed. @@ -178,8 +199,8 @@ impl FontInstance { size, color: color.into(), render_mode, - glyph_options, subpx_dir, + platform_options, } } @@ -192,6 +213,16 @@ impl FontInstance { } } +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)] +pub struct FontInstanceKey(pub IdNamespace, pub u32); + +impl FontInstanceKey { + pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey { + FontInstanceKey(namespace, key) + } +} + #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] pub struct GlyphKey { pub index: u32, diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index 9b5fd755aeda..f489d8f8c4cd 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use {DeviceUintRect, DevicePoint}; use {IdNamespace}; use {TileOffset, TileSize}; -use font::{FontKey, FontTemplate}; +use font::{FontKey, FontInstanceKey, FontTemplate}; #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -59,14 +59,14 @@ pub enum ImageFormat { } impl ImageFormat { - pub fn bytes_per_pixel(self) -> Option { + pub fn bytes_per_pixel(self) -> u32 { match self { - ImageFormat::A8 => Some(1), - ImageFormat::RGB8 => Some(3), - ImageFormat::BGRA8 => Some(4), - ImageFormat::RGBAF32 => Some(16), - ImageFormat::RG8 => Some(2), - ImageFormat::Invalid => None, + ImageFormat::A8 => 1, + ImageFormat::RGB8 => 3, + ImageFormat::BGRA8 => 4, + ImageFormat::RGBAF32 => 16, + ImageFormat::RG8 => 2, + ImageFormat::Invalid => 0, } } } @@ -94,7 +94,7 @@ impl ImageDescriptor { } pub fn compute_stride(&self) -> u32 { - self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap()) + self.stride.unwrap_or(self.width * self.format.bytes_per_pixel()) } } @@ -165,6 +165,8 @@ pub trait BlobImageRenderer: Send { fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult; fn delete_font(&mut self, key: FontKey); + + fn delete_font_instance(&mut self, key: FontInstanceKey); } pub type BlobImageData = Vec; diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml index 87a95ae2024d..73cf68977a9b 100644 --- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"] license = "MPL-2.0" [dependencies] -webrender_api = {path = "../webrender_api", version = "0.49.0"} +webrender_api = {path = "../webrender_api", version = "0.50.0"} rayon = "0.8" thread_profiler = "0.1.1" euclid = "0.15" @@ -14,5 +14,5 @@ gleam = "0.4" [dependencies.webrender] path = "../webrender" -version = "0.49.0" +version = "0.50.0" default-features = false diff --git a/toolkit/library/gtest/rust/Cargo.lock b/toolkit/library/gtest/rust/Cargo.lock index 3a0ffd040104..abefb9a8e594 100644 --- a/toolkit/library/gtest/rust/Cargo.lock +++ b/toolkit/library/gtest/rust/Cargo.lock @@ -1456,7 +1456,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1478,12 +1478,12 @@ dependencies = [ "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.49.0", + "webrender_api 0.50.0", ] [[package]] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1507,8 +1507,8 @@ dependencies = [ "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.49.0", - "webrender_api 0.49.0", + "webrender 0.50.0", + "webrender_api 0.50.0", ] [[package]] diff --git a/toolkit/library/rust/Cargo.lock b/toolkit/library/rust/Cargo.lock index f7534e07b20c..0176fe38ebbf 100644 --- a/toolkit/library/rust/Cargo.lock +++ b/toolkit/library/rust/Cargo.lock @@ -1443,7 +1443,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1465,12 +1465,12 @@ dependencies = [ "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.49.0", + "webrender_api 0.50.0", ] [[package]] name = "webrender_api" -version = "0.49.0" +version = "0.50.0" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1494,8 +1494,8 @@ dependencies = [ "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.49.0", - "webrender_api 0.49.0", + "webrender 0.50.0", + "webrender_api 0.50.0", ] [[package]] From f5e6acfc7d3b555cca36cffa87a50069ac3d0028 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 31 Aug 2017 14:09:26 -0400 Subject: [PATCH 17/51] Bug 1395237 - Update reftest fuzz values as a result of WR cset f1569c04. r=jrmuizel MozReview-Commit-ID: RGvlA2lM8 --- layout/reftests/css-break/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/css-break/reftest.list b/layout/reftests/css-break/reftest.list index e86f5bd4f07e..cfb03d2062fb 100644 --- a/layout/reftests/css-break/reftest.list +++ b/layout/reftests/css-break/reftest.list @@ -1,7 +1,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,4-4,3320-3320) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html +fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,4-4,83-83) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html fuzzy(16,460) fuzzy-if(skiaContent,57,374) fuzzy-if(Android,57,1330) fuzzy-if(styloVsGecko,2,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) HTTP(..) == 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 From 4abab826261104f82216aacacba933d117ddfdd2 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 31 Aug 2017 14:09:26 -0400 Subject: [PATCH 18/51] Bug 1395237 - Fix for change in WR cset da0b2138. r=jrmuizel MozReview-Commit-ID: LC6rp1Vysdp --- gfx/webrender_bindings/src/moz2d_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/webrender_bindings/src/moz2d_renderer.rs b/gfx/webrender_bindings/src/moz2d_renderer.rs index 952b004ef01a..5ab8bff0ebeb 100644 --- a/gfx/webrender_bindings/src/moz2d_renderer.rs +++ b/gfx/webrender_bindings/src/moz2d_renderer.rs @@ -64,7 +64,7 @@ impl BlobImageRenderer for Moz2dImageRenderer { self.workers.spawn(move || { let buf_size = (descriptor.width * descriptor.height - * descriptor.format.bytes_per_pixel().unwrap()) as usize; + * descriptor.format.bytes_per_pixel()) as usize; let mut output = vec![255u8; buf_size]; let result = unsafe { From 251cd46debbe9c9ae3a06cea84dd5ff53814cdf2 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Wed, 30 Aug 2017 13:45:11 -0400 Subject: [PATCH 19/51] Bug 1393055 - map ScaledFonts to WebRender FontInstanceKeys. r=jrmuizel MozReview-Commit-ID: AadEI5CnTZ1 --- gfx/2d/2D.h | 25 +++++++- gfx/2d/ScaledFontBase.cpp | 9 ++- gfx/layers/ipc/PWebRenderBridge.ipdl | 6 ++ gfx/layers/wr/WebRenderBridgeChild.cpp | 61 ++++++++++++------ gfx/layers/wr/WebRenderBridgeChild.h | 19 ++++-- gfx/layers/wr/WebRenderBridgeParent.cpp | 58 +++++++++++++++-- gfx/layers/wr/WebRenderBridgeParent.h | 7 ++ gfx/layers/wr/WebRenderMessageUtils.h | 18 ++++++ gfx/webrender_bindings/WebRenderAPI.cpp | 22 ++++++- gfx/webrender_bindings/WebRenderAPI.h | 12 +++- gfx/webrender_bindings/WebRenderTypes.h | 17 +++++ gfx/webrender_bindings/src/bindings.rs | 40 +++++++++--- gfx/webrender_bindings/src/moz2d_renderer.rs | 4 ++ .../webrender_ffi_generated.h | 64 ++++++++++++++++++- 14 files changed, 311 insertions(+), 51 deletions(-) diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 5358830a9d7b..dc8687456ac4 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -74,6 +74,7 @@ class Mutex; namespace gfx { class UnscaledFont; +class ScaledFont; } template<> @@ -89,6 +90,15 @@ struct WeakPtrTraits } }; +template<> +struct WeakPtrTraits +{ + static void AssertSafeToAccessFromNonOwningThread() + { + AssertIsMainThreadOrServoFontMetricsLocked(); + } +}; + namespace gfx { class ScaledFont; @@ -769,23 +779,29 @@ protected: UnscaledFont() {} private: - static uint32_t sDeletionCounter; + static Atomic sDeletionCounter; }; /** This class is an abstraction of a backend/platform specific font object * at a particular size. It is passed into text drawing calls to describe * the font used for the drawing call. */ -class ScaledFont : public external::AtomicRefCounted +class ScaledFont + : public external::AtomicRefCounted + , public SupportsWeakPtr { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont) - virtual ~ScaledFont() {} + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ScaledFont) + + virtual ~ScaledFont(); virtual FontType GetType() const = 0; virtual Float GetSize() const = 0; virtual AntialiasMode GetDefaultAAMode(); + static uint32_t DeletionCounter() { return sDeletionCounter; } + /** This allows getting a path that describes the outline of a set of glyphs. * A target is passed in so that the guarantee is made the returned path * can be used with any DrawTarget that has the same backend as the one @@ -833,6 +849,9 @@ protected: UserData mUserData; RefPtr mUnscaledFont; + +private: + static Atomic sDeletionCounter; }; /** diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp index 5fb7bcf56443..298e58dce60f 100644 --- a/gfx/2d/ScaledFontBase.cpp +++ b/gfx/2d/ScaledFontBase.cpp @@ -26,13 +26,20 @@ using namespace std; namespace mozilla { namespace gfx { -uint32_t UnscaledFont::sDeletionCounter = 0; +Atomic UnscaledFont::sDeletionCounter(0); UnscaledFont::~UnscaledFont() { sDeletionCounter++; } +Atomic ScaledFont::sDeletionCounter(0); + +ScaledFont::~ScaledFont() +{ + sDeletionCounter++; +} + AntialiasMode ScaledFont::GetDefaultAAMode() { diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl index 069447fdff03..fad50b7acca2 100644 --- a/gfx/layers/ipc/PWebRenderBridge.ipdl +++ b/gfx/layers/ipc/PWebRenderBridge.ipdl @@ -22,6 +22,9 @@ using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h"; using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h"; using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; @@ -54,6 +57,9 @@ parent: async DeleteCompositorAnimations(uint64_t[] aIds); async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex); async DeleteFont(FontKey aFontKey); + async AddFontInstance(FontInstanceKey aInstanceKey, FontKey aFontKey, float aGlyphSize, + MaybeFontInstanceOptions aOptions, MaybeFontInstancePlatformOptions aPlatformOptions); + async DeleteFontInstance(FontInstanceKey aInstanceKey); async DPBegin(IntSize aSize); async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId, LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc, diff --git a/gfx/layers/wr/WebRenderBridgeChild.cpp b/gfx/layers/wr/WebRenderBridgeChild.cpp index 4ba078141ba1..6a7dd8dbc84a 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.cpp +++ b/gfx/layers/wr/WebRenderBridgeChild.cpp @@ -29,6 +29,7 @@ WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId) , mIPCOpen(false) , mDestroyed(false) , mFontKeysDeleted(0) + , mFontInstanceKeysDeleted(0) { } @@ -219,7 +220,7 @@ WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArra MOZ_ASSERT(aFont); MOZ_ASSERT(!aGlyphs.IsEmpty()); - wr::WrFontKey key = GetFontKeyForScaledFont(aFont); + wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont); MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle); nsTArray wr_glyph_instances; @@ -235,11 +236,10 @@ WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArra aSc.ToRelativeLayoutRect(aClip), aColor, key, - Range(wr_glyph_instances.Elements(), wr_glyph_instances.Length()), - aFont->GetSize()); + Range(wr_glyph_instances.Elements(), wr_glyph_instances.Length())); } -wr::FontKey +wr::FontInstanceKey WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont) { MOZ_ASSERT(!mDestroyed); @@ -248,34 +248,54 @@ WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont) (aScaledFont->GetType() == gfx::FontType::MAC) || (aScaledFont->GetType() == gfx::FontType::FONTCONFIG)); + wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 }; + if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) { + return instanceKey; + } + RefPtr unscaled = aScaledFont->GetUnscaledFont(); MOZ_ASSERT(unscaled); - wr::FontKey key = { wr::IdNamespace { 0 }, 0}; - if (mFontKeys.Get(unscaled, &key)) { - return key; + wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0}; + if (!mFontKeys.Get(unscaled, &fontKey)) { + FontFileData data; + if (!unscaled->GetFontFileData(WriteFontFileData, &data) || + !data.mFontBuffer.mData) { + return instanceKey; + } + + fontKey.mNamespace = GetNamespace(); + fontKey.mHandle = GetNextResourceId(); + + SendAddRawFont(fontKey, data.mFontBuffer, data.mFontIndex); + + mFontKeys.Put(unscaled, fontKey); } - FontFileData data; - if (!unscaled->GetFontFileData(WriteFontFileData, &data) || - !data.mFontBuffer.mData) { - return key; - } + instanceKey.mNamespace = GetNamespace(); + instanceKey.mHandle = GetNextResourceId(); - key.mNamespace = GetNamespace(); - key.mHandle = GetNextResourceId(); + SendAddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(), Nothing(), Nothing()); - SendAddRawFont(key, data.mFontBuffer, data.mFontIndex); + mFontInstanceKeys.Put(aScaledFont, instanceKey); - mFontKeys.Put(unscaled, key); - - return key; + return instanceKey; } void WebRenderBridgeChild::RemoveExpiredFontKeys() { - uint32_t counter = gfx::UnscaledFont::DeletionCounter(); + uint32_t counter = gfx::ScaledFont::DeletionCounter(); + if (mFontInstanceKeysDeleted != counter) { + mFontInstanceKeysDeleted = counter; + for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) { + if (!iter.Key()) { + SendDeleteFontInstance(iter.Data()); + iter.Remove(); + } + } + } + counter = gfx::UnscaledFont::DeletionCounter(); if (mFontKeysDeleted != counter) { mFontKeysDeleted = counter; for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) { @@ -459,7 +479,8 @@ WebRenderBridgeChild::RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace) // Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent. // Since usage of invalid keys could cause crash in webrender. mIdNamespace = aNewIdNamespace; - // Just clear FontKeys, they are removed during WebRenderAPI destruction. + // Just clear FontInstaceKeys/FontKeys, they are removed during WebRenderAPI destruction. + mFontInstanceKeys.Clear(); mFontKeys.Clear(); GetCompositorBridgeChild()->RecvInvalidateLayers(wr::AsUint64(mPipelineId)); return IPC_OK(); diff --git a/gfx/layers/wr/WebRenderBridgeChild.h b/gfx/layers/wr/WebRenderBridgeChild.h index 026b703c7916..9f9376f97d08 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.h +++ b/gfx/layers/wr/WebRenderBridgeChild.h @@ -27,13 +27,14 @@ class CompositorBridgeChild; class StackingContextHelper; class TextureForwarder; -class UnscaledFontHashKey : public PLDHashEntryHdr +template +class WeakPtrHashKey : public PLDHashEntryHdr { public: - typedef gfx::UnscaledFont* KeyType; - typedef const gfx::UnscaledFont* KeyTypePointer; + typedef T* KeyType; + typedef const T* KeyTypePointer; - explicit UnscaledFontHashKey(KeyTypePointer aKey) : mKey(const_cast(aKey)) {} + explicit WeakPtrHashKey(KeyTypePointer aKey) : mKey(const_cast(aKey)) {} KeyType GetKey() const { return mKey; } bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } @@ -46,9 +47,12 @@ public: enum { ALLOW_MEMMOVE = true }; private: - WeakPtr mKey; + WeakPtr mKey; }; +typedef WeakPtrHashKey UnscaledFontHashKey; +typedef WeakPtrHashKey ScaledFontHashKey; + class WebRenderBridgeChild final : public PWebRenderBridgeChild , public CompositableForwarder { @@ -109,7 +113,7 @@ public: const StackingContextHelper& aSc, const LayerRect& aBounds, const LayerRect& aClip); - wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont); + wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont); void RemoveExpiredFontKeys(); void ClearReadLocks(); @@ -179,6 +183,9 @@ private: uint32_t mFontKeysDeleted; nsDataHashtable mFontKeys; + + uint32_t mFontInstanceKeysDeleted; + nsDataHashtable mFontInstanceKeys; }; } // namespace layers diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 0cc062d822bf..2a3b9c3cbf7f 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -312,8 +312,7 @@ WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey) return IPC_OK(); } - if (mFontKeys.find(wr::AsUint64(aFontKey)) != mFontKeys.end()) { - mFontKeys.erase(wr::AsUint64(aFontKey)); + if (mFontKeys.erase(wr::AsUint64(aFontKey)) > 0) { mApi->DeleteFont(aFontKey); } else { MOZ_ASSERT_UNREACHABLE("invalid FontKey"); @@ -322,6 +321,54 @@ WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey) return IPC_OK(); } +mozilla::ipc::IPCResult +WebRenderBridgeParent::RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey, + const wr::FontKey& aFontKey, + const float& aGlyphSize, + const MaybeFontInstanceOptions& aOptions, + const MaybeFontInstancePlatformOptions& aPlatformOptions) +{ + if (mDestroyed) { + return IPC_OK(); + } + + // Check if key is obsoleted. + if (aInstanceKey.mNamespace != mIdNamespace) { + return IPC_OK(); + } + + MOZ_ASSERT(mApi); + MOZ_ASSERT(mFontInstanceKeys.find(wr::AsUint64(aInstanceKey)) == mFontInstanceKeys.end()); + + mFontInstanceKeys.insert(wr::AsUint64(aInstanceKey)); + mApi->AddFontInstance(aInstanceKey, aFontKey, aGlyphSize, + aOptions.ptrOr(nullptr), aPlatformOptions.ptrOr(nullptr)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +WebRenderBridgeParent::RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey) +{ + if (mDestroyed) { + return IPC_OK(); + } + MOZ_ASSERT(mApi); + + // Check if key is obsoleted. + if (aInstanceKey.mNamespace != mIdNamespace) { + return IPC_OK(); + } + + if (mFontInstanceKeys.erase(wr::AsUint64(aInstanceKey)) > 0) { + mApi->DeleteFontInstance(aInstanceKey); + } else { + MOZ_ASSERT_UNREACHABLE("invalid FontInstanceKey"); + } + + return IPC_OK(); +} + mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey, const gfx::IntSize& aSize, @@ -357,8 +404,7 @@ WebRenderBridgeParent::RecvDeleteImage(const wr::ImageKey& aImageKey) return IPC_OK(); } - if (mActiveImageKeys.find(wr::AsUint64(aImageKey)) != mActiveImageKeys.end()) { - mActiveImageKeys.erase(wr::AsUint64(aImageKey)); + if (mActiveImageKeys.erase(wr::AsUint64(aImageKey)) > 0) { mKeysToDelete.push_back(aImageKey); } else { MOZ_ASSERT_UNREACHABLE("invalid ImageKey"); @@ -374,9 +420,8 @@ WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray } for (uint32_t i = 0; i < aIds.Length(); i++) { - if (mActiveAnimations.find(aIds[i]) != mActiveAnimations.end()) { + if (mActiveAnimations.erase(aIds[i]) > 0) { mAnimStorage->ClearById(aIds[i]); - mActiveAnimations.erase(aIds[i]); } else { NS_ERROR("Tried to delete invalid animation"); } @@ -1307,6 +1352,7 @@ WebRenderBridgeParent::ClearResources() // Schedule composition to clean up Pipeline mCompositorScheduler->ScheduleComposition(); // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction. + mFontInstanceKeys.clear(); mFontKeys.clear(); mActiveImageKeys.clear(); mKeysToDelete.clear(); diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h index d3a6eac81839..ca3e5fba3354 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -92,6 +92,12 @@ public: const ByteBuffer& aBuffer, const uint32_t& aFontIndex) override; mozilla::ipc::IPCResult RecvDeleteFont(const wr::FontKey& aFontKey) override; + mozilla::ipc::IPCResult RecvAddFontInstance(const wr::FontInstanceKey& aInstanceKey, + const wr::FontKey& aFontKey, + const float& aGlyphSize, + const MaybeFontInstanceOptions& aOptions, + const MaybeFontInstancePlatformOptions& aPlatformOptions) override; + mozilla::ipc::IPCResult RecvDeleteFontInstance(const wr::FontInstanceKey& aInstanceKey) override; mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override; mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize, InfallibleTArray&& aCommands, @@ -272,6 +278,7 @@ private: // WebRenderBridgeParent is destroyed abnormally and Tab move between different windows. std::unordered_set mActiveImageKeys; std::unordered_set mFontKeys; + std::unordered_set mFontInstanceKeys; // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is // destroyed abnormally and Tab move between different windows. std::unordered_set mActiveAnimations; diff --git a/gfx/layers/wr/WebRenderMessageUtils.h b/gfx/layers/wr/WebRenderMessageUtils.h index 7bbc7fdafe93..56694eb72b51 100644 --- a/gfx/layers/wr/WebRenderMessageUtils.h +++ b/gfx/layers/wr/WebRenderMessageUtils.h @@ -53,6 +53,24 @@ struct ParamTraits { }; +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + +template<> +struct ParamTraits + : public PlainOldDataSerializer +{ +}; + template<> struct ParamTraits : public PlainOldDataSerializer diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index f81fcc0b1ff5..043da2a1a51d 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -524,6 +524,22 @@ WebRenderAPI::DeleteFont(wr::FontKey aKey) wr_api_delete_font(mDocHandle, aKey); } +void +WebRenderAPI::AddFontInstance(wr::FontInstanceKey aKey, + wr::FontKey aFontKey, + float aGlyphSize, + const wr::FontInstanceOptions* aOptions, + const wr::FontInstancePlatformOptions* aPlatformOptions) +{ + wr_api_add_font_instance(mDocHandle, aKey, aFontKey, aGlyphSize, aOptions, aPlatformOptions); +} + +void +WebRenderAPI::DeleteFontInstance(wr::FontInstanceKey aKey) +{ + wr_api_delete_font_instance(mDocHandle, aKey); +} + class FrameStartTime : public RendererEvent { public: @@ -922,15 +938,15 @@ void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, const gfx::Color& aColor, - wr::FontKey aFontKey, + wr::FontInstanceKey aFontKey, Range aGlyphBuffer, - float aGlyphSize) + const wr::GlyphOptions* aGlyphOptions) { wr_dp_push_text(mWrState, aBounds, aClip, ToColorF(aColor), aFontKey, &aGlyphBuffer[0], aGlyphBuffer.length(), - aGlyphSize); + aGlyphOptions); } void diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 32a0543f6604..efcaa5fbc592 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -123,6 +123,14 @@ public: void DeleteFont(wr::FontKey aKey); + void AddFontInstance(wr::FontInstanceKey aKey, + wr::FontKey aFontKey, + float aGlyphSize, + const wr::FontInstanceOptions* aOptions, + const wr::FontInstancePlatformOptions* aPlatformOptions); + + void DeleteFontInstance(wr::FontInstanceKey aKey); + void SetFrameStartTime(const TimeStamp& aTime); void RunOnRenderThread(UniquePtr aEvent); @@ -300,9 +308,9 @@ public: void PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, const gfx::Color& aColor, - wr::FontKey aFontKey, + wr::FontInstanceKey aFontKey, Range aGlyphBuffer, - float aGlyphSize); + const wr::GlyphOptions* aGlyphOptions = nullptr); void PushLine(const wr::LayoutRect& aClip, const wr::Line& aLine); diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index 7f146445c930..c3c83e3d0c31 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -27,6 +27,7 @@ typedef wr::WrWindowId WindowId; typedef wr::WrPipelineId PipelineId; typedef wr::WrImageKey ImageKey; typedef wr::WrFontKey FontKey; +typedef wr::WrFontInstanceKey FontInstanceKey; typedef wr::WrEpoch Epoch; typedef wr::WrExternalImageId ExternalImageId; typedef wr::WrDebugFlags DebugFlags; @@ -34,6 +35,9 @@ typedef wr::WrDebugFlags DebugFlags; typedef mozilla::Maybe MaybeImageMask; typedef Maybe MaybeExternalImageId; +typedef Maybe MaybeFontInstanceOptions; +typedef Maybe MaybeFontInstancePlatformOptions; + inline WindowId NewWindowId(uint64_t aId) { WindowId id; id.mHandle = aId; @@ -141,6 +145,19 @@ inline FontKey AsFontKey(const uint64_t& aId) { return fontKey; } +// Whenever possible, use wr::FontInstanceKey instead of manipulating uint64_t. +inline uint64_t AsUint64(const FontInstanceKey& aId) { + return (static_cast(aId.mNamespace.mHandle) << 32) + + static_cast(aId.mHandle); +} + +inline FontInstanceKey AsFontInstanceKey(const uint64_t& aId) { + FontInstanceKey instanceKey; + instanceKey.mNamespace.mHandle = aId >> 32; + instanceKey.mHandle = aId; + return instanceKey; +} + // Whenever possible, use wr::PipelineId instead of manipulating uint64_t. inline uint64_t AsUint64(const PipelineId& aId) { return (static_cast(aId.mNamespace) << 32) diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index abcfb638aaac..d94140d39059 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -39,6 +39,8 @@ type WrImageKey = ImageKey; /// cbindgen:field-names=[mNamespace, mHandle] type WrFontKey = FontKey; /// cbindgen:field-names=[mNamespace, mHandle] +type WrFontInstanceKey = FontInstanceKey; +/// cbindgen:field-names=[mNamespace, mHandle] type WrYuvColorSpace = YuvColorSpace; fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] { @@ -907,6 +909,32 @@ pub extern "C" fn wr_api_delete_font(dh: &mut DocumentHandle, dh.api.update_resources(resources); } +#[no_mangle] +pub extern "C" fn wr_api_add_font_instance(dh: &mut DocumentHandle, + key: WrFontInstanceKey, + font_key: WrFontKey, + glyph_size: f32, + options: *const FontInstanceOptions, + platform_options: *const FontInstancePlatformOptions) { + assert!(unsafe { is_in_compositor_thread() }); + let mut resources = ResourceUpdates::new(); + resources.add_font_instance(key, + font_key, + Au::from_f32_px(glyph_size), + unsafe { options.as_ref().cloned() }, + unsafe { platform_options.as_ref().cloned() }); + dh.api.update_resources(resources); +} + +#[no_mangle] +pub extern "C" fn wr_api_delete_font_instance(dh: &mut DocumentHandle, + key: WrFontInstanceKey) { + assert!(unsafe { is_in_compositor_thread() }); + let mut resources = ResourceUpdates::new(); + resources.delete_font_instance(key); + dh.api.update_resources(resources); +} + #[no_mangle] pub unsafe extern "C" fn wr_api_get_namespace(dh: &mut DocumentHandle) -> WrIdNamespace { dh.api.get_namespace_id() @@ -1264,26 +1292,22 @@ pub extern "C" fn wr_dp_push_text(state: &mut WrState, bounds: LayoutRect, clip: LayoutRect, color: ColorF, - font_key: WrFontKey, + font_key: WrFontInstanceKey, glyphs: *const GlyphInstance, glyph_count: u32, - glyph_size: f32) { + glyph_options: *const GlyphOptions) { assert!(unsafe { is_in_main_thread() }); let glyph_slice = make_slice(glyphs, glyph_count as usize); - let colorf = ColorF::new(color.r, color.g, color.b, color.a); - - let glyph_options = None; // TODO state.frame_builder .dl_builder .push_text(bounds, Some(LocalClip::Rect(clip.into())), &glyph_slice, font_key, - colorf, - Au::from_f32_px(glyph_size), - glyph_options); + color, + unsafe { glyph_options.as_ref().cloned() }); } #[no_mangle] diff --git a/gfx/webrender_bindings/src/moz2d_renderer.rs b/gfx/webrender_bindings/src/moz2d_renderer.rs index 5ab8bff0ebeb..57005f4f635c 100644 --- a/gfx/webrender_bindings/src/moz2d_renderer.rs +++ b/gfx/webrender_bindings/src/moz2d_renderer.rs @@ -120,8 +120,12 @@ impl BlobImageRenderer for Moz2dImageRenderer { // If we break out of the loop above it means the channel closed unexpectedly. Err(BlobImageError::Other("Channel closed".into())) } + fn delete_font(&mut self, _font: FontKey) { } + + fn delete_font_instance(&mut self, _key: FontInstanceKey) { + } } impl Moz2dImageRenderer { diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index c5bcbfa6d3fa..7dbadf5c7ed4 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -59,6 +59,14 @@ enum class ExternalImageType : uint32_t { Sentinel /* this must be last for serialization purposes. */ }; +enum class FontRenderMode : uint32_t { + Mono = 0, + Alpha = 1, + Subpixel = 2, + + Sentinel /* this must be last for serialization purposes. */ +}; + enum class ImageFormat : uint32_t { Invalid = 0, A8 = 1, @@ -244,6 +252,18 @@ struct WrExternalImageId { typedef ExternalImageType WrExternalImageBufferType; +struct FontInstanceKey { + IdNamespace mNamespace; + uint32_t mHandle; + + bool operator==(const FontInstanceKey& aOther) const { + return mNamespace == aOther.mNamespace && + mHandle == aOther.mHandle; + } +}; + +typedef FontInstanceKey WrFontInstanceKey; + struct FontKey { IdNamespace mNamespace; uint32_t mHandle; @@ -256,6 +276,24 @@ struct FontKey { typedef FontKey WrFontKey; +struct FontInstanceOptions { + FontRenderMode render_mode; + + bool operator==(const FontInstanceOptions& aOther) const { + return render_mode == aOther.render_mode; + } +}; + +struct FontInstancePlatformOptions { + bool use_embedded_bitmap; + bool force_gdi_rendering; + + bool operator==(const FontInstancePlatformOptions& aOther) const { + return use_embedded_bitmap == aOther.use_embedded_bitmap && + force_gdi_rendering == aOther.force_gdi_rendering; + } +}; + struct Epoch { uint32_t mHandle; @@ -592,6 +630,14 @@ struct GlyphInstance { } }; +struct GlyphOptions { + FontRenderMode render_mode; + + bool operator==(const GlyphOptions& aOther) const { + return render_mode == aOther.render_mode; + } +}; + struct TextShadow { LayoutVector2D offset; ColorF color; @@ -727,6 +773,15 @@ void wr_api_add_external_image(DocumentHandle *aDh, uint8_t aChannelIndex) WR_FUNC; +WR_INLINE +void wr_api_add_font_instance(DocumentHandle *aDh, + WrFontInstanceKey aKey, + WrFontKey aFontKey, + float aGlyphSize, + const FontInstanceOptions *aOptions, + const FontInstancePlatformOptions *aPlatformOptions) +WR_FUNC; + WR_INLINE void wr_api_add_image(DocumentHandle *aDh, WrImageKey aImageKey, @@ -762,6 +817,11 @@ void wr_api_delete_font(DocumentHandle *aDh, WrFontKey aKey) WR_FUNC; +WR_INLINE +void wr_api_delete_font_instance(DocumentHandle *aDh, + WrFontInstanceKey aKey) +WR_FUNC; + WR_INLINE void wr_api_delete_image(DocumentHandle *aDh, WrImageKey aKey) @@ -1050,10 +1110,10 @@ void wr_dp_push_text(WrState *aState, LayoutRect aBounds, LayoutRect aClip, ColorF aColor, - WrFontKey aFontKey, + WrFontInstanceKey aFontKey, const GlyphInstance *aGlyphs, uint32_t aGlyphCount, - float aGlyphSize) + const GlyphOptions *aGlyphOptions) WR_FUNC; WR_INLINE From d04d8633adb0b7b5a1d1679f116250b28028d6a2 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Thu, 31 Aug 2017 20:47:02 +0200 Subject: [PATCH 20/51] Bug 1313372 - Disable browser/components/extensions/test/browser/browser_ext_browserAction_popup.js on linux32 debug: Fix test annotationsyntax. r=bustage-fix on a CLOSED TREE --- browser/components/extensions/test/browser/browser-common.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index b4b2f469eb50..8a36882ce716 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -41,7 +41,7 @@ skip-if = os == 'linux' [browser_ext_browserAction_pageAction_icon.js] [browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_popup.js] -skip-if = debug && (os == 'linux' && bits=32) # Bug 1313372 +skip-if = debug && (os == 'linux' && bits == 32) # Bug 1313372 [browser_ext_browserAction_popup_preload.js] skip-if = (os == 'win' && !debug) # bug 1352668 [browser_ext_browserAction_popup_resize.js] From f0d8f052808759b5d6e86f93aa543bc0958e4a7b Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Thu, 31 Aug 2017 12:20:05 -0700 Subject: [PATCH 21/51] client code - Bug 1367910 - Delete active-updates.xml instead of writing to it when there isn't an active update. r=dothayer When there are no updates to write to an xml file just delete the associated xml file Cleaned up loading of the updates.xml and active-update.xml files so |_ensureUpdates| is no longer needed Added dirty check for whether to write the update history in updates.xml Adds errorCode property to nsIUpdatePatch so it is saved to the xml Adds errorCode for trying to apply an older version or same version with the same build ID Adds errorCode for when there is a no status file Adds errorCode for channel change --- toolkit/mozapps/update/nsIUpdateService.idl | 19 +- toolkit/mozapps/update/nsUpdateService.js | 436 +++++++++++--------- 2 files changed, 250 insertions(+), 205 deletions(-) diff --git a/toolkit/mozapps/update/nsIUpdateService.idl b/toolkit/mozapps/update/nsIUpdateService.idl index 2ae7efe1d1e1..6d1c7cded98a 100644 --- a/toolkit/mozapps/update/nsIUpdateService.idl +++ b/toolkit/mozapps/update/nsIUpdateService.idl @@ -49,6 +49,14 @@ interface nsIUpdatePatch : nsISupports */ attribute AString state; + /** + * A numeric error code that conveys additional information about the state of + * a failed update. If the update is not in the "failed" state the value is + * zero. The possible values are located in common/errors.h and values between + * 80 and 99 are in nsUpdateService.js. + */ + attribute long errorCode; + /** * true if this patch is currently selected as the patch to be downloaded and * installed for this update transaction, false if another patch from this @@ -183,13 +191,10 @@ interface nsIUpdate : nsISupports attribute AString state; /** - * A numeric error code that conveys additional information about the state - * of a failed update or failed certificate attribute check during an update - * check. If the update is not in the "failed" state or the certificate - * attribute check has not failed the value is zero. - * - * TODO: Define typical error codes (for now, see updater/errors.h and the - * CERT_ATTR_CHECK_FAILED_* values in nsUpdateService.js) + * A numeric error code that conveys additional information about the state of + * a failed update. If the update is not in the "failed" state the value is + * zero. The possible values are located in common/errors.h and values between + * 80 and 99 are in nsUpdateService.js. */ attribute long errorCode; diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 941a4870ec11..0ea3439ff32a 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -144,6 +144,9 @@ const SERVICE_ERRORS = [SERVICE_UPDATER_COULD_NOT_BE_STARTED, // Error codes 80 through 99 are reserved for nsUpdateService.js and are not // defined in common/errors.h +const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; +const ERR_UPDATE_STATE_NONE = 91; +const ERR_CHANNEL_CHANGE = 92; const INVALID_UPDATER_STATE_CODE = 98; const INVALID_UPDATER_STATUS_CODE = 99; @@ -849,6 +852,8 @@ function cleanupActiveUpdate() { // Move the update from the Active Update list into the Past Updates list. var um = Cc["@mozilla.org/updates/update-manager;1"]. getService(Ci.nsIUpdateManager); + // Setting |activeUpdate| to null will move the active update to the update + // history. um.activeUpdate = null; um.saveUpdates(); @@ -1151,17 +1156,22 @@ UpdatePatch.prototype = { */ serialize: function UpdatePatch_serialize(updates) { var patch = updates.createElementNS(URI_UPDATE_NS, "patch"); - patch.setAttribute("type", this.type); - patch.setAttribute("URL", this.URL); + // Don't write an errorCode if it evaluates to false since 0 is the same as + // no error code. + if (this.errorCode) { + patch.setAttribute("errorCode", this.errorCode); + } // finalURL is not available until after the download has started if (this.finalURL) { patch.setAttribute("finalURL", this.finalURL); } - patch.setAttribute("size", this.size); if (this.selected) { patch.setAttribute("selected", this.selected); } + patch.setAttribute("size", this.size); patch.setAttribute("state", this.state); + patch.setAttribute("type", this.type); + patch.setAttribute("URL", this.URL); for (let p in this._properties) { if (this._properties[p].present) { @@ -1218,22 +1228,20 @@ UpdatePatch.prototype = { }, /** - * Returns whether or not the update.status file for this patch exists at the - * appropriate location. + * See nsIUpdateService.idl */ - get statusFileExists() { - var statusFile = getUpdatesDir(); - statusFile.append(FILE_UPDATE_STATUS); - return statusFile.exists(); + get errorCode() { + return this._properties.errorCode || 0; + }, + set errorCode(val) { + this._properties.errorCode = val; }, /** * See nsIUpdateService.idl */ get state() { - if (this._properties.state) - return this._properties.state; - return STATE_NONE; + return this._properties.state || STATE_NONE; }, set state(val) { this._properties.state = val; @@ -1307,6 +1315,11 @@ function Update(update) { if (val) { this.installDate = val; } + } else if (attr.name == "errorCode" && attr.value) { + let val = parseInt(attr.value); + if (val) { + this.errorCode = val; + } } else if (attr.name == "isCompleteUpdate") { this.isCompleteUpdate = attr.value == "true"; } else if (attr.name == "promptWaitTime") { @@ -1383,26 +1396,40 @@ Update.prototype = { * * We use a copy of the state cached on this object in |_state| only when * there is no selected patch, i.e. in the case when we could not load - * |.activeUpdate| from the update manager for some reason but still have + * |activeUpdate| from the update manager for some reason but still have * the update.status file to work with. */ _state: "", - set state(state) { - if (this.selectedPatch) - this.selectedPatch.state = state; - this._state = state; - return state; - }, get state() { if (this.selectedPatch) return this.selectedPatch.state; return this._state; }, + set state(state) { + if (this.selectedPatch) + this.selectedPatch.state = state; + this._state = state; + }, /** * See nsIUpdateService.idl + * + * We use a copy of the errorCode cached on this object in |_errorCode| only + * when there is no selected patch, i.e. in the case when we could not load + * |activeUpdate| from the update manager for some reason but still have + * the update.status file to work with. */ - errorCode: 0, + _errorCode: 0, + get errorCode() { + if (this.selectedPatch) + return this.selectedPatch.errorCode; + return this._errorCode; + }, + set errorCode(errorCode) { + if (this.selectedPatch) + this.selectedPatch.errorCode = errorCode; + this._errorCode = errorCode; + }, /** * See nsIUpdateService.idl @@ -1441,15 +1468,15 @@ Update.prototype = { } var update = updates.createElementNS(URI_UPDATE_NS, "update"); update.setAttribute("appVersion", this.appVersion); + update.setAttribute("backgroundInterval", this.backgroundInterval); update.setAttribute("buildID", this.buildID); update.setAttribute("channel", this.channel); update.setAttribute("displayVersion", this.displayVersion); update.setAttribute("installDate", this.installDate); update.setAttribute("isCompleteUpdate", this.isCompleteUpdate); update.setAttribute("name", this.name); - update.setAttribute("serviceURL", this.serviceURL); update.setAttribute("promptWaitTime", this.promptWaitTime); - update.setAttribute("backgroundInterval", this.backgroundInterval); + update.setAttribute("serviceURL", this.serviceURL); update.setAttribute("type", this.type); if (this.detailsURL) { @@ -1681,40 +1708,85 @@ UpdateService.prototype = { getService(Ci.nsIUpdateManager); var update = um.activeUpdate; var status = readStatusFile(getUpdatesDir()); - pingStateAndStatusCodes(update, true, status); - // STATE_NONE status typically means that the update.status file is present - // but a background download error occurred. if (status == STATE_NONE) { - LOG("UpdateService:_postUpdateProcessing - no status, no update"); + // A status of STATE_NONE in _postUpdateProcessing means that the + // update.status file is present but there isn't an update in progress so + // cleanup the update. + LOG("UpdateService:_postUpdateProcessing - status is none"); + if (!update) { + update = new Update(null); + } + update.state = STATE_FAILED; + update.errorCode = ERR_UPDATE_STATE_NONE; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_UPDATE_STATE_NONE; + pingStateAndStatusCodes(update, true, newStatus); cleanupActiveUpdate(); return; } - // Handle the case when the update is the same or older than the current - // version and nsUpdateDriver.cpp skipped updating due to the version being - // older than the current version. - if (update && update.appVersion && - (status == STATE_PENDING || status == STATE_PENDING_SERVICE || - status == STATE_APPLIED || status == STATE_APPLIED_SERVICE || - status == STATE_PENDING_ELEVATE)) { - if (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || - Services.vc.compare(update.appVersion, Services.appinfo.version) == 0 && - update.buildID == Services.appinfo.appBuildID) { - LOG("UpdateService:_postUpdateProcessing - removing update for older " + - "or same application version"); + if (update && update.channel != UpdateUtils.UpdateChannel) { + LOG("UpdateService:_postUpdateProcessing - channel has changed, " + + "reloading default preferences to workaround bug 802022"); + // Workaround to get the distribution preferences loaded (Bug 774618). + // This can be removed after bug 802022 is fixed. Now that this code runs + // later during startup this code may no longer be necessary but it + // shouldn't be removed until after bug 802022 is fixed. + let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver); + prefSvc.observe(null, "reload-default-prefs", null); + if (update.channel != UpdateUtils.UpdateChannel) { + LOG("UpdateService:_postUpdateProcessing - update channel is " + + "different than application's channel, removing update. update " + + "channel: " + update.channel + ", expected channel: " + + UpdateUtils.UpdateChannel); + // User switched channels, clear out the old active update and remove + // partial downloads + update.state = STATE_FAILED; + update.errorCode = ERR_CHANNEL_CHANGE; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_CHANNEL_CHANGE; + pingStateAndStatusCodes(update, true, newStatus); cleanupActiveUpdate(); return; } } + // Handle the case when the update is the same or older than the current + // version and nsUpdateDriver.cpp skipped updating due to the version being + // older than the current version. This also handles the general case when + // an update is for an older version or the same version and same build ID. + if (update && update.appVersion && + (status == STATE_PENDING || status == STATE_PENDING_SERVICE || + status == STATE_APPLIED || status == STATE_APPLIED_SERVICE || + status == STATE_PENDING_ELEVATE || status == STATE_DOWNLOADING)) { + if (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || + Services.vc.compare(update.appVersion, Services.appinfo.version) == 0 && + update.buildID == Services.appinfo.appBuildID) { + LOG("UpdateService:_postUpdateProcessing - removing update for older " + + "application version or same application version with same build " + + "ID. update application version: " + update.appVersion + ", " + + "application version: " + Services.appinfo.version + ", update " + + "build ID: " + update.buildID + ", application build ID: " + + Services.appinfo.appBuildID); + update.state = STATE_FAILED; + update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + update.errorCode = ERR_OLDER_VERSION_OR_SAME_BUILD; + // This could be split out to report telemetry for each case. + let newStatus = STATE_FAILED + ": " + ERR_OLDER_VERSION_OR_SAME_BUILD; + pingStateAndStatusCodes(update, true, newStatus); + cleanupActiveUpdate(); + return; + } + } + + pingStateAndStatusCodes(update, true, status); if (status == STATE_DOWNLOADING) { LOG("UpdateService:_postUpdateProcessing - patch found in downloading " + "state"); - if (update && update.state != STATE_SUCCEEDED) { - // Resume download - status = this.downloadUpdate(update, true); - if (status == STATE_NONE) - cleanupActiveUpdate(); + // Resume download + status = this.downloadUpdate(update, true); + if (status == STATE_NONE) { + cleanupActiveUpdate(); } return; } @@ -1766,9 +1838,6 @@ UpdateService.prototype = { if (status != STATE_SUCCEEDED) { - // Since the update didn't succeed save a copy of the active update's - // current state to the updates.xml so it is possible to track failures. - um.saveUpdates(); // Rotate the update logs so the update log isn't removed. By passing // false the patch directory won't be removed. cleanUpUpdatesDir(false); @@ -1780,8 +1849,11 @@ UpdateService.prototype = { } update.statusText = gUpdateBundle.GetStringFromName("installSuccess"); - // Update the patch's metadata. - um.activeUpdate = update; + // The only time that update is not a reference to activeUpdate is when + // activeUpdate is null. + if (!um.activeUpdate) { + um.activeUpdate = update; + } // Done with this update. Clean it up. cleanupActiveUpdate(); @@ -2439,68 +2511,80 @@ UpdateService.prototype = { * @constructor */ function UpdateManager() { - // Ensure the Active Update file is loaded - var updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (updates.length > 0) { - // Under some edgecases such as Windows system restore the active-update.xml - // will contain a pending update without the status file which will return - // STATE_NONE. To recover from this situation clean the updates dir and - // rewrite the active-update.xml file without the broken update. + // Ensure the active update file is loaded and that the active update is set + // in the update manager if there is an existing active update. + let activeUpdates = this._loadXMLFileIntoArray(FILE_ACTIVE_UPDATE_XML); + if (activeUpdates.length > 0) { + this._activeUpdate = activeUpdates[0]; + // This check is performed here since UpdateService:_postUpdateProcessing + // won't be called when there isn't an update.status file. if (readStatusFile(getUpdatesDir()) == STATE_NONE) { + // Under some edgecases such as Windows system restore the + // active-update.xml will contain a pending update without the status + // file. To recover from this situation clean the updates dir and move + // the active update to the update history. + this._activeUpdate.state = STATE_FAILED; + this._activeUpdate.errorCode = ERR_UPDATE_STATE_NONE; + this._activeUpdate.statusText = gUpdateBundle.GetStringFromName("statusFailed"); + let newStatus = STATE_FAILED + ": " + ERR_UPDATE_STATE_NONE; + pingStateAndStatusCodes(this._activeUpdate, true, newStatus); + // Setting |activeUpdate| to null will move the active update to the + // update history. + this.activeUpdate = null; + this.saveUpdates(); cleanUpUpdatesDir(); - this._writeUpdatesToXMLFile([], getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - } else - this._activeUpdate = updates[0]; + } } } UpdateManager.prototype = { - /** - * All previously downloaded and installed updates, as an array of nsIUpdate - * objects. - */ - _updates: null, - /** * The current actively downloading/installing update, as a nsIUpdate object. */ _activeUpdate: null, /** - * Handle Observer Service notifications - * @param subject - * The subject of the notification - * @param topic - * The notification name - * @param data - * Additional data + * Whether the update history stored in _updates has changed since it was + * loaded. + */ + _updatesDirty: false, + + /** + * See nsIObserver.idl */ observe: function UM_observe(subject, topic, data) { // Hack to be able to run and cleanup tests by reloading the update data. if (topic == "um-reload-update-data") { - this._updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_UPDATES_XML])); this._activeUpdate = null; - var updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (updates.length > 0) - this._activeUpdate = updates[0]; + let activeUpdates = this._loadXMLFileIntoArray(FILE_ACTIVE_UPDATE_XML); + if (activeUpdates.length > 0) { + this._activeUpdate = activeUpdates[0]; + } + delete this._updates; + let updates = this._loadXMLFileIntoArray(FILE_UPDATES_XML); + Object.defineProperty(this, "_updates", { + value: updates, + writable: true, + configurable: true, + enumerable: true + }); } }, /** * Loads an updates.xml formatted file into an array of nsIUpdate items. - * @param file - * A nsIFile for the updates.xml file + * @param fileName + * The file name in the updates directory to load. * @return The array of nsIUpdate items held in the file. */ - _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(file) { + _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(fileName) { + let updates = []; + let file = getUpdateFile([fileName]); if (!file.exists()) { - LOG("UpdateManager:_loadXMLFileIntoArray: XML file does not exist"); - return []; + LOG("UpdateManager:_loadXMLFileIntoArray - XML file does not exist. " + + "path: " + file.path); + return updates; } - var result = []; var fileStream = Cc["@mozilla.org/network/file-input-stream;1"]. createInstance(Ci.nsIFileInputStream); fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); @@ -2526,43 +2610,44 @@ UpdateManager.prototype = { LOG("UpdateManager:_loadXMLFileIntoArray - invalid update"); continue; } - result.push(update); + updates.push(update); } - } catch (e) { + } catch (ex) { LOG("UpdateManager:_loadXMLFileIntoArray - error constructing update " + - "list. Exception: " + e); + "list. Exception: " + ex); } fileStream.close(); - return result; + return updates; }, /** - * Load the update manager, initializing state from state files. + * Loads the update history from the updates.xml file and then replaces + * _updates with an array of all previously downloaded and installed updates + * so the file is only read once. */ - _ensureUpdates: function UM__ensureUpdates() { - if (!this._updates) { - this._updates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_UPDATES_XML])); - var activeUpdates = this._loadXMLFileIntoArray(getUpdateFile( - [FILE_ACTIVE_UPDATE_XML])); - if (activeUpdates.length > 0) - this._activeUpdate = activeUpdates[0]; - } + get _updates() { + delete this._updates; + let updates = this._loadXMLFileIntoArray(FILE_UPDATES_XML); + Object.defineProperty(this, "_updates", { + value: updates, + writable: true, + configurable: true, + enumerable: true + }); + return this._updates; }, /** * See nsIUpdateService.idl */ - getUpdateAt: function UM_getUpdateAt(index) { - this._ensureUpdates(); - return this._updates[index]; + getUpdateAt: function UM_getUpdateAt(aIndex) { + return this._updates[aIndex]; }, /** * See nsIUpdateService.idl */ get updateCount() { - this._ensureUpdates(); return this._updates.length; }, @@ -2570,76 +2655,40 @@ UpdateManager.prototype = { * See nsIUpdateService.idl */ get activeUpdate() { - if (this._activeUpdate && - this._activeUpdate.channel != UpdateUtils.UpdateChannel) { - LOG("UpdateManager:get activeUpdate - channel has changed, " + - "reloading default preferences to workaround bug 802022"); - // Workaround to get distribution preferences loaded (Bug 774618). This - // can be removed after bug 802022 is fixed. - let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver); - prefSvc.observe(null, "reload-default-prefs", null); - if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) { - // User switched channels, clear out any old active updates and remove - // partial downloads - this._activeUpdate = null; - this.saveUpdates(); - - // Destroy the updates directory, since we're done with it. - cleanUpUpdatesDir(); - } - } return this._activeUpdate; }, - set activeUpdate(activeUpdate) { - this._addUpdate(activeUpdate); - this._activeUpdate = activeUpdate; - if (!activeUpdate) { - // If |activeUpdate| is null, we have updated both lists - the active list - // and the history list, so we want to write both files. - this.saveUpdates(); - } else - this._writeUpdatesToXMLFile([this._activeUpdate], - getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - return activeUpdate; - }, - - /** - * Add an update to the Updates list. If the item already exists in the list, - * replace the existing value with the new value. - * @param update - * The nsIUpdate object to add. - */ - _addUpdate: function UM__addUpdate(update) { - if (!update) - return; - this._ensureUpdates(); - // Only the latest update entry is checked so the the latest successful - // step for an update is recorded and all failures are kept. This way - // mutliple attempts to update for the same update are kept in the update - // history. - if (this._updates && - update.state != STATE_FAILED && - this._updates[0] && - this._updates[0].state != STATE_FAILED && - this._updates[0].appVersion == update.appVersion && - this._updates[0].buildID == update.buildID) { - // Replace the existing entry with the new value, updating - // all metadata. - this._updates[0] = update; - return; + set activeUpdate(aActiveUpdate) { + if (!aActiveUpdate && this._activeUpdate) { + this._updatesDirty = true; + // Add the current active update to the front of the update history. + this._updates.unshift(this._activeUpdate); + // Limit the update history to 10 updates. + this._updates.splice(10); } - // Otherwise add it to the front of the list. - this._updates.unshift(update); + + this._activeUpdate = aActiveUpdate; }, /** - * Serializes an array of updates to an XML file + * Serializes an array of updates to an XML file or removes the file if the + * array length is 0. * @param updates * An array of nsIUpdate objects - * @param file - * The nsIFile object to serialize to + * @param fileName + * The file name in the updates directory to write to. */ - _writeUpdatesToXMLFile: function UM__writeUpdatesToXMLFile(updates, file) { + _writeUpdatesToXMLFile: function UM__writeUpdatesToXMLFile(updates, fileName) { + let file = getUpdateFile([fileName]); + if (updates.length == 0) { + LOG("UpdateManager:_writeUpdatesToXMLFile - no updates to write. " + + "removing file: " + file.path); + try { + file.remove(false); + } catch (e) { + } + return; + } + var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); var modeFlags = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | @@ -2656,11 +2705,7 @@ UpdateManager.prototype = { var doc = parser.parseFromString(EMPTY_UPDATES_DOCUMENT, "text/xml"); for (var i = 0; i < updates.length; ++i) { - // If appVersion isn't defined don't add the update. This happens when - // cleaning up invalid updates (e.g. incorrect channel). - if (updates[i] && updates[i].appVersion) { - doc.documentElement.appendChild(updates[i].serialize(doc)); - } + doc.documentElement.appendChild(updates[i].serialize(doc)); } var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]. @@ -2676,24 +2721,17 @@ UpdateManager.prototype = { * See nsIUpdateService.idl */ saveUpdates: function UM_saveUpdates() { - this._writeUpdatesToXMLFile([this._activeUpdate], - getUpdateFile([FILE_ACTIVE_UPDATE_XML])); - if (this._activeUpdate) - this._addUpdate(this._activeUpdate); - - this._ensureUpdates(); - // Don't write updates that don't have a state to the updates.xml file. - if (this._updates) { - let updates = this._updates.slice(); - for (let i = updates.length - 1; i >= 0; --i) { - let state = updates[i].state; - if (state == STATE_NONE) { - updates.splice(i, 1); - } - } - - this._writeUpdatesToXMLFile(updates.slice(0, 20), - getUpdateFile([FILE_UPDATES_XML])); + // The active update stored in the active-update.xml file will change during + // the lifetime of an active update and should always be updated when + // saveUpdates is called. + this._writeUpdatesToXMLFile(this._activeUpdate ? [this._activeUpdate] : [], + FILE_ACTIVE_UPDATE_XML); + // The update history stored in the updates.xml file should only need to be + // updated when an active update has been added to it in which case + // |_updatesDirty| will be true. + if (this._updatesDirty) { + this._updatesDirty = false; + this._writeUpdatesToXMLFile(this._updates.slice(0, 10), FILE_UPDATES_XML); } }, @@ -2712,11 +2750,6 @@ UpdateManager.prototype = { if (update.state == STATE_FAILED && parts[1]) { update.errorCode = parseInt(parts[1]); } - let um = Cc["@mozilla.org/updates/update-manager;1"]. - getService(Ci.nsIUpdateManager); - // Save a copy of the active update's current state to the updates.xml so - // it is possible to track failures. - um.saveUpdates(); // Rotate the update logs so the update log isn't removed if a complete // update is downloaded. By passing false the patch directory won't be @@ -2728,6 +2761,8 @@ UpdateManager.prototype = { handleFallbackToCompleteUpdate(update, true); } + // This can be removed after the update ui under update/content is + // removed. update.QueryInterface(Ci.nsIWritablePropertyBag); update.setProperty("stagingFailed", "true"); } @@ -2735,6 +2770,11 @@ UpdateManager.prototype = { writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE); } + // Now that the active update's properties have been updated write the + // active-update.xml to disk. Since there have been no changes to the update + // history the updates.xml will not be written to disk. + this.saveUpdates(); + // Send an observer notification which the app update doorhanger uses to // display a restart notification LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " + @@ -3605,6 +3645,8 @@ Downloader.prototype = { getService(Ci.nsIUpdateManager); if (deleteActiveUpdate) { this._update.installDate = (new Date()).getTime(); + // Setting |activeUpdate| to null will move the active update to the + // update history. um.activeUpdate = null; } else if (um.activeUpdate) { um.activeUpdate.state = state; @@ -3664,13 +3706,11 @@ Downloader.prototype = { // downloading) and if at any point this was a foreground download // notify the user about the error. If the update was a background // update there is no notification since the user won't be expecting it. - if (!Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME)) { - this._update.QueryInterface(Ci.nsIWritablePropertyBag); - if (this._update.getProperty("foregroundDownload") == "true") { - let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. - createInstance(Ci.nsIUpdatePrompt); - prompter.showUpdateError(this._update); - } + if (!Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME) && + this._update.getProperty("foregroundDownload") == "true") { + let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. + createInstance(Ci.nsIUpdatePrompt); + prompter.showUpdateError(this._update); } // Prevent leaking the update object (bug 454964). From 9084ce73bba4f985d1e814b21ceaad1eb9945b3b Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Thu, 31 Aug 2017 12:20:22 -0700 Subject: [PATCH 22/51] test code - Bug 1367910 - Delete active-updates.xml instead of writing to it when there isn't an active update. r=dothayer --- toolkit/mozapps/update/tests/data/shared.js | 5 -- .../update/tests/data/sharedUpdateXML.js | 90 ++++++++++--------- .../update/tests/data/xpcshellUtilsAUS.js | 50 +++++++++-- .../cleanupDownloadingForDifferentChannel.js | 28 +++--- .../cleanupDownloadingForOlderAppVersion.js | 23 ++++- ...anupDownloadingForSameVersionAndBuildID.js | 23 ++++- .../cleanupDownloadingIncorrectStatus.js | 31 +++++-- ...leanupPendingVersionFileIncorrectStatus.js | 23 ++++- .../downloadResumeForSameAppVersion.js | 2 +- ...idArgCallbackFileNotInInstallDirFailure.js | 2 + ...nvalidArgCallbackFilePathTooLongFailure.js | 2 + .../invalidArgInstallDirPathTooLongFailure.js | 3 + ...nvalidArgInstallDirPathTraversalFailure.js | 3 + ...InstallWorkingDirPathNotSameFailure_win.js | 3 + .../invalidArgPatchDirPathTraversalFailure.js | 1 + ...idArgStageDirNotInInstallDirFailure_win.js | 3 + ...lidArgWorkingDirPathLocalUNCFailure_win.js | 3 + ...invalidArgWorkingDirPathRelativeFailure.js | 3 + .../marAppApplyDirLockedStageFailure_win.js | 1 + ...pApplyUpdateAppBinInUseStageSuccess_win.js | 9 +- ...marAppApplyUpdateStageOldVersionFailure.js | 30 +++---- .../marAppApplyUpdateStageSuccess.js | 9 +- .../marAppApplyUpdateSuccess.js | 9 +- .../marAppInUseStageFailureComplete_win.js | 9 +- .../marAppInUseStageSuccessComplete_unix.js | 9 +- .../marAppInUseSuccessComplete.js | 9 +- .../marCallbackAppStageSuccessComplete_win.js | 9 +- .../marCallbackAppStageSuccessPartial_win.js | 9 +- .../marCallbackAppSuccessComplete_win.js | 9 +- .../marCallbackAppSuccessPartial_win.js | 9 +- .../unit_base_updater/marFailurePartial.js | 10 +-- .../marFileInUseStageFailureComplete_win.js | 5 +- .../marFileInUseStageFailurePartial_win.js | 5 +- .../marFileInUseSuccessComplete_win.js | 9 +- .../marFileInUseSuccessPartial_win.js | 9 +- .../marFileLockedFailureComplete_win.js | 9 +- .../marFileLockedFailurePartial_win.js | 9 +- .../marFileLockedStageFailureComplete_win.js | 9 +- .../marFileLockedStageFailurePartial_win.js | 9 +- .../marPIDPersistsSuccessComplete_win.js | 9 +- ...MRFDirFileInUseStageFailureComplete_win.js | 9 +- ...RMRFDirFileInUseStageFailurePartial_win.js | 9 +- .../marRMRFDirFileInUseSuccessComplete_win.js | 9 +- .../marRMRFDirFileInUseSuccessPartial_win.js | 9 +- .../marStageFailurePartial.js | 6 +- .../marStageSuccessComplete.js | 9 +- .../marStageSuccessPartial.js | 9 +- .../unit_base_updater/marSuccessComplete.js | 9 +- .../unit_base_updater/marSuccessPartial.js | 9 +- .../unit_base_updater/marVersionDowngrade.js | 12 +-- .../unit_base_updater/marWrongChannel.js | 12 +-- ...validArgInstallDirPathTooLongFailureSvc.js | 3 + ...lidArgInstallDirPathTraversalFailureSvc.js | 3 + ...tallWorkingDirPathNotSameFailureSvc_win.js | 3 + .../invalidArgPatchDirPathSuffixFailureSvc.js | 1 + ...validArgPatchDirPathTraversalFailureSvc.js | 1 + ...rgStageDirNotInInstallDirFailureSvc_win.js | 3 + ...ArgWorkingDirPathLocalUNCFailureSvc_win.js | 3 + ...alidArgWorkingDirPathRelativeFailureSvc.js | 3 + ...marAppApplyDirLockedStageFailureSvc_win.js | 1 + ...plyUpdateAppBinInUseStageSuccessSvc_win.js | 9 +- .../marAppApplyUpdateStageSuccessSvc.js | 9 +- .../marAppApplyUpdateSuccessSvc.js | 9 +- .../marAppInUseStageFailureCompleteSvc_win.js | 9 +- .../marAppInUseSuccessCompleteSvc.js | 9 +- ...rCallbackAppStageSuccessCompleteSvc_win.js | 9 +- ...arCallbackAppStageSuccessPartialSvc_win.js | 9 +- .../marCallbackAppSuccessCompleteSvc_win.js | 9 +- .../marCallbackAppSuccessPartialSvc_win.js | 9 +- .../marFailurePartialSvc.js | 10 +-- ...marFileInUseStageFailureCompleteSvc_win.js | 5 +- .../marFileInUseStageFailurePartialSvc_win.js | 5 +- .../marFileInUseSuccessCompleteSvc_win.js | 9 +- .../marFileInUseSuccessPartialSvc_win.js | 9 +- .../marFileLockedFailureCompleteSvc_win.js | 9 +- .../marFileLockedFailurePartialSvc_win.js | 9 +- ...arFileLockedStageFailureCompleteSvc_win.js | 9 +- ...marFileLockedStageFailurePartialSvc_win.js | 9 +- ...DirFileInUseStageFailureCompleteSvc_win.js | 9 +- ...FDirFileInUseStageFailurePartialSvc_win.js | 9 +- ...rRMRFDirFileInUseSuccessCompleteSvc_win.js | 9 +- ...arRMRFDirFileInUseSuccessPartialSvc_win.js | 9 +- .../marStageFailurePartialSvc.js | 6 +- .../marStageSuccessCompleteSvc.js | 9 +- .../marStageSuccessPartialSvc.js | 9 +- .../marSuccessCompleteSvc.js | 9 +- .../marSuccessPartialSvc.js | 9 +- 87 files changed, 325 insertions(+), 533 deletions(-) diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js index 490123f4ee61..92c7bfebd865 100644 --- a/toolkit/mozapps/update/tests/data/shared.js +++ b/toolkit/mozapps/update/tests/data/shared.js @@ -120,11 +120,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gEnv", "@mozilla.org/process/environment;1", "nsIEnvironment"); -XPCOMUtils.defineLazyGetter(this, "gZipW", function test_gZipW() { - return Cc["@mozilla.org/zipwriter;1"]. - createInstance(Ci.nsIZipWriter); -}); - /* Triggers post-update processing */ function testPostUpdateProcessing() { gAUS.observe(null, "test-post-update-processing", ""); diff --git a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js index 604a8fc8fbb4..88e9519b4699 100644 --- a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js +++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js @@ -45,6 +45,12 @@ const INVALID_WORKING_DIR_PATH_ERROR = 76; const INVALID_CALLBACK_PATH_ERROR = 77; const INVALID_CALLBACK_DIR_ERROR = 78; +// Error codes 80 through 99 are reserved for nsUpdateService.js and are not +// defined in common/errors.h +const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; +const ERR_UPDATE_STATE_NONE = 91; +const ERR_CHANNEL_CHANGE = 92; + const STATE_FAILED_DELIMETER = ": "; const STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE = @@ -111,16 +117,16 @@ function getRemoteUpdatesXMLString(aUpdates) { */ function getRemoteUpdateString(aUpdateProps, aPatches) { const updateProps = { - type: "major", - name: "App Update Test", - displayVersion: null, appVersion: DEFAULT_UPDATE_VERSION, - buildID: "20080811053724", - detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", - promptWaitTime: null, backgroundInterval: null, + buildID: "20080811053724", custom1: null, - custom2: null + custom2: null, + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, + name: "App Update Test", + promptWaitTime: null, + type: "major" }; for (let name in aUpdateProps) { @@ -196,9 +202,6 @@ function getLocalUpdatesXMLString(aUpdates) { */ function getLocalUpdateString(aUpdateProps, aPatches) { const updateProps = { - type: "major", - name: "App Update Test", - displayVersion: null, _appVersion: null, get appVersion() { if (this._appVersion) { @@ -212,46 +215,49 @@ function getLocalUpdateString(aUpdateProps, aPatches) { set appVersion(val) { this._appVersion = val; }, - buildID: "20080811053724", - detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", - promptWaitTime: null, backgroundInterval: null, + buildID: "20080811053724", + channel: gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL), custom1: null, custom2: null, - serviceURL: "http://test_service/", - installDate: "1238441400314", - statusText: "Install Pending", - isCompleteUpdate: "true", - channel: gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL), + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, foregroundDownload: "true", - previousAppVersion: null + installDate: "1238441400314", + isCompleteUpdate: "true", + name: "App Update Test", + previousAppVersion: null, + promptWaitTime: null, + serviceURL: "http://test_service/", + statusText: "Install Pending", + type: "major" }; for (let name in aUpdateProps) { updateProps[name] = aUpdateProps[name]; } - let previousAppVersion = updateProps.previousAppVersion ? - "previousAppVersion=\"" + updateProps.previousAppVersion + "\" " : ""; - let serviceURL = "serviceURL=\"" + updateProps.serviceURL + "\" "; - let installDate = "installDate=\"" + updateProps.installDate + "\" "; - let statusText = updateProps.statusText ? - "statusText=\"" + updateProps.statusText + "\" " : ""; + let channel = "channel=\"" + updateProps.channel + "\" "; let isCompleteUpdate = "isCompleteUpdate=\"" + updateProps.isCompleteUpdate + "\" "; - let channel = "channel=\"" + updateProps.channel + "\" "; let foregroundDownload = updateProps.foregroundDownload ? - "foregroundDownload=\"" + updateProps.foregroundDownload + "\">" : ">"; + "foregroundDownload=\"" + updateProps.foregroundDownload + "\" " : ""; + let installDate = "installDate=\"" + updateProps.installDate + "\" "; + let previousAppVersion = updateProps.previousAppVersion ? + "previousAppVersion=\"" + updateProps.previousAppVersion + "\" " : ""; + let statusText = updateProps.statusText ? + "statusText=\"" + updateProps.statusText + "\" " : ""; + let serviceURL = "serviceURL=\"" + updateProps.serviceURL + "\">"; return getUpdateString(updateProps) + " " + - previousAppVersion + - serviceURL + - installDate + - statusText + - isCompleteUpdate + channel + + isCompleteUpdate + foregroundDownload + + installDate + + previousAppVersion + + statusText + + serviceURL + aPatches + ""; } @@ -310,16 +316,16 @@ function getUpdateString(aUpdateProps) { let custom2 = aUpdateProps.custom2 ? aUpdateProps.custom2 + " " : ""; let buildID = "buildID=\"" + aUpdateProps.buildID + "\""; - return " 0) { + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, aUpdateStatusState, msgTags[i] + + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, aUpdateErrCode, msgTags[i] + + "the first update errorCode" + MSG_SHOULD_EQUAL); + } + if (i != msgTags.length - 1) { + reloadUpdateManagerData(); + } + } +} + /** * On Mac OS X and Windows this checks if the post update '.running' file exists * to determine if the post update binary was launched. @@ -1968,11 +2013,6 @@ function checkUpdateStagedState(aUpdateState) { "the update state" + MSG_SHOULD_EQUAL); } - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE, - "the update state" + MSG_SHOULD_EQUAL); - let log = getUpdateLog(FILE_LAST_UPDATE_LOG); Assert.ok(log.exists(), MSG_SHOULD_EXIST + getMsgPath(log.path)); diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js index 0592ba597987..24cb2e1b23f8 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js @@ -28,19 +28,25 @@ function run_test() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager update count" + MSG_SHOULD_EQUAL); - let update = gUpdateManager.getUpdateAt(0); - Assert.equal(update.name, "Existing", - "the update's name" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - // Verify that the active-update.xml file has had the update from the old - // channel removed. - let file = getUpdatesXMLFile(true); - Assert.equal(readFile(file), getLocalUpdatesXMLString(""), - "the contents of active-update.xml" + MSG_SHOULD_EQUAL); + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, + "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_CHANNEL_CHANGE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js index c59d5fe6b692..04619f109106 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js @@ -17,12 +17,33 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_DOWNLOADING); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_OLDER_VERSION_OR_SAME_BUILD, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js index a440ff8bad98..79ad55a52313 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js @@ -18,12 +18,33 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_DOWNLOADING); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_OLDER_VERSION_OR_SAME_BUILD, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js index ccffbb7a5750..2ce379cd97a8 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js @@ -15,8 +15,34 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeStatusFile(STATE_NONE); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + let updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); + Assert.ok(!gUpdateManager.activeUpdate, + "there should not be an active update"); + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, + "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_UPDATE_STATE_NONE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); + let dir = getUpdatesDir(); dir.append(DIR_PATCH); Assert.ok(dir.exists(), MSG_SHOULD_EXIST); @@ -25,10 +51,5 @@ function run_test() { statusFile.append(FILE_UPDATE_STATUS); Assert.ok(!statusFile.exists(), MSG_SHOULD_NOT_EXIST); - Assert.ok(!gUpdateManager.activeUpdate, - "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, - "the update manager update count" + MSG_SHOULD_EQUAL); - doTestFinish(); } diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js index d514c31e6cee..49a7de1b3bc2 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js @@ -15,14 +15,35 @@ function run_test() { writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); writeVersionFile("99.9"); + patchProps = {state: STATE_FAILED}; + patches = getLocalPatchString(patchProps); + let updateProps = {name: "Existing"}; + updates = getLocalUpdateString(updateProps, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false); + standardInit(); // Check that there is no activeUpdate first so the updates directory is // cleaned up by the UpdateManager before the remaining tests. Assert.ok(!gUpdateManager.activeUpdate, "there should not be an active update"); - Assert.equal(gUpdateManager.updateCount, 0, + let activeUpdateXML = getUpdatesXMLFile(true); + Assert.ok(!activeUpdateXML.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(activeUpdateXML.path)); + Assert.equal(gUpdateManager.updateCount, 2, "the update manager update count" + MSG_SHOULD_EQUAL); + let update = gUpdateManager.getUpdateAt(0); + Assert.equal(update.state, STATE_FAILED, + "the first update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.errorCode, ERR_UPDATE_STATE_NONE, + "the first update errorCode" + MSG_SHOULD_EQUAL); + Assert.equal(update.statusText, getString("statusFailed"), + "the first update statusText " + MSG_SHOULD_EQUAL); + update = gUpdateManager.getUpdateAt(1); + Assert.equal(update.state, STATE_FAILED, + "the second update state" + MSG_SHOULD_EQUAL); + Assert.equal(update.name, "Existing", + "the second update name" + MSG_SHOULD_EQUAL); let dir = getUpdatesDir(); dir.append(DIR_PATCH); diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js index 2b2f172d20d3..3cb4d86fceb3 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js @@ -18,7 +18,7 @@ function run_test() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, + Assert.equal(gUpdateManager.updateCount, 0, "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); Assert.equal(gUpdateManager.activeUpdate.state, STATE_DOWNLOADING, "the update manager activeUpdate state attribute" + diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js index f3f767394514..62a00b5a5183 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js @@ -31,6 +31,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + INVALID_CALLBACK_DIR_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js index 969f84f9df02..5357f43a8451 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js @@ -39,6 +39,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + INVALID_CALLBACK_PATH_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js index 70e03646a643..8099e6bfc573 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js @@ -41,6 +41,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js index 330578de64b0..0ac4fad1539b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js @@ -38,6 +38,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js index 8ddb34af0689..82d99a1d4cce 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_ERROR + : INVALID_APPLYTO_DIR_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js index c8ae3f0c650e..ea5752c98d64 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js @@ -37,6 +37,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js index e9b227657380..5c6369653c52 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR + : INVALID_APPLYTO_DIR_STAGED_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js index 87bbad4aa2b3..e1ba7834e59b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js index a550909b284e..aa27685ea1a1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js @@ -31,6 +31,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js index b9f793236a7a..e00e59c8cad1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js @@ -32,6 +32,7 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { + checkUpdateManager(STATE_AFTER_STAGE, true, STATE_AFTER_STAGE, 0, 0); removeUpdateInProgressLockFile(getAppBaseDir()); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js index aec663092261..51973e9bd27c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js @@ -51,14 +51,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js index fa18b4a9b881..a2663f3faa5e 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js @@ -35,17 +35,9 @@ function stageUpdateFinished() { checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getStageDirFile, true); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true); - // Change the active update to an older version to simulate installing a new - // version of the application while there is an update that has been staged. - let patchProps = {state: STATE_AFTER_STAGE}; - let patches = getLocalPatchString(patchProps); - let updateProps = {appVersion: "1.0"}; - let updates = getLocalUpdateString(updateProps, patches); - writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); // Change the version file to an older version to simulate installing a new // version of the application while there is an update that has been staged. - writeVersionFile("1.0"); - reloadUpdateManagerData(); + writeVersionFile("0.9"); // Try to switch the application to the staged application that was updated. runUpdateUsingApp(STATE_AFTER_STAGE); } @@ -54,15 +46,19 @@ function stageUpdateFinished() { * Called after the call to runUpdateUsingApp finishes. */ function runUpdateFinished() { + // Change the active update to an older version to simulate installing a new + // version of the application while there is an update that has been staged. + let patchProps = {state: STATE_AFTER_STAGE}; + let patches = getLocalPatchString(patchProps); + let updateProps = {appVersion: "0.9"}; + let updates = getLocalUpdateString(updateProps, patches); + getUpdatesXMLFile(true).remove(false); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); + reloadUpdateManagerData(); + standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + ERR_OLDER_VERSION_OR_SAME_BUILD, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile, !IS_MACOSX, false); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js index 5b9b08156d67..ca72310e3e2f 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js @@ -50,14 +50,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js index e76233fe6a74..ae61687ffa0c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js @@ -33,14 +33,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js index b1505d58eb2a..de2bade3b4bc 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js @@ -53,14 +53,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js index a1cc7d043601..650e0ad5a4f5 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js @@ -66,14 +66,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js index 93333cadebd5..5f516fa763ff 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js index 79e54c1825a4..3d1342490020 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js index b1f84715f0bb..955bdfc3e1d7 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js index 85e92d290e1c..2dbb75c9faa1 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js index 1212c9ba2ee1..ab7aca8a5113 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js index 960c96f7b896..73aeb7b35be8 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js @@ -32,14 +32,8 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_FAILURE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js index b39595f921d2..bfd20044969a 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js index 06d386ad63b0..e33bae3cc07d 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js index 89a2fff5ed07..ee32c967cb32 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js index ea85ddccc7a2..1788b2c026e0 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js index c5efaa8c0729..1a6563f55be3 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js index 4fdbadb5beb9..8c4605f2b872 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js index 4d12f4e42f3f..e09ea772296b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js index 5f64df34c997..6466b6938c7f 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js index d66f1f8117b1..d4dbd3d796d5 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marPIDPersistsSuccessComplete_win.js @@ -50,14 +50,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContains(ERR_PARENT_PID_PERSISTS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js index b83bafccc498..c364d99e9eaa 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js @@ -55,14 +55,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js index 39ea485cdcbb..ab40901b0bec 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js index a71bb8d49097..e009440ddc1b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js index 2cbe70ed8efd..8c3c7f4f854c 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js @@ -46,14 +46,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js index a9ce23420743..a6ad6601db1b 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js @@ -30,10 +30,8 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js index f7745f68f41a..ae13c75e7dcd 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js @@ -53,14 +53,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js index ef15326de8d6..3b814ed526a0 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js @@ -52,14 +52,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js index 1008e867fc44..2a6df6cfe1fe 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js @@ -36,14 +36,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js index 616390f55cc7..2e0232136b02 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js @@ -36,14 +36,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js index 86a2eb821ff4..7033b1e3f1e8 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js @@ -34,16 +34,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, VERSION_DOWNGRADE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + VERSION_DOWNGRADE_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(STATE_FAILED_VERSION_DOWNGRADE_ERROR); diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js index 6db906fbc570..fcee265f92da 100644 --- a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js +++ b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js @@ -34,16 +34,8 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, MAR_CHANNEL_MISMATCH_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + MAR_CHANNEL_MISMATCH_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js index 70e03646a643..8099e6bfc573 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js @@ -41,6 +41,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js index 330578de64b0..0ac4fad1539b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js @@ -38,6 +38,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_INSTALL_DIR_PATH_ERROR + : INVALID_INSTALL_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js index 8ddb34af0689..82d99a1d4cce 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_ERROR + : INVALID_APPLYTO_DIR_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js index bbef6c7bd5d6..24146aeeac3c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathSuffixFailureSvc.js @@ -31,6 +31,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js index c8ae3f0c650e..ea5752c98d64 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js @@ -37,6 +37,7 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js index e9b227657380..5c6369653c52 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR + : INVALID_APPLYTO_DIR_STAGED_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js index 87bbad4aa2b3..e1ba7834e59b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js @@ -32,6 +32,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js index a550909b284e..aa27685ea1a1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js @@ -31,6 +31,9 @@ function setupUpdaterTestFinished() { */ function runUpdateFinished() { standardInit(); + let errorCode = IS_SERVICE_TEST ? SERVICE_INVALID_WORKING_DIR_PATH_ERROR + : INVALID_WORKING_DIR_PATH_ERROR; + checkUpdateManager(STATE_NONE, false, STATE_FAILED, errorCode, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); waitForFilesInUse(); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js index b9f793236a7a..e00e59c8cad1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js @@ -32,6 +32,7 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { + checkUpdateManager(STATE_AFTER_STAGE, true, STATE_AFTER_STAGE, 0, 0); removeUpdateInProgressLockFile(getAppBaseDir()); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js index aec663092261..51973e9bd27c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js @@ -51,14 +51,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js index 5b9b08156d67..ca72310e3e2f 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js @@ -50,14 +50,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js index e76233fe6a74..ae61687ffa0c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js @@ -33,14 +33,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js index b1505d58eb2a..de2bade3b4bc 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js @@ -53,14 +53,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js index 93333cadebd5..5f516fa763ff 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js index 79e54c1825a4..3d1342490020 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js index b1f84715f0bb..955bdfc3e1d7 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js @@ -46,14 +46,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js index 85e92d290e1c..2dbb75c9faa1 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js index 1212c9ba2ee1..ab7aca8a5113 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js @@ -33,14 +33,7 @@ function runUpdateFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js index 960c96f7b896..73aeb7b35be8 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js @@ -32,14 +32,8 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_FAILURE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js index b39595f921d2..bfd20044969a 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js index 06d386ad63b0..e33bae3cc07d 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js @@ -54,10 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js index 89a2fff5ed07..ee32c967cb32 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js index ea85ddccc7a2..1788b2c026e0 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js index c5efaa8c0729..1a6563f55be3 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js index 4fdbadb5beb9..8c4605f2b872 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js @@ -40,14 +40,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js index 4d12f4e42f3f..e09ea772296b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_PENDING, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_PENDING, true, STATE_PENDING, WRITE_ERROR, 0); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_RENAME_FILE); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js index 5f64df34c997..6466b6938c7f 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusFile(), STATE_NONE, - "the status file failure code" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.updateCount, 2, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED, - "the update state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, READ_ERROR, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_UNABLE_OPEN_DEST); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js index b83bafccc498..c364d99e9eaa 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js @@ -55,14 +55,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js index 39ea485cdcbb..ab40901b0bec 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js @@ -54,14 +54,7 @@ function runUpdateFinished() { */ function waitForHelperExitFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_AFTER_RUNUPDATE, 0, 1); checkPostUpdateRunningFile(false); setTestFilesAndDirsForFailure(); checkFilesAfterUpdateFailure(getApplyDirFile); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js index a71bb8d49097..e009440ddc1b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js @@ -47,14 +47,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js index 2cbe70ed8efd..8c3c7f4f854c 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js @@ -46,14 +46,7 @@ function waitForHelperExitFinished() { */ function checkPostUpdateAppLogFinished() { standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContains(ERR_BACKUP_DISCARD); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js index a9ce23420743..a6ad6601db1b 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js @@ -30,10 +30,8 @@ function setupUpdaterTestFinished() { * Called after the call to stageUpdate finishes. */ function stageUpdateFinished() { - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE, - "the update errorCode" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_FAILED, + LOADSOURCE_ERROR_WRONG_SIZE, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateFailure(getApplyDirFile); checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js index f7745f68f41a..ae13c75e7dcd 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js @@ -53,14 +53,7 @@ function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); checkSymLinks(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js index ef15326de8d6..3b814ed526a0 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js @@ -52,14 +52,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile, false, true); checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js index 1008e867fc44..2a6df6cfe1fe 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js @@ -36,14 +36,7 @@ function runUpdateFinished() { function checkPostUpdateAppLogFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(true); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true); diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js index 616390f55cc7..2e0232136b02 100644 --- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js +++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js @@ -36,14 +36,7 @@ function setupUpdaterTestFinished() { function runUpdateFinished() { checkAppBundleModTime(); standardInit(); - Assert.equal(readStatusState(), STATE_NONE, - "the status file state" + MSG_SHOULD_EQUAL); - Assert.ok(!gUpdateManager.activeUpdate, - "the active update should not be defined"); - Assert.equal(gUpdateManager.updateCount, 1, - "the update manager updateCount attribute" + MSG_SHOULD_EQUAL); - Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED, - "the update state" + MSG_SHOULD_EQUAL); + checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1); checkPostUpdateRunningFile(false); checkFilesAfterUpdateSuccess(getApplyDirFile); checkUpdateLogContents(LOG_PARTIAL_SUCCESS); From 8d492fe637a24d03a5ce6f56bc5cae92c9c21a3a Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Thu, 31 Aug 2017 06:07:00 -0400 Subject: [PATCH 23/51] Bug 1395525 - Make all changes landed as part of tracker request tailing preferrable. r=dragana --- dom/base/nsContentUtils.cpp | 4 ++++ dom/base/nsContentUtils.h | 4 ++++ dom/fetch/FetchDriver.cpp | 4 +++- dom/script/ScriptLoader.cpp | 7 +++++- dom/xhr/XMLHttpRequestMainThread.cpp | 16 +++++++------ netwerk/base/nsChannelClassifier.cpp | 25 +++++++++++++------- netwerk/protocol/http/nsHttpChannel.cpp | 1 - toolkit/components/places/FaviconHelpers.cpp | 10 ++++---- 8 files changed, 48 insertions(+), 23 deletions(-) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index ac4263a1bcd3..cbeab1dfea27 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -304,6 +304,7 @@ bool nsContentUtils::sGetBoxQuadsEnabled = false; bool nsContentUtils::sSkipCursorMoveForSameValueSet = false; bool nsContentUtils::sRequestIdleCallbackEnabled = false; bool nsContentUtils::sLowerNetworkPriority = false; +bool nsContentUtils::sTailingEnabled = false; bool nsContentUtils::sShowInputPlaceholderOnFocus = true; bool nsContentUtils::sAutoFocusEnabled = true; #ifndef RELEASE_OR_BETA @@ -768,6 +769,9 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sLowerNetworkPriority, "privacy.trackingprotection.lower_network_priority", false); + Preferences::AddBoolVarCache(&sTailingEnabled, + "network.http.tailing.enabled", true); + Preferences::AddBoolVarCache(&sShowInputPlaceholderOnFocus, "dom.placeholder.show_on_focus", true); diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 57c52fb6db24..e52ebb461a70 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -3093,6 +3093,9 @@ public: // if we want to lower the priority of the channel. static bool IsLowerNetworkPriority() { return sLowerNetworkPriority; } + // Whether tracker tailing is turned on - "network.http.tailing.enabled". + static bool IsTailingEnabled() { return sTailingEnabled; } + // Check pref "dom.placeholder.show_on_focus" to see // if we want to show the placeholder inside input elements // when they have focus. @@ -3284,6 +3287,7 @@ private: static bool sSkipCursorMoveForSameValueSet; static bool sRequestIdleCallbackEnabled; static bool sLowerNetworkPriority; + static bool sTailingEnabled; static bool sShowInputPlaceholderOnFocus; static bool sAutoFocusEnabled; #ifndef RELEASE_OR_BETA diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 6bc6d5c95f4f..29e0a491c7bc 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -378,10 +378,12 @@ FetchDriver::HttpFetch() } } - if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) { + if (mIsTrackingFetch && nsContentUtils::IsTailingEnabled()) { cos->AddClassFlags(nsIClassOfService::Throttleable | nsIClassOfService::Tail); + } + if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) { nsCOMPtr p = do_QueryInterface(chan); if (p) { p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index de654faa6451..ff5823e78df9 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -1067,7 +1067,12 @@ ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) // synchronous head scripts block loading of most other non js/css // content such as images, Leader implicitely disallows tailing cos->AddClassFlags(nsIClassOfService::Leader); - } else if (defer && !async) { + } else if (defer && (!async || !nsContentUtils::IsTailingEnabled())) { + // Bug 1395525 and the !nsContentUtils::IsTailingEnabled() bit: + // We want to make sure that turing tailing off by the pref makes + // the browser behave exactly the same way as before landing + // the tailing patch, which has added the "&& !async" part. + // head/body deferred scripts are blocked by leaders but are not // allowed tailing because they block DOMContentLoaded cos->AddClassFlags(nsIClassOfService::TailForbidden); diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 4591fee2079a..e9048785644e 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -2621,13 +2621,15 @@ XMLHttpRequestMainThread::MaybeLowerChannelPriority() return; } - nsCOMPtr cos = do_QueryInterface(mChannel); - if (cos) { - // Adding TailAllowed to overrule the Unblocked flag, but to preserve - // the effect of Unblocked when tailing is off. - cos->AddClassFlags(nsIClassOfService::Throttleable | - nsIClassOfService::Tail | - nsIClassOfService::TailAllowed); + if (nsContentUtils::IsTailingEnabled()) { + nsCOMPtr cos = do_QueryInterface(mChannel); + if (cos) { + // Adding TailAllowed to overrule the Unblocked flag, but to preserve + // the effect of Unblocked when tailing is off. + cos->AddClassFlags(nsIClassOfService::Throttleable | + nsIClassOfService::Tail | + nsIClassOfService::TailAllowed); + } } nsCOMPtr p = do_QueryInterface(mChannel); diff --git a/netwerk/base/nsChannelClassifier.cpp b/netwerk/base/nsChannelClassifier.cpp index 9592a12e96d8..b3267caecc2d 100644 --- a/netwerk/base/nsChannelClassifier.cpp +++ b/netwerk/base/nsChannelClassifier.cpp @@ -210,16 +210,23 @@ LowerPriorityHelper(nsIChannel* aChannel) nsCOMPtr cos(do_QueryInterface(aChannel)); if (cos) { - uint32_t cosFlags = 0; - cos->GetClassFlags(&cosFlags); - isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart | - nsIClassOfService::Leader | - nsIClassOfService::Unblocked); + if (nsContentUtils::IsTailingEnabled()) { + uint32_t cosFlags = 0; + cos->GetClassFlags(&cosFlags); + isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart | + nsIClassOfService::Leader | + nsIClassOfService::Unblocked); + + // Requests not allowed to be tailed are usually those with higher + // prioritization. That overweights being a tracker: don't throttle + // them when not in background. + if (!(cosFlags & nsIClassOfService::TailForbidden)) { + cos->AddClassFlags(nsIClassOfService::Throttleable); + } + } else { + // Yes, we even don't want to evaluate the isBlockingResource when tailing is off + // see bug 1395525. - // Requests not allowed to be tailed are usually those with higher - // prioritization. That overweights being a tracker: don't throttle - // them when not in background. - if (!(cosFlags & nsIClassOfService::TailForbidden)) { cos->AddClassFlags(nsIClassOfService::Throttleable); } } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 78823a95295b..0a4981382d0f 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -7576,7 +7576,6 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st "We should not call OnStopRequest twice"); mListener->OnStopRequest(this, mListenerContext, status); mOnStopRequestCalled = true; - } RemoveAsNonTailRequest(); diff --git a/toolkit/components/places/FaviconHelpers.cpp b/toolkit/components/places/FaviconHelpers.cpp index 2adf9c41aa54..a9e186fd5ed1 100644 --- a/toolkit/components/places/FaviconHelpers.cpp +++ b/toolkit/components/places/FaviconHelpers.cpp @@ -594,10 +594,12 @@ AsyncFetchAndSetIconForPage::FetchFromNetwork() { priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST); } - nsCOMPtr cos = do_QueryInterface(channel); - if (cos) { - cos->AddClassFlags(nsIClassOfService::Tail | - nsIClassOfService::Throttleable); + if (nsContentUtils::IsTailingEnabled()) { + nsCOMPtr cos = do_QueryInterface(channel); + if (cos) { + cos->AddClassFlags(nsIClassOfService::Tail | + nsIClassOfService::Throttleable); + } } rv = channel->AsyncOpen2(this); From de414dfd84f91d2d3f1c1b578b5866cfe50849e6 Mon Sep 17 00:00:00 2001 From: Simon Lindholm Date: Thu, 31 Aug 2017 12:24:00 -0400 Subject: [PATCH 24/51] Bug 1388331 - Use more precise criteria for stopping auto-complete searches. r=mak --- .../test/performance/browser_urlbar_keyed_search_reflows.js | 2 +- .../test/performance/browser_urlbar_search_reflows.js | 4 ++-- toolkit/components/places/UnifiedComplete.js | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js index b4ef2c6ae9d9..48138e1833a6 100644 --- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js @@ -67,7 +67,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 1584, // This number should only ever go down - never up. + times: 1344, // This number should only ever go down - never up. }, { diff --git a/browser/base/content/test/performance/browser_urlbar_search_reflows.js b/browser/base/content/test/performance/browser_urlbar_search_reflows.js index 636379ccc4df..1b3f884d3bd4 100644 --- a/browser/base/content/test/performance/browser_urlbar_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_search_reflows.js @@ -67,7 +67,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 390, // This number should only ever go down - never up. + times: 330, // This number should only ever go down - never up. }, { @@ -119,7 +119,7 @@ const EXPECTED_REFLOWS_SECOND_OPEN = [ "_invalidate@chrome://global/content/bindings/autocomplete.xml", "invalidate@chrome://global/content/bindings/autocomplete.xml" ], - times: 444, // This number should only ever go down - never up. + times: 384, // This number should only ever go down - never up. }, // Bug 1384256 diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index 909a15b39e7a..0a4b2befd305 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -1089,8 +1089,9 @@ Search.prototype = { // If we do not have enough results, and our match type is // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more // results. + let count = this._counts[MATCHTYPE.GENERAL] + this._counts[MATCHTYPE.HEURISTIC]; if (this._matchBehavior == MATCH_BOUNDARY_ANYWHERE && - this._counts[MATCHTYPE.GENERAL] < Prefs.get("maxRichResults")) { + count < Prefs.get("maxRichResults")) { this._matchBehavior = MATCH_ANYWHERE; for (let [query, params] of [ this._adaptiveQuery, this._searchQuery ]) { @@ -1746,7 +1747,8 @@ Search.prototype = { this._addMatch(match); // If the search has been canceled by the user or by _addMatch, or we // fetched enough results, we can stop the underlying Sqlite query. - if (!this.pending || this._counts[MATCHTYPE.GENERAL] == Prefs.get("maxRichResults")) + let count = this._counts[MATCHTYPE.GENERAL] + this._counts[MATCHTYPE.HEURISTIC]; + if (!this.pending || count >= Prefs.get("maxRichResults")) cancel(); }, From d5fab2cf6dc9e2ad2a7c374c46127cb8d2652ca2 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 31 Aug 2017 15:38:49 -0400 Subject: [PATCH 25/51] Bug 1394989 - Disable xpcom/tests/unit/test_nsIProcess_stress.js for coverage builds. r=froydnj --- xpcom/tests/unit/xpcshell.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini index 7c6ec60265e8..bc984c6048c6 100644 --- a/xpcom/tests/unit/xpcshell.ini +++ b/xpcom/tests/unit/xpcshell.ini @@ -39,7 +39,7 @@ skip-if = os == "win" || os == "linux" # bug 582821, bug 1325609 # Bug 676998: test fails consistently on Android fail-if = os == "android" [test_nsIProcess_stress.js] -skip-if = os == "win" # bug 676412, test isn't needed on windows and runs really slowly +skip-if = os == "win" || coverage # bug 676412, test isn't needed on windows and runs really slowly, bug 1394989 [test_pipe.js] [test_process_directives.js] skip-if = os == "android" From 34f00ff865f35b48a71e7be69a3a7bbdffc5b9cc Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 31 Aug 2017 15:39:02 -0400 Subject: [PATCH 26/51] Bug 1336638 - Disable toolkit/components/alerts/test/test_principal.html on windows 7. r=mattn --- toolkit/components/alerts/test/mochitest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/alerts/test/mochitest.ini b/toolkit/components/alerts/test/mochitest.ini index 12e2a8704099..38c0e79ebf56 100644 --- a/toolkit/components/alerts/test/mochitest.ini +++ b/toolkit/components/alerts/test/mochitest.ini @@ -13,4 +13,4 @@ skip-if = toolkit == 'android' [test_image.html] [test_multiple_alerts.html] [test_principal.html] -skip-if = toolkit == 'android' +skip-if = toolkit == 'android' || (os == "win" && bits == 32) # Bug 1336638 From a55d72f6dad0c9c1226a37090184e641b55a94c3 Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Thu, 31 Aug 2017 11:31:28 -0600 Subject: [PATCH 27/51] Bug 1383501: Move a crash annotation so that it does not mask a previous annotation with the same key; r=handyman MozReview-Commit-ID: 9u7VTKDglrS --- ipc/mscom/ProxyStream.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp index 6bbf9b884161..a2b2336c82ed 100644 --- a/ipc/mscom/ProxyStream.cpp +++ b/ipc/mscom/ProxyStream.cpp @@ -127,6 +127,11 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, CrashReporter::AnnotateCrashReport( NS_LITERAL_CSTRING("CoUnmarshalInterfaceResult"), hrAsStr); AnnotateInterfaceRegistration(aIID); + if (!mUnmarshaledProxy) { + CrashReporter::AnnotateCrashReport(kCrashReportKey, + NS_LITERAL_CSTRING("!mUnmarshaledProxy")); + } + #if defined(ACCESSIBILITY) AnnotateClassRegistration(CLSID_AccessibleHandler); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtx"), @@ -254,13 +259,6 @@ ProxyStream::GetInterface(void** aOutInterface) return false; } -#if defined(MOZ_CRASHREPORTER) - if (!mUnmarshaledProxy) { - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamUnmarshalStatus"), - NS_LITERAL_CSTRING("!mUnmarshaledProxy")); - } -#endif // defined(MOZ_CRASHREPORTER) - *aOutInterface = mUnmarshaledProxy.release(); return true; } From 29412ecff375683339211692ee8108ef9ade03e9 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 13:16:07 -0700 Subject: [PATCH 28/51] Bug 1395711: Enable dump() by default in local builds. r=jryans MozReview-Commit-ID: 5tUaoLcT6ZS --HG-- extra : rebase_source : 334af3730f3bcac2b0b08f6164963b570ce41c86 --- modules/libpref/init/all.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 05c2c904a365..5655e455898b 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1051,10 +1051,13 @@ sticky_pref("devtools.debugger.prompt-connection", true); sticky_pref("devtools.chrome.enabled", false); // Disable remote debugging connections sticky_pref("devtools.debugger.remote-enabled", false); +// enable JS dump() function. +sticky_pref("browser.dom.window.dump.enabled", false); #else // In local builds, enable the browser toolbox by default sticky_pref("devtools.chrome.enabled", true); sticky_pref("devtools.debugger.remote-enabled", true); +sticky_pref("browser.dom.window.dump.enabled", true); #endif @@ -5177,9 +5180,6 @@ pref("layout.css.touch_action.enabled", true); // This only has an effect in DEBUG-builds. pref("layout.css.expensive-style-struct-assertions.enabled", false); -// enable JS dump() function. -pref("browser.dom.window.dump.enabled", false); - #if defined(MOZ_WIDGET_ANDROID) // Network Information API pref("dom.netinfo.enabled", true); From 0fa1835bc153f43cd68fbc364042ba91cfe3fa3e Mon Sep 17 00:00:00 2001 From: Dylan Roeh Date: Thu, 31 Aug 2017 15:10:38 -0500 Subject: [PATCH 29/51] Bug 1391181 - Reimplement the quaternion to Tait-Bryan conversion for device orientation events. r=snorp --- dom/system/nsDeviceSensors.cpp | 68 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/dom/system/nsDeviceSensors.cpp b/dom/system/nsDeviceSensors.cpp index 3faf6185e73f..59050f73f632 100644 --- a/dom/system/nsDeviceSensors.cpp +++ b/dom/system/nsDeviceSensors.cpp @@ -283,41 +283,53 @@ struct Orientation }; static Orientation -RotationVectorToOrientation(double aX, double aY, double aZ, double aW) -{ - static const double kFuzzyOne = 1.0 - 1e-6; - static const double kCircleRad = 2.0 * M_PI; +RotationVectorToOrientation(double aX, double aY, double aZ, double aW) { + double mat[9]; - Orientation orient = { 2.0 * std::atan2(aY, aW), - M_PI_2, - 0.0 }; + mat[0] = 1 - 2*aY*aY - 2*aZ*aZ; + mat[1] = 2*aX*aY - 2*aZ*aW; + mat[2] = 2*aX*aZ + 2*aY*aW; - const double sqX = aX * aX; - const double sqY = aY * aY; - const double sqZ = aZ * aZ; - const double sqW = aW * aW; - const double unitLength = sqX + sqY + sqZ + sqW; - const double xwyz = 2.0 * (aX * aW + aY * aZ) / unitLength; + mat[3] = 2*aX*aY + 2*aZ*aW; + mat[4] = 1 - 2*aX*aX - 2*aZ*aZ; + mat[5] = 2*aY*aZ - 2*aX*aW; - if (xwyz < -kFuzzyOne) { - orient.alpha *= -1.0; - orient.beta *= -1.0; - } else if (xwyz <= kFuzzyOne) { - const double gammaX = -sqX - sqY + sqZ + sqW; - const double gammaY = 2.0 * (aY * aW - aX * aZ); - const double alphaX = -sqX + sqY - sqZ + sqW; - const double alphaY = 2.0 * (aZ * aW - aX * aY); - const double fac = gammaX > 0 ? 1.0 : -1.0; + mat[6] = 2*aX*aZ - 2*aY*aW; + mat[7] = 2*aY*aZ + 2*aX*aW; + mat[8] = 1 - 2*aX*aX - 2*aY*aY; - orient.alpha = std::fmod(kCircleRad + std::atan2(fac * alphaY, fac * alphaX), - kCircleRad); - orient.beta = fac * std::asin(xwyz); - orient.gamma = std::atan2(fac * gammaY, fac * gammaX); - if (fac < 0.0) { - orient.beta = fmod(M_PI + orient.beta, M_PI); + Orientation orient; + + if (mat[8] > 0) { + orient.alpha = atan2(-mat[1], mat[4]); + orient.beta = asin(mat[7]); + orient.gamma = atan2(-mat[6], mat[8]); + } else if (mat[8] < 0) { + orient.alpha = atan2(mat[1], -mat[4]); + orient.beta = -asin(mat[7]); + orient.beta += (orient.beta >= 0) ? -M_PI : M_PI; + orient.gamma = atan2(mat[6], -mat[8]); + } else { + if (mat[6] > 0) { + orient.alpha = atan2(-mat[1], mat[4]); + orient.beta = asin(mat[7]); + orient.gamma = -M_PI_2; + } else if (mat[6] < 0) { + orient.alpha = atan2(mat[1], -mat[4]); + orient.beta = -asin(mat[7]); + orient.beta += (orient.beta >= 0) ? -M_PI : M_PI; + orient.gamma = -M_PI_2; + } else { + orient.alpha = atan2(mat[3], mat[0]); + orient.beta = (mat[7] > 0) ? M_PI_2 : -M_PI_2; + orient.gamma = 0; } } + if (orient.alpha < 0) { + orient.alpha += 2*M_PI; + } + return Orientation::RadToDeg(orient); } From f4d5c234eb68612629596b5b7936f633e0c146c0 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 1 Sep 2017 00:10:51 +0200 Subject: [PATCH 30/51] Bug 815077 - Make unicode ETHIOPIC WORDSPACE count as a space character: remove test failure expecation. r=wpt-expectation-update --- testing/web-platform/meta/MANIFEST.json | 18 +++++++++--------- .../css3-text-line-break-baspglwj-022.html.ini | 5 ----- 2 files changed, 9 insertions(+), 14 deletions(-) delete mode 100644 testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 0807700484ab..9911059153bb 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -436317,11 +436317,11 @@ "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden-meta.tentative.html": [ - "15b9eee7b8a6c646ef704c4cc43dc328503383cd", + "a1de5c33148f7e202d13bc5c25d115a29ac81b3b", "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden.tentative.html": [ - "418cd0188ee589563735316ba619dea547f1f467", + "c3265d6ead066201f712aa06beac162f365dd058", "testharness" ], "content-security-policy/nonce-hiding/script-nonces-hidden.tentative.html.headers": [ @@ -436329,11 +436329,11 @@ "support" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.tentative.html": [ - "fdb549d9c1c82890dc5443a69a574633a90c37a2", + "9ea075f30ba75a712f1fd7c05d413caaca0af67a", "testharness" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden.tentative.html": [ - "da72488109e91f1fa0a3f4b5ada3f078562bc8e0", + "7345b65ebfdecad898ce1d3acd6ee5353163a38d", "testharness" ], "content-security-policy/nonce-hiding/svgscript-nonces-hidden.tentative.html.headers": [ @@ -579073,7 +579073,7 @@ "testharness" ], "fetch/api/abort/general.js": [ - "980a50cf0be82bc7ffb2e7024739173273ae1263", + "74ca971f67c7f06ad9555a4cd877753da5718452", "support" ], "fetch/api/abort/serviceworker-intercepted.https.html": [ @@ -579333,7 +579333,7 @@ "testharness" ], "fetch/api/headers/headers-record.html": [ - "84652a79c8ff64826faff6151cf4a48450e706d1", + "25ab8a6a8988b529c7023ed50a62f0ac46e20b8e", "testharness" ], "fetch/api/headers/headers-structure.html": [ @@ -584109,7 +584109,7 @@ "support" ], "html/dom/elements-metadata.js": [ - "0583e1a4a95f902758b3fb30ca451ff06f4c237e", + "edf5b655f9a79d4a3599b5332fde6b211f3ce8eb", "support" ], "html/dom/elements-misc.js": [ @@ -619661,11 +619661,11 @@ "support" ], "service-workers/service-worker/resources/update-max-aged-worker-imported-script.py": [ - "2124eef9484a7ba1d38724b38d944bf1806fc579", + "6a70122d9af961de27dd9958f86a92c0d7ff7f7e", "support" ], "service-workers/service-worker/resources/update-max-aged-worker.py": [ - "6076f9ae7dd0c2e729ac9d3872daed0379555df8", + "dc5784aa563f93a2ebd8c57acddefe8c3dcc1c3f", "support" ], "service-workers/service-worker/resources/update-nocookie-worker.py": [ diff --git a/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini b/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini deleted file mode 100644 index fcd96597dfbc..000000000000 --- a/testing/web-platform/meta/css/css-text-3/i18n/css3-text-line-break-baspglwj-022.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-text-line-break-baspglwj-022.html] - type: testharness - [ ] - expected: FAIL - From 78609e834dcc8d15c7bb06ce2394e862c80594be Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 15:09:41 -0700 Subject: [PATCH 31/51] Bug 1393621: Part 1 - Don't load ext-contextualIdentities at startup without permissions. r=zombie MozReview-Commit-ID: AiIYAXSRrii --HG-- extra : rebase_source : 5004ef9b90391be2ae06a1b610b8922e2a091ffa --- toolkit/components/extensions/ExtensionCommon.jsm | 8 ++++++++ toolkit/components/extensions/ExtensionParent.jsm | 4 +++- toolkit/components/extensions/ext-toolkit.json | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/toolkit/components/extensions/ExtensionCommon.jsm b/toolkit/components/extensions/ExtensionCommon.jsm index a68987658a93..edc1de9d0c45 100644 --- a/toolkit/components/extensions/ExtensionCommon.jsm +++ b/toolkit/components/extensions/ExtensionCommon.jsm @@ -917,6 +917,10 @@ class CanOfAPIs { * its appropriate event handler method to be called. Currently * only accepts "startup". * + * @property {Array} permissions + * An optional list of permissions, any of which must be present + * in order for the module to load. + * * @property {Array>} paths * A list of paths from the root API object which, when accessed, * will cause the API module to be instantiated and injected. @@ -1219,6 +1223,10 @@ class SchemaAPIManager extends EventEmitter { _checkGetAPI(name, extension, scope = null) { let module = this.modules.get(name); + if (module.permissions && !module.permissions.some(perm => extension.hasPermission(perm))) { + return false; + } + if (!scope) { return true; } diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index 6a2b0f20eec2..04c50c032bec 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -80,7 +80,9 @@ let apiManager = new class extends SchemaAPIManager { let promises = []; for (let apiName of this.eventModules.get("startup")) { promises.push(this.asyncGetAPI(apiName, extension).then(api => { - api.onStartup(extension.startupReason); + if (api) { + api.onStartup(extension.startupReason); + } })); } diff --git a/toolkit/components/extensions/ext-toolkit.json b/toolkit/components/extensions/ext-toolkit.json index 63af22e0d395..eac5e906a16f 100644 --- a/toolkit/components/extensions/ext-toolkit.json +++ b/toolkit/components/extensions/ext-toolkit.json @@ -29,6 +29,7 @@ "schema": "chrome://extensions/content/schemas/contextual_identities.json", "scopes": ["addon_parent"], "events": ["startup"], + "permissions": ["contextualIdentities"], "paths": [ ["contextualIdentities"] ] From a15a2a5be1b5e9a993f5b7f10416591bc7f124cd Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 15:14:33 -0700 Subject: [PATCH 32/51] Bug 1393621: Part 2 - Add test for API modules loaded at startup. r=zombie MozReview-Commit-ID: 6gyDqsaImmX --HG-- extra : rebase_source : 17a7a3c83ab0939371baf98b484b092e1681b34a --- .../extensions/test/xpcshell/head.js | 27 +++++++--------- .../test/xpcshell/test_ext_startup_perf.js | 32 +++++++++++++++++++ .../test/xpcshell/xpcshell-common.ini | 1 + 3 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js diff --git a/toolkit/components/extensions/test/xpcshell/head.js b/toolkit/components/extensions/test/xpcshell/head.js index 96bf15cc9476..7efab80051b6 100644 --- a/toolkit/components/extensions/test/xpcshell/head.js +++ b/toolkit/components/extensions/test/xpcshell/head.js @@ -10,22 +10,17 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Timer.jsm"); Components.utils.import("resource://testing-common/AddonTestUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContentTask", - "resource://testing-common/ContentTask.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Extension", - "resource://gre/modules/Extension.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData", - "resource://gre/modules/Extension.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestUtils", - "resource://testing-common/ExtensionXPCShellUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "HttpServer", - "resource://testing-common/httpd.js"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Schemas", - "resource://gre/modules/Schemas.jsm"); +XPCOMUtils.defineLazyModuleGetters(this, { + ContentTask: "resource://testing-common/ContentTask.jsm", + Extension: "resource://gre/modules/Extension.jsm", + ExtensionData: "resource://gre/modules/Extension.jsm", + ExtensionParent: "resource://gre/modules/ExtensionParent.jsm", + ExtensionTestUtils: "resource://testing-common/ExtensionXPCShellUtils.jsm", + FileUtils: "resource://gre/modules/FileUtils.jsm", + HttpServer: "resource://testing-common/httpd.js", + NetUtil: "resource://gre/modules/NetUtil.jsm", + Schemas: "resource://gre/modules/Schemas.jsm", +}); Services.prefs.setBoolPref("extensions.webextensions.remote", false); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js new file mode 100644 index 000000000000..386191422c64 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js @@ -0,0 +1,32 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const STARTUP_APIS = [ + "backgroundPage", +]; + +// Tests that only the minimal set of API scripts are loaded at startup +// for a simple extension. +add_task(async function test_loaded_api_scripts() { + await ExtensionTestUtils.startAddonManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + background() {}, + manifest: {}, + }); + + await extension.startup(); + + const {apiManager} = ExtensionParent; + + const loadedAPIs = Array.from(apiManager.modules.values()) + .filter(m => m.loaded || m.asyncLoaded) + .map(m => m.namespaceName); + + deepEqual(loadedAPIs.sort(), STARTUP_APIS, + "No extra APIs should be loaded at startup for a simple extension"); + + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini index a1c2f6a98ce1..441d057bc4f4 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -58,6 +58,7 @@ skip-if = true # This test no longer tests what it is meant to test. [test_ext_simple.js] [test_ext_startup_cache.js] skip-if = os == "android" +[test_ext_startup_perf.js] [test_ext_storage.js] [test_ext_storage_sync.js] head = head.js head_sync.js From 0c9a3b08ee2d5f0641b7ddfa733267386c822a32 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 15:12:28 -0700 Subject: [PATCH 33/51] Bug 1393621: Part 3 - Add test for framework JSMs loaded at startup. r=zombie MozReview-Commit-ID: DvVO9bzwyXf --HG-- extra : rebase_source : f04d8f1c08508acef915a493e00fbd7f5020dce8 --- .../test/xpcshell/test_ext_startup_perf.js | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js index 386191422c64..29a9e4ff4dae 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js @@ -6,9 +6,26 @@ const STARTUP_APIS = [ "backgroundPage", ]; -// Tests that only the minimal set of API scripts are loaded at startup -// for a simple extension. -add_task(async function test_loaded_api_scripts() { +const STARTUP_MODULES = [ + "resource://gre/modules/Extension.jsm", + "resource://gre/modules/ExtensionCommon.jsm", + "resource://gre/modules/ExtensionParent.jsm", + // FIXME: This is only loaded at startup for new extension installs. + // Otherwise the data comes from the startup cache. We should test for + // this. + "resource://gre/modules/ExtensionPermissions.jsm", + "resource://gre/modules/ExtensionUtils.jsm", +]; + +if (!Services.prefs.getBoolPref("extensions.webextensions.remote")) { + STARTUP_MODULES.push( + "resource://gre/modules/ExtensionChild.jsm", + "resource://gre/modules/ExtensionPageChild.jsm"); +} + +// Tests that only the minimal set of API scripts and modules are loaded at +// startup for a simple extension. +add_task(async function test_loaded_scripts() { await ExtensionTestUtils.startAddonManager(); let extension = ExtensionTestUtils.loadExtension({ @@ -28,5 +45,13 @@ add_task(async function test_loaded_api_scripts() { deepEqual(loadedAPIs.sort(), STARTUP_APIS, "No extra APIs should be loaded at startup for a simple extension"); + + const loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader); + let loadedModules = loader.loadedModules() + .filter(url => url.startsWith("resource://gre/modules/Extension")); + + deepEqual(loadedModules.sort(), STARTUP_MODULES.sort(), + "No extra extension modules should be loaded at startup for a simple extension"); + await extension.unload(); }); From 48761699cfc78f9cfe544635c93e819872b6b30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Fri, 1 Sep 2017 00:42:31 +0200 Subject: [PATCH 34/51] Bug 1391704 - Avoid flickering while moving tabs across windows, r=mconley. --- browser/base/content/browser.js | 54 ++++++++----------- browser/base/content/tabbrowser.xml | 49 +++++++++++++++-- .../customizableui/content/panelUI.js | 10 ++-- .../test/browser/browser_exit_button.js | 3 +- dom/base/test/browser_bug1058164.js | 2 +- 5 files changed, 74 insertions(+), 44 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 9989076b0862..b18ff16e934f 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1379,6 +1379,25 @@ var gBrowserInit = { gRemoteControl.updateVisualCue(Marionette.running); + // If we are given a tab to swap in, take care of it before first paint to + // avoid an about:blank flash. + let tabToOpen = window.arguments && window.arguments[0]; + if (tabToOpen instanceof XULElement) { + // Clear the reference to the tab from the arguments array. + window.arguments[0] = null; + + // Stop the about:blank load + gBrowser.stop(); + // make sure it has a docshell + gBrowser.docShell; + + try { + gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); + } catch (e) { + Cu.reportError(e); + } + } + // Wait until chrome is painted before executing code not critical to making the window visible this._boundDelayedStartup = this._delayedStartup.bind(this); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); @@ -1605,6 +1624,8 @@ var gBrowserInit = { return; } + // We don't check if uriToLoad is a XULElement because this case has + // already been handled before first paint, and the argument cleared. if (uriToLoad instanceof Ci.nsIArray) { let count = uriToLoad.length; let specs = []; @@ -1622,39 +1643,6 @@ var gBrowserInit = { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); } catch (e) {} - } else if (uriToLoad instanceof XULElement) { - // swap the given tab with the default about:blank tab and then close - // the original tab in the other window. - let tabToOpen = uriToLoad; - - // If this tab was passed as a window argument, clear the - // reference to it from the arguments array. - if (window.arguments[0] == tabToOpen) { - window.arguments[0] = null; - } - - // Stop the about:blank load - gBrowser.stop(); - // make sure it has a docshell - gBrowser.docShell; - - // We must set usercontextid before updateBrowserRemoteness() - // so that the newly created remote tab child has correct usercontextid - if (tabToOpen.hasAttribute("usercontextid")) { - let usercontextid = tabToOpen.getAttribute("usercontextid"); - gBrowser.selectedBrowser.setAttribute("usercontextid", usercontextid); - } - - try { - // Make sure selectedBrowser has the same remote settings as the one - // we are swapping in. - gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, - tabToOpen.linkedBrowser.isRemoteBrowser, - { remoteType: tabToOpen.linkedBrowser.remoteType }); - gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); - } catch (e) { - Cu.reportError(e); - } } else if (window.arguments.length >= 3) { // window.arguments[2]: referrer (nsIURI | string) // [3]: postData (nsIInputStream) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index c1558b7381a1..e136224ec89b 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3009,6 +3009,16 @@ newTab = true; } + aTab._endRemoveArgs = [closeWindow, newTab]; + + // swapBrowsersAndCloseOther will take care of closing the window without animation. + if (closeWindow && aAdoptedByTab) { + // Remove the tab's filter to avoid leaking. + if (aTab.linkedPanel) { + this._tabFilters.delete(aTab); + } + return true; + } if (!aTab._fullyOpen) { // If the opening tab animation hasn't finished before we start closing the @@ -3079,7 +3089,6 @@ tab.owner = null; } - aTab._endRemoveArgs = [closeWindow, newTab]; return true; ]]> @@ -3297,6 +3306,25 @@ if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true)) return; + // If this is the last tab of the window, hide the window + // immediately without animation before the docshell swap, to avoid + // about:blank being painted. + let [closeWindow] = aOtherTab._endRemoveArgs; + if (closeWindow) { + let win = aOtherTab.ownerGlobal; + let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + dwu.suppressAnimation(true); + // Only suppressing window animations isn't enough to avoid + // an empty content area being painted. + let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIDocShellTreeItem) + .treeOwner + .QueryInterface(Ci.nsIBaseWindow); + baseWin.visibility = false; + } + let modifiedAttrs = []; if (aOtherTab.hasAttribute("muted")) { aOurTab.setAttribute("muted", "true"); @@ -3363,7 +3391,11 @@ } // Finish tearing down the tab that's going away. - remoteBrowser._endRemoveTab(aOtherTab); + if (closeWindow) { + aOtherTab.ownerGlobal.close(); + } else { + remoteBrowser._endRemoveTab(aOtherTab); + } this.setTabTitle(aOurTab); @@ -3729,6 +3761,14 @@ for (var name in aOptions) options += "," + name + "=" + aOptions[name]; + // Play the tab closing animation to give immediate feedback while + // waiting for the new window to appear. + // content area when the docshells are swapped. + if (this.animationsEnabled) { + aTab.style.maxWidth = ""; // ensure that fade-out transition happens + aTab.removeAttribute("fadein"); + } + // tell a new window to take the "dropped" tab return window.openDialog(getBrowserURL(), "_blank", options, aTab); ]]> @@ -3851,7 +3891,8 @@ let linkedBrowser = aTab.linkedBrowser; let params = { eventDetail: { adoptedTab: aTab }, preferredRemoteType: linkedBrowser.remoteType, - sameProcessAsFrameLoader: linkedBrowser.frameLoader }; + sameProcessAsFrameLoader: linkedBrowser.frameLoader, + skipAnimation: true }; if (aTab.hasAttribute("usercontextid")) { // new tab must have the same usercontextid as the old one params.userContextId = aTab.getAttribute("usercontextid"); @@ -7407,7 +7448,7 @@ window.moveTo(left, top); window.focus(); } else { - let props = { screenX: left, screenY: top }; + let props = { screenX: left, screenY: top, suppressanimation: 1 }; if (AppConstants.platform != "win") { props.outerWidth = winWidth; props.outerHeight = winHeight; diff --git a/browser/components/customizableui/content/panelUI.js b/browser/components/customizableui/content/panelUI.js index ed22bf402ab8..20a7255f975f 100644 --- a/browser/components/customizableui/content/panelUI.js +++ b/browser/components/customizableui/content/panelUI.js @@ -318,15 +318,15 @@ const PanelUI = { * * @return a Promise that resolves once the panel is ready to roll. */ - ensureReady() { - if (this._readyPromise) { - return this._readyPromise; + async ensureReady() { + if (this._isReady) { + return; } + + await window.delayedStartupPromise; this._ensureEventListenersAdded(); this.panel.hidden = false; - this._readyPromise = Promise.resolve(); this._isReady = true; - return this._readyPromise; }, /** diff --git a/devtools/client/responsive.html/test/browser/browser_exit_button.js b/devtools/client/responsive.html/test/browser/browser_exit_button.js index 62e652274826..d2a7e6c332f8 100644 --- a/devtools/client/responsive.html/test/browser/browser_exit_button.js +++ b/devtools/client/responsive.html/test/browser/browser_exit_button.js @@ -26,8 +26,9 @@ add_task(function* () { // Detach the tab with RDM open. let newWindow = gBrowser.replaceTabWithWindow(tab); - // Waiting the tab is detached. + // Wait until the tab is detached and the new window is fully initialized. yield waitTabIsDetached; + yield newWindow.delayedStartupPromise; // Get the new tab instance. tab = newWindow.gBrowser.tabs[0]; diff --git a/dom/base/test/browser_bug1058164.js b/dom/base/test/browser_bug1058164.js index 54f2740b42b3..472b4ac3f1ce 100644 --- a/dom/base/test/browser_bug1058164.js +++ b/dom/base/test/browser_bug1058164.js @@ -69,7 +69,7 @@ add_task(async function test_swap_frameloader_pagevisibility_events() { // We have to wait for the window to load so we can get the selected browser // to listen to. - await BrowserTestUtils.waitForEvent(newWindow, "load"); + await BrowserTestUtils.waitForEvent(newWindow, "DOMContentLoaded"); let newWindowBrowser = newWindow.gBrowser.selectedBrowser; // Wait for the expected pagehide and pageshow events on the initial browser From 5bd0082344c364398d6fabdf23bedebbf9b68e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Fri, 1 Sep 2017 00:42:44 +0200 Subject: [PATCH 35/51] Bug 1394235 - If a browser action icon is rectangular, the height of the icon needs to be set, r=kmag. --- browser/base/content/browser.css | 1 + devtools/client/aboutdebugging/aboutdebugging.css | 1 + 2 files changed, 2 insertions(+) diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index df287198e368..afce4b854577 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -327,6 +327,7 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"] } .webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon { + height: 16px; width: 16px; } diff --git a/devtools/client/aboutdebugging/aboutdebugging.css b/devtools/client/aboutdebugging/aboutdebugging.css index 038d42188752..5c28bf7d8cea 100644 --- a/devtools/client/aboutdebugging/aboutdebugging.css +++ b/devtools/client/aboutdebugging/aboutdebugging.css @@ -63,6 +63,7 @@ button { .target-icon { height: 24px; + width: 24px; margin-inline-end: 5px; } From bbe9f20f8413c3ac266034196bca8563e5dd29e4 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 15:51:21 -0700 Subject: [PATCH 36/51] Bug 1356376: Skip test_ext_i18n.js on Windows debug for frequent intermittent failures. r=me,test-only MozReview-Commit-ID: 9bIpo3XxXnk --HG-- extra : rebase_source : 353770064a511f36910e2c0f9c91967ee15d0d02 --- .../components/extensions/test/xpcshell/xpcshell-content.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini index de8939274a80..e34ec0717691 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini @@ -1,5 +1,5 @@ [test_ext_i18n.js] -skip-if = os == "android" +skip-if = os == "android" || (os == "win" && debug) [test_ext_i18n_css.js] [test_ext_contentscript.js] [test_ext_contentscript_xrays.js] From 9da40f63656ea27145cb62d126dd475538e868ef Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 31 Aug 2017 15:59:13 -0700 Subject: [PATCH 37/51] Bug 1277338 - Part 7: Export mozglue when JS_STANDALONE && !jemalloc; r=glandium,sfink This makes sure that: * We don't define `MOZ_GLUE_IN_PROGRAM` so that everything in mozglue gets defined. * `MFBT_API`'s symbol export rules match `JS_PUBLIC_API` and `EXPORT_JS_API`. * We add mozglue to SpiderMonkey's `USE_LIBS` when jemalloc is disabled. --- config/check_vanilla_allocations.py | 7 +++++- js/moz.configure | 5 ++-- js/src/build.rs | 5 ++++ js/src/devtools/automation/autospider.py | 12 +++++++++ js/src/old-configure.in | 22 ++++++++++------- mfbt/Types.h | 31 ++++++++++++++---------- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/config/check_vanilla_allocations.py b/config/check_vanilla_allocations.py index 2a3be879ec89..8c8efd545fdf 100644 --- a/config/check_vanilla_allocations.py +++ b/config/check_vanilla_allocations.py @@ -128,6 +128,12 @@ def main(): continue filename = m.group(1) + + # mozalloc contains calls to memalign. These are ok, so we whitelist + # them. + if "mozalloc" in filename: + continue + fn = m.group(2) if filename == 'jsutil.o': jsutil_cpp.add(fn) @@ -188,4 +194,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/js/moz.configure b/js/moz.configure index 4a8baf4d3342..5505fd1b7d42 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -22,12 +22,11 @@ option(env='JS_STANDALONE', default=building_js, def js_standalone(value): if value: return True - set_config('JS_STANDALONE', js_standalone) +set_define('JS_STANDALONE', js_standalone) add_old_configure_assignment('JS_STANDALONE', js_standalone) - js_option('--disable-js-shell', default=building_js, - help='Do not build the JS shell') + help='Do not build the JS shell') @depends('--disable-js-shell') def js_disable_shell(value): diff --git a/js/src/build.rs b/js/src/build.rs index bf23fc0461a7..97b57588265f 100644 --- a/js/src/build.rs +++ b/js/src/build.rs @@ -25,7 +25,12 @@ fn main() { let python = env::var("PYTHON").unwrap_or("python2.7".into()); let mut cmd = Command::new(&python); cmd.args(&["./devtools/automation/autospider.py", + // Only build SpiderMonkey, don't run all the tests. "--build-only", + // Disable Mozilla's jemalloc; Rust has its own jemalloc that we + // can swap in instead and everything using a single malloc is + // good. + "--no-jemalloc", "--objdir", &out_dir, variant]) .env("SOURCE", &js_src) diff --git a/js/src/devtools/automation/autospider.py b/js/src/devtools/automation/autospider.py index b5c3695d8ca3..f417f55f037c 100755 --- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -61,6 +61,14 @@ group.add_argument('--no-debug', action='store_false', dest='debug', help='generate a non-debug build. Overrides variant setting.') group.set_defaults(debug=None) +group = parser.add_mutually_exclusive_group() +group.add_argument('--jemalloc', action='store_true', + dest='jemalloc', + help='use mozilla\'s jemalloc instead of the default allocator') +group.add_argument('--no-jemalloc', action='store_false', + dest='jemalloc', + help='use the default allocator instead of mozilla\'s jemalloc') +group.set_defaults(jemalloc=None) parser.add_argument('--run-tests', '--tests', type=str, metavar='TESTSUITE', default='', help="comma-separated set of test suites to add to the variant's default set") @@ -176,6 +184,10 @@ if opt is None: if opt is not None: CONFIGURE_ARGS += (" --enable-debug" if opt else " --disable-debug") +opt = args.jemalloc +if opt is not None: + CONFIGURE_ARGS += (" --enable-jemalloc" if opt else " --disable-jemalloc") + # Any jobs that wish to produce additional output can save them into the upload # directory if there is such a thing, falling back to OBJDIR. env.setdefault('MOZ_UPLOAD_DIR', OBJDIR) diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 2aef42ccfa27..b270a8c2409d 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -1495,16 +1495,20 @@ dnl ======================================================== dnl = Enable jemalloc dnl ======================================================== -case "${OS_TARGET}" in -Android|WINNT|Darwin) +if test "$JS_STANDALONE" -a -z "$MOZ_MEMORY"; then MOZ_GLUE_IN_PROGRAM= - ;; -*) - dnl On !Android !Windows !OSX, we only want to link executables against mozglue - MOZ_GLUE_IN_PROGRAM=1 - AC_DEFINE(MOZ_GLUE_IN_PROGRAM) - ;; -esac +else + case "${OS_TARGET}" in + Android|WINNT|Darwin) + MOZ_GLUE_IN_PROGRAM= + ;; + *) + dnl On !Android !Windows !OSX, we only want to link executables against mozglue + MOZ_GLUE_IN_PROGRAM=1 + AC_DEFINE(MOZ_GLUE_IN_PROGRAM) + ;; + esac +fi if test "$MOZ_MEMORY"; then dnl The generic feature tests that determine how to compute ncpus are long and diff --git a/mfbt/Types.h b/mfbt/Types.h index e7e18abb2746..30f4ea3d1f7c 100644 --- a/mfbt/Types.h +++ b/mfbt/Types.h @@ -78,23 +78,28 @@ * export mfbt declarations when building mfbt, and they expose import mfbt * declarations when using mfbt. */ -#if defined(IMPL_MFBT) +#if defined(IMPL_MFBT) || (defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && (defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API))) # define MFBT_API MOZ_EXPORT # define MFBT_DATA MOZ_EXPORT #else - /* - * On linux mozglue is linked in the program and we link libxul.so with - * -z,defs. Normally that causes the linker to reject undefined references in - * libxul.so, but as a loophole it allows undefined references to weak - * symbols. We add the weak attribute to the import version of the MFBT API - * macros to exploit this. - */ -# if defined(MOZ_GLUE_IN_PROGRAM) -# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API -# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA +# if defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && defined(STATIC_JS_API) +# define MFBT_API +# define MFBT_DATA # else -# define MFBT_API MOZ_IMPORT_API -# define MFBT_DATA MOZ_IMPORT_DATA + /* + * On linux mozglue is linked in the program and we link libxul.so with + * -z,defs. Normally that causes the linker to reject undefined references in + * libxul.so, but as a loophole it allows undefined references to weak + * symbols. We add the weak attribute to the import version of the MFBT API + * macros to exploit this. + */ +# if defined(MOZ_GLUE_IN_PROGRAM) +# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API +# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA +# else +# define MFBT_API MOZ_IMPORT_API +# define MFBT_DATA MOZ_IMPORT_DATA +# endif # endif #endif From 7da6a5caf2c8d958deb4d110ed77f933a8a52ef5 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 31 Aug 2017 19:39:23 -0400 Subject: [PATCH 38/51] Backed out changeset 71d4452dd938 (bug 1341569) for regressing speedometer-misc-Angular2-TypeScript-TodoMVC-CompletingAllItems-sync. --- .../formautofill/FormAutofillContent.jsm | 8 ++-- .../formautofill/FormAutofillHandler.jsm | 45 ------------------- .../formautofill/FormAutofillParent.jsm | 25 ++--------- toolkit/components/telemetry/Histograms.json | 11 ----- 4 files changed, 6 insertions(+), 83 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillContent.jsm b/browser/extensions/formautofill/FormAutofillContent.jsm index 87f1e3861be7..8ffec2550110 100644 --- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -372,12 +372,10 @@ var FormAutofillContent = { * * @param {Object} profile Submitted form's address/creditcard guid and record. * @param {Object} domWin Current content window. - * @param {int} timeStartedFillingMS Time of form filling started. */ - _onFormSubmit(profile, domWin, timeStartedFillingMS) { + _onFormSubmit(profile, domWin) { let mm = this._messageManagerFromWindow(domWin); - mm.sendAsyncMessage("FormAutofill:OnFormSubmit", - {profile, timeStartedFillingMS}); + mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile); }, /** @@ -409,7 +407,7 @@ var FormAutofillContent = { return true; } - this._onFormSubmit(records, domWin, handler.timeStartedFillingMS); + this._onFormSubmit(records, domWin); return true; }, diff --git a/browser/extensions/formautofill/FormAutofillHandler.jsm b/browser/extensions/formautofill/FormAutofillHandler.jsm index e1233c989a8d..935fbb941d32 100644 --- a/browser/extensions/formautofill/FormAutofillHandler.jsm +++ b/browser/extensions/formautofill/FormAutofillHandler.jsm @@ -114,11 +114,6 @@ FormAutofillHandler.prototype = { return this._formFieldCount != this.form.elements.length; }, - /** - * Time in milliseconds since epoch when a user started filling in the form. - */ - timeStartedFillingMS: null, - /** * Set fieldDetails from the form about fields that can be autofilled. * @@ -132,7 +127,6 @@ FormAutofillHandler.prototype = { this._formFieldCount = this.form.elements.length; let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates); this.fieldDetails = fieldDetails ? fieldDetails : []; - this.form.rootElement.addEventListener("input", this); log.debug("Collected details on", this.fieldDetails.length, "fields"); this.address.fieldDetails = this.fieldDetails.filter( @@ -588,43 +582,4 @@ FormAutofillHandler.prototype = { Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth}); }); }, - - /** - * Find the fieldDetail by HTML element (assume all details were collected in collectFormFields). - * - * @param {HTMLElement} element - * - * @returns {Object|null} - * Return fieldDetail if fieldDetail's element ref could match the target. - * (or return null if the element could not match any fieldDetail). - */ - getFieldDetailsForElement(element) { - for (let detail of this.fieldDetails) { - if (detail.elementWeakRef.get() == element) { - return detail; - } - } - return null; - }, - - handleEvent(event) { - switch (event.type) { - case "input": - if (!event.isTrusted) { - return; - } - - if (!FormAutofillUtils.isFieldEligibleForAutofill(event.target)) { - return; - } - - if (!this.getFieldDetailsForElement(event.target)) { - return; - } - - this.form.rootElement.removeEventListener("input", this); - this.timeStartedFillingMS = Date.now(); - break; - } - }, }; diff --git a/browser/extensions/formautofill/FormAutofillParent.jsm b/browser/extensions/formautofill/FormAutofillParent.jsm index 2d00e99a8afd..8da6ab336e75 100644 --- a/browser/extensions/formautofill/FormAutofillParent.jsm +++ b/browser/extensions/formautofill/FormAutofillParent.jsm @@ -355,7 +355,7 @@ FormAutofillParent.prototype = { this._updateStatus(); }, - _onAddressSubmit(address, target, timeStartedFillingMS) { + _onAddressSubmit(address, target) { if (address.guid) { // Avoid updating the fields that users don't modify. let originalAddress = this.profileStorage.addresses.get(address.guid); @@ -366,8 +366,6 @@ FormAutofillParent.prototype = { } if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) { - this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS); - FormAutofillDoorhanger.show(target, "update").then((state) => { let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record); switch (state) { @@ -391,7 +389,6 @@ FormAutofillParent.prototype = { Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1); return; } - this._recordFormFillingTime("address", "autofill", timeStartedFillingMS); this.profileStorage.addresses.notifyUsed(address.guid); // Address is merged successfully Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1); @@ -401,7 +398,6 @@ FormAutofillParent.prototype = { changedGUIDs.push(this.profileStorage.addresses.add(address.record)); } changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid)); - this._recordFormFillingTime("address", "manual", timeStartedFillingMS); // Show first time use doorhanger if (Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) { @@ -445,28 +441,13 @@ FormAutofillParent.prototype = { }, _onFormSubmit(data, target) { - let {profile: {address, creditCard}, timeStartedFillingMS} = data; + let {address, creditCard} = data; if (address) { - this._onAddressSubmit(address, target, timeStartedFillingMS); + this._onAddressSubmit(address, target); } if (creditCard) { this._onCreditCardSubmit(creditCard, target); } }, - /** - * Set the probes for the filling time with specific filling type and form type. - * - * @private - * @param {string} formType - * 3 type of form (address/creditcard/address-creditcard). - * @param {string} fillingType - * 3 filling type (manual/autofill/autofill-update). - * @param {int} startedFillingMS - * Time that form started to filling in ms. - */ - _recordFormFillingTime(formType, fillingType, startedFillingMS) { - let histogram = Services.telemetry.getKeyedHistogramById("FORM_FILLING_REQUIRED_TIME_MS"); - histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS); - }, }; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 4c35f038aba2..1d23d0bf2272 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -13601,16 +13601,5 @@ "n_values": 10, "bug_numbers": [1393392], "description": "The runtime status, sent when a remote HW video decoder is shutdown, of previously-blacklisted driver: 0:remote HW-decoding and GPU process didn't crash, 1:remote HW-decoding and GPU process crashed. The histogram is keyed by the blacklisted driver name." - }, - "FORM_FILLING_REQUIRED_TIME_MS": { - "record_in_processes": ["main"], - "alert_emails": ["autofill@lists.mozilla.org", "jcheng@mozilla.com", "chsiang@mozilla.com"], - "expires_in_version": "60", - "kind": "exponential", - "high": 300000, - "n_buckets": 22, - "keyed": true, - "bug_numbers": [1341569], - "description": "Milliseconds between starting to fill an autofill-eligible form field and submitting the form" } } From 92dcd409a3f0165678ee9bfb38db5f911d0815b6 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 31 Aug 2017 16:01:43 -0700 Subject: [PATCH 39/51] Bug 1366511: Part 1 - Allow packing Result values into a single word. r=ehsan,nbp When used as an error value, nsresult should never be NS_OK, which means that we should be able to safely pack simple nsresult Result values into a single word. MozReview-Commit-ID: GJvnyTPjynk --HG-- extra : rebase_source : ab5a64b545dfbfe9bbef167f8b63ecbf00b16e07 --- mfbt/Result.h | 26 ++++++++++++++++++++++++++ xpcom/base/nscore.h | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/mfbt/Result.h b/mfbt/Result.h index 09c64b7ba5bb..3553a6c67839 100644 --- a/mfbt/Result.h +++ b/mfbt/Result.h @@ -95,6 +95,32 @@ public: E& unwrapErr() const { return *mErrorValue; } }; +/** + * Specialization for when the success type is Ok (or another empty class) and + * the error type is a value type which can never have the value 0 (as + * determined by UnusedZero<>). + */ +template +class ResultImplementation +{ + static constexpr E NullValue = E(0); + + E mErrorValue; + +public: + explicit ResultImplementation(V) : mErrorValue(NullValue) {} + explicit ResultImplementation(E aErrorValue) : mErrorValue(aErrorValue) + { + MOZ_ASSERT(aErrorValue != NullValue); + } + + bool isOk() const { return mErrorValue == NullValue; } + + V unwrap() const { return V(); } + E unwrapErr() const { return mErrorValue; } +}; + + /** * Specialization for when alignment permits using the least significant bit as * a tag bit. diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index 330ba3ecec23..544ac52b935d 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -194,6 +194,26 @@ typedef MozRefCountType nsrefcnt; +namespace mozilla { +// Extensions to the mozilla::Result type for handling of nsresult values. +// +// Note that these specializations need to be defined before Result.h is +// included, or we run into explicit specialization after instantiation errors, +// especially if Result.h is used in multiple sources in a unified compile. + +namespace detail { +// When used as an error value, nsresult should never be NS_OK. +// This specialization allows us to pack Result into a +// nsresult-sized value. +template struct UnusedZero; +template<> +struct UnusedZero +{ + static const bool value = true; +}; +} // namespace detail +} // namespace mozilla + /* * Use these macros to do 64bit safe pointer conversions. */ From c9899cb3fae8cfce5f7ca63f2296765b578f4900 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 29 Aug 2017 21:28:22 -0700 Subject: [PATCH 40/51] Bug 1366511: Part 2 - Allow autoconverting Err(nsresult) to nsresult. r=ehsan,nbp This allows MOZ_TRY and MOZ_TRY_VAR to be transparently used in XPCOM methods when compatible Result types are used. Also removes a compatibility macro in SimpleChannel.cpp, and an identical specialization in AddonManagerStartup, which are no longer necessary after this change. MozReview-Commit-ID: 94iNrPDJEnN --HG-- extra : rebase_source : 24ad4a54cbd170eb04ded21794530e56b1dfbd82 --- js/xpconnect/loader/ScriptPreloader-inl.h | 17 +-------- js/xpconnect/loader/ScriptPreloader.cpp | 2 +- mfbt/ResultExtensions.h | 37 +++++++++++++++++++ mfbt/moz.build | 1 + netwerk/base/SimpleChannel.cpp | 18 +-------- netwerk/base/SimpleChannel.h | 2 +- .../protocol/res/ExtensionProtocolHandler.cpp | 13 ------- .../protocol/res/ExtensionProtocolHandler.h | 1 + .../extensions/WebExtensionPolicy.cpp | 1 + .../extensions/AddonManagerStartup.cpp | 14 +------ xpcom/base/nscore.h | 3 ++ 11 files changed, 49 insertions(+), 60 deletions(-) create mode 100644 mfbt/ResultExtensions.h diff --git a/js/xpconnect/loader/ScriptPreloader-inl.h b/js/xpconnect/loader/ScriptPreloader-inl.h index 6b0c1deec4cf..6cf4b7f383fe 100644 --- a/js/xpconnect/loader/ScriptPreloader-inl.h +++ b/js/xpconnect/loader/ScriptPreloader-inl.h @@ -11,7 +11,7 @@ #include "mozilla/CheckedInt.h" #include "mozilla/EnumSet.h" #include "mozilla/Range.h" -#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/Unused.h" #include "mozilla/dom/ScriptSettings.h" #include "nsString.h" @@ -21,21 +21,6 @@ namespace mozilla { -// A specialization of GenericErrorResult which auto-converts to a nsresult. -// This should be removed when bug 1366511 is fixed. -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - namespace loader { using mozilla::dom::AutoJSAPI; diff --git a/js/xpconnect/loader/ScriptPreloader.cpp b/js/xpconnect/loader/ScriptPreloader.cpp index 437c11bc48b8..2755e8f57954 100644 --- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/ScriptPreloader.h" #include "ScriptPreloader-inl.h" +#include "mozilla/ScriptPreloader.h" #include "mozilla/loader/ScriptCacheActors.h" #include "mozilla/ArrayUtils.h" diff --git a/mfbt/ResultExtensions.h b/mfbt/ResultExtensions.h new file mode 100644 index 000000000000..a0c357f290ac --- /dev/null +++ b/mfbt/ResultExtensions.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Extensions to the Result type to enable simpler handling of XPCOM/NSPR results. */ + +#ifndef mozilla_ResultExtensions_h +#define mozilla_ResultExtensions_h + +#include "mozilla/Assertions.h" +#include "nscore.h" + +namespace mozilla { + +// Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY +// can be used in XPCOM methods with Result results. +template <> +class MOZ_MUST_USE_TYPE GenericErrorResult +{ + nsresult mErrorValue; + + template friend class Result; + +public: + explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) + { + MOZ_ASSERT(NS_FAILED(aErrorValue)); + } + + operator nsresult() { return mErrorValue; } +}; + +} // namespace mozilla + +#endif // mozilla_ResultExtensions_h diff --git a/mfbt/moz.build b/mfbt/moz.build index 3d8812228e2e..276870a7e589 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -76,6 +76,7 @@ EXPORTS.mozilla = [ 'RefCountType.h', 'RefPtr.h', 'Result.h', + 'ResultExtensions.h', 'ReverseIterator.h', 'RollingMean.h', 'Saturate.h', diff --git a/netwerk/base/SimpleChannel.cpp b/netwerk/base/SimpleChannel.cpp index 00cbb34ceda8..2fa882e4c5fe 100644 --- a/netwerk/base/SimpleChannel.cpp +++ b/netwerk/base/SimpleChannel.cpp @@ -21,18 +21,6 @@ namespace mozilla { namespace net { -// Like MOZ_TRY, but returns the unwrapped error value rather than a -// GenericErrorResult on failure. -#define TRY_VAR(target, expr) \ - do { \ - auto result = (expr); \ - if (result.isErr()) { \ - return result.unwrapErr(); \ - } \ - (target) = result.unwrap(); \ - } while (0) - - class SimpleChannel : public nsBaseChannel { public: @@ -63,7 +51,7 @@ SimpleChannel::OpenContentStream(bool async, nsIInputStream **streamOut, nsIChan NS_ENSURE_TRUE(mCallbacks, NS_ERROR_UNEXPECTED); nsCOMPtr stream; - TRY_VAR(stream, mCallbacks->OpenContentStream(async, this)); + MOZ_TRY_VAR(stream, mCallbacks->OpenContentStream(async, this)); MOZ_ASSERT(stream); mCallbacks = nullptr; @@ -79,7 +67,7 @@ SimpleChannel::BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request) NS_ENSURE_TRUE(mCallbacks, NS_ERROR_UNEXPECTED); nsCOMPtr req; - TRY_VAR(req, mCallbacks->StartAsyncRead(listener, this)); + MOZ_TRY_VAR(req, mCallbacks->StartAsyncRead(listener, this)); mCallbacks = nullptr; @@ -87,8 +75,6 @@ SimpleChannel::BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request) return NS_OK; } -#undef TRY_VAR - class SimpleChannelChild final : public SimpleChannel , public nsIChildChannel , public PSimpleChannelChild diff --git a/netwerk/base/SimpleChannel.h b/netwerk/base/SimpleChannel.h index 84c405be88b9..b32e4b52c951 100644 --- a/netwerk/base/SimpleChannel.h +++ b/netwerk/base/SimpleChannel.h @@ -6,7 +6,7 @@ #ifndef SimpleChannel_h #define SimpleChannel_h -#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp index 133c1a7cfad2..9120bade0027 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -52,19 +52,6 @@ using OptionalIPCStream = mozilla::ipc::OptionalIPCStream; namespace mozilla { -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - namespace net { using extensions::URLInfo; diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.h b/netwerk/protocol/res/ExtensionProtocolHandler.h index 4797930ff668..636653f2294b 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.h +++ b/netwerk/protocol/res/ExtensionProtocolHandler.h @@ -8,6 +8,7 @@ #include "mozilla/net/NeckoParent.h" #include "mozilla/LazyIdleThread.h" +#include "mozilla/Result.h" #include "SubstitutingProtocolHandler.h" namespace mozilla { diff --git a/toolkit/components/extensions/WebExtensionPolicy.cpp b/toolkit/components/extensions/WebExtensionPolicy.cpp index b599d3cbb42c..cf0d4de94b9c 100644 --- a/toolkit/components/extensions/WebExtensionPolicy.cpp +++ b/toolkit/components/extensions/WebExtensionPolicy.cpp @@ -8,6 +8,7 @@ #include "mozilla/extensions/WebExtensionPolicy.h" #include "mozilla/AddonManagerWebAPI.h" +#include "mozilla/ResultExtensions.h" #include "nsEscape.h" #include "nsISubstitutingProtocolHandler.h" #include "nsNetUtil.h" diff --git a/toolkit/mozapps/extensions/AddonManagerStartup.cpp b/toolkit/mozapps/extensions/AddonManagerStartup.cpp index 49879e345fda..ea36c2ad70ff 100644 --- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -16,6 +16,7 @@ #include "mozilla/Compression.h" #include "mozilla/LinkedList.h" #include "mozilla/Preferences.h" +#include "mozilla/ResultExtensions.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" #include "mozilla/Unused.h" @@ -42,19 +43,6 @@ namespace mozilla { -template <> -class MOZ_MUST_USE_TYPE GenericErrorResult -{ - nsresult mErrorValue; - - template friend class Result; - -public: - explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} - - operator nsresult() { return mErrorValue; } -}; - static inline Result WrapNSResult(PRStatus aRv) { diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index 544ac52b935d..d10f121b36b1 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -212,6 +212,9 @@ struct UnusedZero static const bool value = true; }; } // namespace detail + +template class MOZ_MUST_USE_TYPE GenericErrorResult; +template <> class MOZ_MUST_USE_TYPE GenericErrorResult; } // namespace mozilla /* From 6bad4f8ef74e34eba3ff8d417854546a51501ebd Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 29 Aug 2017 21:28:31 -0700 Subject: [PATCH 41/51] Bug 1366511: Part 3 - Add mozilla::ToResult() to convert other result types to equivalent Result. r=nbp,ehsan Also adds a mozilla/ResultExtensions.h header to define the appropriate conversion functions for nsresult and PRResult. This is in a separate header since those types are not available in Spidermonkey, and this is the pattern other *Extensions.h headers follow. Also removes equivalent NS_TRY macros and WrapNSResult inlines that served the same purpose in existing code, and are no longer necessary. MozReview-Commit-ID: A85PCAeyWhx --HG-- extra : rebase_source : a5988ff770888f901dd0798e7717bcf6254460cd --- js/xpconnect/loader/AutoMemMap.cpp | 4 +- js/xpconnect/loader/ScriptPreloader-inl.h | 20 --- js/xpconnect/loader/ScriptPreloader.cpp | 24 +-- mfbt/Result.h | 10 +- mfbt/ResultExtensions.h | 28 ++++ .../protocol/res/ExtensionProtocolHandler.cpp | 147 ++++++++---------- .../tests/gtest/BTSerializationTest.cpp | 20 +-- .../tests/gtest/CTSerializationTest.cpp | 4 +- .../certverifier/tests/gtest/CTTestUtils.cpp | 60 +++---- .../extensions/WebExtensionPolicy.cpp | 24 +-- .../extensions/AddonManagerStartup.cpp | 49 ++---- xpcom/base/nscore.h | 6 + 12 files changed, 178 insertions(+), 218 deletions(-) diff --git a/js/xpconnect/loader/AutoMemMap.cpp b/js/xpconnect/loader/AutoMemMap.cpp index 92bc728c243c..466e2db64e29 100644 --- a/js/xpconnect/loader/AutoMemMap.cpp +++ b/js/xpconnect/loader/AutoMemMap.cpp @@ -45,7 +45,7 @@ AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot) { MOZ_ASSERT(!fd); - NS_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget())); + MOZ_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget())); return initInternal(prot); } @@ -76,7 +76,7 @@ AutoMemMap::initInternal(PRFileMapProtect prot) MOZ_ASSERT(!addr); PRFileInfo64 fileInfo; - NS_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo)); + MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo)); if (fileInfo.size > UINT32_MAX) return Err(NS_ERROR_INVALID_ARG); diff --git a/js/xpconnect/loader/ScriptPreloader-inl.h b/js/xpconnect/loader/ScriptPreloader-inl.h index 6cf4b7f383fe..193e5e8813f6 100644 --- a/js/xpconnect/loader/ScriptPreloader-inl.h +++ b/js/xpconnect/loader/ScriptPreloader-inl.h @@ -30,26 +30,6 @@ struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI AutoSafeJSAPI() { Init(); } }; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - class OutputBuffer { diff --git a/js/xpconnect/loader/ScriptPreloader.cpp b/js/xpconnect/loader/ScriptPreloader.cpp index 2755e8f57954..f1c537950ba8 100644 --- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -362,12 +362,12 @@ Result, nsresult> ScriptPreloader::GetCacheFile(const nsAString& suffix) { nsCOMPtr cacheFile; - NS_TRY(mProfD->Clone(getter_AddRefs(cacheFile))); + MOZ_TRY(mProfD->Clone(getter_AddRefs(cacheFile))); - NS_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache"))); + MOZ_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache"))); Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777); - NS_TRY(cacheFile->Append(mBaseName + suffix)); + MOZ_TRY(cacheFile->Append(mBaseName + suffix)); return Move(cacheFile); } @@ -377,18 +377,18 @@ static const uint8_t MAGIC[] = "mozXDRcachev001"; Result ScriptPreloader::OpenCache() { - NS_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD))); + MOZ_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD))); nsCOMPtr cacheFile; MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING(".bin"))); bool exists; - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->Exists(&exists)); if (exists) { - NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING("-current.bin"))); + MOZ_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING("-current.bin"))); } else { - NS_TRY(cacheFile->SetLeafName(mBaseName + NS_LITERAL_STRING("-current.bin"))); - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->SetLeafName(mBaseName + NS_LITERAL_STRING("-current.bin"))); + MOZ_TRY(cacheFile->Exists(&exists)); if (!exists) { return Err(NS_ERROR_FILE_NOT_FOUND); } @@ -627,14 +627,14 @@ ScriptPreloader::WriteCache() MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING("-new.bin"))); bool exists; - NS_TRY(cacheFile->Exists(&exists)); + MOZ_TRY(cacheFile->Exists(&exists)); if (exists) { - NS_TRY(cacheFile->Remove(false)); + MOZ_TRY(cacheFile->Remove(false)); } { AutoFDClose fd; - NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); + MOZ_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); // We also need to hold mMonitor while we're touching scripts in // mScripts, or they may be freed before we're done with them. @@ -675,7 +675,7 @@ ScriptPreloader::WriteCache() } } - NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin"))); + MOZ_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin"))); return Ok(); } diff --git a/mfbt/Result.h b/mfbt/Result.h index 3553a6c67839..8aec3ca2c953 100644 --- a/mfbt/Result.h +++ b/mfbt/Result.h @@ -260,6 +260,14 @@ struct IsResult> : TrueType { }; } // namespace detail +template +auto +ToResult(Result&& aValue) + -> decltype(Forward>(aValue)) +{ + return Forward>(aValue); +} + /** * Result represents the outcome of an operation that can either succeed * or fail. It contains either a success value of type V or an error value of @@ -442,7 +450,7 @@ Err(E&& aErrorValue) */ #define MOZ_TRY(expr) \ do { \ - auto mozTryTempResult_ = (expr); \ + auto mozTryTempResult_ = ::mozilla::ToResult(expr); \ if (mozTryTempResult_.isErr()) { \ return ::mozilla::Err(mozTryTempResult_.unwrapErr()); \ } \ diff --git a/mfbt/ResultExtensions.h b/mfbt/ResultExtensions.h index a0c357f290ac..b47509b6a32a 100644 --- a/mfbt/ResultExtensions.h +++ b/mfbt/ResultExtensions.h @@ -11,6 +11,7 @@ #include "mozilla/Assertions.h" #include "nscore.h" +#include "prtypes.h" namespace mozilla { @@ -32,6 +33,33 @@ public: operator nsresult() { return mErrorValue; } }; +// Allow MOZ_TRY to handle `PRStatus` values. +inline Result ToResult(PRStatus aValue); + +} // namespace mozilla + +#include "mozilla/Result.h" + +namespace mozilla { + +inline Result +ToResult(nsresult aValue) +{ + if (NS_FAILED(aValue)) { + return Err(aValue); + } + return Ok(); +} + +inline Result +ToResult(PRStatus aValue) +{ + if (aValue == PR_SUCCESS) { + return Ok(); + } + return Err(NS_ERROR_FAILURE); +} + } // namespace mozilla #endif // mozilla_ResultExtensions_h diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp index 9120bade0027..8f0e1b616a65 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -15,6 +15,7 @@ #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/RefPtr.h" +#include "mozilla/ResultExtensions.h" #include "FileDescriptor.h" #include "FileDescriptorFile.h" @@ -62,26 +63,6 @@ LazyLogModule gExtProtocolLog("ExtProtocol"); StaticRefPtr ExtensionProtocolHandler::sSingleton; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - /** * Helper class used with SimpleChannel to asynchronously obtain an input * stream or file descriptor from the parent for a remote moz-extension load @@ -449,21 +430,21 @@ ExtensionProtocolHandler::SubstituteRemoteChannel(nsIURI* aURI, nsIChannel** aRetVal) { MOZ_ASSERT(IsNeckoChild()); - NS_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); nsAutoCString unResolvedSpec; - NS_TRY(aURI->GetSpec(unResolvedSpec)); + MOZ_TRY(aURI->GetSpec(unResolvedSpec)); nsAutoCString resolvedSpec; - NS_TRY(ResolveURI(aURI, resolvedSpec)); + MOZ_TRY(ResolveURI(aURI, resolvedSpec)); // Use the target URI scheme to determine if this is a packed or unpacked // extension URI. For unpacked extensions, we'll request an input stream // from the parent. For a packed extension, we'll request a file descriptor // for the JAR file. nsAutoCString scheme; - NS_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); if (scheme.EqualsLiteral("file")) { // Unpacked extension @@ -511,21 +492,21 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, nsresult rv; nsCOMPtr convService = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr uri; - NS_TRY(channel->GetURI(getter_AddRefs(uri))); + MOZ_TRY(channel->GetURI(getter_AddRefs(uri))); const char* kFromType = "application/vnd.mozilla.webext.unlocalized"; const char* kToType = "text/css"; nsCOMPtr converter; - NS_TRY(convService->AsyncConvertData(kFromType, kToType, listener, + MOZ_TRY(convService->AsyncConvertData(kFromType, kToType, listener, uri, getter_AddRefs(converter))); if (haveLoadInfo) { - NS_TRY(origChannel->AsyncOpen2(converter)); + MOZ_TRY(origChannel->AsyncOpen2(converter)); } else { - NS_TRY(origChannel->AsyncOpen(converter, nullptr)); + MOZ_TRY(origChannel->AsyncOpen(converter, nullptr)); } return RequestOrReason(origChannel); @@ -596,7 +577,7 @@ ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile, // On the first invocation, set mDevRepo if (!mAlreadyCheckedDevRepo) { mAlreadyCheckedDevRepo = true; - NS_TRY(mozilla::GetRepoDir(getter_AddRefs(mDevRepo))); + MOZ_TRY(mozilla::GetRepoDir(getter_AddRefs(mDevRepo))); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { nsAutoCString repoPath; Unused << mDevRepo->GetNativePath(repoPath); @@ -605,7 +586,7 @@ ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile, } if (mDevRepo) { - NS_TRY(mDevRepo->Contains(aRequestedFile, aResult)); + MOZ_TRY(mDevRepo->Contains(aRequestedFile, aResult)); } return Ok(); @@ -625,7 +606,7 @@ ExtensionProtocolHandler::AppDirContains(nsIFile* aExtensionDir, // On the first invocation, set mAppDir if (!mAlreadyCheckedAppDir) { mAlreadyCheckedAppDir = true; - NS_TRY(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(mAppDir))); + MOZ_TRY(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(mAppDir))); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { nsAutoCString appDirPath; Unused << mAppDir->GetNativePath(appDirPath); @@ -634,7 +615,7 @@ ExtensionProtocolHandler::AppDirContains(nsIFile* aExtensionDir, } if (mAppDir) { - NS_TRY(mAppDir->Contains(aExtensionDir, aResult)); + MOZ_TRY(mAppDir->Contains(aExtensionDir, aResult)); } return Ok(); @@ -660,8 +641,8 @@ Result, nsresult> ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) { MOZ_ASSERT(!IsNeckoChild()); - NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); *aTerminateSender = true; nsresult rv; @@ -688,21 +669,21 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) */ nsAutoCString host; - NS_TRY(aChildURI->GetAsciiHost(host)); + MOZ_TRY(aChildURI->GetAsciiHost(host)); // Lookup the directory this host string resolves to nsCOMPtr baseURI; - NS_TRY(GetSubstitution(host, getter_AddRefs(baseURI))); + MOZ_TRY(GetSubstitution(host, getter_AddRefs(baseURI))); // The result should be a file URL for the extension base dir nsCOMPtr fileURL = do_QueryInterface(baseURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr extensionDir; - NS_TRY(fileURL->GetFile(getter_AddRefs(extensionDir))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(extensionDir))); bool isDirectory = false; - NS_TRY(extensionDir->IsDirectory(&isDirectory)); + MOZ_TRY(extensionDir->IsDirectory(&isDirectory)); if (!isDirectory) { // The host should map to a directory for unpacked extensions return Err(NS_ERROR_FILE_NOT_DIRECTORY); @@ -713,38 +694,38 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) // file channel because we only request remote streams for unpacked // extension resource loads where the URI resolves to a file. nsAutoCString resolvedSpec; - NS_TRY(ResolveURI(aChildURI, resolvedSpec)); + MOZ_TRY(ResolveURI(aChildURI, resolvedSpec)); nsAutoCString resolvedScheme; - NS_TRY(net_ExtractURLScheme(resolvedSpec, resolvedScheme)); + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, resolvedScheme)); if (!resolvedScheme.EqualsLiteral("file")) { return Err(NS_ERROR_UNEXPECTED); } nsCOMPtr ioService = do_GetIOService(&rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr resolvedURI; - NS_TRY(ioService->NewURI(resolvedSpec, - nullptr, - nullptr, - getter_AddRefs(resolvedURI))); + MOZ_TRY(ioService->NewURI(resolvedSpec, + nullptr, + nullptr, + getter_AddRefs(resolvedURI))); // We use the system principal to get a file channel for the request, // but only after we've checked (above) that the child URI is of // moz-extension scheme and that the URI host maps to a directory. nsCOMPtr channel; - NS_TRY(NS_NewChannel(getter_AddRefs(channel), - resolvedURI, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER)); + MOZ_TRY(NS_NewChannel(getter_AddRefs(channel), + resolvedURI, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER)); nsCOMPtr fileChannel = do_QueryInterface(channel, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr requestedFile; - NS_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile))); + MOZ_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile))); /* * Make sure the file we resolved to is within the extension directory. @@ -752,8 +733,8 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) // Normalize paths for sane comparisons. nsIFile::Contains depends on // it for reliable subpath checks. - NS_TRY(extensionDir->Normalize()); - NS_TRY(requestedFile->Normalize()); + MOZ_TRY(extensionDir->Normalize()); + MOZ_TRY(requestedFile->Normalize()); #if defined(XP_WIN) if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(extensionDir) || !widget::WinUtils::ResolveJunctionPointsAndSymLinks(requestedFile)) { @@ -762,7 +743,7 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) #endif bool isResourceFromExtensionDir = false; - NS_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir)); + MOZ_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir)); if (!isResourceFromExtensionDir) { bool isAllowed = false; MOZ_TRY(AllowExternalResource(extensionDir, requestedFile, &isAllowed)); @@ -773,11 +754,11 @@ ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, bool* aTerminateSender) } nsCOMPtr inputStream; - NS_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), - requestedFile, - PR_RDONLY, - -1, - nsIFileInputStream::DEFER_OPEN)); + MOZ_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), + requestedFile, + PR_RDONLY, + -1, + nsIFileInputStream::DEFER_OPEN)); return inputStream; } @@ -788,8 +769,8 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, NeckoParent::GetExtensionFDResolver& aResolve) { MOZ_ASSERT(!IsNeckoChild()); - NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); - NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); *aTerminateSender = true; nsresult rv; @@ -806,24 +787,24 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, *aTerminateSender = false; nsAutoCString host; - NS_TRY(aChildURI->GetAsciiHost(host)); + MOZ_TRY(aChildURI->GetAsciiHost(host)); // We expect the host string to map to a JAR file because the URI // should refer to a web accessible resource for an enabled extension. nsCOMPtr subURI; - NS_TRY(GetSubstitution(host, getter_AddRefs(subURI))); + MOZ_TRY(GetSubstitution(host, getter_AddRefs(subURI))); nsCOMPtr jarURI = do_QueryInterface(subURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr innerFileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); if (!mFileOpenerThread) { mFileOpenerThread = @@ -838,7 +819,7 @@ ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, mozilla::NewRunnableMethod("ExtensionJarFileOpener", fileOpener, &ExtensionJARFileOpener::OpenFile); - NS_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL)); + MOZ_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL)); return Ok(); } @@ -892,13 +873,13 @@ LogCacheCheck(const nsIJARChannel* aJarChannel, nsresult rv; nsCOMPtr innerFileURI; - NS_TRY(aJarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(aJarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); nsAutoCString uriSpec, jarSpec; Unused << aJarURI->GetSpec(uriSpec); @@ -921,16 +902,16 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, // Build a JAR URI for this jar:file:// URI and use it to extract the // inner file URI. nsCOMPtr uri; - NS_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec)); + MOZ_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec)); nsCOMPtr jarURI = do_QueryInterface(uri, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarChannel = do_QueryInterface(*aRetVal, &rv); - NS_TRY(rv); + MOZ_TRY(rv); bool isCached = false; - NS_TRY(jarChannel->EnsureCached(&isCached)); + MOZ_TRY(jarChannel->EnsureCached(&isCached)); if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) { Unused << LogCacheCheck(jarChannel, jarURI, isCached); } @@ -941,13 +922,13 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, streamGetter = new ExtensionStreamGetter(jarChannel.forget()); } else { nsCOMPtr innerFileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); - NS_TRY(rv); + MOZ_TRY(rv); nsCOMPtr jarFile; - NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + MOZ_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); streamGetter = new ExtensionStreamGetter(aURI, aLoadinfo, @@ -959,7 +940,5 @@ ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, return Ok(); } -#undef NS_TRY - } // namespace net } // namespace mozilla diff --git a/security/certverifier/tests/gtest/BTSerializationTest.cpp b/security/certverifier/tests/gtest/BTSerializationTest.cpp index 8214c82ac590..c692a85864f3 100644 --- a/security/certverifier/tests/gtest/BTSerializationTest.cpp +++ b/security/certverifier/tests/gtest/BTSerializationTest.cpp @@ -74,7 +74,7 @@ TEST_F(BTSerializationTest, FailsDecodingInclusionProofUnexpectedData) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingInvalidHashSize) @@ -83,7 +83,7 @@ TEST_F(BTSerializationTest, FailsDecodingInvalidHashSize) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingInvalidHash) @@ -92,7 +92,7 @@ TEST_F(BTSerializationTest, FailsDecodingInvalidHash) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingMissingLogId) @@ -101,7 +101,7 @@ TEST_F(BTSerializationTest, FailsDecodingMissingLogId) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingNullPathLength) @@ -110,7 +110,7 @@ TEST_F(BTSerializationTest, FailsDecodingNullPathLength) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingPathLengthTooSmall) @@ -119,7 +119,7 @@ TEST_F(BTSerializationTest, FailsDecodingPathLengthTooSmall) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingPathLengthTooLarge) @@ -128,7 +128,7 @@ TEST_F(BTSerializationTest, FailsDecodingPathLengthTooLarge) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingNullTreeSize) @@ -137,7 +137,7 @@ TEST_F(BTSerializationTest, FailsDecodingNullTreeSize) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingLeafIndexOutOfBounds) @@ -146,7 +146,7 @@ TEST_F(BTSerializationTest, FailsDecodingLeafIndexOutOfBounds) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } TEST_F(BTSerializationTest, FailsDecodingExtraData) @@ -155,6 +155,6 @@ TEST_F(BTSerializationTest, FailsDecodingExtraData) Reader encodedProofReader(encodedProofInput); InclusionProofDataV2 ipr; - ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); + ASSERT_EQ(pkix::Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr)); } } } // namespace mozilla::ct diff --git a/security/certverifier/tests/gtest/CTSerializationTest.cpp b/security/certverifier/tests/gtest/CTSerializationTest.cpp index 357fc507c060..82bc6a238e3a 100644 --- a/security/certverifier/tests/gtest/CTSerializationTest.cpp +++ b/security/certverifier/tests/gtest/CTSerializationTest.cpp @@ -229,14 +229,14 @@ TEST_F(CTSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) const uint8_t INVALID_VERSION_BYTES[] = { 0x02, 0x00 }; Input invalidVersionSctInput(INVALID_VERSION_BYTES); Reader invalidVersionSctReader(invalidVersionSctInput); - EXPECT_EQ(Result::ERROR_BAD_DER, + EXPECT_EQ(pkix::Result::ERROR_BAD_DER, DecodeSignedCertificateTimestamp(invalidVersionSctReader, sct)); // Valid version, invalid length (missing data) const uint8_t INVALID_LENGTH_BYTES[] = { 0x00, 0x0a, 0x0b, 0x0c }; Input invalidLengthSctInput(INVALID_LENGTH_BYTES); Reader invalidLengthSctReader(invalidLengthSctInput); - EXPECT_EQ(Result::ERROR_BAD_DER, + EXPECT_EQ(pkix::Result::ERROR_BAD_DER, DecodeSignedCertificateTimestamp(invalidLengthSctReader, sct)); } diff --git a/security/certverifier/tests/gtest/CTTestUtils.cpp b/security/certverifier/tests/gtest/CTTestUtils.cpp index a3c5077f4317..09eb40bf04d0 100644 --- a/security/certverifier/tests/gtest/CTTestUtils.cpp +++ b/security/certverifier/tests/gtest/CTTestUtils.cpp @@ -754,83 +754,83 @@ ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result) class OCSPExtensionTrustDomain : public TrustDomain { public: - Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, - Input, TrustLevel&) override + pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + Input, TrustLevel&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result FindIssuer(Input, IssuerChecker&, Time) override + pkix::Result FindIssuer(Input, IssuerChecker&, Time) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, - const Input*, const Input*) override + pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, + const Input*, const Input*) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override + pkix::Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result DigestBuf(Input item, DigestAlgorithm digestAlg, - /*out*/ uint8_t* digestBuf, size_t digestBufLen) override + pkix::Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) override { return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } - Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) + pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override + pkix::Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, - Input subjectPublicKeyInfo) override + pkix::Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } - Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) + pkix::Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, - Input subjectPublicKeyInfo) override + pkix::Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } - Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) + pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } - Result NetscapeStepUpMatchesServerAuth(Time, bool&) override + pkix::Result NetscapeStepUpMatchesServerAuth(Time, bool&) override { ADD_FAILURE(); - return Result::FATAL_ERROR_LIBRARY_FAILURE; + return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; } void NoteAuxiliaryExtension(AuxiliaryExtension extension, Input data) override @@ -864,10 +864,10 @@ ExtractSCTListFromOCSPResponse(Input cert, bool expired; OCSPExtensionTrustDomain trustDomain; - Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, - time, /*time*/ - 1000, /*maxLifetimeInDays*/ - encodedResponse, expired); + pkix::Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, + time, /*time*/ + 1000, /*maxLifetimeInDays*/ + encodedResponse, expired); ASSERT_EQ(Success, rv); result = Move(trustDomain.signedCertificateTimestamps); diff --git a/toolkit/components/extensions/WebExtensionPolicy.cpp b/toolkit/components/extensions/WebExtensionPolicy.cpp index cf0d4de94b9c..8c8cc1db95ad 100644 --- a/toolkit/components/extensions/WebExtensionPolicy.cpp +++ b/toolkit/components/extensions/WebExtensionPolicy.cpp @@ -19,26 +19,6 @@ namespace extensions { using namespace dom; -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - static const char kProto[] = "moz-extension"; static const char kBackgroundPageHTMLStart[] = "\n\ @@ -225,9 +205,9 @@ WebExtensionPolicy::GetURL(const nsAString& aPath) const nsPrintfCString spec("%s://%s/", kProto, mHostname.get()); nsCOMPtr uri; - NS_TRY(NS_NewURI(getter_AddRefs(uri), spec)); + MOZ_TRY(NS_NewURI(getter_AddRefs(uri), spec)); - NS_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec)); + MOZ_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec)); return NS_ConvertUTF8toUTF16(spec); } diff --git a/toolkit/mozapps/extensions/AddonManagerStartup.cpp b/toolkit/mozapps/extensions/AddonManagerStartup.cpp index ea36c2ad70ff..c31d4b9bdff4 100644 --- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -43,27 +43,6 @@ namespace mozilla { -static inline Result -WrapNSResult(PRStatus aRv) -{ - if (aRv != PR_SUCCESS) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - -static inline Result -WrapNSResult(nsresult aRv) -{ - if (NS_FAILED(aRv)) { - return Err(aRv); - } - return Ok(); -} - -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - - using Compression::LZ4; using dom::ipc::StructuredCloneData; @@ -131,7 +110,7 @@ ReadFile(nsIFile* file) nsCString result; AutoFDClose fd; - NS_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget())); + MOZ_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget())); auto size = PR_Seek64(fd, 0, PR_SEEK_END); PR_Seek64(fd, 0, PR_SEEK_SET); @@ -247,13 +226,13 @@ GetJarCache() NS_ENSURE_TRUE(ios, Err(NS_ERROR_FAILURE)); nsCOMPtr jarProto; - NS_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto))); + MOZ_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto))); nsCOMPtr jar = do_QueryInterface(jarProto); MOZ_ASSERT(jar); nsCOMPtr zipCache; - NS_TRY(jar->GetJARCache(getter_AddRefs(zipCache))); + MOZ_TRY(jar->GetJARCache(getter_AddRefs(zipCache))); return Move(zipCache); } @@ -266,22 +245,22 @@ GetFileLocation(nsIURI* uri) nsCOMPtr fileURL = do_QueryInterface(uri); nsCOMPtr file; if (fileURL) { - NS_TRY(fileURL->GetFile(getter_AddRefs(file))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(file))); location.Init(file); } else { nsCOMPtr jarURI = do_QueryInterface(uri); NS_ENSURE_TRUE(jarURI, Err(NS_ERROR_INVALID_ARG)); nsCOMPtr fileURI; - NS_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI))); + MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI))); fileURL = do_QueryInterface(fileURI); NS_ENSURE_TRUE(fileURL, Err(NS_ERROR_INVALID_ARG)); - NS_TRY(fileURL->GetFile(getter_AddRefs(file))); + MOZ_TRY(fileURL->GetFile(getter_AddRefs(file))); nsCString entry; - NS_TRY(jarURI->GetJAREntry(entry)); + MOZ_TRY(jarURI->GetJAREntry(entry)); location.Init(file, entry.get()); } @@ -485,9 +464,9 @@ Addon::FullPath() } // If not an absolute path, fall back to a relative path from the location. - NS_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file))); + MOZ_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file))); - NS_TRY(file->AppendRelativePath(path)); + MOZ_TRY(file->AppendRelativePath(path)); return Move(file); } @@ -580,7 +559,7 @@ AddonManagerStartup::AddInstallLocation(Addon& addon) MOZ_TRY_VAR(file, addon.FullPath()); nsString path; - NS_TRY(file->GetPath(path)); + MOZ_TRY(file->GetPath(path)); auto type = addon.LocationType(); @@ -699,7 +678,7 @@ AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::Mutabl MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC)); JS::RootedObject obj(cx); - NS_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get())); + MOZ_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get())); result.set(JS::ObjectValue(*obj)); return NS_OK; @@ -749,16 +728,16 @@ AddonManagerStartup::EnumerateZipFile(nsIFile* file, const nsACString& pattern, MOZ_TRY_VAR(zipCache, GetJarCache()); nsCOMPtr zip; - NS_TRY(zipCache->GetZip(file, getter_AddRefs(zip))); + MOZ_TRY(zipCache->GetZip(file, getter_AddRefs(zip))); nsCOMPtr entries; - NS_TRY(zip->FindEntries(pattern, getter_AddRefs(entries))); + MOZ_TRY(zip->FindEntries(pattern, getter_AddRefs(entries))); nsTArray results; bool hasMore; while (NS_SUCCEEDED(entries->HasMore(&hasMore)) && hasMore) { nsAutoCString name; - NS_TRY(entries->GetNext(name)); + MOZ_TRY(entries->GetNext(name)); results.AppendElement(NS_ConvertUTF8toUTF16(name)); } diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index d10f121b36b1..459d5afceff1 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -215,6 +215,12 @@ struct UnusedZero template class MOZ_MUST_USE_TYPE GenericErrorResult; template <> class MOZ_MUST_USE_TYPE GenericErrorResult; + +struct Ok; +template class Result; + +// Allow MOZ_TRY to handle `nsresult` values. +inline Result ToResult(nsresult aValue); } // namespace mozilla /* From 23742b9ac3bc66a394b4d88cab671fadc6b37538 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 30 Aug 2017 16:55:28 -0500 Subject: [PATCH 42/51] Bug 1394927 - Use profiler_add_marker vs. profiler_tracing for accessibility profile events tags. r=aklotz MozReview-Commit-ID: 9oJcPtdodrU --HG-- extra : rebase_source : c56fb92d0521f5d3769ba8952a31d8b2c80cf498 --- accessible/generic/Accessible.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 2e98004304e7..0f28f0698fb7 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -848,8 +848,10 @@ Accessible::HandleAccEvent(AccEvent* aEvent) if (profiler_is_active()) { nsAutoCString strEventType; GetAccService()->GetStringEventType(aEvent->GetEventType(), strEventType); - - profiler_tracing("A11y Event", strEventType.get()); + nsAutoCString strMarker; + strMarker.AppendLiteral("A11y Event - "); + strMarker.Append(strEventType); + profiler_add_marker(strMarker.get()); } if (IPCAccessibilityActive() && Document()) { From 20dce28d41a7b1397bbc7f0754f31d996c3f5ba2 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 31 Aug 2017 21:45:37 -0400 Subject: [PATCH 43/51] Bug 1395787 - Update pdf.js to version 1.9.512. r=bdahl --- browser/extensions/pdfjs/README.mozilla | 4 +- browser/extensions/pdfjs/content/build/pdf.js | 1024 ++++++++--------- .../pdfjs/content/build/pdf.worker.js | 821 +++++++------ .../extensions/pdfjs/content/web/viewer.js | 5 +- 4 files changed, 880 insertions(+), 974 deletions(-) diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 48965f87b2c8..677ae103042d 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ This is the PDF.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.9.489 +Current extension version is: 1.9.512 -Taken from upstream commit: b7fcaff0 +Taken from upstream commit: 066fea9c diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 6de8d9dfd6c9..290e5f9e9982 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -1023,6 +1023,12 @@ function wrapReason(reason) { return new UnknownErrorException(reason.message, reason.details); } } +function makeReasonSerializable(reason) { + if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) { + return reason; + } + return new UnknownErrorException(reason.message, reason.toString()); +} function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); @@ -1080,15 +1086,12 @@ function MessageHandler(sourceName, targetName, comObj) { data: result }); }, reason => { - if (reason instanceof Error) { - reason = reason + ''; - } comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, - error: reason + error: makeReasonSerializable(reason) }); }); } else if (data.streamId) { @@ -1739,17 +1742,16 @@ var _dom_utils = __w_pdfjs_require__(1); var _util = __w_pdfjs_require__(0); -function AnnotationElementFactory() {} -AnnotationElementFactory.prototype = { - create: function AnnotationElementFactory_create(parameters) { - var subtype = parameters.data.annotationType; +class AnnotationElementFactory { + static create(parameters) { + let subtype = parameters.data.annotationType; switch (subtype) { case _util.AnnotationType.LINK: return new LinkAnnotationElement(parameters); case _util.AnnotationType.TEXT: return new TextAnnotationElement(parameters); case _util.AnnotationType.WIDGET: - var fieldType = parameters.data.fieldType; + let fieldType = parameters.data.fieldType; switch (fieldType) { case 'Tx': return new TextWidgetAnnotationElement(parameters); @@ -1783,10 +1785,10 @@ AnnotationElementFactory.prototype = { return new AnnotationElement(parameters); } } -}; -var AnnotationElement = function AnnotationElementClosure() { - function AnnotationElement(parameters, isRenderable, ignoreBorder) { - this.isRenderable = isRenderable || false; +} +class AnnotationElement { + constructor(parameters, isRenderable = false, ignoreBorder = false) { + this.isRenderable = isRenderable; this.data = parameters.data; this.layer = parameters.layer; this.page = parameters.page; @@ -1799,335 +1801,304 @@ var AnnotationElement = function AnnotationElementClosure() { this.container = this._createContainer(ignoreBorder); } } - AnnotationElement.prototype = { - _createContainer: function AnnotationElement_createContainer(ignoreBorder) { - var data = this.data, - page = this.page, - viewport = this.viewport; - var container = document.createElement('section'); - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; - container.setAttribute('data-annotation-id', data.id); - var rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); - _dom_utils.CustomStyle.setProp('transform', container, 'matrix(' + viewport.transform.join(',') + ')'); - _dom_utils.CustomStyle.setProp('transformOrigin', container, -rect[0] + 'px ' + -rect[1] + 'px'); - if (!ignoreBorder && data.borderStyle.width > 0) { - container.style.borderWidth = data.borderStyle.width + 'px'; - if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) { - width = width - 2 * data.borderStyle.width; - height = height - 2 * data.borderStyle.width; - } - var horizontalRadius = data.borderStyle.horizontalCornerRadius; - var verticalRadius = data.borderStyle.verticalCornerRadius; - if (horizontalRadius > 0 || verticalRadius > 0) { - var radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; - _dom_utils.CustomStyle.setProp('borderRadius', container, radius); - } - switch (data.borderStyle.style) { - case _util.AnnotationBorderStyleType.SOLID: - container.style.borderStyle = 'solid'; - break; - case _util.AnnotationBorderStyleType.DASHED: - container.style.borderStyle = 'dashed'; - break; - case _util.AnnotationBorderStyleType.BEVELED: - (0, _util.warn)('Unimplemented border style: beveled'); - break; - case _util.AnnotationBorderStyleType.INSET: - (0, _util.warn)('Unimplemented border style: inset'); - break; - case _util.AnnotationBorderStyleType.UNDERLINE: - container.style.borderBottomStyle = 'solid'; - break; - default: - break; - } - if (data.color) { - container.style.borderColor = _util.Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0); - } else { - container.style.borderWidth = 0; - } + _createContainer(ignoreBorder = false) { + let data = this.data, + page = this.page, + viewport = this.viewport; + let container = document.createElement('section'); + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; + container.setAttribute('data-annotation-id', data.id); + let rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); + _dom_utils.CustomStyle.setProp('transform', container, 'matrix(' + viewport.transform.join(',') + ')'); + _dom_utils.CustomStyle.setProp('transformOrigin', container, -rect[0] + 'px ' + -rect[1] + 'px'); + if (!ignoreBorder && data.borderStyle.width > 0) { + container.style.borderWidth = data.borderStyle.width + 'px'; + if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) { + width = width - 2 * data.borderStyle.width; + height = height - 2 * data.borderStyle.width; } - container.style.left = rect[0] + 'px'; - container.style.top = rect[1] + 'px'; - container.style.width = width + 'px'; - container.style.height = height + 'px'; - return container; - }, - _createPopup: function AnnotationElement_createPopup(container, trigger, data) { - if (!trigger) { - trigger = document.createElement('div'); - trigger.style.height = container.style.height; - trigger.style.width = container.style.width; - container.appendChild(trigger); + let horizontalRadius = data.borderStyle.horizontalCornerRadius; + let verticalRadius = data.borderStyle.verticalCornerRadius; + if (horizontalRadius > 0 || verticalRadius > 0) { + let radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; + _dom_utils.CustomStyle.setProp('borderRadius', container, radius); + } + switch (data.borderStyle.style) { + case _util.AnnotationBorderStyleType.SOLID: + container.style.borderStyle = 'solid'; + break; + case _util.AnnotationBorderStyleType.DASHED: + container.style.borderStyle = 'dashed'; + break; + case _util.AnnotationBorderStyleType.BEVELED: + (0, _util.warn)('Unimplemented border style: beveled'); + break; + case _util.AnnotationBorderStyleType.INSET: + (0, _util.warn)('Unimplemented border style: inset'); + break; + case _util.AnnotationBorderStyleType.UNDERLINE: + container.style.borderBottomStyle = 'solid'; + break; + default: + break; + } + if (data.color) { + container.style.borderColor = _util.Util.makeCssRgb(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0); + } else { + container.style.borderWidth = 0; } - var popupElement = new PopupElement({ - container, - trigger, - color: data.color, - title: data.title, - contents: data.contents, - hideWrapper: true - }); - var popup = popupElement.render(); - popup.style.left = container.style.width; - container.appendChild(popup); - }, - render: function AnnotationElement_render() { - throw new Error('Abstract method AnnotationElement.render called'); } - }; - return AnnotationElement; -}(); -var LinkAnnotationElement = function LinkAnnotationElementClosure() { - function LinkAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); + container.style.left = rect[0] + 'px'; + container.style.top = rect[1] + 'px'; + container.style.width = width + 'px'; + container.style.height = height + 'px'; + return container; } - _util.Util.inherit(LinkAnnotationElement, AnnotationElement, { - render: function LinkAnnotationElement_render() { - this.container.className = 'linkAnnotation'; - var link = document.createElement('a'); - (0, _dom_utils.addLinkAttributes)(link, { - url: this.data.url, - target: this.data.newWindow ? _dom_utils.LinkTarget.BLANK : undefined - }); - if (!this.data.url) { - if (this.data.action) { - this._bindNamedAction(link, this.data.action); - } else { - this._bindLink(link, this.data.dest); - } + _createPopup(container, trigger, data) { + if (!trigger) { + trigger = document.createElement('div'); + trigger.style.height = container.style.height; + trigger.style.width = container.style.width; + container.appendChild(trigger); + } + let popupElement = new PopupElement({ + container, + trigger, + color: data.color, + title: data.title, + contents: data.contents, + hideWrapper: true + }); + let popup = popupElement.render(); + popup.style.left = container.style.width; + container.appendChild(popup); + } + render() { + throw new Error('Abstract method `AnnotationElement.render` called'); + } +} +class LinkAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, true); + } + render() { + this.container.className = 'linkAnnotation'; + let link = document.createElement('a'); + (0, _dom_utils.addLinkAttributes)(link, { + url: this.data.url, + target: this.data.newWindow ? _dom_utils.LinkTarget.BLANK : undefined + }); + if (!this.data.url) { + if (this.data.action) { + this._bindNamedAction(link, this.data.action); + } else { + this._bindLink(link, this.data.dest); } - this.container.appendChild(link); - return this.container; - }, - _bindLink(link, destination) { - link.href = this.linkService.getDestinationHash(destination); - link.onclick = () => { - if (destination) { - this.linkService.navigateTo(destination); - } - return false; - }; + } + this.container.appendChild(link); + return this.container; + } + _bindLink(link, destination) { + link.href = this.linkService.getDestinationHash(destination); + link.onclick = () => { if (destination) { - link.className = 'internalLink'; + this.linkService.navigateTo(destination); } - }, - _bindNamedAction(link, action) { - link.href = this.linkService.getAnchorUrl(''); - link.onclick = () => { - this.linkService.executeNamedAction(action); - return false; - }; + return false; + }; + if (destination) { link.className = 'internalLink'; } - }); - return LinkAnnotationElement; -}(); -var TextAnnotationElement = function TextAnnotationElementClosure() { - function TextAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); } - _util.Util.inherit(TextAnnotationElement, AnnotationElement, { - render: function TextAnnotationElement_render() { - this.container.className = 'textAnnotation'; - var image = document.createElement('img'); - image.style.height = this.container.style.height; - image.style.width = this.container.style.width; - image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg'; - image.alt = '[{{type}} Annotation]'; - image.dataset.l10nId = 'text_annotation_type'; - image.dataset.l10nArgs = JSON.stringify({ type: this.data.name }); - if (!this.data.hasPopup) { - this._createPopup(this.container, image, this.data); - } - this.container.appendChild(image); - return this.container; + _bindNamedAction(link, action) { + link.href = this.linkService.getAnchorUrl(''); + link.onclick = () => { + this.linkService.executeNamedAction(action); + return false; + }; + link.className = 'internalLink'; + } +} +class TextAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } + render() { + this.container.className = 'textAnnotation'; + let image = document.createElement('img'); + image.style.height = this.container.style.height; + image.style.width = this.container.style.width; + image.src = this.imageResourcesPath + 'annotation-' + this.data.name.toLowerCase() + '.svg'; + image.alt = '[{{type}} Annotation]'; + image.dataset.l10nId = 'text_annotation_type'; + image.dataset.l10nArgs = JSON.stringify({ type: this.data.name }); + if (!this.data.hasPopup) { + this._createPopup(this.container, image, this.data); } - }); - return TextAnnotationElement; -}(); -var WidgetAnnotationElement = function WidgetAnnotationElementClosure() { - function WidgetAnnotationElement(parameters, isRenderable) { - AnnotationElement.call(this, parameters, isRenderable); + this.container.appendChild(image); + return this.container; } - _util.Util.inherit(WidgetAnnotationElement, AnnotationElement, { - render: function WidgetAnnotationElement_render() { - return this.container; - } - }); - return WidgetAnnotationElement; -}(); -var TextWidgetAnnotationElement = function TextWidgetAnnotationElementClosure() { - var TEXT_ALIGNMENT = ['left', 'center', 'right']; - function TextWidgetAnnotationElement(parameters) { - var isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue; - WidgetAnnotationElement.call(this, parameters, isRenderable); +} +class WidgetAnnotationElement extends AnnotationElement { + render() { + return this.container; } - _util.Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, { - render: function TextWidgetAnnotationElement_render() { - this.container.className = 'textWidgetAnnotation'; - var element = null; - if (this.renderInteractiveForms) { - if (this.data.multiLine) { - element = document.createElement('textarea'); - element.textContent = this.data.fieldValue; - } else { - element = document.createElement('input'); - element.type = 'text'; - element.setAttribute('value', this.data.fieldValue); - } - element.disabled = this.data.readOnly; - if (this.data.maxLen !== null) { - element.maxLength = this.data.maxLen; - } - if (this.data.comb) { - var fieldWidth = this.data.rect[2] - this.data.rect[0]; - var combWidth = fieldWidth / this.data.maxLen; - element.classList.add('comb'); - element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; - } - } else { - element = document.createElement('div'); +} +class TextWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + let isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue; + super(parameters, isRenderable); + } + render() { + const TEXT_ALIGNMENT = ['left', 'center', 'right']; + this.container.className = 'textWidgetAnnotation'; + let element = null; + if (this.renderInteractiveForms) { + if (this.data.multiLine) { + element = document.createElement('textarea'); element.textContent = this.data.fieldValue; - element.style.verticalAlign = 'middle'; - element.style.display = 'table-cell'; - var font = null; - if (this.data.fontRefName) { - font = this.page.commonObjs.getData(this.data.fontRefName); - } - this._setTextStyle(element, font); + } else { + element = document.createElement('input'); + element.type = 'text'; + element.setAttribute('value', this.data.fieldValue); } - if (this.data.textAlignment !== null) { - element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; - } - this.container.appendChild(element); - return this.container; - }, - _setTextStyle: function TextWidgetAnnotationElement_setTextStyle(element, font) { - var style = element.style; - style.fontSize = this.data.fontSize + 'px'; - style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr'; - if (!font) { - return; - } - style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal'; - style.fontStyle = font.italic ? 'italic' : 'normal'; - var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; - var fallbackName = font.fallbackName || 'Helvetica, sans-serif'; - style.fontFamily = fontFamily + fallbackName; - } - }); - return TextWidgetAnnotationElement; -}(); -var CheckboxWidgetAnnotationElement = function CheckboxWidgetAnnotationElementClosure() { - function CheckboxWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); - } - _util.Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, { - render: function CheckboxWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation checkBox'; - var element = document.createElement('input'); element.disabled = this.data.readOnly; - element.type = 'checkbox'; - if (this.data.fieldValue && this.data.fieldValue !== 'Off') { - element.setAttribute('checked', true); + if (this.data.maxLen !== null) { + element.maxLength = this.data.maxLen; } - this.container.appendChild(element); - return this.container; + if (this.data.comb) { + let fieldWidth = this.data.rect[2] - this.data.rect[0]; + let combWidth = fieldWidth / this.data.maxLen; + element.classList.add('comb'); + element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; + } + } else { + element = document.createElement('div'); + element.textContent = this.data.fieldValue; + element.style.verticalAlign = 'middle'; + element.style.display = 'table-cell'; + let font = null; + if (this.data.fontRefName) { + font = this.page.commonObjs.getData(this.data.fontRefName); + } + this._setTextStyle(element, font); } - }); - return CheckboxWidgetAnnotationElement; -}(); -var RadioButtonWidgetAnnotationElement = function RadioButtonWidgetAnnotationElementClosure() { - function RadioButtonWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + if (this.data.textAlignment !== null) { + element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; + } + this.container.appendChild(element); + return this.container; } - _util.Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, { - render: function RadioButtonWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation radioButton'; - var element = document.createElement('input'); - element.disabled = this.data.readOnly; - element.type = 'radio'; - element.name = this.data.fieldName; - if (this.data.fieldValue === this.data.buttonValue) { - element.setAttribute('checked', true); - } - this.container.appendChild(element); - return this.container; + _setTextStyle(element, font) { + let style = element.style; + style.fontSize = this.data.fontSize + 'px'; + style.direction = this.data.fontDirection < 0 ? 'rtl' : 'ltr'; + if (!font) { + return; } - }); - return RadioButtonWidgetAnnotationElement; -}(); -var ChoiceWidgetAnnotationElement = function ChoiceWidgetAnnotationElementClosure() { - function ChoiceWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + style.fontWeight = font.black ? font.bold ? '900' : 'bold' : font.bold ? 'bold' : 'normal'; + style.fontStyle = font.italic ? 'italic' : 'normal'; + let fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; + let fallbackName = font.fallbackName || 'Helvetica, sans-serif'; + style.fontFamily = fontFamily + fallbackName; } - _util.Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, { - render: function ChoiceWidgetAnnotationElement_render() { - this.container.className = 'choiceWidgetAnnotation'; - var selectElement = document.createElement('select'); - selectElement.disabled = this.data.readOnly; - if (!this.data.combo) { - selectElement.size = this.data.options.length; - if (this.data.multiSelect) { - selectElement.multiple = true; - } - } - for (var i = 0, ii = this.data.options.length; i < ii; i++) { - var option = this.data.options[i]; - var optionElement = document.createElement('option'); - optionElement.textContent = option.displayValue; - optionElement.value = option.exportValue; - if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { - optionElement.setAttribute('selected', true); - } - selectElement.appendChild(optionElement); - } - this.container.appendChild(selectElement); - return this.container; - } - }); - return ChoiceWidgetAnnotationElement; -}(); -var PopupAnnotationElement = function PopupAnnotationElementClosure() { - var IGNORE_TYPES = ['Line']; - function PopupAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); +} +class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); } - _util.Util.inherit(PopupAnnotationElement, AnnotationElement, { - render: function PopupAnnotationElement_render() { - this.container.className = 'popupAnnotation'; - if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { - return this.container; + render() { + this.container.className = 'buttonWidgetAnnotation checkBox'; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'checkbox'; + if (this.data.fieldValue && this.data.fieldValue !== 'Off') { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } +} +class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); + } + render() { + this.container.className = 'buttonWidgetAnnotation radioButton'; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'radio'; + element.name = this.data.fieldName; + if (this.data.fieldValue === this.data.buttonValue) { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } +} +class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); + } + render() { + this.container.className = 'choiceWidgetAnnotation'; + let selectElement = document.createElement('select'); + selectElement.disabled = this.data.readOnly; + if (!this.data.combo) { + selectElement.size = this.data.options.length; + if (this.data.multiSelect) { + selectElement.multiple = true; } - var selector = '[data-annotation-id="' + this.data.parentId + '"]'; - var parentElement = this.layer.querySelector(selector); - if (!parentElement) { - return this.container; + } + for (let i = 0, ii = this.data.options.length; i < ii; i++) { + let option = this.data.options[i]; + let optionElement = document.createElement('option'); + optionElement.textContent = option.displayValue; + optionElement.value = option.exportValue; + if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { + optionElement.setAttribute('selected', true); } - var popup = new PopupElement({ - container: this.container, - trigger: parentElement, - color: this.data.color, - title: this.data.title, - contents: this.data.contents - }); - var parentLeft = parseFloat(parentElement.style.left); - var parentWidth = parseFloat(parentElement.style.width); - _dom_utils.CustomStyle.setProp('transformOrigin', this.container, -(parentLeft + parentWidth) + 'px -' + parentElement.style.top); - this.container.style.left = parentLeft + parentWidth + 'px'; - this.container.appendChild(popup.render()); + selectElement.appendChild(optionElement); + } + this.container.appendChild(selectElement); + return this.container; + } +} +class PopupAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } + render() { + const IGNORE_TYPES = ['Line']; + this.container.className = 'popupAnnotation'; + if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { return this.container; } - }); - return PopupAnnotationElement; -}(); -var PopupElement = function PopupElementClosure() { - var BACKGROUND_ENLIGHT = 0.7; - function PopupElement(parameters) { + let selector = '[data-annotation-id="' + this.data.parentId + '"]'; + let parentElement = this.layer.querySelector(selector); + if (!parentElement) { + return this.container; + } + let popup = new PopupElement({ + container: this.container, + trigger: parentElement, + color: this.data.color, + title: this.data.title, + contents: this.data.contents + }); + let parentLeft = parseFloat(parentElement.style.left); + let parentWidth = parseFloat(parentElement.style.width); + _dom_utils.CustomStyle.setProp('transformOrigin', this.container, -(parentLeft + parentWidth) + 'px -' + parentElement.style.top); + this.container.style.left = parentLeft + parentWidth + 'px'; + this.container.appendChild(popup.render()); + return this.container; + } +} +class PopupElement { + constructor(parameters) { this.container = parameters.container; this.trigger = parameters.trigger; this.color = parameters.color; @@ -2136,174 +2107,157 @@ var PopupElement = function PopupElementClosure() { this.hideWrapper = parameters.hideWrapper || false; this.pinned = false; } - PopupElement.prototype = { - render: function PopupElement_render() { - var wrapper = document.createElement('div'); - wrapper.className = 'popupWrapper'; - this.hideElement = this.hideWrapper ? wrapper : this.container; + render() { + const BACKGROUND_ENLIGHT = 0.7; + let wrapper = document.createElement('div'); + wrapper.className = 'popupWrapper'; + this.hideElement = this.hideWrapper ? wrapper : this.container; + this.hideElement.setAttribute('hidden', true); + let popup = document.createElement('div'); + popup.className = 'popup'; + let color = this.color; + if (color) { + let r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; + let g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; + let b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; + popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0); + } + let contents = this._formatContents(this.contents); + let title = document.createElement('h1'); + title.textContent = this.title; + this.trigger.addEventListener('click', this._toggle.bind(this)); + this.trigger.addEventListener('mouseover', this._show.bind(this, false)); + this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); + popup.addEventListener('click', this._hide.bind(this, true)); + popup.appendChild(title); + popup.appendChild(contents); + wrapper.appendChild(popup); + return wrapper; + } + _formatContents(contents) { + let p = document.createElement('p'); + let lines = contents.split(/(?:\r\n?|\n)/); + for (let i = 0, ii = lines.length; i < ii; ++i) { + let line = lines[i]; + p.appendChild(document.createTextNode(line)); + if (i < ii - 1) { + p.appendChild(document.createElement('br')); + } + } + return p; + } + _toggle() { + if (this.pinned) { + this._hide(true); + } else { + this._show(true); + } + } + _show(pin = false) { + if (pin) { + this.pinned = true; + } + if (this.hideElement.hasAttribute('hidden')) { + this.hideElement.removeAttribute('hidden'); + this.container.style.zIndex += 1; + } + } + _hide(unpin = true) { + if (unpin) { + this.pinned = false; + } + if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { this.hideElement.setAttribute('hidden', true); - var popup = document.createElement('div'); - popup.className = 'popup'; - var color = this.color; - if (color) { - var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; - var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; - var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; - popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0); - } - var contents = this._formatContents(this.contents); - var title = document.createElement('h1'); - title.textContent = this.title; - this.trigger.addEventListener('click', this._toggle.bind(this)); - this.trigger.addEventListener('mouseover', this._show.bind(this, false)); - this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); - popup.addEventListener('click', this._hide.bind(this, true)); - popup.appendChild(title); - popup.appendChild(contents); - wrapper.appendChild(popup); - return wrapper; - }, - _formatContents: function PopupElement_formatContents(contents) { - var p = document.createElement('p'); - var lines = contents.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { - var line = lines[i]; - p.appendChild(document.createTextNode(line)); - if (i < ii - 1) { - p.appendChild(document.createElement('br')); - } - } - return p; - }, - _toggle: function PopupElement_toggle() { - if (this.pinned) { - this._hide(true); - } else { - this._show(true); - } - }, - _show: function PopupElement_show(pin) { - if (pin) { - this.pinned = true; - } - if (this.hideElement.hasAttribute('hidden')) { - this.hideElement.removeAttribute('hidden'); - this.container.style.zIndex += 1; - } - }, - _hide: function PopupElement_hide(unpin) { - if (unpin) { - this.pinned = false; - } - if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { - this.hideElement.setAttribute('hidden', true); - this.container.style.zIndex -= 1; - } + this.container.style.zIndex -= 1; } - }; - return PopupElement; -}(); -var LineAnnotationElement = function LineAnnotationElementClosure() { - var SVG_NS = 'http://www.w3.org/2000/svg'; - function LineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); } - _util.Util.inherit(LineAnnotationElement, AnnotationElement, { - render: function LineAnnotationElement_render() { - this.container.className = 'lineAnnotation'; - var data = this.data; - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; - var svg = document.createElementNS(SVG_NS, 'svg:svg'); - svg.setAttributeNS(null, 'version', '1.1'); - svg.setAttributeNS(null, 'width', width + 'px'); - svg.setAttributeNS(null, 'height', height + 'px'); - svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); - svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); - var line = document.createElementNS(SVG_NS, 'svg:line'); - line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); - line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); - line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); - line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); - line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); - line.setAttributeNS(null, 'stroke', 'transparent'); - svg.appendChild(line); - this.container.append(svg); - this._createPopup(this.container, line, this.data); - return this.container; - } - }); - return LineAnnotationElement; -}(); -var HighlightAnnotationElement = function HighlightAnnotationElementClosure() { - function HighlightAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); +} +class LineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); } - _util.Util.inherit(HighlightAnnotationElement, AnnotationElement, { - render: function HighlightAnnotationElement_render() { - this.container.className = 'highlightAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - } - }); - return HighlightAnnotationElement; -}(); -var UnderlineAnnotationElement = function UnderlineAnnotationElementClosure() { - function UnderlineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); + render() { + const SVG_NS = 'http://www.w3.org/2000/svg'; + this.container.className = 'lineAnnotation'; + let data = this.data; + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; + let svg = document.createElementNS(SVG_NS, 'svg:svg'); + svg.setAttributeNS(null, 'version', '1.1'); + svg.setAttributeNS(null, 'width', width + 'px'); + svg.setAttributeNS(null, 'height', height + 'px'); + svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); + svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); + let line = document.createElementNS(SVG_NS, 'svg:line'); + line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); + line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); + line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); + line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); + line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); + line.setAttributeNS(null, 'stroke', 'transparent'); + svg.appendChild(line); + this.container.append(svg); + this._createPopup(this.container, line, this.data); + return this.container; } - _util.Util.inherit(UnderlineAnnotationElement, AnnotationElement, { - render: function UnderlineAnnotationElement_render() { - this.container.className = 'underlineAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - } - }); - return UnderlineAnnotationElement; -}(); -var SquigglyAnnotationElement = function SquigglyAnnotationElementClosure() { - function SquigglyAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); +} +class HighlightAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); } - _util.Util.inherit(SquigglyAnnotationElement, AnnotationElement, { - render: function SquigglyAnnotationElement_render() { - this.container.className = 'squigglyAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; + render() { + this.container.className = 'highlightAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); } - }); - return SquigglyAnnotationElement; -}(); -var StrikeOutAnnotationElement = function StrikeOutAnnotationElementClosure() { - function StrikeOutAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, true); + return this.container; } - _util.Util.inherit(StrikeOutAnnotationElement, AnnotationElement, { - render: function StrikeOutAnnotationElement_render() { - this.container.className = 'strikeoutAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; +} +class UnderlineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'underlineAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); } - }); - return StrikeOutAnnotationElement; -}(); -var FileAttachmentAnnotationElement = function FileAttachmentAnnotationElementClosure() { - function FileAttachmentAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); - var file = this.data.file; + return this.container; + } +} +class SquigglyAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'squigglyAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} +class StrikeOutAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); + super(parameters, isRenderable, true); + } + render() { + this.container.className = 'strikeoutAnnotation'; + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} +class FileAttachmentAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, true); + let file = this.data.file; this.filename = (0, _dom_utils.getFilenameFromUrl)(file.filename); this.content = file.content; this.linkService.onFileAttachmentAnnotation({ @@ -2312,65 +2266,59 @@ var FileAttachmentAnnotationElement = function FileAttachmentAnnotationElementCl content: file.content }); } - _util.Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, { - render: function FileAttachmentAnnotationElement_render() { - this.container.className = 'fileAttachmentAnnotation'; - var trigger = document.createElement('div'); - trigger.style.height = this.container.style.height; - trigger.style.width = this.container.style.width; - trigger.addEventListener('dblclick', this._download.bind(this)); - if (!this.data.hasPopup && (this.data.title || this.data.contents)) { - this._createPopup(this.container, trigger, this.data); - } - this.container.appendChild(trigger); - return this.container; - }, - _download: function FileAttachmentAnnotationElement_download() { - if (!this.downloadManager) { - (0, _util.warn)('Download cannot be started due to unavailable download manager'); - return; - } - this.downloadManager.downloadData(this.content, this.filename, ''); + render() { + this.container.className = 'fileAttachmentAnnotation'; + let trigger = document.createElement('div'); + trigger.style.height = this.container.style.height; + trigger.style.width = this.container.style.width; + trigger.addEventListener('dblclick', this._download.bind(this)); + if (!this.data.hasPopup && (this.data.title || this.data.contents)) { + this._createPopup(this.container, trigger, this.data); } - }); - return FileAttachmentAnnotationElement; -}(); -var AnnotationLayer = function AnnotationLayerClosure() { - return { - render: function AnnotationLayer_render(parameters) { - var annotationElementFactory = new AnnotationElementFactory(); - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - if (!data) { - continue; - } - var element = annotationElementFactory.create({ - data, - layer: parameters.div, - page: parameters.page, - viewport: parameters.viewport, - linkService: parameters.linkService, - downloadManager: parameters.downloadManager, - imageResourcesPath: parameters.imageResourcesPath || (0, _dom_utils.getDefaultSetting)('imageResourcesPath'), - renderInteractiveForms: parameters.renderInteractiveForms || false - }); - if (element.isRenderable) { - parameters.div.appendChild(element.render()); - } - } - }, - update: function AnnotationLayer_update(parameters) { - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - var element = parameters.div.querySelector('[data-annotation-id="' + data.id + '"]'); - if (element) { - _dom_utils.CustomStyle.setProp('transform', element, 'matrix(' + parameters.viewport.transform.join(',') + ')'); - } - } - parameters.div.removeAttribute('hidden'); + this.container.appendChild(trigger); + return this.container; + } + _download() { + if (!this.downloadManager) { + (0, _util.warn)('Download cannot be started due to unavailable download manager'); + return; } - }; -}(); + this.downloadManager.downloadData(this.content, this.filename, ''); + } +} +class AnnotationLayer { + static render(parameters) { + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + if (!data) { + continue; + } + let element = AnnotationElementFactory.create({ + data, + layer: parameters.div, + page: parameters.page, + viewport: parameters.viewport, + linkService: parameters.linkService, + downloadManager: parameters.downloadManager, + imageResourcesPath: parameters.imageResourcesPath || (0, _dom_utils.getDefaultSetting)('imageResourcesPath'), + renderInteractiveForms: parameters.renderInteractiveForms || false + }); + if (element.isRenderable) { + parameters.div.appendChild(element.render()); + } + } + } + static update(parameters) { + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + let element = parameters.div.querySelector('[data-annotation-id="' + data.id + '"]'); + if (element) { + _dom_utils.CustomStyle.setProp('transform', element, 'matrix(' + parameters.viewport.transform.join(',') + ')'); + } + } + parameters.div.removeAttribute('hidden'); + } +} exports.AnnotationLayer = AnnotationLayer; /***/ }), @@ -3846,8 +3794,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() { }(); var version, build; { - exports.version = version = '1.9.489'; - exports.build = build = 'b7fcaff0'; + exports.version = version = '1.9.512'; + exports.build = build = '066fea9c'; } exports.getDocument = getDocument; exports.LoopbackPort = LoopbackPort; @@ -4903,8 +4851,8 @@ if (!_global_scope2.default.PDFJS) { } var PDFJS = _global_scope2.default.PDFJS; { - PDFJS.version = '1.9.489'; - PDFJS.build = 'b7fcaff0'; + PDFJS.version = '1.9.512'; + PDFJS.build = '066fea9c'; } PDFJS.pdfBug = false; if (PDFJS.verbosity !== undefined) { @@ -10470,8 +10418,8 @@ exports.PDFDataTransportStream = PDFDataTransportStream; "use strict"; -var pdfjsVersion = '1.9.489'; -var pdfjsBuild = 'b7fcaff0'; +var pdfjsVersion = '1.9.512'; +var pdfjsBuild = '066fea9c'; var pdfjsSharedUtil = __w_pdfjs_require__(0); var pdfjsDisplayGlobal = __w_pdfjs_require__(9); var pdfjsDisplayAPI = __w_pdfjs_require__(4); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 7a4513bdff64..76c842d1d12d 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -1023,6 +1023,12 @@ function wrapReason(reason) { return new UnknownErrorException(reason.message, reason.details); } } +function makeReasonSerializable(reason) { + if (!(reason instanceof Error) || reason instanceof AbortException || reason instanceof MissingPDFException || reason instanceof UnexpectedResponseException || reason instanceof UnknownErrorException) { + return reason; + } + return new UnknownErrorException(reason.message, reason.toString()); +} function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); @@ -1080,15 +1086,12 @@ function MessageHandler(sourceName, targetName, comObj) { data: result }); }, reason => { - if (reason instanceof Error) { - reason = reason + ''; - } comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, - error: reason + error: makeReasonSerializable(reason) }); }); } else if (data.streamId) { @@ -22651,13 +22654,19 @@ var XRef = function XRefClosure() { var obj1 = parser.getObj(); var obj2 = parser.getObj(); var obj3 = parser.getObj(); - if (!(0, _util.isInt)(obj1) || parseInt(obj1, 10) !== num || !(0, _util.isInt)(obj2) || parseInt(obj2, 10) !== gen || !(0, _primitives.isCmd)(obj3)) { + if (!Number.isInteger(obj1)) { + obj1 = parseInt(obj1, 10); + } + if (!Number.isInteger(obj2)) { + obj2 = parseInt(obj2, 10); + } + if (obj1 !== num || obj2 !== gen || !(0, _primitives.isCmd)(obj3)) { throw new _util.FormatError('bad XRef entry'); } - if (!(0, _primitives.isCmd)(obj3, 'obj')) { + if (obj3.cmd !== 'obj') { if (obj3.cmd.indexOf('obj') === 0) { num = parseInt(obj3.cmd.substring(3), 10); - if (!isNaN(num)) { + if (!Number.isNaN(num)) { return num; } } @@ -27314,17 +27323,16 @@ var _evaluator = __w_pdfjs_require__(13); var _stream = __w_pdfjs_require__(2); -function AnnotationFactory() {} -AnnotationFactory.prototype = { - create: function AnnotationFactory_create(xref, ref, pdfManager, idFactory) { - var dict = xref.fetchIfRef(ref); +class AnnotationFactory { + static create(xref, ref, pdfManager, idFactory) { + let dict = xref.fetchIfRef(ref); if (!(0, _primitives.isDict)(dict)) { return; } - var id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); - var subtype = dict.get('Subtype'); + let id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); + let subtype = dict.get('Subtype'); subtype = (0, _primitives.isName)(subtype) ? subtype.name : null; - var parameters = { + let parameters = { xref, dict, ref: (0, _primitives.isRef)(ref) ? ref : null, @@ -27338,7 +27346,7 @@ AnnotationFactory.prototype = { case 'Text': return new TextAnnotation(parameters); case 'Widget': - var fieldType = _util.Util.getInheritableProperty(dict, 'FT'); + let fieldType = _util.Util.getInheritableProperty(dict, 'FT'); fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null; switch (fieldType) { case 'Tx': @@ -27373,278 +27381,273 @@ AnnotationFactory.prototype = { return new Annotation(parameters); } } -}; -var Annotation = function AnnotationClosure() { - function getTransformMatrix(rect, bbox, matrix) { - var bounds = _util.Util.getAxialAlignedBoundingBox(bbox, matrix); - var minX = bounds[0]; - var minY = bounds[1]; - var maxX = bounds[2]; - var maxY = bounds[3]; - if (minX === maxX || minY === maxY) { - return [1, 0, 0, 1, rect[0], rect[1]]; - } - var xRatio = (rect[2] - rect[0]) / (maxX - minX); - var yRatio = (rect[3] - rect[1]) / (maxY - minY); - return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio]; +} +function getTransformMatrix(rect, bbox, matrix) { + let bounds = _util.Util.getAxialAlignedBoundingBox(bbox, matrix); + let minX = bounds[0]; + let minY = bounds[1]; + let maxX = bounds[2]; + let maxY = bounds[3]; + if (minX === maxX || minY === maxY) { + return [1, 0, 0, 1, rect[0], rect[1]]; } - function Annotation(params) { - var dict = params.dict; + let xRatio = (rect[2] - rect[0]) / (maxX - minX); + let yRatio = (rect[3] - rect[1]) / (maxY - minY); + return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio]; +} +class Annotation { + constructor(params) { + let dict = params.dict; this.setFlags(dict.get('F')); this.setRectangle(dict.getArray('Rect')); this.setColor(dict.getArray('C')); this.setBorderStyle(dict); this.setAppearance(dict); - this.data = {}; - this.data.id = params.id; - this.data.subtype = params.subtype; - this.data.annotationFlags = this.flags; - this.data.rect = this.rectangle; - this.data.color = this.color; - this.data.borderStyle = this.borderStyle; - this.data.hasAppearance = !!this.appearance; + this.data = { + annotationFlags: this.flags, + borderStyle: this.borderStyle, + color: this.color, + hasAppearance: !!this.appearance, + id: params.id, + rect: this.rectangle, + subtype: params.subtype + }; } - Annotation.prototype = { - _hasFlag: function Annotation_hasFlag(flags, flag) { - return !!(flags & flag); - }, - _isViewable: function Annotation_isViewable(flags) { - return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW); - }, - _isPrintable: function AnnotationFlag_isPrintable(flags) { - return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN); - }, - get viewable() { - if (this.flags === 0) { - return true; - } - return this._isViewable(this.flags); - }, - get printable() { - if (this.flags === 0) { - return false; - } - return this._isPrintable(this.flags); - }, - setFlags: function Annotation_setFlags(flags) { - this.flags = (0, _util.isInt)(flags) && flags > 0 ? flags : 0; - }, - hasFlag: function Annotation_hasFlag(flag) { - return this._hasFlag(this.flags, flag); - }, - setRectangle: function Annotation_setRectangle(rectangle) { - if ((0, _util.isArray)(rectangle) && rectangle.length === 4) { - this.rectangle = _util.Util.normalizeRect(rectangle); - } else { - this.rectangle = [0, 0, 0, 0]; - } - }, - setColor: function Annotation_setColor(color) { - var rgbColor = new Uint8Array(3); - if (!(0, _util.isArray)(color)) { - this.color = rgbColor; - return; - } - switch (color.length) { - case 0: - this.color = null; - break; - case 1: - _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - case 3: - _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - case 4: - _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - default: - this.color = rgbColor; - break; - } - }, - setBorderStyle: function Annotation_setBorderStyle(borderStyle) { - this.borderStyle = new AnnotationBorderStyle(); - if (!(0, _primitives.isDict)(borderStyle)) { - return; - } - if (borderStyle.has('BS')) { - var dict = borderStyle.get('BS'); - var dictType = dict.get('Type'); - if (!dictType || (0, _primitives.isName)(dictType, 'Border')) { - this.borderStyle.setWidth(dict.get('W')); - this.borderStyle.setStyle(dict.get('S')); - this.borderStyle.setDashArray(dict.getArray('D')); - } - } else if (borderStyle.has('Border')) { - var array = borderStyle.getArray('Border'); - if ((0, _util.isArray)(array) && array.length >= 3) { - this.borderStyle.setHorizontalCornerRadius(array[0]); - this.borderStyle.setVerticalCornerRadius(array[1]); - this.borderStyle.setWidth(array[2]); - if (array.length === 4) { - this.borderStyle.setDashArray(array[3]); - } - } - } else { - this.borderStyle.setWidth(0); - } - }, - setAppearance: function Annotation_setAppearance(dict) { - this.appearance = null; - var appearanceStates = dict.get('AP'); - if (!(0, _primitives.isDict)(appearanceStates)) { - return; - } - var normalAppearanceState = appearanceStates.get('N'); - if ((0, _primitives.isStream)(normalAppearanceState)) { - this.appearance = normalAppearanceState; - return; - } - if (!(0, _primitives.isDict)(normalAppearanceState)) { - return; - } - var as = dict.get('AS'); - if (!(0, _primitives.isName)(as) || !normalAppearanceState.has(as.name)) { - return; - } - this.appearance = normalAppearanceState.get(as.name); - }, - _preparePopup: function Annotation_preparePopup(dict) { - if (!dict.has('C')) { - this.data.color = null; - } - this.data.hasPopup = dict.has('Popup'); - this.data.title = (0, _util.stringToPDFString)(dict.get('T') || ''); - this.data.contents = (0, _util.stringToPDFString)(dict.get('Contents') || ''); - }, - loadResources: function Annotation_loadResources(keys) { - return this.appearance.dict.getAsync('Resources').then(resources => { - if (!resources) { - return; - } - let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref); - return objectLoader.load().then(function () { - return resources; - }); - }); - }, - getOperatorList: function Annotation_getOperatorList(evaluator, task, renderForms) { - if (!this.appearance) { - return Promise.resolve(new _evaluator.OperatorList()); - } - var data = this.data; - var appearanceDict = this.appearance.dict; - var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']); - var bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; - var matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; - var transform = getTransformMatrix(data.rect, bbox, matrix); - return resourcesPromise.then(resources => { - var opList = new _evaluator.OperatorList(); - opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList({ - stream: this.appearance, - task, - resources, - operatorList: opList - }).then(() => { - opList.addOp(_util.OPS.endAnnotation, []); - this.appearance.reset(); - return opList; - }); - }); + _hasFlag(flags, flag) { + return !!(flags & flag); + } + _isViewable(flags) { + return !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN) && !this._hasFlag(flags, _util.AnnotationFlag.NOVIEW); + } + _isPrintable(flags) { + return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, _util.AnnotationFlag.HIDDEN); + } + get viewable() { + if (this.flags === 0) { + return true; } - }; - return Annotation; -}(); -var AnnotationBorderStyle = function AnnotationBorderStyleClosure() { - function AnnotationBorderStyle() { + return this._isViewable(this.flags); + } + get printable() { + if (this.flags === 0) { + return false; + } + return this._isPrintable(this.flags); + } + setFlags(flags) { + this.flags = (0, _util.isInt)(flags) && flags > 0 ? flags : 0; + } + hasFlag(flag) { + return this._hasFlag(this.flags, flag); + } + setRectangle(rectangle) { + if ((0, _util.isArray)(rectangle) && rectangle.length === 4) { + this.rectangle = _util.Util.normalizeRect(rectangle); + } else { + this.rectangle = [0, 0, 0, 0]; + } + } + setColor(color) { + let rgbColor = new Uint8Array(3); + if (!(0, _util.isArray)(color)) { + this.color = rgbColor; + return; + } + switch (color.length) { + case 0: + this.color = null; + break; + case 1: + _colorspace.ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + case 3: + _colorspace.ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + case 4: + _colorspace.ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + default: + this.color = rgbColor; + break; + } + } + setBorderStyle(borderStyle) { + this.borderStyle = new AnnotationBorderStyle(); + if (!(0, _primitives.isDict)(borderStyle)) { + return; + } + if (borderStyle.has('BS')) { + let dict = borderStyle.get('BS'); + let dictType = dict.get('Type'); + if (!dictType || (0, _primitives.isName)(dictType, 'Border')) { + this.borderStyle.setWidth(dict.get('W')); + this.borderStyle.setStyle(dict.get('S')); + this.borderStyle.setDashArray(dict.getArray('D')); + } + } else if (borderStyle.has('Border')) { + let array = borderStyle.getArray('Border'); + if ((0, _util.isArray)(array) && array.length >= 3) { + this.borderStyle.setHorizontalCornerRadius(array[0]); + this.borderStyle.setVerticalCornerRadius(array[1]); + this.borderStyle.setWidth(array[2]); + if (array.length === 4) { + this.borderStyle.setDashArray(array[3]); + } + } + } else { + this.borderStyle.setWidth(0); + } + } + setAppearance(dict) { + this.appearance = null; + let appearanceStates = dict.get('AP'); + if (!(0, _primitives.isDict)(appearanceStates)) { + return; + } + let normalAppearanceState = appearanceStates.get('N'); + if ((0, _primitives.isStream)(normalAppearanceState)) { + this.appearance = normalAppearanceState; + return; + } + if (!(0, _primitives.isDict)(normalAppearanceState)) { + return; + } + let as = dict.get('AS'); + if (!(0, _primitives.isName)(as) || !normalAppearanceState.has(as.name)) { + return; + } + this.appearance = normalAppearanceState.get(as.name); + } + _preparePopup(dict) { + if (!dict.has('C')) { + this.data.color = null; + } + this.data.hasPopup = dict.has('Popup'); + this.data.title = (0, _util.stringToPDFString)(dict.get('T') || ''); + this.data.contents = (0, _util.stringToPDFString)(dict.get('Contents') || ''); + } + loadResources(keys) { + return this.appearance.dict.getAsync('Resources').then(resources => { + if (!resources) { + return; + } + let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref); + return objectLoader.load().then(function () { + return resources; + }); + }); + } + getOperatorList(evaluator, task, renderForms) { + if (!this.appearance) { + return Promise.resolve(new _evaluator.OperatorList()); + } + let data = this.data; + let appearanceDict = this.appearance.dict; + let resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']); + let bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; + let matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; + let transform = getTransformMatrix(data.rect, bbox, matrix); + return resourcesPromise.then(resources => { + let opList = new _evaluator.OperatorList(); + opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]); + return evaluator.getOperatorList({ + stream: this.appearance, + task, + resources, + operatorList: opList + }).then(() => { + opList.addOp(_util.OPS.endAnnotation, []); + this.appearance.reset(); + return opList; + }); + }); + } +} +class AnnotationBorderStyle { + constructor() { this.width = 1; this.style = _util.AnnotationBorderStyleType.SOLID; this.dashArray = [3]; this.horizontalCornerRadius = 0; this.verticalCornerRadius = 0; } - AnnotationBorderStyle.prototype = { - setWidth: function AnnotationBorderStyle_setWidth(width) { - if (width === (width | 0)) { - this.width = width; - } - }, - setStyle: function AnnotationBorderStyle_setStyle(style) { - if (!style) { - return; - } - switch (style.name) { - case 'S': - this.style = _util.AnnotationBorderStyleType.SOLID; + setWidth(width) { + if (width === (width | 0)) { + this.width = width; + } + } + setStyle(style) { + if (!style) { + return; + } + switch (style.name) { + case 'S': + this.style = _util.AnnotationBorderStyleType.SOLID; + break; + case 'D': + this.style = _util.AnnotationBorderStyleType.DASHED; + break; + case 'B': + this.style = _util.AnnotationBorderStyleType.BEVELED; + break; + case 'I': + this.style = _util.AnnotationBorderStyleType.INSET; + break; + case 'U': + this.style = _util.AnnotationBorderStyleType.UNDERLINE; + break; + default: + break; + } + } + setDashArray(dashArray) { + if ((0, _util.isArray)(dashArray) && dashArray.length > 0) { + let isValid = true; + let allZeros = true; + for (let i = 0, len = dashArray.length; i < len; i++) { + let element = dashArray[i]; + let validNumber = +element >= 0; + if (!validNumber) { + isValid = false; break; - case 'D': - this.style = _util.AnnotationBorderStyleType.DASHED; - break; - case 'B': - this.style = _util.AnnotationBorderStyleType.BEVELED; - break; - case 'I': - this.style = _util.AnnotationBorderStyleType.INSET; - break; - case 'U': - this.style = _util.AnnotationBorderStyleType.UNDERLINE; - break; - default: - break; - } - }, - setDashArray: function AnnotationBorderStyle_setDashArray(dashArray) { - if ((0, _util.isArray)(dashArray) && dashArray.length > 0) { - var isValid = true; - var allZeros = true; - for (var i = 0, len = dashArray.length; i < len; i++) { - var element = dashArray[i]; - var validNumber = +element >= 0; - if (!validNumber) { - isValid = false; - break; - } else if (element > 0) { - allZeros = false; - } + } else if (element > 0) { + allZeros = false; } - if (isValid && !allZeros) { - this.dashArray = dashArray; - } else { - this.width = 0; - } - } else if (dashArray) { + } + if (isValid && !allZeros) { + this.dashArray = dashArray; + } else { this.width = 0; } - }, - setHorizontalCornerRadius: function AnnotationBorderStyle_setHorizontalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.horizontalCornerRadius = radius; - } - }, - setVerticalCornerRadius: function AnnotationBorderStyle_setVerticalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.verticalCornerRadius = radius; - } + } else if (dashArray) { + this.width = 0; } - }; - return AnnotationBorderStyle; -}(); -var WidgetAnnotation = function WidgetAnnotationClosure() { - function WidgetAnnotation(params) { - Annotation.call(this, params); - var dict = params.dict; - var data = this.data; + } + setHorizontalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.horizontalCornerRadius = radius; + } + } + setVerticalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.verticalCornerRadius = radius; + } + } +} +class WidgetAnnotation extends Annotation { + constructor(params) { + super(params); + let dict = params.dict; + let data = this.data; data.annotationType = _util.AnnotationType.WIDGET; data.fieldName = this._constructFieldName(dict); data.fieldValue = _util.Util.getInheritableProperty(dict, 'V', true); data.alternativeText = (0, _util.stringToPDFString)(dict.get('TU') || ''); data.defaultAppearance = _util.Util.getInheritableProperty(dict, 'DA') || ''; - var fieldType = _util.Util.getInheritableProperty(dict, 'FT'); + let fieldType = _util.Util.getInheritableProperty(dict, 'FT'); data.fieldType = (0, _primitives.isName)(fieldType) ? fieldType.name : null; this.fieldResources = _util.Util.getInheritableProperty(dict, 'DR') || _primitives.Dict.empty; data.fieldFlags = _util.Util.getInheritableProperty(dict, 'Ff'); @@ -27656,47 +27659,50 @@ var WidgetAnnotation = function WidgetAnnotationClosure() { this.setFlags(_util.AnnotationFlag.HIDDEN); } } - _util.Util.inherit(WidgetAnnotation, Annotation, { - _constructFieldName: function WidgetAnnotation_constructFieldName(dict) { - if (!dict.has('T') && !dict.has('Parent')) { - (0, _util.warn)('Unknown field name, falling back to empty field name.'); - return ''; - } - if (!dict.has('Parent')) { - return (0, _util.stringToPDFString)(dict.get('T')); - } - var fieldName = []; - if (dict.has('T')) { - fieldName.unshift((0, _util.stringToPDFString)(dict.get('T'))); - } - var loopDict = dict; - while (loopDict.has('Parent')) { - loopDict = loopDict.get('Parent'); - if (!(0, _primitives.isDict)(loopDict)) { - break; - } - if (loopDict.has('T')) { - fieldName.unshift((0, _util.stringToPDFString)(loopDict.get('T'))); - } - } - return fieldName.join('.'); - }, - hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) { - return !!(this.data.fieldFlags & flag); + _constructFieldName(dict) { + if (!dict.has('T') && !dict.has('Parent')) { + (0, _util.warn)('Unknown field name, falling back to empty field name.'); + return ''; } - }); - return WidgetAnnotation; -}(); -var TextWidgetAnnotation = function TextWidgetAnnotationClosure() { - function TextWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); + if (!dict.has('Parent')) { + return (0, _util.stringToPDFString)(dict.get('T')); + } + let fieldName = []; + if (dict.has('T')) { + fieldName.unshift((0, _util.stringToPDFString)(dict.get('T'))); + } + let loopDict = dict; + while (loopDict.has('Parent')) { + loopDict = loopDict.get('Parent'); + if (!(0, _primitives.isDict)(loopDict)) { + break; + } + if (loopDict.has('T')) { + fieldName.unshift((0, _util.stringToPDFString)(loopDict.get('T'))); + } + } + return fieldName.join('.'); + } + hasFieldFlag(flag) { + return !!(this.data.fieldFlags & flag); + } + getOperatorList(evaluator, task, renderForms) { + if (renderForms) { + return Promise.resolve(new _evaluator.OperatorList()); + } + return super.getOperatorList(evaluator, task, renderForms); + } +} +class TextWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.fieldValue = (0, _util.stringToPDFString)(this.data.fieldValue || ''); - var alignment = _util.Util.getInheritableProperty(params.dict, 'Q'); + let alignment = _util.Util.getInheritableProperty(params.dict, 'Q'); if (!(0, _util.isInt)(alignment) || alignment < 0 || alignment > 2) { alignment = null; } this.data.textAlignment = alignment; - var maximumLength = _util.Util.getInheritableProperty(params.dict, 'MaxLen'); + let maximumLength = _util.Util.getInheritableProperty(params.dict, 'MaxLen'); if (!(0, _util.isInt)(maximumLength) || maximumLength < 0) { maximumLength = null; } @@ -27704,34 +27710,28 @@ var TextWidgetAnnotation = function TextWidgetAnnotationClosure() { this.data.multiLine = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE); this.data.comb = this.hasFieldFlag(_util.AnnotationFieldFlag.COMB) && !this.hasFieldFlag(_util.AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(_util.AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== null; } - _util.Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - if (!this.data.defaultAppearance) { - return Promise.resolve(operatorList); - } - var stream = new _stream.Stream((0, _util.stringToBytes)(this.data.defaultAppearance)); - return evaluator.getOperatorList({ - stream, - task, - resources: this.fieldResources, - operatorList - }).then(function () { - return operatorList; - }); + getOperatorList(evaluator, task, renderForms) { + if (renderForms || this.appearance) { + return super.getOperatorList(evaluator, task, renderForms); } - }); - return TextWidgetAnnotation; -}(); -var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { - function ButtonWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); + let operatorList = new _evaluator.OperatorList(); + if (!this.data.defaultAppearance) { + return Promise.resolve(operatorList); + } + let stream = new _stream.Stream((0, _util.stringToBytes)(this.data.defaultAppearance)); + return evaluator.getOperatorList({ + stream, + task, + resources: this.fieldResources, + operatorList + }).then(function () { + return operatorList; + }); + } +} +class ButtonWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.checkBox = !this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON); if (this.data.checkBox) { if (!(0, _primitives.isName)(this.data.fieldValue)) { @@ -27742,23 +27742,23 @@ var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { this.data.radioButton = this.hasFieldFlag(_util.AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(_util.AnnotationFieldFlag.PUSHBUTTON); if (this.data.radioButton) { this.data.fieldValue = this.data.buttonValue = null; - var fieldParent = params.dict.get('Parent'); + let fieldParent = params.dict.get('Parent'); if ((0, _primitives.isDict)(fieldParent) && fieldParent.has('V')) { - var fieldParentValue = fieldParent.get('V'); + let fieldParentValue = fieldParent.get('V'); if ((0, _primitives.isName)(fieldParentValue)) { this.data.fieldValue = fieldParentValue.name; } } - var appearanceStates = params.dict.get('AP'); + let appearanceStates = params.dict.get('AP'); if (!(0, _primitives.isDict)(appearanceStates)) { return; } - var normalAppearanceState = appearanceStates.get('N'); + let normalAppearanceState = appearanceStates.get('N'); if (!(0, _primitives.isDict)(normalAppearanceState)) { return; } - var keys = normalAppearanceState.getKeys(); - for (var i = 0, ii = keys.length; i < ii; i++) { + let keys = normalAppearanceState.getKeys(); + for (let i = 0, ii = keys.length; i < ii; i++) { if (keys[i] !== 'Off') { this.data.buttonValue = keys[i]; break; @@ -27766,30 +27766,17 @@ var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { } } } - _util.Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function ButtonWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - return Promise.resolve(operatorList); - } - }); - return ButtonWidgetAnnotation; -}(); -var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() { - function ChoiceWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); +} +class ChoiceWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.options = []; - var options = _util.Util.getInheritableProperty(params.dict, 'Opt'); + let options = _util.Util.getInheritableProperty(params.dict, 'Opt'); if ((0, _util.isArray)(options)) { - var xref = params.xref; - for (var i = 0, ii = options.length; i < ii; i++) { - var option = xref.fetchIfRef(options[i]); - var isOptionArray = (0, _util.isArray)(option); + let xref = params.xref; + for (let i = 0, ii = options.length; i < ii; i++) { + let option = xref.fetchIfRef(options[i]); + let isOptionArray = (0, _util.isArray)(option); this.data.options[i] = { exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option, displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option @@ -27802,21 +27789,11 @@ var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() { this.data.combo = this.hasFieldFlag(_util.AnnotationFieldFlag.COMBO); this.data.multiSelect = this.hasFieldFlag(_util.AnnotationFieldFlag.MULTISELECT); } - _util.Util.inherit(ChoiceWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function ChoiceWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { - var operatorList = new _evaluator.OperatorList(); - if (renderForms) { - return Promise.resolve(operatorList); - } - return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); - } - }); - return ChoiceWidgetAnnotation; -}(); -var TextAnnotation = function TextAnnotationClosure() { - var DEFAULT_ICON_SIZE = 22; - function TextAnnotation(parameters) { - Annotation.call(this, parameters); +} +class TextAnnotation extends Annotation { + constructor(parameters) { + const DEFAULT_ICON_SIZE = 22; + super(parameters); this.data.annotationType = _util.AnnotationType.TEXT; if (this.data.hasAppearance) { this.data.name = 'NoIcon'; @@ -27827,34 +27804,29 @@ var TextAnnotation = function TextAnnotationClosure() { } this._preparePopup(parameters.dict); } - _util.Util.inherit(TextAnnotation, Annotation, {}); - return TextAnnotation; -}(); -var LinkAnnotation = function LinkAnnotationClosure() { - function LinkAnnotation(params) { - Annotation.call(this, params); - var data = this.data; - data.annotationType = _util.AnnotationType.LINK; +} +class LinkAnnotation extends Annotation { + constructor(params) { + super(params); + this.data.annotationType = _util.AnnotationType.LINK; _obj.Catalog.parseDestDictionary({ destDict: params.dict, - resultObj: data, + resultObj: this.data, docBaseUrl: params.pdfManager.docBaseUrl }); } - _util.Util.inherit(LinkAnnotation, Annotation, {}); - return LinkAnnotation; -}(); -var PopupAnnotation = function PopupAnnotationClosure() { - function PopupAnnotation(parameters) { - Annotation.call(this, parameters); +} +class PopupAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.POPUP; - var dict = parameters.dict; - var parentItem = dict.get('Parent'); + let dict = parameters.dict; + let parentItem = dict.get('Parent'); if (!parentItem) { (0, _util.warn)('Popup annotation has a missing or invalid parent annotation.'); return; } - var parentSubtype = parentItem.get('Subtype'); + let parentSubtype = parentItem.get('Subtype'); this.data.parentType = (0, _primitives.isName)(parentSubtype) ? parentSubtype.name : null; this.data.parentId = dict.getRaw('Parent').toString(); this.data.title = (0, _util.stringToPDFString)(parentItem.get('T') || ''); @@ -27866,73 +27838,59 @@ var PopupAnnotation = function PopupAnnotationClosure() { this.data.color = this.color; } if (!this.viewable) { - var parentFlags = parentItem.get('F'); + let parentFlags = parentItem.get('F'); if (this._isViewable(parentFlags)) { this.setFlags(parentFlags); } } } - _util.Util.inherit(PopupAnnotation, Annotation, {}); - return PopupAnnotation; -}(); -var LineAnnotation = function LineAnnotationClosure() { - function LineAnnotation(parameters) { - Annotation.call(this, parameters); +} +class LineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.LINE; - var dict = parameters.dict; + let dict = parameters.dict; this.data.lineCoordinates = _util.Util.normalizeRect(dict.getArray('L')); this._preparePopup(dict); } - _util.Util.inherit(LineAnnotation, Annotation, {}); - return LineAnnotation; -}(); -var HighlightAnnotation = function HighlightAnnotationClosure() { - function HighlightAnnotation(parameters) { - Annotation.call(this, parameters); +} +class HighlightAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.HIGHLIGHT; this._preparePopup(parameters.dict); } - _util.Util.inherit(HighlightAnnotation, Annotation, {}); - return HighlightAnnotation; -}(); -var UnderlineAnnotation = function UnderlineAnnotationClosure() { - function UnderlineAnnotation(parameters) { - Annotation.call(this, parameters); +} +class UnderlineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.UNDERLINE; this._preparePopup(parameters.dict); } - _util.Util.inherit(UnderlineAnnotation, Annotation, {}); - return UnderlineAnnotation; -}(); -var SquigglyAnnotation = function SquigglyAnnotationClosure() { - function SquigglyAnnotation(parameters) { - Annotation.call(this, parameters); +} +class SquigglyAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.SQUIGGLY; this._preparePopup(parameters.dict); } - _util.Util.inherit(SquigglyAnnotation, Annotation, {}); - return SquigglyAnnotation; -}(); -var StrikeOutAnnotation = function StrikeOutAnnotationClosure() { - function StrikeOutAnnotation(parameters) { - Annotation.call(this, parameters); +} +class StrikeOutAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = _util.AnnotationType.STRIKEOUT; this._preparePopup(parameters.dict); } - _util.Util.inherit(StrikeOutAnnotation, Annotation, {}); - return StrikeOutAnnotation; -}(); -var FileAttachmentAnnotation = function FileAttachmentAnnotationClosure() { - function FileAttachmentAnnotation(parameters) { - Annotation.call(this, parameters); - var file = new _obj.FileSpec(parameters.dict.get('FS'), parameters.xref); +} +class FileAttachmentAnnotation extends Annotation { + constructor(parameters) { + super(parameters); + let file = new _obj.FileSpec(parameters.dict.get('FS'), parameters.xref); this.data.annotationType = _util.AnnotationType.FILEATTACHMENT; this.data.file = file.serializable; this._preparePopup(parameters.dict); } - _util.Util.inherit(FileAttachmentAnnotation, Annotation, {}); - return FileAttachmentAnnotation; -}(); +} exports.Annotation = Annotation; exports.AnnotationBorderStyle = AnnotationBorderStyle; exports.AnnotationFactory = AnnotationFactory; @@ -29166,10 +29124,9 @@ var Page = function PageClosure() { get annotations() { var annotations = []; var annotationRefs = this.getInheritedPageProp('Annots') || []; - var annotationFactory = new _annotation.AnnotationFactory(); for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; - var annotation = annotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); + var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); if (annotation) { annotations.push(annotation); } @@ -40063,8 +40020,8 @@ exports.Type1Parser = Type1Parser; "use strict"; -var pdfjsVersion = '1.9.489'; -var pdfjsBuild = 'b7fcaff0'; +var pdfjsVersion = '1.9.512'; +var pdfjsBuild = '066fea9c'; var pdfjsCoreWorker = __w_pdfjs_require__(17); exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index 352f8b6cee8e..8d733d793d0c 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -1323,7 +1323,9 @@ let PDFViewerApplication = { } } } - console.error(message + '\n' + moreInfoText); + Promise.all(moreInfoText).then(parts => { + console.error(message + '\n' + parts.join('\n')); + }); this.fallback(); }, progress(level) { @@ -4916,7 +4918,6 @@ class PDFPageView { this.reset(); if (this.pdfPage) { this.pdfPage.cleanup(); - this.pdfPage = null; } } _resetZoomLayer(removeFromDOM = false) { From 9edda378b4ff553f31bab63aa1f9823648271569 Mon Sep 17 00:00:00 2001 From: Henri Kemppainen Date: Fri, 1 Sep 2017 03:15:45 +0300 Subject: [PATCH 44/51] Bug 1395768 - Use absolute path to mach. r=gps --- python/mozboot/mozboot/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/mozboot/mozboot/base.py b/python/mozboot/mozboot/base.py index 87e787984bb4..876084fc699a 100644 --- a/python/mozboot/mozboot/base.py +++ b/python/mozboot/mozboot/base.py @@ -261,6 +261,7 @@ class BaseBootstrapper(object): def install_tooltool_clang_package(self, state_dir, checkout_root, toolchain_job): mach_binary = os.path.join(checkout_root, 'mach') + mach_binary = os.path.abspath(mach_binary) if not os.path.exists(mach_binary): raise ValueError("mach not found at %s" % mach_binary) From c419d5fa48c79e332cc67825ee16776aabe6c280 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Sep 2017 11:52:23 +1000 Subject: [PATCH 45/51] Bug 1389305 (attempt 2) - Add jemalloc_ptr_info() and moz_malloc_enclosing_size_of(). r=glandium. --HG-- extra : rebase_source : 93a6cfcc916fb239581c2892f24b24c6fc65ac71 --- memory/build/malloc_decls.h | 1 + memory/build/mozmemory.h | 7 + memory/build/mozmemory_wrap.c | 1 + memory/build/mozmemory_wrap.h | 3 + memory/gtest/TestJemalloc.cpp | 211 +++++++++++++++++++++-- memory/mozalloc/mozalloc.cpp | 25 ++- memory/mozalloc/mozalloc.h | 6 + memory/mozjemalloc/mozjemalloc.cpp | 154 +++++++++++++++++ memory/mozjemalloc/mozjemalloc_types.h | 65 +++++++ memory/replace/replace/ReplaceMalloc.cpp | 11 ++ mozglue/build/mozglue.def.in | 2 + mozglue/build/replace_malloc.mk | 1 + 12 files changed, 470 insertions(+), 17 deletions(-) diff --git a/memory/build/malloc_decls.h b/memory/build/malloc_decls.h index 2d71badd3c68..a0dd9b9ca219 100644 --- a/memory/build/malloc_decls.h +++ b/memory/build/malloc_decls.h @@ -62,6 +62,7 @@ MALLOC_DECL_VOID(jemalloc_stats, jemalloc_stats_t *) MALLOC_DECL_VOID(jemalloc_purge_freed_pages) MALLOC_DECL_VOID(jemalloc_free_dirty_pages) MALLOC_DECL_VOID(jemalloc_thread_local_arena, bool) +MALLOC_DECL_VOID(jemalloc_ptr_info, const void*, jemalloc_ptr_info_t*) # endif # undef MALLOC_DECL_VOID diff --git a/memory/build/mozmemory.h b/memory/build/mozmemory.h index 5c7565f8d4d4..4310f8c2fbb2 100644 --- a/memory/build/mozmemory.h +++ b/memory/build/mozmemory.h @@ -13,6 +13,7 @@ * - jemalloc_purge_freed_pages * - jemalloc_free_dirty_pages * - jemalloc_thread_local_arena + * - jemalloc_ptr_info */ #ifndef MOZ_MEMORY @@ -87,4 +88,10 @@ MOZ_JEMALLOC_API void jemalloc_free_dirty_pages(); MOZ_JEMALLOC_API void jemalloc_thread_local_arena(bool enabled); +/* + * Provide information about any allocation enclosing the given address. + */ +MOZ_JEMALLOC_API void jemalloc_ptr_info(const void* ptr, + jemalloc_ptr_info_t* info); + #endif /* mozmemory_h */ diff --git a/memory/build/mozmemory_wrap.c b/memory/build/mozmemory_wrap.c index ff0b84513fb5..78c02f28ca61 100644 --- a/memory/build/mozmemory_wrap.c +++ b/memory/build/mozmemory_wrap.c @@ -10,6 +10,7 @@ * argument types. */ #define MALLOC_DECL(name, return_type, ...) \ MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__); +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC #include "malloc_decls.h" #ifdef MOZ_WRAP_NEW_DELETE diff --git a/memory/build/mozmemory_wrap.h b/memory/build/mozmemory_wrap.h index c537b99495f2..40b7c44e9fae 100644 --- a/memory/build/mozmemory_wrap.h +++ b/memory/build/mozmemory_wrap.h @@ -37,6 +37,7 @@ * - jemalloc_purge_freed_pages * - jemalloc_free_dirty_pages * - jemalloc_thread_local_arena + * - jemalloc_ptr_info * (these functions are native to mozjemalloc) * * These functions are all exported as part of libmozglue (see @@ -207,5 +208,7 @@ #define jemalloc_free_dirty_pages_impl mozmem_jemalloc_impl(jemalloc_free_dirty_pages) #define jemalloc_thread_local_arena_impl \ mozmem_jemalloc_impl(jemalloc_thread_local_arena) +#define jemalloc_ptr_info_impl \ + mozmem_jemalloc_impl(jemalloc_ptr_info) #endif /* mozmemory_wrap_h */ diff --git a/memory/gtest/TestJemalloc.cpp b/memory/gtest/TestJemalloc.cpp index f37c57376a79..ce8da8e7e203 100644 --- a/memory/gtest/TestJemalloc.cpp +++ b/memory/gtest/TestJemalloc.cpp @@ -1,40 +1,45 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/mozalloc.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" #include "mozmemory.h" #include "gtest/gtest.h" +using namespace mozilla; + static inline void TestOne(size_t size) { - size_t req = size; - size_t adv = malloc_good_size(req); - char* p = (char*)malloc(req); - size_t usable = moz_malloc_usable_size(p); - // NB: Using EXPECT here so that we still free the memory on failure. - EXPECT_EQ(adv, usable) << - "malloc_good_size(" << req << ") --> " << adv << "; " - "malloc_usable_size(" << req << ") --> " << usable; - free(p); + size_t req = size; + size_t adv = malloc_good_size(req); + char* p = (char*)malloc(req); + size_t usable = moz_malloc_usable_size(p); + // NB: Using EXPECT here so that we still free the memory on failure. + EXPECT_EQ(adv, usable) << + "malloc_good_size(" << req << ") --> " << adv << "; " + "malloc_usable_size(" << req << ") --> " << usable; + free(p); } static inline void TestThree(size_t size) { - ASSERT_NO_FATAL_FAILURE(TestOne(size - 1)); - ASSERT_NO_FATAL_FAILURE(TestOne(size)); - ASSERT_NO_FATAL_FAILURE(TestOne(size + 1)); + ASSERT_NO_FATAL_FAILURE(TestOne(size - 1)); + ASSERT_NO_FATAL_FAILURE(TestOne(size)); + ASSERT_NO_FATAL_FAILURE(TestOne(size + 1)); } +#define K * 1024 +#define M * 1024 * 1024 + TEST(Jemalloc, UsableSizeInAdvance) { - #define K * 1024 - #define M * 1024 * 1024 - /* * Test every size up to a certain point, then (N-1, N, N+1) triplets for a * various sizes beyond that. @@ -49,3 +54,177 @@ TEST(Jemalloc, UsableSizeInAdvance) for (size_t n = 1 M; n < 8 M; n += 128 K) ASSERT_NO_FATAL_FAILURE(TestThree(n)); } + +static int gStaticVar; + +bool InfoEq(jemalloc_ptr_info_t& aInfo, PtrInfoTag aTag, void* aAddr, + size_t aSize) +{ + return aInfo.tag == aTag && aInfo.addr == aAddr && aInfo.size == aSize; +} + +bool InfoEqFreedPage(jemalloc_ptr_info_t& aInfo, void* aAddr, size_t aPageSize) +{ + size_t pageSizeMask = aPageSize - 1; + + return jemalloc_ptr_is_freed_page(&aInfo) && + aInfo.addr == (void*)(uintptr_t(aAddr) & ~pageSizeMask) && + aInfo.size == aPageSize; +} + +TEST(Jemalloc, PtrInfo) +{ + // Some things might be running in other threads, so ensure our assumptions + // (e.g. about isFreedSmall and isFreedPage ratios below) are not altered by + // other threads. + jemalloc_thread_local_arena(true); + + jemalloc_stats_t stats; + jemalloc_stats(&stats); + + jemalloc_ptr_info_t info; + Vector small, large, huge; + + // For small (<= 2KiB) allocations, test every position within many possible + // sizes. + size_t small_max = stats.page_size / 2; + for (size_t n = 0; n <= small_max; n += 8) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(small.append(p)); + for (size_t j = 0; j < usable; j++) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveSmall, p, usable)); + } + } + + // Similar for large (2KiB + 1 KiB .. 1MiB - 8KiB) allocations. + for (size_t n = small_max + 1 K; n <= stats.large_max; n += 1 K) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(large.append(p)); + for (size_t j = 0; j < usable; j += 347) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveLarge, p, usable)); + } + } + + // Similar for huge (> 1MiB - 8KiB) allocations. + for (size_t n = stats.chunksize; n <= 10 M; n += 512 K) { + auto p = (char*)malloc(n); + size_t usable = moz_malloc_size_of(p); + ASSERT_TRUE(huge.append(p)); + for (size_t j = 0; j < usable; j += 567) { + jemalloc_ptr_info(&p[j], &info); + ASSERT_TRUE(InfoEq(info, TagLiveHuge, p, usable)); + } + } + + // The following loops check freed allocations. We step through the vectors + // using prime-sized steps, which gives full coverage of the arrays while + // avoiding deallocating in the same order we allocated. + size_t len; + + // Free the small allocations and recheck them. + int isFreedSmall = 0, isFreedPage = 0; + len = small.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 19) % len) { + char* p = small[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k++) { + jemalloc_ptr_info(&p[k], &info); + // There are two valid outcomes here. + if (InfoEq(info, TagFreedSmall, p, usable)) { + isFreedSmall++; + } else if (InfoEqFreedPage(info, &p[k], stats.page_size)) { + isFreedPage++; + } else { + ASSERT_TRUE(false); + } + } + } + // There should be both FreedSmall and FreedPage results, but a lot more of + // the former. + ASSERT_TRUE(isFreedSmall != 0); + ASSERT_TRUE(isFreedPage != 0); + ASSERT_TRUE(isFreedSmall / isFreedPage > 10); + + // Free the large allocations and recheck them. + len = large.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 31) % len) { + char* p = large[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k += 357) { + jemalloc_ptr_info(&p[k], &info); + ASSERT_TRUE(InfoEqFreedPage(info, &p[k], stats.page_size)); + } + } + + // Free the huge allocations and recheck them. + len = huge.length(); + for (size_t i = 0, j = 0; i < len; i++, j = (j + 7) % len) { + char* p = huge[j]; + size_t usable = moz_malloc_size_of(p); + free(p); + for (size_t k = 0; k < usable; k += 587) { + jemalloc_ptr_info(&p[k], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + } + + // Null ptr. + jemalloc_ptr_info(nullptr, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Near-null ptr. + jemalloc_ptr_info((void*)0x123, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Maximum address. + jemalloc_ptr_info((void*)uintptr_t(-1), &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Stack memory. + int stackVar; + jemalloc_ptr_info(&stackVar, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Code memory. + jemalloc_ptr_info((const void*)&jemalloc_ptr_info, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Static memory. + jemalloc_ptr_info(&gStaticVar, &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + + // Chunk header. + UniquePtr p = MakeUnique(); + size_t chunksizeMask = stats.chunksize - 1; + char* chunk = (char*)(uintptr_t(p.get()) & ~chunksizeMask); + size_t chunkHeaderSize = stats.chunksize - stats.large_max; + for (size_t i = 0; i < chunkHeaderSize; i += 64) { + jemalloc_ptr_info(&chunk[i], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + + // Run header. + size_t page_sizeMask = stats.page_size - 1; + char* run = (char*)(uintptr_t(p.get()) & ~page_sizeMask); + for (size_t i = 0; i < 4 * sizeof(void*); i++) { + jemalloc_ptr_info(&run[i], &info); + ASSERT_TRUE(InfoEq(info, TagUnknown, nullptr, 0U)); + } + + // Entire chunk. It's impossible to check what is put into |info| for all of + // these addresses; this is more about checking that we don't crash. + for (size_t i = 0; i < stats.chunksize; i += 256) { + jemalloc_ptr_info(&chunk[i], &info); + } + + jemalloc_thread_local_arena(false); +} + +#undef K +#undef M diff --git a/memory/mozalloc/mozalloc.cpp b/memory/mozalloc/mozalloc.cpp index 1124e37a545a..764113aa6aa8 100644 --- a/memory/mozalloc/mozalloc.cpp +++ b/memory/mozalloc/mozalloc.cpp @@ -66,6 +66,7 @@ MOZ_MEMORY_API char *strndup_impl(const char *, size_t); #include +#include "mozilla/Assertions.h" #include "mozilla/mozalloc.h" #include "mozilla/mozalloc_oom.h" // for mozalloc_handle_oom @@ -214,8 +215,30 @@ moz_malloc_usable_size(void *ptr) #endif } -size_t moz_malloc_size_of(const void *ptr) +size_t +moz_malloc_size_of(const void *ptr) { return moz_malloc_usable_size((void *)ptr); } + +#if defined(MOZ_MEMORY) +#include "mozjemalloc_types.h" +// mozmemory.h declares jemalloc_ptr_info(), but including that header in this +// file is complicated. So we just redeclare it here instead, and include +// mozjemalloc_types.h for jemalloc_ptr_info_t. +MOZ_JEMALLOC_API void jemalloc_ptr_info(const void* ptr, + jemalloc_ptr_info_t* info); +#endif + +size_t +moz_malloc_enclosing_size_of(const void *ptr) +{ +#if defined(MOZ_MEMORY) + jemalloc_ptr_info_t info; + jemalloc_ptr_info(ptr, &info); + return jemalloc_ptr_is_live(&info) ? info.size : 0; +#else + return 0; +#endif +} #endif diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h index 18752a7987a0..c7af9f5e75fd 100644 --- a/memory/mozalloc/mozalloc.h +++ b/memory/mozalloc/mozalloc.h @@ -98,6 +98,12 @@ MFBT_API size_t moz_malloc_usable_size(void *ptr); MFBT_API size_t moz_malloc_size_of(const void *ptr); +/* + * Like moz_malloc_size_of(), but works reliably with interior pointers, i.e. + * pointers into the middle of a live allocation. + */ +MFBT_API size_t moz_malloc_enclosing_size_of(const void *ptr); + #if defined(HAVE_STRNDUP) MFBT_API char* moz_xstrndup(const char* str, size_t strsize) MOZ_ALLOCATOR; diff --git a/memory/mozjemalloc/mozjemalloc.cpp b/memory/mozjemalloc/mozjemalloc.cpp index b1529111e0f4..405b68d13512 100644 --- a/memory/mozjemalloc/mozjemalloc.cpp +++ b/memory/mozjemalloc/mozjemalloc.cpp @@ -1460,6 +1460,31 @@ extent_ad_comp(extent_node_t *a, extent_node_t *b) rb_wrap(static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, extent_ad_comp) +static inline int +extent_bounds_comp(extent_node_t* aKey, extent_node_t* aNode) +{ + uintptr_t key_addr = (uintptr_t)aKey->addr; + uintptr_t node_addr = (uintptr_t)aNode->addr; + size_t node_size = aNode->size; + + // Is aKey within aNode? + if (node_addr <= key_addr && key_addr < node_addr + node_size) { + return 0; + } + + return ((key_addr > node_addr) - (key_addr < node_addr)); +} + +/* + * This is an expansion of just the search function from the rb_wrap macro. + */ +static extent_node_t * +extent_tree_bounds_search(extent_tree_t *tree, extent_node_t *key) { + extent_node_t *ret; + rb_search(extent_node_t, link_ad, extent_bounds_comp, tree, key, ret); + return ret; +} + /* * End extent tree code. */ @@ -3544,6 +3569,134 @@ isalloc(const void *ptr) return (ret); } +MOZ_JEMALLOC_API void +jemalloc_ptr_info_impl(const void* aPtr, jemalloc_ptr_info_t* aInfo) +{ + arena_chunk_t* chunk = (arena_chunk_t*)CHUNK_ADDR2BASE(aPtr); + + // Is the pointer null, or within one chunk's size of null? + if (!chunk) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + // Look for huge allocations before looking for |chunk| in chunk_rtree. + // This is necessary because |chunk| won't be in chunk_rtree if it's + // the second or subsequent chunk in a huge allocation. + extent_node_t* node; + extent_node_t key; + malloc_mutex_lock(&huge_mtx); + key.addr = const_cast(aPtr); + node = extent_tree_bounds_search(&huge, &key); + if (node) { + *aInfo = { TagLiveHuge, node->addr, node->size }; + } + malloc_mutex_unlock(&huge_mtx); + if (node) { + return; + } + + // It's not a huge allocation. Check if we have a known chunk. + if (!malloc_rtree_get(chunk_rtree, (uintptr_t)chunk)) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + MOZ_DIAGNOSTIC_ASSERT(chunk->arena->magic == ARENA_MAGIC); + + // Get the page number within the chunk. + size_t pageind = (((uintptr_t)aPtr - (uintptr_t)chunk) >> pagesize_2pow); + if (pageind < arena_chunk_header_npages) { + // Within the chunk header. + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + size_t mapbits = chunk->map[pageind].bits; + + if (!(mapbits & CHUNK_MAP_ALLOCATED)) { + PtrInfoTag tag = TagFreedPageDirty; + if (mapbits & CHUNK_MAP_DIRTY) + tag = TagFreedPageDirty; + else if (mapbits & CHUNK_MAP_DECOMMITTED) + tag = TagFreedPageDecommitted; + else if (mapbits & CHUNK_MAP_MADVISED) + tag = TagFreedPageMadvised; + else if (mapbits & CHUNK_MAP_ZEROED) + tag = TagFreedPageZeroed; + else + MOZ_CRASH(); + + void* pageaddr = (void*)(uintptr_t(aPtr) & ~pagesize_mask); + *aInfo = { tag, pageaddr, pagesize }; + return; + } + + if (mapbits & CHUNK_MAP_LARGE) { + // It's a large allocation. Only the first page of a large + // allocation contains its size, so if the address is not in + // the first page, scan back to find the allocation size. + size_t size; + while (true) { + size = mapbits & ~pagesize_mask; + if (size != 0) { + break; + } + + // The following two return paths shouldn't occur in + // practice unless there is heap corruption. + + pageind--; + MOZ_DIAGNOSTIC_ASSERT(pageind >= arena_chunk_header_npages); + if (pageind < arena_chunk_header_npages) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + mapbits = chunk->map[pageind].bits; + MOZ_DIAGNOSTIC_ASSERT(mapbits & CHUNK_MAP_LARGE); + if (!(mapbits & CHUNK_MAP_LARGE)) { + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + } + + void* addr = ((char*)chunk) + (pageind << pagesize_2pow); + *aInfo = { TagLiveLarge, addr, size }; + return; + } + + // It must be a small allocation. + + auto run = (arena_run_t *)(mapbits & ~pagesize_mask); + MOZ_DIAGNOSTIC_ASSERT(run->magic == ARENA_RUN_MAGIC); + + // The allocation size is stored in the run metadata. + size_t size = run->bin->reg_size; + + // Address of the first possible pointer in the run after its headers. + uintptr_t reg0_addr = (uintptr_t)run + run->bin->reg0_offset; + if (aPtr < (void*)reg0_addr) { + // In the run header. + *aInfo = { TagUnknown, nullptr, 0 }; + return; + } + + // Position in the run. + unsigned regind = ((uintptr_t)aPtr - reg0_addr) / size; + + // Pointer to the allocation's base address. + void* addr = (void*)(reg0_addr + regind * size); + + // Check if the allocation has been freed. + unsigned elm = regind >> (SIZEOF_INT_2POW + 3); + unsigned bit = regind - (elm << (SIZEOF_INT_2POW + 3)); + PtrInfoTag tag = ((run->regs_mask[elm] & (1U << bit))) + ? TagFreedSmall : TagLiveSmall; + + *aInfo = { tag, addr, size}; +} + static inline void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_chunk_map_t *mapelm) @@ -4772,6 +4925,7 @@ jemalloc_stats_impl(jemalloc_stats_t *stats) stats->small_max = small_max; stats->large_max = arena_maxclass; stats->chunksize = chunksize; + stats->page_size = pagesize; stats->dirty_max = opt_dirty_max; /* diff --git a/memory/mozjemalloc/mozjemalloc_types.h b/memory/mozjemalloc/mozjemalloc_types.h index 76092f2d40e6..9b22045bc3b8 100644 --- a/memory/mozjemalloc/mozjemalloc_types.h +++ b/memory/mozjemalloc/mozjemalloc_types.h @@ -60,6 +60,7 @@ typedef struct { size_t small_max; /* Max quantum-spaced allocation size. */ size_t large_max; /* Max sub-chunksize allocation size. */ size_t chunksize; /* Size of each virtual memory mapping. */ + size_t page_size; /* Size of pages. */ size_t dirty_max; /* Max dirty pages per arena. */ /* @@ -77,6 +78,70 @@ typedef struct { size_t bin_unused; /* Bytes committed to a bin but currently unused. */ } jemalloc_stats_t; +enum PtrInfoTag { + // The pointer is not currently known to the allocator. + // 'addr' and 'size' are always 0. + TagUnknown, + + // The pointer is within a live allocation. + // 'addr' and 'size' describe the allocation. + TagLiveSmall, + TagLiveLarge, + TagLiveHuge, + + // The pointer is within a small freed allocation. + // 'addr' and 'size' describe the allocation. + TagFreedSmall, + + // The pointer is within a freed page. Details about the original + // allocation, including its size, are not available. + // 'addr' and 'size' describe the page. + TagFreedPageDirty, + TagFreedPageDecommitted, + TagFreedPageMadvised, + TagFreedPageZeroed, +}; + +/* + * The information in jemalloc_ptr_info_t could be represented in a variety of + * ways. The chosen representation has the following properties. + * - The number of fields is minimized. + * - The 'tag' field unambiguously defines the meaning of the subsequent fields. + * Helper functions are used to group together related categories of tags. + */ +typedef struct { + enum PtrInfoTag tag; + void* addr; // meaning depends on tag; see above + size_t size; // meaning depends on tag; see above +} jemalloc_ptr_info_t; + +static inline bool +jemalloc_ptr_is_live(jemalloc_ptr_info_t* info) +{ + return info->tag == TagLiveSmall || + info->tag == TagLiveLarge || + info->tag == TagLiveHuge; +} + +static inline bool +jemalloc_ptr_is_freed(jemalloc_ptr_info_t* info) +{ + return info->tag == TagFreedSmall || + info->tag == TagFreedPageDirty || + info->tag == TagFreedPageDecommitted || + info->tag == TagFreedPageMadvised || + info->tag == TagFreedPageZeroed; +} + +static inline bool +jemalloc_ptr_is_freed_page(jemalloc_ptr_info_t* info) +{ + return info->tag == TagFreedPageDirty || + info->tag == TagFreedPageDecommitted || + info->tag == TagFreedPageMadvised || + info->tag == TagFreedPageZeroed; +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/memory/replace/replace/ReplaceMalloc.cpp b/memory/replace/replace/ReplaceMalloc.cpp index 9b651fc48247..7760c870250c 100644 --- a/memory/replace/replace/ReplaceMalloc.cpp +++ b/memory/replace/replace/ReplaceMalloc.cpp @@ -261,3 +261,14 @@ replace_jemalloc_thread_local_arena(bool aEnabled) hook_table->jemalloc_thread_local_arena_hook(aEnabled); } } + +void +replace_jemalloc_ptr_info(const void* aPtr, jemalloc_ptr_info_t* aInfo) +{ + gFuncs->jemalloc_ptr_info(aPtr, aInfo); + const malloc_hook_table_t* hook_table = gHookTable; + if (hook_table && hook_table->jemalloc_ptr_info_hook) { + hook_table->jemalloc_ptr_info_hook(aPtr, aInfo); + } +} + diff --git a/mozglue/build/mozglue.def.in b/mozglue/build/mozglue.def.in index c12b7375867a..fcee21d4c5fb 100644 --- a/mozglue/build/mozglue.def.in +++ b/mozglue/build/mozglue.def.in @@ -33,8 +33,10 @@ EXPORTS wcsdup=wrap_wcsdup _wcsdup=wrap_wcsdup jemalloc_stats + jemalloc_purge_freed_pages jemalloc_free_dirty_pages jemalloc_thread_local_arena + jemalloc_ptr_info ; A hack to work around the CRT (see giant comment in Makefile.in) frex=dumb_free_thunk #endif diff --git a/mozglue/build/replace_malloc.mk b/mozglue/build/replace_malloc.mk index 162937385562..000d621015ee 100644 --- a/mozglue/build/replace_malloc.mk +++ b/mozglue/build/replace_malloc.mk @@ -20,6 +20,7 @@ OS_LDFLAGS += \ -Wl,-U,_replace_jemalloc_purge_freed_pages \ -Wl,-U,_replace_jemalloc_free_dirty_pages \ -Wl,-U,_replace_jemalloc_thread_local_arena \ + -Wl,-U,_replace_jemalloc_ptr_info \ $(NULL) EXTRA_DEPS += $(topsrcdir)/mozglue/build/replace_malloc.mk From d180d21cf72b66a460f8c5791d86716a902c43ec Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Thu, 31 Aug 2017 18:19:48 +0800 Subject: [PATCH 46/51] Bug 1370787 - add test for download file in PrivateBrowsing mode. r=smaug --- .../exthandler/tests/mochitest/blob.html | 17 +++++ .../exthandler/tests/mochitest/browser.ini | 2 + .../tests/mochitest/browser_ext_helper_pb.js | 74 +++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 uriloader/exthandler/tests/mochitest/blob.html create mode 100644 uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js diff --git a/uriloader/exthandler/tests/mochitest/blob.html b/uriloader/exthandler/tests/mochitest/blob.html new file mode 100644 index 000000000000..5960c50cde9a --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/blob.html @@ -0,0 +1,17 @@ + + + +Test for Bug 13870787 + + + +download + + diff --git a/uriloader/exthandler/tests/mochitest/browser.ini b/uriloader/exthandler/tests/mochitest/browser.ini index a1de09e185d8..361d985e6e23 100644 --- a/uriloader/exthandler/tests/mochitest/browser.ini +++ b/uriloader/exthandler/tests/mochitest/browser.ini @@ -1,6 +1,7 @@ [DEFAULT] head = head.js support-files = + blob.html download_page.html download.bin protocolHandler.html @@ -8,5 +9,6 @@ support-files = [browser_auto_close_window.js] skip-if = !e10s # test relies on e10s behavior [browser_download_always_ask_preferred_app.js] +[browser_ext_helper_pb.js] [browser_remember_download_option.js] [browser_web_protocol_handlers.js] diff --git a/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js b/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js new file mode 100644 index 000000000000..d095198b02ce --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/browser_ext_helper_pb.js @@ -0,0 +1,74 @@ +const { Downloads } = Cu.import("resource://gre/modules/Downloads.jsm", {}); +const URI = "http://example.com/browser/uriloader/exthandler/tests/mochitest/blob.html"; + +add_task(async function testExtHelperInPrivateBrowsing() { + let win = await BrowserTestUtils.openNewBrowserWindow({private: true}); + let browser = win.gBrowser.selectedBrowser; + browser.loadURI(URI); + await BrowserTestUtils.browserLoaded(browser); + + let listener = { + _resolve: null, + + onOpenWindow (aXULWindow) { + info("Download window shown..."); + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + // When the download dialog is shown, its accept button is only enabled + // after 1000ms (which is configured in the pref "security.dialog_enable_delay", + // see SharedPromptUtils.jsm. + setTimeout(() => { + // Set the choice to 'Save As File'. + let mode = domwindow.document.getElementById("mode"); + let save = domwindow.document.getElementById("save"); + let index = mode.getIndexOfItem(save); + mode.selectedItem = save; + domwindow.document.documentElement.acceptDialog(); + // acceptDialog will close domwindow itself, so we don't have to close + // domwindow here. + }, 1000); + }, domwindow); + }, + + onCloseWindow: function(aXULWindow) { + info("onCloseWindow"); + Services.wm.removeListener(listener); + this._resolve(); + }, + onWindowTitleChange: function(aXULWindow, aNewTitle) {}, + + waitForDownload: function() { + return new Promise(resolve => { + this._resolve = resolve; + }); + } + }; + + Services.wm.addListener(listener); + + await ContentTask.spawn(browser, {}, async function() { + var download = content.document.getElementById("download"); + download.click(); + }); + + // Wait until download is finished. + // However there seems to be no easy way to get notified when the download is + // completed, so we use onCloseWindow listener here. + await listener.waitForDownload(); + + let allList = await Downloads.getList(Downloads.ALL); + let allDownloads = await allList.getAll(); + Assert.equal(allDownloads.length, 1, "Should have at least 1 download in ALL mode"); + + let publicList = await Downloads.getList(Downloads.PUBLIC); + let publicDownloads = await publicList.getAll(); + Assert.equal(publicDownloads.length, 0, "Shouldn't have any download in normal mode"); + + let privateList = await Downloads.getList(Downloads.PRIVATE); + let privateDownloads = await privateList.getAll(); + Assert.equal(privateDownloads.length, 1, "Should have 1 download in private mode"); + + win.close(); + finish(); +}); From 2697cc0235cc48d2fd98d89296c4716571f637e3 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 31 Aug 2017 22:45:53 -0400 Subject: [PATCH 47/51] Bug 1395421 part 1. Don't codegen JS-implemented-webidl glue for methods that use maplike/setlike/iterable codegen (and hence wouldn't call into that clue anyway). r=peterv MozReview-Commit-ID: 6voihCBh6wh --- dom/bindings/Codegen.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b00c98e4934e..a595bc4d4ed5 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -14957,9 +14957,15 @@ class CGBindingImplClass(CGClass): if m.isMethod(): if m.isIdentifierLess(): continue + if m.isMaplikeOrSetlikeOrIterableMethod(): + # Handled by generated code already + continue if not m.isStatic() or not skipStaticMethods: appendMethod(m) elif m.isAttr(): + if m.isMaplikeOrSetlikeAttr(): + # Handled by generated code already + continue self.methodDecls.append(cgGetter(descriptor, m)) if not m.readonly: self.methodDecls.append(cgSetter(descriptor, m)) @@ -15213,8 +15219,9 @@ class CGExampleRoot(CGThing): continue if member.isStatic(): builder.addInMozillaDom("GlobalObject") - if member.isAttr() and not member.isMaplikeOrSetlikeAttr(): - builder.forwardDeclareForType(member.type, config) + if member.isAttr(): + if not member.isMaplikeOrSetlikeAttr(): + builder.forwardDeclareForType(member.type, config) else: assert member.isMethod() if not member.isMaplikeOrSetlikeOrIterableMethod(): @@ -15909,13 +15916,19 @@ class CGFastCallback(CGClass): class CGCallbackInterface(CGCallback): def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False): iface = descriptor.interface - attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] + attrs = [m for m in iface.members + if (m.isAttr() and not m.isStatic() and + (not m.isMaplikeOrSetlikeAttr() or + not iface.isJSImplemented()))] getters = [CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs) for a in attrs] setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs) for a in attrs if not a.readonly] methods = [m for m in iface.members - if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] + if (m.isMethod() and not m.isStatic() and + not m.isIdentifierLess() and + (not m.isMaplikeOrSetlikeOrIterableMethod() or + not iface.isJSImplemented()))] methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs) for m in methods for sig in m.signatures()] if iface.isJSImplemented() and iface.ctor(): From 056d8fe612648d8be813de8f16b60b50a207a1bc Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 31 Aug 2017 22:45:53 -0400 Subject: [PATCH 48/51] Bug 1395421 part 2. When a get() happens on a JS-implemented maplike, notify the JS implementation so it can take some sort of action (e.g. logging or warning). r=peterv MozReview-Commit-ID: 9G115wOyzvm --- dom/bindings/Codegen.py | 83 +++++++++++++++++++-- dom/bindings/test/TestInterfaceJSMaplike.js | 6 +- dom/media/PeerConnection.js | 4 + 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index a595bc4d4ed5..33e03b89618f 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -15570,6 +15570,17 @@ class CGJSImplClass(CGBindingImplClass): static=True, body=self.getCreateFromExistingBody())) + if (descriptor.interface.isJSImplemented() and + descriptor.interface.maplikeOrSetlikeOrIterable and + descriptor.interface.maplikeOrSetlikeOrIterable.isMaplike()): + self.methodDecls.append( + ClassMethod("__OnGet", + "void", + [Argument("JS::Handle", "aKey"), + Argument("JS::Handle", "aValue"), + Argument("ErrorResult&", "aRv")], + body="mImpl->__OnGet(aKey, aValue, aRv);\n")) + CGClass.__init__(self, descriptor.name, bases=baseClasses, constructors=[constructor], @@ -15931,16 +15942,31 @@ class CGCallbackInterface(CGCallback): not iface.isJSImplemented()))] methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs) for m in methods for sig in m.signatures()] + + needInitId = False if iface.isJSImplemented() and iface.ctor(): sigs = descriptor.interface.ctor().signatures() if len(sigs) != 1: raise TypeError("We only handle one constructor. See bug 869268.") methods.append(CGJSImplInitOperation(sigs[0], descriptor)) - if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()): - methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name) - for m in iface.members - if m.isAttr() or m.isMethod()] + - (["__init"] if iface.isJSImplemented() and iface.ctor() else []), + needInitId = True + + needOnGetId = False + if (iface.isJSImplemented() and + iface.maplikeOrSetlikeOrIterable and + iface.maplikeOrSetlikeOrIterable.isMaplike()): + methods.append(CGJSImplOnGetOperation(descriptor)) + needOnGetId = True + + idlist = [descriptor.binaryNameFor(m.identifier.name) + for m in iface.members + if m.isAttr() or m.isMethod()] + if needInitId: + idlist.append("__init") + if needOnGetId: + idlist.append("__onget") + if len(idlist) != 0: + methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms")) CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods, getters=getters, setters=setters) @@ -16459,6 +16485,32 @@ class CGJSImplInitOperation(CallbackOperationBase): return "__init" +class CGJSImplOnGetOperation(CallbackOperationBase): + """ + Codegen the __OnGet() method used to notify the JS impl that a get() is + happening on a JS-implemented maplike. This method takes two arguments + (key and value) and returns nothing. + """ + def __init__(self, descriptor): + CallbackOperationBase.__init__( + self, + (BuiltinTypes[IDLBuiltinType.Types.void], + [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], + None, + "key"), + FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], + None, + "value")]), + "__onget", "__OnGet", + descriptor, + singleOperation=False, + rethrowContentException=True, + spiderMonkeyInterfacesAreStructs=True) + + def getPrettyName(self): + return "__onget" + + def getMaplikeOrSetlikeErrorReturn(helperImpl): """ Generate return values based on whether a maplike or setlike generated @@ -16741,7 +16793,21 @@ class CGMaplikeOrSetlikeMethodGenerator(CGThing): JS::Rooted result(cx); """))] arguments = ["&result"] - return self.mergeTuples(r, (code, arguments, [])) + if self.descriptor.interface.isJSImplemented(): + callOnGet = [CGGeneric(dedent( + """ + { + JS::ExposeValueToActiveJS(result); + ErrorResult onGetResult; + self->__OnGet(arg0Val, result, onGetResult); + if (onGetResult.MaybeSetPendingException(cx)) { + return false; + } + } + """))] + else: + callOnGet = [] + return self.mergeTuples(r, (code, arguments, callOnGet)) def has(self): """ @@ -17041,6 +17107,11 @@ class GlobalGenRoots(): if d.interface.isJSImplemented() and d.interface.ctor(): # We'll have an __init() method. members.append(FakeMember('__init')) + if (d.interface.isJSImplemented() and + d.interface.maplikeOrSetlikeOrIterable and + d.interface.maplikeOrSetlikeOrIterable.isMaplike()): + # We'll have an __onget() method. + members.append(FakeMember('__onget')) if len(members) == 0: continue diff --git a/dom/bindings/test/TestInterfaceJSMaplike.js b/dom/bindings/test/TestInterfaceJSMaplike.js index b108ef5b6123..d6d5ef7e8904 100644 --- a/dom/bindings/test/TestInterfaceJSMaplike.js +++ b/dom/bindings/test/TestInterfaceJSMaplike.js @@ -32,7 +32,11 @@ TestInterfaceJSMaplike.prototype = { clearInternal: function() { return this.__DOM_IMPL__.__clear(); - } + }, + + __onget: function(key, value) { + /* no-op */ + }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJSMaplike]) diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 338de7849d89..4ab7b718ca58 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -327,6 +327,10 @@ class RTCStatsReport { } get mozPcid() { return this._pcid; } + + __onget(key, value) { + /* Do whatever here */ + } } setupPrototype(RTCStatsReport, { classID: PC_STATS_CID, From 11a90dfbd92c376906e48b1682ed624fa27d300e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 31 Aug 2017 22:46:35 -0400 Subject: [PATCH 49/51] Bug 1395591. When looking for our layout parent style, make sure to start with the placeholder if we're out of flow. r=emilio MozReview-Commit-ID: 1M52bi3oQFX --- layout/base/ServoRestyleManager.cpp | 12 +++++++++--- layout/base/crashtests/1395591-1.html | 11 +++++++++++ layout/base/crashtests/crashtests.list | 1 + layout/generic/nsPlaceholderFrame.cpp | 6 ++++++ layout/generic/nsPlaceholderFrame.h | 3 +++ 5 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 layout/base/crashtests/1395591-1.html diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index e5ccaf9ecdb6..2949b522c084 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -1554,9 +1554,15 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, if (!providerFrame) { // No providerFrame means we inherited from a display:contents thing. Our - // layout parent style is the style of our nearest ancestor frame. - providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(), - oldContext->GetPseudo()); + // layout parent style is the style of our nearest ancestor frame. But we have + // to be careful to do that with our placeholder, not with us, if we're out of + // flow. + if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { + aFrame->GetPlaceholderFrame()->GetLayoutParentStyleForOutOfFlow(&providerFrame); + } else { + providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(), + oldContext->GetPseudo()); + } } ServoStyleContext* layoutParent = providerFrame->StyleContext()->AsServo(); diff --git a/layout/base/crashtests/1395591-1.html b/layout/base/crashtests/1395591-1.html new file mode 100644 index 000000000000..2fca2ab4d098 --- /dev/null +++ b/layout/base/crashtests/1395591-1.html @@ -0,0 +1,11 @@ + + +
diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index bd0d762a39b2..2e9f80cc84c8 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -495,3 +495,4 @@ load 1362423-1.html load 1381323.html asserts-if(!stylo,1) load 1388625-1.html # bug 1389286 load 1390389.html +load 1395591-1.html diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index 576b67e62507..487af4eda75c 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -203,6 +203,12 @@ nsPlaceholderFrame::GetParentStyleContextForOutOfFlow(nsIFrame** aProviderFrame) } } + return GetLayoutParentStyleForOutOfFlow(aProviderFrame); +} + +nsStyleContext* +nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const +{ nsIFrame* parentFrame = GetParent(); // Placeholder of backdrop frame is a child of the corresponding top // layer frame, and its style context inherits from that frame. In diff --git a/layout/generic/nsPlaceholderFrame.h b/layout/generic/nsPlaceholderFrame.h index 737522c2febe..e270c76cdd31 100644 --- a/layout/generic/nsPlaceholderFrame.h +++ b/layout/generic/nsPlaceholderFrame.h @@ -146,6 +146,9 @@ public: nsStyleContext* GetParentStyleContextForOutOfFlow(nsIFrame** aProviderFrame) const; + // Like GetParentStyleContextForOutOfFlow, but ignores display:contents bits. + nsStyleContext* GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const; + bool RenumberFrameAndDescendants(int32_t* aOrdinal, int32_t aDepth, int32_t aIncrement, From c97121629fb0c3fcfa1b15f95c0f8200c9a6a2c4 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Fri, 21 Jul 2017 13:15:06 -0400 Subject: [PATCH 50/51] Bug 1151468 - add accessibility spec/actor/front to devtools. r=pbro MozReview-Commit-ID: Ln1R8e04mGR --- devtools/server/actors/accessibility.js | 538 ++++++++++++++++++ devtools/server/actors/moz.build | 1 + devtools/server/main.js | 5 + devtools/server/tests/browser/browser.ini | 5 + .../browser/browser_accessibility_node.js | 74 +++ .../browser_accessibility_node_events.js | 93 +++ .../browser/browser_accessibility_simple.js | 21 + .../browser/browser_accessibility_walker.js | 75 +++ .../tests/browser/doc_accessibility.html | 12 + devtools/server/tests/browser/head.js | 66 +++ devtools/shared/fronts/accessibility.js | 136 +++++ devtools/shared/fronts/moz.build | 1 + devtools/shared/specs/accessibility.js | 141 +++++ devtools/shared/specs/moz.build | 1 + 14 files changed, 1169 insertions(+) create mode 100644 devtools/server/actors/accessibility.js create mode 100644 devtools/server/tests/browser/browser_accessibility_node.js create mode 100644 devtools/server/tests/browser/browser_accessibility_node_events.js create mode 100644 devtools/server/tests/browser/browser_accessibility_simple.js create mode 100644 devtools/server/tests/browser/browser_accessibility_walker.js create mode 100644 devtools/server/tests/browser/doc_accessibility.html create mode 100644 devtools/shared/fronts/accessibility.js create mode 100644 devtools/shared/specs/accessibility.js diff --git a/devtools/server/actors/accessibility.js b/devtools/server/actors/accessibility.js new file mode 100644 index 000000000000..45e5ddbf4cf6 --- /dev/null +++ b/devtools/server/actors/accessibility.js @@ -0,0 +1,538 @@ +/* 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/. */ + +"use strict"; + +const { Cc, Ci, Cu } = require("chrome"); +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const Services = require("Services"); +const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); +const defer = require("devtools/shared/defer"); +const events = require("devtools/shared/event-emitter"); +const { + accessibleSpec, + accessibleWalkerSpec, + accessibilitySpec +} = require("devtools/shared/specs/accessibility"); + +const nsIAccessibleEvent = Ci.nsIAccessibleEvent; +const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent; +const nsIPropertyElement = Ci.nsIPropertyElement; + +const { + EVENT_TEXT_CHANGED, + EVENT_TEXT_INSERTED, + EVENT_TEXT_REMOVED, + EVENT_ACCELERATOR_CHANGE, + EVENT_ACTION_CHANGE, + EVENT_DEFACTION_CHANGE, + EVENT_DESCRIPTION_CHANGE, + EVENT_DOCUMENT_ATTRIBUTES_CHANGED, + EVENT_HELP_CHANGE, + EVENT_HIDE, + EVENT_NAME_CHANGE, + EVENT_OBJECT_ATTRIBUTE_CHANGED, + EVENT_REORDER, + EVENT_STATE_CHANGE, + EVENT_TEXT_ATTRIBUTE_CHANGED, + EVENT_VALUE_CHANGE +} = nsIAccessibleEvent; + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +/** + * Set of actors that expose accessibility tree information to the + * devtools protocol clients. + * + * The |Accessibility| actor is the main entry point. It is used to request + * an AccessibleWalker actor that caches the tree of Accessible actors. + * + * The |AccessibleWalker| actor is used to cache all seen Accessible actors as + * well as observe all relevant accesible events. + * + * The |Accessible| actor provides information about a particular accessible + * object, its properties, , attributes, states, relations, etc. + */ + +/** + * The AccessibleActor provides information about a given accessible object: its + * role, name, states, etc. + */ +const AccessibleActor = ActorClassWithSpec(accessibleSpec, { + initialize(walker, rawAccessible) { + Actor.prototype.initialize.call(this, walker.conn); + this.walker = walker; + this.rawAccessible = rawAccessible; + + /** + * Indicates if the raw accessible is no longer alive. + * + * @return Boolean + */ + Object.defineProperty(this, "isDefunct", { + get() { + let defunct = false; + + try { + let extState = {}; + this.rawAccessible.getState({}, extState); + // extState.value is a bitmask. We are applying bitwise AND to mask out + // irrelelvant states. + defunct = !!(extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT); + } catch (e) { + defunct = true; + } + + if (defunct) { + delete this.isDefunct; + this.isDefunct = true; + return this.isDefunct; + } + + return defunct; + }, + configurable: true + }); + }, + + /** + * Items returned by this actor should belong to the parent walker. + */ + marshallPool() { + return this.walker; + }, + + destroy() { + Actor.prototype.destroy.call(this); + this.walker = null; + this.rawAccessible = null; + }, + + get role() { + if (this.isDefunct) { + return null; + } + return this.walker.a11yService.getStringRole(this.rawAccessible.role); + }, + + get name() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.name; + }, + + get value() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.value; + }, + + get description() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.description; + }, + + get help() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.help; + }, + + get keyboardShortcut() { + if (this.isDefunct) { + return null; + } + return this.rawAccessible.keyboardShortcut; + }, + + get childCount() { + if (this.isDefunct) { + return 0; + } + return this.rawAccessible.childCount; + }, + + get domNodeType() { + if (this.isDefunct) { + return 0; + } + return this.rawAccessible.DOMNode ? this.rawAccessible.DOMNode.nodeType : 0; + }, + + children() { + let children = []; + if (this.isDefunct) { + return children; + } + + for (let child = this.rawAccessible.firstChild; child; child = child.nextSibling) { + children.push(this.walker.addRef(child)); + } + return children; + }, + + getIndexInParent() { + if (this.isDefunct) { + return -1; + } + return this.rawAccessible.indexInParent; + }, + + getActions() { + let actions = []; + if (this.isDefunct) { + return actions; + } + + for (let i = 0; i < this.rawAccessible.actionCount; i++) { + actions.push(this.rawAccessible.getActionDescription(i)); + } + return actions; + }, + + getState() { + if (this.isDefunct) { + return []; + } + + let state = {}; + let extState = {}; + this.rawAccessible.getState(state, extState); + return [ + ...this.walker.a11yService.getStringStates(state.value, extState.value) + ]; + }, + + getAttributes() { + if (this.isDefunct || !this.rawAccessible.attributes) { + return {}; + } + + let attributes = {}; + let attrsEnum = this.rawAccessible.attributes.enumerate(); + while (attrsEnum.hasMoreElements()) { + let { key, value } = attrsEnum.getNext().QueryInterface( + nsIPropertyElement); + attributes[key] = value; + } + + return attributes; + }, + + form() { + return { + actor: this.actorID, + role: this.role, + name: this.name, + value: this.value, + description: this.description, + help: this.help, + keyboardShortcut: this.keyboardShortcut, + childCount: this.childCount, + domNodeType: this.domNodeType, + walker: this.walker.form() + }; + } +}); + +/** + * The AccessibleWalkerActor stores a cache of AccessibleActors that represent + * accessible objects in a given document. + * + * It is also responsible for implicitely initializing and shutting down + * accessibility engine by storing a reference to the XPCOM accessibility + * service. + */ +const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { + initialize(conn, tabActor) { + Actor.prototype.initialize.call(this, conn); + this.tabActor = tabActor; + this.rootWin = tabActor.window; + this.rootDoc = tabActor.window.document; + this.refMap = new Map(); + // Accessibility Walker should only be considered ready, when raw accessible + // object for root document is fully initialized (e.g. does not have a + // 'busy' state) + this.readyDeferred = defer(); + + DevToolsUtils.defineLazyGetter(this, "a11yService", () => { + Services.obs.addObserver(this, "accessible-event"); + return Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService); + }); + + this.onLoad = this.onLoad.bind(this); + this.onUnload = this.onUnload.bind(this); + + events.on(tabActor, "will-navigate", this.onUnload); + events.on(tabActor, "window-ready", this.onLoad); + }, + + onUnload({ window }) { + let doc = window.document; + let actor = this.getRef(doc); + + // If an accessible actor was never created for document, then there's + // nothing to clean up. + if (!actor) { + return; + } + + // Purge document's subtree from accessible actors cache. + this.purgeSubtree(this.a11yService.getAccessibleFor(this.doc)); + // If document is a root document, clear it's reference and cache. + if (this.rootDoc === doc) { + this.rootDoc = null; + this.refMap.clear(); + this.readyDeferred = defer(); + } + }, + + onLoad({ window, isTopLevel }) { + if (isTopLevel) { + // If root document is dead, unload it and clean up. + if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) && + this.rootDoc.defaultView) { + this.onUnload({ window: this.rootDoc.defaultView }); + } + + this.rootWin = window; + this.rootDoc = window.document; + } + }, + + destroy() { + if (this._destroyed) { + return; + } + + this._destroyed = true; + + try { + Services.obs.removeObserver(this, "accessible-event"); + } catch (e) { + // Accessible event observer might not have been initialized if a11y + // service was never used. + } + + // Clean up accessible actors cache. + if (this.refMap.size > 0) { + this.purgeSubtree(this.a11yService.getAccessibleFor(this.rootDoc)); + this.refMap.clear(); + } + + events.off(this.tabActor, "will-navigate", this.onUnload); + events.off(this.tabActor, "window-ready", this.onLoad); + + this.onLoad = null; + this.onUnload = null; + delete this.a11yService; + this.tabActor = null; + this.rootDoc = null; + this.refMap = null; + + Actor.prototype.destroy.call(this); + }, + + getRef(rawAccessible) { + return this.refMap.get(rawAccessible); + }, + + addRef(rawAccessible) { + let actor = this.refMap.get(rawAccessible); + if (actor) { + return actor; + } + + actor = new AccessibleActor(this, rawAccessible); + this.manage(actor); + this.refMap.set(rawAccessible, actor); + + return actor; + }, + + /** + * Clean up accessible actors cache for a given accessible's subtree. + * + * @param {nsIAccessible} rawAccessible + */ + purgeSubtree(rawAccessible) { + let actor = this.getRef(rawAccessible); + if (actor && rawAccessible && !actor.isDefunct) { + for (let child = rawAccessible.firstChild; child; child = child.nextSibling) { + this.purgeSubtree(child); + } + } + + this.refMap.delete(rawAccessible); + + if (actor) { + events.emit(this, "accessible-destroy", actor); + actor.destroy(); + } + }, + + /** + * A helper method. Accessibility walker is assumed to have only 1 child which + * is the top level document. + */ + children() { + return Promise.all([this.getDocument()]); + }, + + /** + * A promise for a root document accessible actor that only resolves when its + * corresponding document accessible object is fully loaded. + * + * @return {Promise} + */ + getDocument() { + let doc = this.addRef(this.a11yService.getAccessibleFor(this.rootDoc)); + let states = doc.getState(); + + if (states.includes("busy")) { + return this.readyDeferred.promise.then(() => doc); + } + + this.readyDeferred.resolve(); + return Promise.resolve(doc); + }, + + getAccessibleFor(domNode) { + // We need to make sure that the document is loaded processed by a11y first. + return this.getDocument().then(() => + this.addRef(this.a11yService.getAccessibleFor(domNode.rawNode))); + }, + + /** + * Accessible event observer function. + * + * @param {nsIAccessibleEvent} subject + * accessible event object. + */ + observe(subject) { + let event = subject.QueryInterface(nsIAccessibleEvent); + let rawAccessible = event.accessible; + let accessible = this.getRef(rawAccessible); + + switch (event.eventType) { + case EVENT_STATE_CHANGE: + let { state, isEnabled } = event.QueryInterface(nsIAccessibleStateChangeEvent); + let states = [...this.a11yService.getStringStates(state, 0)]; + + if (states.includes("busy") && !isEnabled) { + let { DOMNode } = event; + // If debugging chrome, wait for top level content document loaded, + // otherwise wait for root document loaded. + if (DOMNode == this.rootDoc || ( + this.rootDoc.documentElement.namespaceURI === XUL_NS && + this.rootWin.gBrowser.selectedBrowser.contentDocument == DOMNode)) { + this.readyDeferred.resolve(); + } + } + + if (accessible) { + // Only propagate state change events for active accessibles. + if (states.includes("busy") && isEnabled) { + return; + } + events.emit(accessible, "state-change", accessible.getState()); + } + + break; + case EVENT_NAME_CHANGE: + if (accessible) { + events.emit(accessible, "name-change", rawAccessible.name, + event.DOMNode == this.rootDoc ? + undefined : this.getRef(rawAccessible.parent)); + } + break; + case EVENT_VALUE_CHANGE: + if (accessible) { + events.emit(accessible, "value-change", rawAccessible.value); + } + break; + case EVENT_DESCRIPTION_CHANGE: + if (accessible) { + events.emit(accessible, "description-change", rawAccessible.description); + } + break; + case EVENT_HELP_CHANGE: + if (accessible) { + events.emit(accessible, "help-change", rawAccessible.help); + } + break; + case EVENT_REORDER: + if (accessible) { + events.emit(accessible, "reorder", rawAccessible.childCount); + } + break; + case EVENT_HIDE: + this.purgeSubtree(rawAccessible); + break; + case EVENT_DEFACTION_CHANGE: + case EVENT_ACTION_CHANGE: + if (accessible) { + events.emit(accessible, "actions-change", accessible.getActions()); + } + break; + case EVENT_TEXT_CHANGED: + case EVENT_TEXT_INSERTED: + case EVENT_TEXT_REMOVED: + if (accessible) { + events.emit(accessible, "text-change"); + } + break; + case EVENT_DOCUMENT_ATTRIBUTES_CHANGED: + case EVENT_OBJECT_ATTRIBUTE_CHANGED: + case EVENT_TEXT_ATTRIBUTE_CHANGED: + if (accessible) { + events.emit(accessible, "attributes-change", accessible.getAttributes()); + } + break; + case EVENT_ACCELERATOR_CHANGE: + if (accessible) { + events.emit(accessible, "shortcut-change", rawAccessible.keyboardShortcut); + } + break; + default: + break; + } + } +}); + +/** + * The AccessibilityActor is a top level container actor that initializes + * accessible walker and is the top-most point of interaction for accessibility + * tools UI. + */ +const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { + initialize(conn, tabActor) { + Actor.prototype.initialize.call(this, conn); + this.tabActor = tabActor; + }, + + getWalker() { + if (!this.walker) { + this.walker = new AccessibleWalkerActor(this.conn, this.tabActor); + } + return this.walker; + }, + + destroy() { + Actor.prototype.destroy.call(this); + this.walker.destroy(); + this.walker = null; + this.tabActor = null; + } +}); + +exports.AccessibleActor = AccessibleActor; +exports.AccessibleWalkerActor = AccessibleWalkerActor; +exports.AccessibilityActor = AccessibilityActor; diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build index 5e0e704a9048..1da8a9c94215 100644 --- a/devtools/server/actors/moz.build +++ b/devtools/server/actors/moz.build @@ -11,6 +11,7 @@ DIRS += [ ] DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addon.js', 'addons.js', diff --git a/devtools/server/main.js b/devtools/server/main.js index ee3ac2cfa7cd..a1fef4b0d5f6 100644 --- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -585,6 +585,11 @@ var DebuggerServer = { constructor: "WebExtensionInspectedWindowActor", type: { tab: true } }); + this.registerModule("devtools/server/actors/accessibility", { + prefix: "accessibility", + constructor: "AccessibilityActor", + type: { tab: true } + }); }, /** diff --git a/devtools/server/tests/browser/browser.ini b/devtools/server/tests/browser/browser.ini index 34c72407c883..93530b3ae743 100644 --- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -4,6 +4,7 @@ subsuite = devtools support-files = head.js animation.html + doc_accessibility.html doc_allocations.html doc_force_cc.html doc_force_gc.html @@ -25,6 +26,10 @@ support-files = storage-helpers.js !/devtools/server/tests/mochitest/hello-actor.js +[browser_accessibility_node_events.js] +[browser_accessibility_node.js] +[browser_accessibility_simple.js] +[browser_accessibility_walker.js] [browser_animation_emitMutations.js] [browser_animation_getFrames.js] [browser_animation_getProperties.js] diff --git a/devtools/server/tests/browser/browser_accessibility_node.js b/devtools/server/tests/browser/browser_accessibility_node.js new file mode 100644 index 000000000000..6dd16da6bf56 --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_node.js @@ -0,0 +1,74 @@ +/* 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/. */ + +"use strict"; + +// Checks for the AccessibleActor + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton", + value: "", + description: "Accessibility Test", + help: "", + keyboardShortcut: "", + childCount: 1, + domNodeType: 1 + }); + + info("Actions"); + let actions = yield accessibleFront.getActions(); + is(actions.length, 1, "Accessible Front has correct number of actions"); + is(actions[0], "Press", "Accessible Front default action is correct"); + + info("Index in parent"); + let index = yield accessibleFront.getIndexInParent(); + is(index, 1, "Accessible Front has correct index in parent"); + + info("State"); + let state = yield accessibleFront.getState(); + SimpleTest.isDeeply(state, + ["focusable", "selectable text", "opaque", "enabled", "sensitive"], + "Accessible Front has correct states"); + + info("Attributes"); + let attributes = yield accessibleFront.getAttributes(); + SimpleTest.isDeeply(attributes, { + "margin-top": "0px", + display: "inline-block", + "text-align": "center", + "text-indent": "0px", + "margin-left": "0px", + tag: "button", + "margin-right": "0px", + id: "button", + "margin-bottom": "0px" + }, "Accessible Front has correct attributes"); + + info("Children"); + let children = yield accessibleFront.children(); + is(children.length, 1, "Accessible Front has correct number of children"); + checkA11yFront(children[0], { + name: "Accessible Button", + role: "text leaf" + }); + + info("DOM Node"); + let node = yield accessibleFront.getDOMNode(walker); + is(node, buttonNode, "Accessible Front has correct DOM node"); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_node_events.js b/devtools/server/tests/browser/browser_accessibility_node_events.js new file mode 100644 index 000000000000..2ae6167e286d --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_node_events.js @@ -0,0 +1,93 @@ +/* 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/. */ + +"use strict"; + +// Checks for the AccessibleActor events + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + let a11yDoc = yield a11yWalker.getDocument(); + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + let sliderNode = yield walker.querySelector(walker.rootNode, "#slider"); + let accessibleSliderFront = yield a11yWalker.getAccessibleFor(sliderNode); + let browser = gBrowser.selectedBrowser; + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton", + value: "", + description: "Accessibility Test", + help: "", + keyboardShortcut: "", + childCount: 1, + domNodeType: 1 + }); + + info("Name change event"); + yield emitA11yEvent(accessibleFront, "name-change", + (name, parent) => { + checkA11yFront(accessibleFront, { name: "Renamed" }); + checkA11yFront(parent, { }, a11yDoc); + }, () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute( + "aria-label", "Renamed"))); + + info("Description change event"); + yield emitA11yEvent(accessibleFront, "description-change", + () => checkA11yFront(accessibleFront, { description: "" }), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").removeAttribute("aria-describedby"))); + + info("State change event"); + let states = yield accessibleFront.getState(); + let expectedStates = ["unavailable", "selectable text", "opaque"]; + SimpleTest.isDeeply(states, ["focusable", "selectable text", "opaque", + "enabled", "sensitive"], "States are correct"); + yield emitA11yEvent(accessibleFront, "state-change", + newStates => SimpleTest.isDeeply(newStates, expectedStates, + "States are updated"), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute("disabled", true))); + states = yield accessibleFront.getState(); + SimpleTest.isDeeply(states, expectedStates, "States are updated"); + + info("Attributes change event"); + let attrs = yield accessibleFront.getAttributes(); + ok(!attrs.live, "Attribute is not present"); + yield emitA11yEvent(accessibleFront, "attributes-change", + newAttrs => is(newAttrs.live, "polite", "Attributes are updated"), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute("aria-live", "polite"))); + attrs = yield accessibleFront.getAttributes(); + is(attrs.live, "polite", "Attributes are updated"); + + info("Value change event"); + checkA11yFront(accessibleSliderFront, { value: "5" }); + yield emitA11yEvent(accessibleSliderFront, "value-change", + () => checkA11yFront(accessibleSliderFront, { value: "6" }), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("slider").setAttribute("aria-valuenow", "6"))); + + info("Reorder event"); + is(accessibleSliderFront.childCount, 1, "Slider has only 1 child"); + yield emitA11yEvent(accessibleSliderFront, "reorder", + childCount => is(childCount, 2, "Child count is updated"), + () => ContentTask.spawn(browser, null, () => { + let button = content.document.createElement("button"); + button.innerText = "Slider button"; + content.document.getElementById("slider").appendChild(button); + })); + is(accessibleSliderFront.childCount, 2, "Child count is updated"); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_simple.js b/devtools/server/tests/browser/browser_accessibility_simple.js new file mode 100644 index 000000000000..ef33b095c5ec --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_simple.js @@ -0,0 +1,21 @@ +/* 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/. */ + +"use strict"; + +// Simple checks for the AccessibilityActor and AccessibleWalkerActor + +add_task(function* () { + let {client, accessibility} = yield initAccessibilityFrontForUrl( + "data:text/html;charset=utf-8,test
"); + + ok(accessibility, "The AccessibilityFront was created"); + ok(accessibility.getWalker, "The getWalker method exists"); + + let a11yWalker = yield accessibility.getWalker(); + ok(a11yWalker, "The AccessibleWalkerFront was returned"); + + yield client.close(); + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/browser_accessibility_walker.js b/devtools/server/tests/browser/browser_accessibility_walker.js new file mode 100644 index 000000000000..0cafa19747e3 --- /dev/null +++ b/devtools/server/tests/browser/browser_accessibility_walker.js @@ -0,0 +1,75 @@ +/* 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/. */ + +"use strict"; + +// Checks for the AccessibleWalkerActor + +add_task(function* () { + let {client, walker, accessibility} = + yield initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html"); + + let a11yWalker = yield accessibility.getWalker(walker); + ok(a11yWalker, "The AccessibleWalkerFront was returned"); + + let a11yDoc = yield a11yWalker.getDocument(); + ok(a11yDoc, "The AccessibleFront for root doc is created"); + + let children = yield a11yWalker.children(); + is(children.length, 1, + "AccessibleWalker only has 1 child - root doc accessible"); + is(a11yDoc, children[0], + "Root accessible must be AccessibleWalker's only child"); + + let buttonNode = yield walker.querySelector(walker.rootNode, "#button"); + let accessibleFront = yield a11yWalker.getAccessibleFor(buttonNode); + + checkA11yFront(accessibleFront, { + name: "Accessible Button", + role: "pushbutton" + }); + + let browser = gBrowser.selectedBrowser; + + // Ensure name-change event is emitted by walker when cached accessible's name + // gets updated (via DOM manipularion). + yield emitA11yEvent(a11yWalker, "name-change", + (front, parent) => { + checkA11yFront(front, { name: "Renamed" }, accessibleFront); + checkA11yFront(parent, { }, a11yDoc); + }, + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").setAttribute( + "aria-label", "Renamed"))); + + // Ensure reorder event is emitted by walker when DOM tree changes. + let docChildren = yield a11yDoc.children(); + is(docChildren.length, 3, "Root doc should have correct number of children"); + + yield emitA11yEvent(a11yWalker, "reorder", + front => checkA11yFront(front, { }, a11yDoc), + () => ContentTask.spawn(browser, null, () => { + let input = content.document.createElement("input"); + input.type = "text"; + input.title = "This is a tooltip"; + input.value = "New input"; + content.document.body.appendChild(input); + })); + + docChildren = yield a11yDoc.children(); + is(docChildren.length, 4, "Root doc should have correct number of children"); + + // Ensure destory event is emitted by walker when cached accessible's raw + // accessible gets destroyed. + yield emitA11yEvent(a11yWalker, "accessible-destroy", + destroyedFront => checkA11yFront(destroyedFront, { }, accessibleFront), + () => ContentTask.spawn(browser, null, () => + content.document.getElementById("button").remove())); + + let a11yShutdown = waitForA11yShutdown(); + yield client.close(); + forceCollections(); + yield a11yShutdown; + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/server/tests/browser/doc_accessibility.html b/devtools/server/tests/browser/doc_accessibility.html new file mode 100644 index 000000000000..ca44efdc2f65 --- /dev/null +++ b/devtools/server/tests/browser/doc_accessibility.html @@ -0,0 +1,12 @@ + + + + + + +

Accessibility Test

+ +
slider
+ + diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js index fc2bb47f477d..03b0791bac31 100644 --- a/devtools/server/tests/browser/head.js +++ b/devtools/server/tests/browser/head.js @@ -80,6 +80,22 @@ function* initLayoutFrontForUrl(url) { return {inspector, walker, layout, client}; } +function* initAccessibilityFrontForUrl(url) { + const {AccessibilityFront} = require("devtools/shared/fronts/accessibility"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + + yield addTab(url); + + initDebuggerServer(); + let client = new DebuggerClient(DebuggerServer.connectPipe()); + let form = yield connectDebuggerClient(client); + let inspector = InspectorFront(client, form); + let walker = yield inspector.getWalker(); + let accessibility = AccessibilityFront(client, form); + + return {inspector, walker, accessibility, client}; +} + function initDebuggerServer() { try { // Sometimes debugger server does not get destroyed correctly by previous @@ -236,3 +252,53 @@ function waitForMarkerType(front, types, predicate, function getCookieId(name, domain, path) { return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`; } + +/** + * Trigger DOM activity and wait for the corresponding accessibility event. + * @param {Object} emitter Devtools event emitter, usually a front. + * @param {Sting} name Accessibility event in question. + * @param {Function} handler Accessibility event handler function with checks. + * @param {Promise} task A promise that resolves when DOM activity is done. + */ +async function emitA11yEvent(emitter, name, handler, task) { + let promise = emitter.once(name, handler); + await task(); + await promise; +} + +/** + * Check that accessibilty front is correct and its attributes are also + * up-to-date. + * @param {Object} front Accessibility front to be tested. + * @param {Object} expected A map of a11y front properties to be verified. + * @param {Object} expectedFront Expected accessibility front. + */ +function checkA11yFront(front, expected, expectedFront) { + ok(front, "The accessibility front is created"); + + if (expectedFront) { + is(front, expectedFront, "Matching accessibility front"); + } + + for (let key in expected) { + is(front[key], expected[key], `accessibility front has correct ${key}`); + } +} + +/** + * Wait for accessibility service to shut down. We consider it shut down when + * an "a11y-init-or-shutdown" event is received with a value of "0". + */ +async function waitForA11yShutdown() { + await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => + new Promise(resolve => { + let observe = (subject, topic, data) => { + Services.obs.removeObserver(observe, "a11y-init-or-shutdown"); + + if (data === "0") { + resolve(); + } + }; + Services.obs.addObserver(observe, "a11y-init-or-shutdown"); + })); +} diff --git a/devtools/shared/fronts/accessibility.js b/devtools/shared/fronts/accessibility.js new file mode 100644 index 000000000000..8497ea11af6c --- /dev/null +++ b/devtools/shared/fronts/accessibility.js @@ -0,0 +1,136 @@ +/* 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/. */ +"use strict"; + +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const { + Front, + FrontClassWithSpec, + preEvent, + types +} = require("devtools/shared/protocol.js"); +const { + accessibleSpec, + accessibleWalkerSpec, + accessibilitySpec +} = require("devtools/shared/specs/accessibility"); + +const events = require("devtools/shared/event-emitter"); +const ACCESSIBLE_PROPERTIES = [ + "role", + "name", + "value", + "description", + "help", + "keyboardShortcut", + "childCount", + "domNodeType" +]; + +const AccessibleFront = FrontClassWithSpec(accessibleSpec, { + initialize(client, form) { + Front.prototype.initialize.call(this, client, form); + + // Define getters for accesible properties that are received from the actor. + // Note: we would like accessible properties to be iterable for a11y + // clients. + for (let key of ACCESSIBLE_PROPERTIES) { + Object.defineProperty(this, key, { + get() { + return this._form[key]; + }, + enumerable: true + }); + } + }, + + marshallPool() { + return this.walker; + }, + + form(form, detail) { + if (detail === "actorid") { + this.actorID = form; + return; + } + + this.actorID = form.actor; + this._form = form; + DevToolsUtils.defineLazyGetter(this, "walker", () => + types.getType("accessiblewalker").read(this._form.walker, this)); + }, + + /** + * Get a dom node front from accessible actor's raw accessible object's + * DONNode property. + */ + getDOMNode(domWalker) { + return domWalker.getNodeFromActor(this.actorID, + ["rawAccessible", "DOMNode"]); + }, + + nameChange: preEvent("name-change", function (name, parent) { + this._form.name = name; + // Name change event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "name-change", this, parent); + } + }), + + valueChange: preEvent("value-change", function (value) { + this._form.value = value; + }), + + descriptionChange: preEvent("description-change", function (description) { + this._form.description = description; + }), + + helpChange: preEvent("help-change", function (help) { + this._form.help = help; + }), + + shortcutChange: preEvent("shortcut-change", function (keyboardShortcut) { + this._form.keyboardShortcut = keyboardShortcut; + }), + + reorder: preEvent("reorder", function (childCount) { + this._form.childCount = childCount; + // Reorder event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "reorder", this); + } + }), + + textChange: preEvent("text-change", function () { + // Text event affects the tree rendering, we fire this event on + // accessibility walker as the point of interaction for UI. + if (this.walker) { + events.emit(this.walker, "text-change", this); + } + }) +}); + +const AccessibleWalkerFront = FrontClassWithSpec(accessibleWalkerSpec, { + accessibleDestroy: preEvent("accessible-destroy", function (accessible) { + accessible.destroy(); + }), + + form(json) { + this.actorID = json.actor; + } +}); + +const AccessibilityFront = FrontClassWithSpec(accessibilitySpec, { + initialize(client, form) { + Front.prototype.initialize.call(this, client, form); + this.actorID = form.accessibilityActor; + this.manage(this); + } +}); + +exports.AccessibleFront = AccessibleFront; +exports.AccessibleWalkerFront = AccessibleWalkerFront; +exports.AccessibilityFront = AccessibilityFront; diff --git a/devtools/shared/fronts/moz.build b/devtools/shared/fronts/moz.build index d8d823db9026..fac715128d8e 100644 --- a/devtools/shared/fronts/moz.build +++ b/devtools/shared/fronts/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addons.js', 'animation.js', diff --git a/devtools/shared/specs/accessibility.js b/devtools/shared/specs/accessibility.js new file mode 100644 index 000000000000..f44c156f03f1 --- /dev/null +++ b/devtools/shared/specs/accessibility.js @@ -0,0 +1,141 @@ +/* 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/. */ + +"use strict"; + +const protocol = require("devtools/shared/protocol"); +const { Arg, generateActorSpec, RetVal, types } = protocol; +// eslint-disable-next-line no-unused-vars +const { nodeSpec } = require("devtools/shared/specs/inspector"); + +types.addActorType("accessible"); + +const accessibleSpec = generateActorSpec({ + typeName: "accessible", + + events: { + "actions-change": { + type: "actionsChange", + actions: Arg(0, "array:string") + }, + "name-change": { + type: "nameChange", + name: Arg(0, "string"), + parent: Arg(1, "nullable:accessible") + }, + "value-change": { + type: "valueChange", + value: Arg(0, "string") + }, + "description-change": { + type: "descriptionChange", + description: Arg(0, "string") + }, + "state-change": { + type: "stateChange", + states: Arg(0, "array:string") + }, + "attributes-change": { + type: "attributesChange", + states: Arg(0, "json") + }, + "help-change": { + type: "helpChange", + help: Arg(0, "string") + }, + "shortcut-change": { + type: "shortcutChange", + shortcut: Arg(0, "string") + }, + "reorder": { + type: "reorder", + childCount: Arg(0, "number") + }, + "text-change": { + type: "textChange" + } + }, + + methods: { + getActions: { + request: {}, + response: { + actions: RetVal("array:string") + } + }, + getIndexInParent: { + request: {}, + response: { + indexInParent: RetVal("number") + } + }, + getState: { + request: {}, + response: { + states: RetVal("array:string") + } + }, + getAttributes: { + request: {}, + response: { + attributes: RetVal("json") + } + }, + children: { + request: {}, + response: { + children: RetVal("array:accessible") + } + } + } +}); + +const accessibleWalkerSpec = generateActorSpec({ + typeName: "accessiblewalker", + + events: { + "accessible-destroy": { + type: "accessibleDestroy", + accessible: Arg(0, "accessible") + } + }, + + methods: { + children: { + request: {}, + response: { + children: RetVal("array:accessible") + } + }, + getDocument: { + request: {}, + response: { + document: RetVal("accessible") + } + }, + getAccessibleFor: { + request: { node: Arg(0, "domnode") }, + response: { + accessible: RetVal("accessible") + } + } + } +}); + +const accessibilitySpec = generateActorSpec({ + typeName: "accessibility", + + methods: { + getWalker: { + request: {}, + response: { + walker: RetVal("accessiblewalker") + } + } + } +}); + +exports.accessibleSpec = accessibleSpec; +exports.accessibleWalkerSpec = accessibleWalkerSpec; +exports.accessibilitySpec = accessibilitySpec; diff --git a/devtools/shared/specs/moz.build b/devtools/shared/specs/moz.build index 4262f4123953..ffb8f1ab65f8 100644 --- a/devtools/shared/specs/moz.build +++ b/devtools/shared/specs/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'accessibility.js', 'actor-registry.js', 'addons.js', 'animation.js', From 71d6942bcbe6b9c9a3a447c10e3facf3d673df59 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 1 Sep 2017 00:53:33 -0400 Subject: [PATCH 51/51] Bug 1395650. Make anonymous column groups into non-inheriting anon boxes, to better match the behavior of other browsers. r=heycam --- layout/base/GeckoRestyleManager.cpp | 64 +++++++++++++------------ layout/base/GeckoRestyleManager.h | 10 ++++ layout/base/ServoRestyleManager.cpp | 53 ++++++++++---------- layout/base/ServoRestyleManager.h | 11 +++++ layout/base/nsCSSFrameConstructor.cpp | 16 +++++-- layout/reftests/bugs/1395650-1-ref.html | 4 ++ layout/reftests/bugs/1395650-1.html | 6 +++ layout/reftests/bugs/reftest.list | 1 + layout/style/nsCSSAnonBoxList.h | 2 +- layout/tables/nsTableFrame.cpp | 24 +--------- 10 files changed, 109 insertions(+), 82 deletions(-) create mode 100644 layout/reftests/bugs/1395650-1-ref.html create mode 100644 layout/reftests/bugs/1395650-1.html diff --git a/layout/base/GeckoRestyleManager.cpp b/layout/base/GeckoRestyleManager.cpp index 610de53f5b44..45f9cb03b2b1 100644 --- a/layout/base/GeckoRestyleManager.cpp +++ b/layout/base/GeckoRestyleManager.cpp @@ -1024,16 +1024,13 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) #endif if (!newParentContext && !oldContext->GetParent()) { - // No need to do anything here. -#ifdef DEBUG - // Make sure we have no children, so we really know there is nothing to do. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - MOZ_ASSERT(lists.CurrentList().IsEmpty(), - "Failing to reparent style context for child of " - "non-inheriting anon box"); - } -#endif // DEBUG + // No need to do anything here for this frame, but we should still reparent + // its descendants, because those may have styles that inherit from the + // parent of this frame (e.g. non-anonymous columns in an anonymous + // colgroup). + MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(), + "Why did this frame not end up with a parent context?"); + ReparentFrameDescendants(aFrame, providerChild); return NS_OK; } @@ -1091,26 +1088,7 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) aFrame->SetStyleContext(newContext); - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - for (nsIFrame* child : lists.CurrentList()) { - // only do frames that are in flow - if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && - child != providerChild) { -#ifdef DEBUG - if (child->IsPlaceholderFrame()) { - nsIFrame* outOfFlowFrame = - nsPlaceholderFrame::GetRealFrameForPlaceholder(child); - NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame"); - - NS_ASSERTION(outOfFlowFrame != providerChild, - "Out of flow provider?"); - } -#endif - ReparentStyleContext(child); - } - } - } + ReparentFrameDescendants(aFrame, providerChild); // If this frame is part of an IB split, then the style context of // the next part of the split might be a child of our style context. @@ -1154,6 +1132,32 @@ GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) return NS_OK; } +void +GeckoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame, + nsIFrame* aProviderChild) +{ + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // only do frames that are in flow + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + child != aProviderChild) { +#ifdef DEBUG + if (child->IsPlaceholderFrame()) { + nsIFrame* outOfFlowFrame = + nsPlaceholderFrame::GetRealFrameForPlaceholder(child); + NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame"); + + NS_ASSERTION(outOfFlowFrame != aProviderChild, + "Out of flow provider?"); + } +#endif + ReparentStyleContext(child); + } + } + } +} + ElementRestyler::ElementRestyler(nsPresContext* aPresContext, nsIFrame* aFrame, nsStyleChangeList* aChangeList, diff --git a/layout/base/GeckoRestyleManager.h b/layout/base/GeckoRestyleManager.h index d8d7e9268f2f..086ef04822e1 100644 --- a/layout/base/GeckoRestyleManager.h +++ b/layout/base/GeckoRestyleManager.h @@ -88,6 +88,16 @@ public: */ nsresult ReparentStyleContext(nsIFrame* aFrame); +private: + /** + * Reparent the descendants of aFrame. This is used by ReparentStyleContext + * and shouldn't be called by anyone else. aProviderChild, if non-null, is a + * child that was the style parent for aFrame and hence shouldn't be + * reparented. + */ + void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild); + +public: void ClearSelectors() { mPendingRestyles.ClearSelectors(); } diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 2949b522c084..076df1e8f698 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -1488,20 +1488,6 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, nsIFrame* providerFrame; nsStyleContext* newParentContext = aFrame->GetParentStyleContext(&providerFrame); - if (!newParentContext) { - // No need to do anything here. -#ifdef DEBUG - // Make sure we have no children, so we really know there is nothing to do. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - MOZ_ASSERT(lists.CurrentList().IsEmpty(), - "Failing to reparent style context for child of " - "non-inheriting anon box"); - } -#endif // DEBUG - return; - } - // If our provider is our child, we want to reparent it first, because we // inherit style from it. bool isChild = providerFrame && providerFrame->GetParent() == aFrame; @@ -1516,6 +1502,17 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, "Out of flow provider?"); } + if (!newParentContext) { + // No need to do anything here for this frame, but we should still reparent + // its descendants, because those may have styles that inherit from the + // parent of this frame (e.g. non-anonymous columns in an anonymous + // colgroup). + MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(), + "Why did this frame not end up with a parent context?"); + ReparentFrameDescendants(aFrame, providerChild, aStyleSet); + return; + } + bool isElement = aFrame->GetContent()->IsElement(); // We probably don't want to initiate transitions from @@ -1605,19 +1602,27 @@ ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame, // reparenting the table wrapper frame. So no need to // UpdateStyleOfOwnedAnonBoxes() here. - nsIFrame::ChildListIterator lists(aFrame); - for (; !lists.IsDone(); lists.Next()) { - for (nsIFrame* child : lists.CurrentList()) { - // only do frames that are in flow - if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && - child != providerChild) { - DoReparentStyleContext(child, aStyleSet); - } - } - } + ReparentFrameDescendants(aFrame, providerChild, aStyleSet); // We do not need to do the equivalent of UpdateFramePseudoElementStyles, // because those are hadled by our descendant walk. } +void +ServoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame, + nsIFrame* aProviderChild, + ServoStyleSet& aStyleSet) +{ + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // only do frames that are in flow + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + child != aProviderChild) { + DoReparentStyleContext(child, aStyleSet); + } + } + } +} + } // namespace mozilla diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 51be8dabae55..f9ac36fadb3b 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -221,6 +221,17 @@ public: // this method accordingly (e.g. to ReparentStyleContextForFirstLine). nsresult ReparentStyleContext(nsIFrame* aFrame); +private: + /** + * Reparent the descendants of aFrame. This is used by ReparentStyleContext + * and shouldn't be called by anyone else. aProviderChild, if non-null, is a + * child that was the style parent for aFrame and hence shouldn't be + * reparented. + */ + void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild, + ServoStyleSet& aStyleSet); + +public: /** * Clears the ServoElementData and HasDirtyDescendants from all elements * in the subtree rooted at aElement. diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index e957f0aac5b2..54a809f76254 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -10247,7 +10247,8 @@ nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = { FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS | FCDATA_SKIP_ABSPOS_PUSH | - FCDATA_IS_WRAPPER_ANON_BOX | + // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to + // restyle these: they have non-inheriting style contexts. FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), NS_NewTableColGroupFrame), &nsCSSAnonBoxes::tableColGroup @@ -10886,9 +10887,16 @@ nsCSSFrameConstructor::WrapItemsInPseudoParent(nsIContent* aParentContent, pseudoType = nsCSSAnonBoxes::inlineTable; } - already_AddRefed wrapperStyle = - mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType, - aParentStyle); + already_AddRefed wrapperStyle; + if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) { + wrapperStyle = + mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType, + aParentStyle); + } else { + wrapperStyle = + mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(pseudoType); + } + FrameConstructionItem* newItem = new FrameConstructionItem(&pseudoData.mFCData, // Use the content of our parent frame diff --git a/layout/reftests/bugs/1395650-1-ref.html b/layout/reftests/bugs/1395650-1-ref.html new file mode 100644 index 000000000000..a3c22b280e90 --- /dev/null +++ b/layout/reftests/bugs/1395650-1-ref.html @@ -0,0 +1,4 @@ + + + +
This text should be visible
diff --git a/layout/reftests/bugs/1395650-1.html b/layout/reftests/bugs/1395650-1.html new file mode 100644 index 000000000000..1ad68348b36f --- /dev/null +++ b/layout/reftests/bugs/1395650-1.html @@ -0,0 +1,6 @@ + + + + + +
This text should be visible
diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index daedfce6217a..db9de2f259bb 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -2038,3 +2038,4 @@ needs-focus != 1377447-1.html 1377447-2.html == 1384065.html 1384065-ref.html == 1384275-1.html 1384275-1-ref.html == 1381821.html 1381821-ref.html +== 1395650-1.html 1395650-1-ref.html diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h index f79aa51beadb..e86726a451a2 100644 --- a/layout/style/nsCSSAnonBoxList.h +++ b/layout/style/nsCSSAnonBoxList.h @@ -81,7 +81,7 @@ CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content") CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table") CSS_WRAPPER_ANON_BOX(table, ":-moz-table") CSS_WRAPPER_ANON_BOX(tableCell, ":-moz-table-cell") -CSS_WRAPPER_ANON_BOX(tableColGroup, ":-moz-table-column-group") +CSS_NON_INHERITING_ANON_BOX(tableColGroup, ":-moz-table-column-group") CSS_NON_INHERITING_ANON_BOX(tableCol, ":-moz-table-column") CSS_ANON_BOX(tableWrapper, ":-moz-table-wrapper") CSS_WRAPPER_ANON_BOX(tableRowGroup, ":-moz-table-row-group") diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 961669771104..64487781027b 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -688,8 +688,7 @@ nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) RefPtr colGroupStyle; colGroupStyle = shell->StyleSet()-> - ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, - mStyleContext); + ResolveNonInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup); // Create a col group frame nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); @@ -8051,27 +8050,6 @@ nsTableFrame::AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) "What happened to our parent?"); aResult.AppendElement( OwnedAnonBox(wrapper, &UpdateStyleOfOwnedAnonBoxesForTableWrapper)); - - // We may also have an anonymous colgroup that we're responsible for. - // Specifically, we can have three types of colgroup frames: (A) corresponding - // to actual elements with "display: table-column-group", (B) wrapping runs of - // "display: table-column" kids, and (C) one colgroup frame added at the end - // to hold the anonymous colframes we need so each cell has an associated - // colframe. - // - // These types of colgroups are supposed to correspond to the values of the - // nsTableColGroupType enum: type (A) to eColGroupContent, type (B) to - // eColGroupAnonymousCol, and type (C) to eColGroupAnonymousCell. But we - // never actually set eColGroupAnonymousCol on any colgroups right now; see - // bug 1387568. In any case, eColGroupAnonymousCell works correctly to detect - // colgroups of type (C), which are the ones we want to restyle here. Type - // (A) will be restyled via their element, and type (B) via the machinery for - // restyling wrapper anonymous frames. - auto colGroupFrame = - static_cast(mColGroups.LastChild()); - if (colGroupFrame && colGroupFrame->GetColType() == eColGroupAnonymousCell) { - aResult.AppendElement(colGroupFrame); - } } /* static */ void