Merge mozilla-central to inbound

This commit is contained in:
Carsten "Tomcat" Book 2017-07-11 13:00:28 +02:00
Родитель 93c690a082 31311070d9
Коммит 7e0d52f50e
194 изменённых файлов: 7980 добавлений и 5408 удалений

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

@ -10,14 +10,12 @@ jobs:
target-tasks-method: nightly_linux
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when:
by-project:
# Match buildbot starts for now
date: [{hour: 15, minute: 0}]
mozilla-central: [{hour: 10, minute: 0}]
mozilla-aurora: [] # bug 1358976
# No default
- name: nightly-desktop-osx
@ -51,14 +49,12 @@ jobs:
target-tasks-method: nightly_fennec
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when:
by-project:
# Match buildbot starts for now
date: [{hour: 15, minute: 0}]
mozilla-central: [{hour: 10, minute: 0}]
mozilla-aurora: [] # bug 1358976
# No default
- name: nightly-mochitest-valgrind
@ -71,3 +67,15 @@ jobs:
when:
- {hour: 16, minute: 0}
- {hour: 4, minute: 0}
- name: nightly-dmd
job:
type: decision-task
treeherder-symbol: Ndmd
target-tasks-method: nightly_dmd
run-on-projects:
- mozilla-central
when:
by-project:
mozilla-central: [{hour: 10, minute: 0}]
# No default

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

@ -1704,6 +1704,8 @@ pref("browser.onboarding.hidden", false);
// So use `browser.onboarding.notification.finished` to let the AS page know
// if our notification is finished and safe to show their snippet.
pref("browser.onboarding.notification.finished", false);
pref("browser.onboarding.newtour", "private,addons,customize,search,default,sync");
pref("browser.onboarding.updatetour", "");
// Preferences for the Screenshots feature:
// Temporarily disable Screenshots in Beta & Release, so that we can gradually

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

@ -23,6 +23,14 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref("browser.rights." + gRightsVersion + ".shown");
});
add_task(async function() {
// The onboarding tour's notification would affect tests
// and it isn't out test target so make sure disabling it
// before any tests start.
await promiseDisableOnboardingTours();
is(false, Services.prefs.getBoolPref("browser.onboarding.enabled"), "Should disable the onboarding tours");
});
add_task(async function() {
info("Check that clearing cookies does not clear storage");

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

@ -829,3 +829,7 @@ function getCertExceptionDialog(aLocation) {
}
return undefined;
}
function promiseDisableOnboardingTours() {
return SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", false]]});
}

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

@ -64,7 +64,7 @@ function promiseTourNotificationOpened(browser) {
return ContentTask.spawn(browser, {}, function() {
return new Promise(resolve => {
let bar = content.document.querySelector("#onboarding-notification-bar");
if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
if (bar && bar.classList.contains("onboarding-opened")) {
resolve(true);
return;
}

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

@ -130,8 +130,6 @@ var whitelist = [
{file: "chrome://global/content/findUtils.js"},
// Bug 1343843
{file: "chrome://global/content/url-classifier/unittests.xul"},
// Bug 1343839
{file: "chrome://global/locale/headsUpDisplay.properties"},
// Bug 1348362
{file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux", "win"]},
// Bug 1348525

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

@ -0,0 +1,3 @@
ac_add_options --enable-dmd
. "$topsrcdir/browser/config/mozconfigs/linux32/nightly"

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

@ -0,0 +1,3 @@
ac_add_options --enable-dmd
. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"

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

@ -0,0 +1,3 @@
ac_add_options --enable-dmd
. "$topsrcdir/browser/config/mozconfigs/macosx64/nightly"

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

@ -0,0 +1,3 @@
ac_add_options --enable-dmd
. "$topsrcdir/browser/config/mozconfigs/win32/nightly"

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

@ -0,0 +1,3 @@
ac_add_options --enable-dmd
. "$topsrcdir/browser/config/mozconfigs/win64/nightly"

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

@ -14,6 +14,10 @@ We would apply some rules:
* All styles and ids should be formated as `onboarding-*` to avoid conflict with the origin page.
* All strings in `locales` should be formated as `onboarding.*` for consistency.
## How to change the order of tours
Edit `browser/app/profile/firefox.js` and modify `browser.onboarding.newtour` for the new user tour or `browser.onboarding.updatetour` for the update user tour. You can change the tour list and the order by concate `tourIds` with `,` sign. You can find available `tourId` from `onboardingTourset` in `onboarding.js`.
## How to pump tour set version after update tours
The tourset version is used to track the last major tourset change version. The `tourset-version` pref store the major tourset version (ex: `1`) but not the current browser version. When browser update to the next version (ex: 58, 59) the tourset pref is still `1` if we didn't do any major tourset update.

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

@ -353,6 +353,7 @@
}
#onboarding-notification-bar.onboarding-opened {
transition: none;
transform: translateY(0px);
}
@ -373,6 +374,7 @@
#onboarding-notification-icon::after {
--height: 22px;
--vpadding: 3px;
content: attr(data-tooltip);
background: #5ce6e6;
position: absolute;
@ -380,11 +382,11 @@
offset-inline-start: 68px;
color: #10404a;
font-size: 12px;
min-height: var(--height);
line-height: var(--height);
line-height: calc(var(--height) - var(--vpadding) * 2);
max-width: 90px;
border-radius: calc(var(--height) / 2);
border: 1px solid #fff;
padding: 0 10px;
padding: var(--vpadding) 10px;
text-align: center;
}
@ -427,7 +429,7 @@
}
#onboarding-notification-tour-icon {
width: 64px;
min-width: 64px;
height: 64px;
background-size: 64px;
background-repeat: no-repeat;

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

@ -22,7 +22,7 @@ const BRAND_SHORT_NAME = Services.strings
/**
* Add any number of tours, following the format
* {
* "tourId": { // The short tour id which could be saved in pref
* // The unique tour id
* id: "onboarding-tour-addons",
* // The string id of tour name which would be displayed on the navigation bar
@ -40,8 +40,8 @@ const BRAND_SHORT_NAME = Services.strings
* getPage() {},
* },
**/
var onboardingTours = [
{
var onboardingTourset = {
"private": {
id: "onboarding-tour-private-browsing",
tourNameId: "onboarding.tour-private-browsing",
getNotificationStrings(bundle) {
@ -68,7 +68,7 @@ var onboardingTours = [
return div;
},
},
{
"addons": {
id: "onboarding-tour-addons",
tourNameId: "onboarding.tour-addons",
getNotificationStrings(bundle) {
@ -95,7 +95,7 @@ var onboardingTours = [
return div;
},
},
{
"customize": {
id: "onboarding-tour-customize",
tourNameId: "onboarding.tour-customize",
getNotificationStrings(bundle) {
@ -122,7 +122,7 @@ var onboardingTours = [
return div;
},
},
{
"search": {
id: "onboarding-tour-search",
tourNameId: "onboarding.tour-search2",
getNotificationStrings(bundle) {
@ -149,7 +149,7 @@ var onboardingTours = [
return div;
},
},
{
"default": {
id: "onboarding-tour-default-browser",
tourNameId: "onboarding.tour-default-browser",
getNotificationStrings(bundle) {
@ -179,7 +179,7 @@ var onboardingTours = [
return div;
},
},
{
"sync": {
id: "onboarding-tour-sync",
tourNameId: "onboarding.tour-sync2",
getNotificationStrings(bundle) {
@ -212,7 +212,7 @@ var onboardingTours = [
return div;
},
},
];
};
/**
* The script won't be initialized if we turned off onboarding by
@ -227,9 +227,16 @@ class Onboarding {
this._window = contentWindow;
this._tourItems = [];
this._tourPages = [];
this._tours = [];
// we only support the new user tour at this moment
if (Services.prefs.getStringPref("browser.onboarding.tour-type", "update") !== "new") {
let tourIds = this._getTourIDList(Services.prefs.getStringPref("browser.onboarding.tour-type", "update"));
tourIds.forEach(tourId => {
if (onboardingTourset[tourId]) {
this._tours.push(onboardingTourset[tourId]);
}
});
if (this._tours.length === 0) {
return;
}
@ -256,6 +263,11 @@ class Onboarding {
this._initNotification();
}
_getTourIDList(tourType) {
let tours = Services.prefs.getStringPref(`browser.onboarding.${tourType}tour`, "");
return tours.split(",").filter(tourId => tourId !== "").map(tourId => tourId.trim());
}
_initNotification() {
let doc = this._window.document;
if (doc.hidden) {
@ -285,7 +297,7 @@ class Onboarding {
this.destroy();
}
});
onboardingTours.forEach(tour => {
this._tours.forEach(tour => {
let tourId = tour.id;
this._prefsObserved.set(`browser.onboarding.tour.${tourId}.completed`, () => {
this.markTourCompletionState(tourId);
@ -355,7 +367,7 @@ class Onboarding {
toggleOverlay() {
if (this._tourItems.length == 0) {
// Lazy loading until first toggle.
this._loadTours(onboardingTours);
this._loadTours(this._tours);
}
this.hideNotification();
@ -418,16 +430,16 @@ class Onboarding {
// Take the last tour as the default last prompted
// so below would start from the 1st one if found no the last prompted from the pref.
let lastPromptedId = onboardingTours[onboardingTours.length - 1].id;
let lastPromptedId = this._tours[this._tours.length - 1].id;
lastPromptedId = Preferences.get("browser.onboarding.notification.lastPrompted", lastPromptedId);
let lastTourIndex = onboardingTours.findIndex(tour => tour.id == lastPromptedId);
let lastTourIndex = this._tours.findIndex(tour => tour.id == lastPromptedId);
if (lastTourIndex < 0) {
// Couldn't find the tour.
// This could be because the pref was manually modified into unknown value
// or the tour version has been updated so have an new tours set.
// Take the last tour as the last prompted so would start from the 1st one below.
lastTourIndex = onboardingTours.length - 1;
lastTourIndex = this._tours.length - 1;
}
// Form tours to notify into the order we want.
@ -435,7 +447,7 @@ class Onboarding {
// This would form [#4, #5, #0, #1, #2, #3].
// So the 1st met incomplete tour in #4 ~ #2 would be the one to show.
// Or #3 would be the one to show if #4 ~ #2 are all completed.
let toursToNotify = [ ...onboardingTours.slice(lastTourIndex + 1), ...onboardingTours.slice(0, lastTourIndex + 1) ];
let toursToNotify = [ ...this._tours.slice(lastTourIndex + 1), ...this._tours.slice(0, lastTourIndex + 1) ];
targetTour = toursToNotify.find(tour => !this.isTourCompleted(tour.id));
@ -460,16 +472,7 @@ class Onboarding {
tourTitle.textContent = notificationStrings.title;
let tourMessage = this._notificationBar.querySelector("#onboarding-notification-tour-message");
tourMessage.textContent = notificationStrings.message;
this._notificationBar.addEventListener("transitionend", () => {
this._notificationBar.dataset.cssTransition = "end";
}, { once: true });
this._window.requestAnimationFrame(() => {
// Request the 2nd animation frame.
// This is to make sure the appending operation above and the css operation happen
// in the different layout tick so as to make sure the transition happens.
this._window.requestAnimationFrame(() => this._notificationBar.classList.add("onboarding-opened"));
});
this._notificationBar.classList.add("onboarding-opened");
this.sendMessageToChrome("set-prefs", [{
name: "browser.onboarding.notification.lastPrompted",
@ -480,7 +483,6 @@ class Onboarding {
hideNotification() {
if (this._notificationBar) {
this._notificationBar.classList.remove("onboarding-opened");
delete this._notificationBar.dataset.cssTransition;
}
}
@ -507,7 +509,7 @@ class Onboarding {
}
hide() {
this.setToursCompleted(onboardingTours.map(tour => tour.id));
this.setToursCompleted(this._tours.map(tour => tour.id));
this.sendMessageToChrome("set-prefs", [
{
name: "browser.onboarding.hidden",

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

@ -4,3 +4,4 @@ support-files =
[browser_onboarding_notification.js]
[browser_onboarding_tours.js]
[browser_onboarding_tourset.js]

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

@ -5,7 +5,6 @@
add_task(async function test_show_tour_notifications_in_order() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tourIds = TOUR_IDs;
let tab = null;
@ -43,7 +42,6 @@ add_task(async function test_show_tour_notifications_in_order() {
add_task(async function test_open_target_tour_from_notification() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
@ -61,7 +59,6 @@ add_task(async function test_open_target_tour_from_notification() {
add_task(async function test_not_show_notification_for_completed_tour() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tourIds = TOUR_IDs;
// Make only the last tour uncompleted

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

@ -29,11 +29,10 @@ function assertTourCompletedStyle(tourId, expectComplete, browser) {
add_task(async function test_hide_onboarding_tours() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tourIds = TOUR_IDs;
let tabs = [];
for (let url of [ABOUT_NEWTAB_URL, ABOUT_HOME_URL]) {
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
@ -61,11 +60,10 @@ add_task(async function test_hide_onboarding_tours() {
add_task(async function test_click_action_button_to_set_tour_completed() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tourIds = TOUR_IDs;
let tabs = [];
for (let url of [ABOUT_NEWTAB_URL, ABOUT_HOME_URL]) {
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
@ -91,7 +89,6 @@ add_task(async function test_click_action_button_to_set_tour_completed() {
add_task(async function test_set_right_tour_completed_style_on_overlay() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
let tourIds = TOUR_IDs;
// Make the tours of even number as completed
@ -100,7 +97,7 @@ add_task(async function test_set_right_tour_completed_style_on_overlay() {
}
let tabs = [];
for (let url of [ABOUT_NEWTAB_URL, ABOUT_HOME_URL]) {
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);

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

@ -0,0 +1,104 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_onboarding_default_new_tourset() {
resetOnboardingDefaultState();
let tabs = [];
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
tabs.push(tab);
}
let doc = content && content.document;
let doms = doc.querySelectorAll(".onboarding-tour-item");
is(doms.length, TOUR_IDs.length, "has exact tour numbers");
doms.forEach((dom, idx) => {
is(TOUR_IDs[idx], dom.id, "contain defined onboarding id");
});
for (let i = tabs.length - 1; i >= 0; --i) {
let tab = tabs[i];
await BrowserTestUtils.removeTab(tab);
}
});
add_task(async function test_onboarding_custom_new_tourset() {
const CUSTOM_NEW_TOURs = [
"onboarding-tour-private-browsing",
"onboarding-tour-addons",
"onboarding-tour-customize",
];
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "new"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.newtour", "private,addons,customize"],
]});
let tabs = [];
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
tabs.push(tab);
}
let doc = content && content.document;
let doms = doc.querySelectorAll(".onboarding-tour-item");
is(doms.length, CUSTOM_NEW_TOURs.length, "has exact tour numbers");
doms.forEach((dom, idx) => {
is(CUSTOM_NEW_TOURs[idx], dom.id, "contain defined onboarding id");
});
for (let i = tabs.length - 1; i >= 0; --i) {
let tab = tabs[i];
await BrowserTestUtils.removeTab(tab);
}
});
add_task(async function test_onboarding_custom_update_tourset() {
const CUSTOM_UPDATE_TOURs = [
"onboarding-tour-customize",
"onboarding-tour-private-browsing",
"onboarding-tour-addons",
];
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "update"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.updatetour", "customize,private,addons"],
]});
let tabs = [];
for (let url of URLs) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
tabs.push(tab);
}
let doc = content && content.document;
let doms = doc.querySelectorAll(".onboarding-tour-item");
is(doms.length, CUSTOM_UPDATE_TOURs.length, "has exact tour numbers");
doms.forEach((dom, idx) => {
is(CUSTOM_UPDATE_TOURs[idx], dom.id, "contain defined onboarding id");
});
for (let i = tabs.length - 1; i >= 0; --i) {
let tab = tabs[i];
await BrowserTestUtils.removeTab(tab);
}
});

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

@ -5,6 +5,7 @@ let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
const ABOUT_HOME_URL = "about:home";
const ABOUT_NEWTAB_URL = "about:newtab";
const URLs = [ABOUT_HOME_URL, ABOUT_NEWTAB_URL];
const TOUR_IDs = [
"onboarding-tour-private-browsing",
"onboarding-tour-addons",
@ -13,10 +14,12 @@ const TOUR_IDs = [
"onboarding-tour-default-browser",
"onboarding-tour-sync",
];
const UPDATE_TOUR_IDs = [];
function resetOnboardingDefaultState() {
// All the prefs should be reset to the default states
// and no need to revert back so we don't use `SpecialPowers.pushPrefEnv` here.
Preferences.set("browser.onboarding.enabled", true);
Preferences.set("browser.onboarding.hidden", false);
Preferences.set("browser.onboarding.notification.finished", false);
Preferences.set("browser.onboarding.notification.lastPrompted", "");
@ -87,7 +90,7 @@ function promiseTourNotificationOpened(browser) {
return ContentTask.spawn(browser, {}, function() {
return new Promise(resolve => {
let bar = content.document.querySelector("#onboarding-notification-bar");
if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
if (bar && bar.classList.contains("onboarding-opened")) {
resolve(true);
return;
}

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

@ -825,6 +825,14 @@ bin/libfreebl_32int64_3.so
@RESPATH@/components/SanityTest.js
#endif
#ifdef MOZ_MULET
#include ../../b2g/installer/package-manifest.in
#ifdef MOZ_DMD
; DMD
@RESPATH@/dmd.py
@RESPATH@/fix_stack_using_bpsyms.py
#ifdef XP_MACOSX
@RESPATH@/fix_macosx_stack.py
#endif
#ifdef XP_LINUX
@RESPATH@/fix_linux_stack.py
#endif
#endif

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

@ -567,7 +567,8 @@ KeyframeUtils::ParseProperty(nsCSSPropertyID aProperty,
&value,
data,
ParsingMode::Default,
aDocument->GetCompatibilityMode()).Consume();
aDocument->GetCompatibilityMode(),
aDocument->CSSLoader()).Consume();
}
// ------------------------------------------------------------------

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

@ -1757,7 +1757,8 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString,
RefPtr<URLExtraData> data = new URLExtraData(baseURI, docURI,
aElement->NodePrincipal());
decl = ServoDeclarationBlock::FromCssText(aString, data,
ownerDoc->GetCompatibilityMode());
ownerDoc->GetCompatibilityMode(),
ownerDoc->CSSLoader());
} else {
css::Loader* cssLoader = ownerDoc->CSSLoader();
nsCSSParser cssParser(cssLoader);

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

@ -2812,7 +2812,8 @@ CreateDeclarationForServo(nsCSSPropertyID aProperty,
&value,
data,
ParsingMode::Default,
aDocument->GetCompatibilityMode()).Consume();
aDocument->GetCompatibilityMode(),
aDocument->CSSLoader()).Consume();
if (!servoDeclarations) {
// We got a syntax error. The spec says this value must be ignored.
@ -2829,7 +2830,8 @@ CreateDeclarationForServo(nsCSSPropertyID aProperty,
false,
data,
ParsingMode::Default,
aDocument->GetCompatibilityMode());
aDocument->GetCompatibilityMode(),
aDocument->CSSLoader());
}
return servoDeclarations.forget();

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

@ -1326,6 +1326,7 @@ ContentChild::RecvReinitRendering(Endpoint<PCompositorManagerChild>&& aComposito
if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) {
return IPC_FAIL_NO_REASON(this);
}
gfxPlatform::GetPlatform()->CompositorUpdated();
// Establish new PLayerTransactions.
for (const auto& tabChild : tabs) {

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

@ -46,11 +46,6 @@ public:
// Can be called on any thread.
virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0;
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull()
{
return nullptr;
};
// Returns an event that will be notified when the owning document changes state
// and we might have a new compositor. If this new compositor requires us to
// recreate our decoders, then we expect the existing decoderis to return an

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

@ -5,6 +5,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileBlockCache.h"
#include "MediaCache.h"
#include "MediaPrefs.h"
#include "mozilla/SharedThreadPool.h"
#include "VideoUtils.h"
#include "prio.h"
@ -121,6 +123,33 @@ FileBlockCache::Init()
return rv;
}
int32_t
FileBlockCache::GetMaxBlocks() const
{
// We look up the cache size every time. This means dynamic changes
// to the pref are applied.
const uint32_t cacheSizeKb =
std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
// Ensure we can divide BLOCK_SIZE by 1024.
static_assert(MediaCacheStream::BLOCK_SIZE % 1024 == 0,
"BLOCK_SIZE should be a multiple of 1024");
// Ensure BLOCK_SIZE/1024 is at least 2.
static_assert(MediaCacheStream::BLOCK_SIZE / 1024 >= 2,
"BLOCK_SIZE / 1024 should be at least 2");
// Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
static_assert(MediaCacheStream::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
"BLOCK_SIZE / 1024 should be at most UINT32_MAX");
// Since BLOCK_SIZE is a strict multiple of 1024,
// cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
// but the latter formula avoids a potential overflow from `* 1024`.
// And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
// INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
constexpr uint32_t blockSizeKb =
uint32_t(MediaCacheStream::BLOCK_SIZE / 1024);
const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
return std::max(maxBlocks, int32_t(1));
}
FileBlockCache::FileBlockCache()
: mFileMutex("MediaCache.Writer.IO.Mutex")
, mFD(nullptr)

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

@ -65,6 +65,10 @@ public:
// If re-initializing, just discard pending writes if any.
nsresult Init() override;
// Maximum number of blocks allowed in this block cache.
// Calculated from "media.cache_size" pref.
int32_t GetMaxBlocks() const override;
// Can be called on any thread. This defers to a non-main thread.
nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,

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

@ -52,6 +52,10 @@ public:
// If called again, re-initialize cache with minimal chance of failure.
virtual nsresult Init() = 0;
// Maximum number of blocks expected in this block cache. (But allow overflow
// to accomodate incoming traffic before MediaCache can handle it.)
virtual int32_t GetMaxBlocks() const = 0;
// Can be called on any thread. This defers to a non-main thread.
virtual nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,

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

@ -8,6 +8,7 @@
#include "FileBlockCache.h"
#include "MediaBlockCacheBase.h"
#include "MediaPrefs.h"
#include "MediaResource.h"
#include "MemoryBlockCache.h"
#include "mozilla/Attributes.h"
@ -737,31 +738,6 @@ MediaCache::ReadCacheFile(
}
}
static int32_t GetMaxBlocks()
{
// We look up the cache size every time. This means dynamic changes
// to the pref are applied.
const uint32_t cacheSizeKb =
std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
// Ensure we can divide BLOCK_SIZE by 1024.
static_assert(MediaCache::BLOCK_SIZE % 1024 == 0,
"BLOCK_SIZE should be a multiple of 1024");
// Ensure BLOCK_SIZE/1024 is at least 2.
static_assert(MediaCache::BLOCK_SIZE / 1024 >= 2,
"BLOCK_SIZE / 1024 should be at least 2");
// Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
static_assert(MediaCache::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
"BLOCK_SIZE / 1024 should be at most UINT32_MAX");
// Since BLOCK_SIZE is a strict multiple of 1024,
// cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
// but the latter formula avoids a potential overflow from `* 1024`.
// And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
// INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
constexpr uint32_t blockSizeKb = uint32_t(MediaCache::BLOCK_SIZE / 1024);
const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
return std::max(maxBlocks, int32_t(1));
}
// Allowed range is whatever can be accessed with an int32_t block index.
static bool
IsOffsetAllowed(int64_t aOffset)
@ -818,8 +794,10 @@ MediaCache::FindBlockForIncomingData(TimeStamp aNow,
// b) the data we're going to store in the free block is not higher
// priority than the data already stored in the free block.
// The latter can lead us to go over the cache limit a bit.
if ((mIndex.Length() < uint32_t(GetMaxBlocks()) || blockIndex < 0 ||
PredictNextUseForIncomingData(aStream) >= PredictNextUse(aNow, blockIndex))) {
if ((mIndex.Length() < uint32_t(mBlockCache->GetMaxBlocks()) ||
blockIndex < 0 ||
PredictNextUseForIncomingData(aStream) >=
PredictNextUse(aNow, blockIndex))) {
blockIndex = mIndex.Length();
if (!mIndex.AppendElement())
return -1;
@ -1163,7 +1141,7 @@ MediaCache::Update()
mInUpdate = true;
#endif
int32_t maxBlocks = GetMaxBlocks();
int32_t maxBlocks = mBlockCache->GetMaxBlocks();
TimeStamp now = TimeStamp::Now();
int32_t freeBlockCount = mFreeBlocks.GetCount();

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

@ -197,13 +197,6 @@ MediaDecoder::GetDuration()
return mDuration;
}
AbstractCanonical<media::NullableTimeUnit>*
MediaDecoder::CanonicalDurationOrNull()
{
MOZ_ASSERT(mDecoderStateMachine);
return mDecoderStateMachine->CanonicalDuration();
}
void
MediaDecoder::SetInfinite(bool aInfinite)
{

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

@ -780,7 +780,6 @@ protected:
Canonical<int64_t> mDecoderPosition;
public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
AbstractCanonical<double>* CanonicalVolume() { return &mVolume; }
AbstractCanonical<bool>* CanonicalPreservesPitch()
{

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

@ -75,9 +75,7 @@ MediaDecoderReader::MediaDecoderReader(const MediaDecoderReaderInit& aInit)
GetMediaThreadPool(MediaThreadType::PLAYBACK),
"MediaDecoderReader::mTaskQueue",
/* aSupportsTailDispatch = */ true))
, mWatchManager(this, mTaskQueue)
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
, mIgnoreAudioOutputFormat(false)
, mHitAudioDecodeError(false)
, mShutdown(false)
@ -90,28 +88,9 @@ MediaDecoderReader::MediaDecoderReader(const MediaDecoderReaderInit& aInit)
nsresult
MediaDecoderReader::Init()
{
// Dispatch initialization that needs to happen on that task queue.
mTaskQueue->Dispatch(
NewRunnableMethod("MediaDecoderReader::InitializationTask",
this,
&MediaDecoderReader::InitializationTask));
return InitInternal();
}
void
MediaDecoderReader::InitializationTask()
{
if (!mDecoder) {
return;
}
if (mDecoder->CanonicalDurationOrNull()) {
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
}
// Initialize watchers.
mWatchManager.Watch(mDuration, &MediaDecoderReader::UpdateBuffered);
}
MediaDecoderReader::~MediaDecoderReader()
{
MOZ_ASSERT(mShutdown);
@ -142,6 +121,14 @@ size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
return mAudioQueue.GetSize();
}
void
MediaDecoderReader::UpdateDuration(const media::TimeUnit& aDuration)
{
MOZ_ASSERT(OnTaskQueue());
mDuration = Some(aDuration);
UpdateBuffered();
}
nsresult MediaDecoderReader::ResetDecode(TrackSet aTracks)
{
if (aTracks.contains(TrackInfo::kVideoTrack)) {
@ -203,13 +190,12 @@ MediaDecoderReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
AutoPinned<MediaResource> stream(mResource);
if (!mDuration.Ref().isSome()) {
if (mDuration.isNothing()) {
return TimeIntervals();
}
return GetEstimatedBufferedTimeRanges(stream, mDuration.Ref().ref().ToMicroseconds());
AutoPinned<MediaResource> stream(mResource);
return GetEstimatedBufferedTimeRanges(stream, mDuration->ToMicroseconds());
}
RefPtr<MediaDecoderReader::MetadataPromise>
@ -371,12 +357,8 @@ MediaDecoderReader::Shutdown()
mBaseVideoPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
ReleaseResources();
mDuration.DisconnectIfConnected();
mBuffered.DisconnectAll();
// Shut down the watch manager before shutting down our task queue.
mWatchManager.Shutdown();
mDecoder = nullptr;
return mTaskQueue->BeginShutdown();

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

@ -132,6 +132,8 @@ public:
return OwnerThread()->IsCurrentThreadIn();
}
void UpdateDuration(const media::TimeUnit& aDuration);
// Resets all state related to decoding, emptying all buffers etc.
// Cancels all pending Request*Data() request callbacks, rejects any
// outstanding seek promises, and flushes the decode pipeline. The
@ -301,17 +303,13 @@ protected:
// Decode task queue.
RefPtr<TaskQueue> mTaskQueue;
// State-watching manager.
WatchManager<MediaDecoderReader> mWatchManager;
// Buffered range.
Canonical<media::TimeIntervals> mBuffered;
// Stores presentation info required for playback.
MediaInfo mInfo;
// Duration, mirrored from the state machine task queue.
Mirror<media::NullableTimeUnit> mDuration;
media::NullableTimeUnit mDuration;
// Whether we should accept media that we know we can't play
// directly, because they have a number of channel higher than
@ -339,12 +337,6 @@ protected:
private:
virtual nsresult InitInternal() { return NS_OK; }
// Does any spinup that needs to happen on this task queue. This runs on a
// different thread than Init, and there should not be ordering dependencies
// between the two (even though in practice, Init will always run first right
// now thanks to the tail dispatcher).
void InitializationTask();
// Read header data for all bitstreams in the file. Fills aInfo with
// the data required to present the media, and optionally fills *aTags
// with tag metadata from the file.

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

@ -13,6 +13,10 @@ MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(AbstractThread* aOwnerThrea
MediaDecoderReader* aReader)
: mOwnerThread(aOwnerThread)
, mReader(aReader)
, mWatchManager(this, aReader->OwnerThread())
, mDuration(aReader->OwnerThread(),
NullableTimeUnit(),
"MediaDecoderReaderWrapper::mDuration (Mirror)")
{
// Must support either heuristic buffering or WaitForData().
MOZ_ASSERT(mReader->UseBufferingHeuristics() ||
@ -134,8 +138,12 @@ MediaDecoderReaderWrapper::Shutdown()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mShutdown = true;
return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
&MediaDecoderReader::Shutdown);
RefPtr<MediaDecoderReaderWrapper> self = this;
return InvokeAsync(mReader->OwnerThread(), __func__, [self]() {
self->mDuration.DisconnectIfConnected();
self->mWatchManager.Shutdown();
return self->mReader->Shutdown();
});
}
RefPtr<MediaDecoderReaderWrapper::MetadataPromise>
@ -171,4 +179,28 @@ MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
mReader->OwnerThread()->Dispatch(r.forget());
}
void
MediaDecoderReaderWrapper::UpdateDuration()
{
MOZ_ASSERT(mReader->OwnerThread()->IsCurrentThreadIn());
mReader->UpdateDuration(mDuration.Ref().ref());
}
void
MediaDecoderReaderWrapper::SetCanonicalDuration(
AbstractCanonical<media::NullableTimeUnit>* aCanonical)
{
using DurationT = AbstractCanonical<media::NullableTimeUnit>;
RefPtr<MediaDecoderReaderWrapper> self = this;
RefPtr<DurationT> canonical = aCanonical;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"MediaDecoderReaderWrapper::SetCanonicalDuration",
[this, self, canonical]() {
mDuration.Connect(canonical);
mWatchManager.Watch(mDuration,
&MediaDecoderReaderWrapper::UpdateDuration);
});
mReader->OwnerThread()->Dispatch(r.forget());
}
} // namespace mozilla

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

@ -88,16 +88,26 @@ public:
void SetVideoBlankDecode(bool aIsBlankDecode);
void SetCanonicalDuration(
AbstractCanonical<media::NullableTimeUnit>* aCanonical);
private:
~MediaDecoderReaderWrapper();
RefPtr<MetadataPromise> OnMetadataRead(MetadataHolder&& aMetadata);
RefPtr<MetadataPromise> OnMetadataNotRead(const MediaResult& aError);
void UpdateDuration();
const RefPtr<AbstractThread> mOwnerThread;
const RefPtr<MediaDecoderReader> mReader;
bool mShutdown = false;
Maybe<media::TimeUnit> mStartTime;
// State-watching manager.
WatchManager<MediaDecoderReaderWrapper> mWatchManager;
// Duration, mirrored from the state machine task queue.
Mirror<media::NullableTimeUnit> mDuration;
};
} // namespace mozilla

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

@ -2961,6 +2961,8 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder)
nsresult rv = mReader->Init();
NS_ENSURE_SUCCESS(rv, rv);
mReader->SetCanonicalDuration(&mDuration);
return NS_OK;
}

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

@ -137,9 +137,23 @@ enum MemoryBlockCacheTelemetryErrors
MoveBlockCannotGrow = 7,
};
static int32_t
CalculateMaxBlocks(int64_t aContentLength)
{
// Note: It doesn't matter if calculations overflow, Init() would later fail.
// We want at least enough blocks to contain the original content length.
const int32_t requiredBlocks =
int32_t((aContentLength - 1) / MediaBlockCacheBase::BLOCK_SIZE + 1);
// Allow at least 1s of ultra HD (25Mbps).
const int32_t workableBlocks =
25 * 1024 * 1024 / 8 / MediaBlockCacheBase::BLOCK_SIZE;
return std::max(requiredBlocks, workableBlocks);
}
MemoryBlockCache::MemoryBlockCache(int64_t aContentLength)
// Buffer whole blocks.
: mInitialContentLength((aContentLength >= 0) ? size_t(aContentLength) : 0)
, mMaxBlocks(CalculateMaxBlocks(aContentLength))
, mMutex("MemoryBlockCache")
, mHasGrown(false)
{
@ -175,31 +189,36 @@ MemoryBlockCache::EnsureBufferCanContain(size_t aContentLength)
}
// Need larger buffer. If we are allowed more memory, attempt to re-allocate.
const size_t extra = desiredLength - initialLength;
// Note: There is a small race between testing `atomic + extra > limit` and
// committing to it with `atomic += extra` below; but this is acceptable, as
// in the worst case it may allow a small number of buffers to go past the
// limit.
// The alternative would have been to reserve the space first with
// `atomic += extra` and then undo it with `atomic -= extra` in case of
// failure; but this would have meant potentially preventing other (small but
// successful) allocations.
static const size_t sysmem =
std::max<size_t>(PR_GetPhysicalMemorySize(), 32 * 1024 * 1024);
const size_t limit = std::min(
size_t(MediaPrefs::MediaMemoryCachesCombinedLimitKb()) * 1024,
sysmem * MediaPrefs::MediaMemoryCachesCombinedLimitPcSysmem() / 100);
const size_t currentSizes = static_cast<size_t>(gCombinedSizes);
if (currentSizes + extra > limit) {
LOG("EnsureBufferCanContain(%zu) - buffer size %zu, wanted + %zu = %zu;"
" combined sizes %zu + %zu > limit %zu",
aContentLength,
initialLength,
extra,
desiredLength,
currentSizes,
extra,
limit);
return false;
// Only check the very first allocation against the combined MemoryBlockCache
// limit. Further growths will always be allowed, assuming MediaCache won't
// go over GetMaxBlocks() by too much.
if (initialLength == 0) {
// Note: There is a small race between testing `atomic + extra > limit` and
// committing to it with `atomic += extra` below; but this is acceptable, as
// in the worst case it may allow a small number of buffers to go past the
// limit.
// The alternative would have been to reserve the space first with
// `atomic += extra` and then undo it with `atomic -= extra` in case of
// failure; but this would have meant potentially preventing other (small
// but successful) allocations.
static const size_t sysmem =
std::max<size_t>(PR_GetPhysicalMemorySize(), 32 * 1024 * 1024);
const size_t limit = std::min(
size_t(MediaPrefs::MediaMemoryCachesCombinedLimitKb()) * 1024,
sysmem * MediaPrefs::MediaMemoryCachesCombinedLimitPcSysmem() / 100);
const size_t currentSizes = static_cast<size_t>(gCombinedSizes);
if (currentSizes + extra > limit) {
LOG("EnsureBufferCanContain(%zu) - buffer size %zu, wanted + %zu = %zu;"
" combined sizes %zu + %zu > limit %zu",
aContentLength,
initialLength,
extra,
desiredLength,
currentSizes,
extra,
limit);
return false;
}
}
if (!mBuffer.SetLength(desiredLength, mozilla::fallible)) {
LOG("EnsureBufferCanContain(%zu) - buffer size %zu, wanted + %zu = %zu, "

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

@ -42,6 +42,10 @@ public:
// If re-initializing, clear buffer.
virtual nsresult Init() override;
// Maximum number of blocks allowed in this block cache.
// Based on initial content length, and minimum usable block cache.
int32_t GetMaxBlocks() const override { return mMaxBlocks; }
// Can be called on any thread.
virtual nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,
@ -71,6 +75,9 @@ private:
// Initial content length.
const size_t mInitialContentLength;
// Maximum number of blocks that this MemoryBlockCache expects.
const int32_t mMaxBlocks;
// Mutex which controls access to all members below.
Mutex mMutex;

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

@ -694,7 +694,8 @@ ValueFromStringHelper(nsCSSPropertyID aPropID,
data,
ParsingMode::AllowUnitlessLength |
ParsingMode::AllowAllNumericValues,
doc->GetCompatibilityMode()).Consume();
doc->GetCompatibilityMode(),
doc->CSSLoader()).Consume();
if (!servoDeclarationBlock) {
return result;
}

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

@ -1193,6 +1193,7 @@ private:
// MEMBER DATA
// -----------
nsCSSParser mParser;
css::Loader* mLoader;
// Arguments for nsCSSParser::ParseProperty
nsIURI* mDocURI;
@ -1212,7 +1213,7 @@ MappedAttrParser::MappedAttrParser(css::Loader* aLoader,
already_AddRefed<nsIURI> aBaseURI,
nsSVGElement* aElement,
StyleBackendType aBackend)
: mParser(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI),
: mParser(aLoader), mLoader(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI),
mElement(aElement), mBackend(aBackend)
{
}
@ -1253,7 +1254,7 @@ MappedAttrParser::ParseMappedAttrValue(nsIAtom* aMappedAttrName,
mElement->NodePrincipal());
changed = Servo_DeclarationBlock_SetPropertyById(
mDecl->AsServo()->Raw(), propertyID, &value, false, data,
ParsingMode::AllowUnitlessLength, mElement->OwnerDoc()->GetCompatibilityMode());
ParsingMode::AllowUnitlessLength, mElement->OwnerDoc()->GetCompatibilityMode(), mLoader);
}
if (changed) {

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

@ -215,17 +215,6 @@ Factory::Init(const Config& aConfig)
MOZ_ASSERT(!sConfig);
sConfig = new Config(aConfig);
// Make sure we don't completely break rendering because of a typo in the
// pref or whatnot.
const int32_t kMinAllocPref = 10000000;
const int32_t kMinSizePref = 2048;
if (sConfig->mMaxAllocSize < kMinAllocPref) {
sConfig->mMaxAllocSize = kMinAllocPref;
}
if (sConfig->mMaxTextureSize < kMinSizePref) {
sConfig->mMaxTextureSize = kMinSizePref;
}
#ifdef MOZ_ENABLE_FREETYPE
mFTLock = new Mutex("Factory::mFTLock");
#endif

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

@ -431,6 +431,17 @@ GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost)
DestroyProcess();
DisableGPUProcess("GPU processed experienced too many device resets");
// Reaches the limited TDR attempts, fallback to software solution.
gfxConfig::SetFailed(Feature::HW_COMPOSITING,
FeatureStatus::Blocked,
"Too many attemps of D3D11 creation, fallback to software solution.");
gfxConfig::SetFailed(Feature::D3D11_COMPOSITING,
FeatureStatus::Blocked,
"Too many attemps of D3D11 creation, fallback to software solution.");
gfxConfig::SetFailed(Feature::DIRECT2D,
FeatureStatus::Blocked,
"Too many attemps of D3D11 creation, fallback to software solution.");
HandleProcessLost();
return;
}

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

@ -237,11 +237,5 @@ FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap()
}
}
int32_t
ClientIPCAllocator::GetMaxTextureSize() const
{
return gfxPrefs::MaxTextureSize();
}
} // namespace layers
} // namespace mozilla

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

@ -120,8 +120,6 @@ public:
virtual MessageLoop * GetMessageLoop() const = 0;
virtual int32_t GetMaxTextureSize() const;
virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
};

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

@ -1645,7 +1645,7 @@ private:
buf.mGlyphs = mGlyphBuffer;
buf.mNumGlyphs = mNumGlyphs;
gfxContext::AzureState state = mRunParams.context->CurrentState();
const gfxContext::AzureState &state = mRunParams.context->CurrentState();
if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
if (state.pattern || mFontParams.contextPaint) {
Pattern *pat;
@ -1661,7 +1661,9 @@ private:
}
if (!fillPattern) {
if (state.pattern) {
pat = state.pattern->GetPattern(mRunParams.dt,
RefPtr<gfxPattern> statePattern =
mRunParams.context->CurrentState().pattern;
pat = statePattern->GetPattern(mRunParams.dt,
state.patternTransformChanged ?
&state.patternTransform : nullptr);
} else {
@ -2070,10 +2072,12 @@ gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
bool sideways = false;
gfxContextMatrixAutoSaveRestore matrixRestore;
gfxPoint origPt = *aPt;
if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
sideways = true;
aRunParams.context->Save();
matrixRestore.SetContext(aRunParams.context);
gfxPoint p(aPt->x * aRunParams.devPerApp,
aPt->y * aRunParams.devPerApp);
const Metrics& metrics = GetMetrics(eHorizontal);
@ -2198,7 +2202,6 @@ gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
if (sideways) {
aRunParams.context->Restore();
// adjust updated aPt to account for the transform we were using
gfxFloat advance = aPt->x - origPt.x;
if (aOrientation ==
@ -2222,7 +2225,6 @@ gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
aContext->Save();
aContext->SetMatrix(
aContext->CurrentMatrix().PreTranslate(aPoint.x, aPoint.y).
PreScale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
@ -2230,7 +2232,6 @@ gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aContextPaint);
aContext->Restore();
aContext->NewPath();
return true;
}

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

@ -106,7 +106,7 @@ gfxPattern::GetInverseMatrix() const
Pattern*
gfxPattern::GetPattern(const DrawTarget *aTarget,
Matrix *aOriginalUserToDevice)
const Matrix *aOriginalUserToDevice)
{
Matrix patternToUser = mPatternToUserSpace;

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

@ -48,7 +48,7 @@ public:
* to the current transform.
*/
mozilla::gfx::Pattern *GetPattern(const mozilla::gfx::DrawTarget *aTarget,
mozilla::gfx::Matrix *aOriginalUserToDevice = nullptr);
const mozilla::gfx::Matrix *aOriginalUserToDevice = nullptr);
bool IsOpaque();
// clamp, repeat, reflect

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

@ -848,18 +848,36 @@ gfxPlatform::IsDXInterop2Blocked()
return status != nsIGfxInfo::FEATURE_STATUS_OK;
}
/* static */ int32_t
gfxPlatform::MaxTextureSize()
{
// Make sure we don't completely break rendering because of a typo in the
// pref or whatnot.
const int32_t kMinSizePref = 2048;
return std::max(kMinSizePref, gfxPrefs::MaxTextureSizeDoNotUseDirectly());
}
/* static */ int32_t
gfxPlatform::MaxAllocSize()
{
// Make sure we don't completely break rendering because of a typo in the
// pref or whatnot.
const int32_t kMinAllocPref = 10000000;
return std::max(kMinAllocPref, gfxPrefs::MaxAllocSizeDoNotUseDirectly());
}
/* static */ void
gfxPlatform::InitMoz2DLogging()
{
auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
mozilla::gfx::Config cfg;
cfg.mLogForwarder = fwd;
cfg.mMaxTextureSize = gfxPrefs::MaxTextureSize();
cfg.mMaxAllocSize = gfxPrefs::MaxAllocSize();
mozilla::gfx::Config cfg;
cfg.mLogForwarder = fwd;
cfg.mMaxTextureSize = gfxPlatform::MaxTextureSize();
cfg.mMaxAllocSize = gfxPlatform::MaxAllocSize();
gfx::Factory::Init(cfg);
gfx::Factory::Init(cfg);
}
/* static */ bool

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

@ -189,6 +189,8 @@ public:
*/
static void InitNullMetadata();
static int32_t MaxTextureSize();
static int32_t MaxAllocSize();
static void InitMoz2DLogging();
static bool IsHeadless();

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

@ -448,8 +448,10 @@ private:
// The maximums here are quite conservative, we can tighten them if problems show up.
DECL_GFX_PREF(Once, "gfx.logging.texture-usage.enabled", GfxLoggingTextureUsageEnabled, bool, false);
DECL_GFX_PREF(Once, "gfx.logging.peak-texture-usage.enabled",GfxLoggingPeakTextureUsageEnabled, bool, false);
DECL_GFX_PREF(Once, "gfx.max-alloc-size", MaxAllocSize, int32_t, (int32_t)500000000);
DECL_GFX_PREF(Once, "gfx.max-texture-size", MaxTextureSize, int32_t, (int32_t)32767);
// Use gfxPlatform::MaxAllocSize instead of the pref directly
DECL_GFX_PREF(Once, "gfx.max-alloc-size", MaxAllocSizeDoNotUseDirectly, int32_t, (int32_t)500000000);
// Use gfxPlatform::MaxTextureSize instead of the pref directly
DECL_GFX_PREF(Once, "gfx.max-texture-size", MaxTextureSizeDoNotUseDirectly, int32_t, (int32_t)32767);
DECL_GFX_PREF(Live, "gfx.partialpresent.force", PartialPresent, int32_t, 0);
DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false);
DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled", SurfaceTextureDetachEnabled, bool, true);

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

@ -499,7 +499,6 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, Range aRange,
(end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
aParams.context->Save();
aParams.context->Clip(clipRect);
}
@ -512,7 +511,7 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, Range aRange,
DrawGlyphs(aFont, data.mRange, &pt,
aProvider, aRange, aParams, aOrientation);
aParams.context->Restore();
aParams.context->PopClip();
if (aParams.isVerticalRun) {
aPt->y += aParams.direction * data.mPartWidth;

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

@ -122,12 +122,17 @@ from its prototype:
This property is writable, so you can change the source map URL by
setting it. All Debugger.Source objects referencing the same
source will see the change. Setting an empty string has no affect
source will see the change. Setting an empty string has no effect
and will not change existing value.
**If the instance refers to WebAssembly code**, `null`. Attempts to write
to this property throw a `TypeError`.
`displayURL`
: If the script had a special `//# sourceURL` comment, as described in
the source maps specification, then this property's value holds
the string that was given. Otherwise, this is `null`.
`element`
: The [`Debugger.Object`][object] instance referring to the DOM element to which
this source code belongs, if any, or `undefined` if it belongs to no DOM

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

@ -935,30 +935,41 @@ nsLayoutUtils::GetCurrentAPZResolutionScale(nsIPresShell* aShell) {
// Return the maximum displayport size, based on the LayerManager's maximum
// supported texture size. The result is in app units.
static nscoord
GetMaxDisplayPortSize(nsIContent* aContent)
GetMaxDisplayPortSize(nsIContent* aContent, nsPresContext* aFallbackPrescontext)
{
MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(), "Do not clamp displayports if tiling is enabled");
// Pick a safe maximum displayport size for sanity purposes. This is the
// lowest maximum texture size on tileless-platforms (Windows, D3D10).
// If the gfx.max-texture-size pref is set, further restrict the displayport
// size to fit within that, because the compositor won't upload stuff larger
// than this size.
nscoord safeMaximum = aFallbackPrescontext
? aFallbackPrescontext->DevPixelsToAppUnits(
std::min(8192, gfxPlatform::MaxTextureSize()))
: nscoord_MAX;
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) {
return nscoord_MAX;
return safeMaximum;
}
frame = nsLayoutUtils::GetDisplayRootFrame(frame);
nsIWidget* widget = frame->GetNearestWidget();
if (!widget) {
return nscoord_MAX;
return safeMaximum;
}
LayerManager* lm = widget->GetLayerManager();
if (!lm) {
return nscoord_MAX;
return safeMaximum;
}
nsPresContext* presContext = frame->PresContext();
int32_t maxSizeInDevPixels = lm->GetMaxTextureSize();
if (maxSizeInDevPixels < 0 || maxSizeInDevPixels == INT_MAX) {
return nscoord_MAX;
return safeMaximum;
}
maxSizeInDevPixels = std::min(maxSizeInDevPixels, gfxPlatform::MaxTextureSize());
return presContext->DevPixelsToAppUnits(maxSizeInDevPixels);
}
@ -1085,12 +1096,8 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
} else {
// Calculate the displayport to make sure we fit within the max texture size
// when not tiling.
nscoord maxSizeAppUnits = GetMaxDisplayPortSize(aContent);
if (maxSizeAppUnits == nscoord_MAX) {
// Pick a safe maximum displayport size for sanity purposes. This is the
// lowest maximum texture size on tileless-platforms (Windows, D3D10).
maxSizeAppUnits = presContext->DevPixelsToAppUnits(8192);
}
nscoord maxSizeAppUnits = GetMaxDisplayPortSize(aContent, presContext);
MOZ_ASSERT(maxSizeAppUnits < nscoord_MAX);
// The alignment code can round up to 3 tiles, we want to make sure
// that the displayport can grow by up to 3 tiles without going
@ -1279,9 +1286,9 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect* aResult, float aMultiplier)
if (!gfxPrefs::LayersTilesEnabled()) {
// Either we should have gotten a valid rect directly from the displayport
// base, or we should have computed a valid rect from the margins.
NS_ASSERTION(result.width <= GetMaxDisplayPortSize(aContent),
NS_ASSERTION(result.width <= GetMaxDisplayPortSize(aContent, nullptr),
"Displayport must be a valid texture size");
NS_ASSERTION(result.height <= GetMaxDisplayPortSize(aContent),
NS_ASSERTION(result.height <= GetMaxDisplayPortSize(aContent, nullptr),
"Displayport must be a valid texture size");
}

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

@ -5245,16 +5245,15 @@ nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder,
LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
extraVisible.Inflate(1);
gfxContextAutoSaveRestore save(aCtx);
gfxRect pixelVisible(extraVisible.x, extraVisible.y,
extraVisible.width, extraVisible.height);
pixelVisible.Inflate(2);
pixelVisible.RoundOut();
if (!aBuilder->IsForGenerateGlyphMask() &&
!aBuilder->IsForPaintingSelectionBG() &&
!aIsRecording) {
bool willClip = !aBuilder->IsForGenerateGlyphMask() &&
!aBuilder->IsForPaintingSelectionBG() &&
!aIsRecording;
if (willClip) {
aCtx->NewPath();
aCtx->Rectangle(pixelVisible);
aCtx->Clip();
@ -5263,17 +5262,20 @@ nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder,
NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
gfxContextMatrixAutoSaveRestore matrixSR;
nsPoint framePt = ToReferenceFrame();
if (f->StyleContext()->IsTextCombined()) {
float scaleFactor = GetTextCombineScaleFactor(f);
if (scaleFactor != 1.0f) {
matrixSR.SetContext(aCtx);
// Setup matrix to compress text for text-combine-upright if
// necessary. This is done here because we want selection be
// compressed at the same time as text.
gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
gfxMatrix mat = aCtx->CurrentMatrix()
.PreTranslate(pt).PreScale(scaleFactor, 1.0).PreTranslate(-pt);
aCtx->SetMatrix(mat);
aCtx->SetMatrix (mat);
}
}
nsTextFrame::PaintTextParams params(aCtx);
@ -5290,6 +5292,10 @@ nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder,
}
f->PaintText(params, *this, mOpacity);
if (willClip) {
aCtx->PopClip();
}
}
void

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

@ -0,0 +1,14 @@
<!DOCTYPE html>
<style>
.bar:before {
content:'SHOULD NOT BE ORANGE'
}
.foo:before {
content:'SHOULD BE ORANGE'
}
.foo:before {
background-color: orange;
}
</style>
<div class="bar"></div>
<div class="foo"></div>

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

@ -0,0 +1,14 @@
<!DOCTYPE html>
<style>
.bar:before {
content:'SHOULD NOT BE ORANGE'
}
.foo:before {
content:'SHOULD BE ORANGE'
}
:-moz-any(.foo):before {
background-color: orange;
}
</style>
<div class="bar"></div>
<div class="foo"></div>

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

@ -2035,3 +2035,4 @@ fails-if(!stylo||styloVsGecko) == 1365162-1.html 1365162-1-ref.html
needs-focus == 1377447-1.html 1377447-1-ref.html
needs-focus != 1377447-1.html 1377447-2.html
== 1379041.html 1379041-ref.html
== 1379696.html 1379696-ref.html

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

@ -228,7 +228,7 @@ asserts(0-10) == grid-fragmentation-015.html grid-fragmentation-015-ref.html # b
== grid-fragmentation-dyn4-004.html grid-fragmentation-004-ref.html
== grid-fragmentation-dyn4-005.html grid-fragmentation-005-ref.html
== grid-fragmentation-dyn5-005.html grid-fragmentation-005-ref.html
skip-if(styloVsGecko) == grid-fragmentation-dyn1-006.html grid-fragmentation-006-ref.html
== grid-fragmentation-dyn1-006.html grid-fragmentation-006-ref.html
== grid-fragmentation-dyn3-007.html grid-fragmentation-007-ref.html
== grid-fragmentation-dyn5-007.html grid-fragmentation-007-ref.html
== grid-fragmentation-dyn5-008.html grid-fragmentation-008-ref.html

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

@ -519,34 +519,6 @@ CSSStyleSheet::EnabledStateChangedInternal()
ClearRuleCascades();
}
uint64_t
CSSStyleSheet::FindOwningWindowInnerID() const
{
uint64_t windowID = 0;
if (mDocument) {
windowID = mDocument->InnerWindowID();
}
if (windowID == 0 && mOwningNode) {
windowID = mOwningNode->OwnerDoc()->InnerWindowID();
}
if (windowID == 0 && mOwnerRule) {
RefPtr<StyleSheet> sheet =
static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet();
if (sheet) {
windowID = sheet->AsGecko()->FindOwningWindowInnerID();
}
}
if (windowID == 0 && mParent) {
CSSStyleSheet* parentAsCSS = mParent->AsGecko();
windowID = parentAsCSS->FindOwningWindowInnerID();
}
return windowID;
}
void
CSSStyleSheet::AppendStyleRule(css::Rule* aRule)
{

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

@ -101,8 +101,6 @@ public:
bool HasRules() const;
// Find the ID of the owner inner window.
uint64_t FindOwningWindowInnerID() const;
#ifdef DEBUG
void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif

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

@ -139,7 +139,7 @@ ErrorReporter::ReleaseGlobals()
}
ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
const CSSStyleSheet* aSheet,
const StyleSheet* aSheet,
const Loader* aLoader,
nsIURI* aURI)
: mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
@ -148,6 +148,15 @@ ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
{
}
ErrorReporter::ErrorReporter(const StyleSheet* aSheet,
const Loader* aLoader,
nsIURI* aURI)
: mScanner(nullptr), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
mErrorColNumber(0)
{
}
ErrorReporter::~ErrorReporter()
{
// Schedule deferred cleanup for cached data. We want to strike a
@ -228,13 +237,33 @@ ErrorReporter::OutputError()
}
void
ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aLineOffset)
ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aColNumber)
{
mErrorLineNumber = aLineNumber;
mErrorColNumber = aLineOffset;
mErrorColNumber = aColNumber;
OutputError();
}
// When Stylo's CSS parser is in use, this reporter does not have access to the CSS parser's
// state. The users of ErrorReporter need to provide:
// - the line number of the error
// - the column number of the error
// - the complete source line containing the invalid CSS
void
ErrorReporter::OutputError(uint32_t aLineNumber,
uint32_t aColNumber,
const nsACString& aSourceLine)
{
mErrorLine.Truncate();
// This could be a really long string for minified CSS; just leave it empty if we OOM.
if (!AppendUTF8toUTF16(aSourceLine, mErrorLine, fallible)) {
mErrorLine.Truncate();
}
mPrevErrorLineNumber = aLineNumber;
OutputError(aLineNumber, aColNumber);
}
void
ErrorReporter::ClearError()
{
@ -248,15 +277,15 @@ ErrorReporter::AddToError(const nsString &aErrorText)
if (mError.IsEmpty()) {
mError = aErrorText;
mErrorLineNumber = mScanner->GetLineNumber();
mErrorColNumber = mScanner->GetColumnNumber();
mErrorLineNumber = mScanner ? mScanner->GetLineNumber() : 0;
mErrorColNumber = mScanner ? mScanner->GetColumnNumber() : 0;
// Retrieve the error line once per line, and reuse the same nsString
// for all errors on that line. That causes the text of the line to
// be shared among all the nsIScriptError objects.
if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
// Be careful here: the error line might be really long and OOM
// when we try to make a copy here. If so, just leave it empty.
if (!mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) {
if (!mScanner || !mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) {
mErrorLine.Truncate();
}
mPrevErrorLineNumber = mErrorLineNumber;
@ -295,6 +324,21 @@ ErrorReporter::ReportUnexpected(const char *aMessage,
AddToError(str);
}
void
ErrorReporter::ReportUnexpectedUnescaped(const char *aMessage,
const nsAutoString& aParam)
{
if (!ShouldReportErrors()) return;
const char16_t *params[1] = { aParam.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpected(const char *aMessage,
const nsCSSToken &aToken)
@ -303,13 +347,7 @@ ErrorReporter::ReportUnexpected(const char *aMessage,
nsAutoString tokenString;
aToken.AppendToString(tokenString);
const char16_t *params[1] = { tokenString.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
ReportUnexpectedUnescaped(aMessage, tokenString);
}
void

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

@ -26,10 +26,13 @@ class Loader;
// If CSS_REPORT_PARSE_ERRORS is not defined, all of this class's
// methods become inline stubs.
class MOZ_STACK_CLASS ErrorReporter {
class ErrorReporter {
public:
ErrorReporter(const nsCSSScanner &aScanner,
const CSSStyleSheet *aSheet,
const StyleSheet *aSheet,
const Loader *aLoader,
nsIURI *aURI);
ErrorReporter(const StyleSheet *aSheet,
const Loader *aLoader,
nsIURI *aURI);
~ErrorReporter();
@ -38,6 +41,7 @@ public:
void OutputError();
void OutputError(uint32_t aLineNumber, uint32_t aLineOffset);
void OutputError(uint32_t aLineNumber, uint32_t aLineOffset, const nsACString& aSource);
void ClearError();
// In all overloads of ReportUnexpected, aMessage is a stringbundle
@ -50,6 +54,9 @@ public:
void ReportUnexpected(const char *aMessage, const nsString& aParam);
// one parameter, a token
void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken);
// one parameter which has already been escaped appropriately
void ReportUnexpectedUnescaped(const char *aMessage,
const nsAutoString& aParam);
// two parameters, a token and a character, in that order
void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken,
char16_t aChar);
@ -71,7 +78,7 @@ private:
nsString mErrorLine;
nsString mFileName;
const nsCSSScanner *mScanner;
const CSSStyleSheet *mSheet;
const StyleSheet *mSheet;
const Loader *mLoader;
nsIURI *mURI;
uint64_t mInnerWindowID;

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

@ -215,7 +215,8 @@ SERVO_BINDING_FUNC(Servo_ParseProperty,
nsCSSPropertyID property, const nsACString* value,
RawGeckoURLExtraData* data,
mozilla::ParsingMode parsing_mode,
nsCompatibility quirks_mode)
nsCompatibility quirks_mode,
mozilla::css::Loader* loader)
SERVO_BINDING_FUNC(Servo_ParseEasing, bool,
const nsAString* easing,
RawGeckoURLExtraData* data,
@ -310,7 +311,8 @@ SERVO_BINDING_FUNC(Servo_AnimationValue_Compute,
SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong,
const nsACString* data,
RawGeckoURLExtraData* extra_data,
nsCompatibility quirks_mode)
nsCompatibility quirks_mode,
mozilla::css::Loader* loader)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_CreateEmpty,
RawServoDeclarationBlockStrong)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_Clone, RawServoDeclarationBlockStrong,
@ -344,14 +346,16 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetProperty, bool,
const nsACString* value, bool is_important,
RawGeckoURLExtraData* data,
mozilla::ParsingMode parsing_mode,
nsCompatibility quirks_mode)
nsCompatibility quirks_mode,
mozilla::css::Loader* loader)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetPropertyById, bool,
RawServoDeclarationBlockBorrowed declarations,
nsCSSPropertyID property,
const nsACString* value, bool is_important,
RawGeckoURLExtraData* data,
mozilla::ParsingMode parsing_mode,
nsCompatibility quirks_mode)
nsCompatibility quirks_mode,
mozilla::css::Loader* loader)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemoveProperty, void,
RawServoDeclarationBlockBorrowed declarations,
const nsACString* property)
@ -485,8 +489,7 @@ SERVO_BINDING_FUNC(Servo_NoteExplicitHints, void, RawGeckoElementBorrowed elemen
SERVO_BINDING_FUNC(Servo_TakeChangeHint, nsChangeHint, RawGeckoElementBorrowed element)
SERVO_BINDING_FUNC(Servo_ResolveStyle, ServoComputedValuesStrong,
RawGeckoElementBorrowed element,
RawServoStyleSetBorrowed set,
bool allow_stale)
RawServoStyleSetBorrowed set)
SERVO_BINDING_FUNC(Servo_ResolvePseudoStyle, ServoComputedValuesStrong,
RawGeckoElementBorrowed element,
mozilla::CSSPseudoElementType pseudo_type,

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

@ -7,6 +7,7 @@
#include "mozilla/ServoBindings.h"
#include "ChildIterator.h"
#include "ErrorReporter.h"
#include "GeckoProfiler.h"
#include "gfxFontFamilyList.h"
#include "nsAnimationManager.h"
@ -70,6 +71,7 @@
#endif
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;
#define SERVO_ARC_TYPE(name_, type_) \
@ -2631,3 +2633,38 @@ Gecko_SetJemallocThreadLocalArena(bool enabled)
#include "ServoBindingList.h"
#undef SERVO_BINDING_FUNC
#endif
ErrorReporter*
Gecko_CreateCSSErrorReporter(ServoStyleSheet* sheet,
Loader* loader,
nsIURI* uri)
{
MOZ_ASSERT(NS_IsMainThread());
return new ErrorReporter(sheet, loader, uri);
}
void
Gecko_DestroyCSSErrorReporter(ErrorReporter* reporter)
{
delete reporter;
}
void
Gecko_ReportUnexpectedCSSError(ErrorReporter* reporter,
const char* message,
const char* param,
uint32_t paramLen,
const char* source,
uint32_t sourceLen,
uint32_t lineNumber,
uint32_t colNumber,
nsIURI* uri)
{
MOZ_ASSERT(NS_IsMainThread());
nsDependentCSubstring paramValue(param, paramLen);
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
reporter->ReportUnexpectedUnescaped(message, wideParam);
nsDependentCSubstring sourceValue(source, sourceLen);
reporter->OutputError(lineNumber, colNumber, sourceValue);
}

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

@ -639,6 +639,20 @@ void Gecko_SetJemallocThreadLocalArena(bool enabled);
#include "mozilla/ServoBindingList.h"
#undef SERVO_BINDING_FUNC
mozilla::css::ErrorReporter* Gecko_CreateCSSErrorReporter(mozilla::ServoStyleSheet* sheet,
mozilla::css::Loader* loader,
nsIURI* uri);
void Gecko_DestroyCSSErrorReporter(mozilla::css::ErrorReporter* reporter);
void Gecko_ReportUnexpectedCSSError(mozilla::css::ErrorReporter* reporter,
const char* message,
const char* param,
uint32_t paramLen,
const char* source,
uint32_t sourceLen,
uint32_t lineNumber,
uint32_t colNumber,
nsIURI* aURI);
} // extern "C"
#endif // mozilla_ServoBindings_h

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

@ -56,6 +56,7 @@ headers = [
"mozilla/Keyframe.h",
"mozilla/ServoElementSnapshot.h",
"mozilla/ServoElementSnapshotTable.h",
"mozilla/css/ErrorReporter.h",
"mozilla/dom/Element.h",
"mozilla/dom/ChildIterator.h",
"mozilla/dom/NameSpaceConstants.h",
@ -122,6 +123,7 @@ whitelist-types = [
"mozilla::ServoElementSnapshot.*",
"mozilla::ServoStyleSheetInner",
"mozilla::CSSPseudoClassType",
"mozilla::css::ErrorReporter",
"mozilla::css::SheetParsingMode",
"mozilla::css::URLMatchingFunction",
"mozilla::dom::IterationCompositeOperation",
@ -329,6 +331,7 @@ raw-lines = [
whitelist-functions = ["Servo_.*", "Gecko_.*"]
structs-types = [
"mozilla::css::GridTemplateAreasValue",
"mozilla::css::ErrorReporter",
"mozilla::css::ImageValue",
"mozilla::css::URLValue",
"mozilla::css::URLValueData",
@ -394,6 +397,7 @@ structs-types = [
"nsCursorImage",
"nsFont",
"nsIAtom",
"nsIURI",
"nsCompatibility",
"nsMediaFeature",
"nsRestyleHint",

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

@ -14,12 +14,13 @@ namespace mozilla {
/* static */ already_AddRefed<ServoDeclarationBlock>
ServoDeclarationBlock::FromCssText(const nsAString& aCssText,
URLExtraData* aExtraData,
nsCompatibility aMode)
nsCompatibility aMode,
css::Loader* aLoader)
{
NS_ConvertUTF16toUTF8 value(aCssText);
// FIXME (bug 1343964): Figure out a better solution for sending the base uri to servo
RefPtr<RawServoDeclarationBlock>
raw = Servo_ParseStyleAttribute(&value, aExtraData, aMode).Consume();
raw = Servo_ParseStyleAttribute(&value, aExtraData, aMode, aLoader).Consume();
RefPtr<ServoDeclarationBlock> decl = new ServoDeclarationBlock(raw.forget());
return decl.forget();
}

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

@ -29,7 +29,7 @@ public:
static already_AddRefed<ServoDeclarationBlock>
FromCssText(const nsAString& aCssText, URLExtraData* aExtraData,
nsCompatibility aMode);
nsCompatibility aMode, css::Loader* aLoader);
RawServoDeclarationBlock* Raw() const { return mRaw; }
RawServoDeclarationBlock* const* RefRaw() const {

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

@ -37,7 +37,6 @@ using namespace mozilla::dom;
ServoStyleSet::ServoStyleSet()
: mPresContext(nullptr)
, mAllowResolveStaleStyles(false)
, mAuthorStyleDisabled(false)
, mStylistState(StylistState::NotDirty)
, mUserFontSetUpdateGeneration(0)
@ -496,8 +495,8 @@ ServoStyleSet::ResolvePseudoElementStyle(Element* aOriginatingElement,
RefPtr<ServoComputedValues> computedValues;
if (aPseudoElement) {
MOZ_ASSERT(aType == aPseudoElement->GetPseudoElementType());
computedValues = Servo_ResolveStyle(aPseudoElement, mRawSet.get(),
mAllowResolveStaleStyles).Consume();
computedValues = Servo_ResolveStyle(aPseudoElement,
mRawSet.get()).Consume();
} else {
const ServoComputedValues* parentStyle =
aParentContext ? aParentContext->ComputedValues() : nullptr;
@ -1152,8 +1151,7 @@ already_AddRefed<ServoComputedValues>
ServoStyleSet::ResolveServoStyle(Element* aElement)
{
UpdateStylistIfNeeded();
return Servo_ResolveStyle(aElement, mRawSet.get(),
mAllowResolveStaleStyles).Consume();
return Servo_ResolveStyle(aElement, mRawSet.get()).Consume();
}
void

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

@ -77,29 +77,6 @@ class ServoStyleSet
typedef ServoElementSnapshotTable SnapshotTable;
public:
class AutoAllowStaleStyles
{
public:
explicit AutoAllowStaleStyles(ServoStyleSet* aStyleSet)
: mStyleSet(aStyleSet)
{
if (mStyleSet) {
MOZ_ASSERT(!mStyleSet->mAllowResolveStaleStyles);
mStyleSet->mAllowResolveStaleStyles = true;
}
}
~AutoAllowStaleStyles()
{
if (mStyleSet) {
mStyleSet->mAllowResolveStaleStyles = false;
}
}
private:
ServoStyleSet* mStyleSet;
};
static bool IsInServoTraversal()
{
// The callers of this function are generally main-thread-only _except_
@ -603,7 +580,6 @@ private:
UniquePtr<RawServoStyleSet> mRawSet;
EnumeratedArray<SheetType, SheetType::Count,
nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
bool mAllowResolveStaleStyles;
bool mAuthorStyleDisabled;
StylistState mStylistState;
uint64_t mUserFontSetUpdateGeneration;

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

@ -611,6 +611,33 @@ StyleSheet::InsertRuleIntoGroup(const nsAString& aRule,
return NS_OK;
}
uint64_t
StyleSheet::FindOwningWindowInnerID() const
{
uint64_t windowID = 0;
if (mDocument) {
windowID = mDocument->InnerWindowID();
}
if (windowID == 0 && mOwningNode) {
windowID = mOwningNode->OwnerDoc()->InnerWindowID();
}
RefPtr<css::Rule> ownerRule;
if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
if (sheet) {
windowID = sheet->FindOwningWindowInnerID();
}
}
if (windowID == 0 && mParent) {
windowID = mParent->FindOwningWindowInnerID();
}
return windowID;
}
void
StyleSheet::EnabledStateChanged()
{

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

@ -264,6 +264,9 @@ public:
nsresult InsertRuleIntoGroup(const nsAString& aRule,
css::GroupRule* aGroup, uint32_t aIndex);
// Find the ID of the owner inner window.
uint64_t FindOwningWindowInnerID() const;
template<typename Func>
void EnumerateChildSheets(Func aCallback) {
for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {

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

@ -178,3 +178,4 @@ load 1375812-1.html
load 1377053-1.html
load 1377256-1.html
load 1378814.html
load link-transition-before.html

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

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<style type="text/css">
a {
border-bottom: 1px solid transparent;
transition: all 2s linear;
}
a.start {
border-bottom: 1px solid #000000;
}
/* Can be anything, just need to ensure pseudos cascade */
:before {
color: blue;
}
</style>
<a href="http://www.example.com/">example</a>
<script>
let a0 = document.querySelectorAll("a")[0];
a0.classList.add("start");
requestIdleCallback(() => {
a0.classList.remove("start");
requestIdleCallback(() => {
a0.classList.add("start");
document.documentElement.removeAttribute("class");
});
});
</script>

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

@ -25,8 +25,10 @@
// OUTPUT_CLASS=nsCSSPseudoElements
// MACRO_NAME=CSS_PSEUDO_ELEMENT
CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2)
CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2)
CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2 |
CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM)
CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2 |
CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM)
CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0)

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

@ -40,6 +40,9 @@
// API for creating pseudo-implementing native anonymous content in JS with this
// pseudo-element?
#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5)
// Does this pseudo-element act like an item for containers (such as flex and
// grid containers) and thus needs parent display-based style fixup?
#define CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM (1<<6)
namespace mozilla {
@ -112,6 +115,12 @@ public:
return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC);
}
static bool PseudoElementIsFlexOrGridItem(const Type aType)
{
return PseudoElementHasFlags(aType,
CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM);
}
static bool IsEnabled(Type aType, EnabledState aEnabledState)
{
return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) ||

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

@ -180,6 +180,7 @@ nsDOMCSSAttributeDeclaration::GetServoCSSParsingEnvironment() const
return {
mElement->GetURLDataForStyleAttr(),
mElement->OwnerDoc()->GetCompatibilityMode(),
mElement->OwnerDoc()->CSSLoader(),
};
}

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

@ -130,7 +130,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
}
newdecl = ServoDeclarationBlock::FromCssText(aCssText, servoEnv.mUrlExtraData,
servoEnv.mCompatMode);
servoEnv.mCompatMode, servoEnv.mLoader);
} else {
CSSParsingEnvironment geckoEnv;
GetCSSParsingEnvironment(geckoEnv);
@ -284,19 +284,21 @@ nsDOMCSSDeclaration::GetServoCSSParsingEnvironmentForRule(const css::Rule* aRule
{
StyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
if (!sheet) {
return { nullptr, eCompatibility_FullStandards };
return { nullptr, eCompatibility_FullStandards, nullptr };
}
if (nsIDocument* document = aRule->GetDocument()) {
return {
sheet->AsServo()->URLData(),
document->GetCompatibilityMode(),
document->CSSLoader(),
};
}
return {
sheet->AsServo()->URLData(),
eCompatibility_FullStandards,
nullptr,
};
}
@ -369,7 +371,7 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID,
NS_ConvertUTF16toUTF8 value(aPropValue);
return Servo_DeclarationBlock_SetPropertyById(
decl->Raw(), aPropID, &value, aIsImportant, env.mUrlExtraData,
ParsingMode::Default, env.mCompatMode);
ParsingMode::Default, env.mCompatMode, env.mLoader);
});
}
@ -392,7 +394,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName,
NS_ConvertUTF16toUTF8 value(aPropValue);
return Servo_DeclarationBlock_SetProperty(
decl->Raw(), &property, &value, aIsImportant, env.mUrlExtraData,
ParsingMode::Default, env.mCompatMode);
ParsingMode::Default, env.mCompatMode, env.mLoader);
});
}

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

@ -153,11 +153,14 @@ protected:
{
mozilla::URLExtraData* mUrlExtraData;
nsCompatibility mCompatMode;
mozilla::css::Loader* mLoader;
ServoCSSParsingEnvironment(mozilla::URLExtraData* aUrlData,
nsCompatibility aCompatMode)
nsCompatibility aCompatMode,
mozilla::css::Loader* aLoader)
: mUrlExtraData(aUrlData)
, mCompatMode(aCompatMode)
, mLoader(aLoader)
{}
};

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

@ -1699,6 +1699,23 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
return ruleWalker.CurrentNode();
}
static bool
SkipsParentDisplayBasedStyleFixup(nsStyleContext* aStyleContext)
{
CSSPseudoElementType type = aStyleContext->GetPseudoType();
switch (type) {
case CSSPseudoElementType::InheritingAnonBox:
return nsCSSAnonBoxes::AnonBoxSkipsParentDisplayBasedStyleFixup(
aStyleContext->GetPseudo());
case CSSPseudoElementType::NonInheritingAnonBox:
return true;
case CSSPseudoElementType::NotPseudo:
return false;
default:
return !nsCSSPseudoElements::PseudoElementIsFlexOrGridItem(type);
}
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleWithReplacement(Element* aElement,
Element* aPseudoElement,
@ -1769,11 +1786,13 @@ nsStyleSet::ResolveStyleWithReplacement(Element* aElement,
#endif
}
if (aElement && aElement->IsRootOfAnonymousSubtree()) {
if ((aElement && aElement->IsRootOfAnonymousSubtree()) ||
SkipsParentDisplayBasedStyleFixup(aOldStyleContext)) {
// For anonymous subtree roots, don't tweak "display" value based on whether
// or not the parent is styled as a flex/grid container. (If the parent
// has anonymous-subtree kids, then we know it's not actually going to get
// a flex/grid container frame, anyway.)
// a flex/grid container frame, anyway.) Same for certain anonymous boxes
// and most pseudos.
flags |= eSkipParentDisplayBasedStyleFixup;
}
@ -1945,11 +1964,11 @@ nsStyleSet::ResolvePseudoElementStyleInternal(
if (aAnimationFlag == eWithAnimation) {
flags |= eDoAnimation;
}
} else {
// Flex and grid containers don't expect to have any pseudo-element children
// aside from ::before and ::after. So if we have such a child, we're not
// actually in a flex/grid container, and we should skip flex/grid item
// style fixup.
}
if (!nsCSSPseudoElements::PseudoElementIsFlexOrGridItem(aType)) {
// Only pseudo-elements that act as items in flex and grid containers
// have parent display-based style fixup.
flags |= eSkipParentDisplayBasedStyleFixup;
}
@ -2043,11 +2062,11 @@ nsStyleSet::ProbePseudoElementStyle(Element* aParentElement,
if (aType == CSSPseudoElementType::before ||
aType == CSSPseudoElementType::after) {
flags |= eDoAnimation;
} else {
// Flex and grid containers don't expect to have any pseudo-element children
// aside from ::before and ::after. So if we have such a child, we're not
// actually in a flex/grid container, and we should skip flex/grid item
// style fixup.
}
if (!nsCSSPseudoElements::PseudoElementIsFlexOrGridItem(aType)) {
// Only pseudo-elements that act as items in flex and grid containers
// have parent display-based style fixup.
flags |= eSkipParentDisplayBasedStyleFixup;
}
@ -2502,13 +2521,12 @@ nsStyleSet::ReparentStyleContext(nsStyleContext* aStyleContext,
}
if ((aElement && aElement->IsRootOfAnonymousSubtree()) ||
(aStyleContext->IsAnonBox() &&
nsCSSAnonBoxes::AnonBoxSkipsParentDisplayBasedStyleFixup(
aStyleContext->GetPseudo()))) {
SkipsParentDisplayBasedStyleFixup(aStyleContext)) {
// For anonymous subtree roots, don't tweak "display" value based on whether
// or not the parent is styled as a flex/grid container. (If the parent
// has anonymous-subtree kids, then we know it's not actually going to get
// a flex/grid container frame, anyway.) Same for certain anonymous boxes.
// a flex/grid container frame, anyway.) Same for certain anonymous boxes
// and most pseudos.
flags |= eSkipParentDisplayBasedStyleFixup;
}

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

@ -38,9 +38,6 @@ to mochitest command.
* Animation support:
* SMIL Animation
* test_restyles_in_smil_animation.html [2]
* console support bug 1352669
* test_bug413958.html `monitorConsole` [3]
* test_parser_diagnostics_unprintables.html [550]
* @namespace support:
* test_namespace_rule.html: bug 1355715 [6]
* test_font_feature_values_parsing.html: \@font-feature-values support bug 1355721 [107]

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

@ -39,18 +39,19 @@ var tests = [
s.color = "black";
},
];
const isStylo = SpecialPowers.getBoolPref('layout.css.servo.enabled', false);
var results = [
[ { errorMessage: /Unknown property \u2018nosuchprop\u2019/,
lineNumber: 1, columnNumber: 14,
lineNumber: 1, columnNumber: isStylo ? 4 : 14,
sourceLine: "#s1{nosuchprop:auto; color:black}" },
{ errorMessage: /Unknown property \u2018nosuchprop\u2019/,
lineNumber: 2, columnNumber: 14, sourceLine:
lineNumber: 2, columnNumber: isStylo ? 4 : 14, sourceLine:
"#s2{nosuchprop:auto; color:black}invalid?sel{}#s3{color:black}" },
{ errorMessage: /Ruleset ignored due to bad selector/,
lineNumber: 2, columnNumber: 40, sourceLine:
lineNumber: 2, columnNumber: isStylo ? 33 : 40, sourceLine:
"#s2{nosuchprop:auto; color:black}invalid?sel{}#s3{color:black}" } ],
[ { errorMessage: /parsing value for \u2018width\u2019/,
lineNumber: 0, columnNumber: 6,
lineNumber: 0, columnNumber: isStylo ? 0 : 6,
sourceLine: "width:200;color:black" } ],
[ { errorMessage: /parsing value for \u2018width\u2019/,
lineNumber: 0, columnNumber: 0,

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

@ -23,7 +23,7 @@
// Each "pattern" is tested once with each of the "substitution"s below:
// <t>, <i>, and <s> are replaced by the t:, i:, and s: fields of
// each substitution object in turn.
const patterns = [
let patterns = [
// REPORT_UNEXPECTED_P (only ever used in contexts where identifier-like
// escaping is appropriate)
{ i: "<t>|x{}", o: "prefix \u2018<i>\u2019" },
@ -42,12 +42,20 @@ const patterns = [
{ i: "x{ '<t>'}" , o: "declaration but found \u2018'<s>'\u2019." },
// _Bad_String
{ i: "x{ '<t>\n}", o: "declaration but found \u2018'<s>\u2019." },
// _URL
{ i: "x{ url('<t>')}", o: "declaration but found \u2018url('<s>')\u2019." },
// _Bad_URL
{ i: "x{ url('<t>'.)}" , o: "declaration but found \u2018url('<s>'\u2019." }
];
const isStylo = SpecialPowers.getBoolPref('layout.css.servo.enabled', false);
// Stylo's CSS parser only reports the 'url(' token, not the actual bad URL.
if (!isStylo) {
patterns.push(
// _URL
{ i: "x{ url('<t>')}", o: "declaration but found \u2018url('<s>')\u2019." })
patterns.push(
// _Bad_URL
{ i: "x{ url('<t>'.)}" , o: "declaration but found \u2018url('<s>'\u2019." });
}
// Blocks of characters to test, and how they should be escaped when
// they appear in identifiers and string constants.
const substitutions = [

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

@ -443,24 +443,6 @@ static malloc_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct malloc_bin_stats_s malloc_bin_stats_t;
struct malloc_bin_stats_s {
/*
* Number of allocation requests that corresponded to the size of this
* bin.
*/
uint64_t nrequests;
/* Total number of runs created for this bin's size class. */
uint64_t nruns;
/*
* Total number of runs reused by extracting them from the runs tree for
* this bin's size class.
*/
uint64_t reruns;
/* High-water mark for this bin. */
unsigned long highruns;
/* Current number of runs in this bin. */
unsigned long curruns;
};
@ -470,35 +452,13 @@ struct arena_stats_s {
/* Number of bytes currently mapped. */
size_t mapped;
/*
* Total number of purge sweeps, total number of madvise calls made,
* and total pages purged in order to keep dirty unused memory under
* control.
*/
uint64_t npurge;
uint64_t nmadvise;
uint64_t purged;
#ifdef MALLOC_DECOMMIT
/*
* Total number of decommit/commit operations, and total number of
* pages decommitted.
*/
uint64_t ndecommit;
uint64_t ncommit;
uint64_t decommitted;
#endif
/* Current number of committed pages. */
size_t committed;
/* Per-size-category statistics. */
size_t allocated_small;
uint64_t nmalloc_small;
uint64_t ndalloc_small;
size_t allocated_large;
uint64_t nmalloc_large;
uint64_t ndalloc_large;
};
/******************************************************************************/
@ -506,6 +466,14 @@ struct arena_stats_s {
* Extent data structures.
*/
enum ChunkType {
UNKNOWN_CHUNK,
ZEROED_CHUNK, // chunk only contains zeroes
ARENA_CHUNK, // used to back arena runs created by arena_run_alloc
HUGE_CHUNK, // used to back huge allocations (e.g. huge_malloc)
RECYCLED_CHUNK, // chunk has been stored for future use by chunk_recycle
};
/* Tree of extents. */
typedef struct extent_node_s extent_node_t;
struct extent_node_s {
@ -521,8 +489,8 @@ struct extent_node_s {
/* Total region size. */
size_t size;
/* True if zero-filled; used by chunk recycling code. */
bool zeroed;
/* What type of chunk is there; used by chunk recycling code. */
ChunkType chunk_type;
};
typedef rb_tree(extent_node_t) extent_tree_t;
@ -795,13 +763,6 @@ struct arena_s {
arena_bin_t bins[1]; /* Dynamically sized. */
};
enum ChunkType {
UNKNOWN_CHUNK,
ARENA_CHUNK, // used to back arena runs created by arena_run_alloc
HUGE_CHUNK, // used to back huge allocations (e.g. huge_malloc)
RECYCLED_CHUNK, // chunk has been stored for future use by chunk_recycle
};
/******************************************************************************/
/*
* Data.
@ -1033,8 +994,9 @@ static size_t opt_chunk_2pow = CHUNK_2POW_DEFAULT;
* Begin forward declarations.
*/
static void *chunk_alloc(size_t size, size_t alignment, bool base, bool zero);
static void chunk_dealloc(void *chunk, size_t size, enum ChunkType type);
static void *chunk_alloc(size_t size, size_t alignment, bool base, bool *zeroed=nullptr);
static void chunk_dealloc(void *chunk, size_t size, ChunkType chunk_type);
static void chunk_ensure_zero(void* ptr, size_t size, bool zeroed);
static arena_t *arenas_extend();
static void *huge_malloc(size_t size, bool zero);
static void *huge_palloc(size_t size, size_t alignment, bool zero);
@ -1079,43 +1041,24 @@ load_acquire_z(size_t *p)
return result;
}
/*
* umax2s() provides minimal integer printing functionality, which is
* especially useful for situations where allocation in vsnprintf() calls would
* potentially cause deadlock.
*/
#define UMAX2S_BUFSIZE 65
char *
umax2s(uintmax_t x, char *s)
{
unsigned i;
i = UMAX2S_BUFSIZE - 1;
s[i] = '\0';
do {
i--;
s[i] = "0123456789"[x % 10];
x /= 10;
} while (x > 0);
return (&s[i]);
}
static void
_malloc_message(const char *p1, const char *p2, const char *p3, const char *p4)
_malloc_message(const char *p)
{
#if !defined(MOZ_MEMORY_WINDOWS)
#define _write write
#endif
// Pretend to check _write() errors to suppress gcc warnings about
// warn_unused_result annotations in some versions of glibc headers.
if (_write(STDERR_FILENO, p1, (unsigned int) strlen(p1)) < 0)
return;
if (_write(STDERR_FILENO, p2, (unsigned int) strlen(p2)) < 0)
return;
if (_write(STDERR_FILENO, p3, (unsigned int) strlen(p3)) < 0)
return;
if (_write(STDERR_FILENO, p4, (unsigned int) strlen(p4)) < 0)
return;
// Pretend to check _write() errors to suppress gcc warnings about
// warn_unused_result annotations in some versions of glibc headers.
if (_write(STDERR_FILENO, p, (unsigned int) strlen(p)) < 0)
return;
}
template <typename... Args>
static void
_malloc_message(const char *p, Args... args)
{
_malloc_message(p);
_malloc_message(args...);
}
#include "mozilla/Assertions.h"
@ -1380,7 +1323,7 @@ base_pages_alloc(size_t minsize)
MOZ_ASSERT(minsize != 0);
csize = CHUNK_CEILING(minsize);
base_pages = chunk_alloc(csize, chunksize, true, false);
base_pages = chunk_alloc(csize, chunksize, true);
if (!base_pages)
return (true);
base_next_addr = base_pages;
@ -1545,7 +1488,7 @@ pages_unmap(void *addr, size_t size)
{
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
_malloc_message(_getprogname(),
": (malloc) Error in VirtualFree()\n", "", "");
": (malloc) Error in VirtualFree()\n");
if (opt_abort)
moz_abort();
}
@ -1923,15 +1866,24 @@ chunk_alloc_mmap(size_t size, size_t alignment)
return (ret);
}
bool
pages_purge(void *addr, size_t length)
/* Purge and release the pages in the chunk of length `length` at `addr` to
* the OS.
* Returns whether the pages are guaranteed to be full of zeroes when the
* function returns.
* The force_zero argument explicitly requests that the memory is guaranteed
* to be full of zeroes when the function returns.
*/
static bool
pages_purge(void *addr, size_t length, bool force_zero)
{
bool unzeroed;
#ifdef MALLOC_DECOMMIT
pages_decommit(addr, length);
unzeroed = false;
return true;
#else
# ifndef MOZ_MEMORY_LINUX
if (force_zero)
memset(addr, 0, length);
# endif
# ifdef MOZ_MEMORY_WINDOWS
/*
* The region starting at addr may have been allocated in multiple calls
@ -1947,33 +1899,32 @@ pages_purge(void *addr, size_t length)
length -= pages_size;
pages_size = std::min(length, chunksize);
}
unzeroed = true;
return force_zero;
# else
# ifdef MOZ_MEMORY_LINUX
# define JEMALLOC_MADV_PURGE MADV_DONTNEED
# define JEMALLOC_MADV_ZEROS true
# else /* FreeBSD and Darwin. */
# define JEMALLOC_MADV_PURGE MADV_FREE
# define JEMALLOC_MADV_ZEROS false
# define JEMALLOC_MADV_ZEROS force_zero
# endif
int err = madvise(addr, length, JEMALLOC_MADV_PURGE);
unzeroed = (JEMALLOC_MADV_ZEROS == false || err != 0);
return JEMALLOC_MADV_ZEROS && err == 0;
# undef JEMALLOC_MADV_PURGE
# undef JEMALLOC_MADV_ZEROS
# endif
#endif
return (unzeroed);
}
static void *
chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
size_t alignment, bool base, bool *zero)
size_t alignment, bool base, bool *zeroed)
{
void *ret;
extent_node_t *node;
extent_node_t key;
size_t alloc_size, leadsize, trailsize;
bool zeroed;
ChunkType chunk_type;
if (base) {
/*
@ -2002,9 +1953,10 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
MOZ_ASSERT(node->size >= leadsize + size);
trailsize = node->size - leadsize - size;
ret = (void *)((uintptr_t)node->addr + leadsize);
zeroed = node->zeroed;
if (zeroed)
*zero = true;
chunk_type = node->chunk_type;
if (zeroed) {
*zeroed = (chunk_type == ZEROED_CHUNK);
}
/* Remove node from the tree. */
extent_tree_szad_remove(chunks_szad, node);
extent_tree_ad_remove(chunks_ad, node);
@ -2028,14 +1980,14 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
malloc_mutex_unlock(&chunks_mtx);
node = base_node_alloc();
if (!node) {
chunk_dealloc(ret, size, RECYCLED_CHUNK);
chunk_dealloc(ret, size, chunk_type);
return nullptr;
}
malloc_mutex_lock(&chunks_mtx);
}
node->addr = (void *)((uintptr_t)(ret) + size);
node->size = trailsize;
node->zeroed = zeroed;
node->chunk_type = chunk_type;
extent_tree_szad_insert(chunks_szad, node);
extent_tree_ad_insert(chunks_ad, node);
node = nullptr;
@ -2049,20 +2001,11 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
base_node_dealloc(node);
#ifdef MALLOC_DECOMMIT
pages_commit(ret, size);
#endif
if (*zero) {
if (zeroed == false)
memset(ret, 0, size);
#ifdef DEBUG
else {
size_t i;
size_t *p = (size_t *)(uintptr_t)ret;
for (i = 0; i < size / sizeof(size_t); i++)
MOZ_ASSERT(p[i] == 0);
}
#endif
// pages_commit is guaranteed to zero the chunk.
if (zeroed) {
*zeroed = true;
}
#endif
return (ret);
}
@ -2078,8 +2021,15 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
#define CAN_RECYCLE(size) true
#endif
/* Allocates `size` bytes of system memory aligned for `alignment`.
* `base` indicates whether the memory will be used for the base allocator
* (e.g. base_alloc).
* `zeroed` is an outvalue that returns whether the allocated memory is
* guaranteed to be full of zeroes. It can be omitted when the caller doesn't
* care about the result.
*/
static void *
chunk_alloc(size_t size, size_t alignment, bool base, bool zero)
chunk_alloc(size_t size, size_t alignment, bool base, bool *zeroed)
{
void *ret;
@ -2090,11 +2040,13 @@ chunk_alloc(size_t size, size_t alignment, bool base, bool zero)
if (CAN_RECYCLE(size)) {
ret = chunk_recycle(&chunks_szad_mmap, &chunks_ad_mmap,
size, alignment, base, &zero);
size, alignment, base, zeroed);
if (ret)
goto RETURN;
}
ret = chunk_alloc_mmap(size, alignment);
if (zeroed)
*zeroed = true;
if (ret) {
goto RETURN;
}
@ -2115,18 +2067,31 @@ RETURN:
}
static void
chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
size_t size, enum ChunkType type)
chunk_ensure_zero(void* ptr, size_t size, bool zeroed)
{
if (zeroed == false)
memset(ptr, 0, size);
#ifdef MOZ_DEBUG
else {
size_t i;
size_t *p = (size_t *)(uintptr_t)ret;
for (i = 0; i < size / sizeof(size_t); i++)
MOZ_ASSERT(p[i] == 0);
}
#endif
}
static void
chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
size_t size, ChunkType chunk_type)
{
bool unzeroed;
extent_node_t *xnode, *node, *prev, *xprev, key;
unzeroed = pages_purge(chunk, size);
/* If purge doesn't zero the chunk, only record arena chunks or
* previously recycled chunks. */
if (unzeroed && type != ARENA_CHUNK && type != RECYCLED_CHUNK) {
return;
if (chunk_type != ZEROED_CHUNK) {
if (pages_purge(chunk, size, chunk_type == HUGE_CHUNK)) {
chunk_type = ZEROED_CHUNK;
}
}
/*
@ -2152,7 +2117,9 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
extent_tree_szad_remove(chunks_szad, node);
node->addr = chunk;
node->size += size;
node->zeroed = (node->zeroed && (unzeroed == false));
if (node->chunk_type != chunk_type) {
node->chunk_type = RECYCLED_CHUNK;
}
extent_tree_szad_insert(chunks_szad, node);
} else {
/* Coalescing forward failed, so insert a new node. */
@ -2169,7 +2136,7 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
xnode = nullptr; /* Prevent deallocation below. */
node->addr = chunk;
node->size = size;
node->zeroed = (unzeroed == false);
node->chunk_type = chunk_type;
extent_tree_ad_insert(chunks_ad, node);
extent_tree_szad_insert(chunks_szad, node);
}
@ -2189,7 +2156,9 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
extent_tree_szad_remove(chunks_szad, node);
node->addr = prev->addr;
node->size += prev->size;
node->zeroed = (node->zeroed && prev->zeroed);
if (node->chunk_type != prev->chunk_type) {
node->chunk_type = RECYCLED_CHUNK;
}
extent_tree_szad_insert(chunks_szad, node);
xprev = prev;
@ -2222,7 +2191,7 @@ chunk_dalloc_mmap(void *chunk, size_t size)
#undef CAN_RECYCLE
static void
chunk_dealloc(void *chunk, size_t size, enum ChunkType type)
chunk_dealloc(void *chunk, size_t size, ChunkType type)
{
MOZ_ASSERT(chunk);
@ -2601,7 +2570,6 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large,
# ifdef MALLOC_DECOMMIT
pages_commit((void *)((uintptr_t)chunk + ((run_ind + i)
<< pagesize_2pow)), (j << pagesize_2pow));
arena->stats.ncommit++;
# endif
arena->stats.committed += j;
@ -2653,9 +2621,21 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large,
}
static void
arena_chunk_init(arena_t *arena, arena_chunk_t *chunk)
arena_chunk_init(arena_t *arena, arena_chunk_t *chunk, bool zeroed)
{
size_t i;
/* WARNING: The following relies on !zeroed meaning "used to be an arena
* chunk".
* When the chunk we're initializating as an arena chunk is zeroed, we
* mark all runs are decommitted and zeroed.
* When it is not, which we can assume means it's a recycled arena chunk,
* all it can contain is an arena chunk header (which we're overwriting),
* and zeroed or poisoned memory (because a recycled arena chunk will
* have been emptied before being recycled). In that case, we can get
* away with reusing the chunk as-is, marking all runs as madvised.
*/
size_t flags = zeroed ? CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED
: CHUNK_MAP_MADVISED;
arena->stats.mapped += chunksize;
@ -2674,11 +2654,11 @@ arena_chunk_init(arena_t *arena, arena_chunk_t *chunk)
for (i = 0; i < arena_chunk_header_npages; i++)
chunk->map[i].bits = 0;
chunk->map[i].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED;
chunk->map[i].bits = arena_maxclass | flags;
for (i++; i < chunk_npages-1; i++) {
chunk->map[i].bits = CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED;
chunk->map[i].bits = flags;
}
chunk->map[chunk_npages-1].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED;
chunk->map[chunk_npages-1].bits = arena_maxclass | flags;
#ifdef MALLOC_DECOMMIT
/*
@ -2686,8 +2666,6 @@ arena_chunk_init(arena_t *arena, arena_chunk_t *chunk)
* between dirty pages and committed untouched pages.
*/
pages_decommit(run, arena_maxclass);
arena->stats.ndecommit++;
arena->stats.decommitted += (chunk_npages - arena_chunk_header_npages);
#endif
arena->stats.committed += arena_chunk_header_npages;
@ -2777,12 +2755,13 @@ arena_run_alloc(arena_t *arena, arena_bin_t *bin, size_t size, bool large,
* the run.
*/
{
bool zeroed;
arena_chunk_t *chunk = (arena_chunk_t *)
chunk_alloc(chunksize, chunksize, false, true);
chunk_alloc(chunksize, chunksize, false, &zeroed);
if (!chunk)
return nullptr;
arena_chunk_init(arena, chunk);
arena_chunk_init(arena, chunk, zeroed);
run = (arena_run_t *)((uintptr_t)chunk +
(arena_chunk_header_npages << pagesize_2pow));
}
@ -2808,8 +2787,6 @@ arena_purge(arena_t *arena, bool all)
#endif
MOZ_DIAGNOSTIC_ASSERT(all || (arena->ndirty > opt_dirty_max));
arena->stats.npurge++;
/*
* Iterate downward through chunks until enough dirty memory has been
* purged. Terminate as soon as possible in order to minimize the
@ -2852,8 +2829,6 @@ arena_purge(arena_t *arena, bool all)
pages_decommit((void *)((uintptr_t)
chunk + (i << pagesize_2pow)),
(npages << pagesize_2pow));
arena->stats.ndecommit++;
arena->stats.decommitted += npages;
#endif
arena->stats.committed -= npages;
@ -2865,8 +2840,6 @@ arena_purge(arena_t *arena, bool all)
madvised = true;
# endif
#endif
arena->stats.nmadvise++;
arena->stats.purged += npages;
if (arena->ndirty <= (dirty_max >> 1))
break;
}
@ -3051,7 +3024,6 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
/* run is guaranteed to have available space. */
arena_run_tree_remove(&bin->runs, mapelm);
run = (arena_run_t *)(mapelm->bits & ~pagesize_mask);
bin->stats.reruns++;
return (run);
}
/* No existing runs have any space available. */
@ -3088,10 +3060,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
run->magic = ARENA_RUN_MAGIC;
#endif
bin->stats.nruns++;
bin->stats.curruns++;
if (bin->stats.curruns > bin->stats.highruns)
bin->stats.highruns = bin->stats.curruns;
return (run);
}
@ -3250,8 +3219,6 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
return nullptr;
}
bin->stats.nrequests++;
arena->stats.nmalloc_small++;
arena->stats.allocated_small += size;
malloc_spin_unlock(&arena->lock);
@ -3279,7 +3246,6 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero)
malloc_spin_unlock(&arena->lock);
return nullptr;
}
arena->stats.nmalloc_large++;
arena->stats.allocated_large += size;
malloc_spin_unlock(&arena->lock);
@ -3374,7 +3340,6 @@ arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size)
}
}
arena->stats.nmalloc_large++;
arena->stats.allocated_large += size;
malloc_spin_unlock(&arena->lock);
@ -3660,7 +3625,6 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
}
}
arena->stats.allocated_small -= size;
arena->stats.ndalloc_small++;
}
static void
@ -3672,7 +3636,6 @@ arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr)
memset(ptr, kAllocPoison, size);
arena->stats.allocated_large -= size;
arena->stats.ndalloc_large++;
arena_run_dalloc(arena, (arena_run_t *)ptr, true);
}
@ -3976,7 +3939,7 @@ arenas_fallback()
* In practice, this is an extremely unlikely failure.
*/
_malloc_message(_getprogname(),
": (malloc) Error initializing arena\n", "", "");
": (malloc) Error initializing arena\n");
if (opt_abort)
moz_abort();
@ -4053,6 +4016,7 @@ huge_palloc(size_t size, size_t alignment, bool zero)
size_t csize;
size_t psize;
extent_node_t *node;
bool zeroed;
/* Allocate one or more contiguous chunks for this request. */
@ -4067,11 +4031,14 @@ huge_palloc(size_t size, size_t alignment, bool zero)
if (!node)
return nullptr;
ret = chunk_alloc(csize, alignment, false, zero);
ret = chunk_alloc(csize, alignment, false, &zeroed);
if (!ret) {
base_node_dealloc(node);
return nullptr;
}
if (zero) {
chunk_ensure_zero(ret, csize, zeroed);
}
/* Insert node into huge. */
node->addr = ret;
@ -4314,8 +4281,7 @@ malloc_init_hard(void)
#ifdef MALLOC_STATIC_SIZES
if (pagesize % (size_t) result) {
_malloc_message(_getprogname(),
"Compile-time page size does not divide the runtime one.\n",
"", "");
"Compile-time page size does not divide the runtime one.\n");
moz_abort();
}
#else

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

@ -578,6 +578,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
// fall through
case BOOKMARKS: {
trace("Deleting bookmarks: " + uri);
// Since we touch multiple records for most deletions, 'deleted' here really means 'affected'.
deleted = deleteBookmarks(uri, selection, selectionArgs);
deleteUnusedImages(uri);
break;
@ -1679,7 +1680,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
beginWrite(db);
final int updated = db.update(TABLE_BOOKMARKS, values, inClause, null);
int updated = db.update(TABLE_BOOKMARKS, values, inClause, null);
if (updated == 0) {
trace("No update on URI: " + uri);
return updated;
@ -1702,7 +1703,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
parentValues.put(Bookmarks.DATE_MODIFIED, lastModified);
// Bump old/new parent's lastModified timestamps.
db.update(TABLE_BOOKMARKS, parentValues,
updated += db.update(TABLE_BOOKMARKS, parentValues,
Bookmarks._ID + " in (?, ?)",
new String[] { String.valueOf(oldParentId), String.valueOf(newParentId) });
@ -2289,28 +2290,27 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
debug("Marking bookmarks as deleted for URI: " + uri);
// Bump parent's lastModified timestamp before record deleted.
// Deletions of bookmarks almost always affect more than one record, and so we keep track of
// number of records changed as opposed to number of records deleted. It's highly unusual,
// but not impossible, for a "delete" operation to modify some of the records without
// actually marking any as "deleted".
// We do this to ensure we correctly fire 'notifyChanged' events whenever > 0 records are touched.
int changed = 0;
// First, bump parents' lastModified timestamp. Running this 'update' query first ensures our
// transaction will start off as a 'writer'. Deletion code below performs a SELECT first,
// requiring transaction to be upgraded from a reader to a writer, which might result in SQL_BUSY.
// NB: this code allows for multi-parent bookmarks.
final ContentValues parentValues = new ContentValues();
parentValues.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
updateBookmarkParents(db, parentValues, selection, selectionArgs);
changed += updateBookmarkParents(db, parentValues, selection, selectionArgs);
final ContentValues values = new ContentValues();
values.put(Bookmarks.IS_DELETED, 1);
values.put(Bookmarks.POSITION, 0);
// updateBookmarks() and getBookmarkDescendantGUIDs() both use use selection as a SQL filter,
// so we don't set column to null here, or the filtering result might be different because of record changed.
// We move all values.putNull() operations into bulkDeleteByBookmarkGUIDs().
// Doing this UPDATE (or the DELETE above) first ensures that the
// first operation within this transaction is a write.
// The cleanup call below will do a SELECT first, and thus would
// require the transaction to be upgraded from a reader to a writer.
updateBookmarks(uri, values, selection, selectionArgs);
// When deleting bookmarks/folders, we have to delete their all descendant bookmarks/folders as well.
// We concatenate all IDs into a clause, and delete them at once.
// Finally, deleted everything that needs to be deleted all at once.
// We need to compute list of all bookmarks and their descendants first.
// We calculate our deletion tree based on 'selection', and so above queries do not null-out
// any of the bookmark fields. This will be done in `bulkDeleteByBookmarkGUIDs`.
final List<String> guids = getBookmarkDescendantGUIDs(db, selection, selectionArgs);
final int updated = bulkDeleteByBookmarkGUIDs(db, guids, TABLE_BOOKMARKS, Bookmarks.GUID);
changed += bulkDeleteByBookmarkGUIDs(db, guids, TABLE_BOOKMARKS, Bookmarks.GUID);
try {
cleanUpSomeDeletedRecords(uri, TABLE_BOOKMARKS);
@ -2318,7 +2318,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
// We don't care.
Log.e(LOGTAG, "Unable to clean up deleted bookmark records: ", e);
}
return updated;
return changed;
}
/**

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

@ -18,7 +18,12 @@ import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers;
import org.robolectric.shadows.ShadowContentResolver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@ -32,7 +37,6 @@ public class BrowserProviderBookmarksTest {
private static final long INVALID_ID = -1;
private ShadowContentResolver contentResolver;
private ContentProviderClient bookmarksClient;
private Uri bookmarksTestUri;
private BrowserProvider provider;
@ -43,7 +47,7 @@ public class BrowserProviderBookmarksTest {
provider.onCreate();
ShadowContentResolver.registerProvider(BrowserContract.AUTHORITY, new DelegatingTestContentProvider(provider));
contentResolver = new ShadowContentResolver();
ShadowContentResolver contentResolver = new ShadowContentResolver();
bookmarksClient = contentResolver.acquireContentProviderClient(BrowserContractHelpers.BOOKMARKS_CONTENT_URI);
bookmarksTestUri = testUri(BrowserContract.Bookmarks.CONTENT_URI);
@ -55,6 +59,88 @@ public class BrowserProviderBookmarksTest {
provider.shutdown();
}
@Test
public void testBookmarkChangedCountsOnUpdates() throws RemoteException {
final long rootId = getBookmarkIdFromGuid(BrowserContract.Bookmarks.MOBILE_FOLDER_GUID);
insertBookmark("bookmark-1", "https://www.mozilla-1.org", "guid-1",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
ContentValues values = new ContentValues();
values.put(BrowserContract.Bookmarks.TITLE, "bookmark-1-2");
int changed = bookmarksClient.update(
bookmarksTestUri, values, BrowserContract.Bookmarks.GUID + " = ?",
new String[] { "guid-1" });
// Only record itself is changed.
assertEquals(1, changed);
// Test that changing a parent of a record affects three records - record itself, new parent, old parent.
insertBookmark("bookmark-1", null, "parent", rootId, BrowserContract.Bookmarks.TYPE_FOLDER);
long parentId = getBookmarkIdFromGuid("parent");
values = new ContentValues();
values.put(BrowserContract.Bookmarks.PARENT, parentId);
changed = bookmarksClient.update(
bookmarksTestUri.buildUpon()
.appendQueryParameter(
BrowserContract.PARAM_OLD_BOOKMARK_PARENT,
String.valueOf(rootId)
).build(),
values,
BrowserContract.Bookmarks.GUID + " = ?",
new String[] { "guid-1" }
);
assertEquals(3, changed);
}
@Test
public void testBookmarkChangedCountsOnDeletions() throws RemoteException {
final long rootId = getBookmarkIdFromGuid(BrowserContract.Bookmarks.MOBILE_FOLDER_GUID);
insertBookmark("bookmark-1", "https://www.mozilla-1.org", "guid-1",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
int changed = bookmarksClient.delete(bookmarksTestUri,
BrowserContract.Bookmarks.URL + " = ?",
new String[] { "https://www.mozilla-1.org" });
// Note that we also modify parent folder, and so changed counts must reflect that.
assertEquals(2, changed);
insertBookmark("bookmark-2", "https://www.mozilla-2.org", "guid-2",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
insertBookmark("bookmark-3", "https://www.mozilla-3.org", "guid-3",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
insertBookmark("bookmark-4", "https://www.mozilla-4.org", "guid-4",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
changed = bookmarksClient.delete(bookmarksTestUri,
BrowserContract.Bookmarks.PARENT + " = ?",
new String[] { String.valueOf(rootId) });
assertEquals(4, changed);
// Test that we correctly count affected records during deletions of subtrees.
final Uri parentUri = insertBookmark("parent", null, "parent", rootId,
BrowserContract.Bookmarks.TYPE_FOLDER);
final long parentId = Long.parseLong(parentUri.getLastPathSegment());
insertBookmark("bookmark-5", "https://www.mozilla-2.org", "guid-5",
parentId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
insertBookmark("bookmark-6", "https://www.mozilla-3.org", "guid-6",
parentId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
// Directly under the root.
insertBookmark("bookmark-7", "https://www.mozilla-4.org", "guid-7",
rootId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
changed = bookmarksClient.delete(bookmarksTestUri,
BrowserContract.Bookmarks.GUID + " = ?",
new String[] { "parent" });
// 4 = parent, its two children, mobile root.
assertEquals(4, changed);
}
@Test
public void testDeleteBookmarkFolder() throws RemoteException {
final long rootId = getBookmarkIdFromGuid(BrowserContract.Bookmarks.MOBILE_FOLDER_GUID);
@ -64,7 +150,7 @@ public class BrowserProviderBookmarksTest {
BrowserContract.Bookmarks.TYPE_FOLDER);
final long parentId = Long.parseLong(parentUri.getLastPathSegment());
// Insert 10 bookmarks into the parent
// Insert 10 bookmarks into the parent.
for (int i = 0; i < 10; i++) {
insertBookmark("bookmark-" + i, "https://www.mozilla-" + i + ".org", "guid-" + i,
parentId, BrowserContract.Bookmarks.TYPE_BOOKMARK);
@ -74,15 +160,16 @@ public class BrowserProviderBookmarksTest {
BrowserContract.Bookmarks.PARENT + " = ?";
final String[] selectionArgs = { String.valueOf(parentId),
String.valueOf(parentId) };
// Check insertions. We should have 11 records(1 parent and 10 children).
// Check insertions. We should have 11 records (1 parent and 10 children).
final int inserted = getRowCount(selection, selectionArgs);
assertEquals(11, inserted);
final int deleted = bookmarksClient.delete(bookmarksTestUri,
final int changed = bookmarksClient.delete(bookmarksTestUri,
BrowserContract.Bookmarks._ID + " = ?",
new String[] { String.valueOf(parentId) });
// Check if parent and all its descendants are deleted.
assertEquals(11, deleted);
// We also modify parent's parent, and so that is counted as well.
assertEquals(12, changed);
// Check records are marked as deleted and column are reset.
final StringBuilder sb = new StringBuilder(BrowserContract.Bookmarks._ID + " = ? OR ");
@ -103,11 +190,19 @@ public class BrowserProviderBookmarksTest {
where, new String[] { String.valueOf(parentId) }, null);
assertNotNull(cursor);
assertEquals(11, cursor.getCount());
List<String> guids = Arrays.asList("parent", "guid-0", "guid-1", "guid-2", "guid-3", "guid-4", "guid-5", "guid-6", "guid-7", "guid-8", "guid-9");
List<String> seenGuids = new ArrayList<>();
try {
while (cursor.moveToNext()) {
final int isDeleted = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.Bookmarks.IS_DELETED));
assertEquals(1, isDeleted);
final String guid = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Bookmarks.GUID));
// Don't depend on iteration order while checking that every guid is correctly preserved.
assertTrue(guids.contains(guid));
assertFalse(seenGuids.contains(guid));
seenGuids.add(guid);
final int position = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.Bookmarks.POSITION));
assertEquals(0, position);

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

@ -656,11 +656,12 @@ public class testBrowserProvider extends ContentProviderTest {
public void test() throws Exception {
long id = insertOneBookmark();
int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
int changed = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
BrowserContract.Bookmarks._ID + " = ?",
new String[] { String.valueOf(id) });
mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
// Deletions also affect parents of folders, and so that must be accounted for.
mAsserter.is((changed == 2), true, "Inserted bookmark was deleted");
Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted");
@ -686,11 +687,12 @@ public class testBrowserProvider extends ContentProviderTest {
"Deleted bookmark GUID is not null");
c.close();
deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
changed = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
BrowserContract.Bookmarks._ID + " = ?",
new String[] { String.valueOf(id) });
mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
// Deletions from sync skip bumping timestamps of parents.
mAsserter.is((changed == 1), true, "Inserted bookmark was deleted");
c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted");
@ -698,8 +700,8 @@ public class testBrowserProvider extends ContentProviderTest {
id = insertOneBookmark();
deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
mAsserter.is((deleted == 1), true,
changed = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
mAsserter.is((changed == 2), true,
"Inserted bookmark was deleted using URI with id");
c = getBookmarkById(id);
@ -724,13 +726,13 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
c.close();
deleted = 0;
changed = 0;
try {
Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, parentId);
deleted = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
changed = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
} catch(Exception e) {}
mAsserter.is((deleted == 0), true,
mAsserter.is((changed == 0), true,
"Should not be able to delete folder that causes orphan bookmarks");
}
}
@ -1802,11 +1804,12 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), combinedBookmarkId,
"Bookmark id should be set correctly on combined entry");
int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
int changed = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
BrowserContract.Bookmarks._ID + " = ?",
new String[] { String.valueOf(combinedBookmarkId) });
mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted");
// Deletion of a bookmark also affects its parent, and that must be reflected in the count.
mAsserter.is((changed == 2), true, "Inserted combined bookmark was deleted");
c.close();
c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);

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

@ -56,11 +56,14 @@ class OptionValue(tuple):
def __eq__(self, other):
# This is to catch naive comparisons against strings and other
# types in moz.configure files, as it is really easy to write
# value == 'foo'.
if not isinstance(other, tuple):
raise TypeError('cannot compare a %s against an %s; OptionValue '
'instances are tuples - did you mean to compare '
'against member elements using [x]?' % (
# value == 'foo'. We only raise a TypeError for instances that
# have content, because value-less instances (like PositiveOptionValue
# and NegativeOptionValue) are common and it is trivial to
# compare these.
if not isinstance(other, tuple) and len(self):
raise TypeError('cannot compare a populated %s against an %s; '
'OptionValue instances are tuples - did you mean to '
'compare against member elements using [x]?' % (
type(other).__name__, type(self).__name__))
# Allow explicit tuples to be compared.

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

@ -297,6 +297,20 @@ class TestOption(unittest.TestCase):
with self.assertRaisesRegexp(TypeError, 'cannot compare a'):
val == 'foo'
# But we allow empty option values to compare otherwise we can't
# easily compare value-less types like PositiveOptionValue and
# NegativeOptionValue.
empty_positive = PositiveOptionValue()
empty_negative = NegativeOptionValue()
self.assertEqual(empty_positive, ())
self.assertEqual(empty_positive, PositiveOptionValue())
self.assertEqual(empty_negative, ())
self.assertEqual(empty_negative, NegativeOptionValue())
self.assertNotEqual(empty_positive, 'foo')
self.assertNotEqual(empty_positive, ('foo',))
self.assertNotEqual(empty_negative, 'foo')
self.assertNotEqual(empty_negative, ('foo',))
def test_option_value_format(self):
val = PositiveOptionValue()
self.assertEquals('--with-value', val.format('--with-value'))

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

@ -275,7 +275,7 @@ static const char contentSandboxRules[] = R"(
; bug 1303987
(if (string? debugWriteDir)
(allow file-write* (subpath debugWriteDir)))
(allow file-write-create file-write-data (subpath debugWriteDir)))
; bug 1324610
(allow network-outbound file-read*
@ -359,7 +359,7 @@ static const char contentSandboxRules[] = R"(
(iokit-user-client-class "Gen6DVDContext"))
; bug 1237847
(allow file-read* file-write*
(allow file-read* file-write-create file-write-data
(subpath appTempDir))
)";

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

@ -215,7 +215,15 @@ async function createTempFile() {
ok(fileCreated == true, "creating a file in content temp is permitted");
// now delete the file
let fileDeleted = await ContentTask.spawn(browser, path, deleteFile);
ok(fileDeleted == true, "deleting a file in content temp is permitted");
if (isMac()) {
// On macOS we do not allow file deletion - it is not needed by the content
// process itself, and macOS uses a different permission to control access
// to revoking it is easy.
ok(fileDeleted == false,
"deleting a file in the content temp is not permitted");
} else {
ok(fileDeleted == true, "deleting a file in content temp is permitted");
}
}
// Test reading files and dirs from web and file content processes.

26
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -325,7 +325,7 @@ version = "0.0.1"
dependencies = [
"azure 0.19.0 (git+https://github.com/servo/rust-azure)",
"canvas_traits 0.0.1",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -340,7 +340,7 @@ dependencies = [
name = "canvas_traits"
version = "0.0.1"
dependencies = [
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -585,7 +585,7 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.16.1"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1017,7 +1017,7 @@ name = "geckoservo"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1083,7 +1083,7 @@ dependencies = [
name = "gfx_tests"
version = "0.0.1"
dependencies = [
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.0.1",
"ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
@ -2418,7 +2418,7 @@ dependencies = [
"caseless 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"deny_public_fields 0.0.1",
"devtools_traits 0.0.1",
"dom_struct 0.0.1",
@ -2491,7 +2491,7 @@ dependencies = [
"app_units 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"canvas_traits 0.0.1",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2562,7 +2562,7 @@ name = "selectors"
version = "0.19.0"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2954,7 +2954,7 @@ dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3007,7 +3007,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"html5ever 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3028,7 +3028,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3041,7 +3041,7 @@ name = "stylo_tests"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"geckoservo 0.0.1",
@ -3624,7 +3624,7 @@ dependencies = [
"checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624"
"checksum core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a9f841e9637adec70838c537cae52cb4c751cc6514ad05669b51d107c2021c79"
"checksum core-text 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74ba2a7abdccb94fb6c00822addef48504182b285aa45a30e78286487888fcb4"
"checksum cssparser 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2842253baded8e712e9d8d80ebfe5ea8e95c5b27071e6a6db6080ca1e81c07d1"
"checksum cssparser 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7063452c60432cb306ed54d538178c20792d47fa960c240ce6c083239ee55ec"
"checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
"checksum dbus 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4aee01fb76ada3e5e7ca642ea6664ebf7308a810739ca2aca44909a1191ac254"

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

@ -12,7 +12,7 @@ path = "lib.rs"
[dependencies]
azure = {git = "https://github.com/servo/rust-azure"}
canvas_traits = {path = "../canvas_traits"}
cssparser = "0.16.1"
cssparser = "0.17.0"
euclid = "0.15"
gleam = "0.4"
ipc-channel = "0.8"

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

@ -10,7 +10,7 @@ name = "canvas_traits"
path = "lib.rs"
[dependencies]
cssparser = "0.16.1"
cssparser = "0.17.0"
euclid = "0.15"
heapsize = "0.4"
heapsize_derive = "0.1"

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

@ -219,9 +219,6 @@ pub struct LayoutThread {
/// All the other elements of this struct are read-only.
rw_data: Arc<Mutex<LayoutThreadData>>,
/// The CSS error reporter for all CSS loaded in this layout thread
error_reporter: CSSErrorReporter,
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder),
WebRenderImageInfo>>>,
/// The executor for paint worklets.
@ -532,10 +529,6 @@ impl LayoutThread {
text_index_response: TextIndexResponse(None),
nodes_from_point_response: vec![],
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
script_chan: Arc::new(Mutex::new(script_chan)),
},
webrender_image_cache:
Arc::new(RwLock::new(FnvHashMap::default())),
timer:
@ -580,7 +573,6 @@ impl LayoutThread {
guards: guards,
running_animations: self.running_animations.clone(),
expired_animations: self.expired_animations.clone(),
error_reporter: &self.error_reporter,
local_context_creation_data: Mutex::new(thread_local_style_context_creation_data),
timer: self.timer.clone(),
quirks_mode: self.quirks_mode.unwrap(),

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

@ -35,7 +35,7 @@ byteorder = "1.0"
canvas_traits = {path = "../canvas_traits"}
caseless = "0.1.0"
cookie = "0.6"
cssparser = "0.16.1"
cssparser = "0.17.0"
deny_public_fields = {path = "../deny_public_fields"}
devtools_traits = {path = "../devtools_traits"}
dom_struct = {path = "../dom_struct"}

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

@ -13,7 +13,7 @@ path = "lib.rs"
app_units = "0.5"
atomic_refcell = "0.1"
canvas_traits = {path = "../canvas_traits"}
cssparser = "0.16.1"
cssparser = "0.17.0"
euclid = "0.15"
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.4"

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

@ -25,7 +25,7 @@ unstable = []
[dependencies]
bitflags = "0.7"
matches = "0.1"
cssparser = "0.16.1"
cssparser = "0.17.0"
log = "0.3"
fnv = "1.0"
phf = "0.7.18"

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

@ -53,24 +53,25 @@ impl ElementSelectorFlags {
}
}
/// Holds per-element data alongside a pointer to MatchingContext.
/// Holds per-selector data alongside a pointer to MatchingContext.
pub struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
/// Shared `MatchingContext`.
pub shared: &'a mut MatchingContext<'b>,
/// A reference to the base selector we're matching against.
pub selector: &'a Selector<Impl>,
/// The offset of the current compound selector being matched, kept up to date by
/// the callees when the iterator is advanced. This, in conjunction with the selector
/// reference above, allows callees to synthesize an iterator for the current compound
/// selector on-demand. This is necessary because the primary iterator may already have
/// been advanced partway through the current compound selector, and the callee may need
/// the whole thing.
/// The offset of the current compound selector being matched, kept up to
/// date by the callees when the iterator is advanced. This, in conjunction
/// with the selector reference above, allows callees to synthesize an
/// iterator for the current compound selector on-demand. This is necessary
/// because the primary iterator may already have been advanced partway
/// through the current compound selector, and the callee may need the whole
/// thing.
offset: usize,
/// The level of nesting for the selector being matched.
pub nesting_level: usize,
/// Holds a bool flag to see whether :active and :hover quirk should try to
/// match or not. This flag can only be true in these two cases:
/// - LocalMatchingContext is currently within a functional pseudo class
/// like `:-moz-any` or `:not`.
/// - PseudoElements are encountered when matching mode is ForStatelessPseudoElement.
/// match or not. This flag can only be true in the case PseudoElements are
/// encountered when matching mode is ForStatelessPseudoElement.
pub hover_active_quirk_disabled: bool,
}
@ -84,6 +85,7 @@ impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl>
shared: shared,
selector: selector,
offset: 0,
nesting_level: 0,
// We flip this off once third sequence is reached.
hover_active_quirk_disabled: selector.has_pseudo_element(),
}
@ -107,8 +109,16 @@ impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl>
/// Returns true if current compound selector matches :active and :hover quirk.
/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk
pub fn active_hover_quirk_matches(&mut self) -> bool {
if self.shared.quirks_mode() != QuirksMode::Quirks ||
self.hover_active_quirk_disabled {
if self.shared.quirks_mode() != QuirksMode::Quirks {
return false;
}
// Don't allow it in recursive selectors such as :not and :-moz-any.
if self.nesting_level != 0 {
return false;
}
if self.hover_active_quirk_disabled {
return false;
}
@ -453,7 +463,8 @@ pub fn matches_complex_selector<E, F>(mut iter: SelectorIter<E::Impl>,
F: FnMut(&E, ElementSelectorFlags),
{
if cfg!(debug_assertions) {
if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
if context.nesting_level == 0 &&
context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
assert!(iter.clone().any(|c| {
matches!(*c, Component::PseudoElement(..))
}));
@ -462,7 +473,8 @@ pub fn matches_complex_selector<E, F>(mut iter: SelectorIter<E::Impl>,
// If this is the special pseudo-element mode, consume the ::pseudo-element
// before proceeding, since the caller has already handled that part.
if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
if context.nesting_level == 0 &&
context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
// Consume the pseudo.
let pseudo = iter.next().unwrap();
debug_assert!(matches!(*pseudo, Component::PseudoElement(..)),
@ -733,13 +745,12 @@ fn matches_simple_selector<E, F>(
matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
}
Component::Negation(ref negated) => {
let old_value = context.hover_active_quirk_disabled;
context.hover_active_quirk_disabled = true;
context.nesting_level += 1;
let result = !negated.iter().all(|ss| {
matches_simple_selector(ss, element, context,
relevant_link, flags_setter)
});
context.hover_active_quirk_disabled = old_value;
context.nesting_level -= 1;
result
}
}

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

@ -59,7 +59,7 @@ pub enum SelectorParseError<'i, T> {
PseudoElementExpectedIdent,
UnsupportedPseudoClass,
UnexpectedIdent(CompactCowStr<'i>),
ExpectedNamespace,
ExpectedNamespace(CompactCowStr<'i>),
Custom(T),
}
@ -1105,9 +1105,10 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
let position = input.position();
match input.next_including_whitespace() {
Ok(Token::Delim('|')) => {
let prefix = from_cow_str(value.into());
let prefix = from_cow_str(value.clone().into());
let result = parser.namespace_for_prefix(&prefix);
let url = result.ok_or(ParseError::Custom(SelectorParseError::ExpectedNamespace))?;
let url = result.ok_or(ParseError::Custom(
SelectorParseError::ExpectedNamespace(value.into())))?;
explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
},
_ => {

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше