diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 60915284a848..0f3610de9e83 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -60,6 +60,7 @@ #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include "mozilla/Unused.h" +#include "Units.h" #include "nsBrowserStatusFilter.h" #include "nsContentUtils.h" #include "nsDocShell.h" @@ -436,7 +437,7 @@ BrowserChild::Observe(nsISupports* aSubject, const char* aTopic, nsCOMPtr subject(do_QueryInterface(aSubject)); nsCOMPtr doc(GetTopLevelDocument()); - if (subject == doc) { + if (subject == doc && doc->IsTopLevelContentDocument()) { RefPtr presShell = doc->GetPresShell(); if (presShell) { presShell->SetIsFirstPaint(true); @@ -2741,7 +2742,7 @@ bool BrowserChild::IsVisible() { } void BrowserChild::UpdateVisibility(bool aForceRepaint) { - bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.mVisible; + bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.IsVisible(); bool isVisible = IsVisible(); if (shouldBeVisible != isVisible) { @@ -3328,6 +3329,17 @@ ScreenIntSize BrowserChild::GetInnerSize() { innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); }; +nsRect BrowserChild::GetVisibleRect() { + bool isForceRendering = mIsTopLevel && mRenderLayers; + if (isForceRendering && !mEffectsInfo.IsVisible()) { + // We are forced to render even though we are not visible. In this case, we + // don't have an accurate visible rect, so we must be conservative. + return nsRect(nsPoint(), CSSPixel::ToAppUnits(mUnscaledInnerSize)); + } else { + return mEffectsInfo.mVisibleRect; + } +} + ScreenIntRect BrowserChild::GetOuterRect() { LayoutDeviceIntRect outerRect = RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale()); diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index f307e28ea2d5..64a30d25a585 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -418,6 +418,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, bool IsTransparent() const { return mIsTransparent; } + const EffectsInfo& GetEffectsInfo() const { return mEffectsInfo; } + void GetMaxTouchPoints(uint32_t* aTouchPoints) { *aTouchPoints = mMaxTouchPoints; } @@ -547,6 +549,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, ScreenIntSize GetInnerSize(); + nsRect GetVisibleRect(); + // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow(). void DoFakeShow(const ShowInfo& aShowInfo); diff --git a/dom/ipc/EffectsInfo.h b/dom/ipc/EffectsInfo.h index 86da4a725145..e7bcbb11848e 100644 --- a/dom/ipc/EffectsInfo.h +++ b/dom/ipc/EffectsInfo.h @@ -7,6 +7,8 @@ #ifndef mozilla_dom_EffectsInfo_h #define mozilla_dom_EffectsInfo_h +#include "nsRect.h" + namespace mozilla { namespace dom { @@ -14,29 +16,37 @@ namespace dom { * An EffectsInfo contains information for a remote browser about the graphical * effects that are being applied to it by ancestor browsers in different * processes. - * - * TODO: This struct currently only reports visibility, and should be extended - * with information on clipping and scaling. */ class EffectsInfo { public: EffectsInfo() { *this = EffectsInfo::FullyHidden(); } - static EffectsInfo FullyVisible() { return EffectsInfo{true}; } - static EffectsInfo FullyHidden() { return EffectsInfo{false}; } + + static EffectsInfo VisibleWithinRect(const nsRect& aVisibleRect, + float aScaleX, float aScaleY) { + return EffectsInfo{aVisibleRect, aScaleX, aScaleY}; + } + static EffectsInfo FullyHidden() { return EffectsInfo{nsRect(), 1.0f, 1.0f}; } bool operator==(const EffectsInfo& aOther) { - return mVisible == aOther.mVisible; + return mVisibleRect == aOther.mVisibleRect && mScaleX == aOther.mScaleX && + mScaleY == aOther.mScaleY; } bool operator!=(const EffectsInfo& aOther) { return !(*this == aOther); } - // If you add new state here, you must also update operator== - bool mVisible; - /* - * TODO: Add information for ancestor scaling and clipping. - */ + bool IsVisible() const { return !mVisibleRect.IsEmpty(); } + + // The visible rect of this browser relative to the root frame. If this is + // empty then the browser can be considered invisible. + nsRect mVisibleRect; + // The desired scale factors to apply to rasterized content to match + // transforms applied in ancestor browsers. + float mScaleX; + float mScaleY; + // If you add new fields here, you must also update operator== private: - explicit EffectsInfo(bool aVisible) : mVisible(aVisible) {} + EffectsInfo(const nsRect& aVisibleRect, float aScaleX, float aScaleY) + : mVisibleRect(aVisibleRect), mScaleX(aScaleX), mScaleY(aScaleY) {} }; } // namespace dom diff --git a/dom/ipc/TabMessageUtils.h b/dom/ipc/TabMessageUtils.h index cb0944976bc0..f058c1ce13eb 100644 --- a/dom/ipc/TabMessageUtils.h +++ b/dom/ipc/TabMessageUtils.h @@ -81,12 +81,16 @@ struct ParamTraits { typedef mozilla::dom::EffectsInfo paramType; static void Write(Message* aMsg, const paramType& aParam) { - WriteParam(aMsg, aParam.mVisible); + WriteParam(aMsg, aParam.mVisibleRect); + WriteParam(aMsg, aParam.mScaleX); + WriteParam(aMsg, aParam.mScaleY); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { - return ReadParam(aMsg, aIter, &aResult->mVisible); + return ReadParam(aMsg, aIter, &aResult->mVisibleRect) && + ReadParam(aMsg, aIter, &aResult->mScaleX) && + ReadParam(aMsg, aIter, &aResult->mScaleY); } }; diff --git a/dom/localstorage/ActorsParent.cpp b/dom/localstorage/ActorsParent.cpp index bcce87ba22ee..f0374271a1a2 100644 --- a/dom/localstorage/ActorsParent.cpp +++ b/dom/localstorage/ActorsParent.cpp @@ -1433,6 +1433,12 @@ class Connection final { void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(Connection); } + QuotaClient* GetQuotaClient() const { + MOZ_ASSERT(mQuotaClient); + + return mQuotaClient; + } + ArchivedOriginScope* GetArchivedOriginScope() const { return mArchivedOriginScope; } @@ -1479,6 +1485,12 @@ class Connection final { nsresult GetCachedStatement(const nsACString& aQuery, CachedStatement* aCachedStatement); + nsresult BeginWriteTransaction(); + + nsresult CommitWriteTransaction(); + + nsresult RollbackWriteTransaction(); + private: // Only created by ConnectionThread. Connection(ConnectionThread* aConnectionThread, const nsACString& aSuffix, @@ -2819,6 +2831,30 @@ class QuotaClient::MatchFunction final : public mozIStorageFunction { NS_DECL_MOZISTORAGEFUNCTION }; +/******************************************************************************* + * Helper classes + ******************************************************************************/ + +class MOZ_STACK_CLASS AutoWriteTransaction final { + Connection* mConnection; + Maybe mShadowDatabaseLock; + bool mShadowWrites; + + public: + explicit AutoWriteTransaction(bool aShadowWrites); + + ~AutoWriteTransaction(); + + nsresult Start(Connection* aConnection); + + nsresult Commit(); + + private: + nsresult LockAndAttachShadowDatabase(Connection* aConnection); + + nsresult DetachShadowDatabaseAndUnlock(); +}; + /******************************************************************************* * Globals ******************************************************************************/ @@ -4342,6 +4378,60 @@ nsresult Connection::GetCachedStatement(const nsACString& aQuery, return NS_OK; } +nsresult Connection::BeginWriteTransaction() { + AssertIsOnConnectionThread(); + MOZ_ASSERT(mStorageConnection); + + CachedStatement stmt; + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult Connection::CommitWriteTransaction() { + AssertIsOnConnectionThread(); + MOZ_ASSERT(mStorageConnection); + + CachedStatement stmt; + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult Connection::RollbackWriteTransaction() { + AssertIsOnConnectionThread(); + MOZ_ASSERT(mStorageConnection); + + CachedStatement stmt; + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // This may fail if SQLite already rolled back the transaction so ignore any + // errors. + Unused << stmt->Execute(); + + return NS_OK; +} + void Connection::ScheduleFlush() { AssertIsOnOwningThread(); MOZ_ASSERT(mWriteOptimizer.HasWrites()); @@ -4497,69 +4587,9 @@ nsresult Connection::FlushOp::DoDatastoreWork() { AssertIsOnConnectionThread(); MOZ_ASSERT(mConnection); - class MOZ_STACK_CLASS AutoDetach final { - nsCOMPtr mConnection; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + AutoWriteTransaction autoWriteTransaction(mShadowWrites); - public: - explicit AutoDetach( - mozIStorageConnection* aConnection MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : mConnection(aConnection) { - MOZ_ASSERT(aConnection); - - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - ~AutoDetach() { - if (mConnection) { - nsresult rv = DetachShadowDatabase(mConnection); - Unused << NS_WARN_IF(NS_FAILED(rv)); - } - } - - void release() { mConnection = nullptr; } - - private: - explicit AutoDetach(const AutoDetach&) = delete; - AutoDetach& operator=(const AutoDetach&) = delete; - AutoDetach& operator=(AutoDetach&&) = delete; - }; - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsCOMPtr storageConnection = - mConnection->StorageConnection(); - MOZ_ASSERT(storageConnection); - - nsresult rv; - - Maybe shadowDatabaseLock; - - Maybe autoDetach; - - if (mShadowWrites) { - MOZ_ASSERT(mConnection->mQuotaClient); - - shadowDatabaseLock.emplace( - mConnection->mQuotaClient->ShadowDatabaseMutex()); - - rv = AttachShadowDatabase(quotaManager->GetBasePath(), storageConnection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - autoDetach.emplace(storageConnection); - } - - CachedStatement stmt; - rv = mConnection->GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); + nsresult rv = autoWriteTransaction.Start(mConnection); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4588,34 +4618,19 @@ nsresult Connection::FlushOp::DoDatastoreWork() { return rv; } - rv = mConnection->GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt); + rv = autoWriteTransaction.Commit(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mShadowWrites) { - autoDetach->release(); - - rv = DetachShadowDatabase(storageConnection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - autoDetach.reset(); - - shadowDatabaseLock.reset(); - } - rv = usageJournalFile->Remove(false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + RefPtr runnable = NS_NewRunnableFunction("dom::localstorage::UpdateUsageRunnable", [origin = mConnection->Origin(), usage]() { @@ -9351,5 +9366,124 @@ QuotaClient::MatchFunction::OnFunctionCall( return NS_OK; } +/******************************************************************************* + * AutoWriteTransaction + ******************************************************************************/ + +AutoWriteTransaction::AutoWriteTransaction(bool aShadowWrites) + : mConnection(nullptr) + , mShadowWrites(aShadowWrites) +{ + AssertIsOnConnectionThread(); + + MOZ_COUNT_CTOR(mozilla::dom::AutoWriteTransaction); +} + +AutoWriteTransaction::~AutoWriteTransaction() { + AssertIsOnConnectionThread(); + + MOZ_COUNT_DTOR(mozilla::dom::AutoWriteTransaction); + + if (mConnection) { + if (NS_FAILED(mConnection->RollbackWriteTransaction())) { + NS_WARNING("Failed to rollback write transaction!"); + } + + if (mShadowWrites && NS_FAILED(DetachShadowDatabaseAndUnlock())) { + NS_WARNING("Failed to detach shadow database!"); + } + } +} + +nsresult AutoWriteTransaction::Start(Connection* aConnection) { + AssertIsOnConnectionThread(); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!mConnection); + + nsresult rv; + + if (mShadowWrites) { + rv = LockAndAttachShadowDatabase(aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aConnection->BeginWriteTransaction(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mConnection = aConnection; + + return NS_OK; +} + +nsresult AutoWriteTransaction::Commit() { + AssertIsOnConnectionThread(); + MOZ_ASSERT(mConnection); + + nsresult rv = mConnection->CommitWriteTransaction(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mShadowWrites) { + rv = DetachShadowDatabaseAndUnlock(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mConnection = nullptr; + + return NS_OK; +} + +nsresult AutoWriteTransaction::LockAndAttachShadowDatabase(Connection* aConnection) { + AssertIsOnConnectionThread(); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!mConnection); + MOZ_ASSERT(mShadowDatabaseLock.isNothing()); + MOZ_ASSERT(mShadowWrites); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsCOMPtr storageConnection = + aConnection->StorageConnection(); + MOZ_ASSERT(storageConnection); + + mShadowDatabaseLock.emplace( + aConnection->GetQuotaClient()->ShadowDatabaseMutex()); + + nsresult rv = AttachShadowDatabase(quotaManager->GetBasePath(), storageConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult AutoWriteTransaction::DetachShadowDatabaseAndUnlock() { + AssertIsOnConnectionThread(); + MOZ_ASSERT(mConnection); + MOZ_ASSERT(mShadowDatabaseLock.isSome()); + MOZ_ASSERT(mShadowWrites); + + nsCOMPtr storageConnection = + mConnection->StorageConnection(); + MOZ_ASSERT(storageConnection); + + nsresult rv = DetachShadowDatabase(storageConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mShadowDatabaseLock.reset(); + + return NS_OK; +} + } // namespace dom } // namespace mozilla diff --git a/dom/localstorage/test/unit/head.js b/dom/localstorage/test/unit/head.js index a1dcfc65d7cb..bf9da017931d 100644 --- a/dom/localstorage/test/unit/head.js +++ b/dom/localstorage/test/unit/head.js @@ -85,6 +85,18 @@ function resetOriginLimit() { Services.prefs.clearUserPref("dom.storage.default_quota"); } +function setTimeout(callback, timeout) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + timer.initWithCallback({ + notify() { + callback(); + }, + }, timeout, Ci.nsITimer.TYPE_ONE_SHOT); + + return timer; +} + function init() { let request = Services.qms.init(); diff --git a/dom/localstorage/test/unit/test_flushing.js b/dom/localstorage/test/unit/test_flushing.js new file mode 100644 index 000000000000..101a507e2961 --- /dev/null +++ b/dom/localstorage/test/unit/test_flushing.js @@ -0,0 +1,67 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +/** + * This test is mainly to verify that the flush operation detaches the shadow + * database in the event of early return due to error. See bug 1559029. + */ + +async function testSteps() { + const principal1 = getPrincipal("http://example1.com"); + + const usageFile1 = + getRelativeFile("storage/default/http+++example1.com/ls/usage"); + + const principal2 = getPrincipal("http://example2.com"); + + const data = { + key: "foo", + value: "bar", + }; + + const flushSleepTimeSec = 6; + + info("Setting prefs"); + + Services.prefs.setBoolPref("dom.storage.next_gen", true); + + info("Getting storage 1"); + + let storage1 = getLocalStorage(principal1); + + info("Adding item"); + + storage1.setItem(data.key, data.value); + + info("Creating usage as a directory"); + + // This will cause a failure during the flush for first principal. + usageFile1.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); + + info("Getting storage 2"); + + let storage2 = getLocalStorage(principal2); + + info("Adding item"); + + storage2.setItem(data.key, data.value); + + // The flush for second principal shouldn't be affected by failed flush for + // first principal. + + info("Sleeping for " + flushSleepTimeSec + " seconds to let all flushes " + + "finish"); + + await new Promise(function(resolve) { + setTimeout(resolve, flushSleepTimeSec * 1000); + }); + + info("Resetting"); + + // Wait for all database connections to close. + let request = reset(); + await requestFinished(request); +} diff --git a/dom/localstorage/test/unit/xpcshell.ini b/dom/localstorage/test/unit/xpcshell.ini index c490897c8e41..194d71d9ebe9 100644 --- a/dom/localstorage/test/unit/xpcshell.ini +++ b/dom/localstorage/test/unit/xpcshell.ini @@ -33,6 +33,7 @@ run-sequentially = test_databaseShadowing_clearOriginsByPrefix2.js depends on a [test_databaseShadowing_clearOriginsByPrefix2.js] run-sequentially = this test depends on a file produced by test_databaseShadowing_clearOriginsByPrefix1.js [test_eviction.js] +[test_flushing.js] [test_groupLimit.js] [test_groupMismatch.js] [test_largeItems.js] diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index f6ba2a6099ed..bf7927d61947 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -406,7 +406,7 @@ void APZCCallbackHelper::InitializeRootDisplayport(PresShell* aPresShell) { &viewId)) { nsPresContext* pc = aPresShell->GetPresContext(); // This code is only correct for root content or toplevel documents. - MOZ_ASSERT(!pc || pc->IsRootContentDocument() || + MOZ_ASSERT(!pc || pc->IsRootContentDocumentCrossProcess() || !pc->GetParentPresContext()); nsIFrame* frame = aPresShell->GetRootScrollFrame(); if (!frame) { diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index a658559a3db3..aaf7863c2546 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1992,7 +1992,7 @@ MOZ_MUST_USE bool TokenStreamSpecific::getDirective( } if (MOZ_LIKELY(isAsciiCodePoint(unit))) { - if (unicode::IsSpaceOrBOM2(unit)) { + if (unicode::IsSpace(AssertedCast(unit))) { break; } @@ -2016,13 +2016,13 @@ MOZ_MUST_USE bool TokenStreamSpecific::getDirective( // This ignores encoding errors: subsequent caller-side code to // handle the remaining source text in the comment will do so. PeekedCodePoint peeked = this->sourceUnits.peekCodePoint(); - if (peeked.isNone() || unicode::IsSpaceOrBOM2(peeked.codePoint())) { + if (peeked.isNone() || unicode::IsSpace(peeked.codePoint())) { break; } MOZ_ASSERT(!IsLineTerminator(peeked.codePoint()), - "!IsSpaceOrBOM2 must imply !IsLineTerminator or else we'll " - "fail to maintain line-info/flags for EOL"); + "!IsSpace must imply !IsLineTerminator or else we'll fail to " + "maintain line-info/flags for EOL"); this->sourceUnits.consumeKnownCodePoint(peeked); if (!appendCodePointToCharBuffer(peeked.codePoint())) { @@ -2743,12 +2743,11 @@ MOZ_MUST_USE bool TokenStreamSpecific::getTokenInternal( } if (MOZ_UNLIKELY(!isAsciiCodePoint(unit))) { - // Non-ASCII code points can only be identifiers or whitespace. - // It would be nice to compute these *after* discarding whitespace, - // but IN A WORLD where |unicode::IsSpaceOrBOM2| requires consuming - // a variable number of code points, it's easier to assume it's an - // identifier and maybe do a little wasted work, than to unget and - // compute and reget if whitespace. + // Non-ASCII code points can only be identifiers or whitespace. It would + // be nice to compute these *after* discarding whitespace, but IN A WORLD + // where |unicode::IsSpace| requires consuming a variable number of code + // units, it's easier to assume it's an identifier and maybe do a little + // wasted work, than to unget and compute and reget if whitespace. TokenStart start(this->sourceUnits, 0); const Unit* identStart = this->sourceUnits.addressOfNextCodeUnit(); @@ -2760,7 +2759,7 @@ MOZ_MUST_USE bool TokenStreamSpecific::getTokenInternal( } char32_t cp = peeked.codePoint(); - if (unicode::IsSpaceOrBOM2(cp)) { + if (unicode::IsSpace(cp)) { this->sourceUnits.consumeKnownCodePoint(peeked); if (IsLineTerminator(cp)) { if (!updateLineInfoForEOL()) { diff --git a/js/src/tests/non262/extensions/regress-368516.js b/js/src/tests/non262/extensions/regress-368516.js index c3e109f7cd26..667b5c1535e1 100644 --- a/js/src/tests/non262/extensions/regress-368516.js +++ b/js/src/tests/non262/extensions/regress-368516.js @@ -19,8 +19,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - var bomchars = ['\uFFFE', - '\uFEFF']; + var bomchars = ['\uFEFF']; for (var i = 0; i < bomchars.length; i++) { diff --git a/js/src/util/Unicode.h b/js/src/util/Unicode.h index 41f79821fcc4..e67d4d723941 100644 --- a/js/src/util/Unicode.h +++ b/js/src/util/Unicode.h @@ -80,7 +80,6 @@ constexpr char16_t GREEK_SMALL_LETTER_SIGMA = 0x03C3; constexpr char16_t LINE_SEPARATOR = 0x2028; constexpr char16_t PARA_SEPARATOR = 0x2029; constexpr char16_t REPLACEMENT_CHARACTER = 0xFFFD; -constexpr char16_t BYTE_ORDER_MARK2 = 0xFFFE; const char16_t LeadSurrogateMin = 0xD800; const char16_t LeadSurrogateMax = 0xDBFF; @@ -204,19 +203,28 @@ inline bool IsUnicodeIDStart(uint32_t codePoint) { return IsUnicodeIDStart(char16_t(codePoint)); } +// IsSpace checks if a code point is included in the merged set of WhiteSpace +// and LineTerminator specified by #sec-white-space and #sec-line-terminators. +// We combine them because nearly every calling function wants this, excepting +// only some tokenizer code that necessarily handles LineTerminator specially +// due to UTF-8/UTF-16 template specialization. inline bool IsSpace(char16_t ch) { - /* - * IsSpace checks if some character is included in the merged set - * of WhiteSpace and LineTerminator, specified by ES2016 11.2 and 11.3. - * We combined them, because in practice nearly every - * calling function wants this, except some code in the tokenizer. - * - * We use a lookup table for ASCII-7 characters, because they are - * very common and must be handled quickly in the tokenizer. - * NO-BREAK SPACE is supposed to be the most common character not in - * this range, so we inline this case, too. - */ + // ASCII code points are very common and must be handled quickly, so use a + // lookup table for them. + if (ch < 128) { + return js_isspace[ch]; + } + // NO-BREAK SPACE is supposed to be the most common non-ASCII WhiteSpace code + // point, so inline its handling too. + if (ch == NO_BREAK_SPACE) { + return true; + } + + return CharInfo(ch).isSpace(); +} + +inline bool IsSpace(JS::Latin1Char ch) { if (ch < 128) { return js_isspace[ch]; } @@ -225,16 +233,20 @@ inline bool IsSpace(char16_t ch) { return true; } - return CharInfo(ch).isSpace(); + MOZ_ASSERT(!CharInfo(ch).isSpace()); + return false; } -inline bool IsSpaceOrBOM2(char32_t ch) { +inline bool IsSpace(char ch) { + return IsSpace(static_cast(ch)); +} + +inline bool IsSpace(char32_t ch) { if (ch < 128) { return js_isspace[ch]; } - /* We accept BOM2 (0xFFFE) for compatibility reasons in the parser. */ - if (ch == NO_BREAK_SPACE || ch == BYTE_ORDER_MARK2) { + if (ch == NO_BREAK_SPACE) { return true; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 42d8a4bcb8d6..39276b818c6e 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -26,6 +26,7 @@ #include "mozilla/StaticPrefs.h" #include "mozilla/Unused.h" #include "nsCharTraits.h" +#include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "nsFontMetrics.h" @@ -3786,12 +3787,20 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, builder->SetInActiveDocShell(isActive); } + nsRect rootVisualOverflow = aFrame->GetVisualOverflowRectRelativeToSelf(); + + // If we are in a remote browser, then apply clipping from ancestor browsers + if (BrowserChild* browserChild = BrowserChild::GetFrom(presShell)) { + rootVisualOverflow.IntersectRect(rootVisualOverflow, + browserChild->GetVisibleRect()); + } + nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); if (rootScrollFrame && !aFrame->GetParent()) { nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable(); MOZ_ASSERT(rootScrollableFrame); - nsRect displayPortBase = aFrame->GetVisualOverflowRectRelativeToSelf(); + nsRect displayPortBase = rootVisualOverflow; nsRect temp = displayPortBase; Unused << rootScrollableFrame->DecideScrollableLayer( builder, &displayPortBase, &temp, @@ -3806,7 +3815,7 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, // |ignoreViewportScrolling| and |usingDisplayPort| are persistent // document-rendering state. We rely on PresShell to flush // retained layers as needed when that persistent state changes. - visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf(); + visibleRegion = rootVisualOverflow; } else { visibleRegion = aDirtyRegion; } @@ -8465,10 +8474,9 @@ static bool UpdateCompositionBoundsForRCDRSF(ParentLayerRect& aCompBounds, #endif if (widget) { - LayoutDeviceIntRect widgetBounds = widget->GetBounds(); - widgetBounds.MoveTo(0, 0); aCompBounds = ParentLayerRect(ViewAs( - widgetBounds, + LayoutDeviceIntRect(LayoutDeviceIntPoint(), + widget->GetCompositionSize()), PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF)); return true; } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 1234faa51675..8b15ac847120 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3801,8 +3801,12 @@ bool ScrollFrameHelper::DecideScrollableLayer( if (aSetBase) { nsRect displayportBase = *aVisibleRect; nsPresContext* pc = mOuter->PresContext(); - if (mIsRoot && - (pc->IsRootContentDocument() || !pc->GetParentPresContext())) { + + bool isContentRootDoc = pc->IsRootContentDocumentCrossProcess(); + bool isChromeRootDoc = + !pc->Document()->IsContentDocument() && !pc->GetParentPresContext(); + + if (mIsRoot && (isContentRootDoc || isChromeRootDoc)) { displayportBase = nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter)); diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 073ab3590c99..1797916be2a6 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -14,6 +14,7 @@ #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs.h" +#include "mozilla/Unused.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLFrameElement.h" #include "mozilla/dom/BrowserParent.h" @@ -1404,8 +1405,20 @@ already_AddRefed nsDisplayRemote::BuildLayer( if (RefPtr remoteBrowser = GetFrameLoader()->GetRemoteBrowser()) { + // Adjust mItemVisibleRect, which is relative to the reference frame, to be + // relative to this frame + nsRect visibleRect; + if (aContainerParameters.mItemVisibleRect) { + visibleRect = *aContainerParameters.mItemVisibleRect - ToReferenceFrame(); + } else { + visibleRect = mFrame->GetContentRectRelativeToSelf(); + } + // Generate an effects update notifying the browser it is visible - aBuilder->AddEffectUpdate(remoteBrowser, EffectsInfo::FullyVisible()); + aBuilder->AddEffectUpdate(remoteBrowser, + EffectsInfo::VisibleWithinRect( + visibleRect, aContainerParameters.mXScale, + aContainerParameters.mYScale)); // FrameLayerBuilder will take care of notifying the browser when it is no // longer visible } @@ -1465,8 +1478,10 @@ bool nsDisplayRemote::CreateWebRenderCommands( if (RefPtr remoteBrowser = GetFrameLoader()->GetRemoteBrowser()) { // Generate an effects update notifying the browser it is visible - aDisplayListBuilder->AddEffectUpdate(remoteBrowser, - EffectsInfo::FullyVisible()); + // TODO - Gather visibleRect and scaling factors + aDisplayListBuilder->AddEffectUpdate( + remoteBrowser, EffectsInfo::VisibleWithinRect( + mFrame->GetContentRectRelativeToSelf(), 1.0f, 1.0f)); // Create a WebRenderRemoteData to notify the RemoteBrowser when it is no // longer visible diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 56bd229ea040..4e2612f93201 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -4657,6 +4657,7 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { transformNode = transformNode->Parent(); } + nsRect itemVisibleRectAu = itemContent; if (transformNode) { // If we are within transform, transform itemContent and itemDrawRect. MOZ_ASSERT(transformNode); @@ -4818,6 +4819,15 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { ContainerLayerParameters params = mParameters; params.mBackgroundColor = uniformColor; params.mLayerCreationHint = GetLayerCreationHint(itemAGR); + if (!transformNode) { + params.mItemVisibleRect = &itemVisibleRectAu; + } else { + // We only use mItemVisibleRect for getting the visible rect for + // remote browsers (which should never have inactive transforms), so we + // avoid doing transforms on itemVisibleRectAu above and can't report + // an accurate bounds here. + params.mItemVisibleRect = nullptr; + } params.mScrollMetadataASR = ActiveScrolledRoot::IsAncestor(scrollMetadataASR, mContainerScrollMetadataASR) diff --git a/layout/painting/FrameLayerBuilder.h b/layout/painting/FrameLayerBuilder.h index 491c37a151b2..3a64053d426d 100644 --- a/layout/painting/FrameLayerBuilder.h +++ b/layout/painting/FrameLayerBuilder.h @@ -285,6 +285,7 @@ struct ContainerLayerParameters { : mXScale(aXScale), mYScale(aYScale), mLayerContentsVisibleRect(nullptr), + mItemVisibleRect(nullptr), mBackgroundColor(NS_RGBA(0, 0, 0, 0)), mScrollMetadataASR(nullptr), mCompositorASR(nullptr), @@ -298,6 +299,7 @@ struct ContainerLayerParameters { : mXScale(aXScale), mYScale(aYScale), mLayerContentsVisibleRect(nullptr), + mItemVisibleRect(nullptr), mOffset(aOffset), mBackgroundColor(aParent.mBackgroundColor), mScrollMetadataASR(aParent.mScrollMetadataASR), @@ -320,6 +322,11 @@ struct ContainerLayerParameters { */ nsIntRect* mLayerContentsVisibleRect; + /** + * If non-null, the rectangle which stores the item's visible rect. + */ + nsRect* mItemVisibleRect; + /** * An offset to apply to all child layers created. */ diff --git a/layout/painting/RetainedDisplayListBuilder.h b/layout/painting/RetainedDisplayListBuilder.h index ffd79f2b7a6d..3d5418a06653 100644 --- a/layout/painting/RetainedDisplayListBuilder.h +++ b/layout/painting/RetainedDisplayListBuilder.h @@ -101,7 +101,8 @@ enum class PartialUpdateFailReason { RebuildLimit, FrameType, Disabled, - Content + Content, + VisibleRect, }; struct RetainedDisplayListMetrics { @@ -146,6 +147,8 @@ struct RetainedDisplayListMetrics { return "Disabled"; case PartialUpdateFailReason::Content: return "Content"; + case PartialUpdateFailReason::VisibleRect: + return "VisibleRect"; default: MOZ_ASSERT_UNREACHABLE("Enum value not handled!"); } @@ -254,6 +257,7 @@ struct RetainedDisplayListBuilder { nsDisplayListBuilder mBuilder; RetainedDisplayList mList; + nsRect mPreviousVisibleRect; WeakFrame mPreviousCaret; RetainedDisplayListMetrics mMetrics; }; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index bc9ead4dbc2e..56b470d0ceb5 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -2882,11 +2882,19 @@ FrameLayerBuilder* nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder, rootLayer->SetScrollMetadata(nsTArray()); } - float rootLayerResolution = StaticPrefs::LayoutUseContainersForRootFrames() - ? presShell->GetResolution() - : 1.0f; - ContainerLayerParameters containerParameters(rootLayerResolution, - rootLayerResolution); + float resolutionUniform = StaticPrefs::LayoutUseContainersForRootFrames() + ? presShell->GetResolution() + : 1.0f; + float resolutionX = resolutionUniform; + float resolutionY = resolutionUniform; + + // If we are in a remote browser, then apply scaling from ancestor browsers + if (BrowserChild* browserChild = BrowserChild::GetFrom(presShell)) { + resolutionX *= browserChild->GetEffectsInfo().mScaleX; + resolutionY *= browserChild->GetEffectsInfo().mScaleY; + } + + ContainerLayerParameters containerParameters(resolutionX, resolutionY); { PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization); @@ -2906,11 +2914,10 @@ FrameLayerBuilder* nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder, if (!root) { return nullptr; } + // Root is being scaled up by the X/Y resolution. Scale it back down. + root->SetPostScale(1.0f / resolutionX, 1.0f / resolutionY); if (StaticPrefs::LayoutUseContainersForRootFrames()) { - // Root is being scaled up by the X/Y resolution. Scale it back down. - root->SetPostScale(1.0f / containerParameters.mXScale, - 1.0f / containerParameters.mYScale); - root->SetScaleToResolution(containerParameters.mXScale); + root->SetScaleToResolution(resolutionUniform); } auto callback = [root](ScrollableLayerGuid::ViewID aScrollId) -> bool { diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 5a7f448d31eb..c534c04396cd 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -1158,6 +1158,15 @@ LayoutDeviceIntRect PuppetWidget::GetScreenBounds() { return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size()); } +LayoutDeviceIntSize PuppetWidget::GetCompositionSize() { + if (!mBrowserChild) { + return nsBaseWidget::GetCompositionSize(); + } + CSSSize visibleSize = + CSSPixel::FromAppUnits(mBrowserChild->GetVisibleRect().Size()); + return RoundedToInt(visibleSize * GetDefaultScale()); +} + uint32_t PuppetWidget::GetMaxTouchPoints() const { uint32_t maxTouchPoints = 0; if (mBrowserChild) { diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index 85a3862783a7..a05a069703af 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -242,6 +242,8 @@ class PuppetWidget : public nsBaseWidget, virtual LayoutDeviceIntRect GetScreenBounds() override; + virtual LayoutDeviceIntSize GetCompositionSize() override; + virtual MOZ_MUST_USE nsresult StartPluginIME( const mozilla::WidgetKeyboardEvent& aKeyboardEvent, int32_t aPanelX, int32_t aPanelY, nsString& aCommitted) override; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 1c6245dab267..6ab94cdfb9a5 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -937,6 +937,16 @@ class nsIWidget : public nsISupports { return GetClientBounds().Size(); } + /** + * Get the size of the bounds of this widget that will be visible when + * rendered. + * + * @return the width and height of the composition size of this widget. + */ + virtual LayoutDeviceIntSize GetCompositionSize() { + return GetBounds().Size(); + } + /** * Set the background color for this widget *