зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c, a=merge
MozReview-Commit-ID: EI1fU13SR79
This commit is contained in:
Коммит
4f5f9f3222
|
@ -65,10 +65,6 @@ function ContentSearchUIController(inputElement, tableParent, healthReportKey,
|
|||
|
||||
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
|
||||
|
@ -716,7 +712,6 @@ ContentSearchUIController.prototype = {
|
|||
this._sendMsg("GetSuggestions", {
|
||||
engineName: this.defaultEngine.name,
|
||||
searchString: this.input.value,
|
||||
remoteTimeout: this.remoteTimeout,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -316,9 +316,6 @@ add_task(function* () {
|
|||
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();
|
||||
|
|
|
@ -27,7 +27,6 @@ var messageHandlers = {
|
|||
ack("init");
|
||||
}
|
||||
});
|
||||
gController.remoteTimeout = 5000;
|
||||
},
|
||||
|
||||
key: function(arg) {
|
||||
|
|
|
@ -125,11 +125,6 @@ add_task(function* () {
|
|||
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.
|
||||
|
|
|
@ -41,7 +41,7 @@ const MAX_SUGGESTIONS = 6;
|
|||
* 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
|
||||
|
@ -259,7 +259,7 @@ this.ContentSearch = {
|
|||
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);
|
||||
|
@ -270,7 +270,6 @@ this.ContentSearch = {
|
|||
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.
|
||||
|
|
|
@ -6,10 +6,6 @@ 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() {
|
||||
|
@ -192,7 +188,6 @@ add_task(function* GetSuggestions_AddFormHistoryEntry_RemoveFormHistoryEntry() {
|
|||
data: {
|
||||
engineName: engine.name,
|
||||
searchString: searchStr,
|
||||
remoteTimeout: SUGGESTIONS_TIMEOUT,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -228,7 +223,6 @@ add_task(function* GetSuggestions_AddFormHistoryEntry_RemoveFormHistoryEntry() {
|
|||
data: {
|
||||
engineName: engine.name,
|
||||
searchString: searchStr,
|
||||
remoteTimeout: SUGGESTIONS_TIMEOUT,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -67,10 +67,6 @@ function checkKeyedScalar(scalars, scalarName, key, expectedValue) {
|
|||
*/
|
||||
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();
|
||||
|
|
|
@ -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
|
|
@ -98,13 +98,13 @@ Timeout::InitTimer(nsIEventTarget* aTarget, uint32_t aDelay)
|
|||
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
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
|
||||
|
||||
#ifdef DEBUG
|
||||
bool HasRefCntOne() const;
|
||||
bool HasRefCnt(uint32_t aCount) const;
|
||||
#endif // DEBUG
|
||||
|
||||
// Window for which this timeout fires
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsITimeoutHandler.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "OrderedTimeoutIterator.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -33,6 +34,13 @@ TimeoutManager::DOMMinTimeoutValue() const {
|
|||
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
|
||||
|
@ -112,6 +120,9 @@ TimeoutManager::Initialize()
|
|||
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
|
||||
|
@ -133,7 +144,8 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -228,8 +240,34 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -242,7 +280,7 @@ 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
|
||||
|
@ -276,9 +314,11 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
|
||||
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
|
||||
|
@ -312,36 +352,53 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
@ -350,92 +407,168 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
for (Timeout* timeout = mTimeouts.GetFirst();
|
||||
timeout != dummy_timeout && !mWindow.IsFrozen();
|
||||
timeout = nextTimeout) {
|
||||
nextTimeout = timeout->getNext();
|
||||
RefPtr<Timeout> timeoutExtraRef1(dummy_normal_timeout);
|
||||
RefPtr<Timeout> timeoutExtraRef2(dummy_tracking_timeout);
|
||||
|
||||
if (timeout->mFiringDepth != firingDepth) {
|
||||
// We skip the timeout since it's on the list to run at another
|
||||
// depth.
|
||||
// Now we need to search the normal and tracking timer list at the same
|
||||
// time to run the timers in the scheduled order.
|
||||
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 (timeout->mFiringDepth != firingDepth) {
|
||||
// We skip the timeout since it's on the list to run at another
|
||||
// depth.
|
||||
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.
|
||||
|
||||
// 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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
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 (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 (!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) {
|
||||
// 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();
|
||||
|
||||
mTimeouts.SetInsertionPoint(last_insertion_point);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
@ -632,13 +765,22 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
|
|||
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
|
||||
|
@ -736,7 +878,7 @@ 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
|
||||
|
@ -764,11 +906,13 @@ TimeoutManager::ClearAllTimeouts()
|
|||
});
|
||||
|
||||
if (seenRunningTimeout) {
|
||||
mTimeouts.SetInsertionPoint(nullptr);
|
||||
mNormalTimeouts.SetInsertionPoint(nullptr);
|
||||
mTrackingTimeouts.SetInsertionPoint(nullptr);
|
||||
}
|
||||
|
||||
// Clear out our list
|
||||
mTimeouts.Clear();
|
||||
mNormalTimeouts.Clear();
|
||||
mTrackingTimeouts.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -826,7 +970,7 @@ TimeoutManager::EndRunningTimeout(Timeout* aTimeout)
|
|||
void
|
||||
TimeoutManager::UnmarkGrayTimers()
|
||||
{
|
||||
ForEachTimeout([](Timeout* aTimeout) {
|
||||
ForEachUnorderedTimeout([](Timeout* aTimeout) {
|
||||
if (aTimeout->mScriptHandler) {
|
||||
aTimeout->mScriptHandler->MarkForCC();
|
||||
}
|
||||
|
@ -836,7 +980,7 @@ TimeoutManager::UnmarkGrayTimers()
|
|||
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.
|
||||
|
@ -859,7 +1003,7 @@ 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.
|
||||
|
@ -905,7 +1049,7 @@ 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
|
||||
|
@ -928,7 +1072,7 @@ 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.
|
||||
|
@ -944,3 +1088,11 @@ TimeoutManager::Thaw()
|
|||
MOZ_ASSERT(!aTimeout->mTimer);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId)
|
||||
{
|
||||
return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
|
||||
return aTimeout->mTimeoutId == aTimeoutId;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ class ThrottledEventQueue;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class OrderedTimeoutIterator;
|
||||
|
||||
// This class manages the timeouts in a Window's setTimeout/setInterval pool.
|
||||
class TimeoutManager final
|
||||
{
|
||||
|
@ -31,7 +33,11 @@ public:
|
|||
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,
|
||||
|
@ -83,26 +89,33 @@ public:
|
|||
// 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)
|
||||
|
@ -149,19 +162,25 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -173,10 +192,15 @@ private:
|
|||
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;
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/gfx/GPUProcessManager.h"
|
||||
#include "mozilla/dom/TimeoutManager.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef GetClassName
|
||||
|
@ -4092,6 +4093,21 @@ nsDOMWindowUtils::GetGpuProcessPid(int32_t* aPid)
|
|||
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)
|
||||
|
|
|
@ -1936,7 +1936,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
|||
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));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
|
||||
#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"
|
||||
|
@ -149,9 +148,6 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
|||
}
|
||||
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -3,15 +3,25 @@
|
|||
<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>
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
<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 => {
|
||||
|
@ -20,15 +22,24 @@ function onLoad() {
|
|||
});
|
||||
}
|
||||
|
||||
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';
|
||||
|
@ -75,7 +86,9 @@ function testRequestAnimationFrame() {
|
|||
|
||||
let floodFrame;
|
||||
|
||||
onLoad().then(_ => {
|
||||
onLoad()
|
||||
.then(setPrefs)
|
||||
.then(_ => {
|
||||
// Start a timer flood in a frame.
|
||||
return withFloodFrame();
|
||||
}).then(frame => {
|
||||
|
@ -109,6 +122,8 @@ onLoad().then(_ => {
|
|||
ok(true, 'completed tests without timing out');
|
||||
floodFrame.remove();
|
||||
SimpleTest.finish();
|
||||
}).catch(_ => {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -270,8 +270,12 @@ public:
|
|||
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
|
||||
|
@ -325,6 +329,8 @@ public:
|
|||
// -------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
|
|
@ -131,13 +131,13 @@ WebGL2Context::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
|||
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
|
||||
|
|
|
@ -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
|
|
@ -112,13 +112,13 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
|
||||
/* 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;
|
||||
|
|
|
@ -67,6 +67,12 @@ WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -182,6 +182,14 @@ WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
|
|||
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));
|
||||
|
|
|
@ -974,6 +974,7 @@ private:
|
|||
realGLboolean mScissorTestEnabled;
|
||||
realGLboolean mDepthTestEnabled;
|
||||
realGLboolean mStencilTestEnabled;
|
||||
GLenum mGenerateMipmapHint;
|
||||
|
||||
bool ValidateCapabilityEnum(GLenum cap, const char* info);
|
||||
realGLboolean* GetStateTrackingSlot(GLenum cap);
|
||||
|
|
|
@ -692,9 +692,6 @@ WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
|
|||
}
|
||||
return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
|
||||
return JS::NullValue();
|
||||
|
||||
////////////////
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
|
||||
|
@ -947,6 +944,8 @@ WebGLContext::Hint(GLenum target, GLenum mode)
|
|||
|
||||
switch (target) {
|
||||
case LOCAL_GL_GENERATE_MIPMAP_HINT:
|
||||
mGenerateMipmapHint = mode;
|
||||
|
||||
// Deprecated and removed in desktop GL Core profiles.
|
||||
if (gl->IsCoreProfile())
|
||||
return;
|
||||
|
@ -1465,7 +1464,7 @@ ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -386,12 +386,15 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
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;
|
||||
|
@ -399,12 +402,14 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
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: {
|
||||
|
@ -414,7 +419,10 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
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;
|
||||
|
|
|
@ -501,6 +501,7 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
|
|||
mDitherEnabled = true;
|
||||
mRasterizerDiscardEnabled = false;
|
||||
mScissorTestEnabled = false;
|
||||
mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
|
||||
|
||||
// Bindings, etc.
|
||||
mActiveTexture = 0;
|
||||
|
|
|
@ -238,16 +238,26 @@ WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
|
|||
|
||||
// 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;
|
||||
|
@ -1014,7 +1024,7 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar
|
|||
{
|
||||
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;
|
||||
|
|
|
@ -444,6 +444,11 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
|
|||
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());
|
||||
}
|
||||
|
||||
|
@ -2019,6 +2024,13 @@ DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName, bool isSubImage,
|
|||
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);
|
||||
|
|
|
@ -73,7 +73,6 @@ UNIFIED_SOURCES += [
|
|||
'WebGL1ContextUniforms.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGL2ContextBuffers.cpp',
|
||||
'WebGL2ContextDraw.cpp',
|
||||
'WebGL2ContextFramebuffers.cpp',
|
||||
'WebGL2ContextMRTs.cpp',
|
||||
'WebGL2ContextPrograms.cpp',
|
||||
|
|
|
@ -4586,7 +4586,6 @@ skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.
|
|||
[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'))
|
||||
|
@ -5769,7 +5768,7 @@ skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.
|
|||
[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]
|
||||
|
@ -5794,8 +5793,7 @@ skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.
|
|||
[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]
|
||||
|
|
|
@ -301,6 +301,9 @@ skip-if = (os == 'android')
|
|||
# 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')
|
||||
|
@ -533,8 +536,6 @@ 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]
|
||||
|
@ -546,12 +547,6 @@ skip-if = (os == 'mac' && debug)
|
|||
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)
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
########################################################################
|
||||
|
|
|
@ -3327,6 +3327,20 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
{
|
||||
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;
|
||||
|
|
|
@ -1973,6 +1973,12 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
*/
|
||||
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;
|
||||
|
|
|
@ -270,6 +270,44 @@ protected:
|
|||
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());
|
||||
|
|
|
@ -51,6 +51,7 @@ TEST_HARNESS_FILES.gtest += [
|
|||
'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',
|
||||
|
|
Двоичный файл не отображается.
|
@ -116,10 +116,11 @@ SourceSurfaceSkia::GetData()
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!raster) {
|
||||
if (raster) {
|
||||
mImage = raster;
|
||||
} else {
|
||||
gfxCriticalError() << "Failed making Skia raster image for GPU surface";
|
||||
}
|
||||
mImage = raster;
|
||||
}
|
||||
#endif
|
||||
SkPixmap pixmap;
|
||||
|
|
|
@ -31,6 +31,9 @@ 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);
|
||||
}
|
||||
|
|
|
@ -1521,6 +1521,17 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
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))
|
||||
|
|
|
@ -923,6 +923,42 @@ jit::FoldTests(MIRGraph& graph)
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -24,6 +24,9 @@ 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);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
/* Specific passes during ion compilation */ \
|
||||
_(PruneUnusedBranches) \
|
||||
_(FoldTests) \
|
||||
_(FoldEmptyBlocks) \
|
||||
_(SplitCriticalEdges) \
|
||||
_(RenumberBlocks) \
|
||||
_(ScalarReplacement) \
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#include "gfxEnv.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsTableWrapperFrame.h"
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsFontFaceList.h"
|
||||
#include "nsFontInflationData.h"
|
||||
|
@ -5868,6 +5869,19 @@ nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
|
|||
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
|
||||
|
|
|
@ -490,12 +490,44 @@ nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItem
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const
|
||||
{
|
||||
nscoord result;
|
||||
if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result))
|
||||
return result;
|
||||
return nsFrame::GetLogicalBaseline(aWritingMode);
|
||||
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
|
||||
|
|
|
@ -121,6 +121,19 @@ public:
|
|||
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;
|
||||
|
|
|
@ -1021,6 +1021,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
|
|||
|
||||
if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
|
||||
PositionFrameView(aKidFrame);
|
||||
PositionChildViews(aKidFrame);
|
||||
}
|
||||
|
||||
// Reflow the child frame
|
||||
|
@ -1064,6 +1065,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
|
|||
|
||||
if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
|
||||
PositionFrameView(aKidFrame);
|
||||
PositionChildViews(aKidFrame);
|
||||
}
|
||||
|
||||
// Reflow the child frame
|
||||
|
|
|
@ -455,7 +455,8 @@ public:
|
|||
nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
|
||||
|
||||
if (!found) {
|
||||
mAscent = mFrame->GetLogicalBaseline(mWM);
|
||||
mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
|
||||
BaselineSharingGroup::eFirst);
|
||||
}
|
||||
}
|
||||
return mAscent;
|
||||
|
@ -2324,6 +2325,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -4259,6 +4264,14 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
|||
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,
|
||||
|
@ -4519,12 +4532,15 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
|||
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,
|
||||
|
@ -4549,6 +4565,16 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -83,6 +83,24 @@ public:
|
|||
|
||||
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,
|
||||
|
@ -116,6 +134,7 @@ protected:
|
|||
explicit nsFlexContainerFrame(nsStyleContext* aContext)
|
||||
: nsContainerFrame(aContext)
|
||||
, mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
|
||||
, mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
|
||||
{}
|
||||
virtual ~nsFlexContainerFrame();
|
||||
|
||||
|
@ -301,6 +320,8 @@ protected:
|
|||
// 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___ */
|
||||
|
|
|
@ -311,6 +311,9 @@ FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_CHILDREN_REORDERED)
|
|||
// '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)
|
||||
|
@ -328,6 +331,9 @@ 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)
|
||||
|
|
|
@ -6159,3 +6159,60 @@ ScrollFrameHelper::UsesContainerScrolling() const
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -460,6 +460,8 @@ public:
|
|||
return mSuppressScrollbarRepaints;
|
||||
}
|
||||
|
||||
bool DragScroll(WidgetEvent* aEvent);
|
||||
|
||||
// owning references to the nsIAnonymousContentCreator-built content
|
||||
nsCOMPtr<nsIContent> mHScrollbarContent;
|
||||
nsCOMPtr<nsIContent> mVScrollbarContent;
|
||||
|
@ -724,6 +726,12 @@ public:
|
|||
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.
|
||||
|
@ -1033,6 +1041,10 @@ public:
|
|||
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
|
||||
|
@ -1110,6 +1122,12 @@ public:
|
|||
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,
|
||||
|
@ -1456,6 +1474,10 @@ public:
|
|||
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
|
||||
|
|
|
@ -198,7 +198,7 @@ struct nsGridContainerFrame::TrackSize
|
|||
eMaxContentMinSizing = 0x4,
|
||||
eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
|
||||
eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
|
||||
eIndefinitePercentMinSizing = 0x8,
|
||||
// 0x8 is unused, feel free to take it!
|
||||
eAutoMaxSizing = 0x10,
|
||||
eMinContentMaxSizing = 0x20,
|
||||
eMaxContentMaxSizing = 0x40,
|
||||
|
@ -269,7 +269,6 @@ nsGridContainerFrame::TrackSize::Initialize(nscoord aPercentageBasis,
|
|||
// "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;
|
||||
|
@ -1162,10 +1161,8 @@ struct nsGridContainerFrame::TrackSizingFunctions
|
|||
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
|
||||
|
@ -1179,9 +1176,7 @@ struct nsGridContainerFrame::TrackSizingFunctions
|
|||
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.
|
||||
|
@ -1190,18 +1185,17 @@ struct nsGridContainerFrame::TrackSizingFunctions
|
|||
}
|
||||
}
|
||||
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;
|
||||
|
@ -1214,7 +1208,7 @@ struct nsGridContainerFrame::TrackSizingFunctions
|
|||
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.
|
||||
|
@ -2587,14 +2581,8 @@ nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4958,31 +4946,21 @@ nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
|
|||
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;
|
||||
}
|
||||
size += mSizes[i].mBase;
|
||||
}
|
||||
|
||||
// Add grid-gap contributions to 'size' and 'percent'.
|
||||
// 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();
|
||||
}
|
||||
|
@ -6118,6 +6096,12 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
} 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;
|
||||
|
@ -6738,11 +6722,8 @@ nsGridContainerFrame::SynthesizeBaseline(
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -15,14 +15,6 @@
|
|||
#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)
|
||||
|
@ -111,11 +103,31 @@ public:
|
|||
|
||||
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
|
||||
|
|
|
@ -417,6 +417,24 @@ enum nsBidiDirection {
|
|||
};
|
||||
|
||||
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:
|
||||
|
@ -499,6 +517,8 @@ static void ReleaseValue(T* aPropertyValue)
|
|||
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;
|
||||
|
@ -1199,11 +1219,93 @@ public:
|
|||
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,
|
||||
|
|
|
@ -96,4 +96,68 @@ 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
|
||||
|
|
|
@ -475,6 +475,14 @@ public:
|
|||
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
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -84,7 +84,7 @@ x { display:block; height:20px; }
|
|||
<!-- 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>
|
||||
|
|
|
@ -16,128 +16,60 @@ html,body {
|
|||
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="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="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="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>
|
||||
|
|
|
@ -17,94 +17,57 @@ html,body {
|
|||
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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -83,7 +83,7 @@ fill,fit {
|
|||
|
||||
.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%);
|
||||
|
|
|
@ -14,9 +14,9 @@ body,html { color:black; background:white; font-family:monospace; font:1px/1 mon
|
|||
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;
|
||||
|
@ -48,22 +48,18 @@ x {
|
|||
}
|
||||
.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;
|
||||
|
|
|
@ -137,6 +137,7 @@ skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baseline
|
|||
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
|
||||
|
@ -270,4 +271,3 @@ asserts(1-10) == grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.htm
|
|||
== 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
|
||||
|
|
|
@ -23,24 +23,29 @@
|
|||
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
|
||||
|
@ -54,5 +59,8 @@
|
|||
<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>
|
||||
|
|
|
@ -30,19 +30,24 @@
|
|||
.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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -17,7 +17,7 @@ fails == flexbox-align-self-baseline-horiz-2.xhtml flexbox-align-self-baseline-
|
|||
# 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.
|
||||
|
@ -59,6 +59,7 @@ fuzzy-if(skiaContent,3,10) == flexbox-dyn-insertAroundSpan-3.xhtml flexbox-dyn-i
|
|||
# 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
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
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>
|
||||
|
@ -21,13 +19,6 @@
|
|||
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.) */
|
||||
|
@ -36,13 +27,11 @@
|
|||
</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>
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
<!-- 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>
|
||||
|
@ -41,5 +42,6 @@
|
|||
<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>
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
<!-- 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>
|
||||
|
@ -42,5 +43,6 @@
|
|||
<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>
|
||||
|
|
|
@ -653,8 +653,12 @@ Gecko_SetMozBinding(nsStyleDisplay* aDisplay,
|
|||
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);
|
||||
|
|
|
@ -3805,35 +3805,54 @@ nsTableFrame::GetRowSpacing(int32_t aStartRowIndex,
|
|||
}
|
||||
|
||||
/* virtual */ nscoord
|
||||
nsTableFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
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
|
||||
{
|
||||
nscoord ascent = 0;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!firstRow)
|
||||
ascent = BSize(aWritingMode);
|
||||
return ascent;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----- global methods ----- */
|
||||
|
||||
nsTableFrame*
|
||||
|
|
|
@ -465,6 +465,10 @@ private:
|
|||
|
||||
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
|
||||
|
|
|
@ -507,7 +507,19 @@ 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;
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@ public:
|
|||
virtual nsIAtom* GetType() const override;
|
||||
|
||||
nsTableRowFrame* GetFirstRow();
|
||||
nsTableRowFrame* GetLastRow();
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override;
|
||||
|
|
|
@ -68,6 +68,25 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ public class SearchEnginePreference extends CustomListPreference {
|
|||
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";
|
||||
}
|
||||
|
||||
|
|
|
@ -105,10 +105,15 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore-
|
|||
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)
|
||||
|
|
|
@ -5,6 +5,7 @@ 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);
|
||||
|
|
|
@ -16,8 +16,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NS_ASSERT", "resource://gre/modules/deb
|
|||
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
|
||||
|
@ -59,11 +60,6 @@ this.SearchSuggestionController.prototype = {
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -192,8 +188,7 @@ this.SearchSuggestionController.prototype = {
|
|||
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) {
|
||||
|
@ -396,3 +391,9 @@ this.SearchSuggestionController.prototype = {
|
|||
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);
|
||||
|
|
|
@ -32,6 +32,16 @@ function checkLoads() {
|
|||
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();
|
||||
}
|
||||
|
@ -41,6 +51,9 @@ function checkLoads() {
|
|||
<!-- 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>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ support-files =
|
|||
bad.css^headers^
|
||||
gethash.sjs
|
||||
gethashFrame.html
|
||||
tracker.js
|
||||
|
||||
[test_classifier.html]
|
||||
skip-if = (os == 'linux' && debug) #Bug 1199778
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
function getTrackerTimeout() {
|
||||
return window.setTimeout(function(){}, 0);
|
||||
}
|
|
@ -68,5 +68,8 @@ add_task(function* () {
|
|||
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);
|
||||
});
|
||||
|
|
|
@ -111,7 +111,9 @@ class WidgetRenderingContext;
|
|||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
#endif
|
||||
mozView, NSTextInputClient>
|
||||
mozView, NSTextInputClient,
|
||||
NSDraggingSource, NSDraggingDestination,
|
||||
NSPasteboardItemDataProvider>
|
||||
{
|
||||
@private
|
||||
// the nsChildView that created the view. It retains this NSView, so
|
||||
|
|
|
@ -3187,8 +3187,14 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
// 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];
|
||||
|
||||
|
@ -3201,16 +3207,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
|
||||
+ (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:
|
||||
|
@ -5722,6 +5729,7 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
|
||||
}
|
||||
|
||||
// NSDraggingDestination
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
@ -5742,13 +5750,14 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
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"));
|
||||
|
@ -5758,6 +5767,7 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
NS_IF_RELEASE(mDragService);
|
||||
}
|
||||
|
||||
// NSDraggingDestination
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
@ -5767,32 +5777,29 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
}
|
||||
|
||||
// NSDraggingSource
|
||||
- (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint
|
||||
// This is just implemented so we comply with the NSDraggingSource protocol.
|
||||
- (NSDragOperation)draggingSession:(NSDraggingSession*)session
|
||||
sourceOperationMaskForDraggingContext:(NSDraggingContext)context
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
|
||||
|
@ -5808,15 +5815,15 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
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)
|
||||
|
@ -5836,10 +5843,45 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
}
|
||||
|
||||
// NSDraggingSource
|
||||
// this is just implemented so we comply with the NSDraggingSource informal protocol
|
||||
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
|
||||
- (void)draggingSession:(NSDraggingSession*)aSession
|
||||
movedToPoint:(NSPoint)aPoint
|
||||
{
|
||||
return UINT_MAX;
|
||||
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
|
||||
|
@ -5903,6 +5945,68 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
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
|
||||
|
@ -5925,7 +6029,9 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
// 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;
|
||||
|
||||
|
@ -5977,8 +6083,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
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.
|
||||
|
@ -6005,16 +6111,18 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
|||
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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ 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;
|
||||
|
||||
|
@ -103,7 +105,8 @@ nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard)
|
|||
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];
|
||||
|
@ -114,15 +117,15 @@ nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard)
|
|||
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 {
|
||||
|
@ -172,7 +175,7 @@ nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteb
|
|||
continue;
|
||||
|
||||
NSData* stringData;
|
||||
if ([pboardType isEqualToString:NSRTFPboardType]) {
|
||||
if ([pboardType isEqualToString:NSPasteboardTypeRTF]) {
|
||||
stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
|
||||
} else {
|
||||
stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
|
||||
|
@ -259,8 +262,10 @@ nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteb
|
|||
// 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,
|
||||
|
@ -555,7 +560,8 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable)
|
|||
continue;
|
||||
}
|
||||
|
||||
[pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType];
|
||||
[pasteboardOutputDict setObject:(NSMutableData*)tiffData
|
||||
forKey:NSPasteboardTypeTIFF];
|
||||
if (tiffData)
|
||||
CFRelease(tiffData);
|
||||
}
|
||||
|
@ -592,7 +598,8 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable)
|
|||
[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;
|
||||
|
@ -645,13 +652,13 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable)
|
|||
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;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define nsDragService_h_
|
||||
|
||||
#include "nsBaseDragService.h"
|
||||
#include "nsChildView.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
|
@ -48,7 +49,7 @@ private:
|
|||
NSString* GetFilePath(NSPasteboardItem* item);
|
||||
|
||||
nsCOMPtr<nsIArray> mDataItems; // only valid for a drag started within gecko
|
||||
NSView* mNativeDragView;
|
||||
ChildView* mNativeDragView;
|
||||
NSEvent* mNativeDragEvent;
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ extern PRLogModuleInfo* sCocoaLog;
|
|||
extern void EnsureLogInitialized();
|
||||
|
||||
extern NSPasteboard* globalDragPboard;
|
||||
extern NSView* gLastDragView;
|
||||
extern ChildView* gLastDragView;
|
||||
extern NSEvent* gLastDragMouseDownEvent;
|
||||
extern bool gUserCancelledDrag;
|
||||
|
||||
|
@ -46,11 +46,14 @@ extern bool gUserCancelledDrag;
|
|||
// 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()
|
||||
|
@ -65,65 +68,6 @@ 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,
|
||||
|
@ -303,9 +247,49 @@ nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray,
|
|||
|
||||
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);
|
||||
|
||||
|
@ -330,36 +314,28 @@ nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray,
|
|||
[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;
|
||||
NSRect localDragRect = image.alignmentRect;
|
||||
localDragRect.origin.x = localPoint.x;
|
||||
localDragRect.origin.y = localPoint.y - localDragRect.size.height;
|
||||
|
||||
nsBaseDragService::StartDragSession();
|
||||
nsBaseDragService::OpenDragPopup();
|
||||
NSDraggingItem* dragItem =
|
||||
[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem];
|
||||
[pbItem release];
|
||||
[dragItem setDraggingFrame:localDragRect contents:image];
|
||||
|
||||
// We need to retain the view and the event during the drag in case either gets destroyed.
|
||||
mNativeDragView = [gLastDragView retain];
|
||||
mNativeDragEvent = [gLastDragMouseDownEvent retain];
|
||||
NSDraggingSession* draggingSession =
|
||||
[mNativeDragView beginDraggingSessionWithItems:
|
||||
[NSArray arrayWithObject:[dragItem autorelease]]
|
||||
event:mNativeDragEvent
|
||||
source:mNativeDragView];
|
||||
draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
|
||||
|
||||
gUserCancelledDrag = false;
|
||||
[mNativeDragView dragImage:image
|
||||
at:localPoint
|
||||
offset:NSZeroSize
|
||||
event:mNativeDragEvent
|
||||
pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
|
||||
source:mNativeDragView
|
||||
slideBack:YES];
|
||||
gUserCancelledDrag = false;
|
||||
|
||||
if (mDoingDrag)
|
||||
nsBaseDragService::EndDragSession(false);
|
||||
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
|
Загрузка…
Ссылка в новой задаче