MozReview-Commit-ID: EI1fU13SR79
This commit is contained in:
Phil Ringnalda 2016-12-20 20:15:20 -08:00
Родитель 89abd57984 f7070d6e85
Коммит 4f5f9f3222
94 изменённых файлов: 1836 добавлений и 949 удалений

Просмотреть файл

@ -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',

Двоичные данные
dom/media/gtest/small-shot-partial-xing.mp3 Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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">&nbsp;</div>
<div class="flexContainer" style="padding-bottom: 20px">&nbsp;</div>
<div class="flexContainer" style="padding: 10px">&nbsp;</div>
<div class="flexContainer" style="border-width: 3px">&nbsp;</div>
<div class="flexContainer" style="border-bottom-width: 4px">&nbsp;</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;