author | Phil Ringnalda <philringnalda@gmail.com> |
Tue, 20 Dec 2016 20:15:20 -0800 | |
changeset 326707 | c36fbe84042debef0a5d58b7fc88185b401762ce |
parent 326706 | 20774bffb62a3c1fecf98ed8ad9ee1a861bcd9b7 (current diff) |
parent 326640 | 70e65619c68e14a51f98e42133322affd6dd2a9f (diff) |
child 326708 | 009bb9bc85e40dad6d22e0b0257f722bf7fd0927 |
child 326754 | b5a511ec22c1a5b881babebba4e009991f23f964 |
child 326787 | 084a46effa8547c355940266cdab6b3b28e8416d |
push id | 85010 |
push user | philringnalda@gmail.com |
push date | Wed, 21 Dec 2016 04:21:25 +0000 |
treeherder | mozilla-inbound@009bb9bc85e4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 53.0a1 |
first release with | nightly linux32
c36fbe84042d
/
53.0a1
/
20161221030226
/
files
nightly linux64
c36fbe84042d
/
53.0a1
/
20161221030226
/
files
nightly mac
c36fbe84042d
/
53.0a1
/
20161221030226
/
files
nightly win32
c36fbe84042d
/
53.0a1
/
20161221030226
/
files
nightly win64
c36fbe84042d
/
53.0a1
/
20161221030226
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
53.0a1
/
20161221030226
/
pushlog to previous
nightly linux64
53.0a1
/
20161221030226
/
pushlog to previous
nightly mac
53.0a1
/
20161221030226
/
pushlog to previous
nightly win32
53.0a1
/
20161221030226
/
pushlog to previous
nightly win64
53.0a1
/
20161221030226
/
pushlog to previous
|
dom/canvas/WebGL2ContextDraw.cpp | file | annotate | diff | comparison | revisions | |
dom/webidl/moz.build | file | annotate | diff | comparison | revisions | |
layout/reftests/css-grid/grid-percent-intrinsic-sizing-002-ref.html | file | annotate | diff | comparison | revisions | |
layout/reftests/css-grid/grid-percent-intrinsic-sizing-002.html | file | annotate | diff | comparison | revisions | |
python/mozbuild/mozbuild/vendor_rust.py | file | annotate | diff | comparison | revisions | |
testing/web-platform/meta/MANIFEST.json | file | annotate | diff | comparison | revisions |
--- a/browser/base/content/contentSearchUI.js +++ b/browser/base/content/contentSearchUI.js @@ -60,20 +60,16 @@ function ContentSearchUIController(input this._hideSuggestions(); this._getSearchEngines(); this._getStrings(); } ContentSearchUIController.prototype = { - // The timeout (ms) of the remote suggestions. Corresponds to - // SearchSuggestionController.remoteTimeout. Uses - // SearchSuggestionController's default timeout if falsey. - remoteTimeout: undefined, _oneOffButtons: [], // Setting up the one off buttons causes an uninterruptible reflow. If we // receive the list of engines while the newtab page is loading, this reflow // may regress performance - so we set this flag and only set up the buttons // if it's set when the suggestions table is actually opened. _pendingOneOffRefresh: undefined, get defaultEngine() { @@ -711,17 +707,16 @@ ContentSearchUIController.prototype = { }, _getSuggestions: function() { this._stickyInputValue = this.input.value; if (this.defaultEngine) { this._sendMsg("GetSuggestions", { engineName: this.defaultEngine.name, searchString: this.input.value, - remoteTimeout: this.remoteTimeout, }); } }, _clearSuggestionRows: function() { while (this._suggestionsList.firstElementChild) { this._suggestionsList.firstElementChild.remove(); }
--- a/browser/base/content/test/general/browser_aboutHome.js +++ b/browser/base/content/test/general/browser_aboutHome.js @@ -311,19 +311,16 @@ add_task(function* () { // Add a test engine that provides suggestions and switch to it. let currEngine = Services.search.currentEngine; let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); let p = promiseContentSearchChange(browser, engine.name); Services.search.currentEngine = engine; yield p; yield ContentTask.spawn(browser, null, function* () { - // Avoid intermittent failures. - content.wrappedJSObject.gContentSearchController.remoteTimeout = 5000; - // Type an X in the search input. let input = content.document.getElementById("searchText"); input.focus(); }); yield BrowserTestUtils.synthesizeKey("x", {}, browser); yield ContentTask.spawn(browser, null, function* () {
--- a/browser/base/content/test/general/contentSearchUI.js +++ b/browser/base/content/test/general/contentSearchUI.js @@ -22,17 +22,16 @@ var messageHandlers = { new content.ContentSearchUIController(input, input.parentNode, "test", "test"); content.addEventListener("ContentSearchService", function listener(aEvent) { if (aEvent.detail.type == "State" && gController.defaultEngine.name == ENGINE_NAME) { content.removeEventListener("ContentSearchService", listener); ack("init"); } }); - gController.remoteTimeout = 5000; }, key: function(arg) { let keyName = typeof(arg) == "string" ? arg : arg.key; content.synthesizeKey(keyName, arg.modifiers || {}); let wait = arg.waitForSuggestions ? waitForSuggestions : cb => cb(); wait(ack.bind(null, "key")); },
--- a/browser/base/content/test/newtab/browser_newtab_search.js +++ b/browser/base/content/test/newtab/browser_newtab_search.js @@ -120,21 +120,16 @@ add_task(function* () { // Add the engine that provides search suggestions and switch to it. let suggestionEngine = yield promiseNewSearchEngine(ENGINE_SUGGESTIONS); searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); Services.search.currentEngine = suggestionEngine; yield searchEventsPromise; yield* checkCurrentEngine(ENGINE_SUGGESTIONS); - // Avoid intermittent failures. - yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { - content.gSearch._contentSearchController.remoteTimeout = 5000; - }); - // Type an X in the search input. This is only a smoke test. See // browser_searchSuggestionUI.js for comprehensive content search suggestion // UI tests. let suggestionsOpenPromise = new Promise(resolve => { mm.addMessageListener("test:newtab-suggestions-open", function onResponse(message) { mm.removeMessageListener("test:newtab-suggestions-open", onResponse); resolve(); });
--- a/browser/modules/ContentSearch.jsm +++ b/browser/modules/ContentSearch.jsm @@ -36,17 +36,17 @@ const MAX_SUGGESTIONS = 6; * * Inbound messages have the following types: * * AddFormHistoryEntry * Adds an entry to the search form history. * data: the entry, a string * GetSuggestions * Retrieves an array of search suggestions given a search string. - * data: { engineName, searchString, [remoteTimeout] } + * data: { engineName, searchString } * GetState * Retrieves the current search engine state. * data: null * GetStrings * Retrieves localized search UI strings. * data: null * ManageEngines * Opens the search engine management window. @@ -254,28 +254,27 @@ this.ContentSearch = { }; win.openUILinkIn(submission.uri.spec, where, params); } win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey, { selection: data.selection }); return; }, - getSuggestions: Task.async(function* (engineName, searchString, browser, remoteTimeout = null) { + getSuggestions: Task.async(function* (engineName, searchString, browser) { let engine = Services.search.getEngineByName(engineName); if (!engine) { throw new Error("Unknown engine name: " + engineName); } let browserData = this._suggestionDataForBrowser(browser, true); let { controller } = browserData; let ok = SearchSuggestionController.engineOffersSuggestions(engine); controller.maxLocalResults = ok ? MAX_LOCAL_SUGGESTIONS : MAX_SUGGESTIONS; controller.maxRemoteResults = ok ? MAX_SUGGESTIONS : 0; - controller.remoteTimeout = remoteTimeout || undefined; let priv = PrivateBrowsingUtils.isBrowserPrivate(browser); // fetch() rejects its promise if there's a pending request, but since we // process our event queue serially, there's never a pending request. this._currentSuggestion = { controller: controller, target: browser }; let suggestions = yield controller.fetch(searchString, priv, engine); this._currentSuggestion = null; // suggestions will be null if the request was cancelled
--- a/browser/modules/test/browser_ContentSearch.js +++ b/browser/modules/test/browser_ContentSearch.js @@ -1,20 +1,16 @@ /* 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/. */ const TEST_MSG = "ContentSearchTest"; const CONTENT_SEARCH_MSG = "ContentSearch"; const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js"; -// This timeout is absurdly high to avoid random failures like bug 1087120. -// That bug was reported when the timeout was 5 seconds, so let's try 10. -const SUGGESTIONS_TIMEOUT = 10000; - var gMsgMan; add_task(function* GetState() { yield addTab(); gMsgMan.sendAsyncMessage(TEST_MSG, { type: "GetState", }); let msg = yield waitForTestMsg("State"); @@ -187,17 +183,16 @@ add_task(function* GetSuggestions_AddFor // Send GetSuggestions using the test engine. Its suggestions should appear // in the remote suggestions in the Suggestions response below. gMsgMan.sendAsyncMessage(TEST_MSG, { type: "GetSuggestions", data: { engineName: engine.name, searchString: searchStr, - remoteTimeout: SUGGESTIONS_TIMEOUT, }, }); // Check the Suggestions response. let msg = yield waitForTestMsg("Suggestions"); checkMsg(msg, { type: "Suggestions", data: { @@ -223,17 +218,16 @@ add_task(function* GetSuggestions_AddFor yield deferred.promise; // Send GetSuggestions again. gMsgMan.sendAsyncMessage(TEST_MSG, { type: "GetSuggestions", data: { engineName: engine.name, searchString: searchStr, - remoteTimeout: SUGGESTIONS_TIMEOUT, }, }); // The formHistory suggestions in the Suggestions response should be empty. msg = yield waitForTestMsg("Suggestions"); checkMsg(msg, { type: "Suggestions", data: {
--- a/browser/modules/test/head.js +++ b/browser/modules/test/head.js @@ -62,20 +62,16 @@ function checkKeyedScalar(scalars, scala * The browser that contains the content. * @param {String} text * The string to write in the search field. * @param {String} fieldName * The name of the field to write to. */ let typeInSearchField = Task.async(function* (browser, text, fieldName) { yield ContentTask.spawn(browser, [fieldName, text], function* ([contentFieldName, contentText]) { - // Avoid intermittent failures. - if (contentFieldName === "searchText") { - content.wrappedJSObject.gContentSearchController.remoteTimeout = 5000; - } // Put the focus on the search box. let searchInput = content.document.getElementById(contentFieldName); searchInput.focus(); searchInput.value = contentText; }); }); /**
new file mode 100644 --- /dev/null +++ b/dom/base/OrderedTimeoutIterator.h @@ -0,0 +1,187 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_OrderedTimeoutIterator_h__ +#define mozilla_dom_OrderedTimeoutIterator_h__ + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/Timeout.h" +#include "mozilla/dom/TimeoutManager.h" + +namespace mozilla { +namespace dom { + +// This class implements and iterator which iterates the normal and tracking +// timeouts lists simultaneously in the mWhen order. +class MOZ_STACK_CLASS OrderedTimeoutIterator final { +public: + typedef TimeoutManager::Timeouts Timeouts; + typedef Timeouts::TimeoutList TimeoutList; + + // Passing null for aNormalStopAt or aTrackingStopAt means that the + // corresponding timeout list should be iterated all the way to the end. + OrderedTimeoutIterator(Timeouts& aNormalTimeouts, + Timeouts& aTrackingTimeouts, + Timeout* aNormalStopAt, + Timeout* aTrackingStopAt) + : mNormalTimeouts(aNormalTimeouts.mTimeoutList), + mTrackingTimeouts(aTrackingTimeouts.mTimeoutList), + mNormalIter(mNormalTimeouts.getFirst()), + mTrackingIter(mTrackingTimeouts.getFirst()), + mNormalStopAt(aNormalStopAt), + mTrackingStopAt(aTrackingStopAt), + mKind(Kind::None), + mUpdateIteratorCalled(true) + { + } + + // Return the current timeout and move to the next one. + // Unless this is the first time calling Next(), you must call + // UpdateIterator() before calling this method. + Timeout* Next() + { + MOZ_ASSERT(mUpdateIteratorCalled); + MOZ_ASSERT_IF(mNormalIter && mNormalIter != mNormalStopAt, + mNormalIter->isInList()); + MOZ_ASSERT_IF(mTrackingIter && mTrackingIter != mTrackingStopAt, + mTrackingIter->isInList()); + + mUpdateIteratorCalled = false; + mKind = Kind::None; + Timeout* timeout = nullptr; + if (mNormalIter == mNormalStopAt) { + if (mTrackingIter == mTrackingStopAt) { + // We have reached the end of both lists. Bail out! + return nullptr; + } else { + // We have reached the end of the normal timeout list, select the next + // tracking timeout. + timeout = mTrackingIter; + mKind = Kind::Tracking; + } + } else if (mTrackingIter == mTrackingStopAt) { + // We have reached the end of the tracking timeout list, select the next + // normal timeout. + timeout = mNormalIter; + mKind = Kind::Normal; + } else { + // If we have a normal and a tracking timer, return the one with the + // smaller mWhen (and prefer the timeout with a lower ID in case they are + // equal.) Otherwise, return whichever iterator has an item left, + // preferring a non-tracking timeout again. Note that in practice, even + // if a web page calls setTimeout() twice in a row, it should get + // different mWhen values, so in practice we shouldn't fall back to + // comparing timeout IDs. + if (mNormalIter && mTrackingIter && + mNormalIter != mNormalStopAt && + mTrackingIter != mTrackingStopAt && + (mTrackingIter->mWhen < mNormalIter->mWhen || + (mTrackingIter->mWhen == mNormalIter->mWhen && + mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) { + timeout = mTrackingIter; + mKind = Kind::Tracking; + } else if (mNormalIter && mNormalIter != mNormalStopAt) { + timeout = mNormalIter; + mKind = Kind::Normal; + } else if (mTrackingIter && mTrackingIter != mTrackingStopAt) { + timeout = mTrackingIter; + mKind = Kind::Tracking; + } + } + if (!timeout) { + // We didn't find any suitable iterator. This can happen for example + // when getNext() in UpdateIterator() returns nullptr and then Next() + // gets called. Bail out! + return nullptr; + } + + MOZ_ASSERT(mKind != Kind::None); + + // Record the current timeout we just found. + mCurrent = timeout; + MOZ_ASSERT(mCurrent); + + return mCurrent; + } + + // Prepare the iterator for the next call to Next(). + // This method can be called as many times as needed. Calling this more than + // once is helpful in cases where we expect the timeouts list has been + // modified before we got a chance to call Next(). + void UpdateIterator() + { + MOZ_ASSERT(mKind != Kind::None); + // Update the winning iterator to point to the next element. Also check to + // see if the other iterator is still valid, otherwise reset it to the + // beginning of the list. This is needed in case a timeout handler removes + // the timeout pointed to from one of our iterators. + if (mKind == Kind::Normal) { + mNormalIter = mCurrent->getNext(); + if (mTrackingIter && mTrackingIter != mTrackingStopAt && + !mTrackingIter->isInList()) { + mTrackingIter = mTrackingTimeouts.getFirst(); + } + } else { + mTrackingIter = mCurrent->getNext(); + if (mNormalIter && mNormalIter != mNormalStopAt && + !mNormalIter->isInList()) { + mNormalIter = mNormalTimeouts.getFirst(); + } + } + + mUpdateIteratorCalled = true; + } + + // This function resets the iterator to a defunct state. It should only be + // used when we want to forcefully sever all of the strong references this + // class holds. + void Clear() + { + // Release all strong references. + mNormalIter = nullptr; + mTrackingIter = nullptr; + mCurrent = nullptr; + mKind = Kind::None; + mUpdateIteratorCalled = true; + } + + // Returns true if the previous call to Next() picked a normal timeout. + // Cannot be called before Next() has been called. Note that the result of + // this method is only affected by Next() and not UpdateIterator(), so calling + // UpdateIterator() before calling this is allowed. + bool PickedNormalIter() const + { + MOZ_ASSERT(mKind != Kind::None); + return mKind == Kind::Normal; + } + + // Returns true if the previous call to Next() picked a tracking timeout. + // Cannot be called before Next() has been called. Note that the result of + // this method is only affected by Next() and not UpdateIterator(), so calling + // UpdateIterator() before calling this is allowed. + bool PickedTrackingIter() const + { + MOZ_ASSERT(mKind != Kind::None); + return mKind == Kind::Tracking; + } + +private: + TimeoutList& mNormalTimeouts; // The list of normal timeouts. + TimeoutList& mTrackingTimeouts; // The list of tracking timeouts. + RefPtr<Timeout> mNormalIter; // The iterator over the normal timeout list. + RefPtr<Timeout> mTrackingIter; // The iterator over the tracking timeout list. + void* mNormalStopAt; // Where to stop iterating the normal list at. + void* mTrackingStopAt; // Where to stop iterating the tracking list at. + RefPtr<Timeout> mCurrent; // The current timeout that Next() just found. + enum class Kind { Normal, Tracking, None }; + Kind mKind; // The kind of iterator picked the last time. + DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next(). +}; + +} +} + +#endif
--- a/dom/base/Timeout.cpp +++ b/dom/base/Timeout.cpp @@ -93,20 +93,20 @@ Timeout::InitTimer(nsIEventTarget* aTarg MOZ_ALWAYS_SUCCEEDS(mTimer->Cancel()); MOZ_ALWAYS_SUCCEEDS(mTimer->SetTarget(aTarget)); } return mTimer->InitWithNameableFuncCallback( TimerCallback, this, aDelay, nsITimer::TYPE_ONE_SHOT, TimerNameCallback); } -// Return true if this timeout has a refcount of 1. This is used to check +// Return true if this timeout has a refcount of aCount. This is used to check // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout. #ifdef DEBUG bool -Timeout::HasRefCntOne() const +Timeout::HasRefCnt(uint32_t aCount) const { - return mRefCnt.get() == 1; + return mRefCnt.get() == aCount; } #endif // DEBUG } // namespace dom } // namespace mozilla
--- a/dom/base/Timeout.h +++ b/dom/base/Timeout.h @@ -40,17 +40,17 @@ public: // The target may be specified to use a particular event queue for the // resulting timer runnable. A nullptr target will result in the // default main thread being used. nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay); enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout }; #ifdef DEBUG - bool HasRefCntOne() const; + bool HasRefCnt(uint32_t aCount) const; #endif // DEBUG // Window for which this timeout fires RefPtr<nsGlobalWindow> mWindow; // The actual timer object nsCOMPtr<nsITimer> mTimer;
--- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TimeoutManager.h" #include "nsGlobalWindow.h" #include "mozilla/ThrottledEventQueue.h" #include "mozilla/TimeStamp.h" #include "nsITimeoutHandler.h" #include "mozilla/dom/TabGroup.h" +#include "OrderedTimeoutIterator.h" using namespace mozilla; using namespace mozilla::dom; static int32_t gRunningTimeoutDepth = 0; // The default shortest interval/timeout we permit #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms @@ -28,16 +29,23 @@ TimeoutManager::DOMMinTimeoutValue() con // Don't use the background timeout value when there are audio contexts // present, so that background audio can keep running smoothly. (bug 1181073) bool isBackground = !mWindow.AsInner()->HasAudioContexts() && mWindow.IsBackgroundInternal(); return std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value); } +#define TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY 0 // Consider all timeouts coming from tracking scripts as tracking +// These strategies are useful for testing. +#define ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY 1 // Consider all timeouts as normal +#define ALTERNATE_TIMEOUT_BUCKETING_STRATEGY 2 // Put every other timeout in the list of tracking timeouts +#define RANDOM_TIMEOUT_BUCKETING_STRATEGY 3 // Put timeouts into either the normal or tracking timeouts list randomly +static int32_t gTimeoutBucketingStrategy = 0; + // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit // uses 5. #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 // The longest interval (as PRIntervalTime) we permit, or that our // timer code can handle, really. See DELAY_INTERVAL_LIMIT in // nsTimerImpl.h for details. #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT @@ -107,16 +115,19 @@ void TimeoutManager::Initialize() { Preferences::AddIntVarCache(&gMinTimeoutValue, "dom.min_timeout_value", DEFAULT_MIN_TIMEOUT_VALUE); Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, "dom.min_background_timeout_value", DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); + Preferences::AddIntVarCache(&gTimeoutBucketingStrategy, + "dom.timeout_bucketing_strategy", + TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY); } uint32_t TimeoutManager::GetTimeoutId(Timeout::Reason aReason) { switch (aReason) { case Timeout::Reason::eIdleCallbackTimeout: return ++mIdleCallbackTimeoutCounter; @@ -128,17 +139,18 @@ TimeoutManager::GetTimeoutId(Timeout::Re nsresult TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler, int32_t interval, bool aIsInterval, Timeout::Reason aReason, int32_t* aReturn) { // If we don't have a document (we could have been unloaded since // the call to setTimeout was made), do nothing. - if (!mWindow.GetExtantDoc()) { + nsCOMPtr<nsIDocument> doc = mWindow.GetExtantDoc(); + if (!doc) { return NS_OK; } // Disallow negative intervals. If aIsInterval also disallow 0, // because we use that as a "don't repeat" flag. interval = std::max(aIsInterval ? 1 : 0, interval); // Make sure we don't proceed with an interval larger than our timer @@ -223,31 +235,57 @@ TimeoutManager::SetTimeout(nsITimeoutHan // This is checking |interval|, not realInterval, on purpose, // because our lower bound for |realInterval| could be pretty high // in some cases. if (interval <= delay) { timeout->mPopupState = mWindow.GetPopupControlState(); } } - mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining - : Timeouts::SortBy::TimeWhen); + bool isTracking = false; + switch (gTimeoutBucketingStrategy) { + default: + case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: { + const char* filename = nullptr; + uint32_t dummyLine = 0, dummyColumn = 0; + aHandler->GetLocation(&filename, &dummyLine, &dummyColumn); + isTracking = doc->IsScriptTracking(nsDependentCString(filename)); + break; + } + case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY: + // isTracking is already false! + break; + case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY: + isTracking = (mTimeoutIdCounter % 2) == 0; + break; + case RANDOM_TIMEOUT_BUCKETING_STRATEGY: + isTracking = (rand() % 2) == 0; + break; + } + + Timeouts::SortBy sort(mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining + : Timeouts::SortBy::TimeWhen); + if (isTracking) { + mTrackingTimeouts.Insert(timeout, sort); + } else { + mNormalTimeouts.Insert(timeout, sort); + } timeout->mTimeoutId = GetTimeoutId(aReason); *aReturn = timeout->mTimeoutId; return NS_OK; } void TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason) { uint32_t timerId = (uint32_t)aTimerId; - ForEachTimeoutAbortable([&](Timeout* aTimeout) { + ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) { if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) { if (aTimeout->mRunning) { /* We're running from inside the aTimeout. Mark this aTimeout for deferred deletion by the code in RunTimeout() */ aTimeout->mIsInterval = false; } else { @@ -271,19 +309,21 @@ void TimeoutManager::RunTimeout(Timeout* aTimeout) { if (mWindow.IsSuspended()) { return; } NS_ASSERTION(!mWindow.IsFrozen(), "Timeout running on a window in the bfcache!"); - Timeout* nextTimeout; - Timeout* last_expired_timeout; - Timeout* last_insertion_point; + Timeout* last_expired_normal_timeout = nullptr; + Timeout* last_expired_tracking_timeout = nullptr; + bool last_expired_timeout_is_normal = false; + Timeout* last_normal_insertion_point = nullptr; + Timeout* last_tracking_insertion_point = nullptr; uint32_t firingDepth = mTimeoutFiringDepth + 1; // Make sure that the window and the script context don't go away as // a result of running timeouts nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(&mWindow); // Silence the static analysis error about windowKungFuDeathGrip. Accessing // members of mWindow here is safe, because the lifetime of TimeoutManager is // the same as the lifetime of the containing nsGlobalWindow. @@ -307,140 +347,233 @@ TimeoutManager::RunTimeout(Timeout* aTim } // The timeout list is kept in deadline order. Discover the latest timeout // whose deadline has expired. On some platforms, native timeout events fire // "early", but we handled that above by setting deadline to aTimeout->mWhen // if the timer fired early. So we can stop walking if we get to timeouts // whose mWhen is greater than deadline, since once that happens we know // nothing past that point is expired. - last_expired_timeout = nullptr; - for (Timeout* timeout = mTimeouts.GetFirst(); - timeout && timeout->mWhen <= deadline; - timeout = timeout->getNext()) { - if (timeout->mFiringDepth == 0) { - // Mark any timeouts that are on the list to be fired with the - // firing depth so that we can reentrantly run timeouts - timeout->mFiringDepth = firingDepth; - last_expired_timeout = timeout; - - // Run available timers until we see our target timer. After - // that, however, stop coalescing timers so we can yield the - // main thread. Further timers that are ready will get picked - // up by their own nsITimer runnables when they execute. - // - // For chrome windows, however, we do coalesce all timers and - // do not yield the main thread. This is partly because we - // trust chrome windows not to misbehave and partly because a - // number of browser chrome tests have races that depend on this - // coalescing. - if (timeout == aTimeout && !mWindow.IsChromeWindow()) { + { + // Use a nested scope in order to make sure the strong references held by + // the iterator are freed after the loop. + OrderedTimeoutIterator expiredIter(mNormalTimeouts, + mTrackingTimeouts, + nullptr, + nullptr); + while (true) { + Timeout* timeout = expiredIter.Next(); + if (!timeout || timeout->mWhen > deadline) { break; } + + if (timeout->mFiringDepth == 0) { + // Mark any timeouts that are on the list to be fired with the + // firing depth so that we can reentrantly run timeouts + timeout->mFiringDepth = firingDepth; + last_expired_timeout_is_normal = expiredIter.PickedNormalIter(); + if (last_expired_timeout_is_normal) { + last_expired_normal_timeout = timeout; + } else { + last_expired_tracking_timeout = timeout; + } + + // Run available timers until we see our target timer. After + // that, however, stop coalescing timers so we can yield the + // main thread. Further timers that are ready will get picked + // up by their own nsITimer runnables when they execute. + // + // For chrome windows, however, we do coalesce all timers and + // do not yield the main thread. This is partly because we + // trust chrome windows not to misbehave and partly because a + // number of browser chrome tests have races that depend on this + // coalescing. + if (timeout == aTimeout && !mWindow.IsChromeWindow()) { + break; + } + } + + expiredIter.UpdateIterator(); } } // Maybe the timeout that the event was fired for has been deleted // and there are no others timeouts with deadlines that make them // eligible for execution yet. Go away. - if (!last_expired_timeout) { + if (!last_expired_normal_timeout && !last_expired_tracking_timeout) { return; } // Insert a dummy timeout into the list of timeouts between the // portion of the list that we are about to process now and those // timeouts that will be processed in a future call to // win_run_timeout(). This dummy timeout serves as the head of the // list for any timeouts inserted as a result of running a timeout. - RefPtr<Timeout> dummy_timeout = new Timeout(); - dummy_timeout->mFiringDepth = firingDepth; - dummy_timeout->mWhen = now; - last_expired_timeout->setNext(dummy_timeout); - RefPtr<Timeout> timeoutExtraRef(dummy_timeout); + RefPtr<Timeout> dummy_normal_timeout = new Timeout(); + dummy_normal_timeout->mFiringDepth = firingDepth; + dummy_normal_timeout->mWhen = now; + if (last_expired_timeout_is_normal) { + last_expired_normal_timeout->setNext(dummy_normal_timeout); + } + + RefPtr<Timeout> dummy_tracking_timeout = new Timeout(); + dummy_tracking_timeout->mFiringDepth = firingDepth; + dummy_tracking_timeout->mWhen = now; + if (!last_expired_timeout_is_normal) { + last_expired_tracking_timeout->setNext(dummy_tracking_timeout); + } + + RefPtr<Timeout> timeoutExtraRef1(dummy_normal_timeout); + RefPtr<Timeout> timeoutExtraRef2(dummy_tracking_timeout); - last_insertion_point = mTimeouts.InsertionPoint(); - // If we ever start setting insertion point to a non-dummy timeout, the logic - // in ResetTimersForThrottleReduction will need to change. - mTimeouts.SetInsertionPoint(dummy_timeout); + // Now we need to search the normal and tracking timer list at the same + // time to run the timers in the scheduled order. - for (Timeout* timeout = mTimeouts.GetFirst(); - timeout != dummy_timeout && !mWindow.IsFrozen(); - timeout = nextTimeout) { - nextTimeout = timeout->getNext(); + last_normal_insertion_point = mNormalTimeouts.InsertionPoint(); + if (last_expired_timeout_is_normal) { + // If we ever start setting insertion point to a non-dummy timeout, the logic + // in ResetTimersForThrottleReduction will need to change. + mNormalTimeouts.SetInsertionPoint(dummy_normal_timeout); + } + + last_tracking_insertion_point = mTrackingTimeouts.InsertionPoint(); + if (!last_expired_timeout_is_normal) { + // If we ever start setting mTrackingTimeoutInsertionPoint to a non-dummy timeout, + // the logic in ResetTimersForThrottleReduction will need to change. + mTrackingTimeouts.SetInsertionPoint(dummy_tracking_timeout); + } - if (timeout->mFiringDepth != firingDepth) { - // We skip the timeout since it's on the list to run at another - // depth. - - continue; - } + // We stop iterating each list when we go past the last expired timeout from + // that list that we have observed above. That timeout will either be the + // dummy timeout for the list that the last expired timeout came from, or it + // will be the next item after the last timeout we looked at (or nullptr if + // we have exhausted the entire list while looking for the last expired + // timeout). + { + // Use a nested scope in order to make sure the strong references held by + // the iterator are freed after the loop. + OrderedTimeoutIterator runIter(mNormalTimeouts, + mTrackingTimeouts, + last_expired_normal_timeout ? + last_expired_normal_timeout->getNext() : + nullptr, + last_expired_tracking_timeout ? + last_expired_tracking_timeout->getNext() : + nullptr); + while (!mWindow.IsFrozen()) { + Timeout* timeout = runIter.Next(); + MOZ_ASSERT(timeout != dummy_normal_timeout && + timeout != dummy_tracking_timeout, + "We should have stopped iterating before getting to the dummy timeout"); + if (!timeout) { + // We have run out of timeouts! + break; + } + runIter.UpdateIterator(); - if (mWindow.IsSuspended()) { - // Some timer did suspend us. Make sure the - // rest of the timers get executed later. - timeout->mFiringDepth = 0; - continue; - } - - // The timeout is on the list to run at this depth, go ahead and - // process it. - - // Get the script context (a strong ref to prevent it going away) - // for this timeout and ensure the script language is enabled. - nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal(); + if (timeout->mFiringDepth != firingDepth) { + // We skip the timeout since it's on the list to run at another + // depth. + continue; + } - if (!scx) { - // No context means this window was closed or never properly - // initialized for this language. - continue; - } + if (mWindow.IsSuspended()) { + // Some timer did suspend us. Make sure the + // rest of the timers get executed later. + timeout->mFiringDepth = 0; + continue; + } + + // The timeout is on the list to run at this depth, go ahead and + // process it. - // This timeout is good to run - bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx); + // Get the script context (a strong ref to prevent it going away) + // for this timeout and ensure the script language is enabled. + nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal(); + + if (!scx) { + // No context means this window was closed or never properly + // initialized for this language. + continue; + } + + // This timeout is good to run + bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx); + + if (timeout_was_cleared) { + // Make sure the iterator isn't holding any Timeout objects alive. + runIter.Clear(); - if (timeout_was_cleared) { - // The running timeout's window was cleared, this means that - // ClearAllTimeouts() was called from a *nested* call, possibly - // through a timeout that fired while a modal (to this window) - // dialog was open or through other non-obvious paths. - MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); - Unused << timeoutExtraRef.forget().take(); + // The running timeout's window was cleared, this means that + // ClearAllTimeouts() was called from a *nested* call, possibly + // through a timeout that fired while a modal (to this window) + // dialog was open or through other non-obvious paths. + // Note that if the last expired timeout corresponding to each list + // is null, then we should expect a refcount of two, since the + // dummy timeout for this queue was never injected into it, and the + // corresponding timeoutExtraRef variable hasn't been cleared yet. + if (last_expired_timeout_is_normal) { + MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(1), "dummy_normal_timeout may leak"); + MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(2), "dummy_tracking_timeout may leak"); + Unused << timeoutExtraRef1.forget().take(); + } else { + MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(2), "dummy_normal_timeout may leak"); + MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(1), "dummy_tracking_timeout may leak"); + Unused << timeoutExtraRef2.forget().take(); + } + + mNormalTimeouts.SetInsertionPoint(last_normal_insertion_point); + mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point); + + return; + } - mTimeouts.SetInsertionPoint(last_insertion_point); + // If we have a regular interval timer, we re-schedule the + // timeout, accounting for clock drift. + bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout); + + // Running a timeout can cause another timeout to be deleted, so + // we need to reset the pointer to the following timeout. + runIter.UpdateIterator(); + + timeout->remove(); - return; + if (needsReinsertion) { + // Insert interval timeout onto the corresponding list sorted in + // deadline order. AddRefs timeout. + if (runIter.PickedTrackingIter()) { + mTrackingTimeouts.Insert(timeout, + mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining + : Timeouts::SortBy::TimeWhen); + } else { + mNormalTimeouts.Insert(timeout, + mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining + : Timeouts::SortBy::TimeWhen); + } + } + + // Release the timeout struct since it's possibly out of the list + timeout->Release(); } - - // If we have a regular interval timer, we re-schedule the - // timeout, accounting for clock drift. - bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout); - - // Running a timeout can cause another timeout to be deleted, so - // we need to reset the pointer to the following timeout. - nextTimeout = timeout->getNext(); - - timeout->remove(); - - if (needsReinsertion) { - // Insert interval timeout onto list sorted in deadline order. - // AddRefs timeout. - mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining - : Timeouts::SortBy::TimeWhen); - } - - // Release the timeout struct since it's possibly out of the list - timeout->Release(); } // Take the dummy timeout off the head of the list - dummy_timeout->remove(); - timeoutExtraRef = nullptr; - MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); + if (dummy_normal_timeout->isInList()) { + dummy_normal_timeout->remove(); + } + timeoutExtraRef1 = nullptr; + MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(1), "dummy_normal_timeout may leak"); + if (dummy_tracking_timeout->isInList()) { + dummy_tracking_timeout->remove(); + } + timeoutExtraRef2 = nullptr; + MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(1), "dummy_tracking_timeout may leak"); - mTimeouts.SetInsertionPoint(last_insertion_point); + mNormalTimeouts.SetInsertionPoint(last_normal_insertion_point); + mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point); MaybeApplyBackPressure(); } void TimeoutManager::MaybeApplyBackPressure() { MOZ_ASSERT(NS_IsMainThread()); @@ -627,23 +760,32 @@ nsresult TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS) { MOZ_ASSERT(aPreviousThrottleDelayMS > 0); if (mWindow.IsFrozen() || mWindow.IsSuspended()) { return NS_OK; } + auto minTimeout = DOMMinTimeoutValue(); Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining : Timeouts::SortBy::TimeWhen; - return mTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS, - DOMMinTimeoutValue(), - sortBy, - mWindow.GetThrottledEventQueue()); + nsresult rv = mNormalTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS, + minTimeout, + sortBy, + mWindow.GetThrottledEventQueue()); + NS_ENSURE_SUCCESS(rv, rv); + rv = mTrackingTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS, + minTimeout, + sortBy, + mWindow.GetThrottledEventQueue()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; } nsresult TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS, int32_t aMinTimeoutValueMS, SortBy aSortBy, ThrottledEventQueue* aQueue) { @@ -731,17 +873,17 @@ TimeoutManager::Timeouts::ResetTimersFor return NS_OK; } void TimeoutManager::ClearAllTimeouts() { bool seenRunningTimeout = false; - ForEachTimeout([&](Timeout* aTimeout) { + ForEachUnorderedTimeout([&](Timeout* aTimeout) { /* If RunTimeout() is higher up on the stack for this window, e.g. as a result of document.write from a timeout, then we need to reset the list insertion point for newly-created timeouts in case the user adds a timeout, before we pop the stack back to RunTimeout. */ if (mRunningTimeout == aTimeout) { seenRunningTimeout = true; } @@ -759,21 +901,23 @@ TimeoutManager::ClearAllTimeouts() // cleared and taken out of the list of timeouts aTimeout->mCleared = true; // Drop the count since we're removing it from the list. aTimeout->Release(); }); if (seenRunningTimeout) { - mTimeouts.SetInsertionPoint(nullptr); + mNormalTimeouts.SetInsertionPoint(nullptr); + mTrackingTimeouts.SetInsertionPoint(nullptr); } // Clear out our list - mTimeouts.Clear(); + mNormalTimeouts.Clear(); + mTrackingTimeouts.Clear(); } void TimeoutManager::Timeouts::Insert(Timeout* aTimeout, SortBy aSortBy) { // Start at mLastTimeout and go backwards. Don't go further than insertion // point, though. This optimizes for the common case of insertion at the end. Timeout* prevSibling; @@ -821,27 +965,27 @@ TimeoutManager::EndRunningTimeout(Timeou --gRunningTimeoutDepth; mRunningTimeout = aTimeout; } void TimeoutManager::UnmarkGrayTimers() { - ForEachTimeout([](Timeout* aTimeout) { + ForEachUnorderedTimeout([](Timeout* aTimeout) { if (aTimeout->mScriptHandler) { aTimeout->mScriptHandler->MarkForCC(); } }); } void TimeoutManager::Suspend() { - ForEachTimeout([](Timeout* aTimeout) { + ForEachUnorderedTimeout([](Timeout* aTimeout) { // Leave the timers with the current time remaining. This will // cause the timers to potentially fire when the window is // Resume()'d. Time effectively passes while suspended. // Drop the XPCOM timer; we'll reschedule when restoring the state. if (aTimeout->mTimer) { aTimeout->mTimer->Cancel(); aTimeout->mTimer = nullptr; @@ -854,17 +998,17 @@ TimeoutManager::Suspend() } void TimeoutManager::Resume() { TimeStamp now = TimeStamp::Now(); DebugOnly<bool> _seenDummyTimeout = false; - ForEachTimeout([&](Timeout* aTimeout) { + ForEachUnorderedTimeout([&](Timeout* aTimeout) { // There's a chance we're being called with RunTimeout on the stack in which // case we have a dummy timeout in the list that *must not* be resumed. It // can be identified by a null mWindow. if (!aTimeout->mWindow) { NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!"); _seenDummyTimeout = true; return; } @@ -900,17 +1044,17 @@ TimeoutManager::Resume() aTimeout->AddRef(); }); } void TimeoutManager::Freeze() { TimeStamp now = TimeStamp::Now(); - ForEachTimeout([&](Timeout* aTimeout) { + ForEachUnorderedTimeout([&](Timeout* aTimeout) { // Save the current remaining time for this timeout. We will // re-apply it when the window is Thaw()'d. This effectively // shifts timers to the right as if time does not pass while // the window is frozen. if (aTimeout->mWhen > now) { aTimeout->mTimeRemaining = aTimeout->mWhen - now; } else { aTimeout->mTimeRemaining = TimeDuration(0); @@ -923,24 +1067,32 @@ TimeoutManager::Freeze() } void TimeoutManager::Thaw() { TimeStamp now = TimeStamp::Now(); DebugOnly<bool> _seenDummyTimeout = false; - ForEachTimeout([&](Timeout* aTimeout) { + ForEachUnorderedTimeout([&](Timeout* aTimeout) { // There's a chance we're being called with RunTimeout on the stack in which // case we have a dummy timeout in the list that *must not* be resumed. It // can be identified by a null mWindow. if (!aTimeout->mWindow) { NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!"); _seenDummyTimeout = true; return; } // Set mWhen back to the time when the timer is supposed to fire. aTimeout->mWhen = now + aTimeout->mTimeRemaining; MOZ_ASSERT(!aTimeout->mTimer); }); } + +bool +TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId) +{ + return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) { + return aTimeout->mTimeoutId == aTimeoutId; + }); +}
--- a/dom/base/TimeoutManager.h +++ b/dom/base/TimeoutManager.h @@ -13,30 +13,36 @@ class nsITimeoutHandler; class nsGlobalWindow; namespace mozilla { class ThrottledEventQueue; namespace dom { +class OrderedTimeoutIterator; + // This class manages the timeouts in a Window's setTimeout/setInterval pool. class TimeoutManager final { public: explicit TimeoutManager(nsGlobalWindow& aWindow); TimeoutManager(const TimeoutManager& rhs) = delete; void operator=(const TimeoutManager& rhs) = delete; bool IsRunningTimeout() const { return mTimeoutFiringDepth > 0; } static uint32_t GetNestingLevel() { return sNestingLevel; } static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; } - bool HasTimeouts() const { return !mTimeouts.IsEmpty(); } + bool HasTimeouts() const + { + return !mNormalTimeouts.IsEmpty() || + !mTrackingTimeouts.IsEmpty(); + } nsresult SetTimeout(nsITimeoutHandler* aHandler, int32_t interval, bool aIsInterval, mozilla::dom::Timeout::Reason aReason, int32_t* aReturn); void ClearTimeout(int32_t aTimerId, mozilla::dom::Timeout::Reason aReason); @@ -78,36 +84,43 @@ public: void Suspend(); void Resume(); void Freeze(); void Thaw(); // Initialize TimeoutManager before the first time it is accessed. static void Initialize(); - // Run some code for each Timeout in our list. + // Exposed only for testing + bool IsTimeoutTracking(uint32_t aTimeoutId); + + // Run some code for each Timeout in our list. Note that this function + // doesn't guarantee that Timeouts are iterated in any particular order. template <class Callable> - void ForEachTimeout(Callable c) + void ForEachUnorderedTimeout(Callable c) { - mTimeouts.ForEach(c); + mNormalTimeouts.ForEach(c); + mTrackingTimeouts.ForEach(c); } - // Run some code for each Timeout in our list, but let the callback cancel - // the iteration by returning true. + // Run some code for each Timeout in our list, but let the callback cancel the + // iteration by returning true. Note that this function doesn't guarantee + // that Timeouts are iterated in any particular order. template <class Callable> - void ForEachTimeoutAbortable(Callable c) + void ForEachUnorderedTimeoutAbortable(Callable c) { - mTimeouts.ForEachAbortable(c); + if (!mNormalTimeouts.ForEachAbortable(c)) { + mTrackingTimeouts.ForEachAbortable(c); + } } private: nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS); private: - typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList; struct Timeouts { Timeouts() : mTimeoutInsertionPoint(nullptr) { } // Insert aTimeout into the list, before all timeouts that would // fire after it, but no earlier than mTimeoutInsertionPoint, if any. @@ -144,44 +157,55 @@ private: { for (Timeout* timeout = GetFirst(); timeout; timeout = timeout->getNext()) { c(timeout); } } + // Returns true when a callback aborts iteration. template <class Callable> - void ForEachAbortable(Callable c) + bool ForEachAbortable(Callable c) { for (Timeout* timeout = GetFirst(); timeout; timeout = timeout->getNext()) { if (c(timeout)) { - break; + return true; } } + return false; } + friend class OrderedTimeoutIterator; + private: + typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList; + // mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is // non-null. In that case, the dummy timeout pointed to by // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts // that come after it. TimeoutList mTimeoutList; // If mTimeoutInsertionPoint is non-null, insertions should happen after it. // This is a dummy timeout at the moment; if that ever changes, the logic in // ResetTimersForThrottleReduction needs to change. mozilla::dom::Timeout* mTimeoutInsertionPoint; }; + friend class OrderedTimeoutIterator; + // Each nsGlobalWindow object has a TimeoutManager member. This reference // points to that holder object. nsGlobalWindow& mWindow; - Timeouts mTimeouts; + // The list of timeouts coming from non-tracking scripts. + Timeouts mNormalTimeouts; + // The list of timeouts coming from scripts on the tracking protection list. + Timeouts mTrackingTimeouts; uint32_t mTimeoutIdCounter; uint32_t mTimeoutFiringDepth; mozilla::dom::Timeout* mRunningTimeout; // The current idle request callback timeout handle uint32_t mIdleCallbackTimeoutCounter; int32_t mBackPressureDelayMS;
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -106,16 +106,17 @@ #include "nsNetUtil.h" #include "nsDocument.h" #include "HTMLImageElement.h" #include "mozilla/css/ImageLoader.h" #include "mozilla/layers/APZCTreeManager.h" // for layers::ZoomToRectBehavior #include "mozilla/dom/Promise.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/dom/TimeoutManager.h" #ifdef XP_WIN #undef GetClassName #endif using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::ipc; @@ -4087,16 +4088,31 @@ nsDOMWindowUtils::GetGpuProcessPid(int32 *aPid = pm->GPUProcessPid(); } else { *aPid = -1; } return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::IsTimeoutTracking(uint32_t aTimeoutId, bool* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = false; + + nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow); + NS_ENSURE_STATE(window); + nsCOMPtr<nsPIDOMWindowInner> innerWindow = window->GetCurrentInnerWindow(); + NS_ENSURE_STATE(innerWindow); + + *aResult = innerWindow->TimeoutManager().IsTimeoutTracking(aTimeoutId); + return NS_OK; +} + NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsTranslationNodeList) NS_IMPL_RELEASE(nsTranslationNodeList)
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1931,17 +1931,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) #endif NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) if (tmp->mTimeoutManager) { - tmp->mTimeoutManager->ForEachTimeout([&cb](Timeout* timeout) { + tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) { cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout)); }); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
--- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -39,17 +39,16 @@ #include "nsLayoutUtils.h" #include "nsIContentPolicy.h" #include "nsSVGEffects.h" #include "gfxPrefs.h" #include "mozAutoDocUpdate.h" #include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/CycleCollectedJSContext.h" #include "mozilla/EventStates.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ImageTracker.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/Preferences.h" #ifdef LoadImage // Undefine LoadImage to prevent naming conflict with Windows. @@ -144,19 +143,16 @@ nsImageLoadingContent::Notify(imgIReques // We should definitely have a request here MOZ_ASSERT(aRequest, "no request?"); NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest, "Unknown request"); } { - MOZ_RELEASE_ASSERT(js::AllowGCBarriers(CycleCollectedJSContext::Get()->Context()), - "ImageObservers can be implement in JS, so they should not be called during painting. See bug 1311841"); - nsAutoScriptBlocker scriptBlocker; for (ImageObserver* observer = &mObserverList, *next; observer; observer = next) { next = observer->mNext; if (observer->mObserver) { observer->mObserver->Notify(aRequest, aType, aData); }
--- a/dom/base/test/file_timer_flood.html +++ b/dom/base/test/file_timer_flood.html @@ -1,19 +1,29 @@ <!DOCTYPE HTML> <html> <body> <script> let count = 0; -function cb() { +let last_timer_set = 0; +let last_timer_observed = 0; +function cb(timer_observed) { + if (timer_observed > last_timer_observed) { + // In order to make the test more efficient, we don't use the SimpleTest + // ok() function to avoid generating one test assertion per one of these + // checks. We only send a message to the parent which fails the test if + // we detect out of order firing of timeouts. + window.parent.postMessage('OUT_OF_ORDER', '*'); + } + last_timer_observed = timer_observed; count += 1; // Notify our parent that we are ready once the timer flood has // warmed up. if (count === 10000) { window.parent.postMessage('STARTED', '*'); } - setTimeout(cb, 0); - setTimeout(cb, 0); + last_timer_set = setTimeout(cb.bind(last_timer_set), 0); + last_timer_set = setTimeout(cb.bind(last_timer_set), 0); } addEventListener('load', cb); </script> </body> </html>
--- a/dom/base/test/test_timer_flood.html +++ b/dom/base/test/test_timer_flood.html @@ -8,32 +8,43 @@ </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> SimpleTest.waitForExplicitFinish(); +// This test takes a long time to run and it times out on Android debug as a result. +SimpleTest.requestLongerTimeout(5); function onLoad() { return new Promise(resolve => { addEventListener('load', resolve, { once: true }); }); } +function setPrefs() { + // Put timeouts randomly in the tracking or normal buffer. We do this in order to + // test to ensure that by default, this will not change the scheduling of timeouts. + return SpecialPowers.pushPrefEnv({"set": [["dom.timeout_bucketing_strategy", 3]]}); +} + // Create a frame that executes a timer flood. The frame signals // that is ready once the flood has had a chance to warm up. function withFloodFrame() { - return new Promise(resolve => { + return new Promise((resolve, reject) => { let frame = document.createElement('iframe'); addEventListener('message', function onMsg(evt) { if (evt.data === 'STARTED') { removeEventListener('message', onMsg); resolve(frame); + } else if (evt.data == 'OUT_OF_ORDER') { + ok(false, "Out of order timeout observed"); + reject(); } }); frame.src = 'file_timer_flood.html'; document.body.appendChild(frame); }); } // Test that we can load documents during a timer flood. @@ -70,17 +81,19 @@ function testRequestAnimationFrame() { } }; requestAnimationFrame(nextFrame); }); } let floodFrame; -onLoad().then(_ => { +onLoad() +.then(setPrefs) +.then(_ => { // Start a timer flood in a frame. return withFloodFrame(); }).then(frame => { floodFrame = frame; // Next we are going to start a bunch of asynchronous work that we // expect to complete in spite of the timer flood. The type of work // is a bit arbitrary, but is chosen to reflect the kinds of things @@ -104,13 +117,15 @@ onLoad().then(_ => { // Wait for all tests to finish. If we do not handle the timer flood // well then this will likely time out. return Promise.all(tests); }).then(_ => { ok(true, 'completed tests without timing out'); floodFrame.remove(); SimpleTest.finish(); +}).catch(_ => { + SimpleTest.finish(); }); </script> </pre> </body> </html>
--- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -265,18 +265,22 @@ public: // ------------------------------------------------------------------------- // Writing to the drawing buffer /* Implemented in WebGLContext void VertexAttribDivisor(GLuint index, GLuint divisor); void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount); void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount); */ - void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset); + void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, + GLenum type, WebGLintptr byteOffset) + { + DrawElements(mode, count, type, byteOffset); + } // ------------------------------------------------------------------------ // Multiple Render Targets - WebGL2ContextMRTs.cpp /* Implemented in WebGLContext void DrawBuffers(const dom::Sequence<GLenum>& buffers); */ private: @@ -320,16 +324,18 @@ public: void SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param); void GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname, JS::MutableHandleValue retval); // ------------------------------------------------------------------------- // Sync objects - WebGL2ContextSync.cpp + const GLuint64 kMaxClientWaitSyncTimeoutNS = 1000 * 1000 * 1000; // 1000ms in ns. + already_AddRefed<WebGLSync> FenceSync(GLenum condition, GLbitfield flags); bool IsSync(const WebGLSync* sync); void DeleteSync(WebGLSync* sync); GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout); void WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout); void GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname, JS::MutableHandleValue retval);
--- a/dom/canvas/WebGL2ContextBuffers.cpp +++ b/dom/canvas/WebGL2ContextBuffers.cpp @@ -126,18 +126,18 @@ WebGL2Context::GetBufferSubData(GLenum t } const GLsizeiptr glByteLen(byteLen); //// gl->MakeCurrent(); const ScopedLazyBind readBind(gl, target, buffer); - const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen, - LOCAL_GL_MAP_READ_BIT); - // Warning: Possibly shared memory. See bug 1225033. if (byteLen) { + const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen, + LOCAL_GL_MAP_READ_BIT); + // Warning: Possibly shared memory. See bug 1225033. memcpy(bytes, mappedBytes, byteLen); + gl->fUnmapBuffer(target); } - gl->fUnmapBuffer(target); } } // namespace mozilla
deleted file mode 100644 --- a/dom/canvas/WebGL2ContextDraw.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "WebGL2Context.h" - -namespace mozilla { - -// ------------------------------------------------------------------------- -// Writing to the drawing buffer - -void -WebGL2Context::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset) -{ - GenerateWarning("drawRangeElements: Not Implemented."); -} - -} // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp +++ b/dom/canvas/WebGL2ContextState.cpp @@ -107,23 +107,23 @@ WebGL2Context::GetParameter(JSContext* c // value is 4 * GL_MAX_VARYING_VECTORS GLint val; gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &val); return JS::Int32Value(4*val); } /* GLint64 */ case LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL: - return JS::NumberValue(0); // TODO + return JS::NumberValue(kMaxClientWaitSyncTimeoutNS); case LOCAL_GL_MAX_ELEMENT_INDEX: // GL_MAX_ELEMENT_INDEX becomes available in GL 4.3 or via ES3 // compatibility if (!gl->IsSupported(gl::GLFeature::ES3_compatibility)) - return JS::NumberValue(0); + return JS::NumberValue(UINT32_MAX); /*** fall through to fGetInteger64v ***/ MOZ_FALLTHROUGH; case LOCAL_GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: case LOCAL_GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE: { GLint64 val;
--- a/dom/canvas/WebGL2ContextSync.cpp +++ b/dom/canvas/WebGL2ContextSync.cpp @@ -62,16 +62,22 @@ WebGL2Context::ClientWaitSync(const WebG if (!ValidateObject(funcName, sync)) return LOCAL_GL_WAIT_FAILED; if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) { ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName); return LOCAL_GL_WAIT_FAILED; } + if (timeout > kMaxClientWaitSyncTimeoutNS) { + ErrorInvalidOperation("%s: `timeout` must not exceed %s nanoseconds.", funcName, + "MAX_CLIENT_WAIT_TIMEOUT_WEBGL"); + return LOCAL_GL_WAIT_FAILED; + } + MakeContextCurrent(); return gl->fClientWaitSync(sync.mGLName, flags, timeout); } void WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout) { const char funcName[] = "waitSync";
--- a/dom/canvas/WebGL2ContextUniforms.cpp +++ b/dom/canvas/WebGL2ContextUniforms.cpp @@ -177,16 +177,24 @@ WebGL2Context::GetActiveUniforms(JSConte return; if (!ValidateUniformEnum(this, pname, funcName)) return; if (!ValidateObject("getActiveUniforms: program", program)) return; + const auto& numActiveUniforms = program.LinkInfo()->uniforms.size(); + for (const auto& curIndex : uniformIndices) { + if (curIndex >= numActiveUniforms) { + ErrorInvalidValue("%s: Too-large active uniform index queried.", funcName); + return; + } + } + const auto& count = uniformIndices.Length(); JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count)); UniquePtr<GLint[]> samples(new GLint[count]); if (!array || !samples) { ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName); return; }
--- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -969,16 +969,17 @@ public: private: // State tracking slots realGLboolean mDitherEnabled; realGLboolean mRasterizerDiscardEnabled; realGLboolean mScissorTestEnabled; realGLboolean mDepthTestEnabled; realGLboolean mStencilTestEnabled; + GLenum mGenerateMipmapHint; bool ValidateCapabilityEnum(GLenum cap, const char* info); realGLboolean* GetStateTrackingSlot(GLenum cap); // ----------------------------------------------------------------------------- // Texture funcions (WebGLContextTextures.cpp) public: void ActiveTexture(GLenum texUnit);
--- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -687,19 +687,16 @@ WebGLContext::GetFramebufferAttachmentPa default: ErrorInvalidEnum("%s: With the default framebuffer, can only query COLOR, DEPTH," " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", funcName); return JS::NullValue(); } return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT); - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return JS::NullValue(); - //////////////// case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: if (attachment == LOCAL_GL_BACK) return JS::NumberValue(8); return JS::NumberValue(0); @@ -942,16 +939,18 @@ WebGLContext::Hint(GLenum target, GLenum { if (IsContextLost()) return; bool isValid = false; switch (target) { case LOCAL_GL_GENERATE_MIPMAP_HINT: + mGenerateMipmapHint = mode; + // Deprecated and removed in desktop GL Core profiles. if (gl->IsCoreProfile()) return; isValid = true; break; case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: @@ -1460,17 +1459,17 @@ ValidateReadPixelsFormatAndType(const we } if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) { webgl->ErrorInvalidEnum("readPixels: Invalid type: 0x%04x", pi.type); return false; } MOZ_ASSERT(gl->IsCurrent()); - if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { + if (gl->IsGLES()) { const auto auxFormat = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT); const auto auxType = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE); if (auxFormat && auxType && pi.format == auxFormat && pi.type == auxType) { return true; }
--- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -381,45 +381,53 @@ WebGLContext::GetParameter(JSContext* cx case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL: case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS: case LOCAL_GL_DEPTH_FUNC: case LOCAL_GL_BLEND_SRC_RGB: case LOCAL_GL_BLEND_SRC_ALPHA: case LOCAL_GL_BLEND_DST_RGB: case LOCAL_GL_BLEND_DST_ALPHA: case LOCAL_GL_BLEND_EQUATION_RGB: - case LOCAL_GL_BLEND_EQUATION_ALPHA: - case LOCAL_GL_GENERATE_MIPMAP_HINT: { + case LOCAL_GL_BLEND_EQUATION_ALPHA: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::NumberValue(uint32_t(i)); } + + case LOCAL_GL_GENERATE_MIPMAP_HINT: + return JS::NumberValue(mGenerateMipmapHint); + case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { const webgl::FormatUsageInfo* usage; uint32_t width, height; if (!ValidateCurFBForRead(funcName, &usage, &width, &height)) return JS::NullValue(); GLint i = 0; - if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { + if (gl->IsGLES()) { + // ES2_compatibility always returns UNSIGNED_BYTE here, so + // branch on actual IsGLES(). + // Also OSX+NV generates an error here. gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_UNSIGNED_BYTE; } - return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: { const webgl::FormatUsageInfo* usage; uint32_t width, height; if (!ValidateCurFBForRead(funcName, &usage, &width, &height)) return JS::NullValue(); GLint i = 0; - if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { + if (gl->IsGLES()) { + // ES2_compatibility always returns UNSIGNED_BYTE here, so + // branch on actual IsGLES(). + // Also OSX+NV generates an error here. gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_RGBA; } // OpenGL ES 3.0.4 p112 Table 3.2 shows that read format SRGB_ALPHA is // not supported. And if internal format of fbo is SRGB8_ALPHA8, then // IMPLEMENTATION_COLOR_READ_FORMAT is SRGB_ALPHA which is not supported
--- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -496,16 +496,17 @@ WebGLContext::InitAndValidateGL(FailureR AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, mStencilValueMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK, mStencilWriteMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK, mStencilWriteMaskBack); mDitherEnabled = true; mRasterizerDiscardEnabled = false; mScissorTestEnabled = false; + mGenerateMipmapHint = LOCAL_GL_DONT_CARE; // Bindings, etc. mActiveTexture = 0; mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; mEmitContextLostErrorOnce = true; mWebGLError = LOCAL_GL_NO_ERROR; mUnderlyingGLError = LOCAL_GL_NO_ERROR;
--- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -233,26 +233,36 @@ WebGLTexture::IsMipmapComplete(uint32_t cur.mFormat != baseImageInfo.mFormat) { return false; } } // GLES 3.0.4, p158: // "[...] until the last array is reached with dimension 1 x 1 x 1." - if (refWidth == 1 && - refHeight == 1 && - refDepth == 1) - { - break; + if (mTarget == LOCAL_GL_TEXTURE_3D) { + if (refWidth == 1 && + refHeight == 1 && + refDepth == 1) + { + break; + } + + refDepth = std::max(uint32_t(1), refDepth / 2); + } else { + // TEXTURE_2D_ARRAY may have depth != 1, but that's normal. + if (refWidth == 1 && + refHeight == 1) + { + break; + } } refWidth = std::max(uint32_t(1), refWidth / 2); refHeight = std::max(uint32_t(1), refHeight / 2); - refDepth = std::max(uint32_t(1), refDepth / 2); } return true; } bool WebGLTexture::IsCubeComplete() const { @@ -1009,17 +1019,17 @@ WebGLTexture::IsTexture() const // See this discussion: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html void WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam, GLfloat* maybeFloatParam) { MOZ_ASSERT(maybeIntParam || maybeFloatParam); - GLint intParam = maybeIntParam ? *maybeIntParam : GLint(*maybeFloatParam); + GLint intParam = maybeIntParam ? *maybeIntParam : GLint(roundf(*maybeFloatParam)); GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam); bool isPNameValid = false; switch (pname) { // GLES 2.0.25 p76: case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_MIN_FILTER:
--- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -439,16 +439,21 @@ WebGLTexture::TexSubImage(const char* fu { const GLint border = 0; dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx()); const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height, depth, border, pi, src, &scopedArr); if (!blob) return; + if (!blob->HasData()) { + mContext->ErrorInvalidValue("%s: Source must not be null.", funcName); + return; + } + TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, pi, blob.get()); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// static bool ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName, @@ -2014,16 +2019,23 @@ DoCopyTexOrSubImage(WebGLContext* webgl, return true; } while (false); if (error == LOCAL_GL_OUT_OF_MEMORY) { webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", funcName); return false; } + if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) { + webgl->ErrorImplementationBug("%s: ANGLE is particular about CopyTexSubImage" + " formats matching exactly.", + funcName); + return false; + } + MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors."); webgl->GenerateWarning("%s: Unexpected error during texture copy. Context lost.", funcName); webgl->ForceLoseContext(); return false; } // There is no CopyTexImage3D.
--- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -68,17 +68,16 @@ SOURCES += [ # WebGL Sources UNIFIED_SOURCES += [ 'TexUnpackBlob.cpp', 'WebGL1Context.cpp', 'WebGL1ContextUniforms.cpp', 'WebGL2Context.cpp', 'WebGL2ContextBuffers.cpp', - 'WebGL2ContextDraw.cpp', 'WebGL2ContextFramebuffers.cpp', 'WebGL2ContextMRTs.cpp', 'WebGL2ContextPrograms.cpp', 'WebGL2ContextQueries.cpp', 'WebGL2ContextRenderbuffers.cpp', 'WebGL2ContextSamplers.cpp', 'WebGL2ContextState.cpp', 'WebGL2ContextSync.cpp',
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini +++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini @@ -4581,17 +4581,16 @@ skip-if = (os == 'android' || os == 'lin skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__samplers__sampler-drawing-test.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__samplers__samplers.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__state__gl-enum-tests.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__state__gl-get-calls.html] -fail-if = (os == 'mac') skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__state__gl-getstring.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__state__gl-object-get-calls.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__transform_feedback__transform_feedback.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html] @@ -5764,17 +5763,17 @@ skip-if = (os == 'android' || os == 'lin skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__gl-scissor-test.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__gl-viewport-test.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__line-loop-tri-fan.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__many-draw-calls.html] -skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) +skip-if = debug || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__more-than-65536-indices.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__multisample-corruption.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__negative-one-index.html] fail-if = (os == 'mac') skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__point-no-attributes.html] @@ -5789,18 +5788,17 @@ skip-if = (os == 'android' || os == 'lin skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__simple.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__rendering__triangle.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__state__gl-enable-enum-test.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__state__gl-get-calls.html] -skip-if = (os == 'mac' && debug) || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) -fail-if = (os == 'mac') +skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__state__gl-geterror.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__state__gl-initial-state.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__state__state-uneffected-after-compositing.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1')) [generated/test_2_conformance__textures__misc__copy-tex-image-2d-formats.html] skip-if = (os == 'mac') || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini +++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini @@ -296,16 +296,19 @@ skip-if = (os == 'android') skip-if = (os == 'android') [generated/test_conformance__glsl__variables__gl-fragcoord.html] # Crashes skip-if = (os == 'android') [generated/test_conformance__rendering__many-draw-calls.html] # Crashes on Android # Times-out on DEBUG builds skip-if = (os == 'android') || debug +[generated/test_2_conformance__rendering__many-draw-calls.html] +# Appears to just take too long on debug, most of the time. +skip-if = debug [generated/test_conformance__uniforms__out-of-bounds-uniform-array-access.html] # Crashes skip-if = (os == 'android') || (os == 'mac' && os_version == '10.6') [generated/test_conformance__glsl__samplers__glsl-function-texture2dproj.html] # Crashes skip-if = (os == 'android') [generated/test_conformance__rendering__framebuffer-switch.html] # Crashes @@ -528,35 +531,27 @@ fail-if = (os == 'mac' && os_version == fail-if = (os == 'mac' && os_version == '10.8') [generated/test_conformance__glsl__variables__gl-pointcoord.html] fail-if = (os == 'mac' && os_version == '10.8') [generated/test_conformance__limits__gl-max-texture-dimensions.html] fail-if = (os == 'mac' && os_version == '10.8') #################### # failure on OSX -[generated/test_2_conformance2__state__gl-get-calls.html] -fail-if = (os == 'mac') [generated/test_conformance__extensions__angle-instanced-arrays.html] fail-if = (os == 'mac') [generated/test_conformance__glsl__misc__shaders-with-invariance.html] fail-if = (os == 'mac') [generated/test_2_conformance2__extensions__ext-color-buffer-float.html] skip-if = (os == 'mac' && debug) [generated/test_2_conformance__limits__gl-line-width.html] skip-if = (os == 'mac') [generated/test_2_conformance__misc__type-conversion-test.html] skip-if = (os == 'mac' && debug) -[generated/test_2_conformance__state__gl-get-calls.html] -# Hit MOZ_GL_DEBUG_ABORT_ON_ERROR on debug build -fail-if = (os == 'mac') -skip-if = (os == 'mac' && debug) - - ######################################################################## ######################################################################## # Win [generated/test_2_conformance__ogles__GL__built_in_varying_array_out_of_bounds__built_in_varying_array_out_of_bounds_001_to_001.html] # time out crash skip-if = (os == 'win') [generated/test_conformance__ogles__GL__built_in_varying_array_out_of_bounds__built_in_varying_array_out_of_bounds_001_to_001.html]
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3322,16 +3322,30 @@ EventStateManager::PostHandleEvent(nsPre } break; case eDragEnter: case eDragOver: { NS_ASSERTION(aEvent->mClass == eDragEventClass, "Expected a drag event"); + // Check if the drag is occurring inside a scrollable area. If so, scroll + // the area when the mouse is near the edges. + if (mCurrentTarget && aEvent->mMessage == eDragOver) { + nsIFrame* checkFrame = mCurrentTarget; + while (checkFrame) { + nsIScrollableFrame* scrollFrame = do_QueryFrame(checkFrame); + // Break out so only the innermost scrollframe is scrolled. + if (scrollFrame && scrollFrame->DragScroll(aEvent)) { + break; + } + checkFrame = checkFrame->GetParent(); + } + } + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); if (!dragSession) break; // Reset the flag. dragSession->SetOnlyChromeDrop(false); if (mPresContext) { EnsureDocument(mPresContext);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1968,16 +1968,22 @@ interface nsIDOMWindowUtils : nsISupport */ void terminateGPUProcess(); /** * Returns the GPU process pid, or -1 if there is no GPU process. */ readonly attribute int32_t gpuProcessPid; + /** + * Returns true if the given timeout ID is in the list of tracking + * timeouts. + */ + boolean isTimeoutTracking(in unsigned long timeoutId); + // Match WidgetMouseEventBase::buttonType. const long MOUSE_BUTTON_LEFT_BUTTON = 0; const long MOUSE_BUTTON_MIDDLE_BUTTON = 1; const long MOUSE_BUTTON_RIGHT_BUTTON = 2; // Match WidgetMouseEventBase::buttonsFlag. const long MOUSE_BUTTONS_NO_BUTTON = 0x00; const long MOUSE_BUTTONS_LEFT_BUTTON = 0x01;
--- a/dom/media/gtest/TestMP3Demuxer.cpp +++ b/dom/media/gtest/TestMP3Demuxer.cpp @@ -265,16 +265,54 @@ protected: res.mDemuxer = new MP3TrackDemuxer(res.mResource); mTargets.push_back(res); streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath); streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource); mTargets.push_back(streamRes); } + { + MP3Resource res; + res.mFilePath = "small-shot-partial-xing.mp3"; + res.mIsVBR = true; + res.mFileSize = 6825; + res.mMPEGLayer = 3; + res.mMPEGVersion = 1; + res.mID3MajorVersion = 4; + res.mID3MinorVersion = 0; + res.mID3Flags = 0; + res.mID3Size = 24; + res.mDuration = 336686; + res.mDurationError = 0.01f; + res.mSeekError = 0.2f; + res.mSampleRate = 44100; + res.mSamplesPerFrame = 1152; + res.mNumSamples = 12; + res.mNumTrailingFrames = 0; + res.mBitrate = 256000; + res.mSlotSize = 1; + res.mPrivate = 0; + const int syncs[] = { 34, 556, 1078, 1601, 2123, 2646, 3168, 3691, 4213, + 4736, 5258, 5781, 6303 }; + res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 13); + + // No content length can be estimated for CBR stream resources. + MP3Resource streamRes = res; + streamRes.mFileSize = -1; + + res.mResource = new MockMP3MediaResource(res.mFilePath); + res.mDemuxer = new MP3TrackDemuxer(res.mResource); + mTargets.push_back(res); + + streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath); + streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource); + mTargets.push_back(streamRes); + } + for (auto& target: mTargets) { ASSERT_EQ(NS_OK, target.mResource->Open(nullptr)); ASSERT_TRUE(target.mDemuxer->Init()); } } std::vector<MP3Resource> mTargets; };
--- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -46,16 +46,17 @@ TEST_HARNESS_FILES.gtest += [ 'id3v2header.mp3', 'mediasource_test.mp4', 'negative_duration.mp4', 'noise.mp3', 'noise_vbr.mp3', 'short-zero-in-moov.mp4', 'short-zero-inband.mov', 'small-shot-false-positive.mp3', + 'small-shot-partial-xing.mp3', 'small-shot.mp3', 'test.webm', 'test_case_1224361.vp8.ivf', 'test_case_1224363.vp8.ivf', 'test_case_1224369.vp8.ivf', ] include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644 index 0000000000000000000000000000000000000000..99d68e3cbe0cc7d8d1bc84082efbf82d9117af26 GIT binary patch literal 6825 zc%1E+c~BGSx`(@y5CU{U5)x=2VgfOOAa)2U2##Tq&4{2d;)V%02#UxK&T)o>uoD<I znSiK4kO9OMXH;~?Y%D@hR1|e|L`6`?1qNkgkzAOAbMD-#Q>X5&`^UXi=c!6{|B<eI zo%iYA`}XJS$-w}#z(E@}te!a%007MsBu6ZDw0E#)BM6|KlmY*Wkf@lQGY4?y0|9_p zHn4CGmpT0B2>V3x9JzBG`ox(z)N|bZ#H%^9bNtIi{hR+~W=z-t02uEjyrrf>6ae7q zG619xv~n&!0*C<MD4zKa5s>I}g)=Ag<7Y8A`hcA85VO9lhL8|}HE{m^*QX&1awB&4 zoeKTwqqARNX4%ENSr|El7LCw8hPCD!+img-{r$(cqnsK1mV|y(sZ_H+j8^@3PavY4 z%Y`Wr76l0Dv&-Tu9Hvyrb4_i|)e)b^39&*0I<<gi0Ue}u^OL&ge_61?NJCx%Eyt+& zZjyHJysRH*;UNa(YCRs=jj4R+s|gm?s~_a^mb*jhs^dH&w@JyRNl<PYb)*>tXQg&r z$n9o&%R7dJ684SWYqg(i7Ax#j8b%+2GuHg*A?xBfYvOzo6_-sUPt}0B-%&H9Y`Pgr z<yo~O^*X(SARptTf9wj|1ak}BzYIB`+l(H5tb^T@5wZf-2OC7OWGM-Bas(wH7j;mp zy+5SYUb%ZaYQ6KGBifv|TJ5@w7Nz)@VEgo?RqySU&8=zgOpKFLsQ~cB>3fY1Vc=0l zE!!ZQY*%fK=&$&k=N^lvUBQNcN!fszO}~>6Zy@I%4RHiT5|u0hjKyPwPCnQolqe&U z8<t)K??<quh$vT|i@%K~=nA!yM|ao~#!ha#ab%YaGiyRW0Gh8(S+VEc3Hgi(GXh{n zFAcA-Y=TNYpc#2WO@9Ewufi4yAxpFYD9ptXLGTN@sb>q8sV^+zQcOYc0!;JA>X>7( z02hZh0u*#v%TZ>@%!59@|3FIe-ieCa6L(w98<ID*__*A-f4%RXk>B)=S8a=&>+%n; zPKiiBXW)t|Enl!>?>;Wud**b&qkf$90Tp_s_H^3e?j>b~4b+{p-t`>og*b=Onaj~( z{gr}ydB;qP-QvV_M!IZM=0I*<hTb<AQSs9=5}9e<6GetfQ4mhwx)PIL=d15Up=D*A z8{DPCTBkGg>+7tRC0<Ogx=_<L@i#1I3;y$tt8r5uf#;NG5<5nU)Ej7WC0)^Tp>r5k z7DYlh_)DUcfJSGQ;#)U>7&KXhqFZWL{SjpSLZWZB^Og5MwAdo1d5jhHrA=Mh7fHsu z)^YO-1kWN!i6)i|>S#mew!kiO?As}K09-zXS>GP95a;IdVCoxik#`}X-d)mupt(o} zJErfKV|+v!CP1e^I23_>qpXrlSQlS^!Mf_^YF>f-wrj!52QfQ3dC*q4lPfOP(BUi9 zXq_UQ;UchA5}>C~@x1(UyaJ=!!rOGDBkfb%PAXMg>DQK;)p32=m5W+~1IoN>^j!D3 z&4z+k;rkQYdd<x}5@%JI=>w*jJ6-oA{;7gIec=x~Ppo>9^hOrh{n&{9xh|~2*IM={ z-bG*T>{z;Do~@gE&dJ8h#aHAxSvrCu0I$z4uV5}vI0oX=N^`1|hg^>uHmV4BUwJqX zUC}09PUsp1DchYMXC~mI_`nVvK~()%2smd8+205rXDg#JS)!(ak-~u&$5smtO^F`r z=Qi0@l!G?e+2chSqCA~_BDHT>f5QRc<GdQ*V);l8-&qnCC}A+f@{6LHKoMwN+@go( z5z(4JJxpeTo@nWnzg_v`e;B54o2<8Mf89MqXts35WRovl(LQin$y<8QCUI#|t$j-s zRZSzfOm7B&W2dQZb!WaPQq$<g!d9M|R|j+~KKlkEO(^6KhZQl9)V6dhEYKAnP6zmW zB|kl^rygiy49A(0pk!yz0eb0#0Gc~1t>W7XTN9U>dp?ohY`CCSY`|yo>{lUNdPi1* zP6m(2LPD%Nk3H2DY6wK6et=CDs?4!G2+t${B7p07<WmW?!*W@&|DGlH<Mw}beWUtp zj*hvB_a@@EA8)@-Fw#0hmtVF&<V1&aGbzuKDB^X98@A3Xz0Nvz$ldhBn$3Dd+IjLT zu8edv0t7OzEe&_A=irS)9#(mc6rcfOy<j#JQVYjow%ssy{nG@K>J-oD;nzKLCR8cK zB^i;j3sA`7;d<uP9n`yvj3^9KBtBAffu6H-$h7L<&b;5u6=QqCqB5^xEt!?4mHhtV z0u=7Kz>SgLZWr0<j&?Nm@#ONK!^$ur(OL+*>H<70n`MK=#mw2NmJ=45R3&K}Z6~a{ z1vgTEyL_Xis_xClq|v|!6%QV_A&1|D!~3x8?UkC?a^KCJ+jwbpGY{2x^0#Mf*PO@2 z29NB^l!TVpE=zW#24{UZ8!mU3f6;49#GpZ;2(Y&FY}PgQmh~S+H=B~!XshLabHb{o z=R2rXG%|;MLm8Y-Ho}oeU>3pTd1_EQ2r+}`h>QZG5K4!8^@5*6Og#Rq$<#CVKy1MN zYge>GJCfR0esAfqj^^KM>w0xZ((s#mQ@w3*BUkbk-2Zj+>u|$L4{AgC$uXnW@bR`I zfo4$y_gXhTx<0}&syZ@t^S(jy=8^-h$5bLeN{PL}N;@nDO0cld^?5dRVy5gwJh0^G zZkzBDEJy>qs*7WRaiO@P66}^s_Z8!cFd<%MbUcNkDbq_?DC{98$j%xr);+;9AN*<i z9YWW>xzz?T^`UP)cu|`8#$%jKORblqYDwg*T-`-C67C&5<<WM1knJAfJoSDy6zU4! zKaZRVg)N^{pzKQtijMwM1!U0iX<x9l;TM{QCf5a-^KDWBvSy0#p?rbQlc_zG9$SAW zb9K}MyD~ALY>0}EGl(<>3eOhvb<E841kX+_5=nCN#CAmh7sOO=y{qw;QrhsE3;R=h zJJA)W!`|uL7?1=s@ngdu7=va85Yc}@Wfi{~H&1+H?q=H2sQxFq6|Y~kM89V1`dWk? zEih<En|wWV++t5m&9&t#+r4!Q4xDL>2o5T;Cq2l!=B`UzzG|%_mC>kwqj5o0bH{Tt zPY%@#Qx&neJ}a`C^}V;3-f^$p*>x4x5jEXeJDnCrmDf>Y!?M5**bR6E^Mt$LeSC`4 zo#6;)@Uviy+)_#fs$i<nMT*Fl!LXYitgixKNB%{a28yMd-5OwTSrWX%&CoRVX~sbZ zRJ@^UYIs<tEUYPy!l#JL1pc~r_}1cjeq+WkzZ~q~ztph{(@8HB1f(Yl?0_Ex+r_nl zZvmwsF#T&muDFA*2h#YNKovhCeUP7)zKpK`oA~R|>f;ts`011jZ?!*b*D^q5rw3Mi z(=(BR=WXx%eLdABkX2Z7@fP}d^P;X3X%iBo$x2R4WvSb;<P$z;OMDJ9*7zkxuZ8Kd z*9N;r8Jf*Jx3#Mq8v%gT1Vqn<!VAKCWZP}iNtaJ1SdH(qw4cU*Y7efoXGY7-0vmG6 zE8SnT9;u84&)uV;OfQjgjv2m>>?-3q>+nulKo^M6DI1BYtt6DmqZ>)!mA7KA3niYO zu<>d$ixi<W0q%S1LwdS4v$<re5pfE=wywFyFx+f)>1XT0t+5GC<Xc1^qt6OCMq*XC za@0s@M9B9bb#Bo2;OOws7Gfi6PVAXGf)BqS3#ErKQac=+LvPZ&|GO&`u^wsu{O#|j zr(Hb{j^gkKThfLSli!73%-XX7{x$U4Q$Z?MsdzM1>=UhLTCQpjiG(}+1*N0|{(!OQ zvM=j<yLtUT#p54e+BK;889vI2unx=xDAISLxV%_q#ozL)bFJS<4}T-bkmtfSgxvI~ zWY|xy=Z2rDpT6lKC%p9Ys<;EbTsT`-Abh#EvTS2+?a<*vXO4qs-Rh)Mg=Y$?*TccN zxus6D3R$jAjt$F{mVGs^V~zBe%yDt`enXl-9I~{6pEdpsH$l2SOOVbTgS{{#IrIv6 z58kG*m>@+&zZuBOQZ8v`XOb-}E5t&#bD2S_kz;Ag`^9EAx5&<ID`vCZN__7`h`01j zPG39u=3Gf{<5$6pI(IEdf06Y1v+X<YKl)_?dwaI}9D8Rcdw?Kb=A_Bk0-8gfLCEt5 zv!O6nI99N!``}kDv2!ZW*(oglFaK0Q<F119dF9?t^s-+4=G8S27Q4I&^YE_G!aT_q zg;1VhCUJJNmIpwPoqJ>(6U8%WrK>(*C2l~6a>|-#nJH7egCwqKTze6jw~E`-UQ5x~ zse9WhY&F&8=N?|RUb_U#RR{1O0_vSSWHZ1G5~NaCwqfyMOdBuS;}zjf&VvU1cLZ2Q zodtN4Pc~#3f{U0?7Q~1V{DjLQG8_eUMqVZ&UKkAg*6H$aXjYC12FfJvrE~Vc{ErKc zcK&ETsPlC1=0{rXuiNjfNuIP>w{gtovom$cv@BmD%kXdnjH+8~(P7AW;LWiKUIq}B zHTiR>r;*}-<?3OMwFX%=fcWaFunI%ACD{rS-^YH35@Y2AhqLC*SmZN=uUBJ;5RgEs zhk5^7_JoO;$PaQb_6cWm-H`$tqt8gcJH7mU)R)@D4#3usnH&zDR_dw{B0sG{6D_4k zpH+gn+e7vMgV>H|r{Yxv;w!eVRVgfJL@G@autX)>lTu?=%|HSP9)#HaNHhtpJG|aM zckMFguP0}2c1Res6p*gRtKE#Gr>3X6BbR*qR;zt^<or60_nOP97<!%U)18vlKA%rI zHvXxCW8+_#4TVX<?;AFC86DpJ$pni{Nle1{+D|6@<pKaOuDw+b%?sL96q$+{u-xQG zsaYaqufIjzy*z-P>?R7!c|H{`Zw%`=N!TuN)PXk4lVmQEgxYI}RH(^Pvx&+JG|{Z2 zOBPv3$mDK!uEva1xX-!ZGGxe=e^ZcNKyQ`IGv#$H;hAstjJR0F9mzloGUfZyEd>X- zQCWj`O$V~D6=?8MK0k`?1dk+Qmsx@ZrF=oL5BxC}E1HKsNRMDR)f`TqnLwt>78ju+ zPHDAc%h#H=>PH4mf6%$*V^-aV^fApGe6BXju8;J#k$5V4bayem^;uIZ22#YoQoc)6 zuRqsHuDbTPLoN>L4~A3+dfHQMc}eZja=@LpSebJ*(L}<)T3_AjC&|(2m%rK$QB=Kc zO`k&~Z?3$ffw#gdwnJ0fy-Oc+J2D1}$yYzxdmIYMnrdIIt6A$qyc)6*8ud_LrNv9E zP(7-uawxbXL~S>p=aW&?CA8(u$5Prqu&!obR(P6dXgV?_pCO@Qm`X|{6s(w1_wzIm z`s#t*^M~GU*J`J8YQ;eYIB`xxL#o1X@~ij$3pd+dO4Ck1+7NSpV#|-z#&l0#{nBtW z*;jk$AEDqF{8wf};Wc3_^s{dBuNwP5sc<aGk}&aSDE#FDKwo@&|I262{gN`VBhvfx za=d#4%xd$9`%acZlX{CUShIA&M1?0+LwDmTa7>Af@U%RD2;r2Fd_WI!O!vtNhX_hL z)d!B7yFdS|IoA9MfzLD<T$)}V7Q1F}8_#dYW@6g{x63<X=n)3}=LFNFHms$+AQ?3X zay0@hF$>Lo$+rU{^wJ%%qb{PN1N<n&8grR@x9zmU(L@MZfUOQ-M*)y!78*<9YE*G} zX2G_Zfp0o7z3jTnsVf`bR`)(PcYTsJIgr1MH)3pi+3t&*cfxHBN3a{iq|;&biQyei z;kiGAvyX=5{ea!#PXZ+&&h2ZOGv=ul*m1xoeu%q{5mD~R-Ug476f)!`&C29V1Y)Xp z3nrPdYY0|Y<U96ntPK?Q9=2$4Gi7}X=be?ung}O{m3Z-MsuySLJe4<@=3v3JvW~Mt zj4XN--y?kn;g<1nNGo0@Fo%H*(_H#)=_(*sB&ERvqVY`mU8xxum{+tBj?Q*2LU`~t zw2fG3E%g$iR&D%oui@L-SvCWeEeu#pz+cm9--j`Sztd_zPMr7q`oyZ3r#GUuzO!6; zWMYx!KU4tl=umS>YA{kyQ}UnAhQh1DvF}_pMxl)Zb0*9aCDH#=D7chBUpqJ|60Z-I z;@FU7n&Xal>T5!#u)99o?<a|Q1`oioNY!P6U#te{0=d4aO1J+FQf8*o>-R=VjIDHf z!jOlQW@hYqcC9hmh%aSxNw)%7EXVyEbjpSg>~Y)%7AF+>fi#L2jk3!uHlX<ZNT2a7 zfNwJo3`h05vGbq=U3ma9LAJv8EJ2FVHE85#BmyiY%YI?I6FhWJtMyS0qEtWFP*|h2 zJD(jp+aErt#8_dB_I+31wd%f<Eq{MWIk~y;4)(<w%Ssc<k3AC)e409P9%ke(%OCA( zh<1r0h?@>^Jky)RWJ)6W5rsx`QcKyW61cB)<@cx{s*WkJW>}40MA)GN`eKgv76v&` zfaB-gc%R?=F63pT7!uve_<dMKNc$B+RHG`$d3@;6!!Kv}+n2Rj&)!WcOG;Xw3+~37 zskWvpD6_w>4kJ91?&*quJXL*u5BK(p_T3|?PhN3XtUfytar$&VNnI_-D_(EsMSl~T zdwT4`z#4ni-bw9}dhNxSNv-*JondhNqw&ci)rV>SjcK&(k*V^K2Pc=k@hE3KK0iJG zeBjUVk3M|8{eg`YaVqAck6<d>&hKHi<v(i=jkBRJQ8+fX*|+qtZTy@H^Q&w8k_!IV zXiOLWm;Ug79{?Z`F#ymrLSmgrJ2DmZew$Wwa{siGHZMJ536w0|J1#jsa+GxSet8Ri zA!nw|0>*Xrl))f8OTVluN?Gi9N;^u-<e<F6ZTvD3q!vl6ojNgNk_N4>nP&ijXdYch zA*{&YYB)BBy2fYHhE9f91a}1^tN*cx5tz-O_ot8#tP6hBIoqOCH=JVEutOL~&PKM- z1%V=w(@TMzHl`>z`q`-yTQ1BRIxb?6GGqbt#Rv_DL5C@FMWCKo2xC|vKqrQ$ZM68} zRG0l9E>Ez<Tmc=n!<?a?#ufo`D$ZSIl}81Pwwi&=J1$cxn(rt2;vTJ<d*!tTl_H@& z9}O&pvBLS<eBnPG6#g*bpb+<R*BXm(88B;te0Gl)tfc_}eRj+F|DOr}=6}S00EM1b A9RL6T
--- a/gfx/2d/SourceSurfaceSkia.cpp +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -111,20 +111,21 @@ SourceSurfaceSkia::GetData() if (size.isValid()) { if (sk_sp<SkData> data = SkData::MakeUninitialized(size.value())) { SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat); if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) { raster = SkImage::MakeRasterData(info, data, mStride); } } } - if (!raster) { + if (raster) { + mImage = raster; + } else { gfxCriticalError() << "Failed making Skia raster image for GPU surface"; } - mImage = raster; } #endif SkPixmap pixmap; if (!mImage->peekPixels(&pixmap)) { gfxCriticalError() << "Failed accessing pixels for Skia raster image"; } return reinterpret_cast<uint8_t*>(pixmap.writable_addr()); }
--- a/image/ScriptedNotificationObserver.cpp +++ b/image/ScriptedNotificationObserver.cpp @@ -26,16 +26,19 @@ ScriptedNotificationObserver::ScriptedNo : mInner(aInner) { } NS_IMETHODIMP ScriptedNotificationObserver::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* /*aUnused*/) { + MOZ_RELEASE_ASSERT(js::AllowGCBarriers(CycleCollectedJSContext::Get()->Context()), + "sending image notification to JS observer during painting. See bug 1311841"); + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { return mInner->SizeAvailable(aRequest); } if (aType == imgINotificationObserver::FRAME_UPDATE) { return mInner->FrameUpdate(aRequest); } if (aType == imgINotificationObserver::FRAME_COMPLETE) { return mInner->FrameComplete(aRequest);
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1517,16 +1517,27 @@ OptimizeMIR(MIRGenerator* mir) gs.spewPass("Prune Unused Branches"); AssertBasicGraphCoherency(graph); if (mir->shouldCancel("Prune Unused Branches")) return false; } { + AutoTraceLog log(logger, TraceLogger_FoldEmptyBlocks); + if (!FoldEmptyBlocks(graph)) + return false; + gs.spewPass("Fold Empty Blocks"); + AssertBasicGraphCoherency(graph); + + if (mir->shouldCancel("Fold Empty Blocks")) + return false; + } + + { AutoTraceLog log(logger, TraceLogger_FoldTests); if (!FoldTests(graph)) return false; gs.spewPass("Fold Tests"); AssertBasicGraphCoherency(graph); if (mir->shouldCancel("Fold Tests")) return false;
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -918,16 +918,52 @@ jit::FoldTests(MIRGraph& graph) { for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { if (!MaybeFoldConditionBlock(graph, *block)) return false; } return true; } +bool +jit::FoldEmptyBlocks(MIRGraph& graph) +{ + for (MBasicBlockIterator iter(graph.begin()); iter != graph.end(); ) { + MBasicBlock* block = *iter; + iter++; + + if (block->numPredecessors() != 1 || block->numSuccessors() != 1) + continue; + + if (!block->phisEmpty()) + continue; + + if (block->outerResumePoint()) + continue; + + if (*block->begin() != *block->rbegin()) + continue; + + MBasicBlock* succ = block->getSuccessor(0); + MBasicBlock* pred = block->getPredecessor(0); + + if (succ->numPredecessors() != 1) + continue; + + size_t pos = pred->getSuccessorIndex(block); + pred->lastIns()->replaceSuccessor(pos, succ); + + graph.removeBlock(block); + + succ->addPredecessorSameInputsAs(pred, block); + succ->removePredecessor(block); + } + return true; +} + static void EliminateTriviallyDeadResumePointOperands(MIRGraph& graph, MResumePoint* rp) { // If we will pop the top of the stack immediately after resuming, // then don't preserve the top value in the resume point. if (rp->mode() != MResumePoint::ResumeAt || *rp->pc() != JSOP_POP) return;
--- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -20,16 +20,19 @@ class MIRGraph; MOZ_MUST_USE bool PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph); MOZ_MUST_USE bool FoldTests(MIRGraph& graph); MOZ_MUST_USE bool +FoldEmptyBlocks(MIRGraph& graph); + +MOZ_MUST_USE bool SplitCriticalEdges(MIRGraph& graph); bool IsUint32Type(const MDefinition* def); enum Observability { ConservativeObservability, AggressiveObservability
--- a/js/src/vm/TraceLoggingTypes.h +++ b/js/src/vm/TraceLoggingTypes.h @@ -37,16 +37,17 @@ _(VM) \ _(CompressSource) \ _(WasmCompilation) \ _(Call) \ \ /* Specific passes during ion compilation */ \ _(PruneUnusedBranches) \ _(FoldTests) \ + _(FoldEmptyBlocks) \ _(SplitCriticalEdges) \ _(RenumberBlocks) \ _(ScalarReplacement) \ _(DominatorTree) \ _(PhiAnalysis) \ _(MakeLoopsContiguous) \ _(ApplyTypes) \ _(EagerSimdUnbox) \
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -79,16 +79,17 @@ #include "nsCSSProps.h" #include "nsListControlFrame.h" #include "mozilla/dom/Element.h" #include "nsCanvasFrame.h" #include "gfxDrawable.h" #include "gfxEnv.h" #include "gfxUtils.h" #include "nsDataHashtable.h" +#include "nsTableWrapperFrame.h" #include "nsTextFrame.h" #include "nsFontFaceList.h" #include "nsFontInflationData.h" #include "nsSVGUtils.h" #include "SVGImageContext.h" #include "SVGTextFrame.h" #include "nsStyleStructInlines.h" #include "nsStyleTransformMatrix.h" @@ -5863,16 +5864,29 @@ nsLayoutUtils::GetFirstLinePosition(Writ const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame)); if (!block) { // For the first-line baseline we also have to check for a table, and if // so, use the baseline of its first row. nsIAtom* fType = aFrame->GetType(); if (fType == nsGkAtoms::tableWrapperFrame || fType == nsGkAtoms::flexContainerFrame || fType == nsGkAtoms::gridContainerFrame) { + if ((fType == nsGkAtoms::gridContainerFrame && + aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) || + (fType == nsGkAtoms::flexContainerFrame && + aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) || + (fType == nsGkAtoms::tableWrapperFrame && + static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() == 0)) { + // empty grid/flex/table container + aResult->mBStart = 0; + aResult->mBaseline = aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM, + BaselineSharingGroup::eFirst); + aResult->mBEnd = aFrame->BSize(aWM); + return true; + } aResult->mBStart = 0; aResult->mBaseline = aFrame->GetLogicalBaseline(aWM); // This is what we want for the list bullet caller; not sure if // other future callers will want the same. aResult->mBEnd = aFrame->BSize(aWM); return true; }
--- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -485,22 +485,54 @@ nsBlockFrame::InvalidateFrameWithRect(co "unexpected block frame in SVG text"); GetParent()->InvalidateFrame(); return; } nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); } nscoord -nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const -{ - nscoord result; - if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result)) - return result; - return nsFrame::GetLogicalBaseline(aWritingMode); +nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const +{ + auto lastBaseline = + BaselineBOffset(aWM, BaselineSharingGroup::eLast, AlignmentContext::eInline); + return BSize(aWM) - lastBaseline; +} + +bool +nsBlockFrame::GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const +{ + if (aBaselineGroup == BaselineSharingGroup::eFirst) { + return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline); + } + + for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd(); + line != line_end; ++line) { + if (line->IsBlock()) { + nscoord offset; + nsIFrame* kid = line->mFirstChild; + if (kid->GetVerticalAlignBaseline(aWM, &offset)) { + // Ignore relative positioning for baseline calculations. + const nsSize& sz = line->mContainerSize; + offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM); + *aBaseline = BSize(aWM) - offset; + return true; + } + } else { + // XXX Is this the right test? We have some bogus empty lines + // floating around, but IsEmpty is perhaps too weak. + if (line->BSize() != 0 || !line->IsEmpty()) { + *aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent()); + return true; + } + } + } + return false; } nscoord nsBlockFrame::GetCaretBaseline() const { nsRect contentRect = GetContentRect(); nsMargin bp = GetUsedBorderAndPadding();
--- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -116,16 +116,29 @@ public: virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsFrameList& aFrameList) override; virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; virtual const nsFrameList& GetChildList(ChildListID aListID) const override; virtual void GetChildLists(nsTArray<ChildList>* aLists) const override; virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override; + bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const override + { + nscoord lastBaseline; + if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) { + *aBaseline = BSize() - lastBaseline; + return true; + } + return false; + } + bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const override; virtual nscoord GetCaretBaseline() const override; virtual void DestroyFrom(nsIFrame* aDestructRoot) override; virtual nsSplittableType GetSplittableType() const override; virtual bool IsFloatContainingBlock() const override; virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; virtual nsIAtom* GetType() const override;
--- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -1016,16 +1016,17 @@ nsContainerFrame::ReflowChild(nsIFrame* // Position the child frame and its view if requested. if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { aKidFrame->SetPosition(aWM, aPos, aContainerSize); } if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { PositionFrameView(aKidFrame); + PositionChildViews(aKidFrame); } // Reflow the child frame aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); // If the child frame is complete, delete any next-in-flows, // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && @@ -1059,16 +1060,17 @@ nsContainerFrame::ReflowChild(nsIFrame* // Position the child frame and its view if requested. if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { aKidFrame->SetPosition(nsPoint(aX, aY)); } if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { PositionFrameView(aKidFrame); + PositionChildViews(aKidFrame); } // Reflow the child frame aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); // If the child frame is complete, delete any next-in-flows, // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
--- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -450,17 +450,18 @@ public: // or a dependency. // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate, // or just GetLogicalBaseline() if that fails. bool found = aUseFirstBaseline ? nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) : nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent); if (!found) { - mAscent = mFrame->GetLogicalBaseline(mWM); + mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM, + BaselineSharingGroup::eFirst); } } return mAscent; } // Convenience methods to compute the main & cross size of our *margin-box*. // The caller is responsible for telling us the right axis, so that we can // pull out the appropriate components of our margin/border/padding structs. @@ -2319,16 +2320,20 @@ nsFlexContainerFrame::GetFrameName(nsASt #endif nscoord nsFlexContainerFrame::GetLogicalBaseline(mozilla::WritingMode aWM) const { NS_ASSERTION(mBaselineFromLastReflow != NS_INTRINSIC_WIDTH_UNKNOWN, "baseline has not been set"); + if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) { + // Return a baseline synthesized from our margin-box. + return nsContainerFrame::GetLogicalBaseline(aWM); + } return mBaselineFromLastReflow; } // Helper for BuildDisplayList, to implement this special-case for flex items // from the spec: // Flex items paint exactly the same as block-level elements in the // normal flow, except that 'z-index' values other than 'auto' create // a stacking context even if 'position' is 'static'. @@ -4254,16 +4259,24 @@ nsFlexContainerFrame::DoFlexLayout(nsPre AutoFlexLineListClearer cleanupLines(lines); GenerateFlexLines(aPresContext, aReflowInput, aContentBoxMainSize, aAvailableBSizeForContent, aStruts, aAxisTracker, placeholderKids, lines); + if (lines.getFirst()->IsEmpty() && + !lines.getFirst()->getNext()) { + // We have no flex items, our parent should synthesize a baseline if needed. + AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE); + } else { + RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE); + } + aContentBoxMainSize = ResolveFlexContainerMainSize(aReflowInput, aAxisTracker, aContentBoxMainSize, aAvailableBSizeForContent, lines.getFirst(), aStatus); for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) { line->ResolveFlexibleLengths(aContentBoxMainSize); } @@ -4514,22 +4527,25 @@ nsFlexContainerFrame::DoFlexLayout(nsPre // Per spec, synthesize baseline from the flex container's content box // (i.e. use block-end side of content-box) // XXXdholbert This only makes sense if parent's writing mode is // horizontal (& even then, really we should be using the BSize in terms // of the parent's writing mode, not ours). Clean up in bug 1155322. flexContainerAscent = desiredSizeInFlexWM.BSize(flexWM); } - // XXXdholbert flexContainerAscent needs to be in terms of - // our parent's writing-mode here. See bug 1155322. - aDesiredSize.SetBlockStartAscent(flexContainerAscent); - - // Cache this baseline for use outside of this call. - mBaselineFromLastReflow = flexContainerAscent; + if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) { + // This will force our parent to call GetLogicalBaseline, which will + // synthesize a margin-box baseline. + aDesiredSize.SetBlockStartAscent(ReflowOutput::ASK_FOR_BASELINE); + } else { + // XXXdholbert flexContainerAscent needs to be in terms of + // our parent's writing-mode here. See bug 1155322. + aDesiredSize.SetBlockStartAscent(flexContainerAscent); + } // Now: If we're complete, add bottom border/padding to desired height (which // we skipped via skipSides) -- unless that pushes us over available height, // in which case we become incomplete (unless we already weren't asking for // any height, in which case we stay complete to avoid looping forever). // NOTE: If we're auto-height, we allow our bottom border/padding to push us // over the available height without requesting a continuation, for // consistency with the behavior of "display:block" elements. @@ -4544,16 +4560,26 @@ nsFlexContainerFrame::DoFlexLayout(nsPre // Update desired height to include block-end border/padding desiredSizeInFlexWM.BSize(flexWM) = desiredBSizeWithBEndBP; } else { // We couldn't fit bottom border/padding, so we'll need a continuation. NS_FRAME_SET_INCOMPLETE(aStatus); } } + // Calculate the container baselines so that our parent can baseline-align us. + mBaselineFromLastReflow = flexContainerAscent; + mLastBaselineFromLastReflow = lines.getLast()->GetLastBaselineOffset(); + if (mLastBaselineFromLastReflow == nscoord_MIN) { + // XXX we fall back to a mirrored first baseline here for now, but this + // should probably use the last baseline of the last item or something. + mLastBaselineFromLastReflow = + desiredSizeInFlexWM.BSize(flexWM) - flexContainerAscent; + } + // Convert flex container's final desired size to parent's WM, for outparam. aDesiredSize.SetSize(flexWM, desiredSizeInFlexWM); // Overflow area = union(my overflow area, kids' overflow areas) aDesiredSize.SetOverflowAreasToDesiredBounds(); for (nsIFrame* childFrame : mFrames) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame); }
--- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -78,16 +78,34 @@ public: virtual nsIAtom* GetType() const override; #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override; + bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const override + { + return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline); + } + + bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const override + { + if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) { + return false; + } + *aBaseline = aBaselineGroup == BaselineSharingGroup::eFirst ? + mBaselineFromLastReflow : mLastBaselineFromLastReflow; + return true; + } + // nsContainerFrame overrides uint16_t CSSAlignmentForAbsPosChild( const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const override; // Flexbox-specific public methods bool IsHorizontal(); @@ -111,16 +129,17 @@ public: uint32_t* aNumPackingSpacesRemaining, nscoord* aPackingSpaceRemaining); protected: // Protected constructor & destructor explicit nsFlexContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) , mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN) + , mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN) {} virtual ~nsFlexContainerFrame(); /* * This method does the bulk of the flex layout, implementing the algorithm * described at: * http://dev.w3.org/csswg/css-flexbox/#layout-algorithm * (with a few initialization pieces happening in the caller, Reflow(). @@ -296,11 +315,13 @@ protected: nsTArray<nsIFrame*>& aPlaceholders, const mozilla::LogicalPoint& aContentBoxOrigin, const nsSize& aContainerSize); bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids // to satisfy their 'order' values? nscoord mBaselineFromLastReflow; + // Note: the last baseline is a distance from our border-box end edge. + nscoord mLastBaselineFromLastReflow; }; #endif /* nsFlexContainerFrame_h___ */
--- a/layout/generic/nsFrameStateBits.h +++ b/layout/generic/nsFrameStateBits.h @@ -306,16 +306,19 @@ FRAME_STATE_GROUP(FlexContainer, nsFlexC // Set for a flex container whose children have been reordered due to 'order'. // (Means that we have to be more thorough about checking them for sortedness.) FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_CHILDREN_REORDERED) // Set for a flex container that is emulating a legacy // 'display:-webkit-{inline-}box' container. FRAME_STATE_BIT(FlexContainer, 21, NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX) +// True if the container has no flex items; may lie if there is a pending reflow +FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_SYNTHESIZE_BASELINE) + // == Frame state bits that apply to grid container frames ==================== FRAME_STATE_GROUP(GridContainer, nsGridContainerFrame) // True iff the normal flow children are already in CSS 'order' in the // order they occur in the child frame list. FRAME_STATE_BIT(GridContainer, 20, NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER) @@ -323,16 +326,19 @@ FRAME_STATE_BIT(GridContainer, 20, NS_ST // Note that those child frames may have been removed without this bit // being updated for performance reasons, so code shouldn't depend on // actually finding any pushed items when this bit is set. FRAME_STATE_BIT(GridContainer, 21, NS_STATE_GRID_DID_PUSH_ITEMS) // True iff computed grid values should be generated on the next reflow FRAME_STATE_BIT(GridContainer, 22, NS_STATE_GRID_GENERATE_COMPUTED_VALUES) +// True if the container has no grid items; may lie if there is a pending reflow +FRAME_STATE_BIT(GridContainer, 23, NS_STATE_GRID_SYNTHESIZE_BASELINE) + // == Frame state bits that apply to SVG frames =============================== FRAME_STATE_GROUP(SVG, nsISVGChildFrame) FRAME_STATE_GROUP(SVG, nsSVGContainerFrame) FRAME_STATE_BIT(SVG, 20, NS_STATE_IS_OUTER_SVG) // If this bit is set, we are a <clipPath> element or descendant.
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -6154,8 +6154,65 @@ ScrollFrameHelper::GetSnapPointForDestin bool ScrollFrameHelper::UsesContainerScrolling() const { if (gfxPrefs::LayoutUseContainersForRootFrames()) { return mIsRoot; } return false; } + +bool +ScrollFrameHelper::DragScroll(WidgetEvent* aEvent) +{ + // Dragging is allowed while within a 20 pixel border. Note that device pixels + // are used so that the same margin is used even when zoomed in or out. + nscoord margin = 20 * mOuter->PresContext()->AppUnitsPerDevPixel(); + + // Don't drag scroll for small scrollareas. + if (mScrollPort.width < margin * 2 || mScrollPort.height < margin * 2) { + return false; + } + + // If willScroll is computed as false, then the frame is already scrolled as + // far as it can go in both directions. Return false so that an ancestor + // scrollframe can scroll instead. + bool willScroll = false; + nsPoint pnt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mOuter); + nsPoint scrollPoint = GetScrollPosition(); + nsRect rangeRect = GetScrollRangeForClamping(); + + // Only drag scroll when a scrollbar is present. + nsPoint offset; + if (mHasHorizontalScrollbar) { + if (pnt.x >= mScrollPort.x && pnt.x <= mScrollPort.x + margin) { + offset.x = -margin; + if (scrollPoint.x > 0) { + willScroll = true; + } + } else if (pnt.x >= mScrollPort.XMost() - margin && pnt.x <= mScrollPort.XMost()) { + offset.x = margin; + if (scrollPoint.x < rangeRect.width) { + willScroll = true; + } + } + } + + if (mHasVerticalScrollbar) { + if (pnt.y >= mScrollPort.y && pnt.y <= mScrollPort.y + margin) { + offset.y = -margin; + if (scrollPoint.y > 0) { + willScroll = true; + } + } else if (pnt.y >= mScrollPort.YMost() - margin && pnt.y <= mScrollPort.YMost()) { + offset.y = margin; + if (scrollPoint.y < rangeRect.height) { + willScroll = true; + } + } + } + + if (offset.x || offset.y) { + ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL); + } + + return willScroll; +}
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -455,16 +455,18 @@ public: int32_t aDirection, nsIScrollableFrame::ScrollUnit aUnit, nsIScrollbarMediator::ScrollSnapMode aSnap = nsIScrollbarMediator::DISABLE_SNAP); bool ShouldSuppressScrollbarRepaints() const { return mSuppressScrollbarRepaints; } + bool DragScroll(WidgetEvent* aEvent); + // owning references to the nsIAnonymousContentCreator-built content nsCOMPtr<nsIContent> mHScrollbarContent; nsCOMPtr<nsIContent> mVScrollbarContent; nsCOMPtr<nsIContent> mScrollCornerContent; nsCOMPtr<nsIContent> mResizerContent; RefPtr<ScrollEvent> mScrollEvent; nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent; @@ -719,16 +721,22 @@ public: ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override { return mHelper.ComputeCustomOverflow(aOverflowAreas); } + bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const override { + *aBaseline = GetLogicalBaseline(aWM); + return true; + } + // Recomputes the scrollable overflow area we store in the helper to take children // that are affected by perpsective set on the outer frame and scroll at different // rates. void AdjustForPerspective(nsRect& aScrollableOverflow); // Called to set the child frames. We typically have three: the scroll area, // the vertical scrollbar, and the horizontal scrollbar. virtual void SetInitialChildList(ChildListID aListID, @@ -1028,16 +1036,20 @@ public: void SetScrollsClipOnUnscrolledOutOfFlow() override { mHelper.SetScrollsClipOnUnscrolledOutOfFlow(); } ScrollSnapInfo GetScrollSnapInfo() const override { return mHelper.GetScrollSnapInfo(); } + virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override { + return mHelper.DragScroll(aEvent); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif #ifdef ACCESSIBILITY virtual mozilla::a11y::AccType AccessibleType() override; #endif @@ -1105,16 +1117,22 @@ public: #if 0 virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; #endif virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override { return mHelper.ComputeCustomOverflow(aOverflowAreas); } + bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const override { + *aBaseline = GetLogicalBaseline(aWM); + return true; + } + // Called to set the child frames. We typically have three: the scroll area, // the vertical scrollbar, and the horizontal scrollbar. virtual void SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) override; virtual void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override; virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, @@ -1451,16 +1469,20 @@ public: void TriggerDisplayPortExpiration() override { mHelper.TriggerDisplayPortExpiration(); } ScrollSnapInfo GetScrollSnapInfo() const override { return mHelper.GetScrollSnapInfo(); } + virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override { + return mHelper.DragScroll(aEvent); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif protected: nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot, bool aClipAllDescendants);
--- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -193,17 +193,17 @@ enum class GridLineSide struct nsGridContainerFrame::TrackSize { enum StateBits : uint16_t { eAutoMinSizing = 0x1, eMinContentMinSizing = 0x2, eMaxContentMinSizing = 0x4, eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing, eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing, - eIndefinitePercentMinSizing = 0x8, + // 0x8 is unused, feel free to take it! eAutoMaxSizing = 0x10, eMinContentMaxSizing = 0x20, eMaxContentMaxSizing = 0x40, eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing, eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing, eFlexMaxSizing = 0x80, eFrozen = 0x100, eSkipGrowUnlimited1 = 0x200, @@ -264,17 +264,16 @@ nsGridContainerFrame::TrackSize::Initial minSizeUnit = eStyleUnit_Auto; maxSizeUnit = eStyleUnit_Enumerated; // triggers max-content sizing below } if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) { // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage // "If the inline or block size of the grid container is indefinite, // <percentage> values relative to that size are treated as 'auto'." minSizeUnit = eStyleUnit_Auto; - mState |= eIndefinitePercentMinSizing; } if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) { maxSizeUnit = eStyleUnit_Auto; } // http://dev.w3.org/csswg/css-grid/#algo-init switch (minSizeUnit) { case eStyleUnit_Auto: mState |= eAutoMinSizing; @@ -1157,69 +1156,64 @@ struct nsGridContainerFrame::TrackSizing const uint32_t numTracks = mMinSizingFunctions.Length(); MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track"); nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize; if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) { // "Otherwise, the specified track list repeats only once." return 1; } nscoord repeatTrackSize = 0; - float repeatTrackPercent = 0.0f; // Note that the repeat() track size is included in |sum| in this loop. nscoord sum = 0; - float percentSum = 0.0f; const nscoord percentBasis = aSize; for (uint32_t i = 0; i < numTracks; ++i) { // "treating each track as its max track sizing function if that is // definite or as its minimum track sizing function otherwise" // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill const auto& maxCoord = mMaxSizingFunctions[i]; const auto* coord = &maxCoord; if (!coord->IsCoordPercentCalcUnit()) { coord = &mMinSizingFunctions[i]; if (!coord->IsCoordPercentCalcUnit()) { return 1; } } - float trackPercent; - nscoord trackSize; - ResolvePercentSizeParts(*coord, percentBasis, &trackSize, &trackPercent); + nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis); if (i == mRepeatAutoStart) { if (percentBasis != NS_UNCONSTRAINEDSIZE) { // Use a minimum 1px for the repeat() track-size. if (trackSize < AppUnitsPerCSSPixel()) { trackSize = AppUnitsPerCSSPixel(); } } repeatTrackSize = trackSize; - repeatTrackPercent = trackPercent; } sum += trackSize; - percentSum += trackPercent; } nscoord gridGap; + float percentSum = 0.0f; float gridGapPercent; ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent); if (numTracks > 1) { // Add grid-gaps for all the tracks including the repeat() track. sum += gridGap * (numTracks - 1); - percentSum += gridGapPercent * (numTracks - 1); + percentSum = gridGapPercent * (numTracks - 1); } // Calculate the max number of tracks that fits without overflow. nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize; nscoord size = nsLayoutUtils::AddPercents(sum, percentSum); if (available - size < 0) { // "if any number of repetitions would overflow, then 1 repetition" return 1; } uint32_t numRepeatTracks = 1; bool exactFit = false; while (true) { sum += gridGap + repeatTrackSize; - percentSum += gridGapPercent + repeatTrackPercent; + percentSum += gridGapPercent; nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum); if (newSize <= size) { // Adding more repeat-tracks won't make forward progress. return numRepeatTracks; } size = newSize; nscoord remaining = available - size; exactFit = remaining == 0; @@ -2582,24 +2576,18 @@ nsGridContainerFrame::GridReflowInput::C mCols.mCanResolveLineRangeSize = true; mRows.CalculateSizes(*this, mGridItems, mRowFunctions, aContentBox.BSize(mWM), &GridArea::mRows, aConstraint); if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) { aContentBox.BSize(mWM) = mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap); - if ((mRows.mStateUnion & TrackSize::eIndefinitePercentMinSizing) || - mGridStyle->mGridRowGap.HasPercent()) { - mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap, - aGrid.mGridRowEnd, aContentBox.BSize(mWM)); - mRows.CalculateSizes(*this, mGridItems, mRowFunctions, - aContentBox.BSize(mWM), &GridArea::mRows, - aConstraint); - } + mRows.mGridGap = + ::ResolveToDefiniteSize(mGridStyle->mGridRowGap, aContentBox.BSize(mWM)); } } /** * (XXX share this utility function with nsFlexContainerFrame at some point) * * Helper for BuildDisplayList, to implement this special-case for grid * items from the spec: @@ -4953,41 +4941,31 @@ nsGridContainerFrame::Tracks::AlignJusti } nscoord nsGridContainerFrame::Tracks::BackComputedIntrinsicSize( const TrackSizingFunctions& aFunctions, const nsStyleCoord& aGridGap) const { // Sum up the current sizes (where percentage tracks were treated as 'auto') - // in 'size' and a sum of percentages in 'percent'. + // in 'size'. nscoord size = 0; - float percent = 0.0f; - bool hasPercent = mStateUnion & TrackSize::eIndefinitePercentMinSizing; for (size_t i = 0, len = mSizes.Length(); i < len; ++i) { - const nscoord trackSize = mSizes[i].mBase; - nscoord length; - float p; - if (hasPercent && - ::GetPercentSizeParts(aFunctions.MinSizingFor(i), &length, &p)) { - size += std::max(length, trackSize); - percent += p; - } else { - size += trackSize; - } - } - - // Add grid-gap contributions to 'size' and 'percent'. + size += mSizes[i].mBase; + } + + // Add grid-gap contributions to 'size' and calculate a 'percent' sum. + float percent = 0.0f; size_t numTracks = mSizes.Length(); if (numTracks > 1) { const size_t gridGapCount = numTracks - 1; nscoord gridGapLength; float gridGapPercent; if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) { - percent += gridGapCount * gridGapPercent; + percent = gridGapCount * gridGapPercent; } else { gridGapLength = aGridGap.ToLength(); } size += gridGapCount * gridGapLength; } return std::max(0, nsLayoutUtils::AddPercents(size, percent)); } @@ -6113,16 +6091,22 @@ nsGridContainerFrame::Reflow(nsPresConte InitImplicitNamedAreas(stylePos); } GridReflowInput gridReflowInput(this, aReflowInput); if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) { AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER); } else { RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER); } + if (gridReflowInput.mIter.AtEnd()) { + // We have no grid items, our parent should synthesize a baseline if needed. + AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE); + } else { + RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE); + } const nscoord computedBSize = aReflowInput.ComputedBSize(); const nscoord computedISize = aReflowInput.ComputedISize(); const WritingMode& wm = gridReflowInput.mWM; LogicalSize computedSize(wm, computedISize, computedBSize); nscoord consumedBSize = 0; nscoord bSize; if (!prevInFlow) { @@ -6733,21 +6717,18 @@ nsGridContainerFrame::SynthesizeBaseline nscoord start; nscoord size; if (aAxis == eLogicalAxisBlock) { start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM); size = child->BSize(aCBWM); if (grid && aGridOrderItem.mIsInEdgeTrack) { isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) : grid->GetBBaseline(aGroup, &baseline); - } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack && - GetBBaseline(aGroup, childWM, child, &baseline)) { - if (aGroup == BaselineSharingGroup::eLast) { - baseline = size - baseline; // convert to distance from border-box end - } + } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) { + baseline = child->BaselineBOffset(childWM, aGroup, AlignmentContext::eGrid); } else { baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size); } } else { start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM); size = child->ISize(aCBWM); if (grid && aGridOrderItem.mIsInEdgeTrack) { isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
--- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -10,24 +10,16 @@ #define nsGridContainerFrame_h___ #include "mozilla/Maybe.h" #include "mozilla/TypeTraits.h" #include "nsContainerFrame.h" #include "nsHashKeys.h" #include "nsTHashtable.h" -// https://drafts.csswg.org/css-align-3/#baseline-sharing-group -enum BaselineSharingGroup -{ - // NOTE Used as an array index so must be 0 and 1. - eFirst = 0, - eLast = 1, -}; - /** * Factory function. * @return a newly allocated nsGridContainerFrame (infallible) */ nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); namespace mozilla { @@ -106,21 +98,41 @@ public: } void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override { + if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) { + // Return a baseline synthesized from our margin-box. + return nsContainerFrame::GetLogicalBaseline(aWM); + } nscoord b; GetBBaseline(BaselineSharingGroup::eFirst, &b); return b; } + bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const override + { + return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline); + } + + bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const override + { + if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) { + return false; + } + return GetBBaseline(aBaselineGroup, aBaseline); + } + #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; #endif // nsContainerFrame overrides bool DrainSelfOverflowList() override; void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override; void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
--- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -412,16 +412,34 @@ enum nsBidiDirection { NSBIDI_LTR, /** All right-to-left text This is a 1 value. */ NSBIDI_RTL, /** Mixed-directional text. */ NSBIDI_MIXED }; namespace mozilla { + +// https://drafts.csswg.org/css-align-3/#baseline-sharing-group +enum BaselineSharingGroup +{ + // NOTE Used as an array index so must be 0 and 1. + eFirst = 0, + eLast = 1, +}; + +// Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context +enum class AlignmentContext +{ + eInline, + eTable, + eFlexbox, + eGrid, +}; + /* * For replaced elements only. Gets the intrinsic dimensions of this element. * The dimensions may only be one of the following two types: * * eStyleUnit_Coord - a length in app units * eStyleUnit_None - the element has no intrinsic size in this dimension */ struct IntrinsicSize { @@ -494,16 +512,18 @@ static void ReleaseValue(T* aPropertyVal * link to many of the functions defined here. Too bad. * * If you're not in layout but you must call functions in here, at least * restrict yourself to calling virtual methods, which won't hurt you as badly. */ class nsIFrame : public nsQueryFrame { public: + using AlignmentContext = mozilla::AlignmentContext; + using BaselineSharingGroup = mozilla::BaselineSharingGroup; template <typename T> using Maybe = mozilla::Maybe<T>; using Nothing = mozilla::Nothing; using OnNonvisible = mozilla::OnNonvisible; template<typename T=void> using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*; using ReflowInput = mozilla::ReflowInput; using ReflowOutput = mozilla::ReflowOutput; using Visibility = mozilla::Visibility; @@ -1194,21 +1214,103 @@ public: nscoord aRadii[8]) const; bool GetBorderRadii(nscoord aRadii[8]) const; bool GetMarginBoxBorderRadii(nscoord aRadii[8]) const; bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const; bool GetContentBoxBorderRadii(nscoord aRadii[8]) const; bool GetShapeBoxBorderRadii(nscoord aRadii[8]) const; /** + * XXX: this method will likely be replaced by GetVerticalAlignBaseline * Get the position of the frame's baseline, relative to the top of * the frame (its top border edge). Only valid when Reflow is not * needed. + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context, with the ltr/rtl + * direction tweak done by nsIFrame::GetWritingMode(nsIFrame*) in inline + * contexts (see that method). */ - virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const = 0; + virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const = 0; + + /** + * Synthesize a first(last) inline-axis baseline from our margin-box. + * An alphabetical baseline is at the start(end) edge and a central baseline + * is at the center of our block-axis margin-box (aWM tells which to use). + * https://drafts.csswg.org/css-align-3/#synthesize-baselines + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context + * @return an offset from our border-box block-axis start(end) edge for + * a first(last) baseline respectively + * (implemented in nsIFrameInlines.h) + */ + inline nscoord SynthesizeBaselineBOffsetFromMarginBox( + mozilla::WritingMode aWM, + BaselineSharingGroup aGroup) const; + + /** + * Synthesize a first(last) inline-axis baseline from our border-box. + * An alphabetical baseline is at the start(end) edge and a central baseline + * is at the center of our block-axis border-box (aWM tells which to use). + * https://drafts.csswg.org/css-align-3/#synthesize-baselines + * @note The returned value is only valid when reflow is not needed. + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context + * @return an offset from our border-box block-axis start(end) edge for + * a first(last) baseline respectively + * (implemented in nsIFrameInlines.h) + */ + inline nscoord SynthesizeBaselineBOffsetFromBorderBox( + mozilla::WritingMode aWM, + BaselineSharingGroup aGroup) const; + + /** + * Return the position of the frame's inline-axis baseline, or synthesize one + * for the given alignment context. The returned baseline is the distance from + * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast). + * @note The returned value is only valid when reflow is not needed. + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context + * @param aBaselineOffset out-param, only valid if the method returns true + * (implemented in nsIFrameInlines.h) + */ + inline nscoord BaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + AlignmentContext aAlignmentContext) const; + + /** + * XXX: this method is taking over the role that GetLogicalBaseline has. + * Return true if the frame has a CSS2 'vertical-align' baseline. + * If it has, then the returned baseline is the distance from the block- + * axis border-box start edge. + * @note This method should only be used in AlignmentContext::eInline contexts. + * @note The returned value is only valid when reflow is not needed. + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context + * @param aBaseline the baseline offset, only valid if the method returns true + */ + virtual bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, + nscoord* aBaseline) const { + return false; + } + + /** + * Return true if the frame has a first(last) inline-axis natural baseline per + * CSS Box Alignment. If so, then the returned baseline is the distance from + * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast). + * https://drafts.csswg.org/css-align-3/#natural-baseline + * @note The returned value is only valid when reflow is not needed. + * @note You should only call this on frames with a WM that's parallel to aWM. + * @param aWM the writing-mode of the alignment context + * @param aBaseline the baseline offset, only valid if the method returns true + */ + virtual bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const { + return false; + } /** * Get the position of the baseline on which the caret needs to be placed, * relative to the top of the frame. This is mostly needed for frames * which return a baseline from GetBaseline which is not useful for * caret positioning. */ virtual nscoord GetCaretBaseline() const {
--- a/layout/generic/nsIFrameInlines.h +++ b/layout/generic/nsIFrameInlines.h @@ -91,9 +91,73 @@ nsIFrame::IsInlineOutside() const } mozilla::StyleDisplay nsIFrame::GetDisplay() const { return StyleDisplay()->GetDisplay(this); } +nscoord +nsIFrame::SynthesizeBaselineBOffsetFromMarginBox( + mozilla::WritingMode aWM, + BaselineSharingGroup aGroup) const +{ + MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode())); + auto margin = GetLogicalUsedMargin(aWM); + if (aGroup == BaselineSharingGroup::eFirst) { + if (aWM.IsAlphabeticalBaseline()) { + // First baseline for inverted-line content is the block-start margin edge, + // as the frame is in effect "flipped" for alignment purposes. + return MOZ_UNLIKELY(aWM.IsLineInverted()) ? -margin.BStart(aWM) + : BSize(aWM) + margin.BEnd(aWM); + } + nscoord marginBoxCenter = (BSize(aWM) + margin.BStartEnd(aWM)) / 2; + return marginBoxCenter - margin.BStart(aWM); + } + MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast); + if (aWM.IsAlphabeticalBaseline()) { + // Last baseline for inverted-line content is the block-start margin edge, + // as the frame is in effect "flipped" for alignment purposes. + return MOZ_UNLIKELY(aWM.IsLineInverted()) ? BSize(aWM) + margin.BStart(aWM) + : -margin.BEnd(aWM); + } + // Round up for central baseline offset, to be consistent with eFirst. + nscoord marginBoxSize = BSize(aWM) + margin.BStartEnd(aWM); + nscoord marginBoxCenter = (marginBoxSize / 2) + (marginBoxSize % 2); + return marginBoxCenter - margin.BEnd(aWM); +} + +nscoord +nsIFrame::SynthesizeBaselineBOffsetFromBorderBox( + mozilla::WritingMode aWM, + BaselineSharingGroup aGroup) const +{ + MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode())); + nscoord borderBoxSize = BSize(aWM); + if (aGroup == BaselineSharingGroup::eFirst) { + return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? borderBoxSize + : borderBoxSize / 2; + } + MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast); + // Round up for central baseline offset, to be consistent with eFirst. + auto borderBoxCenter = (borderBoxSize / 2) + (borderBoxSize % 2); + return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? 0 : borderBoxCenter; +} + +nscoord +nsIFrame::BaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + AlignmentContext aAlignmentContext) const +{ + MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode())); + nscoord baseline; + if (GetNaturalBaselineBOffset(aWM, aBaselineGroup, &baseline)) { + return baseline; + } + if (aAlignmentContext == AlignmentContext::eInline) { + return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup); + } + // XXX AlignmentContext::eTable should use content box? + return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup); +} + #endif
--- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -470,11 +470,19 @@ public: virtual void TriggerDisplayPortExpiration() = 0; /** * Returns information required to determine where to snap to after a scroll. */ virtual ScrollSnapInfo GetScrollSnapInfo() const = 0; virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0; + + /** + * Given the drag event aEvent, determine whether the mouse is near the edge + * of the scrollable area, and scroll the view in the direction of that edge + * if so. If scrolling occurred, true is returned. When false is returned, the + * caller should look for an ancestor to scroll. + */ + virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0; }; #endif
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-grid/grid-container-synthesized-baseline-001-ref.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>Reference: Synthesized grid container baseline.</title> + <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313068"> + <style type="text/css"> +html,body { + color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; +} +.ib { + display: inline-block; +} +.ig { + display: inline-grid; +} +.ib, .ig { + border-style: solid; + border-width: 3px 1px 5px 1px; + padding: 7px 10px 3px 8px; + margin: 5px 3px 2px 1px; +} +</style> + +</head><body> + +<pre>Inline-level context:</pre> +Grid:<div class="ib"></div> +Block:<div class="ig"></div> + +<pre>Grid-level context:</pre> +<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start"> +Grid:<div class="ib"></div> +Block:<div class="ig"></div> +</div> + +<pre>Flexbox-level context:</pre> +<div style="display:inline-flex; align-items:baseline; justify-items:start"> +Grid:<div class="ib" style="margin-bottom:0"></div> +Block:<div class="ig"></div> +</div> + +</body></html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-grid/grid-container-synthesized-baseline-001.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>CSS Grid Test: Synthesized grid container baseline.</title> + <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313068"> + <link rel="help" href="https://drafts.csswg.org/css-grid/#grid-baselines"> + <link rel="match" href="grid-container-synthesized-baseline-001-ref.html"> + <style type="text/css"> +html,body { + color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; +} +.ib { + display: inline-block; +} +.ig { + display: inline-grid; +} +.ib, .ig { + border-style: solid; + border-width: 3px 1px 5px 1px; + padding: 7px 10px 3px 8px; + margin: 5px 3px 2px 1px; +} +</style> + +</head><body> + +<pre>Inline-level context:</pre> +Grid:<div class="ig"></div> +Block:<div class="ib"></div> + +<pre>Grid-level context:</pre> +<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start"> +Grid:<div class="ig"></div> +Block:<div class="ib" style="margin-bottom:0"></div> +</div> + +<pre>Flexbox-level context:</pre> +<div style="display:inline-flex; align-items:baseline; justify-items:start"> +Grid:<div class="ig"></div> +Block:<div class="ib" style="margin-bottom:0"></div> +</div> + +</body></html>
--- a/layout/reftests/css-grid/grid-fragmentation-015-ref.html +++ b/layout/reftests/css-grid/grid-fragmentation-015-ref.html @@ -79,17 +79,17 @@ x { display:block; height:20px; } <span style="grid-row:span 2"><x></x></span> <span><x></x></span> <span><x></x></span> </div></div></div></div> <!-- grid wrapped in FIELDSET inline --> <div class="columns" style="height: 40px; margin-left:200px"> <div style="padding-top:2px; background:grey"> -<div style="display:inline-block; overflow:hidden; border:none; padding:0; margin:0"> +<div style="display:inline-block; border:none; padding:0; margin:0"> <div class="grid"> <span style="grid-row:span 2"><x></x></span> <span><x></x></span> <span><x></x></span> </div></div></div></div> <!-- grid wrapped in FIELDSET overflow:hidden block --> <div class="columns" style="height: 40px; margin-left:400px">
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001-ref.html +++ b/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001-ref.html @@ -11,133 +11,65 @@ <style type="text/css"> html,body { color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; } div { display: grid; float: left; - grid-template-columns: auto auto; - grid-template-rows: 5px; - height: 5px; border: 1px solid; clear: left; align-content: start; justify-content: start; + margin: 3px; } span { - min-width: 10px; background: grey; } +.c > span { width: 10px; } +.r > span { height: 10px; } + span:nth-child(2) { background:lime; } x { background: blue; } x:nth-child(2) { background:pink; } -.g10 { grid-gap:10%; } -.p1 { grid-template-columns: 10px; width: calc(10px / 0.9); } -.p1a { grid-template-columns: 10px; width: calc(10px / 0.9); } -.p10 { grid-template-columns: 10px 10px; width: calc(10px / 0.8); } -.p10a { grid-template-columns: calc((20px / 0.5) * 0.4) calc((20px / 0.5) * 0.1); width: calc(20px / 0.5); } -.p10b { grid-template-columns: calc((110px / 0.6) * 0.4) 100px; width: calc(110px / 0.6); } -.g10.p10b { grid-template-columns: calc((110px / 0.5) * 0.4) 100px; width: calc(110px / 0.5); } -.p10c { grid-template-columns: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); width: calc(110px / 0.6); } -.g10.p10c { grid-template-columns: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); width: calc(110px / 0.4); } -.c10 { grid-template-columns: 62.5px 62.5px; } -.g10.c10 { grid-template-columns: 64.2833px 64.2833px; } -.c10120 { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); width: calc(170px / 0.8); } -.g10.c10120 { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); width: calc(170px / 0.7); } -.c10a { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); width: calc(170px / 0.8); } -.g10.c10a { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); width: calc(170px / 0.7); } -.c10b { grid-template-columns: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); width: 170px; } +.c { grid-auto-rows: 5px; } +.c.p1 { grid-template-columns: 1px; width: 10px; } +.c.p1a { grid-template-columns: minmax(10%,auto); } +.c.p2 { grid-template-columns: 10% 10%; grid-gap: 20%; } +.c.p2a { grid-template-columns: repeat(2,minmax(10%,auto)); grid-gap: 20%; } +.c.c0 { grid-template-columns: 0; } -.p1x { grid-template-columns: 0; } -.p1ax { grid-template-columns: 0; } -.p10x { grid-template-columns: auto auto; } -.p10ax { grid-template-columns: calc((10px / 0.5) * 0.4) calc((10px / 0.5) * 0.1); width: calc(10px / 0.5); } -.g10.p10 { width: calc(10px / 0.7); } -.g10.p10a { grid-template-columns: calc((20px / 0.4) * 0.4) calc((20px / 0.4) * 0.1); width: calc(20px / 0.4); } -.g10.p10ax { grid-template-columns: calc((10px / 0.4) * 0.4) calc((10px / 0.4) * 0.1); width: calc(10px / 0.4); } -.p10axx{ grid-template-columns: auto auto; } -.p10bx { grid-template-columns: calc((110px / 0.6) * 0.4) 100px; width: calc(110px / 0.6); } -.p10bx120 { grid-template-columns: calc((100px / 0.6) * 0.4) 100px; width: calc(100px / 0.6); } -.g10.p10bx { grid-template-columns: calc((110px / 0.5) * 0.4) 100px; width: calc(110px / 0.5); } -.g10.p10bx1 { grid-template-columns: calc((100px / 0.5) * 0.4) 100px; width: calc(100px / 0.5); } -.p10bxx{ grid-template-columns: 66.66667px 100px; } -.g10.p10bxx{ grid-template-columns: 80px 100px; } -.p10cx { grid-template-columns: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); width: calc(110px / 0.6); } -.g10.p10cx { grid-template-columns: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); width: calc(110px / 0.4); } -.c10x { grid-template-columns: 62.5px 62.5px; } -.g10.c10xx { grid-template-columns: 64.2833px 64.2833px; } -.g10.c10x { grid-template-columns: 64.2833px 64.2833px; } -.c10xx { grid-template-columns: 62.5px 62.5px; } -.c10ax { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px) ; width: calc(170px / 0.8); } -.g10.c10ax { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px) ; width: calc(170px / 0.7); } -.c10bx { grid-template-columns: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); width: 170px; } +.r { grid-auto-columns: 5px; grid-auto-flow: column; } +.r.p1 { grid-template-rows: 10%; } +.r.p1a { grid-template-rows: minmax(10%,auto); } +.r.p2 { grid-template-rows: 10% 10%; grid-gap: 20%; } +.r.p2a { grid-template-rows: repeat(2,minmax(10%,auto)); grid-gap: 20%; } +.r.r0 { grid-template-rows: 0; } -.gneg { grid-gap: 0; grid-template-columns: 10px 10px; width:0; } -.gneg.c10a { grid-template-columns: 50px 50px; } -.gneg.p10b { grid-template-columns: 8px 100px; width: 20px; } </style> </head> <body> -<!-- Note that some of the min-width cases below SHOULD overflow. --> +<div class="c p1"><span></span><x></x></div> +<div class="c c0"><x></x></div> +<div class="c p1a"><span></span><x></x></div> +<div class="c c0"><x></x></div> -<div class="p1"><span></span></div> -<div class="p1x"><x></x></div> -<div class="p10"><span></span></div> -<div class="p10x"><x></x></div> -<div class="p1a"><span></span></div> -<div class="p1ax"><x></x></div> -<div class="p10a"><span></span><span></span></div> -<div class="p10ax"><x></x><span></span></div> -<div class="p10axx"><x></x><x></x></div> -<div class="p10b"><span></span><span></span></div> -<div class="p10bx"><span></span><x></x></div> -<div class="p10bxx"><x></x><x></x></div> -<div class="p10b"><span></span><span style="min-width:80px"></span></div> -<div class="p10bx120"><x></x><span style="min-width:120px"></span></div> -<div class="p10c"><span></span><span></span></div> -<div class="p10cx"><span></span><x></x></div> -<div class="c10"><span></span><span></span></div> -<div class="c10xx"><x></x><x></x></div> -<div class="c10120"><span></span><span style="min-width:120px"></span></div> -<div class="c10x"><x></x><span></span></div> -<div class="c10a"><span></span><span style="min-width:120px"></span></div> -<div class="c10ax"><x></x><span style="min-width:120px"></span></div> -<div class="c10b"><span></span><span style="min-width:120px"></span></div> -<div class="c10bx"><x></x><span style="min-width:120px"></span></div> - +<div class="c p2"><span></span><span></span><x></x></div> +<div class="c c0"><x></x></div> +<div class="c p2a"><span></span><span></span><x></x></div> +<div class="c c0"><x></x></div> -<div class="g10"><span></span><span></span></div> -<div class="g10 p1"><span></span></div> -<div class="g10 p1x"><x></x></div> -<div class="g10 p10"><span></span></div> -<div class="g10 p10x"><x></x></div> -<div class="g10 p1a"><span></span></div> -<div class="g10 p1ax"><x></x></div> -<div class="g10 p10a"><span></span><span></span></div> -<div class="g10 p10ax"><x></x><span></span></div> -<div class="g10 p10axx"><x></x><x></x></div> -<div class="g10 p10bx"><span></span><span></span></div> -<div class="g10 p10bx"><span></span><x></x></div> -<div class="g10 p10bxx"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span style="min-width:80px"></span></div> -<div class="g10 p10bx1"><x></x><span style="min-width:120px"></span></div> -<div class="g10 p10c"><span></span><span></span></div> -<div class="g10 p10cx"><span></span><x></x></div> -<div class="g10 c10"><span></span><span></span></div> -<div class="g10 c10xx"><x></x><x></x></div> -<div class="g10 c10120"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10x"><x></x><span></span></div> -<div class="g10 c10a"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10ax"><x></x><span style="min-width:120px"></span></div> -<div class="g10 c10b"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10bx"><x></x><span style="min-width:120px"></span></div> +<div class="r p1"><span></span><x></x></div> +<div class="r r0"><x></x></div> +<div class="r p1a"><span></span><x></x></div> +<div class="r r0"><x></x></div> -<div class="gneg"><span></span><span></span></div> -<div class="gneg c10a"><span></span><span></span></div> -<div class="gneg p10b"><span></span><span></span></div> +<div class="r p2"><span></span><span></span><x></x></div> +<div class="r r0"><x></x></div> +<div class="r p2a"><span></span><span></span><x></x></div> +<div class="r r0"><x></x></div> </body> </html>
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001.html +++ b/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001.html @@ -12,99 +12,62 @@ <style type="text/css"> html,body { color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; } div { display: grid; float: left; - grid-template-columns: auto auto; - grid-template-rows: 5px; - height: 5px; border: 1px solid; clear: left; place-content: start start; + margin: 3px; } span { - min-width: 10px; background: grey; } +.c > span { width: 10px; } +.r > span { height: 10px; } + span:nth-child(2) { background:lime; } x { background: blue; } x:nth-child(2) { background:pink; } -.g10 { grid-gap:10%; } -.gneg { grid-gap: calc(10% - 100px); } -.p1 { grid-template-columns: 10%; } -.p1a { grid-template-columns: minmax(10%,auto); } -.p10 { grid-template-columns: 10% 10%; } -.p10a { grid-template-columns: minmax(40%,auto) minmax(10%,auto); } -.p10b { grid-template-columns: minmax(40%,auto) 100px; } -.p10c { grid-template-columns: minmax(20%,auto) 100px minmax(20%,auto) ; } -.c10 { grid-template-columns: calc(50px + 10%) calc(50px + 10%); } -.c10a { grid-template-columns: minmax(calc(50px + 10%), auto) minmax(calc(50px + 10%), auto); } -.c10b { grid-template-columns: minmax(calc(50px - 10%), auto) minmax(calc(50px - 10%), auto); } +.c { grid-auto-rows: 5px; } +.c.p1 { grid-template-columns: 10%; } +.c.p1a { grid-template-columns: minmax(10%,auto); } +.c.p2 { grid-template-columns: 10% 10%; grid-gap: 20%; } +.c.p2a { grid-template-columns: repeat(2,minmax(10%,auto)); grid-gap: 20%; } + +.r { grid-auto-columns: 5px; grid-auto-flow: column; } +.r.p1 { grid-template-rows: 10%; } +.r.p1a { grid-template-rows: minmax(10%,auto); } +.r.p2 { grid-template-rows: 10% 10%; grid-gap: 20%; } +.r.p2a { grid-template-rows: repeat(2,minmax(10%,auto)); grid-gap: 20%; } </style> </head> <body> -<!-- Note that some of the min-width cases below SHOULD overflow. --> +<div class="c p1"><span></span><x></x></div> +<div class="c p1"><x></x></div> +<div class="c p1a"><span></span><x></x></div> +<div class="c p1a"><x></x></div> -<div class="p1"><span></span></div> -<div class="p1"><x></x></div> -<div class="p10"><span></span></div> -<div class="p10"><x></x></div> -<div class="p1a"><span></span></div> -<div class="p1a"><x></x></div> -<div class="p10a"><span></span><span></span></div> -<div class="p10a"><x></x><span></span></div> -<div class="p10a"><x></x><x></x></div> -<div class="p10b"><span></span><span></span></div> -<div class="p10b"><span></span><x></x></div> -<div class="p10b"><x></x><x></x></div> -<div class="p10b"><span></span><span style="min-width:80px"></span></div> -<div class="p10b"><x></x><span style="min-width:120px"></span></div> -<div class="p10c"><span></span><span></span></div> -<div class="p10c"><span></span><x></x></div> -<div class="c10"><span></span><span></span></div> -<div class="c10"><x></x><x></x></div> -<div class="c10"><span></span><span style="min-width:120px"></span></div> -<div class="c10"><x></x><span></span></div> -<div class="c10a"><span></span><span style="min-width:120px"></span></div> -<div class="c10a"><x></x><span style="min-width:120px"></span></div> -<div class="c10b"><span></span><span style="min-width:120px"></span></div> -<div class="c10b"><x></x><span style="min-width:120px"></span></div> +<div class="c p2"><span></span><span></span><x></x></div> +<div class="c p2"><x></x><x></x></div> +<div class="c p2a"><span></span><span></span><x></x></div> +<div class="c p2a"><x></x><x></x></div> -<div class="g10"><span></span><span></span></div> -<div class="g10 p1"><span></span></div> -<div class="g10 p1"><x></x></div> -<div class="g10 p10"><span></span></div> -<div class="g10 p10"><x></x></div> -<div class="g10 p1a"><span></span></div> -<div class="g10 p1a"><x></x></div> -<div class="g10 p10a"><span></span><span></span></div> -<div class="g10 p10a"><x></x><span></span></div> -<div class="g10 p10a"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span></span></div> -<div class="g10 p10b"><span></span><x></x></div> -<div class="g10 p10b"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span style="min-width:80px"></span></div> -<div class="g10 p10b"><x></x><span style="min-width:120px"></span></div> -<div class="g10 p10c"><span></span><span></span></div> -<div class="g10 p10c"><span></span><x></x></div> -<div class="g10 c10"><span></span><span></span></div> -<div class="g10 c10"><x></x><x></x></div> -<div class="g10 c10"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10"><x></x><span></span></div> -<div class="g10 c10a"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10a"><x></x><span style="min-width:120px"></span></div> -<div class="g10 c10b"><span></span><span style="min-width:120px"></span></div> -<div class="g10 c10b"><x></x><span style="min-width:120px"></span></div> +<div class="r p1"><span></span><x></x></div> +<div class="r p1"><x></x></div> +<div class="r p1a"><span></span><x></x></div> +<div class="r p1a"><x></x></div> -<div class="gneg"><span></span><span></span></div> -<div class="gneg c10a"><span></span><span></span></div> -<div class="gneg p10b"><span></span><span></span></div> +<div class="r p2"><span></span><span></span><x></x></div> +<div class="r p2"><x></x><x></x></div> +<div class="r p2a"><span></span><span></span><x></x></div> +<div class="r p2a"><x></x><x></x></div> </body> </html>
deleted file mode 100644 --- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-002-ref.html +++ /dev/null @@ -1,142 +0,0 @@ -<!DOCTYPE HTML> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<html><head> - <meta charset="utf-8"> - <style type="text/css"> - <title>CSS Grid Test: Grid container intrinsic sizing involving percent track min sizing / grid-gap</title> - <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1302541"> - <style type="text/css"> -html,body { - color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; -} - -div { - display: grid; - float: left; - grid-template-rows: auto auto; - grid-template-columns: 5px; - width: 5px; - border: 1px solid; - align-content: start; - justify-content: start; -} - -span { - min-height: 10px; - background: grey; -} -span:nth-child(2) { background:lime; } -x { background: blue; } -x:nth-child(2) { background:pink; } - -.g10 { grid-gap:10%; } -.p1 { grid-template-rows: 10px; height: calc(10px / 0.9); } -.p1a { grid-template-rows: 10px; height: calc(10px / 0.9); } -.p10 { grid-template-rows: 10px 10px; height: calc(10px / 0.8); } -.p10a { grid-template-rows: calc((20px / 0.5) * 0.4) calc((20px / 0.5) * 0.1); height: calc(20px / 0.5); } -.p10b { grid-template-rows: calc((110px / 0.6) * 0.4) 100px; height: calc(110px / 0.6); } -.g10.p10b { grid-template-rows: calc((110px / 0.5) * 0.4) 100px; height: calc(110px / 0.5); } -.p10c { grid-template-rows: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); height: calc(110px / 0.6); } -.g10.p10c { grid-template-rows: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); height: calc(110px / 0.4); } -.c10 { grid-template-rows: 62.5px 62.5px; } -.g10.c10 { grid-template-rows: 64.2833px 64.2833px; } -.c10120 { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); height: calc(170px / 0.8); } -.g10.c10120 { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); height: calc(170px / 0.7); } -.c10a { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); height: calc(170px / 0.8); } -.g10.c10a { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); height: calc(170px / 0.7); } -.c10b { grid-template-rows: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); height: 170px; } - -.p1x { grid-template-rows: 0; } -.p1ax { grid-template-rows: 0; } -.p10x { grid-template-rows: auto auto; } -.p10ax { grid-template-rows: calc((10px / 0.5) * 0.4) calc((10px / 0.5) * 0.1); height: calc(10px / 0.5); } -.g10.p10 { height: calc(10px / 0.7); } -.g10.p10a { grid-template-rows: calc((20px / 0.4) * 0.4) calc((20px / 0.4) * 0.1); height: calc(20px / 0.4); } -.g10.p10ax { grid-template-rows: calc((10px / 0.4) * 0.4) calc((10px / 0.4) * 0.1); height: calc(10px / 0.4); } -.p10axx{ grid-template-rows: auto auto; } -.p10bx { grid-template-rows: calc((110px / 0.6) * 0.4) 100px; height: calc(110px / 0.6); } -.p10bx120 { grid-template-rows: calc((100px / 0.6) * 0.4) 100px; height: calc(100px / 0.6); } -.g10.p10bx { grid-template-rows: calc((110px / 0.5) * 0.4) 100px; height: calc(110px / 0.5); } -.g10.p10bx1 { grid-template-rows: calc((100px / 0.5) * 0.4) 100px; height: calc(100px / 0.5); } -.p10bxx{ grid-template-rows: 66.66667px 100px; } -.g10.p10bxx{ grid-template-rows: 80px 100px; } -.p10cx { grid-template-rows: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); height: calc(110px / 0.6); } -.g10.p10cx { grid-template-rows: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); height: calc(110px / 0.4); } -.c10x { grid-template-rows: 62.5px 62.5px; } -.g10.c10xx { grid-template-rows: 64.2833px 64.2833px; } -.g10.c10x { grid-template-rows: 64.2833px 64.2833px; } -.c10xx { grid-template-rows: 62.5px 62.5px; } -.c10ax { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px) ; height: calc(170px / 0.8); } -.g10.c10ax { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px) ; height: calc(170px / 0.7); } -.c10bx { grid-template-rows: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); height: 170px; } - -.gneg { grid-gap: 0; grid-template-rows: 10px 10px; height:0; } -.gneg.c10a { grid-template-rows: 50px 50px; } -.gneg.p10b { grid-template-rows: 8px 100px; height: 20px; } - </style> -</head> -<body> - -<!-- Note that some of the min-height cases below SHOULD overflow. --> - -<div class="p1"><span></span></div> -<div class="p1x"><x></x></div> -<div class="p10"><span></span></div> -<div class="p10x"><x></x></div> -<div class="p1a"><span></span></div> -<div class="p1ax"><x></x></div> -<div class="p10a"><span></span><span></span></div> -<div class="p10ax"><x></x><span></span></div> -<div class="p10axx"><x></x><x></x></div> -<div class="p10b"><span></span><span></span></div> -<div class="p10bx"><span></span><x></x></div> -<div class="p10bxx"><x></x><x></x></div> -<div class="p10b"><span></span><span style="min-height:80px"></span></div> -<div class="p10bx120"><x></x><span style="min-height:120px"></span></div> -<div class="p10c"><span></span><span></span></div> -<div class="p10cx"><span></span><x></x></div> -<div class="c10"><span></span><span></span></div> -<div class="c10xx"><x></x><x></x></div> -<div class="c10120"><span></span><span style="min-height:120px"></span></div> -<div class="c10x"><x></x><span></span></div> -<div class="c10a"><span></span><span style="min-height:120px"></span></div> -<div class="c10ax"><x></x><span style="min-height:120px"></span></div> -<div class="c10b"><span></span><span style="min-height:120px"></span></div> -<div class="c10bx"><x></x><span style="min-height:120px"></span></div> - - -<div class="g10"><span></span><span></span></div> -<div class="g10 p1"><span></span></div> -<div class="g10 p1x"><x></x></div> -<div class="g10 p10"><span></span></div> -<div class="g10 p10x"><x></x></div> -<div class="g10 p1a"><span></span></div> -<div class="g10 p1ax"><x></x></div> -<div class="g10 p10a"><span></span><span></span></div> -<div class="g10 p10ax"><x></x><span></span></div> -<div class="g10 p10axx"><x></x><x></x></div> -<div class="g10 p10bx"><span></span><span></span></div> -<div class="g10 p10bx"><span></span><x></x></div> -<div class="g10 p10bxx"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span style="min-height:80px"></span></div> -<div class="g10 p10bx1"><x></x><span style="min-height:120px"></span></div> -<div class="g10 p10c"><span></span><span></span></div> -<div class="g10 p10cx"><span></span><x></x></div> -<div class="g10 c10"><span></span><span></span></div> -<div class="g10 c10xx"><x></x><x></x></div> -<div class="g10 c10120"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10x"><x></x><span></span></div> -<div class="g10 c10a"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10ax"><x></x><span style="min-height:120px"></span></div> -<div class="g10 c10b"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10bx"><x></x><span style="min-height:120px"></span></div> - -<div class="gneg"><span></span><span></span></div> -<div class="gneg c10a"><span></span><span></span></div> -<div class="gneg p10b"><span></span><span></span></div> - -</body> -</html>
deleted file mode 100644 --- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-002.html +++ /dev/null @@ -1,110 +0,0 @@ -<!DOCTYPE HTML> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<html><head> - <meta charset="utf-8"> - <style type="text/css"> - <title>CSS Grid Test: Grid container intrinsic sizing involving percent track min sizing / grid-gap</title> - <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1302541"> - <link rel="match" href="grid-percent-intrinsic-sizing-002-ref.html"> - <style type="text/css"> -html,body { - color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; -} - -div { - display: grid; - float: left; - grid-template-rows: auto auto; - grid-template-columns: 5px; - width: 5px; - border: 1px solid; - align-content: start; - justify-content: start; -} - -span { - min-height: 10px; - background: grey; -} -span:nth-child(2) { background:lime; } -x { background: blue; } -x:nth-child(2) { background:pink; } - -.g10 { grid-gap:10%; } -.gneg { grid-gap: calc(10% - 100px); } -.p1 { grid-template-rows: 10%; } -.p1a { grid-template-rows: minmax(10%,auto); } -.p10 { grid-template-rows: 10% 10%; } -.p10a { grid-template-rows: minmax(40%,auto) minmax(10%,auto); } -.p10b { grid-template-rows: minmax(40%,auto) 100px; } -.p10c { grid-template-rows: minmax(20%,auto) 100px minmax(20%,auto) ; } -.c10 { grid-template-rows: calc(50px + 10%) calc(50px + 10%); } -.c10a { grid-template-rows: minmax(calc(50px + 10%), auto) minmax(calc(50px + 10%), auto); } -.c10b { grid-template-rows: minmax(calc(50px - 10%), auto) minmax(calc(50px - 10%), auto); } - - </style> -</head> -<body> - -<!-- Note that some of the min-height cases below SHOULD overflow. --> - -<div class="p1"><span></span></div> -<div class="p1"><x></x></div> -<div class="p10"><span></span></div> -<div class="p10"><x></x></div> -<div class="p1a"><span></span></div> -<div class="p1a"><x></x></div> -<div class="p10a"><span></span><span></span></div> -<div class="p10a"><x></x><span></span></div> -<div class="p10a"><x></x><x></x></div> -<div class="p10b"><span></span><span></span></div> -<div class="p10b"><span></span><x></x></div> -<div class="p10b"><x></x><x></x></div> -<div class="p10b"><span></span><span style="min-height:80px"></span></div> -<div class="p10b"><x></x><span style="min-height:120px"></span></div> -<div class="p10c"><span></span><span></span></div> -<div class="p10c"><span></span><x></x></div> -<div class="c10"><span></span><span></span></div> -<div class="c10"><x></x><x></x></div> -<div class="c10"><span></span><span style="min-height:120px"></span></div> -<div class="c10"><x></x><span></span></div> -<div class="c10a"><span></span><span style="min-height:120px"></span></div> -<div class="c10a"><x></x><span style="min-height:120px"></span></div> -<div class="c10b"><span></span><span style="min-height:120px"></span></div> -<div class="c10b"><x></x><span style="min-height:120px"></span></div> - -<div class="g10"><span></span><span></span></div> -<div class="g10 p1"><span></span></div> -<div class="g10 p1"><x></x></div> -<div class="g10 p10"><span></span></div> -<div class="g10 p10"><x></x></div> -<div class="g10 p1a"><span></span></div> -<div class="g10 p1a"><x></x></div> -<div class="g10 p10a"><span></span><span></span></div> -<div class="g10 p10a"><x></x><span></span></div> -<div class="g10 p10a"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span></span></div> -<div class="g10 p10b"><span></span><x></x></div> -<div class="g10 p10b"><x></x><x></x></div> -<div class="g10 p10b"><span></span><span style="min-height:80px"></span></div> -<div class="g10 p10b"><x></x><span style="min-height:120px"></span></div> -<div class="g10 p10c"><span></span><span></span></div> -<div class="g10 p10c"><span></span><x></x></div> -<div class="g10 c10"><span></span><span></span></div> -<div class="g10 c10"><x></x><x></x></div> -<div class="g10 c10"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10"><x></x><span></span></div> -<div class="g10 c10a"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10a"><x></x><span style="min-height:120px"></span></div> -<div class="g10 c10b"><span></span><span style="min-height:120px"></span></div> -<div class="g10 c10b"><x></x><span style="min-height:120px"></span></div> - -<div class="gneg"><span></span><span></span></div> -<div class="gneg c10a"><span></span><span></span></div> -<div class="gneg p10b"><span></span><span></span></div> - -</body> -</html>
--- a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html +++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html @@ -78,17 +78,17 @@ x:last-child { fill,fit { float: left; height: 400px; } .zero-progress { grid-row-gap: calc(10px - 1%); - grid-template-rows: [a] 10px repeat(1, [b] 0 [c]) [d]; + grid-template-rows: [a] 10px repeat(4, [b] minmax(0,auto) [c]) [d]; } .w50.zero-progress { grid-row-gap: calc(10px - 1%); grid-template-rows: [a] 10px repeat(3, [b] 0 [c]) [d]; } </style> </head> <body>
--- a/layout/reftests/css-grid/grid-track-percent-sizing-001-ref.html +++ b/layout/reftests/css-grid/grid-track-percent-sizing-001-ref.html @@ -9,19 +9,19 @@ <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1264607"> <style type="text/css"> body,html { color:black; background:white; font-family:monospace; font:1px/1 monospace; padding:0; margin:0; } .grid { display: grid; float: left; border: 3px solid; - grid-template-columns: calc((30px / 0.4) * 0.6); - width: calc(30px / 0.4); - grid-template-rows: calc(((15px / 0.6) * 0.4)) 10px; + grid-template-columns: 18px; + width: 30px; + grid-template-rows: 5px 10px; margin-right: 20px; grid-auto-rows: 10px; align-content: start; justify-content: start; } span { background: grey; @@ -43,32 +43,28 @@ x { } .tA { grid-template-columns: 30px; width: auto; } .tB { grid-template-columns: 30px; - grid-template-rows: calc(10px / 0.6 - 10px) 10px; + grid-template-rows: 0 10px; } -.t0, .t2, .t6, .t7 { height: calc(15px / 0.6); } .t1 { - grid-template-rows: calc(20px / 0.6 - 10px) 10px; - grid-template-columns: calc(((30px / 0.4) * 0.6) + 10px); + grid-template-columns: 28px; } .t3, .t4, .t8 { grid: auto 10px / auto; width: auto; } -.t5 { width: 30px; height: 20px; } -.t9 { width: 30px; grid-template-rows: 5px 10px; } .t9 x { width: 18px } .t3 x, .t4 x, .t8 x, .tA x { width: 0 } .t5 x { width: 10px } -.tB x { width: 45px } +.tB x { width: 18px } .sz { grid-template-rows: 40px; width: 100px; height: 100px; } .sz.t1 { grid-template-rows: 50px; } .sz.t3, .sz.t4 { grid-template-rows: 0; }
--- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -132,16 +132,17 @@ skip-if(!gtkWidget) == grid-item-mixed-b == grid-align-content-001.html grid-align-content-001-ref.html == grid-justify-content-001.html grid-justify-content-001-ref.html skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baselines-001-ref.html skip-if(!gtkWidget) == grid-container-baselines-002.html grid-container-baselines-002-ref.html skip-if(!gtkWidget) == grid-container-baselines-003.html grid-container-baselines-003-ref.html == grid-container-baselines-004.html grid-container-baselines-004-ref.html +== grid-container-synthesized-baseline-001-ref.html grid-container-synthesized-baseline-001-ref.html skip-if(Android&&isDebugBuild) == grid-column-gap-001.html grid-column-gap-001-ref.html # Bug 1245884 - slow == grid-column-gap-002.html grid-column-gap-002-ref.html == grid-column-gap-003.html grid-column-gap-003-ref.html == grid-column-gap-004.html grid-column-gap-004-ref.html == grid-row-gap-001.html grid-row-gap-001-ref.html == grid-percent-grid-gap-001.html grid-percent-grid-gap-001-ref.html skip-if(Android&&isDebugBuild) == grid-row-gap-002.html grid-row-gap-002-ref.html # Bug 1245884 - slow skip-if(Android&&isDebugBuild) == grid-row-gap-003.html grid-row-gap-003-ref.html # Bug 1245884 - slow @@ -265,9 +266,8 @@ asserts(1-10) == grid-fragmentation-dyn4 == grid-fragmentation-dyn4-028.html grid-fragmentation-028-ref.html == grid-fragmentation-dyn5-028.html grid-fragmentation-028-ref.html == grid-fragmentation-dyn1-029.html grid-fragmentation-029-ref.html == grid-fragmentation-dyn2-029.html grid-fragmentation-029-ref.html == grid-fragmentation-dyn2-030.html grid-fragmentation-030-ref.html == grid-fragmentation-dyn2-031.html grid-fragmentation-031-ref.html == bug1306106.html bug1306106-ref.html == grid-percent-intrinsic-sizing-001.html grid-percent-intrinsic-sizing-001-ref.html -== grid-percent-intrinsic-sizing-002.html grid-percent-intrinsic-sizing-002-ref.html
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml +++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml @@ -18,41 +18,49 @@ display: block; border: 1px dashed blue; font: 14px sans-serif; } div { display: inline-block; } table { display: inline-table; } - .big { - height: 100px; - font: 24px sans-serif; - margin-top: 20px; - } - .lime { background: lime; } .pink { background: pink; } .aqua { background: aqua; } + + i { display:inline-block; width:20px; height:2px; background:black; } + .ref { + -moz-appearance:none; + -ms-appearance:none; + -webkit-appearance:none; + appearance:none; + border:none; + margin:0; + padding:0; + border-bottom:2px solid black; + width:20px; + } </style> </head> <body> <div class="flexbox"> <div class="lime">text</div ><button>btn</button - ><input type="radio" - /><input type="checkbox" - /><label class="pink">label</label + ><label class="pink">label</label ><table cellspacing="0" cellpadding="0" class="aqua"> <label>lab<br/>el</label> </table ><table cellspacing="0" cellpadding="0"> <fieldset>field<br/>set</fieldset> </table ><table cellspacing="0" cellpadding="0"> <fieldset><legend>leg</legend>field<br/>set</fieldset> </table ><table cellspacing="0" cellpadding="0"> <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset> </table> </div> + <div class="flexbox" style="font-size:0"><input type="radio" + /><input type="checkbox" + /><input type="checkbox" class="ref"/></div> </body> </html>
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml +++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml @@ -25,24 +25,29 @@ height: 100px; font: 24px sans-serif; margin-top: 20px; } .lime { background: lime; } .pink { background: pink; } .aqua { background: aqua; } + + i { display:inline-block; width:20px; height:2px; background:black; } </style> </head> <body> <div class="flexbox"> <div class="lime">text</div> <button>btn</button> - <input type="radio"/> - <input type="checkbox"/> <label class="pink">label</label> <label class="aqua">lab<br/>el</label> <fieldset>field<br/>set</fieldset> <fieldset><legend>leg</legend>field<br/>set</fieldset> <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset> </div> + <div class="flexbox" style="font-size:0"> + <input type="radio"/> + <input type="checkbox"/> + <i></i> + </div> </body> </html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/flexbox/flexbox-empty-container-synthesized-baseline-001-ref.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>Reference: Synthesized flex container baseline.</title> + <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313811"> + <style type="text/css"> +html,body { + color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; +} +.ib { + display: inline-block; +} +.ig { + display: inline-grid; +} +.ib, .ig { + border-style: solid; + border-width: 3px 1px 5px 1px; + padding: 7px 10px 3px 8px; + margin: 5px 3px 2px 1px; +} +</style> + +</head><body> + +<pre>Inline-level context:</pre> +Flexbox:<div class="ib"></div> +Block:<div class="ig"></div> + +<pre>Grid-level context:</pre> +<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start"> +Flexbox:<div class="ib"></div> +Block:<div class="ig"></div> +</div> + +<pre>Flexbox-level context:</pre> +<div style="display:inline-flex; align-items:baseline; justify-items:start"> +Flexbox:<div class="ib" style="margin-bottom:0"></div> +Block:<div class="ig"></div> +</div> + +</body></html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/flexbox/flexbox-empty-container-synthesized-baseline-001.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>CSS Flexbox Test: Synthesized flex container baseline.</title> + <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313811"> + <link rel="help" href="https://drafts.csswg.org/css-flexbox/#flex-baselines"> + <link rel="match" href="flexbox-empty-container-synthesized-baseline-001.html"> + <style type="text/css"> +html,body { + color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0; +} +.ib { + display: inline-block; +} +.if { + display: inline-flex; +} +.ib, .if { + border-style: solid; + border-width: 3px 1px 5px 1px; + padding: 7px 10px 3px 8px; + margin: 5px 3px 2px 1px; +} +</style> + +</head><body> + +<pre>Inline-level context:</pre> +Flexbox:<div class="if"></div> +Block:<div class="ib"></div> + +<pre>Grid-level context:</pre> +<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start"> +Flexbox:<div class="if"></div> +Block:<div class="ib" style="margin-bottom:0"></div> +</div> + +<pre>Flexbox-level context:</pre> +<div style="display:inline-flex; align-items:baseline; justify-items:start"> +Flexbox:<div class="if"></div> +Block:<div class="ib" style="margin-bottom:0"></div> +</div> + +</body></html>
--- a/layout/reftests/flexbox/reftest.list +++ b/layout/reftests/flexbox/reftest.list @@ -12,17 +12,17 @@ # SUBDIRECTORY: Reftests for paginated flex containers include pagination/reftest.list # Tests for cross-axis alignment (align-self / align-items properties) fails == flexbox-align-self-baseline-horiz-2.xhtml flexbox-align-self-baseline-horiz-2-ref.xhtml # bug 793456, and possibly others # This one fails on windows R (but not Ru, strangely) and GTK. # On Windows R and GTK, the single-line <label> flex item has a different # background size in test vs. ref -fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) == flexbox-align-self-baseline-horiz-3.xhtml flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate +fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) skip-if(Android) == flexbox-align-self-baseline-horiz-3.xhtml flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate the random-if. The skip-if(Android) is because checkbox/radio appearance:none doesn't work as expected. == flexbox-align-self-baseline-horiz-4.xhtml flexbox-align-self-baseline-horiz-4-ref.xhtml # Tests for box-sizing on flex containers and flex items. == flexbox-box-sizing-on-container-horiz-1.html flexbox-box-sizing-on-container-horiz-1-ref.html == flexbox-box-sizing-on-container-vert-1.html flexbox-box-sizing-on-container-vert-1-ref.html == flexbox-box-sizing-on-items-horiz-1a.html flexbox-box-sizing-on-items-horiz-1-ref.html == flexbox-box-sizing-on-items-horiz-1b.html flexbox-box-sizing-on-items-horiz-1-ref.html == flexbox-box-sizing-on-items-vert-1a.html flexbox-box-sizing-on-items-vert-1-ref.html @@ -54,16 +54,17 @@ fuzzy-if(skiaContent,3,10) == flexbox-dy == flexbox-dyn-insertAroundText-3.xhtml flexbox-dyn-insertAroundText-3-ref.xhtml # Variant of one of the above tests, to regression-test an invalidation issue == flexbox-dyn-insertEmptySpan-1.xhtml flexbox-dyn-insertEmptySpan-1-ref.xhtml # Tests for empty flexboxes (with no flex items) == flexbox-empty-1a.xhtml flexbox-empty-1-ref.xhtml == flexbox-empty-1b.xhtml flexbox-empty-1-ref.xhtml +== flexbox-empty-container-synthesized-baseline-001.html flexbox-empty-container-synthesized-baseline-001-ref.html # Tests for handling of floated elements inside a flexbox == flexbox-float-1a.xhtml flexbox-float-1-ref.xhtml == flexbox-float-1b.xhtml flexbox-float-1-ref.xhtml == flexbox-float-1c.xhtml flexbox-float-1-ref.xhtml == flexbox-float-1d.xhtml flexbox-float-1-ref.xhtml == flexbox-float-2a.xhtml flexbox-float-2-ref.xhtml == flexbox-float-2b.xhtml flexbox-float-2-ref.xhtml
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001-ref.html +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001-ref.html @@ -1,48 +1,37 @@ <!DOCTYPE html> <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <!-- In this reference case, we have inline-blocks instead of inline - flex containers. We stick an Ahem whitespace character in each - inline-block, with a customized line-height to make the baseline - end up at the bottom of the inline-block's content-box. --> + flex containers. Otherwise it's the same. --> <html> <head> <title>CSS Reftest Reference</title> <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="support/ahem.css" /> <style> body { font: 20px Ahem; } .flexContainer { display: inline-block; height: 16px; width: 16px; - /* Each inline-block's baseline will be the baseline of the single Ahem - character that it contains. We want to set up that char such that its - baseline is at the bottom of the container's content box (since that's - the corresponding flex container's baseline). So, we use a line-height - of 20px, which gives us a baseline of 20px * 0.8 = 16px, which is the - bottom of the container's content-box -- awesome. */ - line-height: 20px; background: purple; border: 0px dotted black; /* (Elements that want a border will set their border-width.) */ } </style> </head> <body> A - <!-- We have to include a character in the inline-blocks in order for them - to baseline-align; otherwise, they align the bottom of their - border-boxes. --> - <div class="flexContainer"> </div> - <div class="flexContainer" style="padding-bottom: 20px"> </div> - <div class="flexContainer" style="padding: 10px"> </div> - <div class="flexContainer" style="border-width: 3px"> </div> - <div class="flexContainer" style="border-bottom-width: 4px"> </div> + <div class="flexContainer"></div> + <div class="flexContainer" style="padding-bottom: 20px"></div> + <div class="flexContainer" style="padding: 10px"></div> + <div class="flexContainer" style="border-width: 3px"></div> + <div class="flexContainer" style="border-bottom-width: 4px"></div> + <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div> </body> </html>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001a.html +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001a.html @@ -1,21 +1,22 @@ <!DOCTYPE html> <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <!-- Testcase for how we compute the baseline of a horizontal flex container with no flex items. This is the main-axis baseline. The spec says this about this case: + https://drafts.csswg.org/css-flexbox/#flex-baselines + "Otherwise, the flex container has no first/last main-axis baseline set, + and one is synthesized if needed according to the rules of its alignment context." - The flex container's main-axis baseline is synthesized - from ... the flex container's content box. - - I'm taking that to mean the baseline is the bottom of the content box. + The alignment context in this case is inline-level so the margin-box + should be used to synthesize the baseline. --> <html> <head> <title>CSS Test: Testing the baseline of an empty horizontal flex container</title> <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> <link rel="match" href="flexbox-baseline-empty-001-ref.html"> <meta charset="utf-8"> @@ -36,10 +37,11 @@ </head> <body> A <div class="flexContainer"></div> <div class="flexContainer" style="padding-bottom: 20px"></div> <div class="flexContainer" style="padding: 10px"></div> <div class="flexContainer" style="border-width: 3px"></div> <div class="flexContainer" style="border-bottom-width: 4px"></div> + <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div> </body> </html>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001b.html +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001b.html @@ -1,21 +1,22 @@ <!DOCTYPE html> <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <!-- Testcase for how we compute the baseline of a vertical flex container with no flex items. This is the cross-axis baseline. The spec says this about this case: + https://drafts.csswg.org/css-flexbox/#flex-baselines + "Otherwise, the flex container has no first/last main-axis baseline set, + and one is synthesized if needed according to the rules of its alignment context." - ...the flex container's cross-axis baseline is synthesized - from ... the flex container's content box. - - I'm taking that to mean the baseline is the bottom of the content box. + The alignment context in this case is inline-level so the margin-box + should be used to synthesize the baseline. --> <html> <head> <title>CSS Test: Testing the baseline of an empty vertical flex container</title> <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> <link rel="match" href="flexbox-baseline-empty-001-ref.html"> <meta charset="utf-8"> @@ -37,10 +38,11 @@ </head> <body> A <div class="flexContainer"></div> <div class="flexContainer" style="padding-bottom: 20px"></div> <div class="flexContainer" style="padding: 10px"></div> <div class="flexContainer" style="border-width: 3px"></div> <div class="flexContainer" style="border-bottom-width: 4px"></div> + <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div> </body> </html>
--- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -648,18 +648,22 @@ NS_IMPL_HOLDER_FFI_REFCOUNTING(nsIURI, U void Gecko_SetMozBinding(nsStyleDisplay* aDisplay, const uint8_t* aURLString, uint32_t aURLStringLength, ThreadSafeURIHolder* aBaseURI, ThreadSafeURIHolder* aReferrer, ThreadSafePrincipalHolder* aPrincipal) { + if (!aURLString) { + aDisplay->mBinding = nullptr; + return; + } + MOZ_ASSERT(aDisplay); - MOZ_ASSERT(aURLString); MOZ_ASSERT(aBaseURI); MOZ_ASSERT(aReferrer); MOZ_ASSERT(aPrincipal); nsString url; nsDependentCSubstring urlString(reinterpret_cast<const char*>(aURLString), aURLStringLength); AppendUTF8toUTF16(urlString, url);
--- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -3800,45 +3800,64 @@ nsTableFrame::GetRowSpacing(int32_t aSta NS_ASSERTION(aStartRowIndex <= aEndRowIndex, "End index must not be less than start index"); // Only one possible value so just multiply it out. Tables where index // matters will override this function return GetRowSpacing() * (aEndRowIndex - aStartRowIndex); } /* virtual */ nscoord -nsTableFrame::GetLogicalBaseline(WritingMode aWritingMode) const -{ - nscoord ascent = 0; +nsTableFrame::GetLogicalBaseline(WritingMode aWM) const +{ + nscoord baseline; + if (!GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, &baseline)) { + baseline = BSize(aWM); + } + return baseline; +} + +/* virtual */ bool +nsTableFrame::GetNaturalBaselineBOffset(WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const +{ RowGroupArray orderedRowGroups; OrderRowGroups(orderedRowGroups); - nsTableRowFrame* firstRow = nullptr; // XXX not sure if this should be the size of the containing block instead. nsSize containerSize = mRect.Size(); - for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { - nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; - if (rgFrame->GetRowCount()) { - firstRow = rgFrame->GetFirstRow(); - - nscoord rgNormalBStart = - LogicalRect(aWritingMode, rgFrame->GetNormalRect(), containerSize) - .Origin(aWritingMode).B(aWritingMode); - nscoord firstRowNormalBStart = - LogicalRect(aWritingMode, firstRow->GetNormalRect(), containerSize) - .Origin(aWritingMode).B(aWritingMode); - - ascent = rgNormalBStart + firstRowNormalBStart + - firstRow->GetRowBaseline(aWritingMode); - break; - } - } - if (!firstRow) - ascent = BSize(aWritingMode); - return ascent; -} + auto TableBaseline = [aWM, containerSize] (nsTableRowGroupFrame* aRowGroup, + nsTableRowFrame* aRow) { + nscoord rgBStart = LogicalRect(aWM, aRowGroup->GetNormalRect(), + containerSize).BStart(aWM); + nscoord rowBStart = LogicalRect(aWM, aRow->GetNormalRect(), + containerSize).BStart(aWM); + return rgBStart + rowBStart + aRow->GetRowBaseline(aWM); + }; + if (aBaselineGroup == BaselineSharingGroup::eFirst) { + for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; + nsTableRowFrame* row = rgFrame->GetFirstRow(); + if (row) { + *aBaseline = TableBaseline(rgFrame, row); + return true; + } + } + } else { + for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) { + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; + nsTableRowFrame* row = rgFrame->GetLastRow(); + if (row) { + *aBaseline = BSize(aWM) - TableBaseline(rgFrame, row); + return true; + } + } + } + return false; +} + /* ----- global methods ----- */ nsTableFrame* NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsTableFrame(aContext); }
--- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -460,16 +460,20 @@ private: /* For the base implementation of nsTableFrame, cell spacing does not depend * on row/column indexing. */ nscoord GetColSpacing(); nscoord GetRowSpacing(); public: virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override; + bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const override; + /** return the row span of a cell, taking into account row span magic at the bottom * of a table. The row span equals the number of rows spanned by aCell starting at * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row * index in which aCell originates. * * @param aStartRowIndex the cell * @param aCell the cell *
--- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -502,17 +502,29 @@ nsTableRowGroupFrame::ReflowChildren(nsP } } } nsTableRowFrame* nsTableRowGroupFrame::GetFirstRow() { for (nsIFrame* childFrame : mFrames) { - nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); + nsTableRowFrame* rowFrame = do_QueryFrame(childFrame); + if (rowFrame) { + return rowFrame; + } + } + return nullptr; +} + +nsTableRowFrame* +nsTableRowGroupFrame::GetLastRow() +{ + for (auto iter = mFrames.rbegin(), end = mFrames.rend(); iter != end; ++iter) { + nsTableRowFrame* rowFrame = do_QueryFrame(*iter); if (rowFrame) { return rowFrame; } } return nullptr; }
--- a/layout/tables/nsTableRowGroupFrame.h +++ b/layout/tables/nsTableRowGroupFrame.h @@ -98,16 +98,17 @@ public: /** * Get the "type" of the frame * * @see nsGkAtoms::tableRowGroupFrame */ virtual nsIAtom* GetType() const override; nsTableRowFrame* GetFirstRow(); + nsTableRowFrame* GetLastRow(); #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif virtual mozilla::WritingMode GetWritingMode() const override { return GetTableFrame()->GetWritingMode(); }
--- a/layout/tables/nsTableWrapperFrame.h +++ b/layout/tables/nsTableWrapperFrame.h @@ -63,16 +63,35 @@ public: const nsDisplayListSet& aLists) override; void BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists); virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override; + bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + nscoord* aBaseline) const override + { + auto innerTable = InnerTableFrame(); + nscoord offset; + if (innerTable->GetNaturalBaselineBOffset(aWM, aBaselineGroup, &offset)) { + auto bStart = innerTable->BStart(aWM, mRect.Size()); + if (aBaselineGroup == BaselineSharingGroup::eFirst) { + *aBaseline = offset + bStart; + } else { + auto bEnd = bStart + innerTable->BSize(aWM); + *aBaseline = BSize(aWM) - (bEnd - offset); + } + return true; + } + return false; + } + virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; virtual mozilla::LogicalSize ComputeAutoSize(nsRenderingContext* aRenderingContext, mozilla::WritingMode aWM, const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/SearchEnginePreference.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/SearchEnginePreference.java @@ -140,17 +140,17 @@ public class SearchEnginePreference exte /** * Configure this Preference object from the Gecko search engine object. * @param geckoEngine The Gecko-formatted object representing the search engine. */ public void setSearchEngineFromBundle(GeckoBundle geckoEngine) { mIdentifier = geckoEngine.getString("identifier"); // A null JS value gets converted into a string. - if (mIdentifier.equals("null")) { + if (mIdentifier == null || mIdentifier.equals("null")) { mIdentifier = "other"; } final String engineName = geckoEngine.getString("name"); final SpannableString titleSpannable = new SpannableString(engineName); setTitle(titleSpannable);
--- a/python/mozbuild/mozbuild/vendor_rust.py +++ b/python/mozbuild/mozbuild/vendor_rust.py @@ -100,16 +100,21 @@ Please commit or stash these changes bef append_env=env) else: self.log(logging.DEBUG, 'cargo_vendor', {}, 'cargo-vendor already intalled') vendor_dir = mozpath.join(self.topsrcdir, 'third_party/rust') self.log(logging.INFO, 'rm_vendor_dir', {}, 'rm -rf %s' % vendor_dir) mozfile.remove(vendor_dir) # Once we require a new enough cargo to switch to workspaces, we can # just do this once on the workspace root crate. - for crate_root in ('toolkit/library/rust/', - 'toolkit/library/gtest/rust'): + crates_and_roots = ( + ('gkrust', 'toolkit/library/rust'), + ('gkrust-gtest', 'toolkit/library/gtest/rust'), + ('mozjs_sys', 'js/src'), + ) + for (lib, crate_root) in crates_and_roots: path = mozpath.join(self.topsrcdir, crate_root) - self._run_command_in_srcdir(args=[cargo, 'generate-lockfile', '--manifest-path', mozpath.join(path, 'Cargo.toml')]) + # We do an |update -p| here to regenerate the Cargo.lock file with minimal changes. See bug 1324462 + self._run_command_in_srcdir(args=[cargo, 'update', '--manifest-path', mozpath.join(path, 'Cargo.toml'), '-p', lib]) self._run_command_in_srcdir(args=[cargo, 'vendor', '--sync', mozpath.join(path, 'Cargo.lock'), vendor_dir]) #TODO: print stats on size of files added/removed, warn or error # when adding very large files (bug 1306078) self.repository.add_remove_files(vendor_dir)
--- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -1,15 +1,16 @@ // Base preferences file used by most test harnesses user_pref("browser.console.showInPanel", true); user_pref("browser.dom.window.dump.enabled", true); user_pref("browser.firstrun.show.localepicker", false); user_pref("browser.firstrun.show.uidiscovery", false); user_pref("browser.startup.page", 0); // use about:blank, not browser.startup.homepage +user_pref("browser.search.suggest.timeout", 10000); // use a 10s suggestion timeout in tests user_pref("browser.ui.layout.tablet", 0); // force tablet UI off user_pref("dom.allow_scripts_to_close_windows", true); user_pref("dom.disable_open_during_load", false); user_pref("dom.experimental_forms", true); // on for testing user_pref("dom.forms.number", true); // on for testing user_pref("dom.forms.color", true); // on for testing user_pref("dom.forms.datetime", true); // on for testing user_pref("dom.max_script_run_time", 0); // no slow script dialogs
--- a/toolkit/components/search/SearchSuggestionController.jsm +++ b/toolkit/components/search/SearchSuggestionController.jsm @@ -11,18 +11,19 @@ const { classes: Cc, interfaces: Ci, uti Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NS_ASSERT", "resource://gre/modules/debug.js"); const SEARCH_RESPONSE_SUGGESTION_JSON = "application/x-suggestions+json"; const DEFAULT_FORM_HISTORY_PARAM = "searchbar-history"; const HTTP_OK = 200; -const REMOTE_TIMEOUT = 500; // maximum time (ms) to wait before giving up on a remote suggestions const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled"; +const REMOTE_TIMEOUT_PREF = "browser.search.suggest.timeout"; +const REMOTE_TIMEOUT_DEFAULT = 500; // maximum time (ms) to wait before giving up on a remote suggestions /** * Remote search suggestions will be shown if gRemoteSuggestionsEnabled * is true. Global because only one pref observer is needed for all instances. */ var gRemoteSuggestionsEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF); Services.prefs.addObserver(BROWSER_SUGGEST_PREF, function(aSubject, aTopic, aData) { gRemoteSuggestionsEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF); @@ -55,21 +56,16 @@ this.SearchSuggestionController.prototyp /** * The maximum number of remote search engine results to return. * We'll actually only display at most * maxRemoteResults - <displayed local results count> remote results. */ maxRemoteResults: 10, /** - * The maximum time (ms) to wait before giving up on a remote suggestions. - */ - remoteTimeout: REMOTE_TIMEOUT, - - /** * The additional parameter used when searching form history. */ formHistoryParam: DEFAULT_FORM_HISTORY_PARAM, // Private properties /** * The last form history result used to improve the performance of subsequent searches. * This shouldn't be used for any other purpose as it is never cleared and therefore could be stale. @@ -187,18 +183,17 @@ this.SearchSuggestionController.prototyp // Implements nsIAutoCompleteSearch onSearchResult: (search, result) => { this._formHistoryResult = result; if (this._request) { this._remoteResultTimer = Cc["@mozilla.org/timer;1"]. createInstance(Ci.nsITimer); this._remoteResultTimer.initWithCallback(this._onRemoteTimeout.bind(this), - this.remoteTimeout || REMOTE_TIMEOUT, - Ci.nsITimer.TYPE_ONE_SHOT); + this.remoteTimeout, Ci.nsITimer.TYPE_ONE_SHOT); } switch (result.searchResult) { case Ci.nsIAutoCompleteResult.RESULT_SUCCESS: case Ci.nsIAutoCompleteResult.RESULT_NOMATCH: if (result.searchString !== this._searchString) { deferredFormHistory.resolve("Unexpected response, this._searchString does not match form history response"); return; @@ -391,8 +386,14 @@ this.SearchSuggestionController.prototyp * Determines whether the given engine offers search suggestions. * * @param {nsISearchEngine} engine - The search engine * @return {boolean} True if the engine offers suggestions and false otherwise. */ this.SearchSuggestionController.engineOffersSuggestions = function(engine) { return engine.supportsResponseType(SEARCH_RESPONSE_SUGGESTION_JSON); }; + +/** + * The maximum time (ms) to wait before giving up on a remote suggestions. + */ +XPCOMUtils.defineLazyPreferenceGetter(this.SearchSuggestionController.prototype, "remoteTimeout", + REMOTE_TIMEOUT_PREF, REMOTE_TIMEOUT_DEFAULT);
--- a/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html +++ b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html @@ -27,25 +27,38 @@ function checkLoads() { // cache entries. if (window.parent.firstLoad) { window.parent.info("Reloading from cache..."); window.parent.firstLoad = false; window.parent.loadTestFrame(); return; } + let dwu = window.parent.SpecialPowers.getDOMWindowUtils(window); + let timer1 = window.setTimeout(function(){}, 0); + window.parent.ok(!dwu.isTimeoutTracking(timer1), + "Timeout set from main script should not be considered as tracking"); + let timer2 = getTrackerTimeout(); + window.parent.ok(dwu.isTimeoutTracking(timer2), + "Timeout set from included script should be considered as tracking"); + window.clearTimeout(timer1); + window.clearTimeout(timer2); + // End (parent) test. window.parent.SimpleTest.finish(); } </script> <!-- Try loading from a malware javascript URI --> <script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script> +<!-- Try loading from a tracker javascript URI --> +<script type="text/javascript" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/tracker.js"></script> + <!-- Try loading from an uwanted software css URI --> <link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> <!-- Try loading a marked-as-malware css through an @import from a clean URI --> <link rel="stylesheet" type="text/css" href="import.css"></link> </head> <body onload="checkLoads()">
--- a/toolkit/components/url-classifier/tests/mochitest/mochitest.ini +++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini @@ -22,16 +22,17 @@ support-files = basic.vtt dnt.html dnt.sjs update.sjs bad.css bad.css^headers^ gethash.sjs gethashFrame.html + tracker.js [test_classifier.html] skip-if = (os == 'linux' && debug) #Bug 1199778 [test_classifier_worker.html] [test_classify_ping.html] [test_classify_track.html] [test_gethash.html] [test_bug1254766.html]
new file mode 100644 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/tracker.js @@ -0,0 +1,3 @@ +function getTrackerTimeout() { + return window.setTimeout(function(){}, 0); +}
--- a/toolkit/content/tests/browser/browser_bug1198465.js +++ b/toolkit/content/tests/browser/browser_bug1198465.js @@ -63,10 +63,13 @@ add_task(function* () { // doesn't really matter. findBar.onCurrentSelection("foo", true); ok(!findBar._startFindDeferred, "prefilled value fetched"); is(findBar._findField.value, "ab", "ab kept instead of prefill value"); EventUtils.sendChar("c", window); is(findBar._findField.value, "abc", "c is appended after ab"); + // Clear the findField value to make the test run successfully + // for multiple runs in the same browser session. + findBar._findField.value = ""; yield BrowserTestUtils.removeTab(aTab); });
--- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -106,17 +106,19 @@ class WidgetRenderingContext; // NSTitlebarContainerView @end @interface ChildView : NSView< #ifdef ACCESSIBILITY mozAccessible, #endif - mozView, NSTextInputClient> + mozView, NSTextInputClient, + NSDraggingSource, NSDraggingDestination, + NSPasteboardItemDataProvider> { @private // the nsChildView that created the view. It retains this NSView, so // the link back to it must be weak. nsChildView* mGeckoChild; // Text input handler for mGeckoChild and us. Note that this is a weak // reference. Ideally, this should be a strong reference but a ChildView
--- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -3182,40 +3182,47 @@ NSEvent* gLastDragMouseDownEvent = nil; + (void)initialize { static BOOL initialized = NO; if (!initialized) { // Inform the OS about the types of services (from the "Services" menu) // that we can handle. - NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil]; - NSArray *returnTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil]; + NSArray* sendTypes = + [[NSArray alloc] initWithObjects:NSPasteboardTypeString, + NSPasteboardTypeHTML, + nil]; + NSArray* returnTypes = + [[NSArray alloc] initWithObjects:NSPasteboardTypeString, + NSPasteboardTypeHTML, + nil]; [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes]; [sendTypes release]; [returnTypes release]; initialized = YES; } } + (void)registerViewForDraggedTypes:(NSView*)aView { - [aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, - NSStringPboardType, - NSHTMLPboardType, - NSURLPboardType, - NSFilesPromisePboardType, - kWildcardPboardType, - kCorePboardType_url, - kCorePboardType_urld, - kCorePboardType_urln, - nil]]; + [aView registerForDraggedTypes:[NSArray arrayWithObjects: + NSFilenamesPboardType, + NSPasteboardTypeString, + NSPasteboardTypeHTML, + NSURLPboardType, + kPasteboardTypeFileURLPromise, + kWildcardPboardType, + kCorePboardType_url, + kCorePboardType_urld, + kCorePboardType_urln, + nil]]; } // initWithFrame:geckoChild: - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; if ((self = [super initWithFrame:inFrame])) { @@ -5717,16 +5724,17 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) } } return NSDragOperationGeneric; NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone); } +// NSDraggingDestination - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingEntered: entered\n")); // there should never be a globalDragPboard when "draggingEntered:" is // called, but just in case we'll take care of it here. @@ -5737,91 +5745,90 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) // the view or a drop happens within the view). globalDragPboard = [[sender draggingPasteboard] retain]; return [self doDragAction:eDragEnter sender:sender]; NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone); } +// NSDraggingDestination - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n")); - return [self doDragAction:eDragOver sender:sender]; } +// NSDraggingDestination - (void)draggingExited:(id <NSDraggingInfo>)sender { MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingExited: entered\n")); nsAutoRetainCocoaObject kungFuDeathGrip(self); [self doDragAction:eDragExit sender:sender]; NS_IF_RELEASE(mDragService); } +// NSDraggingDestination - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { nsAutoRetainCocoaObject kungFuDeathGrip(self); BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone; NS_IF_RELEASE(mDragService); return handled; } // NSDraggingSource -- (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint -{ - // Get the drag service if it isn't already cached. The drag service - // isn't cached when dragging over a different application. - nsCOMPtr<nsIDragService> dragService = mDragService; - if (!dragService) { - dragService = do_GetService(kDragServiceContractID); - } - - if (dragService) { - NSPoint pnt = [NSEvent mouseLocation]; - FlipCocoaScreenCoordinate(pnt); - - LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt); - dragService->DragMoved(devPoint.x, devPoint.y); - } +// This is just implemented so we comply with the NSDraggingSource protocol. +- (NSDragOperation)draggingSession:(NSDraggingSession*)session + sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + return UINT_MAX; } // NSDraggingSource -- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession*)session +{ + return YES; +} + +// NSDraggingSource +- (void)draggingSession:(NSDraggingSession*)aSession + endedAtPoint:(NSPoint)aPoint + operation:(NSDragOperation)aOperation { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; gDraggedTransferables = nullptr; - NSEvent *currentEvent = [NSApp currentEvent]; + NSEvent* currentEvent = [NSApp currentEvent]; gUserCancelledDrag = ([currentEvent type] == NSKeyDown && [currentEvent keyCode] == kVK_Escape); if (!mDragService) { CallGetService(kDragServiceContractID, &mDragService); NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!"); } if (mDragService) { // set the dragend point from the current mouse location nsDragService* dragService = static_cast<nsDragService *>(mDragService); NSPoint pnt = [NSEvent mouseLocation]; FlipCocoaScreenCoordinate(pnt); dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y)); - // XXX: dropEffect should be updated per |operation|. - // As things stand though, |operation| isn't well handled within "our" + // XXX: dropEffect should be updated per |aOperation|. + // As things stand though, |aOperation| isn't well handled within "our" // events, that is, when the drop happens within the window: it is set // either to NSDragOperationGeneric or to NSDragOperationNone. // For that reason, it's not yet possible to override dropEffect per the // given OS value, and it's also unclear what's the correct dropEffect // value for NSDragOperationGeneric that is passed by other applications. // All that said, NSDragOperationNone is still reliable. - if (operation == NSDragOperationNone) { + if (aOperation == NSDragOperationNone) { nsCOMPtr<nsIDOMDataTransfer> dataTransfer; dragService->GetDataTransfer(getter_AddRefs(dataTransfer)); if (dataTransfer) dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE); } mDragService->EndDragSession(true); NS_RELEASE(mDragService); @@ -5831,20 +5838,55 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) globalDragPboard = nil; [gLastDragMouseDownEvent release]; gLastDragMouseDownEvent = nil; NS_OBJC_END_TRY_ABORT_BLOCK; } // NSDraggingSource -// this is just implemented so we comply with the NSDraggingSource informal protocol -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal -{ - return UINT_MAX; +- (void)draggingSession:(NSDraggingSession*)aSession + movedToPoint:(NSPoint)aPoint +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + // Get the drag service if it isn't already cached. The drag service + // isn't cached when dragging over a different application. + nsCOMPtr<nsIDragService> dragService = mDragService; + if (!dragService) { + dragService = do_GetService(kDragServiceContractID); + } + + if (dragService) { + NSPoint pnt = [NSEvent mouseLocation]; + FlipCocoaScreenCoordinate(pnt); + + LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt); + dragService->DragMoved(devPoint.x, devPoint.y); + } + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + +// NSDraggingSource +- (void)draggingSession:(NSDraggingSession*)aSession + willBeginAtPoint:(NSPoint)aPoint +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + // there should never be a globalDragPboard when "willBeginAtPoint:" is + // called, but just in case we'll take care of it here. + [globalDragPboard release]; + + // Set the global drag pasteboard that will be used for this drag session. + // This will be set back to nil when the drag session ends (mouse exits + // the view or a drop happens within the view). + globalDragPboard = [[aSession draggingPasteboard] retain]; + + NS_OBJC_END_TRY_ABORT_BLOCK; } // This method is a callback typically invoked in response to a drag ending on the desktop // or a Findow folder window; the argument passed is a path to the drop location, to be used // in constructing a complete pathname for the file(s) we want to create as a result of // the drag. - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination { @@ -5898,16 +5940,78 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) [name release]; return rslt; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } +// NSPasteboardItemDataProvider +- (void)pasteboard:(NSPasteboard*)aPasteboard + item:(NSPasteboardItem*)aItem +provideDataForType:(NSString*)aType +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (!gDraggedTransferables) { + return; + } + + uint32_t count = 0; + gDraggedTransferables->GetLength(&count); + + for (uint32_t j = 0; j < count; j++) { + nsCOMPtr<nsITransferable> currentTransferable = + do_QueryElementAt(gDraggedTransferables, j); + if (!currentTransferable) { + return; + } + + // Transform the transferable to an NSDictionary. + NSDictionary* pasteboardOutputDict = + nsClipboard::PasteboardDictFromTransferable(currentTransferable); + if (!pasteboardOutputDict) { + return; + } + + // Write everything out to the pasteboard. + unsigned int typeCount = [pasteboardOutputDict count]; + NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1]; + [types addObjectsFromArray:[pasteboardOutputDict allKeys]]; + [types addObject:kWildcardPboardType]; + for (unsigned int k = 0; k < typeCount; k++) { + NSString* curType = [types objectAtIndex:k]; + if ([curType isEqualToString:NSPasteboardTypeString] || + [curType isEqualToString:kCorePboardType_url] || + [curType isEqualToString:kCorePboardType_urld] || + [curType isEqualToString:kCorePboardType_urln]) { + [aPasteboard setString:[pasteboardOutputDict valueForKey:curType] + forType:curType]; + } else if ([curType isEqualToString:NSPasteboardTypeHTML]) { + [aPasteboard setString: + (nsClipboard::WrapHtmlForSystemPasteboard( + [pasteboardOutputDict valueForKey:curType])) + forType:curType]; + } else if ([curType isEqualToString:NSPasteboardTypeTIFF] || + [curType isEqualToString:kCustomTypesPboardType]) { + [aPasteboard setData:[pasteboardOutputDict valueForKey:curType] + forType:curType]; + } else if ( + [curType isEqualToString:(NSString*)kPasteboardTypeFileURLPromise] || + [curType isEqualToString:NSFilenamesPboardType]) { + [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType] + forType:curType]; + } + } + } + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + #pragma mark - // Support for the "Services" menu. We currently only support sending strings // and HTML to system services. - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType { @@ -5920,17 +6024,19 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) // returnType contains the type of data the the service would like to // return to this application (e.g., to overwrite the selection). // returnType is nil if the service will not return any data. // // The following condition thus triggers when the service expects a string // or HTML from us or no data at all AND when the service will either not // send back any data to us or will send a string or HTML back to us. -#define IsSupportedType(typeStr) ([typeStr isEqual:NSStringPboardType] || [typeStr isEqual:NSHTMLPboardType]) +#define IsSupportedType(typeStr) \ + ([typeStr isEqualToString:NSPasteboardTypeString] || \ + [typeStr isEqualToString:NSPasteboardTypeHTML]) id result = nil; if ((!sendType || IsSupportedType(sendType)) && (!returnType || IsSupportedType(returnType))) { if (mGeckoChild) { // Assume that this object will be able to handle this request. result = self; @@ -5972,18 +6078,18 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; nsAutoRetainCocoaObject kungFuDeathGrip(self); // Make sure that the service will accept strings or HTML. - if ([types containsObject:NSStringPboardType] == NO && - [types containsObject:NSHTMLPboardType] == NO) + if ([types containsObject:NSPasteboardTypeString] == NO && + [types containsObject:NSPasteboardTypeHTML] == NO) return NO; // Bail out if there is no Gecko object. if (!mGeckoChild) return NO; // Transform the transferable to an NSDictionary. NSDictionary* pasteboardOutputDict = nullptr; @@ -6000,26 +6106,28 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]]; [pboard declareTypes:declaredTypes owner:nil]; // Write the data to the pasteboard. for (unsigned int i = 0; i < typeCount; i++) { NSString* currentKey = [declaredTypes objectAtIndex:i]; id currentValue = [pasteboardOutputDict valueForKey:currentKey]; - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { + if ([currentKey isEqualToString:NSPasteboardTypeString] || + [currentKey isEqualToString:kCorePboardType_url] || + [currentKey isEqualToString:kCorePboardType_urld] || + [currentKey isEqualToString:kCorePboardType_urln]) { [pboard setString:currentValue forType:currentKey]; - } else if (currentKey == NSHTMLPboardType) { - [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey]; - } else if (currentKey == NSTIFFPboardType) { + } else if ([currentKey isEqualToString:NSPasteboardTypeHTML]) { + [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) + forType:currentKey]; + } else if ([currentKey isEqualToString:NSPasteboardTypeTIFF]) { [pboard setData:currentValue forType:currentKey]; - } else if (currentKey == NSFilesPromisePboardType) { + } else if ([currentKey isEqualToString: + (NSString*)kPasteboardTypeFileURLPromise]) { [pboard setPropertyList:currentValue forType:currentKey]; } } return YES; NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); }
--- a/widget/cocoa/nsClipboard.mm +++ b/widget/cocoa/nsClipboard.mm @@ -25,17 +25,19 @@ #include "imgIContainer.h" #include "nsCocoaUtils.h" using mozilla::gfx::DataSourceSurface; using mozilla::gfx::SourceSurface; using mozilla::LogLevel; // Screenshots use the (undocumented) png pasteboard type. -#define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil +#define IMAGE_PASTEBOARD_TYPES NSPasteboardTypeTIFF, \ + @"Apple PNG pasteboard type", \ + nil extern PRLogModuleInfo* sCocoaLog; extern void EnsureLogInitialized(); mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache; nsClipboard::nsClipboard() @@ -98,36 +100,37 @@ nsClipboard::SetNativeClipboardData(int3 if (!pasteboardOutputDict) return NS_ERROR_FAILURE; unsigned int outputCount = [pasteboardOutputDict count]; NSArray* outputKeys = [pasteboardOutputDict allKeys]; NSPasteboard* cocoaPasteboard; if (aWhichClipboard == kFindClipboard) { cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; - [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + [cocoaPasteboard declareTypes: + [NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; } else { // Write everything else out to the general pasteboard. cocoaPasteboard = [NSPasteboard generalPasteboard]; [cocoaPasteboard declareTypes:outputKeys owner:nil]; } for (unsigned int i = 0; i < outputCount; i++) { NSString* currentKey = [outputKeys objectAtIndex:i]; id currentValue = [pasteboardOutputDict valueForKey:currentKey]; if (aWhichClipboard == kFindClipboard) { - if (currentKey == NSStringPboardType) + if ([currentKey isEqualToString:NSPasteboardTypeString]) [cocoaPasteboard setString:currentValue forType:currentKey]; } else { - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { + if ([currentKey isEqualToString:NSPasteboardTypeString] || + [currentKey isEqualToString:kCorePboardType_url] || + [currentKey isEqualToString:kCorePboardType_urld] || + [currentKey isEqualToString:kCorePboardType_urln]) { [cocoaPasteboard setString:currentValue forType:currentKey]; - } else if (currentKey == NSHTMLPboardType) { + } else if ([currentKey isEqualToString:NSPasteboardTypeHTML]) { [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey]; } else { [cocoaPasteboard setData:currentValue forType:currentKey]; } } } @@ -167,17 +170,17 @@ nsClipboard::TransferableFromPasteboard( NSString *pboardType = nil; if (nsClipboard::IsStringType(flavorStr, &pboardType)) { NSString* pString = [cocoaPasteboard stringForType:pboardType]; if (!pString) continue; NSData* stringData; - if ([pboardType isEqualToString:NSRTFPboardType]) { + if ([pboardType isEqualToString:NSPasteboardTypeRTF]) { stringData = [pString dataUsingEncoding:NSASCIIStringEncoding]; } else { stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; } unsigned int dataLength = [stringData length]; void* clipboardDataPtr = malloc(dataLength); if (!clipboardDataPtr) return NS_ERROR_OUT_OF_MEMORY; @@ -254,18 +257,20 @@ nsClipboard::TransferableFromPasteboard( outputType = CFSTR("com.compuserve.gif"); else continue; // Use ImageIO to interpret the data on the clipboard and transcode. // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely // and safely in most cases (like ObjC). A notable exception is CFRelease. NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: - (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat, - (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"), + (NSNumber*)kCFBooleanTrue, + kCGImageSourceShouldAllowFloat, + (type == NSPasteboardTypeTIFF ? @"public.tiff" : + @"public.png"), kCGImageSourceTypeIdentifierHint, nil]; CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, (CFDictionaryRef)options); NSMutableData *encodedData = [NSMutableData data]; CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, outputType, 1, NULL); @@ -550,17 +555,18 @@ nsClipboard::PasteboardDictFromTransfera CFRelease(destRef); if (!successfullyConverted) { if (tiffData) CFRelease(tiffData); continue; } - [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType]; + [pasteboardOutputDict setObject:(NSMutableData*)tiffData + forKey:NSPasteboardTypeTIFF]; if (tiffData) CFRelease(tiffData); } else if (flavorStr.EqualsLiteral(kFileMime)) { uint32_t len = 0; nsCOMPtr<nsISupports> genericFile; rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len); if (NS_FAILED(rv)) { @@ -587,17 +593,18 @@ nsClipboard::PasteboardDictFromTransfera continue; } NSString* str = nsCocoaUtils::ToNSString(fileURI); NSArray* fileList = [NSArray arrayWithObjects:str, nil]; [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType]; } else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { - [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType]; + [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] + forKey:(NSString*)kPasteboardTypeFileURLPromise]; } else if (flavorStr.EqualsLiteral(kURLMime)) { uint32_t len = 0; nsCOMPtr<nsISupports> genericURL; rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len); nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL)); nsAutoString url; @@ -640,23 +647,23 @@ nsClipboard::PasteboardDictFromTransfera return pasteboardOutputDict; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) { if (aMIMEType.EqualsLiteral(kUnicodeMime)) { - *aPasteboardType = NSStringPboardType; + *aPasteboardType = NSPasteboardTypeString; return true; } else if (aMIMEType.EqualsLiteral(kRTFMime)) { - *aPasteboardType = NSRTFPboardType; + *aPasteboardType = NSPasteboardTypeRTF; return true; } else if (aMIMEType.EqualsLiteral(kHTMLMime)) { - *aPasteboardType = NSHTMLPboardType; + *aPasteboardType = NSPasteboardTypeHTML; return true; } else { return false; } } NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) {
--- a/widget/cocoa/nsDragService.h +++ b/widget/cocoa/nsDragService.h @@ -2,16 +2,17 @@ /* 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/. */ #ifndef nsDragService_h_ #define nsDragService_h_ #include "nsBaseDragService.h" +#include "nsChildView.h" #include <Cocoa/Cocoa.h> extern NSString* const kWildcardPboardType; extern NSString* const kCorePboardType_url; extern NSString* const kCorePboardType_urld; extern NSString* const kCorePboardType_urln; extern NSString* const kCustomTypesPboardType; @@ -43,13 +44,13 @@ private: nsIScriptableRegion* aRegion); bool IsValidType(NSString* availableType, bool allowFileURL); NSString* GetStringForType(NSPasteboardItem* item, const NSString* type, bool allowFileURL = false); NSString* GetTitleForURL(NSPasteboardItem* item); NSString* GetFilePath(NSPasteboardItem* item); nsCOMPtr<nsIArray> mDataItems; // only valid for a drag started within gecko - NSView* mNativeDragView; + ChildView* mNativeDragView; NSEvent* mNativeDragEvent; }; #endif // nsDragService_h_
--- a/widget/cocoa/nsDragService.mm +++ b/widget/cocoa/nsDragService.mm @@ -33,102 +33,46 @@ using namespace mozilla; using namespace mozilla::gfx; extern PRLogModuleInfo* sCocoaLog; extern void EnsureLogInitialized(); extern NSPasteboard* globalDragPboard; -extern NSView* gLastDragView; +extern ChildView* gLastDragView; extern NSEvent* gLastDragMouseDownEvent; extern bool gUserCancelledDrag; // This global makes the transferable array available to Cocoa's promised // file destination callback. nsIArray *gDraggedTransferables = nullptr; -NSString* const kWildcardPboardType = @"MozillaWildcard"; -NSString* const kCorePboardType_url = @"CorePasteboardFlavorType 0x75726C20"; // 'url ' url -NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld' desc -NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln' title -NSString* const kUTTypeURLName = @"public.url-name"; +NSString* const kWildcardPboardType = @"org.mozilla.MozillaWildcard"; +NSString* const kCorePboardType_url = + @"org.mozilla.CorePasteboardFlavorType0x75726C20"; // 'url ' url +NSString* const kCorePboardType_urld = + @"org.mozilla.CorePasteboardFlavorType0x75726C64"; // 'urld' desc +NSString* const kCorePboardType_urln = + @"org.mozilla.CorePasteboardFlavorType0x75726C6E"; // 'urln' title +NSString* const kUTTypeURLName = @"public.url-name"; NSString* const kCustomTypesPboardType = @"org.mozilla.custom-clipdata"; nsDragService::nsDragService() { mNativeDragView = nil; mNativeDragEvent = nil; EnsureLogInitialized(); } nsDragService::~nsDragService() { } -static nsresult SetUpDragClipboard(nsIArray* aTransferableArray) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!aTransferableArray) - return NS_ERROR_FAILURE; - - uint32_t count = 0; - aTransferableArray->GetLength(&count); - - NSPasteboard* dragPBoard = [NSPasteboard pasteboardWithName:NSDragPboard]; - - for (uint32_t j = 0; j < count; j++) { - nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(aTransferableArray, j); - if (!currentTransferable) - return NS_ERROR_FAILURE; - - // Transform the transferable to an NSDictionary - NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(currentTransferable); - if (!pasteboardOutputDict) - return NS_ERROR_FAILURE; - - // write everything out to the general pasteboard - unsigned int typeCount = [pasteboardOutputDict count]; - NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1]; - [types addObjectsFromArray:[pasteboardOutputDict allKeys]]; - // Gecko is initiating this drag so we always want its own views to consider - // it. Add our wildcard type to the pasteboard to accomplish this. - [types addObject:kWildcardPboardType]; // we don't increase the count for the loop below on purpose - [dragPBoard declareTypes:types owner:nil]; - for (unsigned int k = 0; k < typeCount; k++) { - NSString* currentKey = [types objectAtIndex:k]; - id currentValue = [pasteboardOutputDict valueForKey:currentKey]; - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { - [dragPBoard setString:currentValue forType:currentKey]; - } - else if (currentKey == NSHTMLPboardType) { - [dragPBoard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) - forType:currentKey]; - } - else if (currentKey == NSTIFFPboardType || - currentKey == kCustomTypesPboardType) { - [dragPBoard setData:currentValue forType:currentKey]; - } - else if (currentKey == NSFilesPromisePboardType || - currentKey == NSFilenamesPboardType) { - [dragPBoard setPropertyList:currentValue forType:currentKey]; - } - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - NSImage* nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode, LayoutDeviceIntRect* aDragRect, nsIScriptableRegion* aRegion) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); @@ -298,19 +242,59 @@ nsresult nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray, nsIScriptableRegion* aDragRgn, uint32_t aActionType) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; mDataItems = aTransferableArray; - // put data on the clipboard - if (NS_FAILED(SetUpDragClipboard(aTransferableArray))) - return NS_ERROR_FAILURE; + // Save the transferables away in case a promised file callback is invoked. + gDraggedTransferables = aTransferableArray; + + nsBaseDragService::StartDragSession(); + nsBaseDragService::OpenDragPopup(); + + // We need to retain the view and the event during the drag in case either + // gets destroyed. + mNativeDragView = [gLastDragView retain]; + mNativeDragEvent = [gLastDragMouseDownEvent retain]; + + gUserCancelledDrag = false; + + NSPasteboardItem* pbItem = [NSPasteboardItem new]; + NSMutableArray* types = [NSMutableArray arrayWithCapacity:5]; + + if (gDraggedTransferables) { + uint32_t count = 0; + gDraggedTransferables->GetLength(&count); + + for (uint32_t j = 0; j < count; j++) { + nsCOMPtr<nsITransferable> currentTransferable = + do_QueryElementAt(aTransferableArray, j); + if (!currentTransferable) { + return NS_ERROR_FAILURE; + } + + // Transform the transferable to an NSDictionary + NSDictionary* pasteboardOutputDict = + nsClipboard::PasteboardDictFromTransferable(currentTransferable); + if (!pasteboardOutputDict) { + return NS_ERROR_FAILURE; + } + + // write everything out to the general pasteboard + [types addObjectsFromArray:[pasteboardOutputDict allKeys]]; + // Gecko is initiating this drag so we always want its own views to + // consider it. Add our wildcard type to the pasteboard to accomplish + // this. + [types addObject:kWildcardPboardType]; + } + } + [pbItem setDataProvider:mNativeDragView forTypes:types]; CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); LayoutDeviceIntRect dragRect(0, 0, 20, 20); NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn); if (!image) { // if no image was returned, just draw a rectangle NSSize size; @@ -325,46 +309,38 @@ nsDragService::InvokeDragSessionImpl(nsI [path lineToPoint:NSMakePoint(0, size.height)]; [path lineToPoint:NSMakePoint(size.width, size.height)]; [path lineToPoint:NSMakePoint(size.width, 0)]; [path lineToPoint:NSMakePoint(0, 0)]; [path stroke]; [image unlockFocus]; } + // Make drag image appear in the right place under the cursor. LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost()); NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor); point.y = nsCocoaUtils::FlippedScreenY(point.y); - point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point); NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil]; - - // Save the transferables away in case a promised file callback is invoked. - gDraggedTransferables = aTransferableArray; - - nsBaseDragService::StartDragSession(); - nsBaseDragService::OpenDragPopup(); - - // We need to retain the view and the event during the drag in case either gets destroyed. - mNativeDragView = [gLastDragView retain]; - mNativeDragEvent = [gLastDragMouseDownEvent retain]; + NSRect localDragRect = image.alignmentRect; + localDragRect.origin.x = localPoint.x; + localDragRect.origin.y = localPoint.y - localDragRect.size.height; - gUserCancelledDrag = false; - [mNativeDragView dragImage:image - at:localPoint - offset:NSZeroSize - event:mNativeDragEvent - pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] - source:mNativeDragView - slideBack:YES]; - gUserCancelledDrag = false; + NSDraggingItem* dragItem = + [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem]; + [pbItem release]; + [dragItem setDraggingFrame:localDragRect contents:image]; - if (mDoingDrag) - nsBaseDragService::EndDragSession(false); - + NSDraggingSession* draggingSession = + [mNativeDragView beginDraggingSessionWithItems: + [NSArray arrayWithObject:[dragItem autorelease]] + event:mNativeDragEvent + source:mNativeDragView]; + draggingSession.animatesToStartingPositionsOnCancelOrFail = YES; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {