diff --git a/.cargo/config.in b/.cargo/config.in
index d60f3c36cc9c..7fa81e05b905 100644
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -9,7 +9,7 @@ replace-with = 'vendored-sources'
[source."https://github.com/gankro/serde"]
git = "https://github.com/gankro/serde"
-branch = "deserialize_from_enums3"
+branch = "deserialize_from_enums4"
replace-with = "vendored-sources"
[source.vendored-sources]
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index 00b907106880..ca5cb32cdd01 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1114,11 +1114,6 @@ BrowserGlue.prototype = {
// early, so we use a maximum timeout for it.
Services.tm.idleDispatchToMainThread(() => {
SafeBrowsing.init();
-
- // Login reputation depends on the Safe Browsing API.
- let reputationService = Cc["@mozilla.org/reputationservice/login-reputation-service;1"]
- .getService(Ci.nsILoginReputationService);
- reputationService.init();
}, 5000);
if (AppConstants.MOZ_CRASHREPORTER) {
diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_addFolderDefaultButton.js b/browser/components/places/tests/browser/browser_bookmarkProperties_addFolderDefaultButton.js
index feaca6209019..880b78646c8f 100644
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_addFolderDefaultButton.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addFolderDefaultButton.js
@@ -17,7 +17,7 @@ add_task(async function() {
await withSidebarTree("bookmarks", async function(tree) {
// Select the new bookmark in the sidebar.
tree.selectItems([newBookmark.guid]);
- ok(tree.controller.isCommandEnabled("placesCmd_new:folder"),
+ Assert.ok(tree.controller.isCommandEnabled("placesCmd_new:folder"),
"'placesCmd_new:folder' on current selected node is enabled");
// Create a new folder. Since the new bookmark is selected, and new items
diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js b/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
index 812baa73c2bd..0e93e7de92ce 100644
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
@@ -18,14 +18,14 @@ add_task(async function() {
await withBookmarksDialog(true, AddKeywordForSearchField, async function(dialogWin) {
let acceptBtn = dialogWin.document.documentElement.getButton("accept");
- ok(acceptBtn.disabled, "Accept button is disabled");
+ Assert.ok(acceptBtn.disabled, "Accept button is disabled");
let promiseKeywordNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "keyword" && val == "kw");
fillBookmarkTextField("editBMPanel_keywordField", "kw", dialogWin);
- ok(!acceptBtn.disabled, "Accept button is enabled");
+ Assert.ok(!acceptBtn.disabled, "Accept button is enabled");
// The dialog is instant apply.
await promiseKeywordNotification;
@@ -37,18 +37,18 @@ add_task(async function() {
entry = await PlacesUtils.keywords.fetch("kw");
return !!entry;
}, "Unable to find the expected keyword");
- is(entry.keyword, "kw", "keyword is correct");
- is(entry.url.href, TEST_URL, "URL is correct");
- is(entry.postData, "accenti%3D%E0%E8%EC%F2%F9&search%3D%25s", "POST data is correct");
+ Assert.equal(entry.keyword, "kw", "keyword is correct");
+ Assert.equal(entry.url.href, TEST_URL, "URL is correct");
+ Assert.equal(entry.postData, "accenti%3D%E0%E8%EC%F2%F9&search%3D%25s", "POST data is correct");
info("Check the charset has been saved");
let charset = await PlacesUtils.getCharsetForURI(NetUtil.newURI(TEST_URL));
- is(charset, "windows-1252", "charset is correct");
+ Assert.equal(charset, "windows-1252", "charset is correct");
// Now check getShortcutOrURI.
let data = await getShortcutOrURIAndPostData("kw test");
- is(getPostDataString(data.postData), "accenti=\u00E0\u00E8\u00EC\u00F2\u00F9&search=test", "getShortcutOrURI POST data is correct");
- is(data.url, TEST_URL, "getShortcutOrURI URL is correct");
+ Assert.equal(getPostDataString(data.postData), "accenti=\u00E0\u00E8\u00EC\u00F2\u00F9&search=test", "getShortcutOrURI POST data is correct");
+ Assert.equal(data.url, TEST_URL, "getShortcutOrURI URL is correct");
}, closeHandler);
});
});
diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_addLivemark.js b/browser/components/places/tests/browser/browser_bookmarkProperties_addLivemark.js
index 9af6dbbb9620..b60c6413be21 100644
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_addLivemark.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addLivemark.js
@@ -4,8 +4,7 @@ add_task(async function() {
info("Add a live bookmark editing its data");
await withSidebarTree("bookmarks", async function(tree) {
- let itemId = PlacesUIUtils.leftPaneQueries.UnfiledBookmarks;
- tree.selectItems([itemId]);
+ tree.selectItems([PlacesUtils.bookmarks.unfiledGuid]);
await withBookmarksDialog(
true,
@@ -26,13 +25,13 @@ add_task(async function() {
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
- is(bookmark.title, "modified", "folder name has been edited");
+ Assert.equal(bookmark.title, "modified", "folder name has been edited");
let livemark = await PlacesUtils.livemarks.getLivemark({
guid: bookmark.guid
});
- is(livemark.feedURI.spec, "http://livemark.com/", "livemark has the correct url");
- is(livemark.title, "modified", "livemark has the correct title");
+ Assert.equal(livemark.feedURI.spec, "http://livemark.com/", "livemark has the correct url");
+ Assert.equal(livemark.title, "modified", "livemark has the correct title");
}
);
});
diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js b/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
index 63f87a340362..0ee4e4a07b2a 100644
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
@@ -22,7 +22,7 @@ add_task(async function() {
},
async dialog => {
let acceptBtn = dialog.document.documentElement.getButton("accept");
- ok(!acceptBtn.disabled, "Accept button is enabled");
+ Assert.ok(!acceptBtn.disabled, "Accept button is enabled");
let namepicker = dialog.document.getElementById("editBMPanel_namePicker");
Assert.ok(!namepicker.readOnly, "Name field is writable");
@@ -36,7 +36,7 @@ add_task(async function() {
},
dialog => {
let savedItemId = dialog.gEditItemOverlay.itemId;
- ok(savedItemId > 0, "Found the itemId");
+ Assert.ok(savedItemId > 0, "Found the itemId");
return PlacesTestUtils.waitForNotification("onItemRemoved",
id => id === savedItemId);
}
diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js b/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
index 18d0472eb67a..71a648f5826f 100644
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
@@ -4,10 +4,9 @@ add_task(async function() {
info("Bug 479348 - Properties on a root should be read-only.");
await withSidebarTree("bookmarks", async function(tree) {
- let itemId = PlacesUIUtils.leftPaneQueries.UnfiledBookmarks;
- tree.selectItems([itemId]);
- ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
- "'placesCmd_show:info' on current selected node is enabled");
+ tree.selectItems([PlacesUtils.bookmarks.unfiledGuid]);
+ Assert.ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
+ "'placesCmd_show:info' on current selected node is enabled");
await withBookmarksDialog(
true,
@@ -16,26 +15,24 @@ add_task(async function() {
},
async function test(dialogWin) {
// Check that the dialog is read-only.
- ok(dialogWin.gEditItemOverlay.readOnly, "Dialog is read-only");
+ Assert.ok(dialogWin.gEditItemOverlay.readOnly, "Dialog is read-only");
// Check that accept button is disabled
let acceptButton = dialogWin.document.documentElement.getButton("accept");
- ok(acceptButton.disabled, "Accept button is disabled");
+ Assert.ok(acceptButton.disabled, "Accept button is disabled");
// Check that name picker is read only
let namepicker = dialogWin.document.getElementById("editBMPanel_namePicker");
- ok(namepicker.readOnly, "Name field is read-only");
- is(namepicker.value,
- PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
- "Node title is correct");
+ Assert.ok(namepicker.readOnly, "Name field is read-only");
+ let bookmark = await PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.unfiledGuid);
+ Assert.equal(namepicker.value, bookmark.title, "Node title is correct");
// Blur the field and ensure root's name has not been changed.
namepicker.blur();
- is(namepicker.value,
- PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
- "Root title is correct");
+ bookmark = await PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.unfiledGuid);
+ Assert.equal(namepicker.value, bookmark.title, "Root title is correct");
// Check the shortcut's title.
- let bookmark = await PlacesUtils.bookmarks.fetch(tree.selectedNode.bookmarkGuid);
- is(bookmark.title, "",
- "Shortcut title is null");
+ info(tree.selectedNode.bookmarkGuid);
+ bookmark = await PlacesUtils.bookmarks.fetch(tree.selectedNode.bookmarkGuid);
+ Assert.equal(bookmark.title, "", "Shortcut title is null");
}
);
});
diff --git a/browser/extensions/activity-stream/css/activity-stream-linux.css b/browser/extensions/activity-stream/css/activity-stream-linux.css
index 03192a28a0e3..54edcb10fc0a 100644
--- a/browser/extensions/activity-stream/css/activity-stream-linux.css
+++ b/browser/extensions/activity-stream/css/activity-stream-linux.css
@@ -407,6 +407,8 @@ main {
white-space: nowrap; }
.top-sites-list .top-site-outer .title.pinned span {
padding: 0 13px; }
+ .top-sites-list .top-site-outer .edit-button {
+ background-image: url("../data/content/assets/glyph-edit-16.svg"); }
.top-sites-list .top-site-outer .edit-menu {
background: #FFF;
border: 1px solid #B1B1B3;
diff --git a/browser/extensions/activity-stream/css/activity-stream-mac.css b/browser/extensions/activity-stream/css/activity-stream-mac.css
index 4f8412e290e6..1f53de25f865 100644
--- a/browser/extensions/activity-stream/css/activity-stream-mac.css
+++ b/browser/extensions/activity-stream/css/activity-stream-mac.css
@@ -407,6 +407,8 @@ main {
white-space: nowrap; }
.top-sites-list .top-site-outer .title.pinned span {
padding: 0 13px; }
+ .top-sites-list .top-site-outer .edit-button {
+ background-image: url("../data/content/assets/glyph-edit-16.svg"); }
.top-sites-list .top-site-outer .edit-menu {
background: #FFF;
border: 1px solid #B1B1B3;
diff --git a/browser/extensions/activity-stream/css/activity-stream-windows.css b/browser/extensions/activity-stream/css/activity-stream-windows.css
index bbacd187592d..9769b10af9bc 100644
--- a/browser/extensions/activity-stream/css/activity-stream-windows.css
+++ b/browser/extensions/activity-stream/css/activity-stream-windows.css
@@ -407,6 +407,8 @@ main {
white-space: nowrap; }
.top-sites-list .top-site-outer .title.pinned span {
padding: 0 13px; }
+ .top-sites-list .top-site-outer .edit-button {
+ background-image: url("../data/content/assets/glyph-edit-16.svg"); }
.top-sites-list .top-site-outer .edit-menu {
background: #FFF;
border: 1px solid #B1B1B3;
diff --git a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
index 71e007e6213b..b6bf653b1fe4 100644
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -2513,13 +2513,10 @@ class TopSite_TopSiteLink extends external__React__default.a.PureComponent {
* Helper to determine whether the drop zone should allow a drop. We only allow
* dropping top sites for now.
*/
- _allowDrop(e, index) {
- let draggedIndex = parseInt(e.dataTransfer.getData("text/topsite-index"), 10);
- if (!isNaN(draggedIndex) && draggedIndex !== index) {
- return true;
- }
- return false;
+ _allowDrop(e) {
+ return e.dataTransfer.types.includes("text/topsite-index");
}
+
onDragEvent(event) {
switch (event.type) {
case "dragstart":
@@ -2531,11 +2528,10 @@ class TopSite_TopSiteLink extends external__React__default.a.PureComponent {
case "dragend":
this.props.onDragEvent(event);
break;
- case "dragover":
case "dragenter":
- case "dragleave":
+ case "dragover":
case "drop":
- if (this._allowDrop(event, this.props.index)) {
+ if (this._allowDrop(event)) {
event.preventDefault();
this.props.onDragEvent(event, this.props.index);
}
@@ -2585,7 +2581,7 @@ class TopSite_TopSiteLink extends external__React__default.a.PureComponent {
}
return external__React__default.a.createElement(
"li",
- _extends({ className: topSiteOuterClassName, key: link.guid || link.url, onDrop: this.onDragEvent, onDragOver: this.onDragEvent, onDragEnter: this.onDragEvent, onDragLeave: this.onDragEvent }, draggableProps),
+ _extends({ className: topSiteOuterClassName, onDrop: this.onDragEvent, onDragOver: this.onDragEvent, onDragEnter: this.onDragEvent, onDragLeave: this.onDragEvent }, draggableProps),
external__React__default.a.createElement(
"div",
{ className: "top-site-inner" },
@@ -2773,14 +2769,9 @@ class TopSite_TopSitePlaceholder extends external__React__default.a.PureComponen
return external__React__default.a.createElement(
TopSite_TopSiteLink,
_extends({ className: "placeholder", isDraggable: false }, this.props),
- external__React__default.a.createElement(
- "div",
- { className: "edit-menu" },
- external__React__default.a.createElement("button", {
- className: "icon icon-edit",
- title: this.props.intl.formatMessage({ id: "edit_topsites_edit_button" }),
- onClick: this.onEditButtonClick })
- )
+ external__React__default.a.createElement("button", { className: "context-menu-button edit-button icon",
+ title: this.props.intl.formatMessage({ id: "edit_topsites_edit_button" }),
+ onClick: this.onEditButtonClick })
);
}
}
@@ -2827,19 +2818,20 @@ class TopSite__TopSiteList extends external__React__default.a.PureComponent {
this.setState(this.DEFAULT_STATE);
break;
case "dragenter":
- this.setState({ topSitesPreview: this._makeTopSitesPreview(index) });
- break;
- case "dragleave":
- this.setState({ topSitesPreview: null });
+ if (index === this.state.draggedIndex) {
+ this.setState({ topSitesPreview: null });
+ } else {
+ this.setState({ topSitesPreview: this._makeTopSitesPreview(index) });
+ }
break;
case "drop":
- this.props.dispatch(Actions["actionCreators"].SendToMain({
- type: Actions["actionTypes"].TOP_SITES_INSERT,
- data: { site: { url: this.state.draggedSite.url, label: this.state.draggedTitle }, index }
- }));
- this.userEvent("DROP", index);
- break;
- default:
+ if (index !== this.state.draggedIndex) {
+ this.props.dispatch(Actions["actionCreators"].SendToMain({
+ type: Actions["actionTypes"].TOP_SITES_INSERT,
+ data: { site: { url: this.state.draggedSite.url, label: this.state.draggedTitle }, index }
+ }));
+ this.userEvent("DROP", index);
+ }
break;
}
}
@@ -2907,10 +2899,15 @@ class TopSite__TopSiteList extends external__React__default.a.PureComponent {
dispatch: props.dispatch,
intl: props.intl
};
+ // We assign a key to each placeholder slot. We need it to be independent
+ // of the slot index (i below) so that the keys used stay the same during
+ // drag and drop reordering and the underlying DOM nodes are reused.
+ // This mostly (only?) affects linux so be sure to test on linux before changing.
+ let holeIndex = 0;
for (let i = 0, l = props.TopSitesCount; i < l; i++) {
const link = topSites[i];
const slotProps = {
- key: i,
+ key: link ? link.url : holeIndex++,
index: i
};
topSitesUI.push(!link ? external__React__default.a.createElement(TopSite_TopSitePlaceholder, _extends({}, slotProps, commonProps)) : external__React__default.a.createElement(TopSite_TopSite, _extends({
diff --git a/browser/extensions/activity-stream/install.rdf.in b/browser/extensions/activity-stream/install.rdf.in
index bd078f5ca248..73528c34d197 100644
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -8,7 +8,7 @@
2
true
false
- 2017.12.22.0055-8fe1055e
+ 2018.01.05.1410-84fd2871
Activity Stream
A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.
true
diff --git a/browser/extensions/activity-stream/prerendered/locales/ach/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ach/activity-stream-prerendered.html
index 80c48fa030a1..77744e666a6e 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ach/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ach/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Yeny kakube Yeny
Kakube maloyo Nong kakube ma ilimo loyo.
Ter me dirica matidi manyen
+ Yeny kakube Yeny
Kakube maloyo Nong kakube ma ilimo loyo.
Ter me dirica matidi manyen
diff --git a/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-prerendered.html
index bb9f30838aa1..c0beeb605ab3 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ar/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ابحث في الوِب ابحث
المواقع الأكثر زيارة وصول للمواقع التي تزورها أكثر.
تفضيلات صفحة اللسان الجديد
+ ابحث في الوِب ابحث
المواقع الأكثر زيارة وصول للمواقع التي تزورها أكثر.
تفضيلات صفحة اللسان الجديد
diff --git a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html
index 5a393fd64848..87875769db04 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Guetar na web Guetar
Más visitaos Acceder a les webs que más visites.
Preferencies de Llingüeta nueva
+ Guetar na web Guetar
Más visitaos Acceder a les webs que más visites.
Preferencies de Llingüeta nueva
diff --git a/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-prerendered.html
index e9a58e42d924..09988b4ab329 100644
--- a/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- İnternetdə Axtar Axtar
Qabaqcıl Saytlar Ən çox ziyarət etdiyiniz saytları görün.
Yeni Vərəq Nizamlamaları
+ İnternetdə Axtar Axtar
Qabaqcıl Saytlar Ən çox ziyarət etdiyiniz saytları görün.
Yeni Vərəq Nizamlamaları
diff --git a/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-strings.js
index eedc408c35ac..424c34696e8f 100644
--- a/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/az/activity-stream-strings.js
@@ -38,9 +38,9 @@ window.gActivityStreamStrings = {
"section_info_option": "Məlumat",
"section_info_send_feedback": "Əks-əlaqə göndər",
"section_info_privacy_notice": "Məxfilik Bildirişi",
- "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
- "section_disclaimer_topstories_linktext": "Learn how it works.",
- "section_disclaimer_topstories_buttontext": "Okay, got it",
+ "section_disclaimer_topstories": "Nələr oxuduğunuza əsasən seçilmiş internetin ən maraqlı hekayələri. Pocket-dən, artıq Mozillanın bir hissəsi.",
+ "section_disclaimer_topstories_linktext": "Necə işlədiyini öyrənin.",
+ "section_disclaimer_topstories_buttontext": "Tamam, başa düşdüm",
"welcome_title": "Yeni vərəqə xoş gəldiniz",
"welcome_body": "Firefox bu səhifədə ən uyğun əlfəcin, məqalə, video və son ziyarət etdiyiniz səhifələri göstərərək onları rahat tapmağınıza kömək edəcək.",
"welcome_label": "Seçilmişləriniz təyin edilir",
@@ -67,7 +67,7 @@ window.gActivityStreamStrings = {
"settings_pane_snippets_header": "Hissələr",
"settings_pane_snippets_body": "Mozilladan Firefox, internet mədəniyyəti və digər yeniliklər haqqında qısa bildirişlər oxuyun.",
"settings_pane_done_button": "Oldu",
- "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+ "settings_pane_topstories_options_sponsored": "Sponsor Hekayələrini Göstər",
"edit_topsites_button_text": "Redaktə et",
"edit_topsites_button_label": "Qabaqcıl Saytlar bölümünüzü fərdiləşdirin",
"edit_topsites_showmore_button": "Daha çox göstər",
diff --git a/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-prerendered.html
index 5f925c1c6f23..3018639bbe57 100644
--- a/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Пошук у Інтэрнэце Шукаць
Папулярныя сайты Доступ да сайтаў, якія вы часцей наведваеце.
Налады новай карткі
+ Пошук у Інтэрнэце Шукаць
Папулярныя сайты Доступ да сайтаў, якія вы часцей наведваеце.
Налады новай карткі
diff --git a/browser/extensions/activity-stream/prerendered/locales/bg/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/bg/activity-stream-prerendered.html
index 7bafae4c9baa..2aea66e62237 100644
--- a/browser/extensions/activity-stream/prerendered/locales/bg/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/bg/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Търсене в интернет Търсене
Най-посещавани Достъп до сайтовете, които посещавате най-често.
Настройки на новия раздел
+ Търсене в интернет Търсене
Най-посещавани Достъп до сайтовете, които посещавате най-често.
Настройки на новия раздел
diff --git a/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-prerendered.html
index 63dc1c4b450b..1bf0f93caf1d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ওয়েবে সন্ধান করুন অনুসন্ধান
শীর্ঘ সাইট আপনি যেসব সাইটে বেশি যান সেসব সাইটে প্রবেশ করুন।
নতুন ট্যাব পছন্দসমূহ
+ ওয়েবে সন্ধান করুন অনুসন্ধান
শীর্ঘ সাইট আপনি যেসব সাইটে বেশি যান সেসব সাইটে প্রবেশ করুন।
নতুন ট্যাব পছন্দসমূহ
diff --git a/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-prerendered.html
index 79b21074aa98..5c382186e4a5 100644
--- a/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ওয়েবে সন্ধান করুন অনুসন্ধান
শীর্ষ সাইটগুলি আপনি যেসব সাইটে বেশি যান সেসব সাইটে প্রবেশ করুন।
নতুন ট্যাব পছন্দসমূহ
+ ওয়েবে সন্ধান করুন অনুসন্ধান
শীর্ষ সাইটগুলি আপনি যেসব সাইটে বেশি যান সেসব সাইটে প্রবেশ করুন।
নতুন ট্যাব পছন্দসমূহ
diff --git a/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-prerendered.html
index 5b654457b30c..c59bd27d13f3 100644
--- a/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/br/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Klask er web Klask
Lec'hiennoù pennañ Kit war al lec'hiennoù gweladennet ar muiañ ganeoc'h.
Gwellvezioù an ivinell nevez
+ Klask er web Klask
Lec'hiennoù pennañ Kit war al lec'hiennoù gweladennet ar muiañ ganeoc'h.
Gwellvezioù an ivinell nevez
diff --git a/browser/extensions/activity-stream/prerendered/locales/bs/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/bs/activity-stream-prerendered.html
index ecb4d7213fef..0b3d74f73a1b 100644
--- a/browser/extensions/activity-stream/prerendered/locales/bs/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/bs/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Pretraži web Traži
Najposjećenije stranice Pristupite stranicama koje najčešće posjećujete.
Postavke novog taba
+ Pretraži web Traži
Najposjećenije stranice Pristupite stranicama koje najčešće posjećujete.
Postavke novog taba
diff --git a/browser/extensions/activity-stream/prerendered/locales/ca/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ca/activity-stream-prerendered.html
index 9c55c12576d9..a9bcfd78f2ee 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ca/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ca/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cerca al web Cerca
Llocs principals Accediu als llocs web que visiteu més sovint.
Preferències de pestanya nova
+ Cerca al web Cerca
Llocs principals Accediu als llocs web que visiteu més sovint.
Preferències de pestanya nova
diff --git a/browser/extensions/activity-stream/prerendered/locales/cak/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/cak/activity-stream-prerendered.html
index c6178a04b7d1..9ba2296fc611 100644
--- a/browser/extensions/activity-stream/prerendered/locales/cak/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/cak/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Tikanöx pa Ajk'amaya'l Tikanöx
Utziläj taq Ruxaq K'amaya'l Katok pa ri taq ajk'amaya'l yalan ye'atz'ët.
K'ak'a' Ruwi' Taq Ajowab'äl
+ Tikanöx pa Ajk'amaya'l Tikanöx
Utziläj taq Ruxaq K'amaya'l Katok pa ri taq ajk'amaya'l yalan ye'atz'ët.
K'ak'a' Ruwi' Taq Ajowab'äl
diff --git a/browser/extensions/activity-stream/prerendered/locales/cs/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/cs/activity-stream-prerendered.html
index b218e5fc8254..8c076a86bb71 100644
--- a/browser/extensions/activity-stream/prerendered/locales/cs/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/cs/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Hledat na webu Hledat
Top stránky Přístup ke stránkám, které nejčastěji navštěvujete.
Předvolby nového panelu
Doporučení ze služby Pocket
+ Hledat na webu Hledat
Top stránky Přístup ke stránkám, které nejčastěji navštěvujete.
Předvolby nového panelu
Doporučení ze služby Pocket
diff --git a/browser/extensions/activity-stream/prerendered/locales/cy/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/cy/activity-stream-prerendered.html
index 3d5cd7a637b4..34c9cceaffe8 100644
--- a/browser/extensions/activity-stream/prerendered/locales/cy/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/cy/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Chwilio'r We Chwilio
Hoff Wefannau Cael mynediad at y gwefannau rydych yn ymweld â nhw amlaf.
Dewisiadau Tab Newydd
+ Chwilio'r We Chwilio
Hoff Wefannau Cael mynediad at y gwefannau rydych yn ymweld â nhw amlaf.
Dewisiadau Tab Newydd
diff --git a/browser/extensions/activity-stream/prerendered/locales/da/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/da/activity-stream-prerendered.html
index 20f8c2eb5449..d514f1ea8567 100644
--- a/browser/extensions/activity-stream/prerendered/locales/da/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/da/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Søg på internettet Søg
Mest besøgte websider Adgang til de websider, du besøger oftest.
Indstillinger for Nyt faneblad
+ Søg på internettet Søg
Mest besøgte websider Adgang til de websider, du besøger oftest.
Indstillinger for Nyt faneblad
diff --git a/browser/extensions/activity-stream/prerendered/locales/de/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/de/activity-stream-prerendered.html
index 867597a9d65c..1d088074d0c1 100644
--- a/browser/extensions/activity-stream/prerendered/locales/de/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/de/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Das Web durchsuchen Suchen
Wichtige Seiten Schneller Zugriff auf Ihre meistbesuchten Websites.
Einstellungen für neue Tabs
+ Das Web durchsuchen Suchen
Wichtige Seiten Schneller Zugriff auf Ihre meistbesuchten Websites.
Einstellungen für neue Tabs
diff --git a/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-prerendered.html
index 6a995e92675e..807c587e2cfb 100644
--- a/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/dsb/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Web pśepytaś Pytaś
Nejcesćej woglědane sedła Wócyńśo websedła, kótarež sćo se nejcesćej woglědał.
Nastajenja nowego rejtarka składowaś
+ Web pśepytaś Pytaś
Nejcesćej woglědane sedła Wócyńśo websedła, kótarež sćo se nejcesćej woglědał.
Nastajenja nowego rejtarka składowaś
diff --git a/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-prerendered.html
index 17240ed0d6a7..34e7d3b92c6e 100644
--- a/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/el/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Αναζήτηση στον ιστό Αναζήτηση
Κορυφαίες ιστοσελίδες Πρόσβαση στις ιστοσελίδες που επισκέπτεστε περισσότερο.
Προτιμήσεις νέας καρτέλας
Προτεινόμενο από τον πάροχο Pocket
+ Αναζήτηση στον ιστό Αναζήτηση
Κορυφαίες ιστοσελίδες Πρόσβαση στις ιστοσελίδες που επισκέπτεστε περισσότερο.
Προτιμήσεις νέας καρτέλας
Προτεινόμενο από τον πάροχο Pocket
diff --git a/browser/extensions/activity-stream/prerendered/locales/en-GB/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/en-GB/activity-stream-prerendered.html
index dbccb8f72f26..26576163ebec 100644
--- a/browser/extensions/activity-stream/prerendered/locales/en-GB/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/en-GB/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Search the Web Search
Top Sites Access the web sites you visit most.
New Tab Preferences
+ Search the Web Search
Top Sites Access the web sites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/prerendered/locales/en-US/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/en-US/activity-stream-prerendered.html
index a00e963fa8bf..168d7f771757 100644
--- a/browser/extensions/activity-stream/prerendered/locales/en-US/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/en-US/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Search the Web Search
Top Sites Access the websites you visit most.
New Tab Preferences
+ Search the Web Search
Top Sites Access the websites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/prerendered/locales/eo/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/eo/activity-stream-prerendered.html
index 4a7654ac4ab6..5bcb58369dc9 100644
--- a/browser/extensions/activity-stream/prerendered/locales/eo/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/eo/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Serĉi la reton Serĉi
Plej vizititaj Aliri la plej ofte vizitajn retejojn.
Preferoj pri nova langeto
+ Serĉi la reton Serĉi
Plej vizititaj Aliri la plej ofte vizitajn retejojn.
Preferoj pri nova langeto
diff --git a/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-prerendered.html
index 689dd8656945..28db190e701f 100644
--- a/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Buscar en la web Buscar
Más visitados Acceder a los sitios web más visitados.
Preferencia de nueva pestaña
+ Buscar en la web Buscar
Más visitados Acceder a los sitios web más visitados.
Preferencia de nueva pestaña
diff --git a/browser/extensions/activity-stream/prerendered/locales/es-CL/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/es-CL/activity-stream-prerendered.html
index 84f46873f09d..add8cede016b 100644
--- a/browser/extensions/activity-stream/prerendered/locales/es-CL/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/es-CL/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Buscar en la Web Buscar
Sitios frecuentes Accede a los sitios que más visitas.
Preferencias de Nueva pestaña
+ Buscar en la Web Buscar
Sitios frecuentes Accede a los sitios que más visitas.
Preferencias de Nueva pestaña
diff --git a/browser/extensions/activity-stream/prerendered/locales/es-ES/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/es-ES/activity-stream-prerendered.html
index 1e75ae45563b..e2222b7c6340 100644
--- a/browser/extensions/activity-stream/prerendered/locales/es-ES/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/es-ES/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Buscar en la Web Buscar
Sitios favoritos Accede a las páginas que más visitas.
Preferencias de nueva pestaña
+ Buscar en la Web Buscar
Sitios favoritos Accede a las páginas que más visitas.
Preferencias de nueva pestaña
diff --git a/browser/extensions/activity-stream/prerendered/locales/es-MX/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/es-MX/activity-stream-prerendered.html
index f839ebf0773e..195104997bfe 100644
--- a/browser/extensions/activity-stream/prerendered/locales/es-MX/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/es-MX/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Buscar en la Web Buscar
Sitios favoritos Accede a los sitios web que más visitas.
Preferencias de nueva pestaña
+ Buscar en la Web Buscar
Sitios favoritos Accede a los sitios web que más visitas.
Preferencias de nueva pestaña
diff --git a/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-prerendered.html
index 751337acaeec..c3d8cfefae69 100644
--- a/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Otsi veebist Otsi
Top saidid Ligipääs enim külastatud veebilehtedele.
Uue kaardi sätted
+ Otsi veebist Otsi
Top saidid Ligipääs enim külastatud veebilehtedele.
Uue kaardi sätted
diff --git a/browser/extensions/activity-stream/prerendered/locales/eu/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/eu/activity-stream-prerendered.html
index 3bdcadf05ca1..fd4f849502e1 100644
--- a/browser/extensions/activity-stream/prerendered/locales/eu/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/eu/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Bilatu webean Bilatu
Gune erabilienak Sartu gehien bisitatzen dituzun webguneetara.
Fitxa berriaren hobespenak
Pocket hornitzaileak gomendatuta
+ Bilatu webean Bilatu
Gune erabilienak Sartu gehien bisitatzen dituzun webguneetara.
Fitxa berriaren hobespenak
Pocket hornitzaileak gomendatuta
diff --git a/browser/extensions/activity-stream/prerendered/locales/fa/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/fa/activity-stream-prerendered.html
index d91307e27b3e..2e739ff2a4b6 100644
--- a/browser/extensions/activity-stream/prerendered/locales/fa/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/fa/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- جستوجوی وب جستوجو
سایتهای برتر به وبسایتهایی که بیشترین بازدید از آنها را داشتید دسترسی داشته باشید.
تنظیمات زبانه جدید
+ جستوجوی وب جستوجو
سایتهای برتر به وبسایتهایی که بیشترین بازدید از آنها را داشتید دسترسی داشته باشید.
تنظیمات زبانه جدید
diff --git a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
index d7c38bce0597..69fa95ac7947 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Yiylo geese Yiylo
Lowe dowrowe Access the websites you visit most.
New Tab Preferences
+ Yiylo geese Yiylo
Lowe dowrowe Access the websites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
index cd89c77da2e7..0b374f952def 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ff/activity-stream-strings.js
@@ -45,7 +45,7 @@ window.gActivityStreamStrings = {
"welcome_body": "Firefox will use this space to show your most relevant bookmarks, articles, videos, and pages you’ve recently visited, so you can get back to them easily.",
"welcome_label": "Heɓtinde Jalbine maa",
"time_label_less_than_minute": "<1m",
- "time_label_minute": "{number}m",
+ "time_label_minute": "{number} m",
"time_label_hour": "{number}h",
"time_label_day": "{number}d",
"settings_pane_button_label": "Customize your New Tab page",
diff --git a/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-prerendered.html
index 6589f2cedb2c..0ec77ecbe740 100644
--- a/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Verkkohaku Haku
Ykkössivustot Näe eniten vierailemasi sivustot.
Uuden välilehden asetukset
Suositukset lähteestä Pocket
+ Verkkohaku Haku
Ykkössivustot Näe eniten vierailemasi sivustot.
Uuden välilehden asetukset
Suositukset lähteestä Pocket
diff --git a/browser/extensions/activity-stream/prerendered/locales/fr/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/fr/activity-stream-prerendered.html
index 368110e84996..a7a87685bb70 100644
--- a/browser/extensions/activity-stream/prerendered/locales/fr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/fr/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Rechercher sur le Web Rechercher
Sites les plus visités Accédez aux sites que vous consultez le plus.
Préférences Nouvel onglet
Recommandations par Pocket
+ Rechercher sur le Web Rechercher
Sites les plus visités Accédez aux sites que vous consultez le plus.
Préférences Nouvel onglet
Recommandations par Pocket
diff --git a/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-prerendered.html
index 4dd4a73f567d..dfdbc0291838 100644
--- a/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Sykje op it web Sykje
Topwebsites Benaderje de websites dy't jo it meast besykje.
Nij ljepblêdfoarkarren
Oanrekommandearre troch Pocket
+ Sykje op it web Sykje
Topwebsites Benaderje de websites dy't jo it meast besykje.
Nij ljepblêdfoarkarren
Oanrekommandearre troch Pocket
diff --git a/browser/extensions/activity-stream/prerendered/locales/ga-IE/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ga-IE/activity-stream-prerendered.html
index 482bdab817ec..3a280b15e327 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ga-IE/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ga-IE/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cuardaigh an Gréasán Cuardach
Barrshuímh Na suímh Ghréasáin a dtugann tú cuairt orthu is minice.
Sainroghanna do Chluaisín Nua
Recommended by Pocket Topaicí i mbéal an phobail:
+ Cuardaigh an Gréasán Cuardach
Barrshuímh Na suímh Ghréasáin a dtugann tú cuairt orthu is minice.
Sainroghanna do Chluaisín Nua
Recommended by Pocket Topaicí i mbéal an phobail:
diff --git a/browser/extensions/activity-stream/prerendered/locales/gd/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/gd/activity-stream-prerendered.html
index 4659920a2c3e..7c6147f8ac9d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/gd/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/gd/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Lorg air an lìon Lorg
Brod nan làrach Faigh cothrom air na làraichean air an tadhail thu gu tric.
Roghainnean nan tabaichean ùra
+ Lorg air an lìon Lorg
Brod nan làrach Faigh cothrom air na làraichean air an tadhail thu gu tric.
Roghainnean nan tabaichean ùra
diff --git a/browser/extensions/activity-stream/prerendered/locales/gl/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/gl/activity-stream-prerendered.html
index fe8fb6a3a497..e01d98035905 100644
--- a/browser/extensions/activity-stream/prerendered/locales/gl/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/gl/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Buscar na Web Buscar
Sitios favoritos Acceda aos sitios web que máis visita.
Preferencias de nova lapela
+ Buscar na Web Buscar
Sitios favoritos Acceda aos sitios web que máis visita.
Preferencias de nova lapela
diff --git a/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html
index fc78bc985988..9c2fc2a663a6 100644
--- a/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/gn/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Ñandutivevépe Jeheka Eheka
Tenda Ojehechavéva Eike ñandutirenda rehechajepivévape.
Tendayke Pyahu Jeguererohoryrã
Pocket he'i ndéve reike hag̃ua
+ Ñandutivevépe Jeheka Eheka
Tenda Ojehechavéva Eike ñandutirenda rehechajepivévape.
Tendayke Pyahu Jeguererohoryrã
Pocket he'i ndéve reike hag̃ua
diff --git a/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-prerendered.html
index 177c743b27d0..556f97671569 100644
--- a/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- વેબ પર શોધો શોધો
ટોપ સાઇટ્સ તમે સૌથી વધુ મુલાકાત લો છો તે વેબસાઇટ્સને ઍક્સેસ કરો.
નવી ટેબ પસંદગીઓ
+ વેબ પર શોધો શોધો
ટોપ સાઇટ્સ તમે સૌથી વધુ મુલાકાત લો છો તે વેબસાઇટ્સને ઍક્સેસ કરો.
નવી ટેબ પસંદગીઓ
diff --git a/browser/extensions/activity-stream/prerendered/locales/he/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/he/activity-stream-prerendered.html
index 6a25404f71f7..ec9420786cd9 100644
--- a/browser/extensions/activity-stream/prerendered/locales/he/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/he/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- חיפוש ברשת חיפוש
אתרים מובילים גישה לאתרים בהם ביקרת הכי הרבה.
העדפות לשונית חדשה
+ חיפוש ברשת חיפוש
אתרים מובילים גישה לאתרים בהם ביקרת הכי הרבה.
העדפות לשונית חדשה
diff --git a/browser/extensions/activity-stream/prerendered/locales/hi-IN/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/hi-IN/activity-stream-prerendered.html
index 4db403cb40a7..0fcc805d4301 100644
--- a/browser/extensions/activity-stream/prerendered/locales/hi-IN/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/hi-IN/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- वेब पर खोजें खोज
सर्वोच्च साइटें आपके द्वारा सबसे ज्यादा खोजी जाने वाली वेबसाइट्स देखें.
नयी टैब वरीयताएँ
+ वेब पर खोजें खोज
सर्वोच्च साइटें आपके द्वारा सबसे ज्यादा खोजी जाने वाली वेबसाइट्स देखें.
नयी टैब वरीयताएँ
diff --git a/browser/extensions/activity-stream/prerendered/locales/hr/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/hr/activity-stream-prerendered.html
index 85367f78e4f1..45768ca5e026 100644
--- a/browser/extensions/activity-stream/prerendered/locales/hr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/hr/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Pretraži web Traži
Najbolje stranice Pristupite stranicama koje najčešće posjećujete.
Postavke nove kartice
+ Pretraži web Traži
Najbolje stranice Pristupite stranicama koje najčešće posjećujete.
Postavke nove kartice
diff --git a/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-prerendered.html
index d287f39b78cd..78f6e4d80ed9 100644
--- a/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/hsb/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Web přepytać Pytać
Najhusćišo wopytane sydła Wočińće websydła, kotrež sće najhusćišo wopytał.
Nastajenja noweho rajtarka
+ Web přepytać Pytać
Najhusćišo wopytane sydła Wočińće websydła, kotrež sće najhusćišo wopytał.
Nastajenja noweho rajtarka
diff --git a/browser/extensions/activity-stream/prerendered/locales/hu/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/hu/activity-stream-prerendered.html
index c5770ac5a9cf..7c6b75215c72 100644
--- a/browser/extensions/activity-stream/prerendered/locales/hu/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/hu/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Keresés a weben Keresés
Népszerű oldalak A leggyakrabban látogatott webhelyek elérése.
Új lap beállításai
+ Keresés a weben Keresés
Népszerű oldalak A leggyakrabban látogatott webhelyek elérése.
Új lap beállításai
diff --git a/browser/extensions/activity-stream/prerendered/locales/hy-AM/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/hy-AM/activity-stream-prerendered.html
index d9650318a917..d4f14bdf8db4 100644
--- a/browser/extensions/activity-stream/prerendered/locales/hy-AM/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/hy-AM/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Որոնել առցանց Search
Լավագույն կայքեր Access the websites you visit most.
New Tab Preferences
+ Որոնել առցանց Search
Լավագույն կայքեր Access the websites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/prerendered/locales/ia/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ia/activity-stream-prerendered.html
index 7389d5914287..064edca00bfc 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ia/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ia/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cercar in le Web Cercar
Sitos popular Acceder al sitos web que tu plus visita.
Preferentias de nove scheda
+ Cercar in le Web Cercar
Sitos popular Acceder al sitos web que tu plus visita.
Preferentias de nove scheda
diff --git a/browser/extensions/activity-stream/prerendered/locales/id/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/id/activity-stream-prerendered.html
index aa805f16c59f..1750b472fda5 100644
--- a/browser/extensions/activity-stream/prerendered/locales/id/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/id/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cari di Web Cari
Situs Teratas Mengakses situs web yang paling sering Anda kunjungi.
Preferensi Tab Baru
+ Cari di Web Cari
Situs Teratas Mengakses situs web yang paling sering Anda kunjungi.
Preferensi Tab Baru
diff --git a/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-prerendered.html
index 540f2ca7a264..97c5a45acd79 100644
--- a/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cerca sul Web Cerca
Siti principali Accedi ai siti che visiti più spesso.
Preferenze Nuova scheda
+ Cerca sul Web Cerca
Siti principali Accedi ai siti che visiti più spesso.
Preferenze Nuova scheda
diff --git a/browser/extensions/activity-stream/prerendered/locales/ja/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ja/activity-stream-prerendered.html
index b18f7fe75155..8ea8f0d526e6 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ja/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ja/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
-
+
diff --git a/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-prerendered.html
index 250e6ce65d5a..051b352aa305 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ინტერნეტში ძიება ძიება
რჩეული საიტები წვდომა ხშირად მონახულებულ საიტებთან.
ახალი ჩანართის პარამეტრები
რეკომენდებულია Pocket-ის მიერ
+ ინტერნეტში ძიება ძიება
რჩეული საიტები წვდომა ხშირად მონახულებულ საიტებთან.
ახალი ჩანართის პარამეტრები
რეკომენდებულია Pocket-ის მიერ
diff --git a/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-prerendered.html
index 481640f29d76..8a051a52fec8 100644
--- a/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/kab/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Nadi di Web Nadi
Ismal ifazen Kcem ar yesmal web i trezzuḍ s waṭas.
Ismenyifen n yiccer amaynut
+ Nadi di Web Nadi
Ismal ifazen Kcem ar yesmal web i trezzuḍ s waṭas.
Ismenyifen n yiccer amaynut
diff --git a/browser/extensions/activity-stream/prerendered/locales/kk/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/kk/activity-stream-prerendered.html
index 957d10d0a3ce..c41ae864ab6a 100644
--- a/browser/extensions/activity-stream/prerendered/locales/kk/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/kk/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Интернетте іздеу Іздеу
Үздік сайттар Көбірек қаралатын сайттарға қатынау.
Жаңа бет баптаулары
+ Интернетте іздеу Іздеу
Үздік сайттар Көбірек қаралатын сайттарға қатынау.
Жаңа бет баптаулары
diff --git a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html
index 225b6815aa8c..02c57e6b8ac8 100644
--- a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ស្វែងរកបណ្ដាញ ស្វែងរក
វិបសាយលើគេ ចូលវេបសាយដែលអ្នកទស្សនាច្រើនបំផុត។
ចំណង់ចំណូលចិត្ត ផ្ទាំងថ្មី
+ ស្វែងរកបណ្ដាញ ស្វែងរក
វិបសាយលើគេ ចូលវេបសាយដែលអ្នកទស្សនាច្រើនបំផុត។
ចំណង់ចំណូលចិត្ត ផ្ទាំងថ្មី
diff --git a/browser/extensions/activity-stream/prerendered/locales/kn/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/kn/activity-stream-prerendered.html
index 4d76a1d8b59f..069b6bb009d1 100644
--- a/browser/extensions/activity-stream/prerendered/locales/kn/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/kn/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ಅಂತರ್ಜಾಲವನ್ನು ಹುಡುಕಿ ಹುಡುಕು
ಪ್ರಮುಖ ತಾಣಗಳು ನೀವು ಅತಿ ಹೆಚ್ಚು ನೋಡುವ ಜಾಲತಾಣಗಳಿಗೆ ಪ್ರವೇಶದ್ವಾರ.
ಹೊಸ ಹಾಳೆಯ ಆದ್ಯತೆಗಳು
Pocket ರಿಂದ ಶಿಫಾರಸುಮಾಡುಲಾಗಿದೆ
+ ಅಂತರ್ಜಾಲವನ್ನು ಹುಡುಕಿ ಹುಡುಕು
ಪ್ರಮುಖ ತಾಣಗಳು ನೀವು ಅತಿ ಹೆಚ್ಚು ನೋಡುವ ಜಾಲತಾಣಗಳಿಗೆ ಪ್ರವೇಶದ್ವಾರ.
ಹೊಸ ಹಾಳೆಯ ಆದ್ಯತೆಗಳು
Pocket ರಿಂದ ಶಿಫಾರಸುಮಾಡುಲಾಗಿದೆ
diff --git a/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-prerendered.html
index 7778e5e0f337..62a6a9dd9a84 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- 웹 검색 검색
상위 사이트 가장 많이 방문한 웹 사이트에 접근하세요.
새 탭 설정
+ 웹 검색 검색
상위 사이트 가장 많이 방문한 웹 사이트에 접근하세요.
새 탭 설정
diff --git a/browser/extensions/activity-stream/prerendered/locales/lij/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/lij/activity-stream-prerendered.html
index 3dea0e2b9171..3f16e3ec1c2d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/lij/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/lij/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Çerca inta Ræ Çerca
I megio sciti Acedi a-i sciti che ti vixiti ciù de spesso.
Preferense neuvo feuggio
+ Çerca inta Ræ Çerca
I megio sciti Acedi a-i sciti che ti vixiti ciù de spesso.
Preferense neuvo feuggio
diff --git a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html
index 9017abf0993c..279256f4f868 100644
--- a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ຄົ້ນຫາເວັບ ຊອກຫາ
ເວັບໄຊຕ໌ຍອດນິຍົມ ເຂົ້າເວັບໄຊທ໌ທີ່ທ່ານໄດ້ເຂົ້າໄປຫລາຍທີ່ສຸດ.
ການຕັ້ງຄ່າແທັບໃຫມ່
+ ຄົ້ນຫາເວັບ ຊອກຫາ
ເວັບໄຊຕ໌ຍອດນິຍົມ ເຂົ້າເວັບໄຊທ໌ທີ່ທ່ານໄດ້ເຂົ້າໄປຫລາຍທີ່ສຸດ.
ການຕັ້ງຄ່າແທັບໃຫມ່
diff --git a/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-prerendered.html
index d78995427d3d..f1ec753762bc 100644
--- a/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Ieškokite saityne Ieškoti
Lankomiausios svetainės Pasiekite jūsų dažniausiai lankomas svetaines.
Naujos kortelės nuostatos
+ Ieškokite saityne Ieškoti
Lankomiausios svetainės Pasiekite jūsų dažniausiai lankomas svetaines.
Naujos kortelės nuostatos
diff --git a/browser/extensions/activity-stream/prerendered/locales/ltg/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ltg/activity-stream-prerendered.html
index d6adc0d76410..550d77281910 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ltg/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ltg/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Mekleit teiklā Mekleit
Popularōkōs lopys Sajam pīeju lopom, kuras apskoti vysvaira.
Jaunas cilnes īstatiejumi
+ Mekleit teiklā Mekleit
Popularōkōs lopys Sajam pīeju lopom, kuras apskoti vysvaira.
Jaunas cilnes īstatiejumi
diff --git a/browser/extensions/activity-stream/prerendered/locales/lv/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/lv/activity-stream-prerendered.html
index 266188b04e92..26e8929a821e 100644
--- a/browser/extensions/activity-stream/prerendered/locales/lv/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/lv/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Meklēt internetā Meklēt
Populārākās lapas Ātri nokļūstiet biežāk apmeklētajās lapās.
Jaunas cilnes iestatījumi
+ Meklēt internetā Meklēt
Populārākās lapas Ātri nokļūstiet biežāk apmeklētajās lapās.
Jaunas cilnes iestatījumi
diff --git a/browser/extensions/activity-stream/prerendered/locales/mk/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/mk/activity-stream-prerendered.html
index b01115ae96ec..eca11aeb411c 100644
--- a/browser/extensions/activity-stream/prerendered/locales/mk/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/mk/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Пребарајте на Интернет Барај
Популарни мрежни места Пристапете до мрежните места што ги посетувате најмногу.
Преференци за Ново јазиче
+ Пребарајте на Интернет Барај
Популарни мрежни места Пристапете до мрежните места што ги посетувате најмногу.
Преференци за Ново јазиче
diff --git a/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-prerendered.html
index fd8c293fcf3a..90281264faa5 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ഇൻറർനെറ്റിൽ തിരയുക തിരയുക
മികച്ച സൈറ്റുകൾ നിങ്ങൾ കൂടുതൽ സന്ദർശിക്കുന്ന വെബ്സൈറ്റുകളിൽ പ്രവേശിക്കുക.
പുതിയ ടാബിന്റെ മുൻഗണനകൾ
+ ഇൻറർനെറ്റിൽ തിരയുക തിരയുക
മികച്ച സൈറ്റുകൾ നിങ്ങൾ കൂടുതൽ സന്ദർശിക്കുന്ന വെബ്സൈറ്റുകളിൽ പ്രവേശിക്കുക.
പുതിയ ടാബിന്റെ മുൻഗണനകൾ
diff --git a/browser/extensions/activity-stream/prerendered/locales/mr/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/mr/activity-stream-prerendered.html
index 325a762050c6..1b973949922e 100644
--- a/browser/extensions/activity-stream/prerendered/locales/mr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/mr/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- वेबवर शोधा शोधा
खास साईट्स Access the websites you visit most.
नवीन टॅब प्राधान्ये
+ वेबवर शोधा शोधा
खास साईट्स Access the websites you visit most.
नवीन टॅब प्राधान्ये
diff --git a/browser/extensions/activity-stream/prerendered/locales/ms/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ms/activity-stream-prerendered.html
index 2713db3f0362..54a74f43117a 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ms/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ms/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Cari dalam Web Cari
Laman Teratas Akses laman web yang paling banyak dilawati.
Keutamaan Tab Baru
+ Cari dalam Web Cari
Laman Teratas Akses laman web yang paling banyak dilawati.
Keutamaan Tab Baru
diff --git a/browser/extensions/activity-stream/prerendered/locales/my/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/my/activity-stream-prerendered.html
index 7d8b1794beac..c8a78ff237d2 100644
--- a/browser/extensions/activity-stream/prerendered/locales/my/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/my/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ဝတ်ဘ်ပေါ်တွင် ရှာဖွေခြင်း ရှာ
အများဆုံးသုံးဆိုက်များ သင်အများဆုံးသွားလည်သော ဝတ်ဘ်ဆိုက်များကို ရယူပါ
စာတပ်ဗ်အသစ်အပြင်အဆင်များ
Pocket က အကြံပြုထားသည် လူကြိုက်များခေါင်းစဉ်များ
+ ဝတ်ဘ်ပေါ်တွင် ရှာဖွေခြင်း ရှာ
အများဆုံးသုံးဆိုက်များ သင်အများဆုံးသွားလည်သော ဝတ်ဘ်ဆိုက်များကို ရယူပါ
စာတပ်ဗ်အသစ်အပြင်အဆင်များ
Pocket က အကြံပြုထားသည် လူကြိုက်များခေါင်းစဉ်များ
diff --git a/browser/extensions/activity-stream/prerendered/locales/nb-NO/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/nb-NO/activity-stream-prerendered.html
index 732879ef2cd3..15e3c38dfbaa 100644
--- a/browser/extensions/activity-stream/prerendered/locales/nb-NO/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/nb-NO/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Søk på nettet Søk
Mest besøkte nettsider Tilgang til nettsidene du besøker mest.
Innstillinger for Ny fane
+ Søk på nettet Søk
Mest besøkte nettsider Tilgang til nettsidene du besøker mest.
Innstillinger for Ny fane
diff --git a/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-prerendered.html
index 5501afff8e35..2ecb6d70d1f0 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- वेबमा खोज्नुहोस् खोजी गर्नुहोस्
शीर्ष साइटहरु तपाईले धेरै भ्रमण गर्नुभएका वेबसाइटहरूमा पहुँच गर्नुहोस् ।
नयाँ ट्याब प्राथमिकताहरू
Pocket द्वारा सिफारिस गरिएको
+ वेबमा खोज्नुहोस् खोजी गर्नुहोस्
शीर्ष साइटहरु तपाईले धेरै भ्रमण गर्नुभएका वेबसाइटहरूमा पहुँच गर्नुहोस् ।
नयाँ ट्याब प्राथमिकताहरू
Pocket द्वारा सिफारिस गरिएको
diff --git a/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-strings.js
index 8301f5dc78fe..266fac18cbf3 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/ne-NP/activity-stream-strings.js
@@ -38,9 +38,9 @@ window.gActivityStreamStrings = {
"section_info_option": "जानकारी",
"section_info_send_feedback": "प्रतिक्रिया पठाउनुहोस्",
"section_info_privacy_notice": "गोपनीयता नीति",
- "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
- "section_disclaimer_topstories_linktext": "Learn how it works.",
- "section_disclaimer_topstories_buttontext": "Okay, got it",
+ "section_disclaimer_topstories": "वेबमा सबैभन्दा रोचक कथाहरू, तपाईंले पढ्नु भएको आधारमा Pocket बाट चयन गर्नुभएको छ।",
+ "section_disclaimer_topstories_linktext": "कसरी काम गर्छ हेर्नुहोस्।",
+ "section_disclaimer_topstories_buttontext": "बुझेँ",
"welcome_title": "नयाँ ट्याबमा स्वागत छ",
"welcome_body": "Firefoxले यस ठाउँको प्रयोग तपाईंको सबैभन्दा सान्दर्भिक पुस्तकचिनो, लेखहरू, भिडियोहरू, र तपाईंले हालै भ्रमण गर्नु भएको पृष्ठहरूलाई राख्न प्रयोग गर्दछ, जसले गर्दा तपाइँ तिनीहरूलाई सजिलै भेटाउन सक्नुहुनेछ ।",
"welcome_label": "तपाईँका विशेषताहरु पत्ता लगाउँदै",
@@ -67,7 +67,7 @@ window.gActivityStreamStrings = {
"settings_pane_snippets_header": "स्निप्पेटस्",
"settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.",
"settings_pane_done_button": "सम्पन्न भयो",
- "settings_pane_topstories_options_sponsored": "Show Sponsored Stories",
+ "settings_pane_topstories_options_sponsored": "प्रायोजित गरिएको कथाहरू देखाउनुहोस्",
"edit_topsites_button_text": "सम्पादन गर्नुहोस्",
"edit_topsites_button_label": "तपाईंको शीर्ष साइट खण्ड अनुकूलन गर्नुहोस्",
"edit_topsites_showmore_button": "थप देखाउनुहोस्",
@@ -80,15 +80,15 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "थप्नुहोस्",
"topsites_form_add_header": "नयाँ शीर्ष साइट",
"topsites_form_edit_header": "शीर्ष साइट सम्पादन गर्नुहोस्",
- "topsites_form_title_placeholder": "Enter a title",
- "topsites_form_url_placeholder": "Type or paste a URL",
+ "topsites_form_title_placeholder": "शीर्षक प्रविष्ट गर्नुहोस्",
+ "topsites_form_url_placeholder": "URL लेख्नुहोस् ",
"topsites_form_add_button": "थप्नुहोस्",
"topsites_form_save_button": "सङ्ग्रह गर्नुहोस्",
"topsites_form_cancel_button": "रद्द गर्नुहोस्",
- "topsites_form_url_validation": "Valid URL required",
- "pocket_read_more": "Popular Topics:",
- "pocket_read_even_more": "View More Stories",
- "pocket_feedback_header": "The best of the web, curated by over 25 million people.",
+ "topsites_form_url_validation": "मान्य URL चाहिन्छ",
+ "pocket_read_more": "लोकप्रिय शीर्षकहरू:",
+ "pocket_read_even_more": "अरू कथा देखाउनुहोस्",
+ "pocket_feedback_header": "वेबको सर्वोत्तम, 25 मिलियन भन्दा बढी व्यक्तिहरू द्वारा लिपिबद्ध।",
"pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.",
"highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.",
"topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
diff --git a/browser/extensions/activity-stream/prerendered/locales/nl/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/nl/activity-stream-prerendered.html
index a6cee22486fc..676ab484b4de 100644
--- a/browser/extensions/activity-stream/prerendered/locales/nl/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/nl/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Zoeken op het web Zoeken
Topwebsites De websites benaderen die u het vaakst bezoekt.
Nieuw-tabbladvoorkeuren
+ Zoeken op het web Zoeken
Topwebsites De websites benaderen die u het vaakst bezoekt.
Nieuw-tabbladvoorkeuren
diff --git a/browser/extensions/activity-stream/prerendered/locales/nn-NO/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/nn-NO/activity-stream-prerendered.html
index 16a6940f26f5..d44e41676913 100644
--- a/browser/extensions/activity-stream/prerendered/locales/nn-NO/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/nn-NO/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Søk på nettet Søk
Mest besøkte nettsider Tilgang til nettsidene du besøkjer mest.
Innstillingar for Ny fane
+ Søk på nettet Søk
Mest besøkte nettsider Tilgang til nettsidene du besøkjer mest.
Innstillingar for Ny fane
diff --git a/browser/extensions/activity-stream/prerendered/locales/pa-IN/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/pa-IN/activity-stream-prerendered.html
index d578a24df8c5..1276a964a0f8 100644
--- a/browser/extensions/activity-stream/prerendered/locales/pa-IN/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/pa-IN/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ਵੈੱਬ ਨੂੰ ਖੋਜੋ ਖੋਜੋ
ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੈੱਬਸਾਈਟਾਂ, ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹਿਆ ਹੈ, ਲਈ ਪਹੁੰਚ।
ਨਵੀਂ ਟੈਬ ਲਈ ਪਸੰਦਾਂ
+ ਵੈੱਬ ਨੂੰ ਖੋਜੋ ਖੋਜੋ
ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੈੱਬਸਾਈਟਾਂ, ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹਿਆ ਹੈ, ਲਈ ਪਹੁੰਚ।
ਨਵੀਂ ਟੈਬ ਲਈ ਪਸੰਦਾਂ
diff --git a/browser/extensions/activity-stream/prerendered/locales/pl/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/pl/activity-stream-prerendered.html
index 33ba5d22b9be..e194e529c8f6 100644
--- a/browser/extensions/activity-stream/prerendered/locales/pl/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/pl/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Szukaj Szukaj
Popularne Otwieraj najczęściej odwiedzane strony.
Preferencje nowej karty
+ Szukaj Szukaj
Popularne Otwieraj najczęściej odwiedzane strony.
Preferencje nowej karty
diff --git a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-prerendered.html
index 13ede39d7200..43be3bf98f15 100644
--- a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Pesquisar na Web Pesquisar
Sites preferidos Acesse os sites que você mais visita.
Preferências de novas abas
+ Pesquisar na Web Pesquisar
Sites preferidos Acesse os sites que você mais visita.
Preferências de novas abas
diff --git a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-prerendered.html
index ebebbe6fbacd..a6017a1d36e8 100644
--- a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Pesquisar na Web Pesquisar
Sites mais visitados Aceda aos websites que mais visita.
Preferências de novo separador
+ Pesquisar na Web Pesquisar
Sites mais visitados Aceda aos websites que mais visita.
Preferências de novo separador
diff --git a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js
index 230440c275a5..47e925a0f030 100644
--- a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js
@@ -52,7 +52,7 @@ window.gActivityStreamStrings = {
"settings_pane_header": "Preferências de novo separador",
"settings_pane_body2": "Escolha o que vê nesta página.",
"settings_pane_search_header": "Pesquisa",
- "settings_pane_search_body": "Pesquise na Web a partir do seu 'Novo separador'.",
+ "settings_pane_search_body": "Pesquise na Web a partir do seu novo separador.",
"settings_pane_topsites_header": "Sites mais visitados",
"settings_pane_topsites_body": "Aceda aos websites que mais visita.",
"settings_pane_topsites_options_showmore": "Mostrar duas linhas",
diff --git a/browser/extensions/activity-stream/prerendered/locales/rm/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/rm/activity-stream-prerendered.html
index 1f4874540016..f4c0b5576bdb 100644
--- a/browser/extensions/activity-stream/prerendered/locales/rm/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/rm/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Tschertgar en il Web Tschertgar
Paginas preferidas Acceder las websites che ti visitas il pli savens.
Preferenzas per novs tabs
+ Tschertgar en il Web Tschertgar
Paginas preferidas Acceder las websites che ti visitas il pli savens.
Preferenzas per novs tabs
diff --git a/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-prerendered.html
index 1aa07fe20ac2..73d426d5a96d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Caută pe web Caută
Site-uri de top Accesează site-urile pe care le vizitezi mai des.
Preferințe pentru filă nouă
+ Caută pe web Caută
Site-uri de top Accesează site-urile pe care le vizitezi mai des.
Preferințe pentru filă nouă
diff --git a/browser/extensions/activity-stream/prerendered/locales/ru/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ru/activity-stream-prerendered.html
index bcf9c9cc101c..0ca0b5259e2d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ru/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ru/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Искать в Интернете Искать
Топ сайтов Получите доступ к сайтам, которые вы посещаете чаще всего.
Настройки новой вкладки
+ Искать в Интернете Искать
Топ сайтов Получите доступ к сайтам, которые вы посещаете чаще всего.
Настройки новой вкладки
diff --git a/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html
index ec62ccc51225..58ea1e5af964 100644
--- a/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ජාලය තුළ සොයන්න සොයන්න
ප්රමුඛ අඩවි ඔබ නිරතුරුව පිවිසෙන වෙබ් අඩවි වෙත ප්රවේශය.
නව ටැබ අභිප්රේත
Pocket විසින් නිර්දේශිතයි
+ ජාලය තුළ සොයන්න සොයන්න
ප්රමුඛ අඩවි ඔබ නිරතුරුව පිවිසෙන වෙබ් අඩවි වෙත ප්රවේශය.
නව ටැබ අභිප්රේත
Pocket විසින් නිර්දේශිතයි
diff --git a/browser/extensions/activity-stream/prerendered/locales/sk/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/sk/activity-stream-prerendered.html
index 2a02850bca73..917f673fa9d5 100644
--- a/browser/extensions/activity-stream/prerendered/locales/sk/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sk/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Vyhľadávanie na webe Hľadať
Top stránky Prístup k webovým stránkam, ktoré navštevujete najčastejšie.
Nastavenia Novej karty
+ Vyhľadávanie na webe Hľadať
Top stránky Prístup k webovým stránkam, ktoré navštevujete najčastejšie.
Nastavenia Novej karty
diff --git a/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-prerendered.html
index f27012670f5c..bf6c2d66a970 100644
--- a/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sl/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Iskanje po spletu Iskanje
Glavne strani Priročen dostop do najbolj obiskanih strani.
Nastavitve novega zavihka
+ Iskanje po spletu Iskanje
Glavne strani Priročen dostop do najbolj obiskanih strani.
Nastavitve novega zavihka
diff --git a/browser/extensions/activity-stream/prerendered/locales/sq/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/sq/activity-stream-prerendered.html
index f93df6515d64..b1aa9ba0e5ba 100644
--- a/browser/extensions/activity-stream/prerendered/locales/sq/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sq/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Kërkoni në Web Kërko
Sajte Kryesues Hyni te sajtet që vizitoni më shpesh.
Parapëlqime për Skedë të Re
+ Kërkoni në Web Kërko
Sajte Kryesues Hyni te sajtet që vizitoni më shpesh.
Parapëlqime për Skedë të Re
diff --git a/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
index f6293c4c86bd..854062e50a55 100644
--- a/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sr/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Претражи веб Претражи
Омиљени сајтови Приступите најпосећенијим веб сајтовима.
Поставке новог језичка
+ Претражи веб Претражи
Омиљени сајтови Приступите најпосећенијим веб сајтовима.
Поставке новог језичка
diff --git a/browser/extensions/activity-stream/prerendered/locales/sv-SE/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/sv-SE/activity-stream-prerendered.html
index 73bf7cbad3bb..87b6f4ff747d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/sv-SE/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/sv-SE/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Sök på webben Sök
Mest besökta Åtkomst till de webbplatser du besökt mest.
Inställningar Ny flik
+ Sök på webben Sök
Mest besökta Åtkomst till de webbplatser du besökt mest.
Inställningar Ny flik
diff --git a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html
index b117c903cbeb..a6353332c65a 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- இணையத்தில் தேடு தேடு
சிறந்த தளங்கள் நீங்கள் அடிக்கடி பார்க்கும் தளங்களை அணுகவும்.
புதிய கீற்றின் முன்னுரிமைகள்
Pocket என்பவரால் பரிந்துரைக்கப்பட்டது
+ இணையத்தில் தேடு தேடு
சிறந்த தளங்கள் நீங்கள் அடிக்கடி பார்க்கும் தளங்களை அணுகவும்.
புதிய கீற்றின் முன்னுரிமைகள்
Pocket என்பவரால் பரிந்துரைக்கப்பட்டது
diff --git a/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-prerendered.html
index 10a9c36e6aae..2c84873bfb27 100644
--- a/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- జాలంలో వెతకండి వెతకండి
మేటి సైట్లు మీరు ఎక్కువగా చూసే వెబ్సైట్లను చూడండి.
కొత్త ట్యాబు అభిరుచులు
Pocketచే సిఫార్సు చేయబడినది
+ జాలంలో వెతకండి వెతకండి
మేటి సైట్లు మీరు ఎక్కువగా చూసే వెబ్సైట్లను చూడండి.
కొత్త ట్యాబు అభిరుచులు
Pocketచే సిఫార్సు చేయబడినది
diff --git a/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js
index 3c621894f47a..6147454e32f6 100644
--- a/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/te/activity-stream-strings.js
@@ -38,9 +38,9 @@ window.gActivityStreamStrings = {
"section_info_option": "సమాచారం",
"section_info_send_feedback": "అభిప్రాయాన్ని పంపండి",
"section_info_privacy_notice": "గోప్యతా విధానం",
- "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
- "section_disclaimer_topstories_linktext": "Learn how it works.",
- "section_disclaimer_topstories_buttontext": "Okay, got it",
+ "section_disclaimer_topstories": "జాలంలో అత్యంత ఆసక్తికరమైన కథనాలు, మీరు చదివేవాటి ఆధారంగా ఎంచుకున్నవి. ఇప్పుడు Mozillaలో భాగమైన Pocket నుండి.",
+ "section_disclaimer_topstories_linktext": "ఇది ఎలా పనిచేస్తుందో తెలుసుకోండి.",
+ "section_disclaimer_topstories_buttontext": "సరే, అర్థమయ్యింది",
"welcome_title": "కొత్త ట్యాబుకు స్వాగతం",
"welcome_body": "సముచితమైన మీ ఇష్టాంశాలను, వ్యాసాలను, వీడియోలను, ఇంకా మీరు ఇటీవలే చూసిన పేజీలను మీకు తేలిగ్గా అందుబాటులో ఉంచేందుకు Firefox ఈ జాగాని వాడుకుంటుంది.",
"welcome_label": "మీ ముఖ్యాంశాలను గుర్తిస్తున్నది",
diff --git a/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-prerendered.html
index e0c97c1799f0..1728ede46c5e 100644
--- a/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ค้นหาเว็บ ค้นหา
ไซต์เด่น เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด
ค่ากำหนดแท็บใหม่
+ ค้นหาเว็บ ค้นหา
ไซต์เด่น เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด
ค่ากำหนดแท็บใหม่
diff --git a/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-prerendered.html
index c4fee1030557..a5b5be25ce7c 100644
--- a/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Hanapin sa Web Hanapin
Tuktok na mga Site Ma-access ang mga website na karamihang binibisita.
Bagong Kagustuhan na Tab
+ Hanapin sa Web Hanapin
Tuktok na mga Site Ma-access ang mga website na karamihang binibisita.
Bagong Kagustuhan na Tab
diff --git a/browser/extensions/activity-stream/prerendered/locales/tr/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/tr/activity-stream-prerendered.html
index d38bb057ab68..50c78a309331 100644
--- a/browser/extensions/activity-stream/prerendered/locales/tr/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/tr/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Web’de ara Ara
Sık Kullanılan Siteler En sık ziyaret ettiğiniz web sitelerine erişin.
Yeni Sekme Tercihleri
+ Web’de ara Ara
Sık Kullanılan Siteler En sık ziyaret ettiğiniz web sitelerine erişin.
Yeni Sekme Tercihleri
diff --git a/browser/extensions/activity-stream/prerendered/locales/uk/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/uk/activity-stream-prerendered.html
index 32830624eb49..0a95cf49cf0d 100644
--- a/browser/extensions/activity-stream/prerendered/locales/uk/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/uk/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Пошук в Інтернеті Пошук
Популярні сайти Доступ до найчастіше відвідуваних веб-сайтів.
Налаштування нової вкладки
+ Пошук в Інтернеті Пошук
Популярні сайти Доступ до найчастіше відвідуваних веб-сайтів.
Налаштування нової вкладки
diff --git a/browser/extensions/activity-stream/prerendered/locales/ur/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ur/activity-stream-prerendered.html
index ac8d41927927..68921b6bf9a2 100644
--- a/browser/extensions/activity-stream/prerendered/locales/ur/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/ur/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- ويب پر تلاش کريں تلاش
بہترین سائٹیں اپنی سب سے زیادہ دورہ کردہ ویب سائٹ تک رسائی حاصل کریں۔
نئے َٹیب کی ترجیحات
Pocket کی جانب سے تجویز کردہ
+ ويب پر تلاش کريں تلاش
بہترین سائٹیں اپنی سب سے زیادہ دورہ کردہ ویب سائٹ تک رسائی حاصل کریں۔
نئے َٹیب کی ترجیحات
Pocket کی جانب سے تجویز کردہ
diff --git a/browser/extensions/activity-stream/prerendered/locales/uz/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/uz/activity-stream-prerendered.html
index cac8a4fba48b..656c79d7c46c 100644
--- a/browser/extensions/activity-stream/prerendered/locales/uz/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/uz/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Search the Web Search
Ommabop saytlar Access the websites you visit most.
New Tab Preferences
+ Search the Web Search
Ommabop saytlar Access the websites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-prerendered.html
index c54ff5b358dc..63356e566279 100644
--- a/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
- Tìm trên mạng Tìm kiếm
Trang web hàng đầu Truy cập vào các trang web mà bạn truy cập vào nhiều nhất.
Tùy chỉnh cho tab mới
+ Tìm trên mạng Tìm kiếm
Trang web hàng đầu Truy cập vào các trang web mà bạn truy cập vào nhiều nhất.
Tùy chỉnh cho tab mới
diff --git a/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-strings.js
index 443eee0909dd..940b383e8f36 100644
--- a/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-strings.js
+++ b/browser/extensions/activity-stream/prerendered/locales/vi/activity-stream-strings.js
@@ -4,7 +4,7 @@ window.gActivityStreamStrings = {
"default_label_loading": "Đang tải…",
"header_top_sites": "Trang web hàng đầu",
"header_stories": "Câu chuyện hàng đầu",
- "header_highlights": "Highlights",
+ "header_highlights": "Nổi bật",
"header_visit_again": "Truy cập lại",
"header_bookmarks": "Các bookmark gần đây",
"header_recommended_by": "Được đề nghị bởi {provider}",
@@ -27,7 +27,7 @@ window.gActivityStreamStrings = {
"menu_action_delete": "Xóa từ lịch xử",
"menu_action_pin": "Ghim",
"menu_action_unpin": "Bỏ ghim",
- "confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?",
+ "confirm_history_delete_p1": "Bạn có chắc bạn muốn xóa bỏ mọi thứ của trang này từ lịch sử?",
"confirm_history_delete_notice_p2": "Hành động này không thể hoàn tác.",
"menu_action_save_to_pocket": "Lưu vào Pocket",
"search_for_something_with": "Tìm {search_term} với:",
@@ -36,24 +36,24 @@ window.gActivityStreamStrings = {
"search_web_placeholder": "Tìm trên mạng",
"search_settings": "Thay đổi thiết lập tìm kiếm",
"section_info_option": "Thông tin",
- "section_info_send_feedback": "Send Feedback",
- "section_info_privacy_notice": "Privacy Notice",
+ "section_info_send_feedback": "Gửi phản hồi",
+ "section_info_privacy_notice": "Chính sách riêng tư",
"section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.",
"section_disclaimer_topstories_linktext": "Learn how it works.",
- "section_disclaimer_topstories_buttontext": "Okay, got it",
+ "section_disclaimer_topstories_buttontext": "Ok, đã hiểu",
"welcome_title": "Chào mừng đến với tab mới",
- "welcome_body": "Firefox will use this space to show your most relevant bookmarks, articles, videos, and pages you’ve recently visited, so you can get back to them easily.",
- "welcome_label": "Identifying your Highlights",
+ "welcome_body": "Firefox sẽ sử dụng không gian này để hiển thị của bạn có liên quan nhất đánh dấu, bài viết, video và các trang bạn đã truy cập gần đây, do đó, bạn có thể quay lại công việc một cách dễ dàng.",
+ "welcome_label": "Đang xác định phần nổi bật của bạn",
"time_label_less_than_minute": "<1phút",
"time_label_minute": "{number}phút",
"time_label_hour": "{number}giờ",
"time_label_day": "{number}ngày",
"settings_pane_button_label": "Tùy biến trang Tab mới",
"settings_pane_header": "Tùy chỉnh cho tab mới",
- "settings_pane_body2": "Choose what you see on this page.",
+ "settings_pane_body2": "Chọn những gì bạn thấy trên trang này.",
"settings_pane_search_header": "Tìm kiếm",
"settings_pane_search_body": "Search the Web from your new tab.",
- "settings_pane_topsites_header": "Top Sites",
+ "settings_pane_topsites_header": "Các trang Web hàng đầu",
"settings_pane_topsites_body": "Truy cập vào các trang web mà bạn truy cập vào nhiều nhất.",
"settings_pane_topsites_options_showmore": "Hiển thị hai hàng",
"settings_pane_bookmarks_header": "Recent Bookmarks",
@@ -81,20 +81,18 @@ window.gActivityStreamStrings = {
"topsites_form_add_header": "New Top Site",
"topsites_form_edit_header": "Edit Top Site",
"topsites_form_title_placeholder": "Enter a title",
- "topsites_form_url_placeholder": "Type or paste a URL",
+ "topsites_form_url_placeholder": "Nhập hoặc dán URL",
"topsites_form_add_button": "Thêm",
"topsites_form_save_button": "Lưu lại",
"topsites_form_cancel_button": "Hủy bỏ",
- "topsites_form_url_validation": "Valid URL required",
+ "topsites_form_url_validation": "Yêu cầu URL hợp lệ",
"pocket_read_more": "Các chủ đề phổ biến:",
- "pocket_read_even_more": "View More Stories",
- "pocket_feedback_header": "The best of the web, curated by over 25 million people.",
+ "pocket_read_even_more": "Xem nhiều câu chuyện hơn",
+ "pocket_feedback_header": "Tốt nhất của web, được quản lý bởi hơn 25 triệu người.",
"pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.",
"highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.",
"topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
- "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.",
+ "manual_migration_explanation2": "Thử Firefox với trang đánh dấu, lịch sử và mật khẩu từ trình duyệt khác.",
"manual_migration_cancel_button": "Không, cảm ơn",
- "manual_migration_import_button": "Import Now",
- "settings_pane_body": "Chọn cái bạn muốn tải khi một tab mới được mở ra.",
- "pocket_send_feedback": "Gửi phản hồi"
+ "manual_migration_import_button": "Nhập ngay bây giờ"
};
diff --git a/browser/extensions/activity-stream/prerendered/locales/zh-CN/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/zh-CN/activity-stream-prerendered.html
index f8f8959c73c1..f0b49a5bcfd9 100644
--- a/browser/extensions/activity-stream/prerendered/locales/zh-CN/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/zh-CN/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
-
+
diff --git a/browser/extensions/activity-stream/prerendered/locales/zh-TW/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/zh-TW/activity-stream-prerendered.html
index b63d4c39248f..3f87052ddd84 100644
--- a/browser/extensions/activity-stream/prerendered/locales/zh-TW/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/prerendered/locales/zh-TW/activity-stream-prerendered.html
@@ -7,7 +7,7 @@
-
+
diff --git a/browser/extensions/activity-stream/prerendered/static/activity-stream-prerendered-debug.html b/browser/extensions/activity-stream/prerendered/static/activity-stream-prerendered-debug.html
index da6fe7875b98..8887242cd6d9 100644
--- a/browser/extensions/activity-stream/prerendered/static/activity-stream-prerendered-debug.html
+++ b/browser/extensions/activity-stream/prerendered/static/activity-stream-prerendered-debug.html
@@ -7,7 +7,7 @@
- Search the Web Search
Top Sites Access the websites you visit most.
New Tab Preferences
+ Search the Web Search
Top Sites Access the websites you visit most.
New Tab Preferences
diff --git a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
index 997c175447fd..7196c15ac66b 100644
--- a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js
@@ -677,18 +677,19 @@ describe("TelemetryFeed", () => {
describe("#handleNewTabInit", () => {
it("should set the session as preloaded if the browser is preloaded", () => {
const session = {perf: {}};
+ let preloadedBrowser = {getAttribute() { return "preloaded"; }};
sandbox.stub(instance, "addSession").returns(session);
instance.onAction(ac.SendToMain({
type: at.NEW_TAB_INIT,
- data: {url: "about:newtab", browser}
+ data: {url: "about:newtab", browser: preloadedBrowser}
}));
assert.ok(session.perf.is_preloaded);
});
it("should set the session as non-preloaded if the browser is non-preloaded", () => {
const session = {perf: {}};
- let nonPreloadedBrowser = {getAttribute() { return "false"; }};
+ let nonPreloadedBrowser = {getAttribute() { return ""; }};
sandbox.stub(instance, "addSession").returns(session);
instance.onAction(ac.SendToMain({
diff --git a/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp b/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp
index 28f2b04ed48c..4822111c4b1c 100644
--- a/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp
+++ b/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp
@@ -111,7 +111,7 @@ public:
virtual void MacroExpands(const Token &Tok, const MacroDefinition &Md,
SourceRange Range, const MacroArgs *Ma) override;
#if CLANG_VERSION_MAJOR >= 5
- virtual void MacroUndefined(const Token &tok, const MacroDefinition &md,
+ virtual void MacroUndefined(const Token &Tok, const MacroDefinition &Md,
const MacroDirective *Undef) override;
#else
virtual void MacroUndefined(const Token &Tok,
@@ -1421,8 +1421,8 @@ void PreprocessorHook::MacroExpands(const Token &Tok, const MacroDefinition &Md,
}
#if CLANG_VERSION_MAJOR >= 5
-void PreprocessorHook::MacroUndefined(const Token &tok,
- const MacroDefinition &md,
+void PreprocessorHook::MacroUndefined(const Token &Tok,
+ const MacroDefinition &Md,
const MacroDirective *Undef)
#else
void PreprocessorHook::MacroUndefined(const Token &Tok,
diff --git a/chrome/test/unit/test_no_remote_registration.js b/chrome/test/unit/test_no_remote_registration.js
index e5be45977ef5..78a000c5a6a2 100644
--- a/chrome/test/unit/test_no_remote_registration.js
+++ b/chrome/test/unit/test_no_remote_registration.js
@@ -23,14 +23,14 @@ ProtocolHandler.prototype =
defaultPort: -1,
allowPort: () => false,
newURI(aSpec, aCharset, aBaseURI) {
- let uri = Cc["@mozilla.org/network/standard-url;1"].
- createInstance(Ci.nsIURI);
- uri.spec = aSpec;
- if (!uri.scheme) {
- // We got a partial uri, so let's resolve it with the base one
- uri.spec = aBaseURI.resolve(aSpec);
+ let mutator = Cc["@mozilla.org/network/standard-url-mutator;1"]
+ .createInstance(Ci.nsIURIMutator);
+ if (aBaseURI) {
+ mutator.setSpec(aBaseURI.resolve(aSpec));
+ } else {
+ mutator.setSpec(aSpec);
}
- return uri;
+ return mutator.finalize();
},
newChannel2() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
newChannel() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
diff --git a/config/config.mk b/config/config.mk
index dd382e223f22..871579d4eba2 100644
--- a/config/config.mk
+++ b/config/config.mk
@@ -167,8 +167,9 @@ endif
endif # MOZ_PROFILE_USE
endif # NO_PROFILE_GUIDED_OPTIMIZE
+LOCALE_TOPDIR ?= $(topsrcdir)
MAKE_JARS_FLAGS = \
- -t $(topsrcdir) \
+ -t $(LOCALE_TOPDIR) \
-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
$(NULL)
@@ -354,14 +355,18 @@ ifndef L10NBASEDIR
L10NBASEDIR = $(error L10NBASEDIR not defined by configure)
endif
-EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
+EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(LOCALE_TOPDIR)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
ifdef relativesrcdir
-LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
+LOCALE_RELATIVEDIR ?= $(relativesrcdir)
+endif
+
+ifdef LOCALE_RELATIVEDIR
+LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(LOCALE_RELATIVEDIR))
endif
ifdef relativesrcdir
-MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
+MAKE_JARS_FLAGS += --relativesrcdir=$(LOCALE_RELATIVEDIR)
ifneq (en-US,$(AB_CD))
ifdef IS_LANGUAGE_REPACK
MAKE_JARS_FLAGS += --locale-mergedir=$(REAL_LOCALE_MERGEDIR)
@@ -378,7 +383,7 @@ endif # ! relativesrcdir
ifdef IS_LANGUAGE_REPACK
MERGE_FILE = $(firstword \
- $(wildcard $(REAL_LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir))/$(1)) \
+ $(wildcard $(REAL_LOCALE_MERGEDIR)/$(subst /locales,,$(LOCALE_RELATIVEDIR))/$(1)) \
$(wildcard $(LOCALE_SRCDIR)/$(1)) \
$(srcdir)/en-US/$(1) )
else
diff --git a/devtools/client/shared/test/browser.ini b/devtools/client/shared/test/browser.ini
index 965dc510e224..7c159c2e8101 100644
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -154,7 +154,6 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
[browser_layoutHelpers.js]
skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_layoutHelpers-getBoxQuads.js]
-skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_num-l10n.js]
[browser_options-view-01.js]
[browser_outputparser.js]
diff --git a/devtools/client/shared/test/browser_layoutHelpers-getBoxQuads.js b/devtools/client/shared/test/browser_layoutHelpers-getBoxQuads.js
index 9996693b2d15..99c4c1344e3d 100644
--- a/devtools/client/shared/test/browser_layoutHelpers-getBoxQuads.js
+++ b/devtools/client/shared/test/browser_layoutHelpers-getBoxQuads.js
@@ -6,214 +6,251 @@
"use strict";
-const {getAdjustedQuads} = require("devtools/shared/layout/utils");
-
const TEST_URI = TEST_URI_ROOT + "doc_layoutHelpers-getBoxQuads.html";
-add_task(function* () {
- let tab = yield addTab(TEST_URI);
- let doc = tab.linkedBrowser.contentDocument;
-
- ok(typeof getAdjustedQuads === "function", "getAdjustedQuads is defined");
+add_task(async function () {
+ let tab = await addTab(TEST_URI);
info("Running tests");
- returnsTheRightDataStructure(doc);
- isEmptyForMissingNode(doc);
- isEmptyForHiddenNodes(doc);
- defaultsToBorderBoxIfNoneProvided(doc);
- returnsLikeGetBoxQuadsInSimpleCase(doc);
- takesIframesOffsetsIntoAccount(doc);
- takesScrollingIntoAccount(doc);
- yield takesZoomIntoAccount(doc);
- returnsMultipleItemsForWrappingInlineElements(doc);
+ // `FullZoom` isn't available from the ContentTask. This code is defined by browser
+ // frontend and runs in the parent process. Here, we use the message manager
+ // to allow the Content Task to call this zoom helper whenever it needs to.
+ let mm = tab.linkedBrowser.messageManager;
+ mm.addMessageListener("devtools-test:command", async function ({ data }) {
+ switch (data) {
+ case "zoom-enlarge":
+ window.FullZoom.enlarge();
+ break;
+ case "zoom-reset":
+ await window.FullZoom.reset();
+ break;
+ case "zoom-reduce":
+ window.FullZoom.reduce();
+ break;
+ }
+ mm.sendAsyncMessage("devtools-test:done");
+ });
+
+ await ContentTask.spawn(tab.linkedBrowser, null, async function () {
+ // This function allows the Content Task to easily call `FullZoom` API via
+ // the message manager.
+ function sendCommand(cmd) {
+ let onDone = new Promise(done => {
+ addMessageListener("devtools-test:done", function listener() {
+ removeMessageListener("devtools-test:done", listener);
+ done();
+ });
+ });
+ sendAsyncMessage("devtools-test:command", cmd);
+ return onDone;
+ }
+
+ const doc = content.document;
+
+ const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+ const {getAdjustedQuads} = require("devtools/shared/layout/utils");
+
+ ok(typeof getAdjustedQuads === "function", "getAdjustedQuads is defined");
+
+ returnsTheRightDataStructure();
+ isEmptyForMissingNode();
+ isEmptyForHiddenNodes();
+ defaultsToBorderBoxIfNoneProvided();
+ returnsLikeGetBoxQuadsInSimpleCase();
+ takesIframesOffsetsIntoAccount();
+ takesScrollingIntoAccount();
+ await takesZoomIntoAccount();
+ returnsMultipleItemsForWrappingInlineElements();
+
+ function returnsTheRightDataStructure() {
+ info("Checks that the returned data contains bounds and 4 points");
+
+ let node = doc.querySelector("body");
+ let [res] = getAdjustedQuads(doc.defaultView, node, "content");
+
+ ok("bounds" in res, "The returned data has a bounds property");
+ ok("p1" in res, "The returned data has a p1 property");
+ ok("p2" in res, "The returned data has a p2 property");
+ ok("p3" in res, "The returned data has a p3 property");
+ ok("p4" in res, "The returned data has a p4 property");
+
+ for (let boundProp of
+ ["bottom", "top", "right", "left", "width", "height", "x", "y"]) {
+ ok(boundProp in res.bounds, "The bounds has a " + boundProp + " property");
+ }
+
+ for (let point of ["p1", "p2", "p3", "p4"]) {
+ for (let pointProp of ["x", "y", "z", "w"]) {
+ ok(pointProp in res[point], point + " has a " + pointProp + " property");
+ }
+ }
+ }
+
+ function isEmptyForMissingNode() {
+ info("Checks that null is returned for invalid nodes");
+
+ for (let input of [null, undefined, "", 0]) {
+ is(getAdjustedQuads(doc.defaultView, input).length, 0,
+ "A 0-length array is returned for input " + input);
+ }
+ }
+
+ function isEmptyForHiddenNodes() {
+ info("Checks that null is returned for nodes that aren't rendered");
+
+ let style = doc.querySelector("#styles");
+ is(getAdjustedQuads(doc.defaultView, style).length, 0,
+ "null is returned for a
+
+
diff --git a/editor/libeditor/crashtests/crashtests.list b/editor/libeditor/crashtests/crashtests.list
index 3bc588919f41..cbe19391c749 100644
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -95,3 +95,4 @@ load 1405747.html
load 1408170.html
load 1414581.html
load 1415231.html
+load 1425091.html
diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender
index 0f88db0a2842..5cbbd315e339 100644
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
-1142dfc557c319119a5117450718c5b67a93cb9f
+a422f907be948b92bf5c7003a01f7744391a795e
diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml
index 2f8d010bd924..0898e385086b 100644
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -10,7 +10,8 @@ build = "build.rs"
default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
-debugger = ["ws", "serde_json", "serde", "serde_derive", "image", "base64"]
+debugger = ["ws", "serde_json", "serde", "image", "base64"]
+capture = ["webrender_api/debug-serialization", "ron", "serde"]
[dependencies]
app_units = "0.6"
@@ -31,10 +32,10 @@ plane-split = "0.7"
smallvec = "0.6"
ws = { optional = true, version = "0.7.3" }
serde_json = { optional = true, version = "1.0" }
-serde = { optional = true, version = "1.0" }
-serde_derive = { optional = true, version = "1.0" }
+serde = { optional = true, version = "1.0", features = ["serde_derive"] }
image = { optional = true, version = "0.17" }
base64 = { optional = true, version = "0.3.0" }
+ron = { optional = true, version = "0.1.3" }
[dev-dependencies]
angle = {git = "https://github.com/servo/angle", branch = "servo"}
diff --git a/gfx/webrender/examples/basic.rs b/gfx/webrender/examples/basic.rs
index e83736d5ec58..3b1a2a23356d 100644
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -256,7 +256,7 @@ impl Example for App {
builder.push_border(&info, border_widths, border_details);
builder.pop_clip_id();
- if false {
+ if true {
// draw text?
let font_key = api.generate_font_key();
let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
diff --git a/gfx/webrender/examples/common/boilerplate.rs b/gfx/webrender/examples/common/boilerplate.rs
index c0be1c056d2a..2e0e8f9560d5 100644
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -263,6 +263,19 @@ pub fn main_wrapper(
) => {
api.notify_memory_pressure();
}
+ #[cfg(feature = "capture")]
+ glutin::Event::KeyboardInput(
+ glutin::ElementState::Pressed,
+ _,
+ Some(glutin::VirtualKeyCode::C),
+ ) => {
+ let path: PathBuf = "captures/example".into();
+ if path.is_dir() {
+ api.load_capture(path);
+ } else {
+ api.save_capture(path);
+ }
+ }
_ => if example.on_event(event, &api, document_id) {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut resources = ResourceUpdates::new();
@@ -285,7 +298,7 @@ pub fn main_wrapper(
resources,
);
api.generate_frame(document_id, None);
- },
+ }
}
}
diff --git a/gfx/webrender/res/brush.glsl b/gfx/webrender/res/brush.glsl
index 81e90736e944..77023ef3a364 100644
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -8,49 +8,34 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
- ivec2 user_data
+ ivec2 user_data,
+ PictureTask pic_task
);
-#define RASTERIZATION_MODE_LOCAL_SPACE 0.0
-#define RASTERIZATION_MODE_SCREEN_SPACE 1.0
-
-#define SEGMENT_ALL 0
-#define SEGMENT_TOP_LEFT 1
-#define SEGMENT_TOP_RIGHT 2
-#define SEGMENT_BOTTOM_RIGHT 3
-#define SEGMENT_BOTTOM_LEFT 4
-#define SEGMENT_TOP_MID 5
-#define SEGMENT_MID_RIGHT 6
-#define SEGMENT_BOTTOM_MID 7
-#define SEGMENT_MID_LEFT 8
-#define SEGMENT_CENTER 9
-
-#define AA_KIND_DEFAULT 0
-#define AA_KIND_SEGMENT 1
-
-#define VECS_PER_BRUSH_PRIM 4
+#define VECS_PER_BRUSH_PRIM 2
+#define VECS_PER_SEGMENT 2
struct BrushInstance {
int picture_address;
int prim_address;
- int clip_node_id;
+ int clip_chain_rect_index;
int scroll_node_id;
int clip_address;
int z;
- int segment_kind;
+ int segment_index;
ivec2 user_data;
};
BrushInstance load_brush() {
- BrushInstance bi;
+ BrushInstance bi;
bi.picture_address = aData0.x;
bi.prim_address = aData0.y;
- bi.clip_node_id = aData0.z / 65536;
+ bi.clip_chain_rect_index = aData0.z / 65536;
bi.scroll_node_id = aData0.z % 65536;
bi.clip_address = aData0.w;
bi.z = aData1.x;
- bi.segment_kind = aData1.y;
+ bi.segment_index = aData1.y;
bi.user_data = aData1.zw;
return bi;
@@ -59,19 +44,16 @@ BrushInstance load_brush() {
struct BrushPrimitive {
RectWithSize local_rect;
RectWithSize local_clip_rect;
- vec4 offsets;
- int aa_kind;
};
-BrushPrimitive fetch_brush_primitive(int address) {
- vec4 data[4] = fetch_from_resource_cache_4(address);
+BrushPrimitive fetch_brush_primitive(int address, int clip_chain_rect_index) {
+ vec4 data[2] = fetch_from_resource_cache_2(address);
- BrushPrimitive prim = BrushPrimitive(
- RectWithSize(data[0].xy, data[0].zw),
- RectWithSize(data[1].xy, data[1].zw),
- data[2],
- int(data[3].x)
- );
+ RectWithSize clip_chain_rect = fetch_clip_chain_rect(clip_chain_rect_index);
+ RectWithSize brush_clip_rect = RectWithSize(data[1].xy, data[1].zw);
+ RectWithSize clip_rect = intersect_rects(clip_chain_rect, brush_clip_rect);
+
+ BrushPrimitive prim = BrushPrimitive(RectWithSize(data[0].xy, data[0].zw), clip_rect);
return prim;
}
@@ -83,77 +65,25 @@ void main(void) {
// Load the geometry for this brush. For now, this is simply the
// local rect of the primitive. In the future, this will support
// loading segment rects, and other rect formats (glyphs).
- BrushPrimitive brush_prim = fetch_brush_primitive(brush.prim_address);
+ BrushPrimitive brush_prim =
+ fetch_brush_primitive(brush.prim_address, brush.clip_chain_rect_index);
// Fetch the segment of this brush primitive we are drawing.
- RectWithSize local_segment_rect;
- vec4 edge_aa_segment_mask;
+ int segment_address = brush.prim_address +
+ VECS_PER_BRUSH_PRIM +
+ VECS_PER_SPECIFIC_BRUSH +
+ brush.segment_index * VECS_PER_SEGMENT;
- // p0 = origin of outer rect
- // p1 = origin of inner rect
- // p2 = bottom right corner of inner rect
- // p3 = bottom right corner of outer rect
- vec2 p0 = brush_prim.local_rect.p0;
- vec2 p1 = brush_prim.local_rect.p0 + brush_prim.offsets.xy;
- vec2 p2 = brush_prim.local_rect.p0 + brush_prim.local_rect.size - brush_prim.offsets.zw;
- vec2 p3 = brush_prim.local_rect.p0 + brush_prim.local_rect.size;
-
- switch (brush.segment_kind) {
- case SEGMENT_ALL:
- local_segment_rect = brush_prim.local_rect;
- break;
-
- case SEGMENT_TOP_LEFT:
- local_segment_rect = RectWithSize(p0, p1 - p0);
- break;
- case SEGMENT_TOP_RIGHT:
- local_segment_rect = RectWithSize(vec2(p2.x, p0.y), vec2(p3.x - p2.x, p1.y - p0.y));
- break;
- case SEGMENT_BOTTOM_RIGHT:
- local_segment_rect = RectWithSize(vec2(p2.x, p2.y), vec2(p3.x - p2.x, p3.y - p2.y));
- break;
- case SEGMENT_BOTTOM_LEFT:
- local_segment_rect = RectWithSize(vec2(p0.x, p2.y), vec2(p1.x - p0.x, p3.y - p2.y));
- break;
-
- case SEGMENT_TOP_MID:
- local_segment_rect = RectWithSize(vec2(p1.x, p0.y), vec2(p2.x - p1.x, p1.y - p0.y));
- break;
- case SEGMENT_MID_RIGHT:
- local_segment_rect = RectWithSize(vec2(p2.x, p1.y), vec2(p3.x - p2.x, p2.y - p1.y));
- break;
- case SEGMENT_BOTTOM_MID:
- local_segment_rect = RectWithSize(vec2(p1.x, p2.y), vec2(p2.x - p1.x, p3.y - p2.y));
- break;
- case SEGMENT_MID_LEFT:
- local_segment_rect = RectWithSize(vec2(p0.x, p1.y), vec2(p1.x - p0.x, p2.y - p1.y));
- break;
-
- case SEGMENT_CENTER:
- local_segment_rect = RectWithSize(p1, p2 - p1);
- break;
-
- default:
- local_segment_rect = RectWithSize(vec2(0.0), vec2(0.0));
- break;
- }
-
- switch (brush_prim.aa_kind) {
- case AA_KIND_SEGMENT:
- // TODO: select these correctly based on the segment kind.
- edge_aa_segment_mask = vec4(1.0);
- break;
- case AA_KIND_DEFAULT:
- edge_aa_segment_mask = vec4(1.0);
- break;
- }
+ vec4[2] segment_data = fetch_from_resource_cache_2(segment_address);
+ RectWithSize local_segment_rect = RectWithSize(segment_data[0].xy, segment_data[0].zw);
vec2 device_pos, local_pos;
// Fetch the dynamic picture that we are drawing on.
PictureTask pic_task = fetch_picture_task(brush.picture_address);
+ ClipArea clip_area = fetch_clip_area(brush.clip_address);
- if (pic_task.rasterization_mode == RASTERIZATION_MODE_LOCAL_SPACE) {
+ if (pic_task.pic_kind_and_raster_mode > 0.0) {
local_pos = local_segment_rect.p0 + aPosition.xy * local_segment_rect.size;
// Right now - pictures only support local positions. In the future, this
@@ -161,20 +91,26 @@ void main(void) {
device_pos = pic_task.common_data.task_rect.p0 +
uDevicePixelRatio * (local_pos - pic_task.content_origin);
+#ifdef WR_FEATURE_ALPHA_PASS
+ write_clip(
+ vec2(0.0),
+ clip_area
+ );
+#endif
+
// Write the final position transformed by the orthographic device-pixel projection.
gl_Position = uTransform * vec4(device_pos, 0.0, 1.0);
} else {
VertexInfo vi;
- Layer layer = fetch_layer(brush.clip_node_id, brush.scroll_node_id);
- ClipArea clip_area = fetch_clip_area(brush.clip_address);
+ ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id);
// Write the normal vertex information out.
- if (layer.is_axis_aligned) {
+ if (scroll_node.is_axis_aligned) {
vi = write_vertex(
local_segment_rect,
brush_prim.local_clip_rect,
float(brush.z),
- layer,
+ scroll_node,
pic_task,
brush_prim.local_rect
);
@@ -189,13 +125,14 @@ void main(void) {
vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0));
#endif
} else {
+ bvec4 edge_mask = notEqual(int(segment_data[1].x) & ivec4(1, 2, 4, 8), ivec4(0));
vi = write_transform_vertex(
local_segment_rect,
brush_prim.local_rect,
brush_prim.local_clip_rect,
- edge_aa_segment_mask,
+ mix(vec4(0.0), vec4(1.0), edge_mask),
float(brush.z),
- layer,
+ scroll_node,
pic_task
);
}
@@ -221,7 +158,8 @@ void main(void) {
brush.prim_address + VECS_PER_BRUSH_PRIM,
local_pos,
brush_prim.local_rect,
- brush.user_data
+ brush.user_data,
+ pic_task
);
}
#endif
diff --git a/gfx/webrender/res/brush_image.glsl b/gfx/webrender/res/brush_image.glsl
index adca94181b34..ca10f3c7cbc3 100644
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#define VECS_PER_SPECIFIC_BRUSH 0
+
#include shared,prim_shared,brush
#ifdef WR_FEATURE_ALPHA_PASS
@@ -27,7 +29,8 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
- ivec2 user_data
+ ivec2 user_data,
+ PictureTask pic_task
) {
// TODO(gw): For now, this brush_image shader is only
// being used to draw items from the intermediate
diff --git a/gfx/webrender/res/ps_line.glsl b/gfx/webrender/res/brush_line.glsl
similarity index 78%
rename from gfx/webrender/res/ps_line.glsl
rename to gfx/webrender/res/brush_line.glsl
index d583d527854d..523575d72b44 100644
--- a/gfx/webrender/res/ps_line.glsl
+++ b/gfx/webrender/res/brush_line.glsl
@@ -2,17 +2,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include shared,prim_shared
+#define VECS_PER_SPECIFIC_BRUSH 2
-varying vec4 vColor;
+#include shared,prim_shared,brush
+
+varying vec2 vLocalPos;
+
+flat varying vec4 vColor;
flat varying int vStyle;
flat varying float vAxisSelect;
flat varying vec4 vParams;
flat varying vec2 vLocalOrigin;
-varying vec2 vLocalPos;
-
#ifdef WR_VERTEX_SHADER
+
#define LINE_ORIENTATION_VERTICAL 0
#define LINE_ORIENTATION_HORIZONTAL 1
@@ -28,22 +31,38 @@ Line fetch_line(int address) {
return Line(data[0], data[1].x, data[1].y, data[1].z);
}
-void main(void) {
- Primitive prim = load_primitive();
- Line line = fetch_line(prim.specific_prim_address);
+void brush_vs(
+ int prim_address,
+ vec2 local_pos,
+ RectWithSize local_rect,
+ ivec2 user_data,
+ PictureTask pic_task
+) {
+ vLocalPos = local_pos;
+
+ Line line = fetch_line(prim_address);
+
+ switch (int(abs(pic_task.pic_kind_and_raster_mode))) {
+ case PIC_TYPE_TEXT_SHADOW:
+ vColor = pic_task.color;
+ break;
+ default:
+ vColor = line.color;
+ break;
+ }
vec2 pos, size;
switch (int(line.orientation)) {
case LINE_ORIENTATION_HORIZONTAL:
vAxisSelect = 0.0;
- pos = prim.local_rect.p0;
- size = prim.local_rect.size;
+ pos = local_rect.p0;
+ size = local_rect.size;
break;
case LINE_ORIENTATION_VERTICAL:
vAxisSelect = 1.0;
- pos = prim.local_rect.p0.yx;
- size = prim.local_rect.size.yx;
+ pos = local_rect.p0.yx;
+ size = local_rect.size.yx;
break;
}
@@ -89,39 +108,6 @@ void main(void) {
break;
}
}
-
-#ifdef WR_FEATURE_CACHE
- vec2 device_origin = prim.task.common_data.task_rect.p0 +
- uDevicePixelRatio * (prim.local_rect.p0 - prim.task.content_origin);
- vec2 device_size = uDevicePixelRatio * prim.local_rect.size;
-
- vec2 device_pos = mix(device_origin,
- device_origin + device_size,
- aPosition.xy);
-
- vColor = prim.task.color;
- vLocalPos = mix(prim.local_rect.p0,
- prim.local_rect.p0 + prim.local_rect.size,
- aPosition.xy);
-
- gl_Position = uTransform * vec4(device_pos, 0.0, 1.0);
-#else
- vColor = line.color;
-
- #ifdef WR_FEATURE_TRANSFORM
- VertexInfo vi = write_transform_vertex_primitive(prim);
- #else
- VertexInfo vi = write_vertex(prim.local_rect,
- prim.local_clip_rect,
- prim.z,
- prim.layer,
- prim.task,
- prim.local_rect);
- #endif
-
- vLocalPos = vi.local_pos;
- write_clip(vi.screen_pos, prim.clip_area);
-#endif
}
#endif
@@ -160,22 +146,11 @@ float approx_distance(vec2 p, vec2 b0, vec2 b1, vec2 b2) {
return length(get_distance_vector(b0 - p, b1 - p, b2 - p));
}
-void main(void) {
- float alpha = 1.0;
-
- vec2 local_pos = vLocalPos;
-
-#ifdef WR_FEATURE_CACHE
-#else
- #ifdef WR_FEATURE_TRANSFORM
- alpha = init_transform_fs(vLocalPos);
- #endif
-
- alpha *= do_clip();
-#endif
-
+vec4 brush_fs() {
// Find the appropriate distance to apply the step over.
+ vec2 local_pos = vLocalPos;
float aa_range = compute_aa_range(local_pos);
+ float alpha = 1.0;
// Select the x/y coord, depending on which axis this edge is.
vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
@@ -189,7 +164,7 @@ void main(void) {
float x = mod(pos.x - vLocalOrigin.x, vParams.x);
// Calculate dash alpha (on/off) based on dash length
- alpha = min(alpha, step(x, vParams.y));
+ alpha = step(x, vParams.y);
break;
}
case LINE_STYLE_DOTTED: {
@@ -199,7 +174,7 @@ void main(void) {
// Get the dot alpha
vec2 dot_relative_pos = vec2(x, pos.y) - vParams.yz;
float dot_distance = length(dot_relative_pos) - vParams.y;
- alpha = min(alpha, distance_aa(aa_range, dot_distance));
+ alpha = distance_aa(aa_range, dot_distance);
// Clip off partial dots
alpha *= step(pos.x - vLocalOrigin.x, vParams.w);
break;
@@ -249,6 +224,6 @@ void main(void) {
}
}
- oFragColor = vColor * alpha;
+ return vColor * alpha;
}
#endif
diff --git a/gfx/webrender/res/brush_mask_corner.glsl b/gfx/webrender/res/brush_mask_corner.glsl
index 85ea442690b9..e418c50b692f 100644
--- a/gfx/webrender/res/brush_mask_corner.glsl
+++ b/gfx/webrender/res/brush_mask_corner.glsl
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#define VECS_PER_SPECIFIC_BRUSH 1
+
#include shared,prim_shared,ellipse,brush
flat varying float vClipMode;
@@ -25,7 +27,8 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
- ivec2 user_data
+ ivec2 user_data,
+ PictureTask pic_task
) {
// Load the specific primitive.
BrushMaskCornerPrimitive prim = fetch_primitive(prim_address);
diff --git a/gfx/webrender/res/brush_mask_rounded_rect.glsl b/gfx/webrender/res/brush_mask_rounded_rect.glsl
index 327e7d8f23ae..a3e7e002219d 100644
--- a/gfx/webrender/res/brush_mask_rounded_rect.glsl
+++ b/gfx/webrender/res/brush_mask_rounded_rect.glsl
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#define VECS_PER_SPECIFIC_BRUSH 4
+
#include shared,prim_shared,ellipse,brush
flat varying float vClipMode;
@@ -39,7 +41,8 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
- ivec2 user_data
+ ivec2 user_data,
+ PictureTask pic_task
) {
// Load the specific primitive.
RoundedRectPrimitive prim = fetch_rounded_rect_primitive(prim_address);
diff --git a/gfx/webrender/res/brush_solid.glsl b/gfx/webrender/res/brush_solid.glsl
index 79767072234e..981f238a3e90 100644
--- a/gfx/webrender/res/brush_solid.glsl
+++ b/gfx/webrender/res/brush_solid.glsl
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#define VECS_PER_SPECIFIC_BRUSH 1
+
#include shared,prim_shared,brush
flat varying vec4 vColor;
@@ -25,7 +27,8 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
- ivec2 user_data
+ ivec2 user_data,
+ PictureTask pic_task
) {
SolidBrush prim = fetch_solid_primitive(prim_address);
vColor = prim.color;
diff --git a/gfx/webrender/res/clip_shared.glsl b/gfx/webrender/res/clip_shared.glsl
index 90da9e7799ea..7da359ffdb2a 100644
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -11,13 +11,13 @@
#define SEGMENT_CORNER_BR 4
in int aClipRenderTaskAddress;
-in int aClipLayerAddress;
+in int aScrollNodeId;
in int aClipSegment;
in ivec4 aClipDataResourceAddress;
struct ClipMaskInstance {
int render_task_address;
- int layer_address;
+ int scroll_node_id;
int segment;
ivec2 clip_data_address;
ivec2 resource_address;
@@ -27,7 +27,7 @@ ClipMaskInstance fetch_clip_item() {
ClipMaskInstance cmi;
cmi.render_task_address = aClipRenderTaskAddress;
- cmi.layer_address = aClipLayerAddress;
+ cmi.scroll_node_id = aScrollNodeId;
cmi.segment = aClipSegment;
cmi.clip_data_address = aClipDataResourceAddress.xy;
cmi.resource_address = aClipDataResourceAddress.zw;
@@ -49,13 +49,13 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) {
// The transformed vertex function that always covers the whole clip area,
// which is the intersection of all clip instances of a given primitive
ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
- Layer layer,
+ ClipScrollNode scroll_node,
ClipArea area) {
vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
- vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer);
+ vec4 node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node);
- // compute the point position in side the layer, in CSS space
+ // compute the point position inside the scroll node, in CSS space
vec2 vertex_pos = actual_pos +
area.common_data.task_rect.p0 -
area.screen_origin;
@@ -64,7 +64,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
vLocalBounds = vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size);
- ClipVertexInfo vi = ClipVertexInfo(layer_pos.xyw, actual_pos, local_clip_rect);
+ ClipVertexInfo vi = ClipVertexInfo(node_pos.xyw, actual_pos, local_clip_rect);
return vi;
}
diff --git a/gfx/webrender/res/cs_clip_border.glsl b/gfx/webrender/res/cs_clip_border.glsl
index e2b5ae3b2abb..a82708ffa46f 100644
--- a/gfx/webrender/res/cs_clip_border.glsl
+++ b/gfx/webrender/res/cs_clip_border.glsl
@@ -64,7 +64,7 @@ BorderClipDot fetch_border_clip_dot(ivec2 address, int segment) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
- Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address);
+ ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
// Fetch the header information for this corner clip.
BorderCorner corner = fetch_border_corner(cmi.clip_data_address);
@@ -120,7 +120,7 @@ void main(void) {
vec2 pos = corner.rect.p0 + aPosition.xy * corner.rect.size;
// Transform to world pos
- vec4 world_pos = layer.transform * vec4(pos, 0.0, 1.0);
+ vec4 world_pos = scroll_node.transform * vec4(pos, 0.0, 1.0);
world_pos.xyz /= world_pos.w;
// Scale into device pixels.
@@ -132,8 +132,8 @@ void main(void) {
area.common_data.task_rect.p0;
// Calculate the local space position for this vertex.
- vec4 layer_pos = get_layer_pos(world_pos.xy, layer);
- vPos = layer_pos.xyw;
+ vec4 node_pos = get_node_pos(world_pos.xy, scroll_node);
+ vPos = node_pos.xyw;
gl_Position = uTransform * vec4(final_pos, 0.0, 1.0);
}
diff --git a/gfx/webrender/res/cs_clip_image.glsl b/gfx/webrender/res/cs_clip_image.glsl
index 009896e17c6a..d8d703f16c1e 100644
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -24,13 +24,13 @@ ImageMaskData fetch_mask_data(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
- Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address);
+ ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
ImageMaskData mask = fetch_mask_data(cmi.clip_data_address);
RectWithSize local_rect = mask.local_rect;
ImageResource res = fetch_image_resource_direct(cmi.resource_address);
ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
- layer,
+ scroll_node,
area);
vPos = vi.local_pos;
diff --git a/gfx/webrender/res/cs_clip_rectangle.glsl b/gfx/webrender/res/cs_clip_rectangle.glsl
index c1a985ce1fb9..2dafddab0f73 100644
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -58,13 +58,11 @@ ClipData fetch_clip(ivec2 address) {
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
- Layer layer = fetch_layer(cmi.layer_address, cmi.layer_address);
+ ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
ClipData clip = fetch_clip(cmi.clip_data_address);
RectWithSize local_rect = clip.rect.rect;
- ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
- layer,
- area);
+ ClipVertexInfo vi = write_clip_tile_vertex(local_rect, scroll_node, area);
vPos = vi.local_pos;
vClipMode = clip.rect.mode.x;
diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl
index 153cab776722..14ca38a9330f 100644
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -69,7 +69,8 @@ vec4[2] fetch_from_resource_cache_2(int address) {
#ifdef WR_VERTEX_SHADER
-#define VECS_PER_LAYER 7
+#define VECS_PER_CLIP_SCROLL_NODE 5
+#define VECS_PER_LOCAL_CLIP_RECT 1
#define VECS_PER_RENDER_TASK 3
#define VECS_PER_PRIM_HEADER 2
#define VECS_PER_TEXT_RUN 3
@@ -77,6 +78,7 @@ vec4[2] fetch_from_resource_cache_2(int address) {
#define VECS_PER_GRADIENT_STOP 2
uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
+uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects;
uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
// Instanced attributes
@@ -143,9 +145,6 @@ vec4 fetch_from_resource_cache_1(int address) {
struct ClipScrollNode {
mat4 transform;
- vec4 local_clip_rect;
- vec2 reference_frame_relative_scroll_offset;
- vec2 scroll_offset;
bool is_axis_aligned;
};
@@ -156,7 +155,7 @@ ClipScrollNode fetch_clip_scroll_node(int index) {
// This is required because trying to use an offset
// of more than 8 texels doesn't work on some versions
// of OSX.
- ivec2 uv = get_fetch_uv(index, VECS_PER_LAYER);
+ ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
ivec2 uv0 = ivec2(uv.x + 0, uv.y);
node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
@@ -164,41 +163,16 @@ ClipScrollNode fetch_clip_scroll_node(int index) {
node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
- vec4 clip_rect = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
- node.local_clip_rect = clip_rect;
-
- vec4 offsets = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
- node.reference_frame_relative_scroll_offset = offsets.xy;
- node.scroll_offset = offsets.zw;
-
- vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
+ vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
node.is_axis_aligned = misc.x == 0.0;
return node;
}
-struct Layer {
- mat4 transform;
- RectWithSize local_clip_rect;
- bool is_axis_aligned;
-};
-
-Layer fetch_layer(int clip_node_id, int scroll_node_id) {
- ClipScrollNode clip_node = fetch_clip_scroll_node(clip_node_id);
- ClipScrollNode scroll_node = fetch_clip_scroll_node(scroll_node_id);
-
- Layer layer;
- layer.transform = scroll_node.transform;
-
- vec4 local_clip_rect = clip_node.local_clip_rect;
- local_clip_rect.xy += clip_node.reference_frame_relative_scroll_offset;
- local_clip_rect.xy -= scroll_node.reference_frame_relative_scroll_offset;
- local_clip_rect.xy -= scroll_node.scroll_offset;
-
- layer.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw);
- layer.is_axis_aligned = scroll_node.is_axis_aligned;
-
- return layer;
+RectWithSize fetch_clip_chain_rect(int index) {
+ ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT);
+ vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0));
+ return RectWithSize(rect.xy, rect.zw);
}
struct RenderTaskCommonData {
@@ -257,6 +231,10 @@ RenderTaskCommonData fetch_render_task_common_data(int index) {
return data;
}
+#define PIC_TYPE_IMAGE 1
+#define PIC_TYPE_TEXT_SHADOW 2
+#define PIC_TYPE_BOX_SHADOW 3
+
/*
The dynamic picture that this brush exists on. Right now, it
contains minimal information. In the future, it will describe
@@ -265,7 +243,7 @@ RenderTaskCommonData fetch_render_task_common_data(int index) {
struct PictureTask {
RenderTaskCommonData common_data;
vec2 content_origin;
- float rasterization_mode;
+ float pic_kind_and_raster_mode;
vec4 color;
};
@@ -401,7 +379,7 @@ struct PrimitiveInstance {
int render_task_index;
int clip_task_index;
int scroll_node_id;
- int clip_node_id;
+ int clip_chain_rect_index;
int z;
int user_data0;
int user_data1;
@@ -415,7 +393,7 @@ PrimitiveInstance fetch_prim_instance() {
pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER;
pi.render_task_index = aData0.y;
pi.clip_task_index = aData0.z;
- pi.clip_node_id = aData0.w / 65536;
+ pi.clip_chain_rect_index = aData0.w / 65536;
pi.scroll_node_id = aData0.w % 65536;
pi.z = aData1.x;
pi.user_data0 = aData1.y;
@@ -453,7 +431,7 @@ CompositeInstance fetch_composite_instance() {
}
struct Primitive {
- Layer layer;
+ ClipScrollNode scroll_node;
ClipArea clip_area;
PictureTask task;
RectWithSize local_rect;
@@ -481,13 +459,15 @@ Primitive load_primitive() {
Primitive prim;
- prim.layer = fetch_layer(pi.clip_node_id, pi.scroll_node_id);
+ prim.scroll_node = fetch_clip_scroll_node(pi.scroll_node_id);
prim.clip_area = fetch_clip_area(pi.clip_task_index);
prim.task = fetch_picture_task(pi.render_task_index);
+ RectWithSize clip_chain_rect = fetch_clip_chain_rect(pi.clip_chain_rect_index);
+
PrimitiveGeometry geom = fetch_primitive_geometry(pi.prim_address);
prim.local_rect = geom.local_rect;
- prim.local_clip_rect = geom.local_clip_rect;
+ prim.local_clip_rect = intersect_rects(clip_chain_rect, geom.local_clip_rect);
prim.specific_prim_address = pi.specific_prim_address;
prim.user_data0 = pi.user_data0;
@@ -515,50 +495,50 @@ bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float
// Apply the inverse transform "inv_transform"
// to the reference point "ref" in CSS space,
-// producing a local point on a layer plane,
+// producing a local point on a ClipScrollNode plane,
// set by a base point "a" and a normal "n".
vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
vec3 p = vec3(ref, -10000.0);
vec3 d = vec3(0, 0, 1.0);
float t = 0.0;
- // get an intersection of the layer plane with Z axis vector,
+ // get an intersection of the ClipScrollNode plane with Z axis vector,
// originated from the "ref" point
ray_plane(n, a, p, d, t);
- float z = p.z + d.z * t; // Z of the visible point on the layer
+ float z = p.z + d.z * t; // Z of the visible point on the ClipScrollNode
vec4 r = inv_transform * vec4(ref, z, 1.0);
return r;
}
-// Given a CSS space position, transform it back into the layer space.
-vec4 get_layer_pos(vec2 pos, Layer layer) {
- // get a point on the layer plane
- vec4 ah = layer.transform * vec4(0.0, 0.0, 0.0, 1.0);
+// Given a CSS space position, transform it back into the ClipScrollNode space.
+vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
+ // get a point on the scroll node plane
+ vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
vec3 a = ah.xyz / ah.w;
- // get the normal to the layer plane
- mat4 inv_transform = inverse(layer.transform);
+ // get the normal to the scroll node plane
+ mat4 inv_transform = inverse(node.transform);
vec3 n = transpose(mat3(inv_transform)) * vec3(0.0, 0.0, 1.0);
return untransform(pos, n, a, inv_transform);
}
// Compute a snapping offset in world space (adjusted to pixel ratio),
-// given local position on the layer and a snap rectangle.
+// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
- Layer layer,
+ ClipScrollNode scroll_node,
RectWithSize snap_rect) {
// Ensure that the snap rect is at *least* one device pixel in size.
// TODO(gw): It's not clear to me that this is "correct". Specifically,
// how should it interact with sub-pixel snap rects when there
- // is a layer transform with scale present? But it does fix
+ // is a scroll_node transform with scale present? But it does fix
// the test cases we have in Servo that are failing without it
// and seem better than not having this at all.
snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
// Transform the snap corners to the world space.
- vec4 world_snap_p0 = layer.transform * vec4(snap_rect.p0, 0.0, 1.0);
- vec4 world_snap_p1 = layer.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
+ vec4 world_snap_p0 = scroll_node.transform * vec4(snap_rect.p0, 0.0, 1.0);
+ vec4 world_snap_p1 = scroll_node.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
// Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
vec4(world_snap_p0.ww, world_snap_p1.ww);
@@ -579,7 +559,7 @@ struct VertexInfo {
VertexInfo write_vertex(RectWithSize instance_rect,
RectWithSize local_clip_rect,
float z,
- Layer layer,
+ ClipScrollNode scroll_node,
PictureTask task,
RectWithSize snap_rect) {
@@ -587,13 +567,13 @@ VertexInfo write_vertex(RectWithSize instance_rect,
vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
// Clamp to the two local clip rects.
- vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect);
+ vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
/// Compute the snapping offset.
- vec2 snap_offset = compute_snap_offset(clamped_local_pos, layer, snap_rect);
+ vec2 snap_offset = compute_snap_offset(clamped_local_pos, scroll_node, snap_rect);
// Transform the current vertex to world space.
- vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0);
+ vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
@@ -633,19 +613,15 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
RectWithSize local_clip_rect,
vec4 clip_edge_mask,
float z,
- Layer layer,
+ ClipScrollNode scroll_node,
PictureTask task) {
- // Calculate a clip rect from local clip + layer clip.
+ // Calculate a clip rect from local_rect + local clip
RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect);
- clip_rect.p0 = clamp_rect(clip_rect.p0, layer.local_clip_rect);
- clip_rect.p1 = clamp_rect(clip_rect.p1, layer.local_clip_rect);
-
- // Calculate a clip rect from local_rect + local clip + layer clip.
RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect);
segment_rect.p0 = clamp(segment_rect.p0, clip_rect.p0, clip_rect.p1);
segment_rect.p1 = clamp(segment_rect.p1, clip_rect.p0, clip_rect.p1);
- // Calculate a clip rect from local_rect + local clip + layer clip.
+ // Calculate a clip rect from local_rect + local clip
RectWithEndpoint prim_rect = to_rect_with_endpoint(local_prim_rect);
prim_rect.p0 = clamp(prim_rect.p0, clip_rect.p0, clip_rect.p1);
prim_rect.p1 = clamp(prim_rect.p1, clip_rect.p0, clip_rect.p1);
@@ -666,7 +642,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy;
// Transform the current vertex to the world cpace.
- vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
+ vec4 world_pos = scroll_node.transform * vec4(local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
@@ -676,7 +652,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
// We also want that to apply to any interpolators. However, we
// want a constant Z across the primitive, since we're using it
// for draw ordering - so scale by the W coord to ensure this.
- vec4 final_pos = vec4(world_pos.xy * uDevicePixelRatio + task_offset,
+ vec4 final_pos = vec4((device_pos + task_offset) * world_pos.w,
z * world_pos.w,
world_pos.w);
gl_Position = uTransform * final_pos;
@@ -698,7 +674,7 @@ VertexInfo write_transform_vertex_primitive(Primitive prim) {
prim.local_clip_rect,
vec4(0.0),
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task
);
}
diff --git a/gfx/webrender/res/ps_angle_gradient.glsl b/gfx/webrender/res/ps_angle_gradient.glsl
index 7056fc5c2b18..81abc6bc4dd0 100644
--- a/gfx/webrender/res/ps_angle_gradient.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -23,7 +23,7 @@ void main(void) {
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
diff --git a/gfx/webrender/res/ps_border_corner.glsl b/gfx/webrender/res/ps_border_corner.glsl
index 1064543fb9e7..bea4ffcfea88 100644
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -303,13 +303,13 @@ void main(void) {
prim.local_clip_rect,
vec4(1.0),
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
#endif
diff --git a/gfx/webrender/res/ps_border_edge.glsl b/gfx/webrender/res/ps_border_edge.glsl
index b7a00872173d..7dc070828515 100644
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -221,13 +221,13 @@ void main(void) {
prim.local_clip_rect,
vec4(1.0),
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
#endif
diff --git a/gfx/webrender/res/ps_gradient.glsl b/gfx/webrender/res/ps_gradient.glsl
index 1aec09fc9d1c..390a25058151 100644
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -72,7 +72,7 @@ void main(void) {
prim.local_clip_rect,
vec4(1.0),
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task);
vLocalPos = vi.local_pos;
vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
@@ -80,7 +80,7 @@ void main(void) {
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
diff --git a/gfx/webrender/res/ps_image.glsl b/gfx/webrender/res/ps_image.glsl
index cfff1c3162e3..fe36b9aaf179 100644
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -34,7 +34,7 @@ void main(void) {
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
vLocalPos = vi.local_pos - prim.local_rect.p0;
diff --git a/gfx/webrender/res/ps_radial_gradient.glsl b/gfx/webrender/res/ps_radial_gradient.glsl
index f7d65485f542..46483ee5b07d 100644
--- a/gfx/webrender/res/ps_radial_gradient.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.glsl
@@ -25,7 +25,7 @@ void main(void) {
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
diff --git a/gfx/webrender/res/ps_text_run.glsl b/gfx/webrender/res/ps_text_run.glsl
index ed58e2c6d4b1..3c0dd9069423 100644
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -22,17 +22,18 @@ varying vec4 vUvClip;
#define MODE_SUBPX_BG_PASS0 4
#define MODE_SUBPX_BG_PASS1 5
#define MODE_SUBPX_BG_PASS2 6
-#define MODE_BITMAP 7
-#define MODE_COLOR_BITMAP 8
+#define MODE_SUBPX_DUAL_SOURCE 7
+#define MODE_BITMAP 8
+#define MODE_COLOR_BITMAP 9
VertexInfo write_text_vertex(vec2 clamped_local_pos,
RectWithSize local_clip_rect,
float z,
- Layer layer,
+ ClipScrollNode scroll_node,
PictureTask task,
RectWithSize snap_rect) {
// Transform the current vertex to world space.
- vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0);
+ vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
@@ -44,12 +45,12 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// For transformed subpixels, we just need to align the glyph origin to a device pixel.
- // Only check the layer transform's translation since the scales and axes match.
- vec2 world_snap_p0 = snap_rect.p0 + layer.transform[3].xy * uDevicePixelRatio;
+ // Only check the scroll node transform's translation since the scales and axes match.
+ vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio;
final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0;
#elif !defined(WR_FEATURE_TRANSFORM)
- // Compute the snapping offset only if the layer transform is axis-aligned.
- final_pos += compute_snap_offset(clamped_local_pos, layer, snap_rect);
+ // Compute the snapping offset only if the scroll node transform is axis-aligned.
+ final_pos += compute_snap_offset(clamped_local_pos, scroll_node, snap_rect);
#endif
gl_Position = uTransform * vec4(final_pos, z, 1.0);
@@ -73,7 +74,7 @@ void main(void) {
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// Transform from local space to glyph space.
- mat2 transform = mat2(prim.layer.transform) * uDevicePixelRatio;
+ mat2 transform = mat2(prim.scroll_node.transform) * uDevicePixelRatio;
// Compute the glyph rect in glyph space.
RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset),
@@ -86,15 +87,12 @@ void main(void) {
// Select the corner of the glyph's local space rect that we are processing.
vec2 local_pos = local_rect.p0 + local_rect.size * aPosition.xy;
- // Calculate a combined local clip rect.
- RectWithSize local_clip_rect = intersect_rects(prim.local_clip_rect, prim.layer.local_clip_rect);
-
// If the glyph's local rect would fit inside the local clip rect, then select a corner from
// the device space glyph rect to reduce overdraw of clipped pixels in the fragment shader.
// Otherwise, fall back to clamping the glyph's local rect to the local clip rect.
- local_pos = rect_inside_rect(local_rect, local_clip_rect) ?
+ local_pos = rect_inside_rect(local_rect, prim.local_clip_rect) ?
inv * (glyph_rect.p0 + glyph_rect.size * aPosition.xy) :
- clamp_rect(local_pos, local_clip_rect);
+ clamp_rect(local_pos, prim.local_clip_rect);
#else
// Scale from glyph space to local space.
float scale = res.scale / uDevicePixelRatio;
@@ -106,14 +104,14 @@ void main(void) {
// Select the corner of the glyph rect that we are processing.
vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy;
- // Clamp to the two local clip rects.
- local_pos = clamp_rect(clamp_rect(local_pos, prim.local_clip_rect), prim.layer.local_clip_rect);
+ // Clamp to the local clip rect.
+ local_pos = clamp_rect(local_pos, prim.local_clip_rect);
#endif
VertexInfo vi = write_text_vertex(local_pos,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
glyph_rect);
@@ -134,6 +132,7 @@ void main(void) {
break;
case MODE_SUBPX_PASS1:
case MODE_SUBPX_BG_PASS2:
+ case MODE_SUBPX_DUAL_SOURCE:
vMaskSwizzle = vec2(1.0, 0.0);
vColor = text.color;
break;
@@ -170,6 +169,12 @@ void main(void) {
alpha *= float(all(greaterThanEqual(vUvClip, vec4(0.0))));
#endif
+#ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
+ vec4 alpha_mask = mask * alpha;
+ oFragColor = vColor * alpha_mask;
+ oFragBlend = alpha_mask * vColor.a;
+#else
oFragColor = vColor * mask * alpha;
+#endif
}
#endif
diff --git a/gfx/webrender/res/ps_yuv_image.glsl b/gfx/webrender/res/ps_yuv_image.glsl
index a82ea142f6fe..e8a51016e66b 100644
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -43,7 +43,7 @@ void main(void) {
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
- prim.layer,
+ prim.scroll_node,
prim.task,
prim.local_rect);
vLocalPos = vi.local_pos - prim.local_rect.p0;
diff --git a/gfx/webrender/res/shared.glsl b/gfx/webrender/res/shared.glsl
index 87d0251fd0ce..16248cc243b2 100644
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -8,6 +8,10 @@
#extension GL_OES_EGL_image_external_essl3 : require
#endif
+#ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
+#extension GL_ARB_explicit_attrib_location : require
+#endif
+
#include base
// The textureLod() doesn't support samplerExternalOES for WR_FEATURE_TEXTURE_EXTERNAL.
@@ -46,7 +50,12 @@
// Uniform inputs
// Fragment shader outputs
- out vec4 oFragColor;
+ #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
+ layout(location = 0, index = 0) out vec4 oFragColor;
+ layout(location = 0, index = 1) out vec4 oFragBlend;
+ #else
+ out vec4 oFragColor;
+ #endif
#endif
//======================================================================================
diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs
new file mode 100644
index 000000000000..ef5bdc455d01
--- /dev/null
+++ b/gfx/webrender/src/batch.rs
@@ -0,0 +1,1484 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{DeviceIntRect, DeviceIntSize, ImageKey, LayerToWorldScale};
+use api::{ExternalImageType, FilterOp, ImageRendering, LayerRect};
+use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat};
+use api::{LayerToWorldTransform, WorldPixel};
+use border::{BorderCornerInstance, BorderCornerSide};
+use clip::{ClipSource, ClipStore};
+use clip_scroll_tree::{CoordinateSystemId};
+use euclid::{TypedTransform3D, vec3};
+use glyph_rasterizer::GlyphFormat;
+use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
+use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex};
+use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, PictureType};
+use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
+use internal_types::{FastHashMap, SourceTexture};
+use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
+use plane_split::{BspSplitter, Polygon, Splitter};
+use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
+use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, PrimitiveRun};
+use render_task::{ClipWorkItem};
+use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind};
+use render_task::{RenderTaskTree};
+use renderer::{BlendMode, ImageBufferKind};
+use renderer::BLOCKS_PER_UV_RECT;
+use resource_cache::{GlyphFetchResult, ResourceCache};
+use std::{usize, f32, i32};
+use tiling::{RenderTargetContext, RenderTargetKind};
+use util::{MatrixHelpers, TransformedRectKind};
+
+// Special sentinel value recognized by the shader. It is considered to be
+// a dummy task that doesn't mask out anything.
+const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum TransformBatchKind {
+ TextRun(GlyphFormat),
+ Image(ImageBufferKind),
+ YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
+ AlignedGradient,
+ AngleGradient,
+ RadialGradient,
+ BorderCorner,
+ BorderEdge,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum BrushImageSourceKind {
+ Alpha,
+ Color,
+ ColorAlphaMask,
+}
+
+impl BrushImageSourceKind {
+ pub fn from_render_target_kind(render_target_kind: RenderTargetKind) -> BrushImageSourceKind {
+ match render_target_kind {
+ RenderTargetKind::Color => BrushImageSourceKind::Color,
+ RenderTargetKind::Alpha => BrushImageSourceKind::Alpha,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum BrushBatchKind {
+ Image(BrushImageSourceKind),
+ Solid,
+ Line,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum BatchKind {
+ Composite {
+ task_id: RenderTaskId,
+ source_id: RenderTaskId,
+ backdrop_id: RenderTaskId,
+ },
+ HardwareComposite,
+ SplitComposite,
+ Blend,
+ Transformable(TransformedRectKind, TransformBatchKind),
+ Brush(BrushBatchKind),
+}
+
+/// Optional textures that can be used as a source in the shaders.
+/// Textures that are not used by the batch are equal to TextureId::invalid().
+#[derive(Copy, Clone, Debug)]
+pub struct BatchTextures {
+ pub colors: [SourceTexture; 3],
+}
+
+impl BatchTextures {
+ pub fn no_texture() -> Self {
+ BatchTextures {
+ colors: [SourceTexture::Invalid; 3],
+ }
+ }
+
+ pub fn render_target_cache() -> Self {
+ BatchTextures {
+ colors: [
+ SourceTexture::CacheRGBA8,
+ SourceTexture::CacheA8,
+ SourceTexture::Invalid,
+ ],
+ }
+ }
+
+ pub fn color(texture: SourceTexture) -> Self {
+ BatchTextures {
+ colors: [texture, SourceTexture::Invalid, SourceTexture::Invalid],
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct AlphaPrimitiveBatch {
+ pub key: BatchKey,
+ pub instances: Vec,
+ pub item_rects: Vec,
+}
+
+impl AlphaPrimitiveBatch {
+ pub fn new(key: BatchKey) -> AlphaPrimitiveBatch {
+ AlphaPrimitiveBatch {
+ key,
+ instances: Vec::new(),
+ item_rects: Vec::new(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct OpaquePrimitiveBatch {
+ pub key: BatchKey,
+ pub instances: Vec,
+}
+
+impl OpaquePrimitiveBatch {
+ pub fn new(key: BatchKey) -> OpaquePrimitiveBatch {
+ OpaquePrimitiveBatch {
+ key,
+ instances: Vec::new(),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct BatchKey {
+ pub kind: BatchKind,
+ pub blend_mode: BlendMode,
+ pub textures: BatchTextures,
+}
+
+impl BatchKey {
+ pub fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
+ BatchKey {
+ kind,
+ blend_mode,
+ textures,
+ }
+ }
+
+ pub fn is_compatible_with(&self, other: &BatchKey) -> bool {
+ self.kind == other.kind && self.blend_mode == other.blend_mode &&
+ textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
+ textures_compatible(self.textures.colors[1], other.textures.colors[1]) &&
+ textures_compatible(self.textures.colors[2], other.textures.colors[2])
+ }
+}
+
+#[inline]
+fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
+ t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
+}
+
+pub struct AlphaBatchList {
+ pub batches: Vec,
+}
+
+impl AlphaBatchList {
+ fn new() -> Self {
+ AlphaBatchList {
+ batches: Vec::new(),
+ }
+ }
+
+ pub fn get_suitable_batch(
+ &mut self,
+ key: BatchKey,
+ item_bounding_rect: &DeviceIntRect,
+ ) -> &mut Vec {
+ let mut selected_batch_index = None;
+
+ match (key.kind, key.blend_mode) {
+ (BatchKind::Composite { .. }, _) => {
+ // Composites always get added to their own batch.
+ // This is because the result of a composite can affect
+ // the input to the next composite. Perhaps we can
+ // optimize this in the future.
+ }
+ (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelWithBgColor) |
+ (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelVariableTextColor) => {
+ 'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
+ // Subpixel text is drawn in two passes. Because of this, we need
+ // to check for overlaps with every batch (which is a bit different
+ // than the normal batching below).
+ for item_rect in &batch.item_rects {
+ if item_rect.intersects(item_bounding_rect) {
+ break 'outer_text;
+ }
+ }
+
+ if batch.key.is_compatible_with(&key) {
+ selected_batch_index = Some(batch_index);
+ break;
+ }
+ }
+ }
+ _ => {
+ 'outer_default: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
+ // For normal batches, we only need to check for overlaps for batches
+ // other than the first batch we consider. If the first batch
+ // is compatible, then we know there isn't any potential overlap
+ // issues to worry about.
+ if batch.key.is_compatible_with(&key) {
+ selected_batch_index = Some(batch_index);
+ break;
+ }
+
+ // check for intersections
+ for item_rect in &batch.item_rects {
+ if item_rect.intersects(item_bounding_rect) {
+ break 'outer_default;
+ }
+ }
+ }
+ }
+ }
+
+ if selected_batch_index.is_none() {
+ let new_batch = AlphaPrimitiveBatch::new(key);
+ selected_batch_index = Some(self.batches.len());
+ self.batches.push(new_batch);
+ }
+
+ let batch = &mut self.batches[selected_batch_index.unwrap()];
+ batch.item_rects.push(*item_bounding_rect);
+
+ &mut batch.instances
+ }
+}
+
+pub struct OpaqueBatchList {
+ pub pixel_area_threshold_for_new_batch: i32,
+ pub batches: Vec,
+}
+
+impl OpaqueBatchList {
+ fn new(pixel_area_threshold_for_new_batch: i32) -> Self {
+ OpaqueBatchList {
+ batches: Vec::new(),
+ pixel_area_threshold_for_new_batch,
+ }
+ }
+
+ pub fn get_suitable_batch(
+ &mut self,
+ key: BatchKey,
+ item_bounding_rect: &DeviceIntRect
+ ) -> &mut Vec {
+ let mut selected_batch_index = None;
+ let item_area = item_bounding_rect.size.area();
+
+ // If the area of this primitive is larger than the given threshold,
+ // then it is large enough to warrant breaking a batch for. In this
+ // case we just see if it can be added to the existing batch or
+ // create a new one.
+ if item_area > self.pixel_area_threshold_for_new_batch {
+ if let Some(ref batch) = self.batches.last() {
+ if batch.key.is_compatible_with(&key) {
+ selected_batch_index = Some(self.batches.len() - 1);
+ }
+ }
+ } else {
+ // Otherwise, look back through a reasonable number of batches.
+ for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
+ if batch.key.is_compatible_with(&key) {
+ selected_batch_index = Some(batch_index);
+ break;
+ }
+ }
+ }
+
+ if selected_batch_index.is_none() {
+ let new_batch = OpaquePrimitiveBatch::new(key);
+ selected_batch_index = Some(self.batches.len());
+ self.batches.push(new_batch);
+ }
+
+ let batch = &mut self.batches[selected_batch_index.unwrap()];
+
+ &mut batch.instances
+ }
+
+ fn finalize(&mut self) {
+ // Reverse the instance arrays in the opaque batches
+ // to get maximum z-buffer efficiency by drawing
+ // front-to-back.
+ // TODO(gw): Maybe we can change the batch code to
+ // build these in reverse and avoid having
+ // to reverse the instance array here.
+ for batch in &mut self.batches {
+ batch.instances.reverse();
+ }
+ }
+}
+
+pub struct BatchList {
+ pub alpha_batch_list: AlphaBatchList,
+ pub opaque_batch_list: OpaqueBatchList,
+}
+
+impl BatchList {
+ pub fn new(screen_size: DeviceIntSize) -> Self {
+ // The threshold for creating a new batch is
+ // one quarter the screen size.
+ let batch_area_threshold = screen_size.width * screen_size.height / 4;
+
+ BatchList {
+ alpha_batch_list: AlphaBatchList::new(),
+ opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
+ }
+ }
+
+ pub fn get_suitable_batch(
+ &mut self,
+ key: BatchKey,
+ item_bounding_rect: &DeviceIntRect,
+ ) -> &mut Vec {
+ match key.blend_mode {
+ BlendMode::None => {
+ self.opaque_batch_list
+ .get_suitable_batch(key, item_bounding_rect)
+ }
+ BlendMode::PremultipliedAlpha |
+ BlendMode::PremultipliedDestOut |
+ BlendMode::SubpixelConstantTextColor(..) |
+ BlendMode::SubpixelVariableTextColor |
+ BlendMode::SubpixelWithBgColor |
+ BlendMode::SubpixelDualSource => {
+ self.alpha_batch_list
+ .get_suitable_batch(key, item_bounding_rect)
+ }
+ }
+ }
+
+ fn finalize(&mut self) {
+ self.opaque_batch_list.finalize()
+ }
+}
+
+/// Encapsulates the logic of building batches for items that are blended.
+pub struct AlphaBatcher {
+ pub batch_list: BatchList,
+ pub text_run_cache_prims: FastHashMap>,
+ pub line_cache_prims: Vec,
+ glyph_fetch_buffer: Vec,
+}
+
+impl AlphaBatcher {
+ pub fn new(screen_size: DeviceIntSize) -> Self {
+ AlphaBatcher {
+ batch_list: BatchList::new(screen_size),
+ glyph_fetch_buffer: Vec::new(),
+ text_run_cache_prims: FastHashMap::default(),
+ line_cache_prims: Vec::new(),
+ }
+ }
+
+ pub fn build(
+ &mut self,
+ tasks: &[RenderTaskId],
+ ctx: &RenderTargetContext,
+ gpu_cache: &mut GpuCache,
+ render_tasks: &RenderTaskTree,
+ deferred_resolves: &mut Vec,
+ ) {
+ for &task_id in tasks {
+ match render_tasks[task_id].kind {
+ RenderTaskKind::Picture(ref pic_task) => {
+ let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index;
+ let pic = &ctx.prim_store.cpu_pictures[pic_index.0];
+ self.add_pic_to_batch(
+ pic,
+ task_id,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ deferred_resolves,
+ );
+ }
+ _ => {
+ unreachable!();
+ }
+ }
+ }
+
+ self.batch_list.finalize();
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.batch_list.opaque_batch_list.batches.is_empty() &&
+ self.batch_list.alpha_batch_list.batches.is_empty()
+ }
+
+ fn add_pic_to_batch(
+ &mut self,
+ pic: &PicturePrimitive,
+ task_id: RenderTaskId,
+ ctx: &RenderTargetContext,
+ gpu_cache: &mut GpuCache,
+ render_tasks: &RenderTaskTree,
+ deferred_resolves: &mut Vec,
+ ) {
+ let task_address = render_tasks.get_task_address(task_id);
+
+ // Even though most of the time a splitter isn't used or needed,
+ // they are cheap to construct so we will always pass one down.
+ let mut splitter = BspSplitter::new();
+
+ // Add each run in this picture to the batch.
+ for run in &pic.runs {
+ let scroll_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
+ let scroll_id = scroll_node.node_data_index;
+ self.add_run_to_batch(
+ run,
+ scroll_id,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ task_id,
+ task_address,
+ deferred_resolves,
+ &mut splitter,
+ pic.picture_type(),
+ );
+ }
+
+ // Flush the accumulated plane splits onto the task tree.
+ // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
+ for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
+ let prim_index = PrimitiveIndex(poly.anchor);
+ debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
+ let pp = &poly.points;
+ let gpu_blocks = [
+ [pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
+ [pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
+ [pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
+ ];
+ let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
+ let key = BatchKey::new(
+ BatchKind::SplitComposite,
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::no_texture(),
+ );
+ let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
+ let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
+ let batch = self.batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug"));
+ let source_task_address = render_tasks.get_task_address(pic.render_task_id.expect("bug"));
+ let gpu_address = gpu_handle.as_int(gpu_cache);
+
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ source_task_address,
+ RenderTaskAddress(0),
+ gpu_address,
+ 0,
+ prim_index.0 as i32,
+ 0,
+ 0,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ }
+
+ // Helper to add an entire primitive run to a batch list.
+ // TODO(gw): Restructure this so the param list isn't quite
+ // so daunting!
+ fn add_run_to_batch(
+ &mut self,
+ run: &PrimitiveRun,
+ scroll_id: ClipScrollNodeIndex,
+ ctx: &RenderTargetContext,
+ gpu_cache: &mut GpuCache,
+ render_tasks: &RenderTaskTree,
+ task_id: RenderTaskId,
+ task_address: RenderTaskAddress,
+ deferred_resolves: &mut Vec,
+ splitter: &mut BspSplitter,
+ pic_type: PictureType,
+ ) {
+ for i in 0 .. run.count {
+ let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
+
+ let metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
+
+ // Now that we walk the primitive runs in order to add
+ // items to batches, we need to check if they are
+ // visible here.
+ // We currently only support culling on normal (Image)
+ // picture types.
+ // TODO(gw): Support culling on shadow image types.
+ if pic_type != PictureType::Image || metadata.screen_rect.is_some() {
+ self.add_prim_to_batch(
+ metadata.clip_chain_rect_index,
+ scroll_id,
+ prim_index,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ task_id,
+ task_address,
+ deferred_resolves,
+ splitter,
+ pic_type,
+ );
+ }
+ }
+ }
+
+ // Adds a primitive to a batch.
+ // It can recursively call itself in some situations, for
+ // example if it encounters a picture where the items
+ // in that picture are being drawn into the same target.
+ fn add_prim_to_batch(
+ &mut self,
+ clip_chain_rect_index: ClipChainRectIndex,
+ scroll_id: ClipScrollNodeIndex,
+ prim_index: PrimitiveIndex,
+ ctx: &RenderTargetContext,
+ gpu_cache: &mut GpuCache,
+ render_tasks: &RenderTaskTree,
+ task_id: RenderTaskId,
+ task_address: RenderTaskAddress,
+ deferred_resolves: &mut Vec,
+ splitter: &mut BspSplitter,
+ pic_type: PictureType,
+ ) {
+ let z = prim_index.0 as i32;
+ let prim_metadata = ctx.prim_store.get_metadata(prim_index);
+ let scroll_node = &ctx.node_data[scroll_id.0 as usize];
+ // TODO(gw): Calculating this for every primitive is a bit
+ // wasteful. We should probably cache this in
+ // the scroll node...
+ let transform_kind = scroll_node.transform.transform_kind();
+ let item_bounding_rect = &match prim_metadata.screen_rect {
+ Some(screen_rect) => screen_rect,
+ None => {
+ debug_assert_ne!(pic_type, PictureType::Image);
+ DeviceIntRect::zero()
+ }
+ };
+ let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
+ let no_textures = BatchTextures::no_texture();
+ let clip_task_address = prim_metadata
+ .clip_task_id
+ .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
+ let base_instance = SimplePrimitiveInstance::new(
+ prim_cache_address,
+ task_address,
+ clip_task_address,
+ clip_chain_rect_index,
+ scroll_id,
+ z,
+ );
+
+ let blend_mode = ctx.prim_store.get_blend_mode(prim_metadata, transform_kind);
+
+ match prim_metadata.prim_kind {
+ PrimitiveKind::Brush => {
+ let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
+ let base_instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ user_data0: 0,
+ user_data1: 0,
+ };
+
+ match brush.segment_desc {
+ Some(ref segment_desc) => {
+ let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch(
+ brush.get_batch_key(
+ BlendMode::None
+ ),
+ item_bounding_rect
+ );
+ let alpha_batch = self.batch_list.alpha_batch_list.get_suitable_batch(
+ brush.get_batch_key(
+ BlendMode::PremultipliedAlpha
+ ),
+ item_bounding_rect
+ );
+
+ for (i, segment) in segment_desc.segments.iter().enumerate() {
+ let is_inner = segment.edge_flags.is_empty();
+ let needs_blending = !prim_metadata.opacity.is_opaque ||
+ segment.clip_task_id.is_some() ||
+ (!is_inner && transform_kind == TransformedRectKind::Complex);
+
+ let clip_task_address = segment
+ .clip_task_id
+ .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
+
+ let instance = PrimitiveInstance::from(BrushInstance {
+ segment_index: i as i32,
+ clip_task_address,
+ ..base_instance
+ });
+
+ if needs_blending {
+ alpha_batch.push(instance);
+ } else {
+ opaque_batch.push(instance);
+ }
+ }
+ }
+ None => {
+ let batch = self.batch_list.get_suitable_batch(brush.get_batch_key(blend_mode), item_bounding_rect);
+ batch.push(PrimitiveInstance::from(base_instance));
+ }
+ }
+ }
+ PrimitiveKind::Border => {
+ let border_cpu =
+ &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0];
+ // TODO(gw): Select correct blend mode for edges and corners!!
+ let corner_kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::BorderCorner,
+ );
+ let corner_key = BatchKey::new(corner_kind, blend_mode, no_textures);
+ let edge_kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::BorderEdge,
+ );
+ let edge_key = BatchKey::new(edge_kind, blend_mode, no_textures);
+
+ // Work around borrow ck on borrowing batch_list twice.
+ {
+ let batch =
+ self.batch_list.get_suitable_batch(corner_key, item_bounding_rect);
+ for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
+ {
+ let sub_index = i as i32;
+ match *instance_kind {
+ BorderCornerInstance::None => {}
+ BorderCornerInstance::Single => {
+ batch.push(base_instance.build(
+ sub_index,
+ BorderCornerSide::Both as i32,
+ 0,
+ ));
+ }
+ BorderCornerInstance::Double => {
+ batch.push(base_instance.build(
+ sub_index,
+ BorderCornerSide::First as i32,
+ 0,
+ ));
+ batch.push(base_instance.build(
+ sub_index,
+ BorderCornerSide::Second as i32,
+ 0,
+ ));
+ }
+ }
+ }
+ }
+
+ let batch = self.batch_list.get_suitable_batch(edge_key, item_bounding_rect);
+ for border_segment in 0 .. 4 {
+ batch.push(base_instance.build(border_segment, 0, 0));
+ }
+ }
+ PrimitiveKind::Line => {
+ let base_instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ user_data0: 0,
+ user_data1: 0,
+ };
+
+ let instance = PrimitiveInstance::from(base_instance);
+
+ match pic_type {
+ PictureType::TextShadow => {
+ self.line_cache_prims.push(instance);
+ }
+ PictureType::Image => {
+ let kind =
+ BatchKind::Brush(BrushBatchKind::Line);
+ let key = BatchKey::new(kind, blend_mode, no_textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ batch.push(instance);
+ }
+ PictureType::BoxShadow => unreachable!(),
+ }
+ }
+ PrimitiveKind::Image => {
+ let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
+
+ let (color_texture_id, uv_address) = resolve_image(
+ image_cpu.image_key,
+ image_cpu.image_rendering,
+ image_cpu.tile_offset,
+ ctx.resource_cache,
+ gpu_cache,
+ deferred_resolves,
+ );
+
+ if color_texture_id == SourceTexture::Invalid {
+ warn!("Warnings: skip a PrimitiveKind::Image at {:?}.\n", item_bounding_rect);
+ return;
+ }
+
+ let batch_kind = match color_texture_id {
+ SourceTexture::External(ext_image) => {
+ match ext_image.image_type {
+ ExternalImageType::Texture2DHandle => {
+ TransformBatchKind::Image(ImageBufferKind::Texture2D)
+ }
+ ExternalImageType::Texture2DArrayHandle => {
+ TransformBatchKind::Image(ImageBufferKind::Texture2DArray)
+ }
+ ExternalImageType::TextureRectHandle => {
+ TransformBatchKind::Image(ImageBufferKind::TextureRect)
+ }
+ ExternalImageType::TextureExternalHandle => {
+ TransformBatchKind::Image(ImageBufferKind::TextureExternal)
+ }
+ ExternalImageType::ExternalBuffer => {
+ // The ExternalImageType::ExternalBuffer should be handled by resource_cache.
+ // It should go through the non-external case.
+ panic!(
+ "Non-texture handle type should be handled in other way"
+ );
+ }
+ }
+ }
+ _ => TransformBatchKind::Image(ImageBufferKind::Texture2DArray),
+ };
+
+ let textures = BatchTextures {
+ colors: [
+ color_texture_id,
+ SourceTexture::Invalid,
+ SourceTexture::Invalid,
+ ],
+ };
+
+ let key = BatchKey::new(
+ BatchKind::Transformable(transform_kind, batch_kind),
+ blend_mode,
+ textures,
+ );
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ batch.push(base_instance.build(uv_address.as_int(gpu_cache), 0, 0));
+ }
+ PrimitiveKind::TextRun => {
+ let text_cpu =
+ &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
+ let is_shadow = pic_type == PictureType::TextShadow;
+
+ // TODO(gw): It probably makes sense to base this decision on the content
+ // origin field in the future (once that's configurable).
+ let font_transform = if is_shadow {
+ None
+ } else {
+ Some(&scroll_node.transform)
+ };
+
+ let font = text_cpu.get_font(
+ ctx.device_pixel_scale,
+ font_transform,
+ );
+
+ let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
+ let batch_list = &mut self.batch_list;
+ let text_run_cache_prims = &mut self.text_run_cache_prims;
+
+ ctx.resource_cache.fetch_glyphs(
+ font,
+ &text_cpu.glyph_keys,
+ glyph_fetch_buffer,
+ gpu_cache,
+ |texture_id, mut glyph_format, glyphs| {
+ debug_assert_ne!(texture_id, SourceTexture::Invalid);
+
+ // Ignore color and only sample alpha when shadowing.
+ if text_cpu.is_shadow() {
+ glyph_format = glyph_format.ignore_color();
+ }
+
+ let subpx_dir = match glyph_format {
+ GlyphFormat::Bitmap |
+ GlyphFormat::ColorBitmap => SubpixelDirection::None,
+ _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
+ };
+
+ let batch = if is_shadow {
+ text_run_cache_prims
+ .entry(texture_id)
+ .or_insert(Vec::new())
+ } else {
+ let textures = BatchTextures {
+ colors: [
+ texture_id,
+ SourceTexture::Invalid,
+ SourceTexture::Invalid,
+ ],
+ };
+
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::TextRun(glyph_format),
+ );
+
+ let blend_mode = match glyph_format {
+ GlyphFormat::Subpixel |
+ GlyphFormat::TransformedSubpixel => {
+ if text_cpu.font.bg_color.a != 0 {
+ BlendMode::SubpixelWithBgColor
+ } else if ctx.use_dual_source_blending {
+ BlendMode::SubpixelDualSource
+ } else {
+ BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
+ }
+ }
+ GlyphFormat::Alpha |
+ GlyphFormat::TransformedAlpha |
+ GlyphFormat::Bitmap |
+ GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
+ };
+
+ let key = BatchKey::new(kind, blend_mode, textures);
+ batch_list.get_suitable_batch(key, item_bounding_rect)
+ };
+
+ for glyph in glyphs {
+ batch.push(base_instance.build(
+ glyph.index_in_text_run,
+ glyph.uv_rect_address.as_int(),
+ subpx_dir as u32 as i32,
+ ));
+ }
+ },
+ );
+ }
+ PrimitiveKind::Picture => {
+ let picture =
+ &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
+
+ match picture.render_task_id {
+ Some(cache_task_id) => {
+ let cache_task_address = render_tasks.get_task_address(cache_task_id);
+ let textures = BatchTextures::render_target_cache();
+
+ match picture.kind {
+ PictureKind::TextShadow { .. } => {
+ let kind = BatchKind::Brush(
+ BrushBatchKind::Image(
+ BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
+ );
+ let key = BatchKey::new(kind, blend_mode, textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ user_data0: cache_task_address.0 as i32,
+ user_data1: BrushImageKind::Simple as i32,
+ };
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ PictureKind::BoxShadow { image_kind, .. } => {
+ let kind = BatchKind::Brush(
+ BrushBatchKind::Image(
+ BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
+ );
+ let key = BatchKey::new(kind, blend_mode, textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ user_data0: cache_task_address.0 as i32,
+ user_data1: image_kind as i32,
+ };
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ PictureKind::Image {
+ composite_mode,
+ secondary_render_task_id,
+ is_in_3d_context,
+ reference_frame_id,
+ real_local_rect,
+ ..
+ } => {
+ // If this picture is participating in a 3D rendering context,
+ // then don't add it to any batches here. Instead, create a polygon
+ // for it and add it to the current plane splitter.
+ if is_in_3d_context {
+ // Push into parent plane splitter.
+
+ let real_xf = &ctx.clip_scroll_tree.nodes[&reference_frame_id].world_content_transform;
+
+ let polygon = make_polygon(
+ real_local_rect,
+ &real_xf,
+ prim_index.0,
+ );
+
+ splitter.add(polygon);
+
+ return;
+ }
+
+ // Depending on the composite mode of the picture, we generate the
+ // old style Composite primitive instances. In the future, we'll
+ // remove these and pass them through the brush batching pipeline.
+ // This will allow us to unify some of the shaders, apply clip masks
+ // when compositing pictures, and also correctly apply pixel snapping
+ // to picture compositing operations.
+ let source_id = picture.render_task_id.expect("no source!?");
+
+ match composite_mode.expect("bug: only composites here") {
+ PictureCompositeMode::Filter(filter) => {
+ match filter {
+ FilterOp::Blur(..) => {
+ let src_task_address = render_tasks.get_task_address(source_id);
+ let key = BatchKey::new(
+ BatchKind::HardwareComposite,
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::render_target_cache(),
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ src_task_address,
+ RenderTaskAddress(0),
+ item_bounding_rect.origin.x,
+ item_bounding_rect.origin.y,
+ z,
+ item_bounding_rect.size.width,
+ item_bounding_rect.size.height,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ FilterOp::DropShadow(offset, _, _) => {
+ let kind = BatchKind::Brush(
+ BrushBatchKind::Image(BrushImageSourceKind::ColorAlphaMask),
+ );
+ let key = BatchKey::new(kind, blend_mode, textures);
+
+ let instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: prim_cache_address,
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ user_data0: cache_task_address.0 as i32,
+ user_data1: BrushImageKind::Simple as i32,
+ };
+
+ {
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ batch.push(PrimitiveInstance::from(instance));
+ }
+
+ let secondary_id = secondary_render_task_id.expect("no secondary!?");
+ let render_task = &render_tasks[secondary_id];
+ let secondary_task_address = render_tasks.get_task_address(secondary_id);
+ let render_pass_index = render_task.pass_index.expect("no render_pass_index!?");
+ let secondary_textures = BatchTextures {
+ colors: [
+ SourceTexture::RenderTaskCacheRGBA8(render_pass_index),
+ SourceTexture::Invalid,
+ SourceTexture::Invalid,
+ ],
+ };
+ let key = BatchKey::new(
+ BatchKind::HardwareComposite,
+ BlendMode::PremultipliedAlpha,
+ secondary_textures,
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let device_offset = (offset * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32();
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ secondary_task_address,
+ RenderTaskAddress(0),
+ item_bounding_rect.origin.x - device_offset.x,
+ item_bounding_rect.origin.y - device_offset.y,
+ z,
+ item_bounding_rect.size.width,
+ item_bounding_rect.size.height,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ _ => {
+ let key = BatchKey::new(
+ BatchKind::Blend,
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::no_texture(),
+ );
+ let src_task_address = render_tasks.get_task_address(source_id);
+
+ let (filter_mode, amount) = match filter {
+ FilterOp::Blur(..) => (0, 0.0),
+ FilterOp::Contrast(amount) => (1, amount),
+ FilterOp::Grayscale(amount) => (2, amount),
+ FilterOp::HueRotate(angle) => (3, angle),
+ FilterOp::Invert(amount) => (4, amount),
+ FilterOp::Saturate(amount) => (5, amount),
+ FilterOp::Sepia(amount) => (6, amount),
+ FilterOp::Brightness(amount) => (7, amount),
+ FilterOp::Opacity(_, amount) => (8, amount),
+ FilterOp::DropShadow(..) => unreachable!(),
+ };
+
+ let amount = (amount * 65535.0).round() as i32;
+ let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ src_task_address,
+ RenderTaskAddress(0),
+ filter_mode,
+ amount,
+ z,
+ 0,
+ 0,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ }
+ }
+ PictureCompositeMode::MixBlend(mode) => {
+ let backdrop_id = secondary_render_task_id.expect("no backdrop!?");
+
+ let key = BatchKey::new(
+ BatchKind::Composite {
+ task_id,
+ source_id,
+ backdrop_id,
+ },
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::no_texture(),
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
+ let source_task_address = render_tasks.get_task_address(source_id);
+
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ source_task_address,
+ backdrop_task_address,
+ mode as u32 as i32,
+ 0,
+ z,
+ 0,
+ 0,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ PictureCompositeMode::Blit => {
+ let src_task_address = render_tasks.get_task_address(source_id);
+ let key = BatchKey::new(
+ BatchKind::HardwareComposite,
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::render_target_cache(),
+ );
+ let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let instance = CompositePrimitiveInstance::new(
+ task_address,
+ src_task_address,
+ RenderTaskAddress(0),
+ item_bounding_rect.origin.x,
+ item_bounding_rect.origin.y,
+ z,
+ item_bounding_rect.size.width,
+ item_bounding_rect.size.height,
+ );
+
+ batch.push(PrimitiveInstance::from(instance));
+ }
+ }
+ }
+ }
+ }
+ None => {
+ // If this picture is being drawn into an existing target (i.e. with
+ // no composition operation), recurse and add to the current batch list.
+ self.add_pic_to_batch(
+ picture,
+ task_id,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ deferred_resolves,
+ );
+ }
+ }
+ }
+ PrimitiveKind::AlignedGradient => {
+ let gradient_cpu =
+ &ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::AlignedGradient,
+ );
+ let key = BatchKey::new(kind, blend_mode, no_textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ for part_index in 0 .. (gradient_cpu.stops_count - 1) {
+ batch.push(base_instance.build(part_index as i32, 0, 0));
+ }
+ }
+ PrimitiveKind::AngleGradient => {
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::AngleGradient,
+ );
+ let key = BatchKey::new(kind, blend_mode, no_textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ batch.push(base_instance.build(0, 0, 0));
+ }
+ PrimitiveKind::RadialGradient => {
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::RadialGradient,
+ );
+ let key = BatchKey::new(kind, blend_mode, no_textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ batch.push(base_instance.build(0, 0, 0));
+ }
+ PrimitiveKind::YuvImage => {
+ let mut textures = BatchTextures::no_texture();
+ let mut uv_rect_addresses = [0; 3];
+ let image_yuv_cpu =
+ &ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
+
+ //yuv channel
+ let channel_count = image_yuv_cpu.format.get_plane_num();
+ debug_assert!(channel_count <= 3);
+ for channel in 0 .. channel_count {
+ let image_key = image_yuv_cpu.yuv_key[channel];
+
+ let (texture, address) = resolve_image(
+ image_key,
+ image_yuv_cpu.image_rendering,
+ None,
+ ctx.resource_cache,
+ gpu_cache,
+ deferred_resolves,
+ );
+
+ if texture == SourceTexture::Invalid {
+ warn!("Warnings: skip a PrimitiveKind::YuvImage at {:?}.\n", item_bounding_rect);
+ return;
+ }
+
+ textures.colors[channel] = texture;
+ uv_rect_addresses[channel] = address.as_int(gpu_cache);
+ }
+
+ let get_buffer_kind = |texture: SourceTexture| {
+ match texture {
+ SourceTexture::External(ext_image) => {
+ match ext_image.image_type {
+ ExternalImageType::Texture2DHandle => {
+ ImageBufferKind::Texture2D
+ }
+ ExternalImageType::Texture2DArrayHandle => {
+ ImageBufferKind::Texture2DArray
+ }
+ ExternalImageType::TextureRectHandle => {
+ ImageBufferKind::TextureRect
+ }
+ ExternalImageType::TextureExternalHandle => {
+ ImageBufferKind::TextureExternal
+ }
+ ExternalImageType::ExternalBuffer => {
+ // The ExternalImageType::ExternalBuffer should be handled by resource_cache.
+ // It should go through the non-external case.
+ panic!("Unexpected non-texture handle type");
+ }
+ }
+ }
+ _ => ImageBufferKind::Texture2DArray,
+ }
+ };
+
+ // All yuv textures should be the same type.
+ let buffer_kind = get_buffer_kind(textures.colors[0]);
+ assert!(
+ textures.colors[1 .. image_yuv_cpu.format.get_plane_num()]
+ .iter()
+ .all(|&tid| buffer_kind == get_buffer_kind(tid))
+ );
+
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::YuvImage(
+ buffer_kind,
+ image_yuv_cpu.format,
+ image_yuv_cpu.color_space,
+ ),
+ );
+ let key = BatchKey::new(kind, blend_mode, textures);
+ let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+
+ batch.push(base_instance.build(
+ uv_rect_addresses[0],
+ uv_rect_addresses[1],
+ uv_rect_addresses[2],
+ ));
+ }
+ }
+ }
+}
+
+impl BrushPrimitive {
+ fn get_batch_key(&self, blend_mode: BlendMode) -> BatchKey {
+ match self.kind {
+ BrushKind::Solid { .. } => {
+ BatchKey::new(
+ BatchKind::Brush(BrushBatchKind::Solid),
+ blend_mode,
+ BatchTextures::no_texture(),
+ )
+ }
+ BrushKind::Clear => {
+ BatchKey::new(
+ BatchKind::Brush(BrushBatchKind::Solid),
+ BlendMode::PremultipliedDestOut,
+ BatchTextures::no_texture(),
+ )
+ }
+ BrushKind::Mask { .. } => {
+ unreachable!("bug: mask brushes not expected in normal alpha pass");
+ }
+ }
+ }
+}
+
+trait AlphaBatchHelpers {
+ fn get_blend_mode(
+ &self,
+ metadata: &PrimitiveMetadata,
+ transform_kind: TransformedRectKind,
+ ) -> BlendMode;
+}
+
+impl AlphaBatchHelpers for PrimitiveStore {
+ fn get_blend_mode(
+ &self,
+ metadata: &PrimitiveMetadata,
+ transform_kind: TransformedRectKind,
+ ) -> BlendMode {
+ let needs_blending = !metadata.opacity.is_opaque || metadata.clip_task_id.is_some() ||
+ transform_kind == TransformedRectKind::Complex;
+
+ match metadata.prim_kind {
+ // Can only resolve the TextRun's blend mode once glyphs are fetched.
+ PrimitiveKind::TextRun => BlendMode::PremultipliedAlpha,
+ PrimitiveKind::Border |
+ PrimitiveKind::Image |
+ PrimitiveKind::YuvImage |
+ PrimitiveKind::AlignedGradient |
+ PrimitiveKind::AngleGradient |
+ PrimitiveKind::RadialGradient |
+ PrimitiveKind::Line |
+ PrimitiveKind::Brush |
+ PrimitiveKind::Picture => if needs_blending {
+ BlendMode::PremultipliedAlpha
+ } else {
+ BlendMode::None
+ },
+ }
+ }
+}
+
+fn resolve_image(
+ image_key: ImageKey,
+ image_rendering: ImageRendering,
+ tile_offset: Option,
+ resource_cache: &ResourceCache,
+ gpu_cache: &mut GpuCache,
+ deferred_resolves: &mut Vec,
+) -> (SourceTexture, GpuCacheHandle) {
+ match resource_cache.get_image_properties(image_key) {
+ Some(image_properties) => {
+ // Check if an external image that needs to be resolved
+ // by the render thread.
+ match image_properties.external_image {
+ Some(external_image) => {
+ // This is an external texture - we will add it to
+ // the deferred resolves list to be patched by
+ // the render thread...
+ let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
+ deferred_resolves.push(DeferredResolve {
+ image_properties,
+ address: gpu_cache.get_address(&cache_handle),
+ });
+
+ (SourceTexture::External(external_image), cache_handle)
+ }
+ None => {
+ if let Ok(cache_item) = resource_cache.get_cached_image(image_key, image_rendering, tile_offset) {
+ (cache_item.texture_id, cache_item.uv_rect_handle)
+ } else {
+ // There is no usable texture entry for the image key. Just return an invalid texture here.
+ (SourceTexture::Invalid, GpuCacheHandle::new())
+ }
+ }
+ }
+ }
+ None => (SourceTexture::Invalid, GpuCacheHandle::new()),
+ }
+}
+
+/// Construct a polygon from stacking context boundaries.
+/// `anchor` here is an index that's going to be preserved in all the
+/// splits of the polygon.
+fn make_polygon(
+ rect: LayerRect,
+ transform: &LayerToWorldTransform,
+ anchor: usize,
+) -> Polygon {
+ let mat = TypedTransform3D::row_major(
+ transform.m11 as f64,
+ transform.m12 as f64,
+ transform.m13 as f64,
+ transform.m14 as f64,
+ transform.m21 as f64,
+ transform.m22 as f64,
+ transform.m23 as f64,
+ transform.m24 as f64,
+ transform.m31 as f64,
+ transform.m32 as f64,
+ transform.m33 as f64,
+ transform.m34 as f64,
+ transform.m41 as f64,
+ transform.m42 as f64,
+ transform.m43 as f64,
+ transform.m44 as f64);
+ Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
+}
+
+/// Batcher managing draw calls into the clip mask (in the RT cache).
+#[derive(Debug)]
+pub struct ClipBatcher {
+ /// Rectangle draws fill up the rectangles with rounded corners.
+ pub rectangles: Vec,
+ /// Image draws apply the image masking.
+ pub images: FastHashMap>,
+ pub border_clears: Vec,
+ pub borders: Vec,
+}
+
+impl ClipBatcher {
+ pub fn new() -> Self {
+ ClipBatcher {
+ rectangles: Vec::new(),
+ images: FastHashMap::default(),
+ border_clears: Vec::new(),
+ borders: Vec::new(),
+ }
+ }
+
+ pub fn add(
+ &mut self,
+ task_address: RenderTaskAddress,
+ clips: &[ClipWorkItem],
+ coordinate_system_id: CoordinateSystemId,
+ resource_cache: &ResourceCache,
+ gpu_cache: &GpuCache,
+ clip_store: &ClipStore,
+ ) {
+ let mut coordinate_system_id = coordinate_system_id;
+ for work_item in clips.iter() {
+ let instance = ClipMaskInstance {
+ render_task_address: task_address,
+ scroll_node_data_index: work_item.scroll_node_data_index,
+ segment: 0,
+ clip_data_address: GpuCacheAddress::invalid(),
+ resource_address: GpuCacheAddress::invalid(),
+ };
+ let info = clip_store
+ .get_opt(&work_item.clip_sources)
+ .expect("bug: clip handle should be valid");
+
+ for &(ref source, ref handle) in &info.clips {
+ let gpu_address = gpu_cache.get_address(handle);
+
+ match *source {
+ ClipSource::Image(ref mask) => {
+ if let Ok(cache_item) = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None) {
+ self.images
+ .entry(cache_item.texture_id)
+ .or_insert(Vec::new())
+ .push(ClipMaskInstance {
+ clip_data_address: gpu_address,
+ resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
+ ..instance
+ });
+ } else {
+ warn!("Warnings: skip a image mask. Key:{:?} Rect::{:?}.\n", mask.image, mask.rect);
+ continue;
+ }
+ }
+ ClipSource::Rectangle(..) => {
+ if work_item.coordinate_system_id != coordinate_system_id {
+ self.rectangles.push(ClipMaskInstance {
+ clip_data_address: gpu_address,
+ ..instance
+ });
+ coordinate_system_id = work_item.coordinate_system_id;
+ }
+ }
+ ClipSource::RoundedRectangle(..) => {
+ self.rectangles.push(ClipMaskInstance {
+ clip_data_address: gpu_address,
+ ..instance
+ });
+ }
+ ClipSource::BorderCorner(ref source) => {
+ self.border_clears.push(ClipMaskInstance {
+ clip_data_address: gpu_address,
+ segment: 0,
+ ..instance
+ });
+ for clip_index in 0 .. source.actual_clip_count {
+ self.borders.push(ClipMaskInstance {
+ clip_data_address: gpu_address,
+ segment: 1 + clip_index as i32,
+ ..instance
+ })
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs
index 3d4ea7818665..9c4b1b168858 100644
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -9,8 +9,8 @@ use clip::ClipSource;
use ellipse::Ellipse;
use frame_builder::FrameBuilder;
use gpu_cache::GpuDataRequest;
-use prim_store::{BrushAntiAliasMode, BrushSegmentDescriptor, BrushSegmentKind};
-use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect};
+use prim_store::{BorderPrimitiveCpu, BrushSegment, BrushSegmentDescriptor};
+use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer, TexelRect};
use util::{lerp, pack_as_float};
#[repr(u8)]
@@ -422,84 +422,90 @@ impl FrameBuilder {
let has_no_curve = radius.is_zero();
if has_no_curve && all_corners_simple && all_edges_simple {
- let inner_rect = LayerRect::new(
- LayerPoint::new(
- info.rect.origin.x + left_len,
- info.rect.origin.y + top_len,
- ),
- LayerSize::new(
- info.rect.size.width - left_len - right_len,
- info.rect.size.height - top_len - bottom_len,
- ),
+ let p0 = info.rect.origin;
+ let p1 = LayerPoint::new(
+ info.rect.origin.x + left_len,
+ info.rect.origin.y + top_len,
+ );
+ let p2 = LayerPoint::new(
+ info.rect.origin.x + info.rect.size.width - right_len,
+ info.rect.origin.y + info.rect.size.height - bottom_len,
+ );
+ let p3 = info.rect.bottom_right();
+
+ let segment = |x0, y0, x1, y1| BrushSegment::new(
+ LayerPoint::new(x0, y0),
+ LayerSize::new(x1-x0, y1-y0),
+ false,
+ EdgeAaSegmentMask::all() // Note: this doesn't seem right, needs revision
);
// Add a solid rectangle for each visible edge/corner combination.
if top_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor::new(
- &info.rect,
- &inner_rect,
- Some(&[
- BrushSegmentKind::TopLeft,
- BrushSegmentKind::TopMid,
- BrushSegmentKind::TopRight
- ]),
- );
+ let descriptor = BrushSegmentDescriptor {
+ segments: vec![
+ segment(p0.x, p0.y, p1.x, p1.y),
+ segment(p2.x, p0.y, p3.x, p1.y),
+ segment(p1.x, p0.y, p2.x, p1.y),
+ ],
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ };
+
self.add_solid_rectangle(
clip_and_scroll,
&info,
border.top.color,
- Some(Box::new(descriptor)),
- BrushAntiAliasMode::Segment,
+ Some(descriptor),
);
}
+
if left_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor::new(
- &info.rect,
- &inner_rect,
- Some(&[
- BrushSegmentKind::MidLeft,
- ]),
- );
+ let descriptor = BrushSegmentDescriptor {
+ segments: vec![
+ segment(p0.x, p1.y, p1.x, p2.y),
+ ],
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ };
+
self.add_solid_rectangle(
clip_and_scroll,
&info,
border.left.color,
- Some(Box::new(descriptor)),
- BrushAntiAliasMode::Segment,
+ Some(descriptor),
);
}
+
if right_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor::new(
- &info.rect,
- &inner_rect,
- Some(&[
- BrushSegmentKind::MidRight,
- ]),
- );
+ let descriptor = BrushSegmentDescriptor {
+ segments: vec![
+ segment(p2.x, p1.y, p3.x, p2.y),
+ ],
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ };
+
self.add_solid_rectangle(
clip_and_scroll,
&info,
border.right.color,
- Some(Box::new(descriptor)),
- BrushAntiAliasMode::Segment,
+ Some(descriptor),
);
}
+
if bottom_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor::new(
- &info.rect,
- &inner_rect,
- Some(&[
- BrushSegmentKind::BottomLeft,
- BrushSegmentKind::BottomMid,
- BrushSegmentKind::BottomRight
- ]),
- );
+ let descriptor = BrushSegmentDescriptor {
+ segments: vec![
+ segment(p1.x, p2.y, p2.x, p3.y),
+ segment(p2.x, p2.y, p3.x, p3.y),
+ segment(p0.x, p2.y, p1.x, p3.y),
+ ],
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ };
+
self.add_solid_rectangle(
clip_and_scroll,
&info,
border.bottom.color,
- Some(Box::new(descriptor)),
- BrushAntiAliasMode::Segment,
+ Some(descriptor),
);
}
} else {
@@ -928,4 +934,4 @@ impl ImageBorderSegment {
tile_spacing,
}
}
-}
\ No newline at end of file
+}
diff --git a/gfx/webrender/src/box_shadow.rs b/gfx/webrender/src/box_shadow.rs
index b85925fa45a8..2a3a06601db9 100644
--- a/gfx/webrender/src/box_shadow.rs
+++ b/gfx/webrender/src/box_shadow.rs
@@ -10,7 +10,7 @@ use app_units::Au;
use clip::ClipSource;
use frame_builder::FrameBuilder;
use gpu_types::BrushImageKind;
-use prim_store::{BrushAntiAliasMode, PrimitiveContainer};
+use prim_store::{PrimitiveContainer};
use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive};
use picture::PicturePrimitive;
use util::RectHelpers;
@@ -136,7 +136,6 @@ impl FrameBuilder {
color: *color,
},
None,
- BrushAntiAliasMode::Primitive,
)
),
);
@@ -188,7 +187,6 @@ impl FrameBuilder {
kind: BrushMaskKind::Corner(corner_size),
},
None,
- BrushAntiAliasMode::Primitive,
);
} else {
// Create a minimal size primitive mask to blur. In this
@@ -226,7 +224,6 @@ impl FrameBuilder {
kind: BrushMaskKind::RoundedRect(clip_rect, shadow_radius),
},
None,
- BrushAntiAliasMode::Primitive,
);
};
@@ -301,7 +298,7 @@ impl FrameBuilder {
let mut inflate_size = 1.0;
while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
adjusted_blur_std_deviation *= 0.5;
- inflate_size += 1.0;
+ inflate_size *= 2.0;
}
let brush_rect = brush_rect.inflate(inflate_size, inflate_size);
@@ -311,7 +308,6 @@ impl FrameBuilder {
kind: BrushMaskKind::RoundedRect(clip_rect, shadow_radius),
},
None,
- BrushAntiAliasMode::Primitive,
);
let brush_info = LayerPrimitiveInfo::new(brush_rect);
let brush_prim_index = self.create_primitive(
diff --git a/gfx/webrender/src/clip.rs b/gfx/webrender/src/clip.rs
index 338045b7b0a8..48ebf298f710 100644
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, ImageMask, ImageRendering};
-use api::{LayerPoint, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
+use api::{LayerPoint, LayerRect, LayoutPoint, LayoutVector2D, LocalClip};
+use api::{DevicePixelScale, LayerToWorldTransform};
use border::{BorderCornerClipSource, ensure_no_corner_overlap};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
@@ -168,21 +169,18 @@ impl ClipSources {
let mut local_outer = Some(LayerRect::max_rect());
let mut local_inner = local_outer;
let mut can_calculate_inner_rect = true;
- let mut can_calculate_outer_rect = true;
+ let mut can_calculate_outer_rect = false;
for source in clips {
match *source {
ClipSource::Image(ref mask) => {
if !mask.repeat {
+ can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(&mask.rect));
- can_calculate_inner_rect = false;
- } else {
- can_calculate_inner_rect = false;
- can_calculate_outer_rect = false;
- break;
}
local_inner = None;
}
ClipSource::Rectangle(rect) => {
+ can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(&rect));
local_inner = local_inner.and_then(|r| r.intersection(&rect));
}
@@ -191,10 +189,10 @@ impl ClipSources {
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
can_calculate_inner_rect = false;
- can_calculate_outer_rect = false;
break;
}
+ can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(rect));
let inner_rect = extract_inner_rect_safe(rect, radius);
@@ -203,19 +201,18 @@ impl ClipSources {
}
ClipSource::BorderCorner { .. } => {
can_calculate_inner_rect = false;
- can_calculate_outer_rect = false;
break;
}
}
}
let outer = match can_calculate_outer_rect {
- true => local_outer,
+ true => Some(local_outer.unwrap_or_else(LayerRect::zero)),
false => None,
};
let inner = match can_calculate_inner_rect {
- true => local_inner.unwrap_or(LayerRect::zero()),
+ true => local_inner.unwrap_or_else(LayerRect::zero),
false => LayerRect::zero(),
};
@@ -262,7 +259,7 @@ impl ClipSources {
pub fn get_screen_bounds(
&self,
transform: &LayerToWorldTransform,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
) -> (DeviceIntRect, Option) {
// If this translation isn't axis aligned or has a perspective component, don't try to
// calculate the inner rectangle. The rectangle that we produce would include potentially
@@ -273,13 +270,13 @@ impl ClipSources {
let can_calculate_inner_rect =
transform.preserves_2d_axis_alignment() && !transform.has_perspective_component();
let screen_inner_rect = if can_calculate_inner_rect {
- calculate_screen_bounding_rect(transform, &self.local_inner_rect, device_pixel_ratio)
+ calculate_screen_bounding_rect(transform, &self.local_inner_rect, device_pixel_scale)
} else {
DeviceIntRect::zero()
};
let screen_outer_rect = self.local_outer_rect.map(|outer_rect|
- calculate_screen_bounding_rect(transform, &outer_rect, device_pixel_ratio)
+ calculate_screen_bounding_rect(transform, &outer_rect, device_pixel_scale)
);
(screen_inner_rect, screen_outer_rect)
diff --git a/gfx/webrender/src/clip_scroll_node.rs b/gfx/webrender/src/clip_scroll_node.rs
index 2918b409c0c7..6b67f04a59e0 100644
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -2,10 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
-use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
-use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
-use api::{LayoutTransform, PropertyBinding, StickyOffsetBounds, WorldPoint};
+use api::{ClipId, DeviceIntRect, DevicePixelScale, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D};
+use api::{PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase, ScrollLocation};
+use api::{ScrollSensitivity, StickyOffsetBounds, WorldPoint};
use clip::{ClipSourcesHandle, ClipStore};
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use euclid::SideOffsets2D;
@@ -17,7 +17,7 @@ use resource_cache::ResourceCache;
use scene::SceneProperties;
use spring::{DAMPING, STIFFNESS, Spring};
use std::rc::Rc;
-use util::{MatrixHelpers, MaxRect, TransformedRectKind};
+use util::{MatrixHelpers, TransformOrOffset, TransformedRectKind};
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@@ -85,10 +85,6 @@ pub struct ClipScrollNode {
/// Viewing rectangle in the coordinate system of the parent reference frame.
pub local_viewport_rect: LayerRect,
- /// Clip rect of this node - typically the same as viewport rect, except
- /// in overscroll cases.
- pub local_clip_rect: LayerRect,
-
/// The transformation for this viewport in world coordinates is the transformation for
/// our parent reference frame, plus any accumulated scrolling offsets from nodes
/// between our reference frame and this node. For reference frames, we also include
@@ -116,12 +112,19 @@ pub struct ClipScrollNode {
/// generate clip tasks.
pub clip_chain_node: ClipChain,
- /// The intersected outer bounds of the clips for this node.
- pub combined_clip_outer_bounds: DeviceIntRect,
+ /// True if this node is transformed by an invertible transform. If not, display items
+ /// transformed by this node will not be displayed and display items not transformed by this
+ /// node will not be clipped by clips that are transformed by this node.
+ pub invertible: bool,
/// The axis-aligned coordinate system id of this node.
pub coordinate_system_id: CoordinateSystemId,
+ /// The transformation from the coordinate system which established our compatible coordinate
+ /// system (same coordinate system id) and us. This can change via scroll offsets and via new
+ /// reference frame transforms.
+ pub coordinate_system_relative_transform: TransformOrOffset,
+
/// A linear ID / index of this clip-scroll node. Used as a reference to
/// pass to shaders, to allow them to fetch a given clip-scroll node.
pub node_data_index: ClipScrollNodeIndex,
@@ -136,7 +139,6 @@ impl ClipScrollNode {
) -> Self {
ClipScrollNode {
local_viewport_rect: *rect,
- local_clip_rect: *rect,
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
parent: parent_id,
@@ -144,8 +146,9 @@ impl ClipScrollNode {
pipeline_id,
node_type: node_type,
clip_chain_node: None,
- combined_clip_outer_bounds: DeviceIntRect::max_rect(),
+ invertible: true,
coordinate_system_id: CoordinateSystemId(0),
+ coordinate_system_relative_transform: TransformOrOffset::zero(),
node_data_index: ClipScrollNodeIndex(0),
}
}
@@ -187,10 +190,11 @@ impl ClipScrollNode {
) -> Self {
let identity = LayoutTransform::identity();
let info = ReferenceFrameInfo {
- resolved_transform: LayerToScrollTransform::identity(),
+ resolved_transform: LayerTransform::identity(),
source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
source_perspective: source_perspective.unwrap_or(identity),
origin_in_parent_reference_frame,
+ invertible: true,
};
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
}
@@ -264,60 +268,26 @@ impl ClipScrollNode {
true
}
- pub fn update_to_empty_rect(&mut self) {
- self.combined_clip_outer_bounds = DeviceIntRect::zero();
+ pub fn mark_uninvertible(&mut self) {
+ self.invertible = false;
self.world_content_transform = LayerToWorldTransform::identity();
self.world_viewport_transform = LayerToWorldTransform::identity();
self.clip_chain_node = None;
}
- pub fn push_gpu_node_data(
- &mut self,
- state: &TransformUpdateState,
- node_data: &mut Vec
- ) {
- if self.combined_clip_outer_bounds.is_empty() {
+ pub fn push_gpu_node_data(&mut self, node_data: &mut Vec) {
+ if !self.invertible {
node_data.push(ClipScrollNodeData::invalid());
return;
}
- let local_clip_rect = match self.node_type {
- _ if self.world_content_transform.has_perspective_component() => LayerRect::max_rect(),
- NodeType::ReferenceFrame(ref info) => {
- info.resolved_transform.with_destination::()
- .inverse_rect_footprint(&state.parent_combined_viewport_rect)
- }
- NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
- state.parent_combined_viewport_rect
- .intersection(&self.local_clip_rect)
- .unwrap_or(LayerRect::zero())
- }
- NodeType::StickyFrame(ref sticky_info) => {
- state.parent_combined_viewport_rect
- .translate(&-sticky_info.current_offset)
- .intersection(&self.local_clip_rect)
- .unwrap_or(LayerRect::zero())
- }
- };
-
let transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() {
TransformedRectKind::AxisAligned
} else {
TransformedRectKind::Complex
};
-
- let reference_frame_relative_scroll_offset = match self.node_type {
- NodeType::ReferenceFrame(_) => LayerVector2D::zero(),
- NodeType::Clip(_) | NodeType::ScrollFrame(_) => state.parent_accumulated_scroll_offset,
- NodeType::StickyFrame(ref sticky_info) =>
- state.parent_accumulated_scroll_offset + sticky_info.current_offset,
- };
-
let data = ClipScrollNodeData {
transform: self.world_content_transform,
- local_clip_rect,
- reference_frame_relative_scroll_offset,
- scroll_offset: self.scroll_offset(),
transform_kind: transform_kind as u32 as f32,
padding: [0.0; 3],
};
@@ -330,7 +300,8 @@ impl ClipScrollNode {
&mut self,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
- device_pixel_ratio: f32,
+ screen_rect: &DeviceIntRect,
+ device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
@@ -338,52 +309,53 @@ impl ClipScrollNode {
) {
// If any of our parents was not rendered, we are not rendered either and can just
// quit here.
- if state.combined_outer_clip_bounds.is_empty() {
- self.update_to_empty_rect();
+ if !state.invertible {
+ self.mark_uninvertible();
return;
}
+ self.update_transform(state, next_coordinate_system_id, scene_properties);
+
// If this node is a reference frame, we check if the determinant is 0, which means it
// has a non-invertible matrix. For non-reference-frames we assume that they will
// produce only additional translations which should be invertible.
- if self.node_type.is_reference_frame() {
- if self.world_content_transform.determinant() == 0.0 {
- self.update_to_empty_rect();
+ match self.node_type {
+ NodeType::ReferenceFrame(info) if !info.invertible => {
+ self.mark_uninvertible();
return;
}
+ _ => self.invertible = true,
}
- self.update_transform(state, next_coordinate_system_id, scene_properties);
self.update_clip_work_item(
state,
- device_pixel_ratio,
+ screen_rect,
+ device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
);
-
- // This indicates that we are entirely clipped out.
- if state.combined_outer_clip_bounds.is_empty() {
- self.update_to_empty_rect();
- return;
- }
-
}
pub fn update_clip_work_item(
&mut self,
state: &mut TransformUpdateState,
- device_pixel_ratio: f32,
+ screen_rect: &DeviceIntRect,
+ device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
) {
- let mut current_clip_chain = state.parent_clip_chain.clone();
+ let current_clip_chain = state.parent_clip_chain.clone();
+ let combined_outer_screen_rect = current_clip_chain.as_ref().map_or(
+ *screen_rect, |clip| clip.combined_outer_screen_rect,
+ );
+
let clip_sources_handle = match self.node_type {
NodeType::Clip(ref handle) => handle,
_ => {
- self.clip_chain_node = current_clip_chain;
- self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
+ self.clip_chain_node = current_clip_chain.clone();
+ self.invertible = true;
return;
}
};
@@ -391,51 +363,39 @@ impl ClipScrollNode {
let clip_sources = clip_store.get_mut(clip_sources_handle);
clip_sources.update(gpu_cache, resource_cache);
let (screen_inner_rect, screen_outer_rect) =
- clip_sources.get_screen_bounds(&self.world_viewport_transform, device_pixel_ratio);
+ clip_sources.get_screen_bounds(&self.world_viewport_transform, device_pixel_scale);
+
+ // All clipping ClipScrollNodes should have outer rectangles, because they never
+ // use the BorderCorner clip type and they always have at last one non-ClipOut
+ // Rectangle ClipSource.
+ let screen_outer_rect = screen_outer_rect.expect("Clipping node didn't have outer rect.");
+ let local_outer_rect = clip_sources.local_outer_rect.expect(
+ "Clipping node didn't have outer rect."
+ );
// If this clip's inner rectangle completely surrounds the existing clip
// chain's outer rectangle, we can discard this clip entirely since it isn't
// going to affect anything.
- if screen_inner_rect.contains_rect(&state.combined_outer_clip_bounds) {
+ if screen_inner_rect.contains_rect(&combined_outer_screen_rect) {
self.clip_chain_node = current_clip_chain;
- self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
return;
}
- let combined_outer_screen_rect = match screen_outer_rect {
- Some(outer_rect) => {
- // If this clips outer rectangle is completely enclosed by the clip
- // chain's inner rectangle, then the only clip that matters from this point
- // on is this clip. We can disconnect this clip from the parent clip chain.
- if state.combined_inner_clip_bounds.contains_rect(&outer_rect) {
- current_clip_chain = None;
- }
- outer_rect.intersection(&state.combined_outer_clip_bounds)
- .unwrap_or_else(DeviceIntRect::zero)
- }
- None => state.combined_outer_clip_bounds,
+ let work_item = ClipWorkItem {
+ scroll_node_data_index: self.node_data_index,
+ clip_sources: clip_sources_handle.weak(),
+ coordinate_system_id: state.current_coordinate_system_id,
};
- let combined_inner_screen_rect =
- state.combined_inner_clip_bounds.intersection(&screen_inner_rect)
- .unwrap_or_else(DeviceIntRect::zero);
-
- state.combined_outer_clip_bounds = combined_outer_screen_rect;
- state.combined_inner_clip_bounds = combined_inner_screen_rect;
- self.combined_clip_outer_bounds = combined_outer_screen_rect;
-
- self.clip_chain_node = Some(Rc::new(ClipChainNode {
- work_item: ClipWorkItem {
- scroll_node_data_index: self.node_data_index,
- clip_sources: clip_sources_handle.weak(),
- coordinate_system_id: state.current_coordinate_system_id,
- },
+ let clip_chain_node = ClipChainNode::new(
+ work_item,
+ self.coordinate_system_relative_transform.apply(&local_outer_rect),
+ screen_outer_rect,
screen_inner_rect,
- combined_outer_screen_rect,
- combined_inner_screen_rect,
- prev: current_clip_chain,
- }));
+ current_clip_chain
+ );
+ self.clip_chain_node = Some(Rc::new(clip_chain_node));
state.parent_clip_chain = self.clip_chain_node.clone();
}
@@ -479,6 +439,10 @@ impl ClipScrollNode {
self.world_viewport_transform
};
+ let added_offset = state.parent_accumulated_scroll_offset + sticky_offset + scroll_offset;
+ self.coordinate_system_relative_transform =
+ state.coordinate_system_relative_transform.offset(added_offset);
+
match self.node_type {
NodeType::StickyFrame(ref mut info) => info.current_offset = sticky_offset,
_ => {},
@@ -500,30 +464,41 @@ impl ClipScrollNode {
// Resolve the transform against any property bindings.
let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
- info.resolved_transform = LayerToScrollTransform::create_translation(
+ info.resolved_transform = LayerTransform::create_translation(
info.origin_in_parent_reference_frame.x,
info.origin_in_parent_reference_frame.y,
0.0
).pre_mul(&source_transform)
.pre_mul(&info.source_perspective);
- if !info.resolved_transform.preserves_2d_axis_alignment() ||
- info.resolved_transform.has_perspective_component() {
- state.current_coordinate_system_id = *next_coordinate_system_id;
- next_coordinate_system_id.advance();
- }
- self.coordinate_system_id = state.current_coordinate_system_id;
-
// The transformation for this viewport in world coordinates is the transformation for
// our parent reference frame, plus any accumulated scrolling offsets from nodes
// between our reference frame and this node. Finally, we also include
// whatever local transformation this reference frame provides. This can be combined
// with the local_viewport_rect to get its position in world space.
- self.world_viewport_transform = state
- .parent_reference_frame_transform
- .pre_translate(state.parent_accumulated_scroll_offset.to_3d())
- .pre_mul(&info.resolved_transform.with_destination::());
+ let relative_transform = info.resolved_transform
+ .post_translate(state.parent_accumulated_scroll_offset.to_3d());
+ self.world_viewport_transform = state.parent_reference_frame_transform
+ .pre_mul(&relative_transform.with_destination::());
self.world_content_transform = self.world_viewport_transform;
+
+ info.invertible = relative_transform.determinant() != 0.0;
+ if !info.invertible {
+ return;
+ }
+
+ // Try to update our compatible coordinate system transform. If we cannot, start a new
+ // incompatible coordinate system.
+ match state.coordinate_system_relative_transform.update(relative_transform) {
+ Some(offset) => self.coordinate_system_relative_transform = offset,
+ None => {
+ self.coordinate_system_relative_transform = TransformOrOffset::zero();
+ state.current_coordinate_system_id = *next_coordinate_system_id;
+ next_coordinate_system_id.advance();
+ }
+ }
+
+ self.coordinate_system_id = state.current_coordinate_system_id;
}
fn calculate_sticky_offset(
@@ -635,21 +610,13 @@ impl ClipScrollNode {
sticky_offset
}
- pub fn prepare_state_for_children(
- &self,
- state: &mut TransformUpdateState,
- node_data: &Vec
- ) {
- if self.combined_clip_outer_bounds.is_empty() {
- state.parent_combined_viewport_rect = LayerRect::zero();
- state.combined_outer_clip_bounds = DeviceIntRect::zero();
+ pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) {
+ if !self.invertible {
+ state.invertible = false;
state.parent_clip_chain = None;
return;
}
- let combined_local_viewport_rect =
- node_data[self.node_data_index.0 as usize].local_clip_rect;
-
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
@@ -657,19 +624,16 @@ impl ClipScrollNode {
match self.node_type {
NodeType::ReferenceFrame(ref info) => {
state.parent_reference_frame_transform = self.world_viewport_transform;
- state.parent_combined_viewport_rect = combined_local_viewport_rect;
state.parent_accumulated_scroll_offset = LayerVector2D::zero();
+ state.coordinate_system_relative_transform =
+ self.coordinate_system_relative_transform.clone();
let translation = -info.origin_in_parent_reference_frame;
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
}
- NodeType::Clip(..) => {
- state.parent_combined_viewport_rect = combined_local_viewport_rect;
- },
+ NodeType::Clip(..) => { }
NodeType::ScrollFrame(ref scrolling) => {
- state.parent_combined_viewport_rect =
- combined_local_viewport_rect.translate(&-scrolling.offset);
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
state.nearest_scrolling_ancestor_offset = scrolling.offset;
@@ -679,7 +643,6 @@ impl ClipScrollNode {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
- state.parent_combined_viewport_rect = combined_local_viewport_rect;
state.parent_accumulated_scroll_offset =
info.current_offset + state.parent_accumulated_scroll_offset;
}
@@ -817,7 +780,13 @@ impl ClipScrollNode {
}
pub fn is_visible(&self) -> bool {
- self.combined_clip_outer_bounds != DeviceIntRect::zero()
+ if !self.invertible {
+ return false;
+ }
+ match self.clip_chain_node {
+ Some(ref node) if node.combined_outer_screen_rect.is_empty() => false,
+ _ => true,
+ }
}
}
@@ -898,7 +867,7 @@ impl ScrollingState {
pub struct ReferenceFrameInfo {
/// The transformation that establishes this reference frame, relative to the parent
/// reference frame. The origin of the reference frame is included in the transformation.
- pub resolved_transform: LayerToScrollTransform,
+ pub resolved_transform: LayerTransform,
/// The source transform and perspective matrices provided by the stacking context
/// that forms this reference frame. We maintain the property binding information
@@ -911,4 +880,7 @@ pub struct ReferenceFrameInfo {
/// origin of this reference frame. This is already rolled into the `transform' property, but
/// we also store it here to properly transform the viewport for sticky positioning.
pub origin_in_parent_reference_frame: LayerVector2D,
+
+ /// True if the resolved transform is invertible.
+ pub invertible: bool,
}
diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs
index 30751b1ab8c0..71936c972daa 100644
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
+use api::{ClipId, DeviceIntRect, DevicePixelScale, LayerPoint, LayerRect};
use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
use api::{PropertyBinding, LayoutTransform, ScrollLayerState, ScrollLocation, WorldPoint};
use clip::ClipStore;
@@ -14,7 +14,7 @@ use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use scene::SceneProperties;
-use util::MaxRect;
+use util::TransformOrOffset;
pub type ScrollStates = FastHashMap;
@@ -26,11 +26,11 @@ pub type ScrollStates = FastHashMap;
pub struct CoordinateSystemId(pub u32);
impl CoordinateSystemId {
- pub fn root() -> CoordinateSystemId {
+ pub fn root() -> Self {
CoordinateSystemId(0)
}
- pub fn next(&self) -> CoordinateSystemId {
+ pub fn next(&self) -> Self {
let CoordinateSystemId(id) = *self;
CoordinateSystemId(id + 1)
}
@@ -69,19 +69,24 @@ pub struct ClipScrollTree {
#[derive(Clone)]
pub struct TransformUpdateState {
pub parent_reference_frame_transform: LayerToWorldTransform,
- pub parent_combined_viewport_rect: LayerRect,
pub parent_accumulated_scroll_offset: LayerVector2D,
pub nearest_scrolling_ancestor_offset: LayerVector2D,
pub nearest_scrolling_ancestor_viewport: LayerRect,
pub parent_clip_chain: ClipChain,
- pub combined_outer_clip_bounds: DeviceIntRect,
- pub combined_inner_clip_bounds: DeviceIntRect,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
/// coordinate systems which are relatively axis aligned.
pub current_coordinate_system_id: CoordinateSystemId,
+
+ /// Transform from the coordinate system that started this compatible coordinate system.
+ pub coordinate_system_relative_transform: TransformOrOffset,
+
+ /// True if this node is transformed by an invertible transform. If not, display items
+ /// transformed by this node will not be displayed and display items not transformed by this
+ /// node will not be clipped by clips that are transformed by this node.
+ pub invertible: bool,
}
impl ClipScrollTree {
@@ -198,11 +203,6 @@ impl ClipScrollTree {
}
};
- if !node.local_clip_rect.contains(&transformed_point) {
- cache.insert(*node_id, None);
- return false;
- }
-
for &(ref clip, _) in clip_store.get(&clip_sources_handle).clips() {
if !clip.contains(&transformed_point) {
cache.insert(*node_id, None);
@@ -337,11 +337,11 @@ impl ClipScrollTree {
pub fn update_tree(
&mut self,
screen_rect: &DeviceIntRect,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
- pan: LayerPoint,
+ pan: WorldPoint,
node_data: &mut Vec,
scene_properties: &SceneProperties,
) {
@@ -350,29 +350,27 @@ impl ClipScrollTree {
}
let root_reference_frame_id = self.root_reference_frame_id();
- let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
-
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayerToWorldTransform::create_translation(
pan.x,
pan.y,
0.0,
),
- parent_combined_viewport_rect: root_viewport,
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayerRect::zero(),
parent_clip_chain: None,
- combined_outer_clip_bounds: *screen_rect,
- combined_inner_clip_bounds: DeviceIntRect::max_rect(),
current_coordinate_system_id: CoordinateSystemId::root(),
+ coordinate_system_relative_transform: TransformOrOffset::zero(),
+ invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_id,
&mut state,
&mut next_coordinate_system_id,
- device_pixel_ratio,
+ screen_rect,
+ device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
@@ -386,7 +384,8 @@ impl ClipScrollTree {
layer_id: ClipId,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
- device_pixel_ratio: f32,
+ screen_rect: &DeviceIntRect,
+ device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
@@ -408,29 +407,32 @@ impl ClipScrollTree {
node.update(
&mut state,
next_coordinate_system_id,
- device_pixel_ratio,
+ screen_rect,
+ device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
scene_properties,
);
- node.push_gpu_node_data(&state, gpu_node_data);
+ node.push_gpu_node_data(gpu_node_data);
if node.children.is_empty() {
return;
}
- node.prepare_state_for_children(&mut state, gpu_node_data);
+
+ node.prepare_state_for_children(&mut state);
node.children.clone()
};
- for child_layer_id in node_children {
+ for child_node_id in node_children {
self.update_node(
- child_layer_id,
+ child_node_id,
&mut state,
next_coordinate_system_id,
- device_pixel_ratio,
+ screen_rect,
+ device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
@@ -566,7 +568,6 @@ impl ClipScrollTree {
"local_viewport_rect: {:?}",
node.local_viewport_rect
));
- pt.add_item(format!("local_clip_rect: {:?}", node.local_clip_rect));
pt.add_item(format!(
"world_viewport_transform: {:?}",
node.world_viewport_transform
@@ -575,6 +576,10 @@ impl ClipScrollTree {
"world_content_transform: {:?}",
node.world_content_transform
));
+ pt.add_item(format!(
+ "coordinate_system_id: {:?}",
+ node.coordinate_system_id
+ ));
for child_id in &node.children {
self.print_node(child_id, pt, clip_store);
diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs
index c8e14ca39181..0444ff32a346 100644
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::shader_source;
-use api::{ColorF, ImageFormat};
+use api::{ColorF, ImageDescriptor, ImageFormat};
use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize};
use euclid::Transform3D;
use gleam::gl;
@@ -223,21 +223,6 @@ pub fn build_shader_strings(
vs_source.push_str(&shared_result);
fs_source.push_str(&shared_result);
- // Append legacy (.vs and .fs) files if they exist.
- // TODO(gw): Once all shaders are ported to just use the
- // .glsl file, we can remove this code.
- let vs_name = format!("{}.vs", base_filename);
- if let Some(old_vs_source) = get_shader_source(&vs_name, override_path) {
- vs_source.push_str(SHADER_LINE_MARKER);
- vs_source.push_str(&old_vs_source);
- }
-
- let fs_name = format!("{}.fs", base_filename);
- if let Some(old_fs_source) = get_shader_source(&fs_name, override_path) {
- fs_source.push_str(SHADER_LINE_MARKER);
- fs_source.push_str(&old_fs_source);
- }
-
(vs_source, fs_source)
}
@@ -473,7 +458,6 @@ impl Texture {
pub fn get_bpp(&self) -> u32 {
match self.format {
ImageFormat::A8 => 1,
- ImageFormat::RGB8 => 3,
ImageFormat::BGRA8 => 4,
ImageFormat::RG8 => 2,
ImageFormat::RGBAF32 => 16,
@@ -675,6 +659,9 @@ pub struct Device {
// Frame counter. This is used to map between CPU
// frames and GPU frames.
frame_id: FrameId,
+
+ // GL extensions
+ extensions: Vec,
}
impl Device {
@@ -688,6 +675,12 @@ impl Device {
let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
let renderer_name = gl.get_string(gl::RENDERER);
+ let mut extensions = Vec::new();
+ let extension_count = gl.get_integer_v(gl::NUM_EXTENSIONS) as gl::GLuint;
+ for i in 0 .. extension_count {
+ extensions.push(gl.get_string_i(gl::EXTENSIONS, i));
+ }
+
Device {
gl,
resource_override_path,
@@ -713,6 +706,7 @@ impl Device {
renderer_name,
cached_programs,
frame_id: FrameId(0),
+ extensions,
}
}
@@ -1495,12 +1489,16 @@ impl Device {
}
}
- pub fn read_pixels(&mut self, width: i32, height: i32) -> Vec {
+ pub fn read_pixels(&mut self, desc: &ImageDescriptor) -> Vec {
+ let (_, gl_format) = gl_texture_formats_for_image_format(self.gl(), desc.format);
+ let type_ = gl_type_for_texture_format(desc.format);
+
self.gl.read_pixels(
0, 0,
- width as i32, height as i32,
- gl::RGBA,
- gl::UNSIGNED_BYTE
+ desc.width as i32,
+ desc.height as i32,
+ gl_format,
+ type_,
)
}
@@ -1911,6 +1909,13 @@ impl Device {
.blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
self.gl.blend_equation(gl::FUNC_ADD);
}
+ pub fn set_blend_mode_subpixel_dual_source(&self) {
+ self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC1_COLOR);
+ }
+
+ pub fn supports_extension(&self, extension: &str) -> bool {
+ self.extensions.iter().any(|s| s == extension)
+ }
}
/// return (gl_internal_format, gl_format)
@@ -1924,7 +1929,6 @@ fn gl_texture_formats_for_image_format(
} else {
(GL_FORMAT_A as gl::GLint, GL_FORMAT_A)
},
- ImageFormat::RGB8 => (gl::RGB as gl::GLint, gl::RGB),
ImageFormat::BGRA8 => match gl.get_type() {
gl::GlType::Gl => (gl::RGBA as gl::GLint, get_gl_format_bgra(gl)),
gl::GlType::Gles => (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl)),
@@ -2050,7 +2054,6 @@ impl<'a> UploadTarget<'a> {
fn update_impl(&mut self, chunk: UploadChunk) {
let (gl_format, bpp, data_type) = match self.texture.format {
ImageFormat::A8 => (GL_FORMAT_A, 1, gl::UNSIGNED_BYTE),
- ImageFormat::RGB8 => (gl::RGB, 3, gl::UNSIGNED_BYTE),
ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl), 4, gl::UNSIGNED_BYTE),
ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE),
ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT),
diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs
index 71e158fbc7fe..31610f86c323 100644
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -4,7 +4,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
-use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch, FilterOp};
+use api::{DevicePixelScale, DeviceUintRect, DeviceUintSize};
+use api::{DisplayItemRef, DocumentLayer, Epoch, FilterOp};
use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerSize, LayerVector2D, LayoutSize};
use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
@@ -17,7 +18,6 @@ use euclid::rect;
use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
use gpu_cache::GpuCache;
use internal_types::{FastHashMap, FastHashSet, RenderedDocument};
-use prim_store::{BrushAntiAliasMode};
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
@@ -106,7 +106,6 @@ impl<'a> FlattenContext<'a> {
&info,
bg_color,
None,
- BrushAntiAliasMode::Primitive,
);
}
}
@@ -448,7 +447,6 @@ impl<'a> FlattenContext<'a> {
&prim_info,
info.color,
None,
- BrushAntiAliasMode::Primitive,
);
}
SpecificDisplayItem::ClearRectangle => {
@@ -930,7 +928,7 @@ pub struct FrameContext {
clip_scroll_tree: ClipScrollTree,
pipeline_epoch_map: FastHashMap,
id: FrameId,
- frame_builder_config: FrameBuilderConfig,
+ pub frame_builder_config: FrameBuilderConfig,
}
impl FrameContext {
@@ -992,7 +990,7 @@ impl FrameContext {
resource_cache: &mut ResourceCache,
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
output_pipelines: &FastHashSet,
) -> FrameBuilder {
let root_pipeline_id = match scene.root_pipeline_id {
@@ -1043,9 +1041,8 @@ impl FrameContext {
);
roller.builder.setup_viewport_offset(
- window_size,
inner_rect,
- device_pixel_ratio,
+ device_pixel_scale,
roller.clip_scroll_tree,
);
@@ -1080,9 +1077,9 @@ impl FrameContext {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pipelines: &FastHashMap,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
layer: DocumentLayer,
- pan: LayerPoint,
+ pan: WorldPoint,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
scene_properties: &SceneProperties,
@@ -1094,7 +1091,7 @@ impl FrameContext {
&mut self.clip_scroll_tree,
pipelines,
self.window_size,
- device_pixel_ratio,
+ device_pixel_scale,
layer,
pan,
texture_cache_profile,
diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs
index 7e9f5f45e925..2907929249b1 100644
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -2,16 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
-use api::{ClipAndScrollInfo, ClipId, ColorF, ColorU, PropertyBinding};
-use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
-use api::{DocumentLayer, ExtendMode, FontRenderMode, LayoutTransform};
-use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
-use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
-use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
-use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
-use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
-use api::{PremultipliedColorF, WorldPoint, YuvColorSpace, YuvData};
+use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF};
+use api::{ColorU, DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect};
+use api::{DeviceUintSize, DocumentLayer, ExtendMode, FontRenderMode, GlyphInstance, GlyphOptions};
+use api::{GradientStop, HitTestFlags, HitTestItem, HitTestResult, ImageKey, ImageRendering};
+use api::{ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
+use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation};
+use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode};
+use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle, WorldPoint, YuvColorSpace};
+use api::YuvData;
use app_units::Au;
use border::ImageBorderSegment;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
@@ -21,10 +20,10 @@ use euclid::{SideOffsets2D, vec2};
use frame::FrameId;
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCache;
-use gpu_types::ClipScrollNodeData;
+use gpu_types::{ClipScrollNodeData, PictureType};
use internal_types::{FastHashMap, FastHashSet, RenderPassIndex};
-use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace};
-use prim_store::{BrushAntiAliasMode, BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
+use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive};
+use prim_store::{BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
@@ -69,10 +68,13 @@ struct StackingContext {
}
#[derive(Clone, Copy)]
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct FrameBuilderConfig {
pub enable_scrollbars: bool,
pub default_font_render_mode: FontRenderMode,
pub debug: bool,
+ pub dual_source_blending_is_supported: bool,
+ pub dual_source_blending_is_enabled: bool,
}
#[derive(Debug)]
@@ -125,7 +127,7 @@ pub struct FrameBuilder {
}
pub struct PrimitiveContext<'a> {
- pub device_pixel_ratio: f32,
+ pub device_pixel_scale: DevicePixelScale,
pub display_list: &'a BuiltDisplayList,
pub clip_node: &'a ClipScrollNode,
pub scroll_node: &'a ClipScrollNode,
@@ -133,13 +135,13 @@ pub struct PrimitiveContext<'a> {
impl<'a> PrimitiveContext<'a> {
pub fn new(
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
display_list: &'a BuiltDisplayList,
clip_node: &'a ClipScrollNode,
scroll_node: &'a ClipScrollNode,
) -> Self {
PrimitiveContext {
- device_pixel_ratio,
+ device_pixel_scale,
display_list,
clip_node,
scroll_node,
@@ -165,6 +167,8 @@ impl FrameBuilder {
enable_scrollbars: false,
default_font_render_mode: FontRenderMode::Mono,
debug: false,
+ dual_source_blending_is_enabled: true,
+ dual_source_blending_is_supported: false,
},
}
}
@@ -587,46 +591,20 @@ impl FrameBuilder {
pub fn setup_viewport_offset(
&mut self,
- window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
clip_scroll_tree: &mut ClipScrollTree,
) {
- let inner_origin = inner_rect.origin.to_f32();
- let viewport_offset = LayerPoint::new(
- (inner_origin.x / device_pixel_ratio).round(),
- (inner_origin.y / device_pixel_ratio).round(),
- );
- let outer_size = window_size.to_f32();
- let outer_size = LayerSize::new(
- (outer_size.width / device_pixel_ratio).round(),
- (outer_size.height / device_pixel_ratio).round(),
- );
- let clip_size = LayerSize::new(
- outer_size.width + 2.0 * viewport_offset.x,
- outer_size.height + 2.0 * viewport_offset.y,
- );
-
- let viewport_clip = LayerRect::new(
- LayerPoint::new(-viewport_offset.x, -viewport_offset.y),
- LayerSize::new(clip_size.width, clip_size.height),
- );
-
+ let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
let root_id = clip_scroll_tree.root_reference_frame_id();
if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) {
if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type {
- info.resolved_transform = LayerToScrollTransform::create_translation(
+ info.resolved_transform = LayerTransform::create_translation(
viewport_offset.x,
viewport_offset.y,
0.0,
);
}
- root_node.local_clip_rect = viewport_clip;
- }
-
- let clip_id = clip_scroll_tree.topmost_scrolling_node_id();
- if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&clip_id) {
- root_node.local_clip_rect = viewport_clip;
}
}
@@ -758,8 +736,7 @@ impl FrameBuilder {
clip_and_scroll: ClipAndScrollInfo,
info: &LayerPrimitiveInfo,
color: ColorF,
- segments: Option>,
- aa_mode: BrushAntiAliasMode,
+ segments: Option,
) {
if color.a == 0.0 {
// Don't add transparent rectangles to the draw list, but do consider them for hit
@@ -773,7 +750,6 @@ impl FrameBuilder {
color,
},
segments,
- aa_mode,
);
self.add_primitive(
@@ -792,7 +768,6 @@ impl FrameBuilder {
let prim = BrushPrimitive::new(
BrushKind::Clear,
None,
- BrushAntiAliasMode::Primitive,
);
self.add_primitive(
@@ -819,7 +794,6 @@ impl FrameBuilder {
color,
},
None,
- BrushAntiAliasMode::Primitive,
);
let prim_index = self.add_primitive(
@@ -1585,9 +1559,10 @@ impl FrameBuilder {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
profile_counters: &mut FrameProfileCounters,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
scene_properties: &SceneProperties,
node_data: &[ClipScrollNodeData],
+ local_rects: &mut Vec,
) -> Option {
profile_scope!("cull");
@@ -1605,7 +1580,7 @@ impl FrameBuilder {
.display_list;
let root_prim_context = PrimitiveContext::new(
- device_pixel_ratio,
+ device_pixel_scale,
display_list,
root_clip_scroll_node,
root_clip_scroll_node,
@@ -1631,6 +1606,7 @@ impl FrameBuilder {
SpecificPrimitiveIndex(0),
&self.screen_rect.to_i32(),
node_data,
+ local_rects,
);
let pic = &mut self.prim_store.cpu_pictures[0];
@@ -1640,13 +1616,12 @@ impl FrameBuilder {
None,
PrimitiveIndex(0),
RenderTargetKind::Color,
- 0.0,
- 0.0,
+ ContentOrigin::Screen(DeviceIntPoint::zero()),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- RasterizationSpace::Screen,
child_tasks,
None,
+ PictureType::Image,
);
pic.render_task_id = Some(render_tasks.add(root_render_task));
@@ -1690,9 +1665,9 @@ impl FrameBuilder {
clip_scroll_tree: &mut ClipScrollTree,
pipelines: &FastHashMap,
window_size: DeviceUintSize,
- device_pixel_ratio: f32,
+ device_pixel_scale: DevicePixelScale,
layer: DocumentLayer,
- pan: LayerPoint,
+ pan: WorldPoint,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
scene_properties: &SceneProperties,
@@ -1712,9 +1687,14 @@ impl FrameBuilder {
gpu_cache.begin_frame();
let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len());
+ let total_prim_runs =
+ self.prim_store.cpu_pictures.iter().fold(1, |count, ref pic| count + pic.runs.len());
+ let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs);
+ clip_chain_local_clip_rects.push(LayerRect::max_rect());
+
clip_scroll_tree.update_tree(
&self.screen_rect.to_i32(),
- device_pixel_ratio,
+ device_pixel_scale,
&mut self.clip_store,
resource_cache,
gpu_cache,
@@ -1734,9 +1714,10 @@ impl FrameBuilder {
gpu_cache,
&mut render_tasks,
&mut profile_counters,
- device_pixel_ratio,
+ device_pixel_scale,
scene_properties,
&node_data,
+ &mut clip_chain_local_clip_rects,
);
let mut passes = Vec::new();
@@ -1762,14 +1743,17 @@ impl FrameBuilder {
}
let mut deferred_resolves = vec![];
+ let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
+ self.config.dual_source_blending_is_supported;
for (pass_index, pass) in passes.iter_mut().enumerate() {
let ctx = RenderTargetContext {
- device_pixel_ratio,
+ device_pixel_scale,
prim_store: &self.prim_store,
resource_cache,
node_data: &node_data,
clip_scroll_tree,
+ use_dual_source_blending,
};
pass.build(
@@ -1791,12 +1775,13 @@ impl FrameBuilder {
Frame {
window_size,
inner_rect: self.screen_rect,
- device_pixel_ratio,
+ device_pixel_ratio: device_pixel_scale.0,
background_color: self.background_color,
layer,
profile_counters,
passes,
node_data,
+ clip_chain_local_clip_rects,
render_tasks,
deferred_resolves,
gpu_cache_updates: Some(gpu_cache_updates),
diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs
index 05bca07b6649..9bf21bf7a7b7 100644
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -25,8 +25,11 @@ use std::mem;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
use texture_cache::{TextureCache, TextureCacheHandle};
+#[cfg(test)]
+use thread_profiler::register_thread_with_profiler;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct FontTransform {
pub scale_x: f32,
pub skew_x: f32,
@@ -99,25 +102,17 @@ impl FontTransform {
)
}
- #[allow(dead_code)]
- pub fn inverse(&self) -> Option {
- let det = self.determinant();
- if det != 0.0 {
- let inv_det = det.recip() as f32;
- Some(FontTransform::new(
- self.scale_y * inv_det,
- -self.skew_x * inv_det,
- -self.skew_y * inv_det,
- self.scale_x * inv_det
- ))
- } else {
- None
- }
+ pub fn invert_scale(&self, x_scale: f64, y_scale: f64) -> Self {
+ self.pre_scale(x_scale.recip() as f32, y_scale.recip() as f32)
}
- #[allow(dead_code)]
- pub fn apply(&self, x: f32, y: f32) -> (f32, f32) {
- (self.scale_x * x + self.skew_x * y, self.skew_y * x + self.scale_y * y)
+ pub fn synthesize_italics(&self, skew_factor: f32) -> Self {
+ FontTransform::new(
+ self.scale_x,
+ self.skew_x - self.scale_x * skew_factor,
+ self.skew_y,
+ self.scale_y - self.skew_y * skew_factor,
+ )
}
}
@@ -128,6 +123,7 @@ impl<'a> From<&'a LayerToWorldTransform> for FontTransform {
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct FontInstance {
pub font_key: FontKey,
// The font size is in *device* pixels, not logical pixels.
@@ -567,6 +563,13 @@ impl GlyphRasterizer {
});
}
}
+
+ #[cfg(feature = "capture")]
+ pub fn reset(&mut self) {
+ //TODO: any signals need to be sent to the workers?
+ self.pending_glyphs.clear();
+ self.fonts_to_remove.clear();
+ }
}
impl FontContext {
@@ -603,7 +606,7 @@ struct GlyphRasterJob {
}
#[test]
-fn raterize_200_glyphs() {
+fn rasterize_200_glyphs() {
// This test loads a font from disc, the renders 4 requests containing
// 50 glyphs each, deletes the font and waits for the result.
@@ -611,7 +614,12 @@ fn raterize_200_glyphs() {
use std::fs::File;
use std::io::Read;
- let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap());
+ let worker_config = Configuration::new()
+ .thread_name(|idx|{ format!("WRWorker#{}", idx) })
+ .start_handler(move |idx| {
+ register_thread_with_profiler(format!("WRWorker#{}", idx));
+ });
+ let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
let mut glyph_rasterizer = GlyphRasterizer::new(workers);
let mut glyph_cache = GlyphCache::new();
let mut gpu_cache = GpuCache::new();
diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs
index 293fe76d74e0..5116f58f95ef 100644
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{LayerVector2D, LayerRect, LayerToWorldTransform};
+use api::{LayerRect, LayerToWorldTransform};
use gpu_cache::GpuCacheAddress;
use render_task::RenderTaskAddress;
@@ -47,7 +47,7 @@ pub struct SimplePrimitiveInstance {
pub specific_prim_address: GpuCacheAddress,
pub task_address: RenderTaskAddress,
pub clip_task_address: RenderTaskAddress,
- pub clip_id: ClipScrollNodeIndex,
+ pub clip_chain_rect_index: ClipChainRectIndex,
pub scroll_id: ClipScrollNodeIndex,
pub z_sort_index: i32,
}
@@ -57,7 +57,7 @@ impl SimplePrimitiveInstance {
specific_prim_address: GpuCacheAddress,
task_address: RenderTaskAddress,
clip_task_address: RenderTaskAddress,
- clip_id: ClipScrollNodeIndex,
+ clip_chain_rect_index: ClipChainRectIndex,
scroll_id: ClipScrollNodeIndex,
z_sort_index: i32,
) -> SimplePrimitiveInstance {
@@ -65,7 +65,7 @@ impl SimplePrimitiveInstance {
specific_prim_address,
task_address,
clip_task_address,
- clip_id,
+ clip_chain_rect_index,
scroll_id,
z_sort_index,
}
@@ -77,7 +77,7 @@ impl SimplePrimitiveInstance {
self.specific_prim_address.as_int(),
self.task_address.0 as i32,
self.clip_task_address.0 as i32,
- ((self.clip_id.0 as i32) << 16) | self.scroll_id.0 as i32,
+ ((self.clip_chain_rect_index.0 as i32) << 16) | self.scroll_id.0 as i32,
self.z_sort_index,
data0,
data1,
@@ -150,11 +150,11 @@ impl From for PrimitiveInstance {
pub struct BrushInstance {
pub picture_address: RenderTaskAddress,
pub prim_address: GpuCacheAddress,
- pub clip_id: ClipScrollNodeIndex,
+ pub clip_chain_rect_index: ClipChainRectIndex,
pub scroll_id: ClipScrollNodeIndex,
pub clip_task_address: RenderTaskAddress,
pub z: i32,
- pub segment_kind: i32,
+ pub segment_index: i32,
pub user_data0: i32,
pub user_data1: i32,
}
@@ -165,10 +165,10 @@ impl From for PrimitiveInstance {
data: [
instance.picture_address.0 as i32,
instance.prim_address.as_int(),
- ((instance.clip_id.0 as i32) << 16) | instance.scroll_id.0 as i32,
+ ((instance.clip_chain_rect_index.0 as i32) << 16) | instance.scroll_id.0 as i32,
instance.clip_task_address.0 as i32,
instance.z,
- instance.segment_kind,
+ instance.segment_index,
instance.user_data0,
instance.user_data1,
]
@@ -195,20 +195,6 @@ pub struct ClipScrollNodeIndex(pub u32);
#[repr(C)]
pub struct ClipScrollNodeData {
pub transform: LayerToWorldTransform,
-
- /// Viewport rectangle clipped against parent viewport rectangles. This is
- /// in the coordinate system of the node origin. Precisely, it combines the
- /// local clipping rectangles of all the parent nodes on the way to the root,
- /// including those of `ClipRegion` rectangles. The combined clip is reset to
- /// maximum when an incompatible coordinate system is encountered.
- pub local_clip_rect: LayerRect,
-
- /// The scroll offset of all the nodes between us and our parent reference frame.
- /// This is used to calculate intersections between us and content or nodes that
- /// are also direct children of our reference frame.
- pub reference_frame_relative_scroll_offset: LayerVector2D,
-
- pub scroll_offset: LayerVector2D,
pub transform_kind: f32,
pub padding: [f32; 3],
}
@@ -217,11 +203,20 @@ impl ClipScrollNodeData {
pub fn invalid() -> ClipScrollNodeData {
ClipScrollNodeData {
transform: LayerToWorldTransform::identity(),
- local_clip_rect: LayerRect::zero(),
- reference_frame_relative_scroll_offset: LayerVector2D::zero(),
- scroll_offset: LayerVector2D::zero(),
transform_kind: 0.0,
padding: [0.0; 3],
}
}
}
+
+#[derive(Copy, Debug, Clone, PartialEq)]
+#[repr(C)]
+pub struct ClipChainRectIndex(pub usize);
+
+#[derive(Copy, Debug, Clone, PartialEq)]
+#[repr(C)]
+pub enum PictureType {
+ Image = 1,
+ TextShadow = 2,
+ BoxShadow = 3,
+}
diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs
index 1e04fb8da53f..3fe6498dacc9 100644
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -5,6 +5,8 @@
use api::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch};
use api::{ExternalImageData, ExternalImageId};
use api::{ImageFormat, PipelineId};
+#[cfg(feature = "capture")]
+use api::ImageDescriptor;
use api::DebugCommand;
use device::TextureFilter;
use fxhash::FxHasher;
@@ -57,37 +59,6 @@ pub enum SourceTexture {
pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
-/// Optional textures that can be used as a source in the shaders.
-/// Textures that are not used by the batch are equal to TextureId::invalid().
-#[derive(Copy, Clone, Debug)]
-pub struct BatchTextures {
- pub colors: [SourceTexture; 3],
-}
-
-impl BatchTextures {
- pub fn no_texture() -> Self {
- BatchTextures {
- colors: [SourceTexture::Invalid; 3],
- }
- }
-
- pub fn render_target_cache() -> Self {
- BatchTextures {
- colors: [
- SourceTexture::CacheRGBA8,
- SourceTexture::CacheA8,
- SourceTexture::Invalid,
- ],
- }
- }
-
- pub fn color(texture: SourceTexture) -> Self {
- BatchTextures {
- colors: [texture, SourceTexture::Invalid, SourceTexture::Invalid],
- }
- }
-}
-
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RenderTargetInfo {
pub has_depth: bool,
@@ -171,9 +142,20 @@ impl RenderedDocument {
}
}
+#[cfg(feature = "capture")]
+pub struct ExternalCaptureImage {
+ pub short_path: String,
+ pub descriptor: ImageDescriptor,
+ pub external: ExternalImageData,
+}
+
pub enum DebugOutput {
FetchDocuments(String),
FetchClipScrollTree(String),
+ #[cfg(feature = "capture")]
+ SaveCapture(PathBuf, Vec),
+ #[cfg(feature = "capture")]
+ LoadCapture,
}
pub enum ResultMsg {
diff --git a/gfx/webrender/src/lib.rs b/gfx/webrender/src/lib.rs
index ccc1f954210a..0ade76834a39 100644
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -48,7 +48,11 @@ extern crate lazy_static;
extern crate log;
#[macro_use]
extern crate thread_profiler;
+#[cfg(any(feature = "debugger", feature = "capture"))]
+#[macro_use]
+extern crate serde;
+mod batch;
mod border;
mod box_shadow;
mod clip;
@@ -83,6 +87,7 @@ mod render_task;
mod renderer;
mod resource_cache;
mod scene;
+mod segment;
mod spring;
mod texture_allocator;
mod texture_cache;
@@ -139,9 +144,8 @@ extern crate gleam;
extern crate num_traits;
extern crate plane_split;
extern crate rayon;
-#[cfg(feature = "debugger")]
-#[macro_use]
-extern crate serde_derive;
+#[cfg(feature = "capture")]
+extern crate ron;
#[cfg(feature = "debugger")]
extern crate serde_json;
extern crate smallvec;
diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs
index 60cbc93b548c..ca41acdcf10f 100644
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -3,13 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
-use api::{device_length, DeviceIntRect, DeviceIntSize, PipelineId};
-use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
+use api::{DeviceIntPoint, DeviceIntRect, LayerToWorldScale, PipelineId};
+use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerVector2D, Shadow};
use api::{ClipId, PremultipliedColorF};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use frame_builder::PrimitiveContext;
use gpu_cache::GpuDataRequest;
-use gpu_types::BrushImageKind;
+use gpu_types::{BrushImageKind, PictureType};
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
use scene::{FilterOpHelpers, SceneProperties};
@@ -38,13 +38,12 @@ pub enum PictureCompositeMode {
Blit,
}
-/// Configure whether the primitives on this picture
-/// should be rasterized in screen space or local space.
-#[repr(C)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum RasterizationSpace {
- Local = 0,
- Screen = 1,
+/// Configure whether the content to be drawn by a picture
+/// in local space rasterization or the screen space.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum ContentOrigin {
+ Local(LayerPoint),
+ Screen(DeviceIntPoint),
}
#[derive(Debug)]
@@ -108,10 +107,6 @@ pub struct PicturePrimitive {
// picture. For text shadows and box shadows, we want to
// unconditionally draw them.
pub cull_children: bool,
-
- /// Configure whether the primitives on this picture
- /// should be rasterized in screen space or local space.
- pub rasterization_kind: RasterizationSpace,
}
impl PicturePrimitive {
@@ -127,7 +122,6 @@ impl PicturePrimitive {
},
pipeline_id,
cull_children: false,
- rasterization_kind: RasterizationSpace::Local,
}
}
@@ -175,7 +169,6 @@ impl PicturePrimitive {
},
pipeline_id,
cull_children: false,
- rasterization_kind: RasterizationSpace::Local,
}
}
@@ -185,7 +178,7 @@ impl PicturePrimitive {
pipeline_id: PipelineId,
reference_frame_id: ClipId,
frame_output_pipeline_id: Option,
- ) -> PicturePrimitive {
+ ) -> Self {
PicturePrimitive {
runs: Vec::new(),
render_task_id: None,
@@ -199,9 +192,6 @@ impl PicturePrimitive {
},
pipeline_id,
cull_children: true,
- // TODO(gw): Make this configurable based on an
- // exposed API parameter in StackingContext.
- rasterization_kind: RasterizationSpace::Screen,
}
}
@@ -262,46 +252,48 @@ impl PicturePrimitive {
}
PictureKind::BoxShadow { blur_radius, clip_mode, image_kind, ref mut content_rect, .. } => {
// We need to inflate the content rect if outset.
- match clip_mode {
+ *content_rect = match clip_mode {
BoxShadowClipMode::Outset => {
- let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
-
- // If the radii are uniform, we can render just the top
- // left corner and mirror it across the primitive. In
- // this case, shift the content rect to leave room
- // for the blur to take effect.
match image_kind {
BrushImageKind::Mirror => {
- let origin = LayerPoint::new(
- local_content_rect.origin.x - blur_offset,
- local_content_rect.origin.y - blur_offset,
- );
- let size = LayerSize::new(
- local_content_rect.size.width + blur_offset,
- local_content_rect.size.height + blur_offset,
- );
- *content_rect = LayerRect::new(origin, size);
+ let half_offset = 0.5 * blur_radius * BLUR_SAMPLE_SCALE;
+ // If the radii are uniform, we can render just the top
+ // left corner and mirror it across the primitive. In
+ // this case, shift the content rect to leave room
+ // for the blur to take effect.
+ local_content_rect
+ .translate(&-LayerVector2D::new(half_offset, half_offset))
+ .inflate(half_offset, half_offset)
}
BrushImageKind::NinePatch | BrushImageKind::Simple => {
+ let full_offset = blur_radius * BLUR_SAMPLE_SCALE;
// For a non-uniform radii, we need to expand
// the content rect on all sides for the blur.
- *content_rect = local_content_rect.inflate(
- blur_offset,
- blur_offset,
- );
+ local_content_rect.inflate(
+ full_offset,
+ full_offset,
+ )
}
}
}
BoxShadowClipMode::Inset => {
- *content_rect = local_content_rect;
+ local_content_rect
}
- }
+ };
prim_local_rect
}
}
}
+ pub fn picture_type(&self) -> PictureType {
+ match self.kind {
+ PictureKind::Image { .. } => PictureType::Image,
+ PictureKind::BoxShadow { .. } => PictureType::BoxShadow,
+ PictureKind::TextShadow { .. } => PictureType::TextShadow,
+ }
+ }
+
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
@@ -311,29 +303,30 @@ impl PicturePrimitive {
child_tasks: Vec,
parent_tasks: &mut Vec,
) {
+ let content_scale = LayerToWorldScale::new(1.0) * prim_context.device_pixel_scale;
+
match self.kind {
PictureKind::Image {
ref mut secondary_render_task_id,
composite_mode,
..
} => {
+ let content_origin = ContentOrigin::Screen(prim_screen_rect.origin);
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
let picture_task = RenderTask::new_picture(
Some(prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
- prim_screen_rect.origin.x as f32,
- prim_screen_rect.origin.y as f32,
+ content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- self.rasterization_kind,
child_tasks,
None,
+ PictureType::Image,
);
- let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
- let blur_std_deviation = blur_radius.0 as f32;
+ let blur_std_deviation = blur_radius * prim_context.device_pixel_scale.0;
let picture_task_id = render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
@@ -351,24 +344,24 @@ impl PicturePrimitive {
self.render_task_id = Some(blur_render_task_id);
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
+ let screen_offset = (offset * content_scale).round().to_i32();
let picture_task = RenderTask::new_picture(
Some(prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
- prim_screen_rect.origin.x as f32 - offset.x,
- prim_screen_rect.origin.y as f32 - offset.y,
+ ContentOrigin::Screen(prim_screen_rect.origin - screen_offset),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- self.rasterization_kind,
child_tasks,
None,
+ PictureType::Image,
);
- let blur_std_deviation = blur_radius * prim_context.device_pixel_ratio;
+ let blur_std_deviation = blur_radius * prim_context.device_pixel_scale.0;
let picture_task_id = render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
- blur_std_deviation,
+ blur_std_deviation.round(),
picture_task_id,
render_tasks,
RenderTargetKind::Color,
@@ -386,13 +379,12 @@ impl PicturePrimitive {
Some(prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
- prim_screen_rect.origin.x as f32,
- prim_screen_rect.origin.y as f32,
+ content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- self.rasterization_kind,
child_tasks,
None,
+ PictureType::Image,
);
let readback_task_id = render_tasks.add(RenderTask::new_readback(*prim_screen_rect));
@@ -416,13 +408,12 @@ impl PicturePrimitive {
Some(prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
- prim_screen_rect.origin.x as f32,
- prim_screen_rect.origin.y as f32,
+ content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- self.rasterization_kind,
child_tasks,
None,
+ PictureType::Image,
);
self.render_task_id = Some(render_tasks.add(picture_task));
@@ -433,13 +424,12 @@ impl PicturePrimitive {
Some(prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
- prim_screen_rect.origin.x as f32,
- prim_screen_rect.origin.y as f32,
+ content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
- self.rasterization_kind,
child_tasks,
None,
+ PictureType::Image,
);
self.render_task_id = Some(render_tasks.add(picture_task));
@@ -456,35 +446,29 @@ impl PicturePrimitive {
// blur to that text run in order to build the actual primitive
// which will be blitted to the framebuffer.
- let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
-
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
- let cache_width =
- (content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
- let cache_height =
- (content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
- let cache_size = DeviceIntSize::new(cache_width, cache_height);
+ let cache_size = (content_rect.size * content_scale).round().to_i32();
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
- let blur_std_deviation = blur_radius.0 as f32 * 0.5;
+ let device_radius = (blur_radius * prim_context.device_pixel_scale.0).round();
+ let blur_std_deviation = device_radius * 0.5;
let picture_task = RenderTask::new_picture(
Some(cache_size),
prim_index,
RenderTargetKind::Color,
- content_rect.origin.x,
- content_rect.origin.y,
+ ContentOrigin::Local(content_rect.origin),
color.premultiplied(),
ClearMode::Transparent,
- self.rasterization_kind,
Vec::new(),
None,
+ PictureType::TextShadow,
);
let picture_task_id = render_tasks.add(picture_task);
@@ -503,23 +487,18 @@ impl PicturePrimitive {
self.render_task_id = Some(render_tasks.add(render_task));
}
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, cache_key, .. } => {
- let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
-
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
- let cache_width =
- (content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
- let cache_height =
- (content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
- let cache_size = DeviceIntSize::new(cache_width, cache_height);
+ let cache_size = (content_rect.size * content_scale).round().to_i32();
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
- let blur_std_deviation = blur_radius.0 as f32 * 0.5;
+ let device_radius = (blur_radius * prim_context.device_pixel_scale.0).round();
+ let blur_std_deviation = device_radius * 0.5;
let blur_clear_mode = match clip_mode {
BoxShadowClipMode::Outset => {
@@ -534,13 +513,12 @@ impl PicturePrimitive {
Some(cache_size),
prim_index,
RenderTargetKind::Alpha,
- content_rect.origin.x,
- content_rect.origin.y,
+ ContentOrigin::Local(content_rect.origin),
color.premultiplied(),
ClearMode::Zero,
- self.rasterization_kind,
Vec::new(),
Some(cache_key),
+ PictureType::BoxShadow,
);
let picture_task_id = render_tasks.add(picture_task);
diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs
index 83d7c211fb12..9d3d70658cb7 100644
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -22,7 +22,7 @@ use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat, RasterizedGlyph};
use internal_types::FastHashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
@@ -264,6 +264,9 @@ fn is_bitmap_font(ct_font: &CTFont) -> bool {
(traits & kCTFontColorGlyphsTrait) != 0
}
+// Skew factor matching Gecko/CG.
+const OBLIQUE_SKEW_FACTOR: f32 = 0.25;
+
impl FontContext {
pub fn new() -> FontContext {
debug!("Test for subpixel AA support: {}", supports_subpixel_aa());
@@ -358,7 +361,28 @@ impl FontContext {
let glyph = key.index as CGGlyph;
let bitmap = is_bitmap_font(ct_font);
let (x_offset, y_offset) = if bitmap { (0.0, 0.0) } else { font.get_subpx_offset(key) };
- let metrics = get_glyph_metrics(ct_font, None, glyph, x_offset, y_offset, 0.0);
+ let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR);
+ Some(CGAffineTransform {
+ a: shape.scale_x as f64,
+ b: -shape.skew_y as f64,
+ c: -shape.skew_x as f64,
+ d: shape.scale_y as f64,
+ tx: 0.0,
+ ty: 0.0,
+ })
+ } else {
+ None
+ };
+ let extra_strikes = font.get_extra_strikes(1.0);
+ let metrics = get_glyph_metrics(
+ ct_font,
+ transform.as_ref(),
+ glyph,
+ x_offset,
+ y_offset,
+ extra_strikes as f64,
+ );
if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
None
} else {
@@ -454,22 +478,28 @@ impl FontContext {
};
let bitmap = is_bitmap_font(&ct_font);
- let shape = font.transform.pre_scale(y_scale.recip() as f32, y_scale.recip() as f32);
- let transform = if bitmap || shape.is_identity() {
- None
+ let (mut shape, (x_offset, y_offset)) = if bitmap {
+ (FontTransform::identity(), (0.0, 0.0))
} else {
+ (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
+ };
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
+ }
+ let transform = if !shape.is_identity() {
Some(CGAffineTransform {
a: shape.scale_x as f64,
b: -shape.skew_y as f64,
c: -shape.skew_x as f64,
d: shape.scale_y as f64,
tx: 0.0,
- ty: 0.0
+ ty: 0.0,
})
+ } else {
+ None
};
let glyph = key.index as CGGlyph;
- let (x_offset, y_offset) = if bitmap { (0.0, 0.0) } else { font.get_subpx_offset(key) };
let (strike_scale, pixel_step) = if bitmap { (y_scale, 1.0) } else { (x_scale, y_scale / x_scale) };
let extra_strikes = font.get_extra_strikes(strike_scale);
let metrics = get_glyph_metrics(
diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs
index e55567e79090..814466183543 100644
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -53,7 +53,56 @@ unsafe impl Send for FontContext {}
extern "C" {
fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
- fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot);
+}
+
+// Skew factor matching Gecko/FreeType.
+const OBLIQUE_SKEW_FACTOR: f32 = 0.2;
+
+fn get_skew_bounds(bottom: i32, top: i32) -> (f32, f32) {
+ let skew_min = ((bottom as f32 + 0.5) * OBLIQUE_SKEW_FACTOR).floor();
+ let skew_max = ((top as f32 - 0.5) * OBLIQUE_SKEW_FACTOR).ceil();
+ (skew_min, skew_max)
+}
+
+fn skew_bitmap(bitmap: &[u8], width: usize, height: usize, left: i32, top: i32) -> (Vec, usize, i32) {
+ let stride = width * 4;
+ // Calculate the skewed horizontal offsets of the bottom and top of the glyph.
+ let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
+ // Allocate enough extra width for the min/max skew offsets.
+ let skew_width = width + (skew_max - skew_min) as usize;
+ let mut skew_buffer = vec![0u8; skew_width * height * 4];
+ for y in 0 .. height {
+ // Calculate a skew offset at the vertical center of the current row.
+ let offset = (top as f32 - y as f32 - 0.5) * OBLIQUE_SKEW_FACTOR - skew_min;
+ // Get a blend factor in 0..256 constant across all pixels in the row.
+ let blend = (offset.fract() * 256.0) as u32;
+ let src_row = y * stride;
+ let dest_row = (y * skew_width + offset.floor() as usize) * 4;
+ let mut prev_px = [0u32; 4];
+ for (src, dest) in
+ bitmap[src_row .. src_row + stride].chunks(4).zip(
+ skew_buffer[dest_row .. dest_row + stride].chunks_mut(4)
+ ) {
+ let px = [src[0] as u32, src[1] as u32, src[2] as u32, src[3] as u32];
+ // Blend current pixel with previous pixel based on blend factor.
+ let next_px = [px[0] * blend, px[1] * blend, px[2] * blend, px[3] * blend];
+ dest[0] = ((((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8) as u8;
+ dest[1] = ((((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8) as u8;
+ dest[2] = ((((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8) as u8;
+ dest[3] = ((((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8) as u8;
+ // Save the remainder for blending onto the next pixel.
+ prev_px = next_px;
+ }
+ // If the skew misaligns the final pixel, write out the remainder.
+ if blend > 0 {
+ let dest = &mut skew_buffer[dest_row + stride .. dest_row + stride + 4];
+ dest[0] = ((prev_px[0] + 128) >> 8) as u8;
+ dest[1] = ((prev_px[1] + 128) >> 8) as u8;
+ dest[2] = ((prev_px[2] + 128) >> 8) as u8;
+ dest[3] = ((prev_px[3] + 128) >> 8) as u8;
+ }
+ }
+ (skew_buffer, skew_width, left + skew_min as i32)
}
impl FontContext {
@@ -151,7 +200,13 @@ impl FontContext {
let face = self.faces.get(&font.font_key).unwrap();
let mut load_flags = FT_LOAD_DEFAULT;
- let FontInstancePlatformOptions { hinting, .. } = font.platform_options.unwrap_or_default();
+ let FontInstancePlatformOptions { mut hinting, .. } = font.platform_options.unwrap_or_default();
+ // Disable hinting if there is a non-axis-aligned transform.
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) ||
+ ((font.transform.scale_x != 0.0 || font.transform.scale_y != 0.0) &&
+ (font.transform.skew_x != 0.0 || font.transform.skew_y != 0.0)) {
+ hinting = FontHinting::None;
+ }
match (hinting, font.render_mode) {
(FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING,
(FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO,
@@ -194,7 +249,10 @@ impl FontContext {
unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) };
self.choose_bitmap_size(face.face, req_size * y_scale)
} else {
- let shape = font.transform.pre_scale(x_scale.recip() as f32, y_scale.recip() as f32);
+ let mut shape = font.transform.invert_scale(x_scale, y_scale);
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
+ };
let mut ft_shape = FT_Matrix {
xx: (shape.scale_x * 65536.0) as FT_Fixed,
xy: (shape.skew_x * -65536.0) as FT_Fixed,
@@ -305,40 +363,42 @@ impl FontContext {
slot: FT_GlyphSlot,
font: &FontInstance,
glyph: &GlyphKey,
- scale_bitmaps: bool,
+ transform_bitmaps: bool,
) -> Option {
let metrics = unsafe { &(*slot).metrics };
- let advance = metrics.horiAdvance as f32 / 64.0;
+ let mut advance = metrics.horiAdvance as f32 / 64.0;
match unsafe { (*slot).format } {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
- let left = unsafe { (*slot).bitmap_left };
- let top = unsafe { (*slot).bitmap_top };
- let width = unsafe { (*slot).bitmap.width };
- let height = unsafe { (*slot).bitmap.rows };
- if scale_bitmaps {
+ let mut left = unsafe { (*slot).bitmap_left };
+ let mut top = unsafe { (*slot).bitmap_top };
+ let mut width = unsafe { (*slot).bitmap.width };
+ let mut height = unsafe { (*slot).bitmap.rows };
+ if transform_bitmaps {
let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
let scale = font.size.to_f32_px() / y_size as f32;
let x0 = left as f32 * scale;
let x1 = width as f32 * scale + x0;
let y1 = top as f32 * scale;
let y0 = y1 - height as f32 * scale;
- Some(GlyphDimensions {
- left: x0.round() as i32,
- top: y1.round() as i32,
- width: (x1.ceil() - x0.floor()) as u32,
- height: (y1.ceil() - y0.floor()) as u32,
- advance: advance * scale,
- })
- } else {
- Some(GlyphDimensions {
- left,
- top,
- width,
- height,
- advance,
- })
+ left = x0.round() as i32;
+ top = y1.round() as i32;
+ width = (x1.ceil() - x0.floor()) as u32;
+ height = (y1.ceil() - y0.floor()) as u32;
+ advance *= scale;
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
+ left += skew_min as i32;
+ width += (skew_max - skew_min) as u32;
+ }
}
+ Some(GlyphDimensions {
+ left,
+ top,
+ width,
+ height,
+ advance,
+ })
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
let cbox = self.get_bounding_box(slot, font, glyph);
@@ -429,10 +489,6 @@ impl FontContext {
dx - ((cbox.xMin + dx) & !63),
dy - ((cbox.yMin + dy) & !63),
);
-
- if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
- FT_GlyphSlot_Oblique(slot);
- }
}
if font.render_mode == FontRenderMode::Subpixel {
@@ -514,23 +570,23 @@ impl FontContext {
let bitmap = unsafe { &(*slot).bitmap };
let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
- let (actual_width, actual_height) = match pixel_mode {
+ let (mut actual_width, actual_height) = match pixel_mode {
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
assert!(bitmap.width % 3 == 0);
- ((bitmap.width / 3) as i32, bitmap.rows as i32)
+ ((bitmap.width / 3) as usize, bitmap.rows as usize)
}
FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
assert!(bitmap.rows % 3 == 0);
- (bitmap.width as i32, (bitmap.rows / 3) as i32)
+ (bitmap.width as usize, (bitmap.rows / 3) as usize)
}
FT_Pixel_Mode::FT_PIXEL_MODE_MONO |
FT_Pixel_Mode::FT_PIXEL_MODE_GRAY |
FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
- (bitmap.width as i32, bitmap.rows as i32)
+ (bitmap.width as usize, bitmap.rows as usize)
}
_ => panic!("Unsupported {:?}", pixel_mode),
};
- let mut final_buffer = vec![0; (actual_width * actual_height * 4) as usize];
+ let mut final_buffer = vec![0u8; actual_width * actual_height * 4];
// Extract the final glyph from FT format into BGRA8 format, which is
// what WR expects.
@@ -539,7 +595,7 @@ impl FontContext {
let mut dest: usize = 0;
while dest < final_buffer.len() {
let mut src = src_row;
- let row_end = dest + actual_width as usize * 4;
+ let row_end = dest + actual_width * 4;
match pixel_mode {
FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
while dest < row_end {
@@ -613,10 +669,19 @@ impl FontContext {
}
match format {
+ FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ let (skew_buffer, skew_width, skew_left) =
+ skew_bitmap(&final_buffer, actual_width, actual_height, left, top);
+ final_buffer = skew_buffer;
+ actual_width = skew_width;
+ left = skew_left;
+ }
+ }
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
unsafe {
left += (*slot).bitmap_left;
- top += (*slot).bitmap_top - actual_height;
+ top += (*slot).bitmap_top - height as i32;
}
}
_ => {}
diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs
index 2f71d61645c0..326800280445 100644
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -6,7 +6,7 @@ use api::{FontInstanceFlags, FontKey, FontRenderMode};
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
use dwrote;
use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat, RasterizedGlyph};
use internal_types::FastHashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
@@ -86,6 +86,9 @@ fn is_bitmap_font(font: &FontInstance) -> bool {
font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS)
}
+// Skew factor matching Gecko/DWrite.
+const OBLIQUE_SKEW_FACTOR: f32 = 0.3;
+
impl FontContext {
pub fn new() -> FontContext {
// These are the default values we use in Gecko.
@@ -160,16 +163,10 @@ impl FontContext {
&mut self,
font: &FontInstance,
) -> &dwrote::FontFace {
- if !font.flags.intersects(FontInstanceFlags::SYNTHETIC_BOLD | FontInstanceFlags::SYNTHETIC_ITALICS) {
+ if !font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
return self.fonts.get(&font.font_key).unwrap();
}
- let mut sims = dwrote::DWRITE_FONT_SIMULATIONS_NONE;
- if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
- sims = sims | dwrote::DWRITE_FONT_SIMULATIONS_BOLD;
- }
- if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
- sims = sims | dwrote::DWRITE_FONT_SIMULATIONS_OBLIQUE;
- }
+ let sims = dwrote::DWRITE_FONT_SIMULATIONS_BOLD;
match self.simulations.entry((font.font_key, sims)) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
@@ -239,7 +236,20 @@ impl FontContext {
) -> Option {
let size = font.size.to_f32_px();
let bitmaps = is_bitmap_font(font);
- let analysis = self.create_glyph_analysis(font, key, size, None, bitmaps);
+ let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR);
+ Some(dwrote::DWRITE_MATRIX {
+ m11: shape.scale_x,
+ m12: shape.skew_y,
+ m21: shape.skew_x,
+ m22: shape.scale_y,
+ dx: 0.0,
+ dy: 0.0,
+ })
+ } else {
+ None
+ };
+ let analysis = self.create_glyph_analysis(font, key, size, transform, bitmaps);
let texture_type = dwrite_texture_type(font.render_mode);
@@ -344,11 +354,15 @@ impl FontContext {
let (.., y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
let size = (font.size.to_f64_px() * y_scale) as f32;
let bitmaps = is_bitmap_font(font);
- let transform = if bitmaps {
- None
+ let (mut shape, (x_offset, y_offset)) = if bitmaps {
+ (FontTransform::identity(), (0.0, 0.0))
} else {
- let (x_offset, y_offset) = font.get_subpx_offset(key);
- let shape = font.transform.pre_scale(y_scale.recip() as f32, y_scale.recip() as f32);
+ (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
+ };
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
+ }
+ let transform = if !shape.is_identity() || (x_offset, y_offset) != (0.0, 0.0) {
Some(dwrote::DWRITE_MATRIX {
m11: shape.scale_x,
m12: shape.skew_y,
@@ -357,6 +371,8 @@ impl FontContext {
dx: x_offset as f32,
dy: y_offset as f32,
})
+ } else {
+ None
};
let analysis = self.create_glyph_analysis(font, key, size, transform, bitmaps);
diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs
index 37cb1e3035f8..2f9ae00498ed 100644
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -3,30 +3,33 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode, ColorF, ColorU};
-use api::{ComplexClipRegion, DeviceIntRect, DevicePoint, ExtendMode, FontRenderMode};
+use api::{DeviceIntRect, DevicePixelScale, DevicePoint};
+use api::{ComplexClipRegion, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTransform};
use api::{YuvColorSpace, YuvFormat};
use border::BorderCornerInstance;
use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree};
+use clip_scroll_node::ClipScrollNode;
use clip::{ClipSource, ClipSourcesHandle, ClipStore};
use frame_builder::PrimitiveContext;
use glyph_rasterizer::{FontInstance, FontTransform};
use internal_types::{FastHashMap};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
-use gpu_types::ClipScrollNodeData;
-use picture::{PictureKind, PicturePrimitive, RasterizationSpace};
+use gpu_types::{ClipChainRectIndex, ClipScrollNodeData};
+use picture::{PictureKind, PicturePrimitive};
use profiler::FrameProfileCounters;
use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipWorkItem, RenderTask};
use render_task::{RenderTaskId, RenderTaskTree};
use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ImageProperties, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
-use std::{mem, u16, usize};
+use segment::SegmentBuilder;
+use std::{mem, usize};
use std::rc::Rc;
-use util::{MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe, pack_as_float};
+use util::{MatrixHelpers, calculate_screen_bounding_rect, pack_as_float};
use util::recycle_vec;
@@ -183,6 +186,7 @@ pub struct PrimitiveMetadata {
// storing them here.
pub local_rect: LayerRect,
pub local_clip_rect: LayerRect,
+ pub clip_chain_rect_index: ClipChainRectIndex,
pub is_backface_visible: bool,
pub screen_rect: Option,
@@ -219,167 +223,77 @@ impl BrushKind {
}
}
-#[derive(Debug, Copy, Clone)]
-#[repr(u32)]
-pub enum BrushAntiAliasMode {
- Primitive = 0,
- Segment = 1,
-}
-
-#[allow(dead_code)]
-#[derive(Debug, Copy, Clone)]
-#[repr(C)]
-pub enum BrushSegmentKind {
- TopLeft = 0,
- TopRight,
- BottomRight,
- BottomLeft,
-
- TopMid,
- MidRight,
- BottomMid,
- MidLeft,
-
- Center,
+bitflags! {
+ /// Each bit of the edge AA mask is:
+ /// 0, when the edge of the primitive needs to be considered for AA
+ /// 1, when the edge of the segment needs to be considered for AA
+ ///
+ /// *Note*: the bit values have to match the shader logic in
+ /// `write_transform_vertex()` function.
+ pub struct EdgeAaSegmentMask: u8 {
+ const LEFT = 0x1;
+ const TOP = 0x2;
+ const RIGHT = 0x4;
+ const BOTTOM = 0x8;
+ }
}
#[derive(Debug)]
pub struct BrushSegment {
pub local_rect: LayerRect,
pub clip_task_id: Option,
+ pub may_need_clip_mask: bool,
+ pub edge_flags: EdgeAaSegmentMask,
}
impl BrushSegment {
- fn new(
+ pub fn new(
origin: LayerPoint,
size: LayerSize,
+ may_need_clip_mask: bool,
+ edge_flags: EdgeAaSegmentMask,
) -> BrushSegment {
BrushSegment {
local_rect: LayerRect::new(origin, size),
clip_task_id: None,
+ may_need_clip_mask,
+ edge_flags,
}
}
}
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum BrushClipMaskKind {
+ Unknown,
+ Individual,
+ Global,
+}
+
#[derive(Debug)]
pub struct BrushSegmentDescriptor {
- pub top_left_offset: LayerVector2D,
- pub bottom_right_offset: LayerVector2D,
- pub segments: [BrushSegment; 9],
- pub enabled_segments: u16,
- pub can_optimize_clip_mask: bool,
-}
-
-impl BrushSegmentDescriptor {
- pub fn new(
- outer_rect: &LayerRect,
- inner_rect: &LayerRect,
- valid_segments: Option<&[BrushSegmentKind]>,
- ) -> BrushSegmentDescriptor {
- let p0 = outer_rect.origin;
- let p1 = inner_rect.origin;
- let p2 = inner_rect.bottom_right();
- let p3 = outer_rect.bottom_right();
-
- let enabled_segments = match valid_segments {
- Some(valid_segments) => {
- valid_segments.iter().fold(
- 0,
- |acc, segment| acc | 1 << *segment as u32
- )
- }
- None => u16::MAX,
- };
-
- BrushSegmentDescriptor {
- enabled_segments,
- can_optimize_clip_mask: false,
- top_left_offset: p1 - p0,
- bottom_right_offset: p3 - p2,
- segments: [
- BrushSegment::new(
- LayerPoint::new(p0.x, p0.y),
- LayerSize::new(p1.x - p0.x, p1.y - p0.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p2.x, p0.y),
- LayerSize::new(p3.x - p2.x, p1.y - p0.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p2.x, p2.y),
- LayerSize::new(p3.x - p2.x, p3.y - p2.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p0.x, p2.y),
- LayerSize::new(p1.x - p0.x, p3.y - p2.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p1.x, p0.y),
- LayerSize::new(p2.x - p1.x, p1.y - p0.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p2.x, p1.y),
- LayerSize::new(p3.x - p2.x, p2.y - p1.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p1.x, p2.y),
- LayerSize::new(p2.x - p1.x, p3.y - p2.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p0.x, p1.y),
- LayerSize::new(p1.x - p0.x, p2.y - p1.y),
- ),
- BrushSegment::new(
- LayerPoint::new(p1.x, p1.y),
- LayerSize::new(p2.x - p1.x, p2.y - p1.y),
- ),
- ],
- }
- }
+ pub segments: Vec,
+ pub clip_mask_kind: BrushClipMaskKind,
}
#[derive(Debug)]
pub struct BrushPrimitive {
pub kind: BrushKind,
- pub segment_desc: Option>,
- pub aa_mode: BrushAntiAliasMode,
+ pub segment_desc: Option,
}
impl BrushPrimitive {
pub fn new(
kind: BrushKind,
- segment_desc: Option>,
- aa_mode: BrushAntiAliasMode,
+ segment_desc: Option,
) -> BrushPrimitive {
BrushPrimitive {
kind,
segment_desc,
- aa_mode,
}
}
-}
-impl ToGpuBlocks for BrushPrimitive {
- fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
- match self.segment_desc {
- Some(ref segment_desc) => {
- request.push([
- segment_desc.top_left_offset.x,
- segment_desc.top_left_offset.y,
- segment_desc.bottom_right_offset.x,
- segment_desc.bottom_right_offset.y,
- ]);
- }
- None => {
- request.push([0.0; 4]);
- }
- }
- request.push([
- self.aa_mode as u32 as f32,
- 0.0,
- 0.0,
- 0.0,
- ]);
+ fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
+ // has to match VECS_PER_SPECIFIC_BRUSH
match self.kind {
BrushKind::Solid { color } => {
request.push(color.premultiplied());
@@ -430,8 +344,8 @@ pub struct LinePrimitive {
pub orientation: LineOrientation,
}
-impl ToGpuBlocks for LinePrimitive {
- fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+impl LinePrimitive {
+ fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
request.push(self.color);
request.push([
self.wavy_line_thickness,
@@ -746,13 +660,12 @@ pub struct TextRunPrimitiveCpu {
impl TextRunPrimitiveCpu {
pub fn get_font(
&self,
- device_pixel_ratio: f32,
- transform: &LayerToWorldTransform,
- rasterization_kind: RasterizationSpace,
+ device_pixel_scale: DevicePixelScale,
+ transform: Option<&LayerToWorldTransform>,
) -> FontInstance {
let mut font = self.font.clone();
- font.size = font.size.scale_by(device_pixel_ratio);
- if rasterization_kind == RasterizationSpace::Screen {
+ font.size = font.size.scale_by(device_pixel_scale.0);
+ if let Some(transform) = transform {
if transform.has_perspective_component() || !transform.has_2d_inverse() {
font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
} else {
@@ -769,13 +682,12 @@ impl TextRunPrimitiveCpu {
fn prepare_for_render(
&mut self,
resource_cache: &mut ResourceCache,
- device_pixel_ratio: f32,
- transform: &LayerToWorldTransform,
+ device_pixel_scale: DevicePixelScale,
+ transform: Option<&LayerToWorldTransform>,
display_list: &BuiltDisplayList,
gpu_cache: &mut GpuCache,
- rasterization_kind: RasterizationSpace,
) {
- let font = self.get_font(device_pixel_ratio, transform, rasterization_kind);
+ let font = self.get_font(device_pixel_scale, transform);
// Cache the glyph positions, if not in the cache already.
// TODO(gw): In the future, remove `glyph_instances`
@@ -1098,6 +1010,7 @@ impl PrimitiveStore {
clip_task_id: None,
local_rect: *local_rect,
local_clip_rect: *local_clip_rect,
+ clip_chain_rect_index: ClipChainRectIndex(0),
is_backface_visible: is_backface_visible,
screen_rect: None,
tag,
@@ -1269,13 +1182,20 @@ impl PrimitiveStore {
PrimitiveKind::TextRun => {
let pic = &self.cpu_pictures[pic_index.0];
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
+ // The transform only makes sense for screen space rasterization
+ let transform = match pic.kind {
+ PictureKind::BoxShadow { .. } => None,
+ PictureKind::TextShadow { .. } => None,
+ PictureKind::Image { .. } => {
+ Some(&prim_context.scroll_node.world_content_transform)
+ },
+ };
text.prepare_for_render(
resource_cache,
- prim_context.device_pixel_ratio,
- &prim_context.scroll_node.world_content_transform,
+ prim_context.device_pixel_scale,
+ transform,
prim_context.display_list,
gpu_cache,
- pic.rasterization_kind,
);
}
PrimitiveKind::Image => {
@@ -1322,13 +1242,29 @@ impl PrimitiveStore {
// Mark this GPU resource as required for this frame.
if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
+ // has to match VECS_PER_BRUSH_PRIM
request.push(metadata.local_rect);
request.push(metadata.local_clip_rect);
match metadata.prim_kind {
PrimitiveKind::Line => {
let line = &self.cpu_lines[metadata.cpu_prim_index.0];
- line.write_gpu_blocks(request);
+ line.write_gpu_blocks(&mut request);
+
+ // TODO(gw): This is a bit of a hack. The Line type
+ // is drawn by the brush_line shader, so the
+ // layout here needs to conform to the same
+ // BrushPrimitive layout. We should tidy this
+ // up in the future so it's enforced that these
+ // types use a shared function to write out the
+ // GPU blocks...
+ request.push(metadata.local_rect);
+ request.push([
+ EdgeAaSegmentMask::empty().bits() as f32,
+ 0.0,
+ 0.0,
+ 0.0
+ ]);
}
PrimitiveKind::Border => {
let border = &self.cpu_borders[metadata.cpu_prim_index.0];
@@ -1359,6 +1295,9 @@ impl PrimitiveStore {
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::Picture => {
+ self.cpu_pictures[metadata.cpu_prim_index.0]
+ .write_gpu_blocks(&mut request);
+
// TODO(gw): This is a bit of a hack. The Picture type
// is drawn by the brush_image shader, so the
// layout here needs to conform to the same
@@ -1366,26 +1305,46 @@ impl PrimitiveStore {
// up in the future so it's enforced that these
// types use a shared function to write out the
// GPU blocks...
- request.push([0.0; 4]);
+ request.push(metadata.local_rect);
request.push([
- BrushAntiAliasMode::Primitive as u32 as f32,
- 0.0,
+ EdgeAaSegmentMask::empty().bits() as f32,
0.0,
0.0,
+ 0.0
]);
-
- self.cpu_pictures[metadata.cpu_prim_index.0]
- .write_gpu_blocks(&mut request);
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
- brush.write_gpu_blocks(request);
+ brush.write_gpu_blocks(&mut request);
+ match brush.segment_desc {
+ Some(ref segment_desc) => {
+ for segment in &segment_desc.segments {
+ // has to match VECS_PER_SEGMENT
+ request.push(segment.local_rect);
+ request.push([
+ segment.edge_flags.bits() as f32,
+ 0.0,
+ 0.0,
+ 0.0
+ ]);
+ }
+ }
+ None => {
+ request.push(metadata.local_rect);
+ request.push([
+ EdgeAaSegmentMask::empty().bits() as f32,
+ 0.0,
+ 0.0,
+ 0.0
+ ]);
+ }
+ }
}
}
}
}
- fn write_brush_nine_patch_segment_description(
+ fn write_brush_segment_description(
&mut self,
prim_index: PrimitiveIndex,
prim_context: &PrimitiveContext,
@@ -1395,75 +1354,124 @@ impl PrimitiveStore {
) {
debug_assert!(self.cpu_metadata[prim_index.0].prim_kind == PrimitiveKind::Brush);
- if clips.len() != 1 {
- return;
- }
-
- let clip_item = clips.first().unwrap();
- if clip_item.coordinate_system_id != prim_context.scroll_node.coordinate_system_id {
- return;
- }
-
let metadata = &self.cpu_metadata[prim_index.0];
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
- if brush.segment_desc.is_some() {
- return;
- }
- if !brush.kind.is_solid() {
- return;
- }
- if metadata.local_rect.size.area() <= MIN_BRUSH_SPLIT_AREA {
- return;
- }
- let local_clips = clip_store.get_opt(&clip_item.clip_sources).expect("bug");
- let mut selected_clip = None;
- for &(ref clip, _) in &local_clips.clips {
- match *clip {
- ClipSource::RoundedRectangle(rect, radii, ClipMode::Clip) => {
- if selected_clip.is_some() {
- selected_clip = None;
- break;
- }
- selected_clip = Some((rect, radii, clip_item.scroll_node_data_index));
+ match brush.segment_desc {
+ Some(ref segment_desc) => {
+ // If we already have a segment descriptor, only run through the
+ // clips list if we haven't already determined the mask kind.
+ if segment_desc.clip_mask_kind != BrushClipMaskKind::Unknown {
+ return;
}
- ClipSource::Rectangle(..) => {}
- ClipSource::RoundedRectangle(_, _, ClipMode::ClipOut) |
- ClipSource::BorderCorner(..) |
- ClipSource::Image(..) => {
- selected_clip = None;
- break;
+ }
+ None => {
+ // If no segment descriptor built yet, see if it is a brush
+ // type that wants to be segmented.
+ if !brush.kind.is_solid() {
+ return;
+ }
+ if metadata.local_rect.size.area() <= MIN_BRUSH_SPLIT_AREA {
+ return;
}
}
}
- if let Some((rect, radii, clip_scroll_node_data_index)) = selected_clip {
- // If the scroll node transforms are different between the clip
- // node and the primitive, we need to get the clip rect in the
- // local space of the primitive, in order to generate correct
- // local segments.
- let local_clip_rect = if clip_scroll_node_data_index == prim_context.scroll_node.node_data_index {
- rect
- } else {
- let clip_transform_data = &node_data[clip_scroll_node_data_index.0 as usize];
- let prim_transform = &prim_context.scroll_node.world_content_transform;
+ let mut segment_builder = SegmentBuilder::new(
+ metadata.local_rect,
+ metadata.local_clip_rect
+ );
- let relative_transform = prim_transform
- .inverse()
- .unwrap_or(WorldToLayerTransform::identity())
- .pre_mul(&clip_transform_data.transform);
+ // If true, we need a clip mask for the entire primitive. This
+ // is either because we don't handle segmenting this clip source,
+ // or we have a clip source from a different coordinate system.
+ let mut clip_mask_kind = BrushClipMaskKind::Individual;
- relative_transform.transform_rect(&rect)
- };
- brush.segment_desc = create_nine_patch(
- &metadata.local_rect,
- &local_clip_rect,
- &radii
- );
+ // Segment the primitive on all the local-space clip sources
+ // that we can.
+ for clip_item in clips {
+ if clip_item.coordinate_system_id != prim_context.scroll_node.coordinate_system_id {
+ clip_mask_kind = BrushClipMaskKind::Global;
+ continue;
+ }
+
+ let local_clips = clip_store.get_opt(&clip_item.clip_sources).expect("bug");
+
+ for &(ref clip, _) in &local_clips.clips {
+ let (local_clip_rect, radius, mode) = match *clip {
+ ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
+ (rect, Some(radii), clip_mode)
+ }
+ ClipSource::Rectangle(rect) => {
+ (rect, None, ClipMode::Clip)
+ }
+ ClipSource::BorderCorner(..) |
+ ClipSource::Image(..) => {
+ // TODO(gw): We can easily extend the segment builder
+ // to support these clip sources in the
+ // future, but they are rarely used.
+ clip_mask_kind = BrushClipMaskKind::Global;
+ continue;
+ }
+ };
+
+ // If the scroll node transforms are different between the clip
+ // node and the primitive, we need to get the clip rect in the
+ // local space of the primitive, in order to generate correct
+ // local segments.
+ let local_clip_rect = if clip_item.scroll_node_data_index == prim_context.scroll_node.node_data_index {
+ local_clip_rect
+ } else {
+ let clip_transform_data = &node_data[clip_item.scroll_node_data_index.0 as usize];
+ let prim_transform = &prim_context.scroll_node.world_content_transform;
+
+ let relative_transform = prim_transform
+ .inverse()
+ .unwrap_or(WorldToLayerTransform::identity())
+ .pre_mul(&clip_transform_data.transform);
+
+ relative_transform.transform_rect(&local_clip_rect)
+ };
+
+ segment_builder.push_rect(
+ local_clip_rect,
+ radius,
+ mode
+ );
+ }
+ }
+
+ match brush.segment_desc {
+ Some(ref mut segment_desc) => {
+ segment_desc.clip_mask_kind = clip_mask_kind;
+ }
+ None => {
+ // TODO(gw): We can probably make the allocation
+ // patterns of this and the segment
+ // builder significantly better, by
+ // retaining it across primitives.
+ let mut segments = Vec::new();
+
+ segment_builder.build(|segment| {
+ segments.push(
+ BrushSegment::new(
+ segment.rect.origin,
+ segment.rect.size,
+ segment.has_mask,
+ segment.edge_flags,
+ ),
+ );
+ });
+
+ brush.segment_desc = Some(BrushSegmentDescriptor {
+ segments,
+ clip_mask_kind,
+ });
+ }
}
}
- fn update_nine_patch_clip_task_for_brush(
+ fn update_clip_task_for_brush(
&mut self,
prim_context: &PrimitiveContext,
prim_index: PrimitiveIndex,
@@ -1478,7 +1486,7 @@ impl PrimitiveStore {
return false;
}
- self.write_brush_nine_patch_segment_description(
+ self.write_brush_segment_description(
prim_index,
prim_context,
clip_store,
@@ -1492,24 +1500,14 @@ impl PrimitiveStore {
Some(ref mut description) => description,
None => return false,
};
+ let clip_mask_kind = segment_desc.clip_mask_kind;
- let enabled_segments = segment_desc.enabled_segments;
- let can_optimize_clip_mask = segment_desc.can_optimize_clip_mask;
-
- for (i, segment) in segment_desc.segments.iter_mut().enumerate() {
- // We only build clips for the corners. The ordering of the
- // BrushSegmentKind enum is such that corners come first, then
- // edges, then inner.
- let segment_enabled = ((1 << i) & enabled_segments) != 0;
- let create_clip_task =
- segment_enabled &&
- (!can_optimize_clip_mask || i <= BrushSegmentKind::BottomLeft as usize);
-
- segment.clip_task_id = if create_clip_task {
+ for segment in &mut segment_desc.segments {
+ segment.clip_task_id = if segment.may_need_clip_mask || clip_mask_kind == BrushClipMaskKind::Global {
let segment_screen_rect = calculate_screen_bounding_rect(
&prim_context.scroll_node.world_content_transform,
&segment.local_rect,
- prim_context.device_pixel_ratio
+ prim_context.device_pixel_scale,
);
combined_outer_rect.intersection(&segment_screen_rect).map(|bounds| {
@@ -1570,7 +1568,7 @@ impl PrimitiveStore {
if prim_clips.has_clips() {
prim_clips.update(gpu_cache, resource_cache);
let (screen_inner_rect, screen_outer_rect) =
- prim_clips.get_screen_bounds(transform, prim_context.device_pixel_ratio);
+ prim_clips.get_screen_bounds(transform, prim_context.device_pixel_scale);
if let Some(outer) = screen_outer_rect {
combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
@@ -1582,7 +1580,13 @@ impl PrimitiveStore {
clip_sources: metadata.clip_sources.weak(),
coordinate_system_id: prim_coordinate_system_id,
},
+ // The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
+ // It's used to calculate a local clipping rectangle before we reach this
+ // point, so we can set it to zero here. It should be unused from this point
+ // on.
+ local_clip_rect: LayerRect::zero(),
screen_inner_rect,
+ screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect),
combined_outer_screen_rect:
combined_outer_rect.unwrap_or_else(DeviceIntRect::zero),
combined_inner_screen_rect: DeviceIntRect::zero(),
@@ -1633,8 +1637,8 @@ impl PrimitiveStore {
return true;
}
- // First try to render this primitive's mask using optimized nine-patch brush rendering.
- if self.update_nine_patch_clip_task_for_brush(
+ // First try to render this primitive's mask using optimized brush rendering.
+ if self.update_clip_task_for_brush(
prim_context,
prim_index,
render_tasks,
@@ -1677,7 +1681,9 @@ impl PrimitiveStore {
profile_counters: &mut FrameProfileCounters,
pic_index: SpecificPrimitiveIndex,
screen_rect: &DeviceIntRect,
+ clip_chain_rect_index: ClipChainRectIndex,
node_data: &[ClipScrollNodeData],
+ local_rects: &mut Vec,
) -> Option {
// Reset the visibility of this primitive.
// Do some basic checks first, that can early out
@@ -1745,6 +1751,7 @@ impl PrimitiveStore {
cpu_prim_index,
screen_rect,
node_data,
+ local_rects,
);
let metadata = &mut self.cpu_metadata[prim_index.0];
@@ -1767,7 +1774,7 @@ impl PrimitiveStore {
return None;
}
- let local_rect = metadata.local_rect.intersection(&metadata.local_clip_rect);
+ let local_rect = metadata.local_clip_rect.intersection(&metadata.local_rect);
let local_rect = match local_rect {
Some(local_rect) => local_rect,
None if perform_culling => return None,
@@ -1777,16 +1784,21 @@ impl PrimitiveStore {
let screen_bounding_rect = calculate_screen_bounding_rect(
&prim_context.scroll_node.world_content_transform,
&local_rect,
- prim_context.device_pixel_ratio
+ prim_context.device_pixel_scale,
);
- let clip_bounds = &prim_context.clip_node.combined_clip_outer_bounds;
- metadata.screen_rect = screen_bounding_rect.intersection(clip_bounds);
+ let clip_bounds = match prim_context.clip_node.clip_chain_node {
+ Some(ref node) => node.combined_outer_screen_rect,
+ None => *screen_rect,
+ };
+ metadata.screen_rect = screen_bounding_rect.intersection(&clip_bounds);
if metadata.screen_rect.is_none() && perform_culling {
return None;
}
+ metadata.clip_chain_rect_index = clip_chain_rect_index;
+
(local_rect, screen_bounding_rect)
};
@@ -1846,6 +1858,7 @@ impl PrimitiveStore {
pic_index: SpecificPrimitiveIndex,
screen_rect: &DeviceIntRect,
node_data: &[ClipScrollNodeData],
+ local_rects: &mut Vec,
) -> PrimitiveRunLocalRect {
let mut result = PrimitiveRunLocalRect {
local_rect_in_actual_parent_space: LayerRect::zero(),
@@ -1889,12 +1902,28 @@ impl PrimitiveStore {
.display_list;
let child_prim_context = PrimitiveContext::new(
- parent_prim_context.device_pixel_ratio,
+ parent_prim_context.device_pixel_scale,
display_list,
clip_node,
scroll_node,
);
+
+ let clip_chain_rect = match perform_culling {
+ true => get_local_clip_rect_for_nodes(scroll_node, clip_node),
+ false => None,
+ };
+
+ let clip_chain_rect_index = match clip_chain_rect {
+ Some(rect) if rect.is_empty() => continue,
+ Some(rect) => {
+ local_rects.push(rect);
+ ClipChainRectIndex(local_rects.len() - 1)
+ }
+ None => ClipChainRectIndex(0), // This is no clipping.
+ };
+
+
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
@@ -1913,7 +1942,9 @@ impl PrimitiveStore {
profile_counters,
pic_index,
screen_rect,
+ clip_chain_rect_index,
node_data,
+ local_rects,
) {
profile_counters.visible_primitives.inc();
@@ -1961,23 +1992,6 @@ impl InsideTest for ComplexClipRegion {
}
}
-fn create_nine_patch(
- local_rect: &LayerRect,
- local_clip_rect: &LayerRect,
- radii: &BorderRadius
-) -> Option> {
- extract_inner_rect_safe(local_clip_rect, radii).map(|inner| {
- let mut desc = BrushSegmentDescriptor::new(
- local_rect,
- &inner,
- None,
- );
- desc.can_optimize_clip_mask = true;
-
- Box::new(desc)
- })
-}
-
fn convert_clip_chain_to_clip_vector(
clip_chain: ClipChain,
extra_clip: ClipChain,
@@ -2007,3 +2021,29 @@ fn convert_clip_chain_to_clip_vector(
})
.collect()
}
+
+fn get_local_clip_rect_for_nodes(
+ scroll_node: &ClipScrollNode,
+ clip_node: &ClipScrollNode,
+) -> Option {
+ let local_rect = ClipChainNodeIter { current: clip_node.clip_chain_node.clone() }.fold(
+ None,
+ |combined_local_clip_rect: Option, node| {
+ if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id {
+ return combined_local_clip_rect;
+ }
+
+ Some(match combined_local_clip_rect {
+ Some(combined_rect) =>
+ combined_rect.intersection(&node.local_clip_rect).unwrap_or_else(LayerRect::zero),
+ None => node.local_clip_rect,
+ })
+ }
+ );
+
+ match local_rect {
+ Some(local_rect) =>
+ Some(scroll_node.coordinate_system_relative_transform.unapply(&local_rect)),
+ None => None,
+ }
+}
diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs
index b14df5b1b392..8a045e7aea2d 100644
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -314,20 +314,16 @@ impl FrameProfileCounters {
#[derive(Clone)]
pub struct TextureCacheProfileCounters {
pub pages_a8_linear: ResourceProfileCounter,
- pub pages_rgb8_linear: ResourceProfileCounter,
pub pages_rgba8_linear: ResourceProfileCounter,
pub pages_rgba8_nearest: ResourceProfileCounter,
- pub pages_rg8_linear: ResourceProfileCounter,
}
impl TextureCacheProfileCounters {
pub fn new() -> Self {
TextureCacheProfileCounters {
pages_a8_linear: ResourceProfileCounter::new("Texture A8 cached pages"),
- pages_rgb8_linear: ResourceProfileCounter::new("Texture RGB8 cached pages"),
pages_rgba8_linear: ResourceProfileCounter::new("Texture RGBA8 cached pages (L)"),
pages_rgba8_nearest: ResourceProfileCounter::new("Texture RGBA8 cached pages (N)"),
- pages_rg8_linear: ResourceProfileCounter::new("Texture RG8 cached pages"),
}
}
}
@@ -998,10 +994,8 @@ impl Profiler {
Profiler::draw_counters(
&[
&backend_profile.resources.texture_cache.pages_a8_linear,
- &backend_profile.resources.texture_cache.pages_rgb8_linear,
&backend_profile.resources.texture_cache.pages_rgba8_linear,
&backend_profile.resources.texture_cache.pages_rgba8_nearest,
- &backend_profile.resources.texture_cache.pages_rg8_linear,
&backend_profile.ipc.display_lists,
],
debug_renderer,
diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs
index b4b8ec358429..ee0fb4ef32f4 100644
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -5,9 +5,9 @@
use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DebugCommand, DeviceIntPoint};
#[cfg(feature = "debugger")]
use api::{BuiltDisplayListIter, SpecificDisplayItem};
-use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{DocumentId, DocumentLayer, DocumentMsg};
-use api::{IdNamespace, LayerPoint, PipelineId, RenderNotifier};
+use api::{IdNamespace, PipelineId, RenderNotifier};
use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
use api::channel::{PayloadSender, PayloadSenderHelperMethods};
#[cfg(feature = "debugger")]
@@ -16,13 +16,21 @@ use frame::FrameContext;
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use gpu_cache::GpuCache;
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
+#[cfg(feature = "capture")]
+use internal_types::ExternalCaptureImage;
use profiler::{BackendProfileCounters, ResourceProfileCounters};
use rayon::ThreadPool;
use record::ApiRecordingReceiver;
use resource_cache::ResourceCache;
+#[cfg(feature = "capture")]
+use resource_cache::PlainResources;
use scene::Scene;
+#[cfg(feature = "serialize")]
+use serde::{Serialize, Serializer};
#[cfg(feature = "debugger")]
use serde_json;
+#[cfg(feature = "capture")]
+use std::path::PathBuf;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::mpsc::Sender;
@@ -30,11 +38,9 @@ use std::u32;
use texture_cache::TextureCache;
use time::precise_time_ns;
-struct Document {
- scene: Scene,
- frame_ctx: FrameContext,
- // the `Option` here is only to deal with borrow checker
- frame_builder: Option,
+
+#[cfg_attr(feature = "capture", derive(Clone, Serialize, Deserialize))]
+struct DocumentView {
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
layer: DocumentLayer,
@@ -42,6 +48,24 @@ struct Document {
device_pixel_ratio: f32,
page_zoom_factor: f32,
pinch_zoom_factor: f32,
+}
+
+impl DocumentView {
+ fn accumulated_scale_factor(&self) -> DevicePixelScale {
+ DevicePixelScale::new(
+ self.device_pixel_ratio *
+ self.page_zoom_factor *
+ self.pinch_zoom_factor
+ )
+ }
+}
+
+struct Document {
+ scene: Scene,
+ view: DocumentView,
+ frame_ctx: FrameContext,
+ // the `Option` here is only to deal with borrow checker
+ frame_builder: Option,
// A set of pipelines that the caller has requested be
// made available as output textures.
output_pipelines: FastHashSet,
@@ -68,36 +92,31 @@ impl Document {
};
Document {
scene: Scene::new(),
+ view: DocumentView {
+ window_size,
+ inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
+ layer,
+ pan: DeviceIntPoint::zero(),
+ page_zoom_factor: 1.0,
+ pinch_zoom_factor: 1.0,
+ device_pixel_ratio: default_device_pixel_ratio,
+ },
frame_ctx: FrameContext::new(config),
frame_builder: Some(FrameBuilder::empty()),
- window_size,
- inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
- layer,
- pan: DeviceIntPoint::zero(),
- page_zoom_factor: 1.0,
- pinch_zoom_factor: 1.0,
- device_pixel_ratio: default_device_pixel_ratio,
render_on_scroll,
output_pipelines: FastHashSet::default(),
}
}
- fn accumulated_scale_factor(&self) -> f32 {
- self.device_pixel_ratio *
- self.page_zoom_factor *
- self.pinch_zoom_factor
- }
-
fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
- let accumulated_scale_factor = self.accumulated_scale_factor();
// this code is why we have `Option`, which is never `None`
let frame_builder = self.frame_ctx.create(
self.frame_builder.take().unwrap(),
&self.scene,
resource_cache,
- self.window_size,
- self.inner_rect,
- accumulated_scale_factor,
+ self.view.window_size,
+ self.view.inner_rect,
+ self.view.accumulated_scale_factor(),
&self.output_pipelines,
);
self.frame_builder = Some(frame_builder);
@@ -109,18 +128,15 @@ impl Document {
gpu_cache: &mut GpuCache,
resource_profile: &mut ResourceProfileCounters,
) -> RenderedDocument {
- let accumulated_scale_factor = self.accumulated_scale_factor();
- let pan = LayerPoint::new(
- self.pan.x as f32 / accumulated_scale_factor,
- self.pan.y as f32 / accumulated_scale_factor,
- );
+ let accumulated_scale_factor = self.view.accumulated_scale_factor();
+ let pan = self.view.pan.to_f32() / accumulated_scale_factor;
self.frame_ctx.build_rendered_document(
self.frame_builder.as_mut().unwrap(),
resource_cache,
gpu_cache,
&self.scene.pipelines,
accumulated_scale_factor,
- self.layer,
+ self.view.layer,
pan,
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
@@ -140,6 +156,16 @@ enum DocumentOp {
/// The unique id for WR resource identification.
static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
+#[cfg(feature = "capture")]
+#[derive(Serialize, Deserialize)]
+struct PlainRenderBackend {
+ default_device_pixel_ratio: f32,
+ enable_render_on_scroll: bool,
+ frame_config: FrameBuilderConfig,
+ documents: FastHashMap,
+ resources: PlainResources,
+}
+
/// The render backend is responsible for transforming high level display lists into
/// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
///
@@ -210,8 +236,9 @@ impl RenderBackend {
let doc = self.documents.get_mut(&document_id).expect("No document?");
match message {
+ //TODO: move view-related messages in a separate enum?
DocumentMsg::SetPageZoom(factor) => {
- doc.page_zoom_factor = factor.get();
+ doc.view.page_zoom_factor = factor.get();
DocumentOp::Nop
}
DocumentMsg::EnableFrameOutput(pipeline_id, enable) => {
@@ -223,11 +250,11 @@ impl RenderBackend {
DocumentOp::Nop
}
DocumentMsg::SetPinchZoom(factor) => {
- doc.pinch_zoom_factor = factor.get();
+ doc.view.pinch_zoom_factor = factor.get();
DocumentOp::Nop
}
DocumentMsg::SetPan(pan) => {
- doc.pan = pan;
+ doc.view.pan = pan;
DocumentOp::Nop
}
DocumentMsg::SetWindowParameters {
@@ -235,9 +262,9 @@ impl RenderBackend {
inner_rect,
device_pixel_ratio,
} => {
- doc.window_size = window_size;
- doc.inner_rect = inner_rect;
- doc.device_pixel_ratio = device_pixel_ratio;
+ doc.view.window_size = window_size;
+ doc.view.inner_rect = inner_rect;
+ doc.view.device_pixel_ratio = device_pixel_ratio;
DocumentOp::Nop
}
DocumentMsg::SetDisplayList {
@@ -550,6 +577,34 @@ impl RenderBackend {
let json = self.get_clip_scroll_tree_for_debugger();
ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json))
}
+ #[cfg(feature = "capture")]
+ DebugCommand::SaveCapture(root) => {
+ let deferred = self.save_capture(&root);
+ ResultMsg::DebugOutput(DebugOutput::SaveCapture(root, deferred))
+ },
+ #[cfg(feature = "capture")]
+ DebugCommand::LoadCapture(root) => {
+ NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
+ frame_counter += 1;
+ self.load_capture(&root, &mut profile_counters);
+ ResultMsg::DebugOutput(DebugOutput::LoadCapture)
+ },
+ DebugCommand::EnableDualSourceBlending(enable) => {
+ // Set in the config used for any future documents
+ // that are created.
+ self.frame_config
+ .dual_source_blending_is_enabled = enable;
+
+ // Set for any existing documents.
+ for (_, doc) in &mut self.documents {
+ doc.frame_ctx
+ .frame_builder_config
+ .dual_source_blending_is_enabled = enable;
+ }
+
+ // We don't want to forward this message to the renderer.
+ continue;
+ }
_ => ResultMsg::DebugCommand(option),
};
self.result_tx.send(msg).unwrap();
@@ -717,3 +772,107 @@ impl ToDebugString for SpecificDisplayItem {
}
}
}
+
+
+#[cfg(feature = "capture")]
+impl RenderBackend {
+ // Note: the mutable `self` is only needed here for resolving blob images
+ fn save_capture(&mut self, root: &PathBuf) -> Vec {
+ use ron::ser::pretty;
+ use std::fs;
+ use std::io::Write;
+
+ info!("capture: saving {}", root.to_string_lossy());
+ let (resources, deferred) = self.resource_cache.save_capture(root);
+
+ for (&id, doc) in &self.documents {
+ info!("\tdocument {:?}", id);
+ let ron = pretty::to_string(&doc.scene).unwrap();
+ let file_name = format!("scene-{}-{}.ron", (id.0).0, id.1);
+ let ron_path = root.clone().join(file_name);
+ let mut file = fs::File::create(ron_path).unwrap();
+ write!(file, "{}\n", ron).unwrap();
+ }
+
+ info!("\tbackend");
+ let serial = PlainRenderBackend {
+ default_device_pixel_ratio: self.default_device_pixel_ratio,
+ enable_render_on_scroll: self.enable_render_on_scroll,
+ frame_config: self.frame_config.clone(),
+ documents: self.documents
+ .iter()
+ .map(|(id, doc)| (*id, doc.view.clone()))
+ .collect(),
+ resources,
+ };
+
+ let ron = pretty::to_string(&serial).unwrap();
+ let ron_path = root.clone().join("backend.ron");
+ let mut file = fs::File::create(ron_path).unwrap();
+ write!(file, "{}\n", ron).unwrap();
+
+ deferred
+ }
+
+ fn load_capture(
+ &mut self,
+ root: &PathBuf,
+ profile_counters: &mut BackendProfileCounters,
+ ) {
+ use ron::de;
+ use std::fs::File;
+ use std::io::Read;
+
+ let mut string = String::new();
+ info!("capture: loading {}", root.to_string_lossy());
+
+ File::open(root.join("backend.ron"))
+ .unwrap()
+ .read_to_string(&mut string)
+ .unwrap();
+ let backend: PlainRenderBackend = de::from_str(&string)
+ .unwrap();
+
+ // Note: it would be great to have RenderBackend to be split
+ // rather explicitly on what's used before and after scene building
+ // so that, for example, we never miss anything in the code below:
+
+ self.resource_cache.load_capture(backend.resources, root);
+ self.gpu_cache = GpuCache::new();
+ self.documents.clear();
+ self.default_device_pixel_ratio = backend.default_device_pixel_ratio;
+ self.frame_config = backend.frame_config;
+ self.enable_render_on_scroll = backend.enable_render_on_scroll;
+
+ for (id, view) in backend.documents {
+ info!("\tdocument {:?}", id);
+ string.clear();
+ let file_name = format!("scene-{}-{}.ron", (id.0).0, id.1);
+ File::open(root.join(file_name))
+ .expect(&format!("Unable to open scene {:?}", id))
+ .read_to_string(&mut string)
+ .unwrap();
+ let scene: Scene = de::from_str(&string)
+ .unwrap();
+
+ let mut doc = Document {
+ scene,
+ view,
+ frame_ctx: FrameContext::new(self.frame_config.clone()),
+ frame_builder: Some(FrameBuilder::empty()),
+ output_pipelines: FastHashSet::default(),
+ render_on_scroll: None,
+ };
+
+ doc.build_scene(&mut self.resource_cache);
+ let render_doc = doc.render(
+ &mut self.resource_cache,
+ &mut self.gpu_cache,
+ &mut profile_counters.resources,
+ );
+ self.publish_document_and_notify_compositor(id, render_doc, profile_counters);
+
+ self.documents.insert(id, doc);
+ }
+ }
+}
diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs
index e2a743bc934b..18fd5d438828 100644
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -2,15 +2,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel};
-use api::{LayerPoint, LayerRect, PremultipliedColorF};
+use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{LayerRect, PremultipliedColorF};
use box_shadow::BoxShadowCacheKey;
use clip::{ClipSourcesWeakHandle};
use clip_scroll_tree::CoordinateSystemId;
-use euclid::TypedSize2D;
-use gpu_types::{ClipScrollNodeIndex};
+use gpu_types::{ClipScrollNodeIndex, PictureType};
use internal_types::RenderPassIndex;
-use picture::RasterizationSpace;
+use picture::ContentOrigin;
use prim_store::{PrimitiveIndex};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
@@ -41,12 +40,59 @@ pub type ClipChain = Option>;
#[derive(Debug)]
pub struct ClipChainNode {
pub work_item: ClipWorkItem,
+ pub local_clip_rect: LayerRect,
+ pub screen_outer_rect: DeviceIntRect,
pub screen_inner_rect: DeviceIntRect,
pub combined_outer_screen_rect: DeviceIntRect,
pub combined_inner_screen_rect: DeviceIntRect,
pub prev: ClipChain,
}
+impl ClipChainNode {
+ pub fn new(
+ work_item: ClipWorkItem,
+ local_clip_rect: LayerRect,
+ screen_outer_rect: DeviceIntRect,
+ screen_inner_rect: DeviceIntRect,
+ parent_chain: ClipChain,
+ ) -> ClipChainNode {
+ let mut node = ClipChainNode {
+ work_item,
+ local_clip_rect,
+ screen_outer_rect,
+ screen_inner_rect,
+ combined_outer_screen_rect: screen_outer_rect,
+ combined_inner_screen_rect: screen_inner_rect,
+ prev: None,
+ };
+ node.set_parent(parent_chain);
+ node
+ }
+
+ fn set_parent(&mut self, new_parent: ClipChain) {
+ self.prev = new_parent.clone();
+
+ let parent_node = match new_parent {
+ Some(ref parent_node) => parent_node,
+ None => return,
+ };
+
+ // If this clip's outer rectangle is completely enclosed by the clip
+ // chain's inner rectangle, then the only clip that matters from this point
+ // on is this clip. We can disconnect this clip from the parent clip chain.
+ if parent_node.combined_inner_screen_rect.contains_rect(&self.screen_outer_rect) {
+ self.prev = None;
+ }
+
+ self.combined_outer_screen_rect =
+ parent_node.combined_outer_screen_rect.intersection(&self.screen_outer_rect)
+ .unwrap_or_else(DeviceIntRect::zero);
+ self.combined_inner_screen_rect =
+ parent_node.combined_inner_screen_rect.intersection(&self.screen_inner_rect)
+ .unwrap_or_else(DeviceIntRect::zero);
+ }
+}
+
pub struct ClipChainNodeIter {
pub current: ClipChain,
}
@@ -154,7 +200,7 @@ impl ops::IndexMut for RenderTaskTree {
pub enum RenderTaskKey {
/// Draw the alpha mask for a shared clip.
CacheMask(ClipId),
- CacheScaling(BoxShadowCacheKey, TypedSize2D),
+ CacheScaling(BoxShadowCacheKey, DeviceIntSize),
CacheBlur(BoxShadowCacheKey, i32),
CachePicture(BoxShadowCacheKey),
}
@@ -183,9 +229,9 @@ pub struct CacheMaskTask {
pub struct PictureTask {
pub prim_index: PrimitiveIndex,
pub target_kind: RenderTargetKind,
- pub content_origin: LayerPoint,
+ pub content_origin: ContentOrigin,
pub color: PremultipliedColorF,
- pub rasterization_kind: RasterizationSpace,
+ pub pic_type: PictureType,
}
#[derive(Debug)]
@@ -250,13 +296,12 @@ impl RenderTask {
size: Option,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
- content_origin_x: f32,
- content_origin_y: f32,
+ content_origin: ContentOrigin,
color: PremultipliedColorF,
clear_mode: ClearMode,
- rasterization_kind: RasterizationSpace,
children: Vec,
box_shadow_cache_key: Option,
+ pic_type: PictureType,
) -> Self {
let location = match size {
Some(size) => RenderTaskLocation::Dynamic(None, size),
@@ -273,9 +318,9 @@ impl RenderTask {
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
target_kind,
- content_origin: LayerPoint::new(content_origin_x, content_origin_y),
+ content_origin,
color,
- rasterization_kind,
+ pic_type,
}),
clear_mode,
pass_index: None,
@@ -443,11 +488,20 @@ impl RenderTask {
let (data1, data2) = match self.kind {
RenderTaskKind::Picture(ref task) => {
(
- [
- task.content_origin.x,
- task.content_origin.y,
- task.rasterization_kind as u32 as f32,
- ],
+ // Note: has to match `PICTURE_TYPE_*` in shaders
+ // TODO(gw): Instead of using the sign of the picture
+ // type here, we should consider encoding it
+ // as a set of flags that get casted here
+ // and in the shader. This is a bit tidier
+ // and allows for future expansion of flags.
+ match task.content_origin {
+ ContentOrigin::Local(point) => [
+ point.x, point.y, task.pic_type as u32 as f32,
+ ],
+ ContentOrigin::Screen(point) => [
+ point.x as f32, point.y as f32, -(task.pic_type as u32 as f32),
+ ],
+ },
task.color.to_array()
)
}
@@ -592,7 +646,6 @@ impl RenderTask {
RenderTaskKind::Picture(ref task) => {
pt.new_level(format!("Picture of {:?}", task.prim_index));
pt.add_item(format!("kind: {:?}", task.target_kind));
- pt.add_item(format!("space: {:?}", task.rasterization_kind));
}
RenderTaskKind::CacheMask(ref task) => {
pt.new_level(format!("CacheMask with {} clips", task.clips.len()));
diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs
index 058875400e7e..537a543d79a0 100644
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -9,18 +9,17 @@
//!
//! [renderer]: struct.Renderer.html
-use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, DocumentId, Epoch, PipelineId, RenderApiSender, RenderNotifier};
-use api::{DevicePixel, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, ColorU};
-use api::{ExternalImageId, ExternalImageType, ImageFormat};
-use api::{YUV_COLOR_SPACES, YUV_FORMATS};
-use api::{YuvColorSpace, YuvFormat};
+use api::{BlobImageRenderer, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
+use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId, RenderApiSender};
+use api::{RenderNotifier, YUV_COLOR_SPACES, YUV_FORMATS, YuvColorSpace, YuvFormat, channel};
#[cfg(not(feature = "debugger"))]
use api::ApiMsg;
use api::DebugCommand;
#[cfg(not(feature = "debugger"))]
use api::channel::MsgSender;
+use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind};
+use batch::{BrushImageSourceKind, TransformBatchKind};
use debug_colors;
use debug_render::DebugRenderer;
#[cfg(feature = "debugger")]
@@ -32,15 +31,16 @@ use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttr
use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget,
VertexUsageHint, VAO, VBO, CustomVAO};
use device::ProgramCache;
-use euclid::{rect, TypedScale, Transform3D};
+use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_types::PrimitiveInstance;
-use internal_types::{BatchTextures, SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
+use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp};
use internal_types::{DebugOutput, RenderPassIndex, RenderTargetInfo, TextureUpdateList, TextureUpdateSource};
+use picture::ContentOrigin;
use profiler::{BackendProfileCounters, Profiler};
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
use query::{GpuProfiler, GpuTimer};
@@ -66,7 +66,7 @@ use texture_cache::TextureCache;
use thread_profiler::{register_thread_with_profiler, write_profile};
use tiling::{AlphaRenderTarget, ColorRenderTarget};
use tiling::{RenderPass, RenderPassKind, RenderTargetList};
-use tiling::{BatchKey, BatchKind, BrushBatchKind, BrushImageSourceKind, Frame, RenderTarget, ScalingInfo, TransformBatchKind};
+use tiling::{Frame, RenderTarget, ScalingInfo};
use time::precise_time_ns;
use util::TransformedRectKind;
@@ -91,6 +91,10 @@ const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
label: "B_Image",
color: debug_colors::SILVER,
};
+const GPU_TAG_BRUSH_LINE: GpuProfileTag = GpuProfileTag {
+ label: "Line",
+ color: debug_colors::DARKRED,
+};
const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
label: "C_Clip",
color: debug_colors::PURPLE,
@@ -111,10 +115,6 @@ const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag {
label: "data init",
color: debug_colors::LIGHTGREY,
};
-const GPU_TAG_PRIM_LINE: GpuProfileTag = GpuProfileTag {
- label: "Line",
- color: debug_colors::DARKRED,
-};
const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag {
label: "Image",
color: debug_colors::GREEN,
@@ -198,13 +198,11 @@ impl TransformBatchKind {
TransformBatchKind::RadialGradient => "RadialGradient",
TransformBatchKind::BorderCorner => "BorderCorner",
TransformBatchKind::BorderEdge => "BorderEdge",
- TransformBatchKind::Line => "Line",
}
}
fn gpu_sampler_tag(&self) -> GpuProfileTag {
match *self {
- TransformBatchKind::Line => GPU_TAG_PRIM_LINE,
TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
TransformBatchKind::Image(..) => GPU_TAG_PRIM_IMAGE,
TransformBatchKind::YuvImage(..) => GPU_TAG_PRIM_YUV_IMAGE,
@@ -229,6 +227,7 @@ impl BatchKind {
match kind {
BrushBatchKind::Image(..) => "Brush (Image)",
BrushBatchKind::Solid => "Brush (Solid)",
+ BrushBatchKind::Line => "Brush (Line)",
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
@@ -245,6 +244,7 @@ impl BatchKind {
match kind {
BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID,
+ BrushBatchKind::Line => GPU_TAG_BRUSH_LINE,
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.gpu_sampler_tag(),
@@ -288,8 +288,9 @@ enum TextShaderMode {
SubpixelWithBgColorPass0 = 4,
SubpixelWithBgColorPass1 = 5,
SubpixelWithBgColorPass2 = 6,
- Bitmap = 7,
- ColorBitmap = 8,
+ SubpixelDualSource = 7,
+ Bitmap = 8,
+ ColorBitmap = 9,
}
impl Into for TextShaderMode {
@@ -326,6 +327,7 @@ enum TextureSampler {
// the *first* pass. Items rendered in this target are
// available as inputs to tasks in any subsequent pass.
SharedCacheA8,
+ LocalClipRects
}
impl TextureSampler {
@@ -354,6 +356,7 @@ impl Into for TextureSampler {
TextureSampler::RenderTasks => TextureSlot(7),
TextureSampler::Dither => TextureSlot(8),
TextureSampler::SharedCacheA8 => TextureSlot(9),
+ TextureSampler::LocalClipRects => TextureSlot(10),
}
}
}
@@ -433,7 +436,7 @@ const DESC_CLIP: VertexDescriptor = VertexDescriptor {
kind: VertexAttributeKind::I32,
},
VertexAttribute {
- name: "aClipLayerAddress",
+ name: "aScrollNodeId",
count: 1,
kind: VertexAttributeKind::I32,
},
@@ -768,6 +771,7 @@ pub enum BlendMode {
None,
PremultipliedAlpha,
PremultipliedDestOut,
+ SubpixelDualSource,
SubpixelConstantTextColor(ColorF),
SubpixelWithBgColor,
SubpixelVariableTextColor,
@@ -1297,6 +1301,7 @@ impl BrushShader {
}
BlendMode::PremultipliedAlpha |
BlendMode::PremultipliedDestOut |
+ BlendMode::SubpixelDualSource |
BlendMode::SubpixelConstantTextColor(..) |
BlendMode::SubpixelVariableTextColor |
BlendMode::SubpixelWithBgColor => {
@@ -1491,6 +1496,7 @@ fn create_prim_shader(
("sRenderTasks", TextureSampler::RenderTasks),
("sResourceCache", TextureSampler::ResourceCache),
("sSharedCacheA8", TextureSampler::SharedCacheA8),
+ ("sLocalClipRects", TextureSampler::LocalClipRects),
],
);
}
@@ -1518,6 +1524,7 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result>,
ps_yuv_image: Vec>,
ps_border_corner: PrimitiveShader,
@@ -1604,7 +1612,6 @@ pub struct Renderer {
ps_gradient: PrimitiveShader,
ps_angle_gradient: PrimitiveShader,
ps_radial_gradient: PrimitiveShader,
- ps_line: PrimitiveShader,
ps_blend: LazilyCompiledShader,
ps_hw_composite: LazilyCompiledShader,
@@ -1629,6 +1636,7 @@ pub struct Renderer {
clip_vao: VAO,
node_data_texture: VertexDataTexture,
+ local_clip_rects_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
gpu_cache_texture: CacheTexture,
@@ -1724,6 +1732,9 @@ impl Renderer {
options.cached_programs,
);
+ let ext_dual_source_blending = !options.disable_dual_source_blending &&
+ device.supports_extension("GL_ARB_blend_func_extended");
+
let device_max_size = device.max_texture_size();
// 512 is the minimum that the texture cache can work with.
// Broken GL contexts can return a max texture size of zero (See #1260). Better to
@@ -1756,14 +1767,6 @@ impl Renderer {
options.precache_shaders)
};
- let cs_line = try!{
- LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Primitive),
- "ps_line",
- &["CACHE"],
- &mut device,
- options.precache_shaders)
- };
-
let brush_mask_corner = try!{
LazilyCompiledShader::new(ShaderKind::Brush,
"brush_mask_corner",
@@ -1787,6 +1790,13 @@ impl Renderer {
options.precache_shaders)
};
+ let brush_line = try!{
+ BrushShader::new("brush_line",
+ &mut device,
+ &[],
+ options.precache_shaders)
+ };
+
let brush_image_a8 = try!{
BrushShader::new("brush_image",
&mut device,
@@ -1848,18 +1858,18 @@ impl Renderer {
options.precache_shaders)
};
- let ps_line = try!{
- PrimitiveShader::new("ps_line",
- &mut device,
- &[],
- options.precache_shaders)
- };
-
let ps_text_run = try!{
TextShader::new("ps_text_run",
&mut device,
&[],
- options.precache_shaders)
+ options.precache_shaders)
+ };
+
+ let ps_text_run_dual_source = try!{
+ TextShader::new("ps_text_run",
+ &mut device,
+ &["DUAL_SOURCE_BLENDING"],
+ options.precache_shaders)
};
// All image configuration.
@@ -2129,6 +2139,7 @@ impl Renderer {
let texture_resolver = SourceTextureResolver::new(&mut device);
let node_data_texture = VertexDataTexture::new(&mut device);
+ let local_clip_rects_texture = VertexDataTexture::new(&mut device);
let render_task_texture = VertexDataTexture::new(&mut device);
let gpu_cache_texture = CacheTexture::new(
@@ -2150,6 +2161,8 @@ impl Renderer {
enable_scrollbars: options.enable_scrollbars,
default_font_render_mode,
debug: options.debug,
+ dual_source_blending_is_enabled: true,
+ dual_source_blending_is_supported: ext_dual_source_blending,
};
let device_pixel_ratio = options.device_pixel_ratio;
@@ -2223,7 +2236,6 @@ impl Renderer {
pending_gpu_cache_updates: Vec::new(),
pending_shader_updates: Vec::new(),
cs_text_run,
- cs_line,
cs_blur_a8,
cs_blur_rgba8,
brush_mask_corner,
@@ -2232,10 +2244,12 @@ impl Renderer {
brush_image_rgba8_alpha_mask,
brush_image_a8,
brush_solid,
+ brush_line,
cs_clip_rectangle,
cs_clip_border,
cs_clip_image,
ps_text_run,
+ ps_text_run_dual_source,
ps_image,
ps_yuv_image,
ps_border_corner,
@@ -2247,7 +2261,6 @@ impl Renderer {
ps_hw_composite,
ps_split_composite,
ps_composite,
- ps_line,
debug: debug_renderer,
debug_flags,
backend_profile_counters: BackendProfileCounters::new(),
@@ -2263,6 +2276,7 @@ impl Renderer {
blur_vao,
clip_vao,
node_data_texture,
+ local_clip_rects_texture,
render_task_texture,
pipeline_epoch_map: FastHashMap::default(),
dither_matrix_texture,
@@ -2376,6 +2390,48 @@ impl Renderer {
DebugOutput::FetchClipScrollTree(string) => {
self.debug_server.send(string);
}
+ #[cfg(feature = "capture")]
+ DebugOutput::SaveCapture(path, deferred)=> {
+ use std::fs::File;
+ use std::io::Write;
+ use api::ExternalImageData;
+
+ if deferred.is_empty() {
+ continue
+ }
+
+ info!("saving external images");
+ let handler = self.external_image_handler
+ .as_mut()
+ .expect("Unable to lock the external image handler!");
+ for def in deferred {
+ let ExternalImageData { id, channel_index, .. } = def.external;
+ let data = match handler.lock(id, channel_index).source {
+ ExternalImageSource::RawData(data) => data.to_vec(),
+ ExternalImageSource::NativeTexture(_gl_id) => {
+ //TODO: make a read FBO with this GL texture
+ //self.device.read_pixels(&def.descriptor);
+ unimplemented!()
+ }
+ ExternalImageSource::Invalid => {
+ // Create a dummy buffer...
+ let stride = def.descriptor.compute_stride();
+ let total_size = def.descriptor.height * stride;
+ vec![0xFF; total_size as usize]
+ }
+ };
+ handler.unlock(id, channel_index);
+
+ let full_path = format!("{}/{}",
+ path.to_string_lossy(), def.short_path);
+ File::create(full_path)
+ .expect(&format!("Unable to create {}", def.short_path))
+ .write_all(&data)
+ .unwrap();
+ }
+ }
+ #[cfg(feature = "capture")]
+ DebugOutput::LoadCapture => {}
},
ResultMsg::DebugCommand(command) => {
self.handle_debug_command(command);
@@ -2394,8 +2450,11 @@ impl Renderer {
#[cfg(feature = "debugger")]
fn get_screenshot_for_debugger(&mut self) -> String {
- let data = self.device.read_pixels(1024, 768);
- let screenshot = debug_server::Screenshot::new(1024, 768, data);
+ use api::ImageDescriptor;
+
+ let desc = ImageDescriptor::new(1024, 768, ImageFormat::BGRA8, true);
+ let data = self.device.read_pixels(&desc);
+ let screenshot = debug_server::Screenshot::new(desc.width, desc.height, data);
serde_json::to_string(&screenshot).unwrap()
}
@@ -2467,7 +2526,7 @@ impl Renderer {
"Horizontal Blur",
target.horizontal_blurs.len(),
);
- for (_, batch) in &target.text_run_cache_prims {
+ for (_, batch) in &target.alpha_batcher.text_run_cache_prims {
debug_target.add(
debug_server::BatchKind::Cache,
"Text Shadow",
@@ -2477,7 +2536,7 @@ impl Renderer {
debug_target.add(
debug_server::BatchKind::Cache,
"Lines",
- target.line_cache_prims.len(),
+ target.alpha_batcher.line_cache_prims.len(),
);
for batch in target
@@ -2589,6 +2648,13 @@ impl Renderer {
let json = self.get_screenshot_for_debugger();
self.debug_server.send(json);
}
+ DebugCommand::SaveCapture(_) |
+ DebugCommand::LoadCapture(_) => {
+ panic!("Capture commands are not welcome here!")
+ }
+ DebugCommand::EnableDualSourceBlending(_) => {
+ panic!("Should be handled by render backend");
+ }
}
}
@@ -3053,18 +3119,18 @@ impl Renderer {
&mut self.renderer_errors,
);
}
+ BrushBatchKind::Line => {
+ self.brush_line.bind(
+ &mut self.device,
+ key.blend_mode,
+ projection,
+ 0,
+ &mut self.renderer_errors,
+ );
+ }
}
}
BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
- TransformBatchKind::Line => {
- self.ps_line.bind(
- &mut self.device,
- transform_kind,
- projection,
- 0,
- &mut self.renderer_errors,
- );
- }
TransformBatchKind::TextRun(..) => {
unreachable!("bug: text batches are special cased");
}
@@ -3160,19 +3226,18 @@ impl Renderer {
let (readback_rect, readback_layer) = readback.get_target_rect();
let (backdrop_rect, _) = backdrop.get_target_rect();
- let content_to_device_scale = TypedScale::<_, _, DevicePixel>::new(1i32);
let backdrop_screen_origin = match backdrop.kind {
- RenderTaskKind::Picture(ref task_info) => task_info
- .content_origin
- .to_i32()
- * content_to_device_scale,
+ RenderTaskKind::Picture(ref task_info) => match task_info.content_origin {
+ ContentOrigin::Local(_) => panic!("bug: composite from a local-space rasterized picture?"),
+ ContentOrigin::Screen(p) => p,
+ },
_ => panic!("bug: composite on non-picture?"),
};
let source_screen_origin = match source.kind {
- RenderTaskKind::Picture(ref task_info) => task_info
- .content_origin
- .to_i32()
- * content_to_device_scale,
+ RenderTaskKind::Picture(ref task_info) => match task_info.content_origin {
+ ContentOrigin::Local(_) => panic!("bug: composite from a local-space rasterized picture?"),
+ ContentOrigin::Screen(p) => p,
+ },
_ => panic!("bug: composite on non-picture?"),
};
@@ -3343,14 +3408,14 @@ impl Renderer {
// considering using this for (some) other text runs, since
// it removes the overhead of submitting many small glyphs
// to multiple tiles in the normal text run case.
- if !target.text_run_cache_prims.is_empty() {
+ if !target.alpha_batcher.text_run_cache_prims.is_empty() {
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
self.cs_text_run
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
- for (texture_id, instances) in &target.text_run_cache_prims {
+ for (texture_id, instances) in &target.alpha_batcher.text_run_cache_prims {
self.draw_instanced_batch(
instances,
VertexArrayKind::Primitive,
@@ -3359,17 +3424,22 @@ impl Renderer {
);
}
}
- if !target.line_cache_prims.is_empty() {
+ if !target.alpha_batcher.line_cache_prims.is_empty() {
// TODO(gw): Technically, we don't need blend for solid
// lines. We could check that here?
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE);
- self.cs_line
- .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
+ self.brush_line.bind(
+ &mut self.device,
+ BlendMode::PremultipliedAlpha,
+ projection,
+ 0,
+ &mut self.renderer_errors,
+ );
self.draw_instanced_batch(
- &target.line_cache_prims,
+ &target.alpha_batcher.line_cache_prims,
VertexArrayKind::Primitive,
&BatchTextures::no_texture(),
stats,
@@ -3427,6 +3497,7 @@ impl Renderer {
BlendMode::SubpixelConstantTextColor(..) => debug_colors::GREEN,
BlendMode::SubpixelVariableTextColor => debug_colors::RED,
BlendMode::SubpixelWithBgColor => debug_colors::BLUE,
+ BlendMode::SubpixelDualSource => debug_colors::YELLOW,
}.into();
for item_rect in &batch.item_rects {
self.debug.add_rect(item_rect, color);
@@ -3466,6 +3537,25 @@ impl Renderer {
stats,
);
}
+ BlendMode::SubpixelDualSource => {
+ self.device.set_blend_mode_subpixel_dual_source();
+
+ self.ps_text_run_dual_source.bind(
+ &mut self.device,
+ glyph_format,
+ transform_kind,
+ projection,
+ TextShaderMode::SubpixelDualSource,
+ &mut self.renderer_errors,
+ );
+
+ self.draw_instanced_batch(
+ &batch.instances,
+ VertexArrayKind::Primitive,
+ &batch.key.textures,
+ stats,
+ );
+ }
BlendMode::SubpixelConstantTextColor(color) => {
self.device.set_blend_mode_subpixel_constant_text_color(color);
@@ -3606,7 +3696,8 @@ impl Renderer {
}
BlendMode::SubpixelConstantTextColor(..) |
BlendMode::SubpixelVariableTextColor |
- BlendMode::SubpixelWithBgColor => {
+ BlendMode::SubpixelWithBgColor |
+ BlendMode::SubpixelDualSource => {
unreachable!("bug: subpx text handled earlier");
}
}
@@ -4018,10 +4109,17 @@ impl Renderer {
}
}
- self.node_data_texture
- .update(&mut self.device, &mut frame.node_data);
- self.device
- .bind_texture(TextureSampler::ClipScrollNodes, &self.node_data_texture.texture);
+ self.node_data_texture.update(&mut self.device, &mut frame.node_data);
+ self.device.bind_texture(TextureSampler::ClipScrollNodes, &self.node_data_texture.texture);
+
+ self.local_clip_rects_texture.update(
+ &mut self.device,
+ &mut frame.clip_chain_local_clip_rects
+ );
+ self.device.bind_texture(
+ TextureSampler::LocalClipRects,
+ &self.local_clip_rects_texture.texture
+ );
self.render_task_texture
.update(&mut self.device, &mut frame.render_tasks.task_data);
@@ -4407,6 +4505,7 @@ impl Renderer {
self.device.delete_texture(dither_matrix_texture);
}
self.node_data_texture.deinit(&mut self.device);
+ self.local_clip_rects_texture.deinit(&mut self.device);
self.render_task_texture.deinit(&mut self.device);
self.device.delete_pbo(self.texture_cache_upload_pbo);
self.texture_resolver.deinit(&mut self.device);
@@ -4415,7 +4514,6 @@ impl Renderer {
self.device.delete_vao(self.blur_vao);
self.debug.deinit(&mut self.device);
self.cs_text_run.deinit(&mut self.device);
- self.cs_line.deinit(&mut self.device);
self.cs_blur_a8.deinit(&mut self.device);
self.cs_blur_rgba8.deinit(&mut self.device);
self.brush_mask_rounded_rect.deinit(&mut self.device);
@@ -4424,10 +4522,12 @@ impl Renderer {
self.brush_image_rgba8_alpha_mask.deinit(&mut self.device);
self.brush_image_a8.deinit(&mut self.device);
self.brush_solid.deinit(&mut self.device);
+ self.brush_line.deinit(&mut self.device);
self.cs_clip_rectangle.deinit(&mut self.device);
self.cs_clip_image.deinit(&mut self.device);
self.cs_clip_border.deinit(&mut self.device);
self.ps_text_run.deinit(&mut self.device);
+ self.ps_text_run_dual_source.deinit(&mut self.device);
for shader in self.ps_image {
if let Some(shader) = shader {
shader.deinit(&mut self.device);
@@ -4446,7 +4546,6 @@ impl Renderer {
self.ps_gradient.deinit(&mut self.device);
self.ps_angle_gradient.deinit(&mut self.device);
self.ps_radial_gradient.deinit(&mut self.device);
- self.ps_line.deinit(&mut self.device);
self.ps_blend.deinit(&mut self.device);
self.ps_hw_composite.deinit(&mut self.device);
self.ps_split_composite.deinit(&mut self.device);
@@ -4533,6 +4632,7 @@ pub struct RendererOptions {
pub cached_programs: Option>,
pub debug_flags: DebugFlags,
pub renderer_id: Option,
+ pub disable_dual_source_blending: bool,
}
impl Default for RendererOptions {
@@ -4564,6 +4664,7 @@ impl Default for RendererOptions {
enable_render_on_scroll: true,
renderer_id: None,
cached_programs: None,
+ disable_dual_source_blending: false,
}
}
}
diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs
index e916503c95fa..2066656e26c8 100644
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -12,6 +12,8 @@ use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
use api::{GlyphDimensions, GlyphKey, IdNamespace};
use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
use api::{TileOffset, TileSize};
+#[cfg(feature = "capture")]
+use api::{NativeFontHandle};
use app_units::Au;
use device::TextureFilter;
use frame::FrameId;
@@ -19,6 +21,8 @@ use glyph_cache::GlyphCache;
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
+#[cfg(feature = "capture")]
+use internal_types::ExternalCaptureImage;
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
use rayon::ThreadPool;
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
@@ -26,9 +30,12 @@ use std::cmp;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
+#[cfg(feature = "capture")]
+use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use texture_cache::{TextureCache, TextureCacheHandle};
+
const DEFAULT_TILE_SIZE: TileSize = 512;
pub struct GlyphFetchResult {
@@ -236,7 +243,7 @@ impl ResourceCache {
texture_cache: TextureCache,
workers: Arc,
blob_image_renderer: Option>,
- ) -> ResourceCache {
+ ) -> Self {
ResourceCache {
cached_glyphs: GlyphCache::new(),
cached_images: ResourceClassCache::new(),
@@ -525,8 +532,10 @@ impl ResourceCache {
// valid to use as-is.
let (entry, needs_update) = match self.cached_images.entry(request) {
Occupied(entry) => {
- let needs_update = entry.get().as_ref().unwrap().epoch != template.epoch;
- (entry.into_mut(), needs_update)
+ let info = entry.into_mut();
+ let needs_update = info.as_mut().unwrap().epoch != template.epoch;
+ info.as_mut().unwrap().epoch = template.epoch;
+ (info, needs_update)
}
Vacant(entry) => (
entry.insert(Ok(
@@ -549,43 +558,41 @@ impl ResourceCache {
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
- if self.pending_image_requests.insert(request) {
- if template.data.is_blob() {
- if let Some(ref mut renderer) = self.blob_image_renderer {
- let (offset, w, h) = match template.tiling {
- Some(tile_size) => {
- let tile_offset = request.tile.unwrap();
- let (w, h) = compute_tile_size(
- &template.descriptor,
- tile_size,
- tile_offset,
- );
- let offset = DevicePoint::new(
- tile_offset.x as f32 * tile_size as f32,
- tile_offset.y as f32 * tile_size as f32,
- );
+ if self.pending_image_requests.insert(request) && template.data.is_blob() {
+ if let Some(ref mut renderer) = self.blob_image_renderer {
+ let (offset, w, h) = match template.tiling {
+ Some(tile_size) => {
+ let tile_offset = request.tile.unwrap();
+ let (w, h) = compute_tile_size(
+ &template.descriptor,
+ tile_size,
+ tile_offset,
+ );
+ let offset = DevicePoint::new(
+ tile_offset.x as f32 * tile_size as f32,
+ tile_offset.y as f32 * tile_size as f32,
+ );
- (offset, w, h)
- }
- None => (
- DevicePoint::zero(),
- template.descriptor.width,
- template.descriptor.height,
- ),
- };
+ (offset, w, h)
+ }
+ None => (
+ DevicePoint::zero(),
+ template.descriptor.width,
+ template.descriptor.height,
+ ),
+ };
- renderer.request(
- &self.resources,
- request.into(),
- &BlobImageDescriptor {
- width: w,
- height: h,
- offset,
- format: template.descriptor.format,
- },
- template.dirty_rect,
- );
- }
+ renderer.request(
+ &self.resources,
+ request.into(),
+ &BlobImageDescriptor {
+ width: w,
+ height: h,
+ offset,
+ format: template.descriptor.format,
+ },
+ template.dirty_rect,
+ );
}
}
}
@@ -932,3 +939,267 @@ pub fn compute_tile_size(
(actual_width, actual_height)
}
+
+#[cfg(feature = "capture")]
+#[derive(Serialize, Deserialize)]
+enum PlainFontTemplate {
+ Raw {
+ data: String,
+ index: u32,
+ },
+ Native(NativeFontHandle),
+}
+
+#[cfg(feature = "capture")]
+#[derive(Serialize, Deserialize)]
+struct PlainImageTemplate {
+ data: String,
+ descriptor: ImageDescriptor,
+ tiling: Option,
+}
+
+#[cfg(feature = "capture")]
+#[derive(Serialize, Deserialize)]
+pub struct PlainResources {
+ font_templates: FastHashMap,
+ font_instances: FastHashMap,
+ image_templates: FastHashMap,
+}
+
+#[cfg(feature = "capture")]
+impl ResourceCache {
+ pub fn save_capture(
+ &mut self, root: &PathBuf
+ ) -> (PlainResources, Vec) {
+ use std::fs;
+ use std::io::Write;
+
+ info!("saving resource cache");
+ let res = &self.resources;
+ if !root.is_dir() {
+ fs::create_dir_all(&root).unwrap()
+ }
+ let path_fonts = root.clone().join("fonts");
+ if !path_fonts.is_dir() {
+ fs::create_dir(&path_fonts).unwrap();
+ }
+ let path_images = root.clone().join("images");
+ if !path_images.is_dir() {
+ fs::create_dir(&path_images).unwrap();
+ }
+ let path_blobs = root.clone().join("blobs");
+ if !path_blobs.is_dir() {
+ fs::create_dir(&path_blobs).unwrap();
+ }
+
+ info!("\tfont templates");
+ let mut font_paths = FastHashMap::default();
+ let mut num_fonts = 0;
+ for template in res.font_templates.values() {
+ let data: &[u8] = match *template {
+ FontTemplate::Raw(ref arc, _) => arc,
+ FontTemplate::Native(_) => continue,
+ };
+ let entry = match font_paths.entry(data.as_ptr()) {
+ Entry::Occupied(_) => continue,
+ Entry::Vacant(e) => e,
+ };
+ num_fonts += 1;
+ let file_name = format!("{}.raw", num_fonts);
+ let short_path = format!("fonts/{}", file_name);
+ let full_path = path_fonts.clone().join(&file_name);
+ fs::File::create(full_path)
+ .expect(&format!("Unable to create {}", short_path))
+ .write_all(data)
+ .unwrap();
+ entry.insert(short_path);
+ }
+
+ info!("\timage templates");
+ let mut image_paths = FastHashMap::default();
+ let mut other_paths = FastHashMap::default();
+ let mut num_images = 0;
+ let mut external_images = Vec::new();
+ for (&key, template) in res.image_templates.images.iter() {
+ let desc = &template.descriptor;
+ match template.data {
+ ImageData::Raw(ref arc) => {
+ let entry = match image_paths.entry(arc.as_ptr()) {
+ Entry::Occupied(_) => continue,
+ Entry::Vacant(e) => e,
+ };
+
+ //TODO: option to save as PNG
+ num_images += 1;
+ let file_name = format!("{}.raw", num_images);
+ let short_path = format!("images/{}", file_name);
+ let full_path = path_images.clone().join(&file_name);
+ fs::File::create(full_path)
+ .expect(&format!("Unable to create {}", short_path))
+ .write_all(&*arc)
+ .unwrap();
+ entry.insert(short_path);
+ }
+ ImageData::Blob(_) => {
+ assert_eq!(template.tiling, None);
+ let request = BlobImageRequest {
+ key,
+ tile: None, //TODO: tiled blob images
+ };
+
+ let renderer = self.blob_image_renderer.as_mut().unwrap();
+ renderer.request(
+ &self.resources,
+ request,
+ &BlobImageDescriptor {
+ width: desc.width,
+ height: desc.height,
+ offset: DevicePoint::zero(),
+ format: desc.format,
+ },
+ None,
+ );
+ let result = renderer.resolve(request)
+ .expect("Blob resolve failed");
+ assert_eq!((result.width, result.height), (desc.width, desc.height));
+
+ let file_name = format!("{}.raw", other_paths.len() + 1);
+ let short_path = format!("blobs/{}", file_name);
+ let full_path = path_blobs.clone().join(&file_name);
+ fs::File::create(full_path)
+ .expect(&format!("Unable to create {}", short_path))
+ .write_all(&result.data)
+ .unwrap();
+ other_paths.insert(key, short_path);
+ }
+ ImageData::External(ref ext) => {
+ let short_path = format!("blobs/{}.raw", other_paths.len() + 1);
+ other_paths.insert(key, short_path.clone());
+ external_images.push(ExternalCaptureImage {
+ short_path,
+ descriptor: desc.clone(),
+ external: ext.clone(),
+ });
+ }
+ }
+ }
+
+ let resources = PlainResources {
+ font_templates: res.font_templates
+ .iter()
+ .map(|(key, template)| {
+ (*key, match *template {
+ FontTemplate::Raw(ref arc, index) => {
+ PlainFontTemplate::Raw {
+ data: font_paths[&arc.as_ptr()].clone(),
+ index,
+ }
+ }
+ FontTemplate::Native(ref native) => {
+ PlainFontTemplate::Native(native.clone())
+ }
+ })
+ })
+ .collect(),
+ font_instances: res.font_instances.read().unwrap().clone(),
+ image_templates: res.image_templates.images
+ .iter()
+ .map(|(key, template)| {
+ (*key, PlainImageTemplate {
+ data: match template.data {
+ ImageData::Raw(ref arc) => image_paths[&arc.as_ptr()].clone(),
+ _ => other_paths[key].clone(),
+ },
+ descriptor: template.descriptor.clone(),
+ tiling: template.tiling,
+ })
+ })
+ .collect(),
+ };
+
+ (resources, external_images)
+ }
+
+ pub fn load_capture(&mut self, resources: PlainResources, root: &PathBuf) {
+ use std::fs::File;
+ use std::io::Read;
+
+ info!("loading resource cache");
+ self.cached_glyphs.clear();
+ self.cached_images.clear();
+
+ self.state = State::Idle;
+ self.current_frame_id = FrameId(0);
+
+ let max_texture_size = self.texture_cache.max_texture_size();
+ self.texture_cache = TextureCache::new(max_texture_size);
+
+ self.cached_glyph_dimensions.clear();
+ self.glyph_rasterizer.reset();
+ self.pending_image_requests.clear();
+
+ let res = &mut self.resources;
+ res.font_templates.clear();
+ *res.font_instances.write().unwrap() = resources.font_instances;
+ res.image_templates.images.clear();
+ let mut raw_map = FastHashMap::>>::default();
+
+ info!("\tfont templates...");
+ for (key, plain_template) in resources.font_templates {
+ let template = match plain_template {
+ PlainFontTemplate::Raw { data, index } => {
+ let arc = match raw_map.entry(data) {
+ Entry::Occupied(e) => {
+ e.get().clone()
+ }
+ Entry::Vacant(e) => {
+ let path = format!("{}/{}", root.to_string_lossy(), e.key());
+ let mut buffer = Vec::new();
+ File::open(path)
+ .expect(&format!("Unable to open {}", e.key()))
+ .read_to_end(&mut buffer)
+ .unwrap();
+ e.insert(Arc::new(buffer))
+ .clone()
+ }
+ };
+ FontTemplate::Raw(arc, index)
+ }
+ PlainFontTemplate::Native(native) => {
+ FontTemplate::Native(native)
+ }
+ };
+
+ self.glyph_rasterizer.add_font(key, template.clone());
+ res.font_templates.insert(key, template);
+ }
+
+ info!("\timage templates...");
+ for (key, template) in resources.image_templates {
+ let arc = match raw_map.entry(template.data) {
+ Entry::Occupied(e) => {
+ e.get().clone()
+ }
+ Entry::Vacant(e) => {
+ //TODO: consider merging the code path with font loading
+ let path = format!("{}/{}", root.to_string_lossy(), e.key());
+ let mut buffer = Vec::new();
+ File::open(path)
+ .expect(&format!("Unable to open {}", e.key()))
+ .read_to_end(&mut buffer)
+ .unwrap();
+ e.insert(Arc::new(buffer))
+ .clone()
+ }
+ };
+
+ res.image_templates.images.insert(key, ImageResource {
+ data: ImageData::Raw(arc),
+ descriptor: template.descriptor,
+ tiling: template.tiling,
+ epoch: Epoch(0),
+ dirty_rect: None,
+ });
+ }
+ }
+}
diff --git a/gfx/webrender/src/scene.rs b/gfx/webrender/src/scene.rs
index fc5f665ccb8b..e8aee97e5ae7 100644
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -10,13 +10,14 @@ use internal_types::FastHashMap;
/// Stores a map of the animated property bindings for the current display list. These
/// can be used to animate the transform and/or opacity of a display list without
/// re-submitting the display list itself.
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct SceneProperties {
transform_properties: FastHashMap,
float_properties: FastHashMap,
}
impl SceneProperties {
- pub fn new() -> SceneProperties {
+ pub fn new() -> Self {
SceneProperties {
transform_properties: FastHashMap::default(),
float_properties: FastHashMap::default(),
@@ -80,6 +81,7 @@ impl SceneProperties {
}
/// A representation of the layout within the display port for a given document or iframe.
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct ScenePipeline {
pub pipeline_id: PipelineId,
pub epoch: Epoch,
@@ -90,6 +92,7 @@ pub struct ScenePipeline {
}
/// A complete representation of the layout bundling visible pipelines together.
+#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))]
pub struct Scene {
pub root_pipeline_id: Option,
pub pipelines: FastHashMap,
@@ -97,7 +100,7 @@ pub struct Scene {
}
impl Scene {
- pub fn new() -> Scene {
+ pub fn new() -> Self {
Scene {
root_pipeline_id: None,
pipelines: FastHashMap::default(),
diff --git a/gfx/webrender/src/segment.rs b/gfx/webrender/src/segment.rs
new file mode 100644
index 000000000000..baa4847f33f8
--- /dev/null
+++ b/gfx/webrender/src/segment.rs
@@ -0,0 +1,808 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{BorderRadius, ClipMode, LayerPoint, LayerPointAu, LayerRect, LayerSize};
+use app_units::Au;
+use prim_store::EdgeAaSegmentMask;
+use std::cmp;
+use util::extract_inner_rect_safe;
+
+// The segment builder outputs a list of these segments.
+#[derive(Debug, PartialEq)]
+pub struct Segment {
+ pub rect: LayerRect,
+ pub has_mask: bool,
+ pub edge_flags: EdgeAaSegmentMask,
+}
+
+// The segment builder creates a list of x/y axis events
+// that are used to build a segment list. Right now, we
+// don't bother providing a list of *which* clip regions
+// are active for a given segment. Instead, if there is
+// any clip mask present in a segment, we will just end
+// up drawing each of the masks to that segment clip.
+// This is a fairly rare case, but we can detect this
+// in the future and only apply clip masks that are
+// relevant to each segment region.
+// TODO(gw): Provide clip region info with each segment.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
+enum EventKind {
+ Begin,
+ End,
+}
+
+// Events must be ordered such that when the coordinates
+// of two events are the same, the end events are processed
+// before the begin events. This ensures that we're able
+// to detect which regions are active for a given segment.
+impl Ord for EventKind {
+ fn cmp(&self, other: &EventKind) -> cmp::Ordering {
+ match (*self, *other) {
+ (EventKind::Begin, EventKind::Begin) |
+ (EventKind::End, EventKind::End) => {
+ cmp::Ordering::Equal
+ }
+ (EventKind::Begin, EventKind::End) => {
+ cmp::Ordering::Greater
+ }
+ (EventKind::End, EventKind::Begin) => {
+ cmp::Ordering::Less
+ }
+ }
+ }
+}
+
+// A x/y event where we will create a vertex in the
+// segment builder.
+#[derive(Debug, Eq, PartialEq, PartialOrd)]
+struct Event {
+ value: Au,
+ item_index: ItemIndex,
+ kind: EventKind,
+}
+
+impl Ord for Event {
+ fn cmp(&self, other: &Event) -> cmp::Ordering {
+ self.value
+ .cmp(&other.value)
+ .then(self.kind.cmp(&other.kind))
+ }
+}
+
+impl Event {
+ fn begin(value: f32, index: usize) -> Event {
+ Event {
+ value: Au::from_f32_px(value),
+ item_index: ItemIndex(index),
+ kind: EventKind::Begin,
+ }
+ }
+
+ fn end(value: f32, index: usize) -> Event {
+ Event {
+ value: Au::from_f32_px(value),
+ item_index: ItemIndex(index),
+ kind: EventKind::End,
+ }
+ }
+
+ fn is_active(&self) -> bool {
+ match self.kind {
+ EventKind::Begin => true,
+ EventKind::End => false,
+ }
+ }
+}
+
+// An item that provides some kind of clip region (either
+// a clip in/out rect, or a mask region).
+#[derive(Debug)]
+struct Item {
+ rect: LayerRect,
+ mode: ClipMode,
+ has_mask: bool,
+ active_x: bool,
+ active_y: bool,
+}
+
+impl Item {
+ fn new(
+ rect: LayerRect,
+ mode: ClipMode,
+ has_mask: bool,
+ ) -> Item {
+ Item {
+ rect,
+ mode,
+ has_mask,
+ active_x: false,
+ active_y: false,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
+struct ItemIndex(usize);
+
+// The main public interface to the segment module.
+pub struct SegmentBuilder {
+ items: Vec- ,
+ bounding_rect: Option
,
+}
+
+impl SegmentBuilder {
+ // Create a new segment builder, supplying the primitive
+ // local rect and associated local clip rect.
+ pub fn new(
+ local_rect: LayerRect,
+ local_clip_rect: LayerRect,
+ ) -> SegmentBuilder {
+ let mut builder = SegmentBuilder {
+ items: Vec::new(),
+ bounding_rect: Some(local_rect),
+ };
+
+ builder.push_rect(local_rect, None, ClipMode::Clip);
+ builder.push_rect(local_clip_rect, None, ClipMode::Clip);
+
+ builder
+ }
+
+ // Push some kind of clipping region into the segment builder.
+ // If radius is None, it's a simple rect.
+ pub fn push_rect(
+ &mut self,
+ rect: LayerRect,
+ radius: Option,
+ mode: ClipMode,
+ ) {
+ // Keep track of a minimal bounding rect for the set of
+ // segments that will be generated.
+ if mode == ClipMode::Clip {
+ self.bounding_rect = self.bounding_rect.and_then(|bounding_rect| {
+ bounding_rect.intersection(&rect)
+ });
+ }
+
+ match radius {
+ Some(radius) => {
+ // For a rounded rect, try to create a nine-patch where there
+ // is a clip item for each corner, inner and edge region.
+ match extract_inner_rect_safe(&rect, &radius) {
+ Some(inner) => {
+ let p0 = rect.origin;
+ let p1 = inner.origin;
+ let p2 = inner.bottom_right();
+ let p3 = rect.bottom_right();
+
+ let corner_segments = &[
+ LayerRect::new(
+ LayerPoint::new(p0.x, p0.y),
+ LayerSize::new(p1.x - p0.x, p1.y - p0.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p2.x, p0.y),
+ LayerSize::new(p3.x - p2.x, p1.y - p0.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p2.x, p2.y),
+ LayerSize::new(p3.x - p2.x, p3.y - p2.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p0.x, p2.y),
+ LayerSize::new(p1.x - p0.x, p3.y - p2.y),
+ ),
+ ];
+
+ for segment in corner_segments {
+ self.items.push(Item::new(
+ *segment,
+ mode,
+ true
+ ));
+ }
+
+ let other_segments = &[
+ LayerRect::new(
+ LayerPoint::new(p1.x, p0.y),
+ LayerSize::new(p2.x - p1.x, p1.y - p0.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p2.x, p1.y),
+ LayerSize::new(p3.x - p2.x, p2.y - p1.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p1.x, p2.y),
+ LayerSize::new(p2.x - p1.x, p3.y - p2.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p0.x, p1.y),
+ LayerSize::new(p1.x - p0.x, p2.y - p1.y),
+ ),
+ LayerRect::new(
+ LayerPoint::new(p1.x, p1.y),
+ LayerSize::new(p2.x - p1.x, p2.y - p1.y),
+ ),
+ ];
+
+ for segment in other_segments {
+ self.items.push(Item::new(
+ *segment,
+ mode,
+ false,
+ ));
+ }
+ }
+ None => {
+ // If we get here, we could not extract an inner rectangle
+ // for this clip region. This can occur in cases such as
+ // a rounded rect where the top-left and bottom-left radii
+ // result in overlapping rects. In that case, just create
+ // a single clip region for the entire rounded rect.
+ self.items.push(Item::new(
+ rect,
+ mode,
+ true,
+ ))
+ }
+ }
+ }
+ None => {
+ // For a simple rect, just create one clipping item.
+ self.items.push(Item::new(
+ rect,
+ mode,
+ false,
+ ))
+ }
+ }
+ }
+
+ // Consume this segment builder and produce a list of segments.
+ pub fn build(self, mut f: F) where F: FnMut(Segment) {
+ let bounding_rect = match self.bounding_rect {
+ Some(bounding_rect) => bounding_rect,
+ None => return,
+ };
+
+ // First, filter out any items that don't intersect
+ // with the visible bounding rect.
+ let mut items: Vec- = self.items
+ .into_iter()
+ .filter(|item| item.rect.intersects(&bounding_rect))
+ .collect();
+
+ // Create events for each item
+ let mut x_events = Vec::new();
+ let mut y_events = Vec::new();
+
+ for (item_index, item) in items.iter().enumerate() {
+ let p0 = item.rect.origin;
+ let p1 = item.rect.bottom_right();
+
+ x_events.push(Event::begin(p0.x, item_index));
+ x_events.push(Event::end(p1.x, item_index));
+ y_events.push(Event::begin(p0.y, item_index));
+ y_events.push(Event::end(p1.y, item_index));
+ }
+
+ // Get the minimal bounding rect in app units. We will
+ // work in fixed point in order to avoid float precision
+ // error while handling events.
+ let p0 = LayerPointAu::new(
+ Au::from_f32_px(bounding_rect.origin.x),
+ Au::from_f32_px(bounding_rect.origin.y),
+ );
+
+ let p1 = LayerPointAu::new(
+ Au::from_f32_px(bounding_rect.origin.x + bounding_rect.size.width),
+ Au::from_f32_px(bounding_rect.origin.y + bounding_rect.size.height),
+ );
+
+ // Sort the events in ascending order.
+ x_events.sort();
+ y_events.sort();
+
+ // Generate segments from the event lists, by sweeping the y-axis
+ // and then the x-axis for each event. This can generate a significant
+ // number of segments, but most importantly, it ensures that there are
+ // no t-junctions in the generated segments. It's probably possible
+ // to come up with more efficient segmentation algorithms, at least
+ // for simple / common cases.
+
+ // Each coordinate is clamped to the bounds of the minimal
+ // bounding rect. This ensures that we don't generate segments
+ // outside that bounding rect, but does allow correctly handling
+ // clips where the clip region starts outside the minimal
+ // rect but still intersects with it.
+
+ let mut prev_y = clamp(p0.y, y_events[0].value, p1.y);
+
+ for ey in &y_events {
+ let cur_y = clamp(p0.y, ey.value, p1.y);
+
+ if cur_y != prev_y {
+ let mut prev_x = clamp(p0.x, x_events[0].value, p1.x);
+
+ for ex in &x_events {
+ let cur_x = clamp(p0.x, ex.value, p1.x);
+
+ if cur_x != prev_x {
+ if let Some(segment) = emit_segment_if_needed(
+ prev_x,
+ prev_y,
+ cur_x,
+ cur_y,
+ &items,
+ &p0,
+ &p1,
+ ) {
+ f(segment);
+ }
+
+ prev_x = cur_x;
+ }
+
+ items[ex.item_index.0].active_x = ex.is_active();
+ }
+
+ prev_y = cur_y;
+ }
+
+ items[ey.item_index.0].active_y = ey.is_active();
+ }
+ }
+}
+
+fn clamp(low: Au, value: Au, high: Au) -> Au {
+ value.max(low).min(high)
+}
+
+fn emit_segment_if_needed(
+ x0: Au,
+ y0: Au,
+ x1: Au,
+ y1: Au,
+ items: &[Item],
+ bounds_p0: &LayerPointAu,
+ bounds_p1: &LayerPointAu,
+) -> Option
{
+ debug_assert!(x1 > x0);
+ debug_assert!(y1 > y0);
+
+ // TODO(gw): Don't scan the whole list of items for
+ // each segment rect. Store active list
+ // in a hash set or similar if this ever
+ // shows up in a profile.
+ let mut has_clip_mask = false;
+
+ for item in items {
+ if item.active_x && item.active_y {
+ has_clip_mask |= item.has_mask;
+
+ if item.mode == ClipMode::ClipOut && !item.has_mask {
+ return None;
+ }
+ }
+ }
+
+ let segment_rect = LayerRect::new(
+ LayerPoint::new(
+ x0.to_f32_px(),
+ y0.to_f32_px(),
+ ),
+ LayerSize::new(
+ (x1 - x0).to_f32_px(),
+ (y1 - y0).to_f32_px(),
+ ),
+ );
+
+ // Determine which edges touch the bounding rect. This allows
+ // the shaders to apply AA correctly along those edges. It also
+ // allows the batching code to determine which are inner segments
+ // without edges, and push those through the opaque pass.
+ let mut edge_flags = EdgeAaSegmentMask::empty();
+ if x0 == bounds_p0.x {
+ edge_flags |= EdgeAaSegmentMask::LEFT;
+ }
+ if x1 == bounds_p1.x {
+ edge_flags |= EdgeAaSegmentMask::RIGHT;
+ }
+ if y0 == bounds_p0.y {
+ edge_flags |= EdgeAaSegmentMask::TOP;
+ }
+ if y1 == bounds_p1.y {
+ edge_flags |= EdgeAaSegmentMask::BOTTOM;
+ }
+
+ Some(Segment {
+ rect: segment_rect,
+ has_mask: has_clip_mask,
+ edge_flags,
+ })
+}
+
+#[cfg(test)]
+mod test {
+ use api::{BorderRadius, ClipMode, LayerPoint, LayerRect, LayerSize};
+ use prim_store::EdgeAaSegmentMask;
+ use super::{Segment, SegmentBuilder};
+ use std::cmp;
+
+ fn rect(x0: f32, y0: f32, x1: f32, y1: f32) -> LayerRect {
+ LayerRect::new(
+ LayerPoint::new(x0, y0),
+ LayerSize::new(x1-x0, y1-y0),
+ )
+ }
+
+ fn seg(
+ x0: f32,
+ y0: f32,
+ x1: f32,
+ y1: f32,
+ has_mask: bool,
+ edge_flags: Option,
+ ) -> Segment {
+ Segment {
+ rect: LayerRect::new(
+ LayerPoint::new(x0, y0),
+ LayerSize::new(x1-x0, y1-y0),
+ ),
+ has_mask,
+ edge_flags: edge_flags.unwrap_or(EdgeAaSegmentMask::empty()),
+ }
+ }
+
+ fn segment_sorter(s0: &Segment, s1: &Segment) -> cmp::Ordering {
+ let r0 = &s0.rect;
+ let r1 = &s1.rect;
+
+ (
+ (r0.origin.x, r0.origin.y, r0.size.width, r0.size.height)
+ ).partial_cmp(&
+ (r1.origin.x, r1.origin.y, r1.size.width, r1.size.height)
+ ).unwrap()
+ }
+
+ fn seg_test(
+ local_rect: LayerRect,
+ local_clip_rect: LayerRect,
+ clips: &[(LayerRect, Option, ClipMode)],
+ expected_segments: &mut [Segment]
+ ) {
+ let mut sb = SegmentBuilder::new(
+ local_rect,
+ local_clip_rect,
+ );
+ let mut segments = Vec::new();
+ for &(rect, radius, mode) in clips {
+ sb.push_rect(rect, radius, mode);
+ }
+ sb.build(|rect| {
+ segments.push(rect);
+ });
+ segments.sort_by(segment_sorter);
+ expected_segments.sort_by(segment_sorter);
+ assert_eq!(
+ segments.len(),
+ expected_segments.len(),
+ "segments\n{:?}\nexpected\n{:?}\n",
+ segments,
+ expected_segments
+ );
+ for (segment, expected) in segments.iter().zip(expected_segments.iter()) {
+ assert_eq!(segment, expected);
+ }
+ }
+
+ #[test]
+ fn segment_empty() {
+ seg_test(
+ rect(0.0, 0.0, 0.0, 0.0),
+ rect(0.0, 0.0, 0.0, 0.0),
+ &[],
+ &mut [],
+ );
+ }
+
+ #[test]
+ fn segment_single() {
+ seg_test(
+ rect(10.0, 20.0, 30.0, 40.0),
+ rect(10.0, 20.0, 30.0, 40.0),
+ &[],
+ &mut [
+ seg(10.0, 20.0, 30.0, 40.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_single_clip() {
+ seg_test(
+ rect(10.0, 20.0, 30.0, 40.0),
+ rect(10.0, 20.0, 25.0, 35.0),
+ &[],
+ &mut [
+ seg(10.0, 20.0, 25.0, 35.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_inner_clip() {
+ seg_test(
+ rect(10.0, 20.0, 30.0, 40.0),
+ rect(15.0, 25.0, 25.0, 35.0),
+ &[],
+ &mut [
+ seg(15.0, 25.0, 25.0, 35.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_outer_clip() {
+ seg_test(
+ rect(15.0, 25.0, 25.0, 35.0),
+ rect(10.0, 20.0, 30.0, 40.0),
+ &[],
+ &mut [
+ seg(15.0, 25.0, 25.0, 35.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_clip_int() {
+ seg_test(
+ rect(10.0, 20.0, 30.0, 40.0),
+ rect(20.0, 10.0, 40.0, 30.0),
+ &[],
+ &mut [
+ seg(20.0, 20.0, 30.0, 30.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_clip_disjoint() {
+ seg_test(
+ rect(10.0, 20.0, 30.0, 40.0),
+ rect(30.0, 20.0, 50.0, 40.0),
+ &[],
+ &mut [],
+ );
+ }
+
+ #[test]
+ fn segment_clips() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(-1000.0, -1000.0, 1000.0, 1000.0),
+ &[
+ (rect(20.0, 20.0, 40.0, 40.0), None, ClipMode::Clip),
+ (rect(40.0, 20.0, 60.0, 40.0), None, ClipMode::Clip),
+ ],
+ &mut [
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_rounded_clip() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(-1000.0, -1000.0, 1000.0, 1000.0),
+ &[
+ (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
+ ],
+ &mut [
+ // corners
+ seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
+ seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
+ seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)),
+ seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
+
+ // inner
+ seg(30.0, 30.0, 50.0, 50.0, false, None),
+
+ // edges
+ seg(30.0, 20.0, 50.0, 30.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(30.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(20.0, 30.0, 30.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT)),
+ seg(50.0, 30.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_clip_out() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(-1000.0, -1000.0, 2000.0, 2000.0),
+ &[
+ (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::ClipOut),
+ ],
+ &mut [
+ seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)),
+ seg(20.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),
+
+ seg(0.0, 20.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT)),
+ seg(60.0, 20.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+
+ seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
+ seg(20.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_rounded_clip_out() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(-1000.0, -1000.0, 2000.0, 2000.0),
+ &[
+ (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::ClipOut),
+ ],
+ &mut [
+ // top row
+ seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)),
+ seg(20.0, 0.0, 30.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(30.0, 0.0, 50.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(50.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),
+
+ // left
+ seg(0.0, 20.0, 20.0, 30.0, false, Some(EdgeAaSegmentMask::LEFT)),
+ seg(0.0, 30.0, 20.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT)),
+ seg(0.0, 50.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT)),
+
+ // right
+ seg(60.0, 20.0, 100.0, 30.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+ seg(60.0, 30.0, 100.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+ seg(60.0, 50.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+
+ // bottom row
+ seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
+ seg(20.0, 60.0, 30.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(30.0, 60.0, 50.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(50.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
+
+ // inner corners
+ seg(20.0, 20.0, 30.0, 30.0, true, None),
+ seg(20.0, 50.0, 30.0, 60.0, true, None),
+ seg(50.0, 20.0, 60.0, 30.0, true, None),
+ seg(50.0, 50.0, 60.0, 60.0, true, None),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_clip_in_clip_out() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(-1000.0, -1000.0, 2000.0, 2000.0),
+ &[
+ (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::Clip),
+ (rect(50.0, 50.0, 80.0, 80.0), None, ClipMode::ClipOut),
+ ],
+ &mut [
+ seg(20.0, 20.0, 50.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)),
+ seg(50.0, 20.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)),
+ seg(20.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_rounded_clip_overlap() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(0.0, 0.0, 100.0, 100.0),
+ &[
+ (rect(0.0, 0.0, 10.0, 10.0), None, ClipMode::ClipOut),
+ (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
+ ],
+ &mut [
+ // corners
+ seg(0.0, 90.0, 10.0, 100.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)),
+ seg(90.0, 0.0, 100.0, 10.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)),
+ seg(90.0, 90.0, 100.0, 100.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
+
+ // inner
+ seg(10.0, 10.0, 90.0, 90.0, false, None),
+
+ // edges
+ seg(10.0, 0.0, 90.0, 10.0, false, Some(EdgeAaSegmentMask::TOP)),
+ seg(10.0, 90.0, 90.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)),
+ seg(0.0, 10.0, 10.0, 90.0, false, Some(EdgeAaSegmentMask::LEFT)),
+ seg(90.0, 10.0, 100.0, 90.0, false, Some(EdgeAaSegmentMask::RIGHT)),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_rounded_clip_overlap_reverse() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(0.0, 0.0, 100.0, 100.0),
+ &[
+ (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
+ (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip),
+ ],
+ &mut [
+ seg(10.0, 10.0, 90.0, 90.0, false,
+ Some(EdgeAaSegmentMask::LEFT |
+ EdgeAaSegmentMask::TOP |
+ EdgeAaSegmentMask::RIGHT |
+ EdgeAaSegmentMask::BOTTOM
+ )
+ ),
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_clip_in_clip_out_overlap() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(0.0, 0.0, 100.0, 100.0),
+ &[
+ (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip),
+ (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::ClipOut),
+ ],
+ &mut [
+ ],
+ );
+ }
+
+ #[test]
+ fn segment_event_order() {
+ seg_test(
+ rect(0.0, 0.0, 100.0, 100.0),
+ rect(0.0, 0.0, 100.0, 100.0),
+ &[
+ (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut),
+ ],
+ &mut [
+ seg(0.0, 90.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)),
+ ],
+ );
+ }
+}
diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs
index 56e98e2f3fc7..638819c9fd37 100644
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -166,13 +166,9 @@ impl TextureCacheHandle {
pub struct TextureCache {
// A lazily allocated, fixed size, texture array for
// each format the texture cache supports.
- // TODO(gw): Do we actually need RG8 and RGB8 or
- // are they only used by external textures?
array_rgba8_nearest: TextureArray,
array_a8_linear: TextureArray,
array_rgba8_linear: TextureArray,
- array_rg8_linear: TextureArray,
- array_rgb8_linear: TextureArray,
// Maximum texture size supported by hardware.
max_texture_size: u32,
@@ -219,16 +215,6 @@ impl TextureCache {
TextureFilter::Linear,
TEXTURE_ARRAY_LAYERS_LINEAR,
),
- array_rg8_linear: TextureArray::new(
- ImageFormat::RG8,
- TextureFilter::Linear,
- TEXTURE_ARRAY_LAYERS_LINEAR,
- ),
- array_rgb8_linear: TextureArray::new(
- ImageFormat::RGB8,
- TextureFilter::Linear,
- TEXTURE_ARRAY_LAYERS_LINEAR,
- ),
array_rgba8_nearest: TextureArray::new(
ImageFormat::BGRA8,
TextureFilter::Nearest,
@@ -252,10 +238,6 @@ impl TextureCache {
self.array_a8_linear
.update_profile(&mut texture_cache_profile.pages_a8_linear);
- self.array_rg8_linear
- .update_profile(&mut texture_cache_profile.pages_rg8_linear);
- self.array_rgb8_linear
- .update_profile(&mut texture_cache_profile.pages_rgb8_linear);
self.array_rgba8_linear
.update_profile(&mut texture_cache_profile.pages_rgba8_linear);
self.array_rgba8_nearest
@@ -386,13 +368,10 @@ impl TextureCache {
(ImageFormat::A8, TextureFilter::Linear) => &mut self.array_a8_linear,
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
- (ImageFormat::RGB8, TextureFilter::Linear) => &mut self.array_rgb8_linear,
- (ImageFormat::RG8, TextureFilter::Linear) => &mut self.array_rg8_linear,
(ImageFormat::Invalid, _) |
(ImageFormat::RGBAF32, _) |
- (ImageFormat::A8, TextureFilter::Nearest) |
- (ImageFormat::RG8, TextureFilter::Nearest) |
- (ImageFormat::RGB8, TextureFilter::Nearest) => unreachable!(),
+ (ImageFormat::RG8, _) |
+ (ImageFormat::A8, TextureFilter::Nearest) => unreachable!(),
};
&mut texture_array.regions[region_index as usize]
@@ -561,13 +540,10 @@ impl TextureCache {
(ImageFormat::A8, TextureFilter::Linear) => &mut self.array_a8_linear,
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
- (ImageFormat::RGB8, TextureFilter::Linear) => &mut self.array_rgb8_linear,
- (ImageFormat::RG8, TextureFilter::Linear) => &mut self.array_rg8_linear,
(ImageFormat::Invalid, _) |
(ImageFormat::RGBAF32, _) |
(ImageFormat::A8, TextureFilter::Nearest) |
- (ImageFormat::RG8, TextureFilter::Nearest) |
- (ImageFormat::RGB8, TextureFilter::Nearest) => unreachable!(),
+ (ImageFormat::RG8, _) => unreachable!(),
};
// Lazy initialize this texture array if required.
diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs
index 20ee1e0cd383..9e0786d3a5ee 100644
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,129 +2,32 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{ClipId, ColorF, DeviceIntPoint, ImageKey};
-use api::{DeviceIntRect, DeviceIntSize, device_length, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
-use api::{DocumentLayer, ExternalImageType, FilterOp};
-use api::{ImageFormat, ImageRendering};
+use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use api::{DocumentLayer, FilterOp, ImageFormat};
use api::{LayerRect, MixBlendMode, PipelineId};
-use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat};
-use api::{LayerToWorldTransform, WorldPixel};
-use border::{BorderCornerInstance, BorderCornerSide};
-use clip::{ClipSource, ClipStore};
-use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId};
+use batch::{AlphaBatcher, ClipBatcher};
+use clip::{ClipStore};
+use clip_scroll_tree::{ClipScrollTree};
use device::Texture;
-use euclid::{TypedTransform3D, vec3};
-use glyph_rasterizer::GlyphFormat;
-use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
-use gpu_types::{BlurDirection, BlurInstance, BrushInstance, BrushImageKind, ClipMaskInstance};
-use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
-use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
-use internal_types::{FastHashMap, SourceTexture};
-use internal_types::{BatchTextures, RenderPassIndex};
-use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace};
-use plane_split::{BspSplitter, Polygon, Splitter};
-use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
-use prim_store::{BrushPrimitive, BrushMaskKind, BrushKind, BrushSegmentKind, DeferredResolve, PrimitiveRun};
+use gpu_cache::{GpuCache, GpuCacheUpdateList};
+use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipChainRectIndex};
+use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex};
+use gpu_types::{PrimitiveInstance};
+use internal_types::{FastHashMap, RenderPassIndex};
+use picture::{PictureKind};
+use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
+use prim_store::{BrushMaskKind, BrushKind, DeferredResolve};
use profiler::FrameProfileCounters;
-use render_task::{ClipWorkItem};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
-use renderer::{BlendMode, ImageBufferKind};
-use renderer::BLOCKS_PER_UV_RECT;
-use resource_cache::{GlyphFetchResult, ResourceCache};
+use resource_cache::{ResourceCache};
use std::{cmp, usize, f32, i32};
use std::collections::hash_map::Entry;
use texture_allocator::GuillotineAllocator;
-use util::{MatrixHelpers, TransformedRectKind};
-// Special sentinel value recognized by the shader. It is considered to be
-// a dummy task that doesn't mask out anything.
-const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
const MIN_TARGET_SIZE: u32 = 2048;
-// Helper to add an entire primitive run to a batch list.
-// TODO(gw): Restructure this so the param list isn't quite
-// so daunting!
-impl PrimitiveRun {
- fn add_to_batch(
- &self,
- clip_id: ClipScrollNodeIndex,
- scroll_id: ClipScrollNodeIndex,
- batch_list: &mut BatchList,
- ctx: &RenderTargetContext,
- gpu_cache: &mut GpuCache,
- render_tasks: &RenderTaskTree,
- task_id: RenderTaskId,
- task_address: RenderTaskAddress,
- deferred_resolves: &mut Vec,
- glyph_fetch_buffer: &mut Vec,
- splitter: &mut BspSplitter,
- ) {
- for i in 0 .. self.count {
- let prim_index = PrimitiveIndex(self.base_prim_index.0 + i);
-
- let md = &ctx.prim_store.cpu_metadata[prim_index.0];
-
- // Now that we walk the primitive runs in order to add
- // items to batches, we need to check if they are
- // visible here.
- if md.screen_rect.is_some() {
- add_to_batch(
- clip_id,
- scroll_id,
- prim_index,
- batch_list,
- ctx,
- gpu_cache,
- render_tasks,
- task_id,
- task_address,
- deferred_resolves,
- glyph_fetch_buffer,
- splitter,
- );
- }
- }
- }
-}
-
-trait AlphaBatchHelpers {
- fn get_blend_mode(
- &self,
- metadata: &PrimitiveMetadata,
- transform_kind: TransformedRectKind,
- ) -> BlendMode;
-}
-
-impl AlphaBatchHelpers for PrimitiveStore {
- fn get_blend_mode(
- &self,
- metadata: &PrimitiveMetadata,
- transform_kind: TransformedRectKind,
- ) -> BlendMode {
- let needs_blending = !metadata.opacity.is_opaque || metadata.clip_task_id.is_some() ||
- transform_kind == TransformedRectKind::Complex;
-
- match metadata.prim_kind {
- // Can only resolve the TextRun's blend mode once glyphs are fetched.
- PrimitiveKind::TextRun => BlendMode::PremultipliedAlpha,
- PrimitiveKind::Border |
- PrimitiveKind::Image |
- PrimitiveKind::YuvImage |
- PrimitiveKind::AlignedGradient |
- PrimitiveKind::AngleGradient |
- PrimitiveKind::RadialGradient |
- PrimitiveKind::Line |
- PrimitiveKind::Brush |
- PrimitiveKind::Picture => if needs_blending {
- BlendMode::PremultipliedAlpha
- } else {
- BlendMode::None
- },
- }
- }
-}
-
#[derive(Debug)]
pub struct ScrollbarPrimitive {
pub clip_id: ClipId,
@@ -141,1111 +44,13 @@ struct DynamicTaskInfo {
rect: DeviceIntRect,
}
-pub struct AlphaBatchList {
- pub batches: Vec,
-}
-
-impl AlphaBatchList {
- fn new() -> Self {
- AlphaBatchList {
- batches: Vec::new(),
- }
- }
-
- fn get_suitable_batch(
- &mut self,
- key: BatchKey,
- item_bounding_rect: &DeviceIntRect,
- ) -> &mut Vec {
- let mut selected_batch_index = None;
-
- match (key.kind, key.blend_mode) {
- (BatchKind::Composite { .. }, _) => {
- // Composites always get added to their own batch.
- // This is because the result of a composite can affect
- // the input to the next composite. Perhaps we can
- // optimize this in the future.
- }
- (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelWithBgColor) |
- (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelVariableTextColor) => {
- 'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
- // Subpixel text is drawn in two passes. Because of this, we need
- // to check for overlaps with every batch (which is a bit different
- // than the normal batching below).
- for item_rect in &batch.item_rects {
- if item_rect.intersects(item_bounding_rect) {
- break 'outer_text;
- }
- }
-
- if batch.key.is_compatible_with(&key) {
- selected_batch_index = Some(batch_index);
- break;
- }
- }
- }
- _ => {
- 'outer_default: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
- // For normal batches, we only need to check for overlaps for batches
- // other than the first batch we consider. If the first batch
- // is compatible, then we know there isn't any potential overlap
- // issues to worry about.
- if batch.key.is_compatible_with(&key) {
- selected_batch_index = Some(batch_index);
- break;
- }
-
- // check for intersections
- for item_rect in &batch.item_rects {
- if item_rect.intersects(item_bounding_rect) {
- break 'outer_default;
- }
- }
- }
- }
- }
-
- if selected_batch_index.is_none() {
- let new_batch = AlphaPrimitiveBatch::new(key);
- selected_batch_index = Some(self.batches.len());
- self.batches.push(new_batch);
- }
-
- let batch = &mut self.batches[selected_batch_index.unwrap()];
- batch.item_rects.push(*item_bounding_rect);
-
- &mut batch.instances
- }
-}
-
-pub struct OpaqueBatchList {
- pub pixel_area_threshold_for_new_batch: i32,
- pub batches: Vec,
-}
-
-impl OpaqueBatchList {
- fn new(pixel_area_threshold_for_new_batch: i32) -> Self {
- OpaqueBatchList {
- batches: Vec::new(),
- pixel_area_threshold_for_new_batch,
- }
- }
-
- fn get_suitable_batch(
- &mut self,
- key: BatchKey,
- item_bounding_rect: &DeviceIntRect
- ) -> &mut Vec {
- let mut selected_batch_index = None;
- let item_area = item_bounding_rect.size.area();
-
- // If the area of this primitive is larger than the given threshold,
- // then it is large enough to warrant breaking a batch for. In this
- // case we just see if it can be added to the existing batch or
- // create a new one.
- if item_area > self.pixel_area_threshold_for_new_batch {
- if let Some(ref batch) = self.batches.last() {
- if batch.key.is_compatible_with(&key) {
- selected_batch_index = Some(self.batches.len() - 1);
- }
- }
- } else {
- // Otherwise, look back through a reasonable number of batches.
- for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
- if batch.key.is_compatible_with(&key) {
- selected_batch_index = Some(batch_index);
- break;
- }
- }
- }
-
- if selected_batch_index.is_none() {
- let new_batch = OpaquePrimitiveBatch::new(key);
- selected_batch_index = Some(self.batches.len());
- self.batches.push(new_batch);
- }
-
- let batch = &mut self.batches[selected_batch_index.unwrap()];
-
- &mut batch.instances
- }
-
- fn finalize(&mut self) {
- // Reverse the instance arrays in the opaque batches
- // to get maximum z-buffer efficiency by drawing
- // front-to-back.
- // TODO(gw): Maybe we can change the batch code to
- // build these in reverse and avoid having
- // to reverse the instance array here.
- for batch in &mut self.batches {
- batch.instances.reverse();
- }
- }
-}
-
-pub struct BatchList {
- pub alpha_batch_list: AlphaBatchList,
- pub opaque_batch_list: OpaqueBatchList,
-}
-
-impl BatchList {
- fn new(screen_size: DeviceIntSize) -> Self {
- // The threshold for creating a new batch is
- // one quarter the screen size.
- let batch_area_threshold = screen_size.width * screen_size.height / 4;
-
- BatchList {
- alpha_batch_list: AlphaBatchList::new(),
- opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
- }
- }
-
- fn get_suitable_batch(
- &mut self,
- key: BatchKey,
- item_bounding_rect: &DeviceIntRect,
- ) -> &mut Vec {
- match key.blend_mode {
- BlendMode::None => {
- self.opaque_batch_list
- .get_suitable_batch(key, item_bounding_rect)
- }
- BlendMode::PremultipliedAlpha |
- BlendMode::PremultipliedDestOut |
- BlendMode::SubpixelConstantTextColor(..) |
- BlendMode::SubpixelVariableTextColor |
- BlendMode::SubpixelWithBgColor => {
- self.alpha_batch_list
- .get_suitable_batch(key, item_bounding_rect)
- }
- }
- }
-
- fn finalize(&mut self) {
- self.opaque_batch_list.finalize()
- }
-}
-
-/// Encapsulates the logic of building batches for items that are blended.
-pub struct AlphaBatcher {
- pub batch_list: BatchList,
- tasks: Vec,
- glyph_fetch_buffer: Vec,
-}
-
-// A free function that adds a primitive to a batch.
-// It can recursively call itself in some situations, for
-// example if it encounters a picture where the items
-// in that picture are being drawn into the same target.
-fn add_to_batch(
- clip_id: ClipScrollNodeIndex,
- scroll_id: ClipScrollNodeIndex,
- prim_index: PrimitiveIndex,
- batch_list: &mut BatchList,
- ctx: &RenderTargetContext,
- gpu_cache: &mut GpuCache,
- render_tasks: &RenderTaskTree,
- task_id: RenderTaskId,
- task_address: RenderTaskAddress,
- deferred_resolves: &mut Vec,
- glyph_fetch_buffer: &mut Vec,
- splitter: &mut BspSplitter,
-) {
- let z = prim_index.0 as i32;
- let prim_metadata = ctx.prim_store.get_metadata(prim_index);
- let scroll_node = &ctx.node_data[scroll_id.0 as usize];
- // TODO(gw): Calculating this for every primitive is a bit
- // wasteful. We should probably cache this in
- // the scroll node...
- let transform_kind = scroll_node.transform.transform_kind();
- let item_bounding_rect = prim_metadata.screen_rect.as_ref().unwrap();
- let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
- let no_textures = BatchTextures::no_texture();
- let clip_task_address = prim_metadata
- .clip_task_id
- .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
- let base_instance = SimplePrimitiveInstance::new(
- prim_cache_address,
- task_address,
- clip_task_address,
- clip_id,
- scroll_id,
- z,
- );
-
- let blend_mode = ctx.prim_store.get_blend_mode(prim_metadata, transform_kind);
-
- match prim_metadata.prim_kind {
- PrimitiveKind::Brush => {
- let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
- let base_instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_id,
- scroll_id,
- clip_task_address,
- z,
- segment_kind: 0,
- user_data0: 0,
- user_data1: 0,
- };
-
- match brush.segment_desc {
- Some(ref segment_desc) => {
- let opaque_batch = batch_list.opaque_batch_list.get_suitable_batch(
- brush.get_batch_key(
- BlendMode::None
- ),
- item_bounding_rect
- );
- let alpha_batch = batch_list.alpha_batch_list.get_suitable_batch(
- brush.get_batch_key(
- BlendMode::PremultipliedAlpha
- ),
- item_bounding_rect
- );
-
- for (i, segment) in segment_desc.segments.iter().enumerate() {
- if ((1 << i) & segment_desc.enabled_segments) != 0 {
- let is_inner = i == BrushSegmentKind::Center as usize;
- let needs_blending = !prim_metadata.opacity.is_opaque ||
- segment.clip_task_id.is_some() ||
- (!is_inner && transform_kind == TransformedRectKind::Complex);
-
- let clip_task_address = segment
- .clip_task_id
- .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
-
- let instance = PrimitiveInstance::from(BrushInstance {
- segment_kind: 1 + i as i32,
- clip_task_address,
- ..base_instance
- });
-
- if needs_blending {
- alpha_batch.push(instance);
- } else {
- opaque_batch.push(instance);
- }
- }
- }
- }
- None => {
- let batch = batch_list.get_suitable_batch(brush.get_batch_key(blend_mode), item_bounding_rect);
- batch.push(PrimitiveInstance::from(base_instance));
- }
- }
- }
- PrimitiveKind::Border => {
- let border_cpu =
- &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0];
- // TODO(gw): Select correct blend mode for edges and corners!!
- let corner_kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::BorderCorner,
- );
- let corner_key = BatchKey::new(corner_kind, blend_mode, no_textures);
- let edge_kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::BorderEdge,
- );
- let edge_key = BatchKey::new(edge_kind, blend_mode, no_textures);
-
- // Work around borrow ck on borrowing batch_list twice.
- {
- let batch =
- batch_list.get_suitable_batch(corner_key, item_bounding_rect);
- for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
- {
- let sub_index = i as i32;
- match *instance_kind {
- BorderCornerInstance::None => {}
- BorderCornerInstance::Single => {
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::Both as i32,
- 0,
- ));
- }
- BorderCornerInstance::Double => {
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::First as i32,
- 0,
- ));
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::Second as i32,
- 0,
- ));
- }
- }
- }
- }
-
- let batch = batch_list.get_suitable_batch(edge_key, item_bounding_rect);
- for border_segment in 0 .. 4 {
- batch.push(base_instance.build(border_segment, 0, 0));
- }
- }
- PrimitiveKind::Line => {
- let kind =
- BatchKind::Transformable(transform_kind, TransformBatchKind::Line);
- let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- batch.push(base_instance.build(0, 0, 0));
- }
- PrimitiveKind::Image => {
- let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
-
- let (color_texture_id, uv_address) = resolve_image(
- image_cpu.image_key,
- image_cpu.image_rendering,
- image_cpu.tile_offset,
- ctx.resource_cache,
- gpu_cache,
- deferred_resolves,
- );
-
- if color_texture_id == SourceTexture::Invalid {
- warn!("Warnings: skip a PrimitiveKind::Image at {:?}.\n", item_bounding_rect);
- return;
- }
-
- let batch_kind = match color_texture_id {
- SourceTexture::External(ext_image) => {
- match ext_image.image_type {
- ExternalImageType::Texture2DHandle => {
- TransformBatchKind::Image(ImageBufferKind::Texture2D)
- }
- ExternalImageType::Texture2DArrayHandle => {
- TransformBatchKind::Image(ImageBufferKind::Texture2DArray)
- }
- ExternalImageType::TextureRectHandle => {
- TransformBatchKind::Image(ImageBufferKind::TextureRect)
- }
- ExternalImageType::TextureExternalHandle => {
- TransformBatchKind::Image(ImageBufferKind::TextureExternal)
- }
- ExternalImageType::ExternalBuffer => {
- // The ExternalImageType::ExternalBuffer should be handled by resource_cache.
- // It should go through the non-external case.
- panic!(
- "Non-texture handle type should be handled in other way"
- );
- }
- }
- }
- _ => TransformBatchKind::Image(ImageBufferKind::Texture2DArray),
- };
-
- let textures = BatchTextures {
- colors: [
- color_texture_id,
- SourceTexture::Invalid,
- SourceTexture::Invalid,
- ],
- };
-
- let key = BatchKey::new(
- BatchKind::Transformable(transform_kind, batch_kind),
- blend_mode,
- textures,
- );
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- batch.push(base_instance.build(uv_address.as_int(gpu_cache), 0, 0));
- }
- PrimitiveKind::TextRun => {
- let text_cpu =
- &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
-
- let font = text_cpu.get_font(
- ctx.device_pixel_ratio,
- &scroll_node.transform,
- RasterizationSpace::Screen,
- );
-
- ctx.resource_cache.fetch_glyphs(
- font,
- &text_cpu.glyph_keys,
- glyph_fetch_buffer,
- gpu_cache,
- |texture_id, mut glyph_format, glyphs| {
- debug_assert_ne!(texture_id, SourceTexture::Invalid);
-
- let textures = BatchTextures {
- colors: [
- texture_id,
- SourceTexture::Invalid,
- SourceTexture::Invalid,
- ],
- };
-
- // Ignore color and only sample alpha when shadowing.
- if text_cpu.is_shadow() {
- glyph_format = glyph_format.ignore_color();
- }
-
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::TextRun(glyph_format),
- );
-
- let blend_mode = match glyph_format {
- GlyphFormat::Subpixel |
- GlyphFormat::TransformedSubpixel => {
- if text_cpu.font.bg_color.a != 0 {
- BlendMode::SubpixelWithBgColor
- } else {
- BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
- }
- }
- GlyphFormat::Alpha |
- GlyphFormat::TransformedAlpha |
- GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
- };
- let subpx_dir = match glyph_format {
- GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => SubpixelDirection::None,
- _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
- };
-
- let key = BatchKey::new(kind, blend_mode, textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
-
- for glyph in glyphs {
- batch.push(base_instance.build(
- glyph.index_in_text_run,
- glyph.uv_rect_address.as_int(),
- subpx_dir as u32 as i32,
- ));
- }
- },
- );
- }
- PrimitiveKind::Picture => {
- let picture =
- &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
-
- match picture.render_task_id {
- Some(cache_task_id) => {
- let cache_task_address = render_tasks.get_task_address(cache_task_id);
- let textures = BatchTextures::render_target_cache();
-
- match picture.kind {
- PictureKind::TextShadow { .. } => {
- let kind = BatchKind::Brush(
- BrushBatchKind::Image(
- BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
- );
- let key = BatchKey::new(kind, blend_mode, textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
-
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_id,
- scroll_id,
- clip_task_address,
- z,
- segment_kind: 0,
- user_data0: cache_task_address.0 as i32,
- user_data1: BrushImageKind::Simple as i32,
- };
- batch.push(PrimitiveInstance::from(instance));
- }
- PictureKind::BoxShadow { image_kind, .. } => {
- let kind = BatchKind::Brush(
- BrushBatchKind::Image(
- BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
- );
- let key = BatchKey::new(kind, blend_mode, textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
-
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_id,
- scroll_id,
- clip_task_address,
- z,
- segment_kind: 0,
- user_data0: cache_task_address.0 as i32,
- user_data1: image_kind as i32,
- };
- batch.push(PrimitiveInstance::from(instance));
- }
- PictureKind::Image {
- composite_mode,
- secondary_render_task_id,
- is_in_3d_context,
- reference_frame_id,
- real_local_rect,
- ..
- } => {
- // If this picture is participating in a 3D rendering context,
- // then don't add it to any batches here. Instead, create a polygon
- // for it and add it to the current plane splitter.
- if is_in_3d_context {
- // Push into parent plane splitter.
-
- let real_xf = &ctx.clip_scroll_tree.nodes[&reference_frame_id].world_content_transform;
-
- let polygon = make_polygon(
- real_local_rect,
- &real_xf,
- prim_index.0,
- );
-
- splitter.add(polygon);
-
- return;
- }
-
- // Depending on the composite mode of the picture, we generate the
- // old style Composite primitive instances. In the future, we'll
- // remove these and pass them through the brush batching pipeline.
- // This will allow us to unify some of the shaders, apply clip masks
- // when compositing pictures, and also correctly apply pixel snapping
- // to picture compositing operations.
- let source_id = picture.render_task_id.expect("no source!?");
-
- match composite_mode.expect("bug: only composites here") {
- PictureCompositeMode::Filter(filter) => {
- match filter {
- FilterOp::Blur(..) => {
- let src_task_address = render_tasks.get_task_address(source_id);
- let key = BatchKey::new(
- BatchKind::HardwareComposite,
- BlendMode::PremultipliedAlpha,
- BatchTextures::render_target_cache(),
- );
- let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
- let instance = CompositePrimitiveInstance::new(
- task_address,
- src_task_address,
- RenderTaskAddress(0),
- item_bounding_rect.origin.x,
- item_bounding_rect.origin.y,
- z,
- item_bounding_rect.size.width,
- item_bounding_rect.size.height,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- FilterOp::DropShadow(offset, _, _) => {
- let kind = BatchKind::Brush(
- BrushBatchKind::Image(BrushImageSourceKind::ColorAlphaMask),
- );
- let key = BatchKey::new(kind, blend_mode, textures);
-
- let instance = BrushInstance {
- picture_address: task_address,
- prim_address: prim_cache_address,
- clip_id,
- scroll_id,
- clip_task_address,
- z,
- segment_kind: 0,
- user_data0: cache_task_address.0 as i32,
- user_data1: BrushImageKind::Simple as i32,
- };
-
- {
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- batch.push(PrimitiveInstance::from(instance));
- }
-
- let secondary_id = secondary_render_task_id.expect("no secondary!?");
- let render_task = &render_tasks[secondary_id];
- let secondary_task_address = render_tasks.get_task_address(secondary_id);
- let render_pass_index = render_task.pass_index.expect("no render_pass_index!?");
- let secondary_textures = BatchTextures {
- colors: [
- SourceTexture::RenderTaskCacheRGBA8(render_pass_index),
- SourceTexture::Invalid,
- SourceTexture::Invalid,
- ],
- };
- let key = BatchKey::new(
- BatchKind::HardwareComposite,
- BlendMode::PremultipliedAlpha,
- secondary_textures,
- );
- let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
- let device_offset_x = device_length(offset.x, ctx.device_pixel_ratio);
- let device_offset_y = device_length(offset.y, ctx.device_pixel_ratio);
- let instance = CompositePrimitiveInstance::new(
- task_address,
- secondary_task_address,
- RenderTaskAddress(0),
- item_bounding_rect.origin.x - device_offset_x.0,
- item_bounding_rect.origin.y - device_offset_y.0,
- z,
- item_bounding_rect.size.width,
- item_bounding_rect.size.height,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- _ => {
- let key = BatchKey::new(
- BatchKind::Blend,
- BlendMode::PremultipliedAlpha,
- BatchTextures::no_texture(),
- );
- let src_task_address = render_tasks.get_task_address(source_id);
-
- let (filter_mode, amount) = match filter {
- FilterOp::Blur(..) => (0, 0.0),
- FilterOp::Contrast(amount) => (1, amount),
- FilterOp::Grayscale(amount) => (2, amount),
- FilterOp::HueRotate(angle) => (3, angle),
- FilterOp::Invert(amount) => (4, amount),
- FilterOp::Saturate(amount) => (5, amount),
- FilterOp::Sepia(amount) => (6, amount),
- FilterOp::Brightness(amount) => (7, amount),
- FilterOp::Opacity(_, amount) => (8, amount),
- FilterOp::DropShadow(..) => unreachable!(),
- };
-
- let amount = (amount * 65535.0).round() as i32;
- let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
-
- let instance = CompositePrimitiveInstance::new(
- task_address,
- src_task_address,
- RenderTaskAddress(0),
- filter_mode,
- amount,
- z,
- 0,
- 0,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- }
- }
- PictureCompositeMode::MixBlend(mode) => {
- let backdrop_id = secondary_render_task_id.expect("no backdrop!?");
-
- let key = BatchKey::new(
- BatchKind::Composite {
- task_id,
- source_id,
- backdrop_id,
- },
- BlendMode::PremultipliedAlpha,
- BatchTextures::no_texture(),
- );
- let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
- let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
- let source_task_address = render_tasks.get_task_address(source_id);
-
- let instance = CompositePrimitiveInstance::new(
- task_address,
- source_task_address,
- backdrop_task_address,
- mode as u32 as i32,
- 0,
- z,
- 0,
- 0,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- PictureCompositeMode::Blit => {
- let src_task_address = render_tasks.get_task_address(source_id);
- let key = BatchKey::new(
- BatchKind::HardwareComposite,
- BlendMode::PremultipliedAlpha,
- BatchTextures::render_target_cache(),
- );
- let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
- let instance = CompositePrimitiveInstance::new(
- task_address,
- src_task_address,
- RenderTaskAddress(0),
- item_bounding_rect.origin.x,
- item_bounding_rect.origin.y,
- z,
- item_bounding_rect.size.width,
- item_bounding_rect.size.height,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- }
- }
- }
- }
- None => {
- // If this picture is being drawn into an existing target (i.e. with
- // no composition operation), recurse and add to the current batch list.
- picture.add_to_batch(
- task_id,
- ctx,
- gpu_cache,
- render_tasks,
- deferred_resolves,
- batch_list,
- glyph_fetch_buffer,
- );
- }
- }
- }
- PrimitiveKind::AlignedGradient => {
- let gradient_cpu =
- &ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::AlignedGradient,
- );
- let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- for part_index in 0 .. (gradient_cpu.stops_count - 1) {
- batch.push(base_instance.build(part_index as i32, 0, 0));
- }
- }
- PrimitiveKind::AngleGradient => {
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::AngleGradient,
- );
- let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- batch.push(base_instance.build(0, 0, 0));
- }
- PrimitiveKind::RadialGradient => {
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::RadialGradient,
- );
- let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
- batch.push(base_instance.build(0, 0, 0));
- }
- PrimitiveKind::YuvImage => {
- let mut textures = BatchTextures::no_texture();
- let mut uv_rect_addresses = [0; 3];
- let image_yuv_cpu =
- &ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
-
- //yuv channel
- let channel_count = image_yuv_cpu.format.get_plane_num();
- debug_assert!(channel_count <= 3);
- for channel in 0 .. channel_count {
- let image_key = image_yuv_cpu.yuv_key[channel];
-
- let (texture, address) = resolve_image(
- image_key,
- image_yuv_cpu.image_rendering,
- None,
- ctx.resource_cache,
- gpu_cache,
- deferred_resolves,
- );
-
- if texture == SourceTexture::Invalid {
- warn!("Warnings: skip a PrimitiveKind::YuvImage at {:?}.\n", item_bounding_rect);
- return;
- }
-
- textures.colors[channel] = texture;
- uv_rect_addresses[channel] = address.as_int(gpu_cache);
- }
-
- let get_buffer_kind = |texture: SourceTexture| {
- match texture {
- SourceTexture::External(ext_image) => {
- match ext_image.image_type {
- ExternalImageType::Texture2DHandle => {
- ImageBufferKind::Texture2D
- }
- ExternalImageType::Texture2DArrayHandle => {
- ImageBufferKind::Texture2DArray
- }
- ExternalImageType::TextureRectHandle => {
- ImageBufferKind::TextureRect
- }
- ExternalImageType::TextureExternalHandle => {
- ImageBufferKind::TextureExternal
- }
- ExternalImageType::ExternalBuffer => {
- // The ExternalImageType::ExternalBuffer should be handled by resource_cache.
- // It should go through the non-external case.
- panic!("Unexpected non-texture handle type");
- }
- }
- }
- _ => ImageBufferKind::Texture2DArray,
- }
- };
-
- // All yuv textures should be the same type.
- let buffer_kind = get_buffer_kind(textures.colors[0]);
- assert!(
- textures.colors[1 .. image_yuv_cpu.format.get_plane_num()]
- .iter()
- .all(|&tid| buffer_kind == get_buffer_kind(tid))
- );
-
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::YuvImage(
- buffer_kind,
- image_yuv_cpu.format,
- image_yuv_cpu.color_space,
- ),
- );
- let key = BatchKey::new(kind, blend_mode, textures);
- let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
-
- batch.push(base_instance.build(
- uv_rect_addresses[0],
- uv_rect_addresses[1],
- uv_rect_addresses[2],
- ));
- }
- }
-}
-
-impl PicturePrimitive {
- fn add_to_batch(
- &self,
- task_id: RenderTaskId,
- ctx: &RenderTargetContext,
- gpu_cache: &mut GpuCache,
- render_tasks: &RenderTaskTree,
- deferred_resolves: &mut Vec,
- batch_list: &mut BatchList,
- glyph_fetch_buffer: &mut Vec,
- ) {
- let task_address = render_tasks.get_task_address(task_id);
-
- // Even though most of the time a splitter isn't used or needed,
- // they are cheap to construct so we will always pass one down.
- let mut splitter = BspSplitter::new();
-
- // Add each run in this picture to the batch.
- for run in &self.runs {
- let clip_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()];
- let clip_id = clip_node.node_data_index;
-
- let scroll_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
- let scroll_id = scroll_node.node_data_index;
-
- run.add_to_batch(
- clip_id,
- scroll_id,
- batch_list,
- ctx,
- gpu_cache,
- render_tasks,
- task_id,
- task_address,
- deferred_resolves,
- glyph_fetch_buffer,
- &mut splitter,
- );
- }
-
- // Flush the accumulated plane splits onto the task tree.
- // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
- for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
- let prim_index = PrimitiveIndex(poly.anchor);
- debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
- let pp = &poly.points;
- let gpu_blocks = [
- [pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
- [pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
- [pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
- ];
- let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
- let key = BatchKey::new(
- BatchKind::SplitComposite,
- BlendMode::PremultipliedAlpha,
- BatchTextures::no_texture(),
- );
- let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
- let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
- let batch = batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug"));
- let source_task_address = render_tasks.get_task_address(pic.render_task_id.expect("bug"));
- let gpu_address = gpu_handle.as_int(gpu_cache);
-
- let instance = CompositePrimitiveInstance::new(
- task_address,
- source_task_address,
- RenderTaskAddress(0),
- gpu_address,
- 0,
- prim_index.0 as i32,
- 0,
- 0,
- );
-
- batch.push(PrimitiveInstance::from(instance));
- }
- }
-}
-
-impl AlphaBatcher {
- fn new(screen_size: DeviceIntSize) -> Self {
- AlphaBatcher {
- tasks: Vec::new(),
- batch_list: BatchList::new(screen_size),
- glyph_fetch_buffer: Vec::new(),
- }
- }
-
- fn add_task(&mut self, task_id: RenderTaskId) {
- self.tasks.push(task_id);
- }
-
- fn build(
- &mut self,
- ctx: &RenderTargetContext,
- gpu_cache: &mut GpuCache,
- render_tasks: &RenderTaskTree,
- deferred_resolves: &mut Vec,
- ) {
- for &task_id in &self.tasks {
- match render_tasks[task_id].kind {
- RenderTaskKind::Picture(ref pic_task) => {
- let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index;
- let pic = &ctx.prim_store.cpu_pictures[pic_index.0];
- pic.add_to_batch(
- task_id,
- ctx,
- gpu_cache,
- render_tasks,
- deferred_resolves,
- &mut self.batch_list,
- &mut self.glyph_fetch_buffer
- );
- }
- _ => {
- unreachable!();
- }
- }
- }
-
- self.batch_list.finalize();
- }
-
- pub fn is_empty(&self) -> bool {
- self.batch_list.opaque_batch_list.batches.is_empty() &&
- self.batch_list.alpha_batch_list.batches.is_empty()
- }
-}
-
-/// Batcher managing draw calls into the clip mask (in the RT cache).
-#[derive(Debug)]
-pub struct ClipBatcher {
- /// Rectangle draws fill up the rectangles with rounded corners.
- pub rectangles: Vec,
- /// Image draws apply the image masking.
- pub images: FastHashMap>,
- pub border_clears: Vec,
- pub borders: Vec,
-}
-
-impl ClipBatcher {
- fn new() -> Self {
- ClipBatcher {
- rectangles: Vec::new(),
- images: FastHashMap::default(),
- border_clears: Vec::new(),
- borders: Vec::new(),
- }
- }
-
- fn add(
- &mut self,
- task_address: RenderTaskAddress,
- clips: &[ClipWorkItem],
- coordinate_system_id: CoordinateSystemId,
- resource_cache: &ResourceCache,
- gpu_cache: &GpuCache,
- clip_store: &ClipStore,
- ) {
- let mut coordinate_system_id = coordinate_system_id;
- for work_item in clips.iter() {
- let instance = ClipMaskInstance {
- render_task_address: task_address,
- scroll_node_data_index: work_item.scroll_node_data_index,
- segment: 0,
- clip_data_address: GpuCacheAddress::invalid(),
- resource_address: GpuCacheAddress::invalid(),
- };
- let info = clip_store
- .get_opt(&work_item.clip_sources)
- .expect("bug: clip handle should be valid");
-
- for &(ref source, ref handle) in &info.clips {
- let gpu_address = gpu_cache.get_address(handle);
-
- match *source {
- ClipSource::Image(ref mask) => {
- if let Ok(cache_item) = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None) {
- self.images
- .entry(cache_item.texture_id)
- .or_insert(Vec::new())
- .push(ClipMaskInstance {
- clip_data_address: gpu_address,
- resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
- ..instance
- });
- } else {
- warn!("Warnings: skip a image mask. Key:{:?} Rect::{:?}.\n", mask.image, mask.rect);
- continue;
- }
- }
- ClipSource::Rectangle(..) => {
- if work_item.coordinate_system_id != coordinate_system_id {
- self.rectangles.push(ClipMaskInstance {
- clip_data_address: gpu_address,
- ..instance
- });
- coordinate_system_id = work_item.coordinate_system_id;
- }
- }
- ClipSource::RoundedRectangle(..) => {
- self.rectangles.push(ClipMaskInstance {
- clip_data_address: gpu_address,
- ..instance
- });
- }
- ClipSource::BorderCorner(ref source) => {
- self.border_clears.push(ClipMaskInstance {
- clip_data_address: gpu_address,
- segment: 0,
- ..instance
- });
- for clip_index in 0 .. source.actual_clip_count {
- self.borders.push(ClipMaskInstance {
- clip_data_address: gpu_address,
- segment: 1 + clip_index as i32,
- ..instance
- })
- }
- }
- }
- }
- }
- }
-}
-
pub struct RenderTargetContext<'a> {
- pub device_pixel_ratio: f32,
+ pub device_pixel_scale: DevicePixelScale,
pub prim_store: &'a PrimitiveStore,
pub resource_cache: &'a ResourceCache,
pub node_data: &'a [ClipScrollNodeData],
pub clip_scroll_tree: &'a ClipScrollTree,
+ pub use_dual_source_blending: bool,
}
struct TextureAllocator {
@@ -1415,9 +220,6 @@ pub struct ScalingInfo {
/// A render target represents a number of rendering operations on a surface.
pub struct ColorRenderTarget {
pub alpha_batcher: AlphaBatcher,
- // List of text runs to be cached to this render target.
- pub text_run_cache_prims: FastHashMap>,
- pub line_cache_prims: Vec,
// List of blur operations to apply for this render target.
pub vertical_blurs: Vec,
pub horizontal_blurs: Vec,
@@ -1426,7 +228,7 @@ pub struct ColorRenderTarget {
// List of frame buffer outputs for this render target.
pub outputs: Vec,
allocator: Option,
- glyph_fetch_buffer: Vec,
+ alpha_tasks: Vec,
}
impl RenderTarget for ColorRenderTarget {
@@ -1443,15 +245,13 @@ impl RenderTarget for ColorRenderTarget {
) -> Self {
ColorRenderTarget {
alpha_batcher: AlphaBatcher::new(screen_size),
- text_run_cache_prims: FastHashMap::default(),
- line_cache_prims: Vec::new(),
vertical_blurs: Vec::new(),
horizontal_blurs: Vec::new(),
readbacks: Vec::new(),
scalings: Vec::new(),
allocator: size.map(TextureAllocator::new),
- glyph_fetch_buffer: Vec::new(),
outputs: Vec::new(),
+ alpha_tasks: Vec::new(),
}
}
@@ -1462,15 +262,20 @@ impl RenderTarget for ColorRenderTarget {
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec,
) {
- self.alpha_batcher
- .build(ctx, gpu_cache, render_tasks, deferred_resolves);
+ self.alpha_batcher.build(
+ &self.alpha_tasks,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ deferred_resolves,
+ );
}
fn add_task(
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
- gpu_cache: &GpuCache,
+ _: &GpuCache,
render_tasks: &RenderTaskTree,
_: &ClipStore,
) {
@@ -1504,90 +309,16 @@ impl RenderTarget for ColorRenderTarget {
PrimitiveKind::Picture => {
let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
- match prim.kind {
- PictureKind::Image { frame_output_pipeline_id, .. } => {
- self.alpha_batcher.add_task(task_id);
+ self.alpha_tasks.push(task_id);
- // If this pipeline is registered as a frame output
- // store the information necessary to do the copy.
- if let Some(pipeline_id) = frame_output_pipeline_id {
- self.outputs.push(FrameOutput {
- pipeline_id,
- task_id,
- });
- }
- }
- PictureKind::TextShadow { .. } |
- PictureKind::BoxShadow { .. } => {
- let task_index = render_tasks.get_task_address(task_id);
-
- for run in &prim.runs {
- for i in 0 .. run.count {
- let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
-
- let sub_metadata = ctx.prim_store.get_metadata(sub_prim_index);
- let sub_prim_address =
- gpu_cache.get_address(&sub_metadata.gpu_location);
- let instance = SimplePrimitiveInstance::new(
- sub_prim_address,
- task_index,
- RenderTaskAddress(0),
- ClipScrollNodeIndex(0),
- ClipScrollNodeIndex(0),
- 0,
- ); // z is disabled for rendering cache primitives
-
- match sub_metadata.prim_kind {
- PrimitiveKind::TextRun => {
- // Add instances that reference the text run GPU location. Also supply
- // the parent shadow prim address as a user data field, allowing
- // the shader to fetch the shadow parameters.
- let text = &ctx.prim_store.cpu_text_runs
- [sub_metadata.cpu_prim_index.0];
- let text_run_cache_prims = &mut self.text_run_cache_prims;
-
- let font = text.get_font(
- ctx.device_pixel_ratio,
- &LayerToWorldTransform::identity(),
- RasterizationSpace::Local,
- );
-
- ctx.resource_cache.fetch_glyphs(
- font,
- &text.glyph_keys,
- &mut self.glyph_fetch_buffer,
- gpu_cache,
- |texture_id, glyph_format, glyphs| {
- let batch = text_run_cache_prims
- .entry(texture_id)
- .or_insert(Vec::new());
-
- let subpx_dir = match glyph_format {
- GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => SubpixelDirection::None,
- _ => text.font.subpx_dir.limit_by(text.font.render_mode),
- };
-
- for glyph in glyphs {
- batch.push(instance.build(
- glyph.index_in_text_run,
- glyph.uv_rect_address.as_int(),
- subpx_dir as u32 as i32,
- ));
- }
- },
- );
- }
- PrimitiveKind::Line => {
- self.line_cache_prims
- .push(instance.build(0, 0, 0));
- }
- _ => {
- unreachable!("Unexpected sub primitive type");
- }
- }
- }
- }
+ if let PictureKind::Image { frame_output_pipeline_id, .. } = prim.kind {
+ // If this pipeline is registered as a frame output
+ // store the information necessary to do the copy.
+ if let Some(pipeline_id) = frame_output_pipeline_id {
+ self.outputs.push(FrameOutput {
+ pipeline_id,
+ task_id,
+ });
}
}
}
@@ -1729,11 +460,11 @@ impl RenderTarget for AlphaRenderTarget {
// tasks support clip masks and
// transform primitives, these
// will need to be filled out!
- clip_id: ClipScrollNodeIndex(0),
+ clip_chain_rect_index: ClipChainRectIndex(0),
scroll_id: ClipScrollNodeIndex(0),
clip_task_address: RenderTaskAddress(0),
z: 0,
- segment_kind: 0,
+ segment_index: 0,
user_data0: 0,
user_data1: 0,
};
@@ -1934,116 +665,6 @@ impl RenderPass {
}
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum TransformBatchKind {
- TextRun(GlyphFormat),
- Image(ImageBufferKind),
- YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
- AlignedGradient,
- AngleGradient,
- RadialGradient,
- BorderCorner,
- BorderEdge,
- Line,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum BrushImageSourceKind {
- Alpha,
- Color,
- ColorAlphaMask,
-}
-
-impl BrushImageSourceKind {
- pub fn from_render_target_kind(render_target_kind: RenderTargetKind) -> BrushImageSourceKind {
- match render_target_kind {
- RenderTargetKind::Color => BrushImageSourceKind::Color,
- RenderTargetKind::Alpha => BrushImageSourceKind::Alpha,
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum BrushBatchKind {
- Image(BrushImageSourceKind),
- Solid,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum BatchKind {
- Composite {
- task_id: RenderTaskId,
- source_id: RenderTaskId,
- backdrop_id: RenderTaskId,
- },
- HardwareComposite,
- SplitComposite,
- Blend,
- Transformable(TransformedRectKind, TransformBatchKind),
- Brush(BrushBatchKind),
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct BatchKey {
- pub kind: BatchKind,
- pub blend_mode: BlendMode,
- pub textures: BatchTextures,
-}
-
-impl BatchKey {
- fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
- BatchKey {
- kind,
- blend_mode,
- textures,
- }
- }
-
- fn is_compatible_with(&self, other: &BatchKey) -> bool {
- self.kind == other.kind && self.blend_mode == other.blend_mode &&
- textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
- textures_compatible(self.textures.colors[1], other.textures.colors[1]) &&
- textures_compatible(self.textures.colors[2], other.textures.colors[2])
- }
-}
-
-#[inline]
-fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
- t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
-}
-
-#[derive(Debug)]
-pub struct AlphaPrimitiveBatch {
- pub key: BatchKey,
- pub instances: Vec,
- pub item_rects: Vec,
-}
-
-impl AlphaPrimitiveBatch {
- fn new(key: BatchKey) -> AlphaPrimitiveBatch {
- AlphaPrimitiveBatch {
- key,
- instances: Vec::new(),
- item_rects: Vec::new(),
- }
- }
-}
-
-#[derive(Debug)]
-pub struct OpaquePrimitiveBatch {
- pub key: BatchKey,
- pub instances: Vec,
-}
-
-impl OpaquePrimitiveBatch {
- fn new(key: BatchKey) -> OpaquePrimitiveBatch {
- OpaquePrimitiveBatch {
- key,
- instances: Vec::new(),
- }
- }
-}
-
#[derive(Debug, Clone, Default)]
pub struct CompositeOps {
// Requires only a single texture as input (e.g. most filters)
@@ -2078,6 +699,7 @@ pub struct Frame {
pub profile_counters: FrameProfileCounters,
pub node_data: Vec,
+ pub clip_chain_local_clip_rects: Vec,
pub render_tasks: RenderTaskTree,
// List of updates that need to be pushed to the
@@ -2091,45 +713,6 @@ pub struct Frame {
pub deferred_resolves: Vec,
}
-fn resolve_image(
- image_key: ImageKey,
- image_rendering: ImageRendering,
- tile_offset: Option,
- resource_cache: &ResourceCache,
- gpu_cache: &mut GpuCache,
- deferred_resolves: &mut Vec,
-) -> (SourceTexture, GpuCacheHandle) {
- match resource_cache.get_image_properties(image_key) {
- Some(image_properties) => {
- // Check if an external image that needs to be resolved
- // by the render thread.
- match image_properties.external_image {
- Some(external_image) => {
- // This is an external texture - we will add it to
- // the deferred resolves list to be patched by
- // the render thread...
- let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
- deferred_resolves.push(DeferredResolve {
- image_properties,
- address: gpu_cache.get_address(&cache_handle),
- });
-
- (SourceTexture::External(external_image), cache_handle)
- }
- None => {
- if let Ok(cache_item) = resource_cache.get_cached_image(image_key, image_rendering, tile_offset) {
- (cache_item.texture_id, cache_item.uv_rect_handle)
- } else {
- // There is no usable texture entry for the image key. Just return an invalid texture here.
- (SourceTexture::Invalid, GpuCacheHandle::new())
- }
- }
- }
- }
- None => (SourceTexture::Invalid, GpuCacheHandle::new()),
- }
-}
-
impl BlurTask {
fn add_instances(
&self,
@@ -2158,55 +741,3 @@ impl BlurTask {
}
}
}
-
-/// Construct a polygon from stacking context boundaries.
-/// `anchor` here is an index that's going to be preserved in all the
-/// splits of the polygon.
-fn make_polygon(
- rect: LayerRect,
- transform: &LayerToWorldTransform,
- anchor: usize,
-) -> Polygon {
- let mat = TypedTransform3D::row_major(
- transform.m11 as f64,
- transform.m12 as f64,
- transform.m13 as f64,
- transform.m14 as f64,
- transform.m21 as f64,
- transform.m22 as f64,
- transform.m23 as f64,
- transform.m24 as f64,
- transform.m31 as f64,
- transform.m32 as f64,
- transform.m33 as f64,
- transform.m34 as f64,
- transform.m41 as f64,
- transform.m42 as f64,
- transform.m43 as f64,
- transform.m44 as f64);
- Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
-}
-
-impl BrushPrimitive {
- fn get_batch_key(&self, blend_mode: BlendMode) -> BatchKey {
- match self.kind {
- BrushKind::Solid { .. } => {
- BatchKey::new(
- BatchKind::Brush(BrushBatchKind::Solid),
- blend_mode,
- BatchTextures::no_texture(),
- )
- }
- BrushKind::Clear => {
- BatchKey::new(
- BatchKind::Brush(BrushBatchKind::Solid),
- BlendMode::PremultipliedDestOut,
- BatchTextures::no_texture(),
- )
- }
- BrushKind::Mask { .. } => {
- unreachable!("bug: mask brushes not expected in normal alpha pass");
- }
- }
- }
-}
diff --git a/gfx/webrender/src/util.rs b/gfx/webrender/src/util.rs
index 50989ca8fc3c..758291256d80 100644
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -2,10 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, DeviceRect};
-use api::{DeviceSize, LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, WorldRect};
-use euclid::{Point2D, Rect, TypedScale, Size2D, TypedPoint2D, TypedRect, TypedSize2D};
-use euclid::{TypedTransform2D, TypedTransform3D};
+use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
+use api::{DevicePoint, DeviceRect, DeviceSize, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, WorldRect};
+use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};
+use euclid::TypedTransform3D;
use num_traits::Zero;
use std::{i32, f32};
@@ -20,6 +21,7 @@ pub trait MatrixHelpers {
fn inverse_project(&self, target: &TypedPoint2D) -> Option>;
fn inverse_rect_footprint(&self, rect: &TypedRect) -> TypedRect;
fn transform_kind(&self) -> TransformedRectKind;
+ fn is_simple_translation(&self) -> bool;
}
impl MatrixHelpers for TypedTransform3D {
@@ -92,6 +94,17 @@ impl MatrixHelpers for TypedTransform3D {
TransformedRectKind::Complex
}
}
+
+ fn is_simple_translation(&self) -> bool {
+ if self.m11 != 1. || self.m22 != 1. || self.m33 != 1. {
+ return false;
+ }
+ self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO &&
+ self.m14.abs() < NEARLY_ZERO && self.m21.abs() < NEARLY_ZERO &&
+ self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO &&
+ self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO &&
+ self.m34.abs() < NEARLY_ZERO
+ }
}
pub trait RectHelpers
@@ -140,7 +153,7 @@ pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
pub fn calculate_screen_bounding_rect(
transform: &LayerToWorldTransform,
rect: &LayerRect,
- device_pixel_ratio: f32
+ device_pixel_scale: DevicePixelScale,
) -> DeviceIntRect {
let points = [
transform.transform_point2d(&rect.origin),
@@ -149,9 +162,7 @@ pub fn calculate_screen_bounding_rect(
transform.transform_point2d(&rect.bottom_right()),
];
- let scale = TypedScale::new(device_pixel_ratio);
- let rect: DeviceRect = WorldRect::from_points(&points) * scale;
-
+ let rect = WorldRect::from_points(&points) * device_pixel_scale;
let max_rect = DeviceRect::max_rect();
rect
.round_out()
@@ -324,3 +335,74 @@ impl MaxRect for DeviceRect {
)
}
}
+
+/// An enum that tries to avoid expensive transformation matrix calculations
+/// when possible when dealing with non-perspective axis-aligned transformations.
+#[derive(Debug, Clone)]
+pub enum TransformOrOffset {
+ /// A simple offset, which can be used without doing any matrix math.
+ Offset(LayerVector2D),
+
+ /// A transformation with an inverse. If the inverse isn't present, this isn't a 2D
+ /// transformation, which means we need to fall back to using inverse_rect_footprint.
+ /// Since this operation is so expensive, we avoid it for the 2D case.
+ Transform {
+ transform: LayerTransform,
+ inverse: Option,
+ }
+}
+
+impl TransformOrOffset {
+ pub fn zero() -> TransformOrOffset {
+ TransformOrOffset::Offset(LayerVector2D::zero())
+ }
+
+ fn new_transform(transform: LayerTransform) -> TransformOrOffset {
+ if transform.is_2d() {
+ TransformOrOffset::Transform {
+ transform,
+ inverse: Some(transform.inverse().expect("Expected invertible matrix."))
+ }
+ } else {
+ TransformOrOffset::Transform { transform, inverse: None }
+ }
+ }
+
+ pub fn apply(&self, rect: &LayerRect) -> LayerRect {
+ match *self {
+ TransformOrOffset::Offset(offset) => rect.translate(&offset),
+ TransformOrOffset::Transform {transform, .. } => transform.transform_rect(&rect),
+ }
+ }
+
+ pub fn unapply(&self, rect: &LayerRect) -> LayerRect {
+ match *self {
+ TransformOrOffset::Offset(offset) => rect.translate(&-offset),
+ TransformOrOffset::Transform { inverse: Some(inverse), .. } =>
+ inverse.transform_rect(&rect),
+ TransformOrOffset::Transform { transform, inverse: None } =>
+ transform.inverse_rect_footprint(rect),
+ }
+ }
+
+ pub fn offset(&self, new_offset: LayerVector2D) -> TransformOrOffset {
+ match *self {
+ TransformOrOffset::Offset(offset) => TransformOrOffset::Offset(offset + new_offset),
+ TransformOrOffset::Transform { transform, .. } => {
+ let transform = transform.pre_translate(new_offset.to_3d());
+ TransformOrOffset::new_transform(transform)
+ }
+ }
+ }
+
+ pub fn update(&self, transform: LayerTransform) -> Option {
+ if transform.is_simple_translation() {
+ let offset = LayerVector2D::new(transform.m41, transform.m42);
+ Some(self.offset(offset))
+ } else {
+ // If we break 2D axis alignment or have a perspective component, we need to start a
+ // new incompatible coordinate system with which we cannot share clips without masking.
+ None
+ }
+ }
+}
diff --git a/gfx/webrender/tests/angle_shader_validation.rs b/gfx/webrender/tests/angle_shader_validation.rs
index 274b50cb545a..f3cd4251ff06 100644
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -42,10 +42,6 @@ const SHADERS: &[Shader] = &[
features: CACHE_FEATURES,
},
// Prim shaders
- Shader {
- name: "ps_line",
- features: &["", "TRANSFORM", "CACHE"],
- },
Shader {
name: "ps_border_corner",
features: PRIM_FEATURES,
@@ -107,6 +103,10 @@ const SHADERS: &[Shader] = &[
name: "brush_image",
features: &["COLOR_TARGET", "ALPHA_TARGET"],
},
+ Shader {
+ name: "brush_line",
+ features: &[],
+ },
];
const VERSION_STRING: &str = "#version 300 es\n";
diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml
index 2ef8b3c931a3..017e9aaeac0e 100644
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -8,6 +8,7 @@ repository = "https://github.com/servo/webrender"
[features]
nightly = ["euclid/unstable", "serde/unstable"]
ipc = ["ipc-channel"]
+debug-serialization = []
[dependencies]
app_units = "0.6"
@@ -16,8 +17,8 @@ bincode = "0.9"
byteorder = "1.2.1"
euclid = "0.16"
ipc-channel = {version = "0.9", optional = true}
-serde = { version = "=1.0.23", features = ["rc", "derive"] }
-serde_derive = { version = "=1.0.23", features = ["deserialize_from"] }
+serde = { version = "=1.0.27", features = ["rc"] }
+serde_derive = { version = "=1.0.27", features = ["deserialize_in_place"] }
time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs
index a70d102050de..969e4929b58f 100644
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -12,6 +12,7 @@ use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethod
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
+use std::path::PathBuf;
pub type TileSize = u16;
/// Documents are rendered in the ascending order of their associated layer values.
@@ -267,6 +268,12 @@ pub enum DebugCommand {
FetchRenderTasks,
/// Fetch screenshot.
FetchScreenshot,
+ /// Save a capture of all the documents state.
+ SaveCapture(PathBuf),
+ /// Load a capture of all the documents state.
+ LoadCapture(PathBuf),
+ /// Configure if dual-source blending is used, if available.
+ EnableDualSourceBlending(bool),
}
#[derive(Clone, Deserialize, Serialize)]
@@ -759,6 +766,23 @@ impl RenderApi {
) {
self.send(document_id, DocumentMsg::GenerateFrame(property_bindings));
}
+
+ /// Save a capture of the current frame state for debugging.
+ pub fn save_capture(&self, path: PathBuf) {
+ let msg = ApiMsg::DebugCommand(DebugCommand::SaveCapture(path));
+ self.send_message(msg);
+ }
+
+ /// Load a capture of the current frame state for debugging.
+ pub fn load_capture(&self, path: PathBuf) {
+ let msg = ApiMsg::DebugCommand(DebugCommand::LoadCapture(path));
+ self.send_message(msg);
+ }
+
+ pub fn send_debug_cmd(&self, cmd: DebugCommand) {
+ let msg = ApiMsg::DebugCommand(cmd);
+ self.send_message(msg);
+ }
}
impl Drop for RenderApi {
diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs
index 8417e3138a06..390c27feb780 100644
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -8,6 +8,9 @@ use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding};
use euclid::{SideOffsets2D, TypedRect};
use std::ops::Not;
+#[cfg(feature = "debug-serialization")]
+use GlyphInstance;
+
// NOTE: some of these structs have an "IMPLICIT" comment.
// This indicates that the BuiltDisplayList will have serialized
// a list of values nearby that this item consumes. The traversal
@@ -48,13 +51,17 @@ impl ClipAndScrollInfo {
/// events.
pub type ItemTag = (u64, u16);
+/// The DI is generic over the specifics, while allows to use
+/// the "complete" version of it for convenient serialization.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct DisplayItem {
- pub item: SpecificDisplayItem,
+pub struct GenericDisplayItem {
+ pub item: T,
pub clip_and_scroll: ClipAndScrollInfo,
pub info: LayoutPrimitiveInfo,
}
+pub type DisplayItem = GenericDisplayItem;
+
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct PrimitiveInfo {
pub rect: TypedRect,
@@ -112,6 +119,33 @@ pub enum SpecificDisplayItem {
PopAllShadows,
}
+/// This is a "complete" version of the DI specifics,
+/// containing the auxiliary data within the corresponding
+/// enumeration variants, to be used for debug serialization.
+#[cfg(feature = "debug-serialization")]
+#[derive(Deserialize, Serialize)]
+pub enum CompletelySpecificDisplayItem {
+ Clip(ClipDisplayItem, Vec),
+ ScrollFrame(ScrollFrameDisplayItem, Vec),
+ StickyFrame(StickyFrameDisplayItem),
+ Rectangle(RectangleDisplayItem),
+ ClearRectangle,
+ Line(LineDisplayItem),
+ Text(TextDisplayItem, Vec),
+ Image(ImageDisplayItem),
+ YuvImage(YuvImageDisplayItem),
+ Border(BorderDisplayItem),
+ BoxShadow(BoxShadowDisplayItem),
+ Gradient(GradientDisplayItem),
+ RadialGradient(RadialGradientDisplayItem),
+ Iframe(IframeDisplayItem),
+ PushStackingContext(PushStackingContextDisplayItem, Vec),
+ PopStackingContext,
+ SetGradientStops(Vec),
+ PushShadow(Shadow),
+ PopAllShadows,
+}
+
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClipDisplayItem {
pub id: ClipId,
diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs
index a6bae8f991a1..02d28990fb67 100644
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -15,14 +15,18 @@ use {StickyOffsetBounds, TextDisplayItem, TransformStyle, YuvColorSpace, YuvData
use YuvImageDisplayItem;
use bincode;
use euclid::SideOffsets2D;
-use serde::{Deserialize, Serialize, Serializer};
-use serde::ser::{SerializeMap, SerializeSeq};
+use serde::{Deserialize, Serialize};
use std::io::{Read, Write};
-use std::{io, ptr};
+use std::{io, mem, ptr};
use std::marker::PhantomData;
use std::slice;
use time::precise_time_ns;
+#[cfg(feature = "debug-serialization")]
+use serde::de::Deserializer;
+#[cfg(feature = "debug-serialization")]
+use serde::ser::{Serializer, SerializeSeq};
+
// We don't want to push a long text-run. If a text-run is too long, split it into several parts.
// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_PRIM_HEADER - VECS_PER_TEXT_RUN) * 2
pub const MAX_TEXT_RUN_LENGTH: usize = 2038;
@@ -48,7 +52,7 @@ impl Default for ItemRange {
impl ItemRange {
pub fn is_empty(&self) -> bool {
// Nothing more than space for a length (0).
- self.length <= ::std::mem::size_of::()
+ self.length <= mem::size_of::()
}
}
@@ -228,7 +232,7 @@ impl<'a> BuiltDisplayListIter<'a> {
{
let reader = bincode::read_types::IoReader::new(UnsafeReader::new(&mut self.data));
let mut deserializer = bincode::Deserializer::new(reader, bincode::Infinite);
- self.cur_item.deserialize_from(&mut deserializer)
+ Deserialize::deserialize_in_place(&mut deserializer, &mut self.cur_item)
.expect("MEH: malicious process?");
}
@@ -336,8 +340,8 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
&self.iter.cur_item.item
}
- pub fn complex_clip(&self) -> &(ItemRange, usize) {
- &self.iter.cur_complex_clip
+ pub fn complex_clip(&self) -> (ItemRange, usize) {
+ self.iter.cur_complex_clip
}
pub fn gradient_stops(&self) -> ItemRange {
@@ -405,58 +409,140 @@ impl<'a, T: for<'de> Deserialize<'de>> Iterator for AuxIter<'a, T> {
impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> {}
-// This is purely for the JSON/RON writers in wrench
+#[cfg(feature = "debug-serialization")]
impl Serialize for BuiltDisplayList {
fn serialize(&self, serializer: S) -> Result {
+ use display_item::CompletelySpecificDisplayItem::*;
+ use display_item::GenericDisplayItem;
+
let mut seq = serializer.serialize_seq(None)?;
let mut traversal = self.iter();
while let Some(item) = traversal.next() {
- seq.serialize_element(&item)?
+ let di = item.display_item();
+ let serial_di = GenericDisplayItem {
+ item: match di.item {
+ SpecificDisplayItem::Clip(v) => Clip(v,
+ item.iter.list.get(item.iter.cur_complex_clip.0).collect()
+ ),
+ SpecificDisplayItem::ScrollFrame(v) => ScrollFrame(v,
+ item.iter.list.get(item.iter.cur_complex_clip.0).collect()
+ ),
+ SpecificDisplayItem::StickyFrame(v) => StickyFrame(v),
+ SpecificDisplayItem::Rectangle(v) => Rectangle(v),
+ SpecificDisplayItem::ClearRectangle => ClearRectangle,
+ SpecificDisplayItem::Line(v) => Line(v),
+ SpecificDisplayItem::Text(v) => Text(v,
+ item.iter.list.get(item.iter.cur_glyphs).collect()
+ ),
+ SpecificDisplayItem::Image(v) => Image(v),
+ SpecificDisplayItem::YuvImage(v) => YuvImage(v),
+ SpecificDisplayItem::Border(v) => Border(v),
+ SpecificDisplayItem::BoxShadow(v) => BoxShadow(v),
+ SpecificDisplayItem::Gradient(v) => Gradient(v),
+ SpecificDisplayItem::RadialGradient(v) => RadialGradient(v),
+ SpecificDisplayItem::Iframe(v) => Iframe(v),
+ SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(v,
+ item.iter.list.get(item.iter.cur_filters).collect()
+ ),
+ SpecificDisplayItem::PopStackingContext => PopStackingContext,
+ SpecificDisplayItem::SetGradientStops => SetGradientStops(
+ item.iter.list.get(item.iter.cur_stops).collect()
+ ),
+ SpecificDisplayItem::PushShadow(v) => PushShadow(v),
+ SpecificDisplayItem::PopAllShadows => PopAllShadows,
+ },
+ clip_and_scroll: di.clip_and_scroll,
+ info: di.info,
+ };
+ seq.serialize_element(&serial_di)?
}
seq.end()
}
}
-impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
- fn serialize(&self, serializer: S) -> Result {
- let mut map = serializer.serialize_map(None)?;
+// The purpose of this implementation is to deserialize
+// a display list from one format just to immediately
+// serialize then into a "built" `Vec`.
- map.serialize_entry("item", self.display_item())?;
+#[cfg(feature = "debug-serialization")]
+impl<'de> Deserialize<'de> for BuiltDisplayList {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ use display_item::CompletelySpecificDisplayItem::*;
+ use display_item::{CompletelySpecificDisplayItem, GenericDisplayItem};
- match *self.item() {
- SpecificDisplayItem::Text(_) => {
- map.serialize_entry(
- "glyphs",
- &self.iter.list.get(self.glyphs()).collect::>(),
- )?;
- }
- SpecificDisplayItem::PushStackingContext(_) => {
- map.serialize_entry(
- "filters",
- &self.iter.list.get(self.filters()).collect::>(),
- )?;
- }
- _ => {}
+ // Push a vector of things into the DL according to the
+ // convention used by `skip_iter` and `push_iter`
+ fn push_vec(data: &mut Vec, vec: Vec) {
+ let vec_len = vec.len();
+ let byte_size = mem::size_of::() * vec_len;
+ serialize_fast(data, &byte_size);
+ serialize_fast(data, &vec_len);
+ let count = serialize_iter_fast(data, vec.into_iter());
+ assert_eq!(count, vec_len);
}
- let &(complex_clips, number_of_complex_clips) = self.complex_clip();
- let gradient_stops = self.gradient_stops();
+ let list = Vec::>
+ ::deserialize(deserializer)?;
- if number_of_complex_clips > 0 {
- map.serialize_entry(
- "complex_clips",
- &self.iter.list.get(complex_clips).collect::>(),
- )?;
+ let mut data = Vec::new();
+ let mut temp = Vec::new();
+ for complete in list {
+ let item = DisplayItem {
+ item: match complete.item {
+ Clip(v, complex_clips) => {
+ push_vec(&mut temp, complex_clips);
+ SpecificDisplayItem::Clip(v)
+ },
+ ScrollFrame(v, complex_clips) => {
+ push_vec(&mut temp, complex_clips);
+ SpecificDisplayItem::ScrollFrame(v)
+ },
+ StickyFrame(v) => SpecificDisplayItem::StickyFrame(v),
+ Rectangle(v) => SpecificDisplayItem::Rectangle(v),
+ ClearRectangle => SpecificDisplayItem::ClearRectangle,
+ Line(v) => SpecificDisplayItem::Line(v),
+ Text(v, glyphs) => {
+ push_vec(&mut temp, glyphs);
+ SpecificDisplayItem::Text(v)
+ },
+ Image(v) => SpecificDisplayItem::Image(v),
+ YuvImage(v) => SpecificDisplayItem::YuvImage(v),
+ Border(v) => SpecificDisplayItem::Border(v),
+ BoxShadow(v) => SpecificDisplayItem::BoxShadow(v),
+ Gradient(v) => SpecificDisplayItem::Gradient(v),
+ RadialGradient(v) => SpecificDisplayItem::RadialGradient(v),
+ Iframe(v) => SpecificDisplayItem::Iframe(v),
+ PushStackingContext(v, filters) => {
+ push_vec(&mut temp, filters);
+ SpecificDisplayItem::PushStackingContext(v)
+ },
+ PopStackingContext => SpecificDisplayItem::PopStackingContext,
+ SetGradientStops(stops) => {
+ push_vec(&mut temp, stops);
+ SpecificDisplayItem::SetGradientStops
+ },
+ PushShadow(v) => SpecificDisplayItem::PushShadow(v),
+ PopAllShadows => SpecificDisplayItem::PopAllShadows,
+ },
+ clip_and_scroll: complete.clip_and_scroll,
+ info: complete.info,
+ };
+ serialize_fast(&mut data, &item);
+ // the aux data is serialized after the item, hence the temporary
+ data.extend(temp.drain(..));
}
- if !gradient_stops.is_empty() {
- map.serialize_entry(
- "gradient_stops",
- &self.iter.list.get(gradient_stops).collect::>(),
- )?;
- }
-
- map.end()
+ Ok(BuiltDisplayList {
+ data,
+ descriptor: BuiltDisplayListDescriptor {
+ builder_start_time: 0,
+ builder_finish_time: 1,
+ send_start_time: 0,
+ },
+ })
}
}
@@ -697,7 +783,7 @@ pub struct DisplayListBuilder {
}
impl DisplayListBuilder {
- pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> DisplayListBuilder {
+ pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> Self {
Self::with_capacity(pipeline_id, content_size, 0)
}
@@ -705,7 +791,7 @@ impl DisplayListBuilder {
pipeline_id: PipelineId,
content_size: LayoutSize,
capacity: usize,
- ) -> DisplayListBuilder {
+ ) -> Self {
let start_time = precise_time_ns();
// We start at 1 here, because the root scroll id is always 0.
@@ -762,7 +848,7 @@ impl DisplayListBuilder {
pub fn print_display_list(&mut self) {
let mut temp = BuiltDisplayList::default();
- ::std::mem::swap(&mut temp.data, &mut self.data);
+ mem::swap(&mut temp.data, &mut self.data);
{
let mut iter = BuiltDisplayListIter::new(&temp);
@@ -1137,6 +1223,20 @@ impl DisplayListBuilder {
self.push_item(item, info);
}
+ /// Pushes a linear gradient to be displayed.
+ ///
+ /// The gradient itself is described in the
+ /// `gradient` parameter. It is drawn on
+ /// a "tile" with the dimensions from `tile_size`.
+ /// These tiles are now repeated to the right and
+ /// to the bottom infinitly. If `tile_spacing`
+ /// is not zero spacers with the given dimensions
+ /// are inserted between the tiles as seams.
+ ///
+ /// The origin of the tiles is given in `info.rect.origin`.
+ /// If the gradient should only be displayed once limit
+ /// the `info.rect.size` to a single tile.
+ /// The gradient is only visible within the local clip.
pub fn push_gradient(
&mut self,
info: &LayoutPrimitiveInfo,
@@ -1153,6 +1253,9 @@ impl DisplayListBuilder {
self.push_item(item, info);
}
+ /// Pushes a radial gradient to be displayed.
+ ///
+ /// See [`push_gradient`](#method.push_gradient) for explanation.
pub fn push_radial_gradient(
&mut self,
info: &LayoutPrimitiveInfo,
diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs
index d067d282ca93..b3d0b964fc73 100644
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -52,7 +52,6 @@ pub struct ExternalImageData {
pub enum ImageFormat {
Invalid = 0,
A8 = 1,
- RGB8 = 2,
BGRA8 = 3,
RGBAF32 = 4,
RG8 = 5,
@@ -62,7 +61,6 @@ impl ImageFormat {
pub fn bytes_per_pixel(self) -> u32 {
match self {
ImageFormat::A8 => 1,
- ImageFormat::RGB8 => 3,
ImageFormat::BGRA8 => 4,
ImageFormat::RGBAF32 => 16,
ImageFormat::RG8 => 2,
diff --git a/gfx/webrender_api/src/lib.rs b/gfx/webrender_api/src/lib.rs
index ec701dc7209d..c95f5d1416c4 100644
--- a/gfx/webrender_api/src/lib.rs
+++ b/gfx/webrender_api/src/lib.rs
@@ -15,8 +15,9 @@ extern crate core;
extern crate euclid;
#[cfg(feature = "ipc")]
extern crate ipc_channel;
-#[macro_use]
extern crate serde;
+#[macro_use]
+extern crate serde_derive;
extern crate time;
#[cfg(target_os = "macos")]
diff --git a/gfx/webrender_api/src/units.rs b/gfx/webrender_api/src/units.rs
index cf57cbbe3257..88818c4a71d3 100644
--- a/gfx/webrender_api/src/units.rs
+++ b/gfx/webrender_api/src/units.rs
@@ -12,7 +12,8 @@
//! The terms "layer" and "stacking context" can be used interchangeably
//! in the context of coordinate systems.
-use euclid::{Length, TypedRect, TypedSize2D, TypedTransform3D};
+use app_units::Au;
+use euclid::{Length, TypedRect, TypedScale, TypedSize2D, TypedTransform3D};
use euclid::{TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D};
/// Geometry in the coordinate system of the render target (screen or intermediate
@@ -87,6 +88,12 @@ pub type WorldVector3D = TypedVector3D;
pub struct Tiles;
pub type TileOffset = TypedPoint2D;
+/// Scaling ratio from world pixels to device pixels.
+pub type DevicePixelScale = TypedScale;
+/// Scaling ratio from layer to world. Used for cases where we know the layer
+/// is in world space, or specifically want to treat it this way.
+pub type LayerToWorldScale = TypedScale;
+
pub type LayoutTransform = TypedTransform3D;
pub type LayerTransform = TypedTransform3D;
pub type LayerToScrollTransform = TypedTransform3D;
@@ -95,10 +102,10 @@ pub type LayerToWorldTransform = TypedTransform3D;
pub type WorldToLayerTransform = TypedTransform3D;
pub type ScrollToWorldTransform = TypedTransform3D;
-
-pub fn device_length(value: f32, device_pixel_ratio: f32) -> DeviceIntLength {
- DeviceIntLength::new((value * device_pixel_ratio).round() as i32)
-}
+// Fixed position coordinates, to avoid float precision errors.
+pub type LayerPointAu = TypedPoint2D;
+pub type LayerRectAu = TypedRect;
+pub type LayerSizeAu = TypedSize2D;
pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
ScrollLayerRect::from_untyped(&rect.to_untyped())
diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h
index 6828bd1f9761..2a5af00e9cbf 100644
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -75,8 +75,6 @@ SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat) {
// have it yet (cf. issue #732).
case gfx::SurfaceFormat::B8G8R8A8:
return Some(wr::ImageFormat::BGRA8);
- case gfx::SurfaceFormat::B8G8R8:
- return Some(wr::ImageFormat::RGB8);
case gfx::SurfaceFormat::A8:
return Some(wr::ImageFormat::A8);
case gfx::SurfaceFormat::R8G8:
@@ -95,8 +93,6 @@ ImageFormatToSurfaceFormat(ImageFormat aFormat) {
return gfx::SurfaceFormat::B8G8R8A8;
case ImageFormat::A8:
return gfx::SurfaceFormat::A8;
- case ImageFormat::RGB8:
- return gfx::SurfaceFormat::B8G8R8;
default:
return gfx::SurfaceFormat::UNKNOWN;
}
diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h
index 298d67ab40e0..685c37a8ef69 100644
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -97,7 +97,6 @@ enum class FontRenderMode : uint32_t {
enum class ImageFormat : uint32_t {
Invalid = 0,
A8 = 1,
- RGB8 = 2,
BGRA8 = 3,
RGBAF32 = 4,
RG8 = 5,
diff --git a/ipc/chromium/src/base/process_util_bsd.cc b/ipc/chromium/src/base/process_util_bsd.cc
index 6e004947d2d3..614a5e55116c 100644
--- a/ipc/chromium/src/base/process_util_bsd.cc
+++ b/ipc/chromium/src/base/process_util_bsd.cc
@@ -41,7 +41,7 @@ bool LaunchApp(const std::vector& argv,
// as close-on-exec.
SetAllFDsToCloseOnExec();
- EnvironmentArray vars = BuildEnvironmentArray(options.environ);
+ EnvironmentArray vars = BuildEnvironmentArray(options.env_map);
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
diff --git a/js/moz.build b/js/moz.build
index d1a4acb2d339..e007b38bea50 100644
--- a/js/moz.build
+++ b/js/moz.build
@@ -11,3 +11,9 @@ for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'Slice
with Files('public/TrackedOptimizationInfo.h'):
BUG_COMPONENT = component_jit
+
+with Files("src/**"):
+ SCHEDULES.inclusive += ['jittest', 'jsreftest']
+
+with Files("public/**"):
+ SCHEDULES.inclusive += ['jittest', 'jsreftest']
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 287e0721daf2..609733cb0c1e 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -107,6 +107,7 @@
#include "nsTransitionManager.h"
#include "DetailsFrame.h"
#include "nsThemeConstants.h"
+#include "mozilla/Preferences.h"
#ifdef MOZ_XUL
#include "nsIRootBox.h"
@@ -134,6 +135,7 @@ using namespace mozilla::dom;
// An alias for convenience.
static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
+static const char* kPrefSelectPopupInContent = "dom.select_popup_in_content.enabled";
nsIFrame*
NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
@@ -3234,11 +3236,17 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
// Notify combobox that it should use the listbox as it's popup
comboboxFrame->SetDropDown(listFrame);
- NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(),
- "Ended up with positioned dropdown list somehow.");
+ if (!Preferences::GetBool(kPrefSelectPopupInContent)) {
+ // TODO(kuoe0) Remove this assertion when content-select is shipped.
+ NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(),
+ "Ended up with positioned dropdown list somehow.");
+ }
NS_ASSERTION(!listFrame->IsFloating(),
"Ended up with floating dropdown list somehow.");
+ // child frames of combobox frame
+ nsFrameItems childItems;
+
// Initialize the scroll frame positioned. Note that it is NOT
// initialized as absolutely positioned.
nsContainerFrame* scrolledFrame =
@@ -3246,15 +3254,14 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
comboboxFrame, listStyle, true,
- aItem.mPendingBinding, aFrameItems);
+ aItem.mPendingBinding, childItems);
NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
// Create display and button frames from the combobox's anonymous content.
// The anonymous content is appended to existing anonymous content for this
// element (the scrollbars).
- nsFrameItems childItems;
-
+ //
// nsComboboxControlFrame needs special frame creation behavior for its first
// piece of anonymous content, which means that we can't take the normal
// ProcessChildren path.
@@ -3281,12 +3288,14 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
- // Initialize the additional popup child list which contains the
- // dropdown list frame.
- nsFrameItems popupItems;
- popupItems.AddChild(listFrame);
- comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
- popupItems);
+ if (!Preferences::GetBool(kPrefSelectPopupInContent)) {
+ // Initialize the additional popup child list which contains the
+ // dropdown list frame.
+ nsFrameItems popupItems;
+ popupItems.AddChild(listFrame);
+ comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
+ popupItems);
+ }
aState.mFrameState = historyState;
if (aState.mFrameState) {
@@ -3338,7 +3347,7 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
scrollFrame->Init(aContent, geometricParent, nullptr);
- if (!aBuildCombobox) {
+ if (!aBuildCombobox || Preferences::GetBool(kPrefSelectPopupInContent)) {
aState.AddChild(scrollFrame, aFrameItems, aContent, aParentFrame);
}
diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
index 7707114f1b45..79991fa6e68a 100644
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -57,6 +57,7 @@
#include "FrameLayerBuilder.h"
#include "nsISelectionController.h"
#include "nsISelection.h"
+#include "nsIURIMutator.h"
#include "imgIContainer.h"
#include "imgLoader.h"
@@ -2109,7 +2110,9 @@ nsImageFrame::HandleEvent(nsPresContext* aPresContext,
NS_ENSURE_SUCCESS(rv, rv);
spec += nsPrintfCString("?%d,%d", p.x, p.y);
- rv = uri->SetSpec(spec);
+ rv = NS_MutateURI(uri)
+ .SetSpec(spec)
+ .Finalize(uri);
NS_ENSURE_SUCCESS(rv, rv);
bool clicked = false;
diff --git a/layout/reftests/bidi/reftest.list b/layout/reftests/bidi/reftest.list
index 02bd3282848f..7648b446bc23 100644
--- a/layout/reftests/bidi/reftest.list
+++ b/layout/reftests/bidi/reftest.list
@@ -108,7 +108,7 @@ random-if(winWidget) == 305643-1.html 305643-1-ref.html # depends on windows ver
== 588739-1.html 588739-ref.html
== 588739-2.html 588739-ref.html
== 588739-3.html 588739-ref.html
-fails-if(webrender) == 612843-1.html 612843-1-ref.html
+== 612843-1.html 612843-1-ref.html
== 613149-1a.html 613149-1-ref.html
== 613149-1b.html 613149-1-ref.html
fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,36,2) == 613149-2a.html 613149-2-ref.html
diff --git a/layout/reftests/box-shadow/reftest.list b/layout/reftests/box-shadow/reftest.list
index 3b0ff0d4f745..53d2a5bba748 100644
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -25,7 +25,7 @@ random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html
fuzzy(2,440) fails-if(webrender) == boxshadow-skiprect.html boxshadow-skiprect-ref.html
== boxshadow-opacity.html boxshadow-opacity-ref.html
== boxshadow-color-rounding.html boxshadow-color-rounding-ref.html
-fuzzy-if(webrender,1-1,7284-7284) == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
+== boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
fuzzy(3,500) fuzzy-if(d2d,2,1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
== boxshadow-inset-neg-spread.html about:blank
== boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html
diff --git a/layout/reftests/font-features/reftest.list b/layout/reftests/font-features/reftest.list
index c7a697a14cba..203d8d47100a 100644
--- a/layout/reftests/font-features/reftest.list
+++ b/layout/reftests/font-features/reftest.list
@@ -120,4 +120,4 @@ random-if(cocoaWidget) == subsuper-nofallback.html subsuper-nofallback-ref2.html
== subsuper-fallback-size.html subsuper-fallback-size-ref.html
# GPOS spacing adjustments in vertical mode -- subsetted opentype/cff test font fails to load on Win7
-fails-if(webrender) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1376231-vertical-gpos-adjustments.html 1376231-vertical-gpos-adjustments-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1376231-vertical-gpos-adjustments.html 1376231-vertical-gpos-adjustments-ref.html
diff --git a/layout/reftests/outline/reftest.list b/layout/reftests/outline/reftest.list
index 93844ae0c09c..b3182c34cc8b 100644
--- a/layout/reftests/outline/reftest.list
+++ b/layout/reftests/outline/reftest.list
@@ -1,6 +1,6 @@
fuzzy(2,18) fuzzy-if(webrender,52,1452) == outline-and-box-shadow.html outline-and-box-shadow-ref.html
== outline-and-3d-transform-1a.html outline-and-3d-transform-1-ref.html
-fails-if(webrender) == outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html
+== outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html
fuzzy-if(gtkWidget,136,120) fuzzy-if(Android,255,356) fuzzy-if(d2d,16,96) fuzzy-if(cocoaWidget,255,120) fuzzy-if(winWidget,255,216) == outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html
== outline-dynamic-change-1a.html outline-dynamic-change-1-ref.html
== outline-dynamic-change-1b.html outline-dynamic-change-1-ref.html
diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list
index 2a5fa6178fed..45f80fe9f962 100644
--- a/layout/reftests/transform-3d/reftest.list
+++ b/layout/reftests/transform-3d/reftest.list
@@ -82,7 +82,7 @@ fuzzy(1,10000) == opacity-preserve3d-4.html opacity-preserve3d-4-ref.html
== snap-perspective-1.html snap-perspective-1-ref.html
== mask-layer-1.html mask-layer-ref.html
== mask-layer-2.html mask-layer-ref.html
-fails-if(webrender) == mask-layer-3.html mask-layer-ref.html
+== mask-layer-3.html mask-layer-ref.html
== split-intersect1.html split-intersect1-ref.html
fuzzy(255,150) == split-intersect2.html split-intersect2-ref.html
fuzzy(255,100) fails-if(webrender) == split-non-ortho1.html split-non-ortho1-ref.html
diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css
index 4a3a493f4c74..835d817062b2 100644
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -422,6 +422,13 @@ optgroup:before {
border-inline-start-width: 2px ! important;
}
+@supports -moz-bool-pref("dom.select_popup_in_content.enabled") {
+ *|*::-moz-dropdown-list {
+ -moz-top-layer: top !important;
+ position: absolute !important;
+ }
+}
+
input:disabled,
textarea:disabled,
option:disabled,
diff --git a/media/webrtc/signaling/gtest/MockCall.h b/media/webrtc/signaling/gtest/MockCall.h
index f96add980097..733819205831 100644
--- a/media/webrtc/signaling/gtest/MockCall.h
+++ b/media/webrtc/signaling/gtest/MockCall.h
@@ -70,7 +70,10 @@ public:
return nullptr;
}
- void ReconfigureVideoEncoder(VideoEncoderConfig config) override {}
+ void ReconfigureVideoEncoder(VideoEncoderConfig config) override
+ {
+ mEncoderConfig = config.Copy();
+ }
Stats GetStats() override
{
@@ -82,6 +85,7 @@ public:
virtual ~MockVideoSendStream() {}
+ VideoEncoderConfig mEncoderConfig;
VideoSendStream::Stats mStats;
};
@@ -117,7 +121,8 @@ public:
MockCall()
: mAudioSendConfig(nullptr)
, mVideoReceiveConfig(nullptr)
- , mVideoSendConfig(nullptr) {}
+ , mVideoSendConfig(nullptr)
+ , mCurrentVideoSendStream(nullptr) {}
AudioSendStream* CreateAudioSendStream(const AudioSendStream::Config& config) override
{
@@ -142,13 +147,17 @@ public:
VideoSendStream* CreateVideoSendStream(VideoSendStream::Config config,
VideoEncoderConfig encoder_config) override {
+ MOZ_RELEASE_ASSERT(!mCurrentVideoSendStream);
mVideoSendConfig = config.Copy();
mEncoderConfig = encoder_config.Copy();
- return new MockVideoSendStream;
+ mCurrentVideoSendStream = new MockVideoSendStream;
+ return mCurrentVideoSendStream;
}
void DestroyVideoSendStream(VideoSendStream* send_stream) override
{
+ MOZ_RELEASE_ASSERT(mCurrentVideoSendStream == send_stream);
+ mCurrentVideoSendStream = nullptr;
delete static_cast(send_stream);
}
@@ -208,6 +217,7 @@ public:
VideoSendStream::Config mVideoSendConfig;
VideoEncoderConfig mEncoderConfig;
Call::Stats mStats;
+ MockVideoSendStream* mCurrentVideoSendStream;
};
}
diff --git a/media/webrtc/signaling/gtest/videoconduit_unittests.cpp b/media/webrtc/signaling/gtest/videoconduit_unittests.cpp
index 8f46b620c8fc..8ee30cc36398 100644
--- a/media/webrtc/signaling/gtest/videoconduit_unittests.cpp
+++ b/media/webrtc/signaling/gtest/videoconduit_unittests.cpp
@@ -120,7 +120,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecs)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
// Defaults
std::vector codecs;
@@ -178,7 +177,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsFEC)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
std::vector codecs;
VideoCodecConfig codecConfig(120, "VP8", constraints);
@@ -211,7 +209,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsH264)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
WebrtcGmpPCHandleSetter setter("hi there");
@@ -244,7 +241,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsKeyframeRequestType)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
std::vector codecs;
// PLI should be preferred to FIR
@@ -272,7 +268,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsNack)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
std::vector codecs;
VideoCodecConfig codecConfig(120, "VP8", constraints);
@@ -301,7 +296,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsRemb)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
std::vector codecs;
VideoCodecConfig codecConfig(120, "VP8", constraints);
@@ -330,7 +324,6 @@ TEST_F(VideoConduitTest, TestConfigureReceiveMediaCodecsTmmbr)
{
MediaConduitErrorCode ec;
EncodingConstraints constraints;
- VideoCodecConfig::SimulcastEncoding encoding;
std::vector codecs;
VideoCodecConfig codecConfig(120, "VP8", constraints);
@@ -737,6 +730,366 @@ TEST_F(VideoConduitTest, TestOnSinkWantsChanged)
ASSERT_EQ(mAdapter->mMaxPixelCount, 64000);
}
+TEST_F(VideoConduitTest, TestReconfigureReceiveMediaCodecs)
+{
+ MediaConduitErrorCode ec;
+ EncodingConstraints constraints;
+ VideoCodecConfig::SimulcastEncoding encoding;
+ std::vector codecs;
+
+ WebrtcGmpPCHandleSetter setter("hi there");
+
+ // Defaults
+ VideoCodecConfig codecConfig(120, "VP8", constraints);
+ codecs.push_back(&codecConfig);
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "VP8");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 0);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+
+ // FEC
+ codecs.clear();
+ VideoCodecConfig codecConfigFecFb(120, "VP8", constraints);
+ codecConfigFecFb.mFECFbSet = true;
+ codecs.push_back(&codecConfigFecFb);
+ VideoCodecConfig codecConfigFEC(1, "ulpfec", constraints);
+ codecs.push_back(&codecConfigFEC);
+ VideoCodecConfig codecConfigRED(2, "red", constraints);
+ codecs.push_back(&codecConfigRED);
+
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "VP8");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 0);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, 1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, 2);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+
+ // H264
+ codecs.clear();
+ VideoCodecConfig codecConfigH264(120, "H264", constraints);
+ codecs.push_back(&codecConfigH264);
+
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "H264");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 0);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+
+ // Nack
+ codecs.clear();
+ VideoCodecConfig codecConfigNack(120, "VP8", constraints);
+ codecConfigNack.mNackFbTypes.push_back("");
+ codecs.push_back(&codecConfigNack);
+
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "VP8");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 1000);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+
+ // Remb
+ codecs.clear();
+ VideoCodecConfig codecConfigRemb(120, "VP8", constraints);
+ codecConfigRemb.mRembFbSet = true;
+ codecs.push_back(&codecConfigRemb);
+
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "VP8");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 0);
+ ASSERT_TRUE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+
+ // Tmmbr
+ codecs.clear();
+ VideoCodecConfig codecConfigTmmbr(120, "VP8", constraints);
+ codecConfigTmmbr.mCcmFbTypes.push_back("tmmbr");
+ codecs.push_back(&codecConfigTmmbr);
+
+ ec = mVideoConduit->ConfigureRecvMediaCodecs(codecs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders.size(), 1U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_type, 120);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.decoders[0].payload_name, "VP8");
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.local_ssrc, 0U);
+ ASSERT_NE(mCall->mVideoReceiveConfig.rtp.remote_ssrc, 0U);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.nack.rtp_history_ms, 0);
+ ASSERT_FALSE(mCall->mVideoReceiveConfig.rtp.remb);
+ ASSERT_TRUE(mCall->mVideoReceiveConfig.rtp.tmmbr);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.keyframe_method, webrtc::kKeyFrameReqPliRtcp);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.ulpfec_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.ulpfec.red_rtx_payload_type, -1);
+ ASSERT_EQ(mCall->mVideoReceiveConfig.rtp.rtx.size(), 0U);
+}
+
+TEST_F(VideoConduitTest, TestReconfigureSendMediaCodec)
+{
+ MediaConduitErrorCode ec;
+ EncodingConstraints constraints;
+ VideoCodecConfig::SimulcastEncoding encoding;
+ std::vector videoStreams;
+
+ WebrtcGmpPCHandleSetter setter("hi there");
+
+ VideoCodecConfig codecConfig(120, "VP8", constraints);
+ codecConfig.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfig);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+
+ // Defaults
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfig);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_name, "VP8");
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_type, 120);
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.internal_source, false);
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.full_overuse_time, false);
+ ASSERT_NE(mCall->mVideoSendConfig.encoder_settings.encoder, nullptr);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.max_packet_size, kVideoMtu);
+ ASSERT_EQ(mCall->mEncoderConfig.content_type,
+ VideoEncoderConfig::ContentType::kRealtimeVideo);
+ ASSERT_EQ(mCall->mEncoderConfig.min_transmit_bitrate_bps, 0);
+ ASSERT_EQ(mCall->mEncoderConfig.max_bitrate_bps, 0);
+ ASSERT_EQ(mCall->mEncoderConfig.number_of_streams, 1U);
+ ASSERT_EQ(mCall->mEncoderConfig.resolution_divisor, 1);
+ mVideoConduit->StopTransmitting();
+
+ // FEC
+ VideoCodecConfig codecConfigFEC(120, "VP8", constraints);
+ codecConfigFEC.mSimulcastEncodings.push_back(encoding);
+ codecConfigFEC.mFECFbSet = true;
+ codecConfigFEC.mNackFbTypes.push_back("");
+ codecConfigFEC.mULPFECPayloadType = 1;
+ codecConfigFEC.mREDPayloadType = 2;
+ codecConfigFEC.mREDRTXPayloadType = 3;
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigFEC);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.ulpfec.ulpfec_payload_type, codecConfigFEC.mULPFECPayloadType);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.ulpfec.red_payload_type, codecConfigFEC.mREDPayloadType);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.ulpfec.red_rtx_payload_type, codecConfigFEC.mREDRTXPayloadType);
+ mVideoConduit->StopTransmitting();
+
+ // H264
+ VideoCodecConfig codecConfigH264(120, "H264", constraints);
+ codecConfigH264.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigH264);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_name, "H264");
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_type, 120);
+ mVideoConduit->StopTransmitting();
+
+ // TIAS
+ VideoCodecConfig codecConfigTias(120, "VP8", constraints);
+ codecConfigTias.mSimulcastEncodings.push_back(encoding);
+ codecConfigTias.mTias = 1000000;
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigTias);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ SendVideoFrame(1280, 720, 1);
+
+ videoStreams = mCall->mEncoderConfig.video_stream_factory->CreateEncoderStreams(1280, 720, mCall->mEncoderConfig);
+ ASSERT_EQ(videoStreams.size(), 1U);
+ ASSERT_EQ(videoStreams[0].min_bitrate_bps, 600000);
+ ASSERT_EQ(videoStreams[0].target_bitrate_bps, 800000);
+ ASSERT_EQ(videoStreams[0].max_bitrate_bps, 1000000);
+ mVideoConduit->StopTransmitting();
+
+ // MaxBr
+ VideoCodecConfig codecConfigMaxBr(120, "VP8", constraints);
+ encoding.constraints.maxBr = 50000;
+ codecConfigMaxBr.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigMaxBr);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ SendVideoFrame(1280, 720, 1);
+ videoStreams = mCall->mEncoderConfig.video_stream_factory->CreateEncoderStreams(1280, 720, mCall->mEncoderConfig);
+ ASSERT_EQ(videoStreams.size(), 1U);
+ ASSERT_LE(videoStreams[0].min_bitrate_bps, 50000);
+ ASSERT_LE(videoStreams[0].target_bitrate_bps, 50000);
+ ASSERT_EQ(videoStreams[0].max_bitrate_bps, 50000);
+ mVideoConduit->StopTransmitting();
+
+ // MaxFs
+ VideoCodecConfig codecConfigMaxFs(120, "VP8", constraints);
+ codecConfigMaxFs.mEncodingConstraints.maxFs = 3600;
+ encoding.constraints.maxBr = 0;
+ codecConfigMaxFs.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigMaxFs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+
+ UniquePtr sink(new MockVideoSink());
+ rtc::VideoSinkWants wants;
+ mVideoConduit->AddOrUpdateSink(sink.get(), wants);
+
+ mVideoConduit->StartTransmitting();
+ SendVideoFrame(1280, 720, 1);
+ ASSERT_EQ(sink->mVideoFrame.width(), 1280);
+ ASSERT_EQ(sink->mVideoFrame.height(), 720);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 1000U);
+
+ SendVideoFrame(640, 360, 2);
+ ASSERT_EQ(sink->mVideoFrame.width(), 640);
+ ASSERT_EQ(sink->mVideoFrame.height(), 360);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 2000U);
+
+ SendVideoFrame(1920, 1280, 3);
+ ASSERT_EQ(sink->mVideoFrame.width(), 960);
+ ASSERT_EQ(sink->mVideoFrame.height(), 640);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 3000U);
+ mVideoConduit->StopTransmitting();
+}
+
+TEST_F(VideoConduitTest, TestReconfigureSendMediaCodecWhileTransmitting)
+{
+ MediaConduitErrorCode ec;
+ EncodingConstraints constraints;
+ VideoCodecConfig::SimulcastEncoding encoding;
+ std::vector videoStreams;
+
+ WebrtcGmpPCHandleSetter setter("hi there");
+
+ VideoCodecConfig codecConfig(120, "VP8", constraints);
+ codecConfig.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfig);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+
+ // Defaults
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfig);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ mVideoConduit->StartTransmitting();
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_name, "VP8");
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.payload_type, 120);
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.internal_source, false);
+ ASSERT_EQ(mCall->mVideoSendConfig.encoder_settings.full_overuse_time, false);
+ ASSERT_NE(mCall->mVideoSendConfig.encoder_settings.encoder, nullptr);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.rtcp_mode, webrtc::RtcpMode::kCompound);
+ ASSERT_EQ(mCall->mVideoSendConfig.rtp.max_packet_size, kVideoMtu);
+ ASSERT_EQ(mCall->mEncoderConfig.content_type,
+ VideoEncoderConfig::ContentType::kRealtimeVideo);
+ ASSERT_EQ(mCall->mEncoderConfig.min_transmit_bitrate_bps, 0);
+ ASSERT_EQ(mCall->mEncoderConfig.max_bitrate_bps, 0);
+ ASSERT_EQ(mCall->mEncoderConfig.number_of_streams, 1U);
+ ASSERT_EQ(mCall->mEncoderConfig.resolution_divisor, 1);
+
+ // Changing these parameters should not require a call to StartTransmitting
+ // for the changes to take effect.
+
+ // TIAS
+ VideoCodecConfig codecConfigTias(120, "VP8", constraints);
+ codecConfigTias.mSimulcastEncodings.push_back(encoding);
+ codecConfigTias.mTias = 1000000;
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigTias);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ SendVideoFrame(1280, 720, 1);
+
+ videoStreams = mCall->mCurrentVideoSendStream->mEncoderConfig.video_stream_factory->CreateEncoderStreams(1280, 720, mCall->mCurrentVideoSendStream->mEncoderConfig);
+ ASSERT_EQ(videoStreams.size(), 1U);
+ ASSERT_EQ(videoStreams[0].min_bitrate_bps, 600000);
+ ASSERT_EQ(videoStreams[0].target_bitrate_bps, 800000);
+ ASSERT_EQ(videoStreams[0].max_bitrate_bps, 1000000);
+
+ // MaxBr
+ VideoCodecConfig codecConfigMaxBr(120, "VP8", constraints);
+ encoding.constraints.maxBr = 50000;
+ codecConfigMaxBr.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigMaxBr);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+ SendVideoFrame(1280, 720, 1);
+ videoStreams = mCall->mCurrentVideoSendStream->mEncoderConfig.video_stream_factory->CreateEncoderStreams(1280, 720, mCall->mCurrentVideoSendStream->mEncoderConfig);
+ ASSERT_EQ(videoStreams.size(), 1U);
+ ASSERT_LE(videoStreams[0].min_bitrate_bps, 50000);
+ ASSERT_LE(videoStreams[0].target_bitrate_bps, 50000);
+ ASSERT_EQ(videoStreams[0].max_bitrate_bps, 50000);
+
+ // MaxFs
+ VideoCodecConfig codecConfigMaxFs(120, "VP8", constraints);
+ codecConfigMaxFs.mEncodingConstraints.maxFs = 3600;
+ encoding.constraints.maxBr = 0;
+ codecConfigMaxFs.mSimulcastEncodings.push_back(encoding);
+ ec = mVideoConduit->ConfigureSendMediaCodec(&codecConfigMaxFs);
+ ASSERT_EQ(ec, kMediaConduitNoError);
+
+ UniquePtr sink(new MockVideoSink());
+ rtc::VideoSinkWants wants;
+ mVideoConduit->AddOrUpdateSink(sink.get(), wants);
+
+ SendVideoFrame(1280, 720, 1);
+ ASSERT_EQ(sink->mVideoFrame.width(), 1280);
+ ASSERT_EQ(sink->mVideoFrame.height(), 720);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 1000U);
+
+ SendVideoFrame(640, 360, 2);
+ ASSERT_EQ(sink->mVideoFrame.width(), 640);
+ ASSERT_EQ(sink->mVideoFrame.height(), 360);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 2000U);
+
+ SendVideoFrame(1920, 1280, 3);
+ ASSERT_EQ(sink->mVideoFrame.width(), 960);
+ ASSERT_EQ(sink->mVideoFrame.height(), 640);
+ ASSERT_EQ(sink->mVideoFrame.timestamp_us(), 3000U);
+
+ mVideoConduit->StopTransmitting();
+}
+
TEST_F(VideoConduitTest, TestVideoEncode)
{
MediaConduitErrorCode ec;
diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
index 17f05a49169a..fe08cd66a47a 100644
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -808,6 +808,11 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
// Recreating on PayloadType change may be overkill, but is safe.
if (mSendStream) {
if (!RequiresNewSendStream(*codecConfig)) {
+ {
+ MutexAutoLock lock(mCodecMutex);
+ mCurSendCodecConfig->mEncodingConstraints = codecConfig->mEncodingConstraints;
+ mCurSendCodecConfig->mSimulcastEncodings = codecConfig->mSimulcastEncodings;
+ }
mSendStream->ReconfigureVideoEncoder(mEncoderConfig.CopyConfig());
return kMediaConduitNoError;
}
@@ -1398,6 +1403,11 @@ WebrtcVideoConduit::ConfigureRecvMediaCodecs(
mRecvStreamConfig.rtp.ulpfec.ulpfec_payload_type = ulpfec_payload_type;
mRecvStreamConfig.rtp.ulpfec.red_payload_type = red_payload_type;
mRecvStreamConfig.rtp.ulpfec.red_rtx_payload_type = -1;
+ } else {
+ // Reset to defaults
+ mRecvStreamConfig.rtp.ulpfec.ulpfec_payload_type = -1;
+ mRecvStreamConfig.rtp.ulpfec.red_payload_type = -1;
+ mRecvStreamConfig.rtp.ulpfec.red_rtx_payload_type = -1;
}
// SetRemoteSSRC should have populated this already
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 10b7611fb275..29a8dcc0c823 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1336,6 +1336,8 @@ pref("dom.forms.select.customstyling", false);
pref("dom.forms.select.customstyling", true);
#endif
pref("dom.select_popup_in_parent.enabled", false);
+// Bug 1421229 - content-select
+pref("dom.select_popup_in_content.enabled", false);
// Enable Directory API. By default, disabled.
pref("dom.input.dirpicker", false);
@@ -5391,7 +5393,7 @@ pref("urlclassifier.trackingTable", "test-track-simple,base-track-digest256");
pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
// These tables will never trigger a gethash call.
-pref("urlclassifier.disallow_completions", "test-malware-simple,test-harmful-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,goog-downloadwhite-digest256,base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,except-flashinfobar-digest256");
+pref("urlclassifier.disallow_completions", "test-malware-simple,test-harmful-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,goog-downloadwhite-digest256,base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,except-flashinfobar-digest256,goog-passwordwhite-proto");
// Number of random entries to send with a gethash request
pref("urlclassifier.gethashnoise", 4);
diff --git a/moz.build b/moz.build
index b498c26ce64e..c7aef870f62e 100644
--- a/moz.build
+++ b/moz.build
@@ -55,6 +55,18 @@ with Files('**/Makefile.in'):
BUG_COMPONENT = ('Core', 'Build Config')
FINAL = True
+with Files("**/*.js"):
+ SCHEDULES.inclusive += ['test-verify']
+
+with Files("**/*.html"):
+ SCHEDULES.inclusive += ['test-verify']
+
+with Files("**/*.xhtml"):
+ SCHEDULES.inclusive += ['test-verify']
+
+with Files("**/*.xul"):
+ SCHEDULES.inclusive += ['test-verify']
+
CONFIGURE_SUBST_FILES += [
'config/autoconf.mk',
'config/emptyvars.mk',
diff --git a/netwerk/base/nsINetUtil.idl b/netwerk/base/nsINetUtil.idl
index 800a9ae905f5..0a7a7d348b3f 100644
--- a/netwerk/base/nsINetUtil.idl
+++ b/netwerk/base/nsINetUtil.idl
@@ -227,4 +227,11 @@ interface nsINetUtil : nsISupports
* ReferrerPolicy.h for details)
*/
unsigned long parseAttributePolicyString(in AString aPolicyString);
+
+
+ /**
+ * This is a void method that is C++ implemented and always
+ * returns NS_ERROR_NOT_IMPLEMENTED. To be used for testing.
+ */
+ void notImplemented();
};
diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp
index 6308dc376bbf..e465a8070195 100644
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -1909,5 +1909,11 @@ nsIOService::BlockToplevelDataUriNavigations()
return sBlockToplevelDataUriNavigations;
}
+NS_IMETHODIMP
+nsIOService::NotImplemented()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/base/nsIURIMutator.idl b/netwerk/base/nsIURIMutator.idl
index 0211adea8818..146374a3f6de 100644
--- a/netwerk/base/nsIURIMutator.idl
+++ b/netwerk/base/nsIURIMutator.idl
@@ -66,8 +66,16 @@ protected:
MOZ_MUST_USE nsresult InitFromSpec(const nsACString& aSpec)
{
- RefPtr uri = new T();
- nsresult rv = uri->SetSpec(aSpec);
+ nsresult rv = NS_OK;
+ RefPtr uri;
+ if (mURI) {
+ // This only works because all other Init methods create a new object
+ mURI.swap(uri);
+ } else {
+ uri = new T();
+ }
+
+ rv = uri->SetSpec(aSpec);
if (NS_FAILED(rv)) {
return rv;
}
diff --git a/netwerk/base/nsIURIMutatorUtils.cpp b/netwerk/base/nsIURIMutatorUtils.cpp
index 20b0c8c46def..ad76d74f81a6 100644
--- a/netwerk/base/nsIURIMutatorUtils.cpp
+++ b/netwerk/base/nsIURIMutatorUtils.cpp
@@ -19,4 +19,5 @@ NS_MutateURI::NS_MutateURI(nsIURI* aURI)
NS_MutateURI::NS_MutateURI(const char * aContractID)
{
mMutator = do_CreateInstance(aContractID, &mStatus);
+ MOZ_ASSERT(NS_SUCCEEDED(mStatus), "Called with wrong aContractID");
}
diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp
index 9ae47c805236..14e411b3c77d 100644
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -22,6 +22,7 @@
#include "SerializedLoadContext.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsIPrompt.h"
+#include "nsIURIMutator.h"
using mozilla::dom::ContentChild;
using namespace mozilla::ipc;
@@ -328,7 +329,10 @@ FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
nsCOMPtr uri = DeserializeURI(aURI);
nsresult rv = uri->GetSpec(spec);
if (NS_SUCCEEDED(rv)) {
- rv = nsBaseChannel::URI()->SetSpec(spec);
+ // Changes nsBaseChannel::URI()
+ rv = NS_MutateURI(mURI)
+ .SetSpec(spec)
+ .Finalize(mURI);
if (NS_FAILED(rv)) {
Cancel(rv);
}
diff --git a/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp
index 6deeea99f4bf..58119ed5d2d7 100644
--- a/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp
+++ b/netwerk/protocol/wyciwyg/nsWyciwygProtocolHandler.cpp
@@ -13,6 +13,7 @@
#include "plstr.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
+#include "nsIURIMutator.h"
#include "mozilla/net/NeckoChild.h"
@@ -65,17 +66,9 @@ nsWyciwygProtocolHandler::NewURI(const nsACString &aSpec,
nsIURI *aBaseURI,
nsIURI **result)
{
- nsresult rv;
-
- nsCOMPtr url = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = url->SetSpec(aSpec);
- NS_ENSURE_SUCCESS(rv, rv);
-
- url.forget(result);
-
- return rv;
+ return NS_MutateURI(NS_SIMPLEURIMUTATOR_CONTRACTID)
+ .SetSpec(aSpec)
+ .Finalize(result);
}
NS_IMETHODIMP
diff --git a/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js
index e6669008b6f3..6d6b2fcb2dd2 100644
--- a/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js
+++ b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js
@@ -33,10 +33,10 @@ CustomProtocolHandler.prototype = {
Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE;
},
newURI: function(aSpec, aOriginCharset, aBaseURI) {
- var uri = Cc["@mozilla.org/network/standard-url;1"].
- createInstance(Ci.nsIURI);
- uri.spec = aSpec;
- return uri;
+ return Cc["@mozilla.org/network/standard-url-mutator;1"]
+ .createInstance(Ci.nsIURIMutator)
+ .setSpec(aSpec)
+ .finalize()
},
newChannel2: function(aURI, aLoadInfo) {
return new CustomChannel(aURI, aLoadInfo);
diff --git a/netwerk/test/unit/test_1351443-missing-NewChannel2.js b/netwerk/test/unit/test_1351443-missing-NewChannel2.js
index 0f024a4ebe11..3c4f001e7fc7 100644
--- a/netwerk/test/unit/test_1351443-missing-NewChannel2.js
+++ b/netwerk/test/unit/test_1351443-missing-NewChannel2.js
@@ -13,9 +13,10 @@ var contentSecManager = Cc["@mozilla.org/contentsecuritymanager;1"]
.getService(Ci.nsIContentSecurityManager);
function ProtocolHandler() {
- this.uri = Cc["@mozilla.org/network/simple-uri;1"].
- createInstance(Ci.nsIURI);
- this.uri.spec = this.scheme + ":dummy";
+ this.uri = Cc["@mozilla.org/network/simple-uri-mutator;1"]
+ .createInstance(Ci.nsIURIMutator)
+ .setSpec(this.scheme + ":dummy")
+ .finalize();
this.uri.QueryInterface(Ci.nsIMutable).mutable = false;
}
diff --git a/netwerk/test/unit/test_URIs.js b/netwerk/test/unit/test_URIs.js
index 5f08f5cf4be3..a9a0af66dd41 100644
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -485,8 +485,11 @@ function do_test_mutate_ref(aTest, aSuffix) {
var specWithSuffix = aTest.spec + aSuffix;
do_info("testing that setting spec to " +
specWithSuffix + " and then clearing ref does what we expect");
- testURI.spec = specWithSuffix;
- testURI.ref = "";
+
+ testURI = testURI.mutate()
+ .setSpec(specWithSuffix)
+ .setRef("")
+ .finalize();
do_check_uri_eq(testURI, refURIWithoutSuffix);
do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
diff --git a/netwerk/test/unit/test_URIs2.js b/netwerk/test/unit/test_URIs2.js
index 7bd9ad96f997..76115eaee594 100644
--- a/netwerk/test/unit/test_URIs2.js
+++ b/netwerk/test/unit/test_URIs2.js
@@ -587,8 +587,10 @@ function do_test_mutate_ref(aTest, aSuffix) {
var specWithSuffix = aTest.spec + aSuffix;
do_info("testing that setting spec to " +
specWithSuffix + " and then clearing ref does what we expect");
- testURI.spec = specWithSuffix;
- testURI.ref = "";
+ testURI = testURI.mutate()
+ .setSpec(specWithSuffix)
+ .setRef("")
+ .finalize();
do_check_uri_eq(testURI, refURIWithoutSuffix);
do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
diff --git a/netwerk/test/unit/test_bug1177909.js b/netwerk/test/unit/test_bug1177909.js
index e6643c61c15e..5d1f0c63b163 100644
--- a/netwerk/test/unit/test_bug1177909.js
+++ b/netwerk/test/unit/test_bug1177909.js
@@ -99,10 +99,13 @@ add_task(async function testSocksProxy() {
add_task(async function testDirectProxy() {
// Do what |WebSocketChannel::AsyncOpen| do, but do not prefer https proxy.
- let proxyURI = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURI);
- proxyURI.spec = "wss://ws.mozilla.org/";
- let uri = proxyURI.clone();
- uri.scheme = "https";
+ let proxyURI = Cc["@mozilla.org/network/standard-url-mutator;1"]
+ .createInstance(Ci.nsIURIMutator)
+ .setSpec("wss://ws.mozilla.org/")
+ .finalize();
+ let uri = proxyURI.mutate()
+ .setScheme("https")
+ .finalize();
let ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
@@ -122,10 +125,13 @@ add_task(async function testDirectProxy() {
add_task(async function testWebSocketProxy() {
// Do what |WebSocketChannel::AsyncOpen| do
- let proxyURI = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURI);
- proxyURI.spec = "wss://ws.mozilla.org/";
- let uri = proxyURI.clone();
- uri.scheme = "https";
+ let proxyURI = Cc["@mozilla.org/network/standard-url-mutator;1"]
+ .createInstance(Ci.nsIURIMutator)
+ .setSpec("wss://ws.mozilla.org/")
+ .finalize();
+ let uri = proxyURI.mutate()
+ .setScheme("https")
+ .finalize();
let proxyFlags = Ci.nsIProtocolProxyService.RESOLVE_PREFER_HTTPS_PROXY |
Ci.nsIProtocolProxyService.RESOLVE_ALWAYS_TUNNEL;
diff --git a/netwerk/test/unit/test_bug261425.js b/netwerk/test/unit/test_bug261425.js
index 1aa6d4bf96e0..a43a0f2fe176 100644
--- a/netwerk/test/unit/test_bug261425.js
+++ b/netwerk/test/unit/test_bug261425.js
@@ -6,7 +6,7 @@ function run_test() {
var success = false;
try {
- newURI.spec = "http: //foo.com";
+ newURI = newURI.mutate().setSpec("http: //foo.com").finalize();
}
catch (e) {
success = e.result == Cr.NS_ERROR_MALFORMED_URI;
diff --git a/netwerk/test/unit/test_bug412457.js b/netwerk/test/unit/test_bug412457.js
index 7f9e6dd98da2..4343479126bc 100644
--- a/netwerk/test/unit/test_bug412457.js
+++ b/netwerk/test/unit/test_bug412457.js
@@ -7,38 +7,38 @@ function run_test() {
Assert.equal(newURI.asciiHost, "xn--kkr.com");
// escaped UTF8
- newURI.spec = "http://%e5%8d%81.com";
+ newURI = newURI.mutate().setSpec("http://%e5%8d%81.com").finalize();
Assert.equal(newURI.asciiHost, "xn--kkr.com");
// There should be only allowed characters in hostname after
// unescaping and attempting to apply IDNA. "\x80" is illegal in
// UTF-8, so IDNA fails, and 0x80 is illegal in DNS too.
- Assert.throws(() => { newURI.spec = "http://%80.com"; }, "illegal UTF character");
+ Assert.throws(() => { newURI = newURI.mutate().setSpec("http://%80.com").finalize(); }, "illegal UTF character");
// test parsing URL with all possible host terminators
- newURI.spec = "http://example.com?foo";
+ newURI = newURI.mutate().setSpec("http://example.com?foo").finalize();
Assert.equal(newURI.asciiHost, "example.com");
- newURI.spec = "http://example.com#foo";
+ newURI = newURI.mutate().setSpec("http://example.com#foo").finalize();
Assert.equal(newURI.asciiHost, "example.com");
- newURI.spec = "http://example.com:80";
+ newURI = newURI.mutate().setSpec("http://example.com:80").finalize();
Assert.equal(newURI.asciiHost, "example.com");
- newURI.spec = "http://example.com/foo";
+ newURI = newURI.mutate().setSpec("http://example.com/foo").finalize();
Assert.equal(newURI.asciiHost, "example.com");
// Characters that are invalid in the host, shouldn't be decoded.
- newURI.spec = "http://example.com%3ffoo";
+ newURI = newURI.mutate().setSpec("http://example.com%3ffoo").finalize();
Assert.equal(newURI.asciiHost, "example.com%3ffoo");
- newURI.spec = "http://example.com%23foo";
+ newURI = newURI.mutate().setSpec("http://example.com%23foo").finalize();
Assert.equal(newURI.asciiHost, "example.com%23foo");
- newURI.spec = "http://example.com%3bfoo";
+ newURI = newURI.mutate().setSpec("http://example.com%3bfoo").finalize();
Assert.equal(newURI.asciiHost, "example.com%3bfoo");
- newURI.spec = "http://example.com%3a80";
+ newURI = newURI.mutate().setSpec("http://example.com%3a80").finalize();
Assert.equal(newURI.asciiHost, "example.com%3a80");
- newURI.spec = "http://example.com%2ffoo";
+ newURI = newURI.mutate().setSpec("http://example.com%2ffoo").finalize();
Assert.equal(newURI.asciiHost, "example.com%2ffoo");
- newURI.spec = "http://example.com%00";
+ newURI = newURI.mutate().setSpec("http://example.com%00").finalize();
Assert.equal(newURI.asciiHost, "example.com%00");
}
\ No newline at end of file
diff --git a/netwerk/test/unit/test_bug429347.js b/netwerk/test/unit/test_bug429347.js
index b6244c86562a..df231e5737a9 100644
--- a/netwerk/test/unit/test_bug429347.js
+++ b/netwerk/test/unit/test_bug429347.js
@@ -6,33 +6,33 @@ function run_test() {
var uri2 = ios.newURI("http://example.com/#bar");
Assert.ok(uri1.equals(uri2));
- uri1.spec = "http://example.com?bar";
- uri2.spec = "http://example.com/?bar";
+ uri1 = uri1.mutate().setSpec("http://example.com?bar").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/?bar").finalize();
Assert.ok(uri1.equals(uri2));
// see https://bugzilla.mozilla.org/show_bug.cgi?id=665706
// ";" is not parsed as special anymore and thus ends up
// in the authority component (see RFC 3986)
- uri1.spec = "http://example.com;bar";
- uri2.spec = "http://example.com/;bar";
+ uri1 = uri1.mutate().setSpec("http://example.com;bar").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/;bar").finalize();
Assert.ok(!uri1.equals(uri2));
- uri1.spec = "http://example.com#";
- uri2.spec = "http://example.com/#";
+ uri1 = uri1.mutate().setSpec("http://example.com#").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/#").finalize();
Assert.ok(uri1.equals(uri2));
- uri1.spec = "http://example.com?";
- uri2.spec = "http://example.com/?";
+ uri1 = uri1.mutate().setSpec("http://example.com?").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/?").finalize();
Assert.ok(uri1.equals(uri2));
// see https://bugzilla.mozilla.org/show_bug.cgi?id=665706
// ";" is not parsed as special anymore and thus ends up
// in the authority component (see RFC 3986)
- uri1.spec = "http://example.com;";
- uri2.spec = "http://example.com/;";
+ uri1 = uri1.mutate().setSpec("http://example.com;").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/;").finalize();
Assert.ok(!uri1.equals(uri2));
- uri1.spec = "http://example.com";
- uri2.spec = "http://example.com/";
+ uri1 = uri1.mutate().setSpec("http://example.com").finalize();
+ uri2 = uri2.mutate().setSpec("http://example.com/").finalize();
Assert.ok(uri1.equals(uri2));
}
diff --git a/netwerk/test/unit/test_bug479485.js b/netwerk/test/unit/test_bug479485.js
index f8b018059d98..9791aaef46e5 100644
--- a/netwerk/test/unit/test_bug479485.js
+++ b/netwerk/test/unit/test_bug479485.js
@@ -19,7 +19,7 @@ function run_test() {
exception_threw = false;
newURI = ios.newURI("http://foo.com");
try {
- newURI.spec = "http://foo.com"+port;
+ newURI = newURI.mutate().setSpec("http://foo.com"+port).finalize();
}
catch (e) {
exception_threw = e.result == Cr.NS_ERROR_MALFORMED_URI;
diff --git a/netwerk/test/unit/test_bug660066.js b/netwerk/test/unit/test_bug660066.js
index 91856f7aafeb..635928d9515b 100644
--- a/netwerk/test/unit/test_bug660066.js
+++ b/netwerk/test/unit/test_bug660066.js
@@ -32,7 +32,7 @@ function run_test()
do_check_uri_neq(simpleURI, fileDataURI);
do_info("Changing the nsSimpleURI spec to match the nsFileDataURI");
- simpleURI.spec = BLOBURI_SPEC;
+ simpleURI = simpleURI.mutate().setSpec(BLOBURI_SPEC).finalize();
do_info("Verifying that .spec matches");
Assert.equal(simpleURI.spec, fileDataURI.spec);
diff --git a/netwerk/test/unit/test_bug894586.js b/netwerk/test/unit/test_bug894586.js
index eef9b1470f4e..b02ebfa11c21 100644
--- a/netwerk/test/unit/test_bug894586.js
+++ b/netwerk/test/unit/test_bug894586.js
@@ -11,9 +11,10 @@ var contentSecManager = Cc["@mozilla.org/contentsecuritymanager;1"]
.getService(Ci.nsIContentSecurityManager);
function ProtocolHandler() {
- this.uri = Cc["@mozilla.org/network/simple-uri;1"].
- createInstance(Ci.nsIURI);
- this.uri.spec = this.scheme + ":dummy";
+ this.uri = Cc["@mozilla.org/network/simple-uri-mutator;1"]
+ .createInstance(Ci.nsIURIMutator)
+ .setSpec(this.scheme + ":dummy")
+ .finalize();
this.uri.QueryInterface(Ci.nsIMutable).mutable = false;
}
diff --git a/netwerk/test/unit/test_protocolproxyservice.js b/netwerk/test/unit/test_protocolproxyservice.js
index 05054f753d73..02b7d6e54eae 100644
--- a/netwerk/test/unit/test_protocolproxyservice.js
+++ b/netwerk/test/unit/test_protocolproxyservice.js
@@ -49,10 +49,10 @@ TestProtocolHandler.prototype = {
Components.interfaces.nsIProtocolHandler.ALLOWS_PROXY |
Components.interfaces.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD,
newURI: function(spec, originCharset, baseURI) {
- var uri = Components.classes["@mozilla.org/network/simple-uri;1"]
- .createInstance(Components.interfaces.nsIURI);
- uri.spec = spec;
- return uri;
+ return Components.classes["@mozilla.org/network/simple-uri-mutator;1"]
+ .createInstance(Components.interfaces.nsIURIMutator)
+ .setSpec(spec)
+ .finalize();
},
newChannel2: function(uri, aLoadInfo) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
diff --git a/netwerk/test/unit/test_standardurl.js b/netwerk/test/unit/test_standardurl.js
index 838fea88ae27..ea764e1bda58 100644
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -218,8 +218,8 @@ add_test(function test_ipv6_fail()
add_test(function test_clearedSpec()
{
var url = stringToURL("http://example.com/path");
- Assert.throws(() => { url.spec = "http: example"; }, "set bad spec");
- Assert.throws(() => { url.spec = ""; }, "set empty spec");
+ Assert.throws(() => { url = url.mutate().setSpec("http: example").finalize(); }, "set bad spec");
+ Assert.throws(() => { url = url.mutate().setSpec("").finalize(); }, "set empty spec");
Assert.equal(url.spec, "http://example.com/path");
url.host = "allizom.org";
@@ -359,9 +359,11 @@ add_test(function test_trim_C0_and_space()
{
var url = stringToURL("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f http://example.com/ \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ");
Assert.equal(url.spec, "http://example.com/");
- url.spec = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f http://test.com/ \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ";
+ url = url.mutate()
+ .setSpec("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f http://test.com/ \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ")
+ .finalize();
Assert.equal(url.spec, "http://test.com/");
- Assert.throws(() => { url.spec = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 "; }, "set empty spec");
+ Assert.throws(() => { url = url.mutate().setSpec("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 ").finalize(); }, "set empty spec");
run_next_test();
});
diff --git a/netwerk/test/unit/test_standardurl_port.js b/netwerk/test/unit/test_standardurl_port.js
index e7cb1dca5d80..a86f13132923 100644
--- a/netwerk/test/unit/test_standardurl_port.js
+++ b/netwerk/test/unit/test_standardurl_port.js
@@ -23,9 +23,9 @@ function run_test() {
Assert.ok(!/80/.test(httpURI.spec));
// URL parsers shouldn't set ports to default value (bug 407538)
- httpURI.spec = "http://foo.com:81";
+ httpURI = httpURI.mutate().setSpec("http://foo.com:81").finalize();
Assert.equal(81, httpURI.port);
- httpURI.spec = "http://foo.com:80";
+ httpURI = httpURI.mutate().setSpec("http://foo.com:80").finalize();
Assert.equal(-1, httpURI.port);
Assert.ok(!/80/.test(httpURI.spec));
diff --git a/python/mozbuild/mozbuild/action/langpack_manifest.py b/python/mozbuild/mozbuild/action/langpack_manifest.py
index 3ca0395709b9..39d333109b32 100644
--- a/python/mozbuild/mozbuild/action/langpack_manifest.py
+++ b/python/mozbuild/mozbuild/action/langpack_manifest.py
@@ -51,6 +51,9 @@ pushlog_api_url = "{0}/json-rev/{1}"
###
def get_dt_from_hg(path):
with mozversioncontrol.get_repository_object(path=path) as repo:
+ phase = repo._run_in_client(["log", "-r", ".", "-T" "{phase}"])
+ if phase.strip() != "public":
+ return datetime.datetime.utcnow()
repo_url = repo._run_in_client(["paths", "default"])
repo_url = repo_url.strip().replace("ssh://", "https://")
repo_url = repo_url.replace("hg://", "https://")
diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py
index 60a9a93bb2f2..ab78f1e0045b 100644
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -804,7 +804,7 @@ class Schedules(object):
self._inclusive = TypedList(Enum(*schedules.INCLUSIVE_COMPONENTS))()
self._exclusive = ImmutableStrictOrderingOnAppendList(schedules.EXCLUSIVE_COMPONENTS)
- # inclusive is mutable cannot be assigned to (+= only)
+ # inclusive is mutable but cannot be assigned to (+= only)
@property
def inclusive(self):
return self._inclusive
@@ -815,9 +815,9 @@ class Schedules(object):
raise AttributeError("Cannot assign to this value - use += instead")
unexpected = [v for v in value if v not in schedules.INCLUSIVE_COMPONENTS]
if unexpected:
- raise Exception("unexpected exclusive component(s) " + ', '.join(unexpected))
+ raise Exception("unexpected inclusive component(s) " + ', '.join(unexpected))
- # exclusive is immuntable but can be set (= only)
+ # exclusive is immutable but can be set (= only)
@property
def exclusive(self):
return self._exclusive
@@ -836,6 +836,25 @@ class Schedules(object):
def components(self):
return list(sorted(set(self._inclusive) | set(self._exclusive)))
+ # The `Files` context uses | to combine SCHEDULES from multiple levels; at this
+ # point the immutability is no longer needed so we use plain lists
+ def __or__(self, other):
+ rv = Schedules()
+ rv._inclusive = self._inclusive + other._inclusive
+ if other._exclusive == self._exclusive:
+ rv._exclusive = self._exclusive
+ elif self._exclusive == schedules.EXCLUSIVE_COMPONENTS:
+ rv._exclusive = other._exclusive
+ elif other._exclusive == schedules.EXCLUSIVE_COMPONENTS:
+ rv._exclusive = self._exclusive
+ else:
+ msg = 'Two Files sections have set SCHEDULES.exclusive to different' \
+ 'values; these cannot be combined: {} and {}'
+ msg = msg.format(self._exclusive, other._exclusive)
+ raise ValueError(msg)
+ return rv
+
+
@memoize
def ContextDerivedTypedHierarchicalStringList(type):
"""Specialized HierarchicalStringList for use with ContextDerivedValue
@@ -1086,6 +1105,10 @@ class Files(SubContext):
self.test_flavors |= set(v.flavors)
continue
+ if k == 'SCHEDULES' and 'SCHEDULES' in self:
+ self['SCHEDULES'] = self['SCHEDULES'] | v
+ continue
+
# Ignore updates to finalized flags.
if k in self.finalized:
continue
diff --git a/python/mozbuild/mozbuild/schedules.py b/python/mozbuild/mozbuild/schedules.py
index 3bc880de7567..15b67389f756 100644
--- a/python/mozbuild/mozbuild/schedules.py
+++ b/python/mozbuild/mozbuild/schedules.py
@@ -15,6 +15,11 @@ INCLUSIVE_COMPONENTS = [
'py-lint',
'js-lint',
'yaml-lint',
+ # inclusive test suites -- these *only* run when certain files have changed
+ 'jittest',
+ 'test-verify',
+ 'test-verify-wpt',
+ 'jsreftest',
]
INCLUSIVE_COMPONENTS = sorted(INCLUSIVE_COMPONENTS)
@@ -30,7 +35,6 @@ EXCLUSIVE_COMPONENTS = [
'firefox-ui',
'geckoview',
'gtest',
- 'jittest',
'marionette',
'mochitest',
'reftest',
diff --git a/python/mozbuild/mozbuild/test/frontend/data/schedules/moz.build b/python/mozbuild/mozbuild/test/frontend/data/schedules/moz.build
index d104c5290711..9ffce649969c 100644
--- a/python/mozbuild/mozbuild/test/frontend/data/schedules/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/schedules/moz.build
@@ -7,5 +7,13 @@ with Files('*.win'):
with Files('*.osx'):
SCHEDULES.exclusive = ['macosx']
+with Files('bad.osx'):
+ # this conflicts with the previous clause and will cause an error
+ # when read
+ SCHEDULES.exclusive = ['macosx', 'windows']
+
with Files('subd/**.py'):
SCHEDULES.inclusive += ['py-lint']
+
+with Files('**/*.js'):
+ SCHEDULES.inclusive += ['js-lint']
diff --git a/python/mozbuild/mozbuild/test/frontend/data/schedules/subd/moz.build b/python/mozbuild/mozbuild/test/frontend/data/schedules/subd/moz.build
index d078a8e69db8..b2b4ef44543d 100644
--- a/python/mozbuild/mozbuild/test/frontend/data/schedules/subd/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/schedules/subd/moz.build
@@ -1,2 +1,5 @@
with Files('yaml.py'):
SCHEDULES.inclusive += ['yaml-lint']
+
+with Files('win.js'):
+ SCHEDULES.exclusive = ['windows']
diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py
index a6be6bd3b5ed..9f227fcc9f78 100644
--- a/python/mozbuild/mozbuild/test/frontend/test_reader.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py
@@ -483,7 +483,14 @@ class TestBuildReader(unittest.TestCase):
def test_schedules(self):
reader = self.reader('schedules')
- info = reader.files_info(['somefile', 'foo.win', 'foo.osx', 'subd/aa.py', 'subd/yaml.py'])
+ info = reader.files_info([
+ 'somefile',
+ 'foo.win',
+ 'foo.osx',
+ 'subd/aa.py',
+ 'subd/yaml.py',
+ 'subd/win.js',
+ ])
# default: all exclusive, no inclusive
self.assertEqual(info['somefile']['SCHEDULES'].inclusive, [])
self.assertEqual(info['somefile']['SCHEDULES'].exclusive, schedules.EXCLUSIVE_COMPONENTS)
@@ -496,12 +503,23 @@ class TestBuildReader(unittest.TestCase):
# top-level moz.build specifies subd/**.py with an inclusive option
self.assertEqual(info['subd/aa.py']['SCHEDULES'].inclusive, ['py-lint'])
self.assertEqual(info['subd/aa.py']['SCHEDULES'].exclusive, schedules.EXCLUSIVE_COMPONENTS)
- # Files('yaml.py') in subd/moz.build *overrides* Files('subdir/**.py')
- self.assertEqual(info['subd/yaml.py']['SCHEDULES'].inclusive, ['yaml-lint'])
+ # Files('yaml.py') in subd/moz.build combines with Files('subdir/**.py')
+ self.assertEqual(info['subd/yaml.py']['SCHEDULES'].inclusive, ['py-lint', 'yaml-lint'])
self.assertEqual(info['subd/yaml.py']['SCHEDULES'].exclusive, schedules.EXCLUSIVE_COMPONENTS)
+ # .. but exlusive does not override inclusive
+ self.assertEqual(info['subd/win.js']['SCHEDULES'].inclusive, ['js-lint'])
+ self.assertEqual(info['subd/win.js']['SCHEDULES'].exclusive, ['windows'])
self.assertEqual(set(info['subd/yaml.py']['SCHEDULES'].components),
- set(schedules.EXCLUSIVE_COMPONENTS + ['yaml-lint']))
+ set(schedules.EXCLUSIVE_COMPONENTS + ['py-lint', 'yaml-lint']))
+
+ def test_schedules_conflicting_excludes(self):
+ reader = self.reader('schedules')
+
+ # bad.osx is defined explicitly, and matches *.osx, and the two have
+ # conflicting SCHEDULES.exclusive settings
+ with self.assertRaisesRegexp(ValueError, r"Two Files sections"):
+ reader.files_info(['bad.osx'])
if __name__ == '__main__':
main()
diff --git a/servo/components/script/dom/domimplementation.rs b/servo/components/script/dom/domimplementation.rs
index b81f9284b4ab..c25908e67a70 100644
--- a/servo/components/script/dom/domimplementation.rs
+++ b/servo/components/script/dom/domimplementation.rs
@@ -114,7 +114,7 @@ impl DOMImplementationMethods for DOMImplementation {
}
// Step 6.
- // FIXME: https://github.com/mozilla/servo/issues/1522
+ // The origin is already set
// Step 7.
Ok(doc)
@@ -183,7 +183,7 @@ impl DOMImplementationMethods for DOMImplementation {
}
// Step 8.
- // FIXME: https://github.com/mozilla/servo/issues/1522
+ // The origin is already set
// Step 9.
doc
diff --git a/servo/components/script/dom/domparser.rs b/servo/components/script/dom/domparser.rs
index 00c18541a9b5..c0de086569ac 100644
--- a/servo/components/script/dom/domparser.rs
+++ b/servo/components/script/dom/domparser.rs
@@ -77,7 +77,6 @@ impl DOMParserMethods for DOMParser {
Ok(document)
}
Text_xml | Application_xml | Application_xhtml_xml => {
- // FIXME: this should probably be FromParser when we actually parse the string (#3756).
let document = Document::new(&self.window,
HasBrowsingContext::No,
Some(url.clone()),
@@ -86,12 +85,13 @@ impl DOMParserMethods for DOMParser {
Some(content_type),
None,
DocumentActivity::Inactive,
- DocumentSource::NotFromParser,
+ DocumentSource::FromParser,
loader,
None,
None,
Default::default());
ServoParser::parse_xml_document(&document, s, url);
+ document.set_ready_state(DocumentReadyState::Complete);
Ok(document)
}
}
diff --git a/taskcluster/ci/test/compiled.yml b/taskcluster/ci/test/compiled.yml
index afbe6c9bce13..c0aa442ca654 100644
--- a/taskcluster/ci/test/compiled.yml
+++ b/taskcluster/ci/test/compiled.yml
@@ -75,10 +75,6 @@ jittest:
windows.*: false
macosx.*: false
default: true
- when:
- files-changed:
- - js/src/**
- - js/public/**
tier:
by-test-platform:
windows10-64-asan.*: 3
diff --git a/taskcluster/ci/test/misc.yml b/taskcluster/ci/test/misc.yml
index 5975b94f0240..2bcf172c4440 100644
--- a/taskcluster/ci/test/misc.yml
+++ b/taskcluster/ci/test/misc.yml
@@ -54,7 +54,7 @@ telemetry-tests-client:
test-verify:
description: "Extra verification of tests modified on this push"
- suite: test-verification
+ suite: test-verify
treeherder-symbol: tc(TV)
loopback-video: true
instance-size:
@@ -92,9 +92,3 @@ test-verify:
no-read-buildbot-config: true
extra-options:
- --verify
- when:
- files-changed:
- - '**/*.js'
- - '**/*.html'
- - '**/*.xhtml'
- - '**/*.xul'
diff --git a/taskcluster/ci/test/reftest.yml b/taskcluster/ci/test/reftest.yml
index 6719c0101f67..6540ea5026a0 100644
--- a/taskcluster/ci/test/reftest.yml
+++ b/taskcluster/ci/test/reftest.yml
@@ -75,10 +75,6 @@ jsreftest:
by-test-platform:
android.*: 7200
default: 3600
- when:
- files-changed:
- - js/src/**
- - js/public/**
tier:
by-test-platform:
linux64-qr/.*: 1
diff --git a/taskcluster/ci/test/web-platform.yml b/taskcluster/ci/test/web-platform.yml
index 4b680bc99b4c..efa136164d30 100644
--- a/taskcluster/ci/test/web-platform.yml
+++ b/taskcluster/ci/test/web-platform.yml
@@ -144,6 +144,3 @@ test-verify-wpt:
mozharness:
extra-options:
- --verify
- when:
- files-changed:
- - 'testing/web-platform/tests/**'
diff --git a/taskcluster/docs/optimization-process.rst b/taskcluster/docs/optimization-process.rst
new file mode 100644
index 000000000000..c9dd20647a49
--- /dev/null
+++ b/taskcluster/docs/optimization-process.rst
@@ -0,0 +1,75 @@
+Optimization Process
+====================
+
+Optimization proceeds in three phases: removing tasks, replacing tasks,
+and finally generating a subgraph containing only the remaining tasks.
+
+Assume the following task graph as context for these examples::
+
+ TC1 <--\ ,- UP1
+ , B1 <--- T1a
+ I1 <-| `- T1b
+ ` B2 <--- T2a
+ TC2 <--/ |- T2b
+ `- UP2
+
+Removing Tasks
+--------------
+
+This phase begins with tasks on which nothing depends and follows the
+dependency graph backward from there -- right to left in the diagram above. If
+a task is not removed, then nothing it depends on will be removed either.
+Thus if T1a and T1b are both removed, B1 may be removed as well. But if T2b is
+not removed, then B2 may not be removed either.
+
+For each task with no remaining dependencies, the decision whether to remove is
+made by calling the optimization strategy's ``should_remove_task`` method. If
+this method returns True, the task is removed.
+
+The optimization process takes a ``do_not_optimize`` argument containing a list
+of tasks that cannot be removed under any circumstances. This is used to
+"force" running specific tasks.
+
+Replacing Tasks
+---------------
+
+This phase begins with tasks having no dependencies and follows the reversed
+dependency graph from there -- left to right in the diagram above. If a task is
+not replaced, then anything depending on that task cannot be replaced.
+Replacement is generally done on the basis of some hash of the inputs to the
+task. In the diagram above, if both TC1 and I1 are replaced with existing
+tasks, then B1 is a candidate for replacement. But if TC2 has no replacement,
+then replacement of B2 will not be considered.
+
+It is possible to replace a task with nothing. This is similar to optimzing
+away, but is useful for utility tasks like UP1. If such a task is considered
+for replacement, then all of its dependencies (here, B1) have already been
+replaced and there is no utility in running the task and no need for a
+replacement task. It is an error for a task on which others depend to be
+replaced with nothing.
+
+The ``do_not_optimize`` set applies to task replacement, as does an additional
+``existing_tasks`` dictionary which allows the caller to supply as set of
+known, pre-existing tasks. This is used for action tasks, for example, where it
+contains the entire task-graph generated by the original decision task.
+
+Subgraph Generation
+-------------------
+
+The first two phases annotate each task in the existing taskgraph with their
+fate: removed, replaced, or retained. The tasks that are replaced also have a
+replacement taskId.
+
+The last phase constructs a subgraph containing the retained tasks, and
+simultaneously rewrites all dependencies to refer to taskIds instead of labels.
+To do so, it assigns a taskId to each retained task and uses the replacement
+taskId for all replaced tasks.
+
+The result is an optimized taskgraph with tasks named by taskId instead of
+label. At this phase, the edges in the task graph diverge from the
+``task.dependencies`` attributes, as the latter may contain dependencies
+outside of the taskgraph (for replacement tasks).
+
+As a side-effect, this phase also expands all ``{"task-reference": ".."}``
+objects within the task definitions.
+
diff --git a/taskcluster/docs/optimization-schedules.rst b/taskcluster/docs/optimization-schedules.rst
new file mode 100644
index 000000000000..47e500a2e389
--- /dev/null
+++ b/taskcluster/docs/optimization-schedules.rst
@@ -0,0 +1,89 @@
+Optimization and SCHEDULES
+==========================
+
+Most optimization of builds and tests is handled with ``SCHEDULES``.
+The concept is this: we allocate tasks into named components, and associate a set of such components to each file in the source tree.
+Given a set of files changed in a push, we then calculate the union of components affected by each file, and remove tasks that are not tagged with any of them.
+
+This optimization system is intended to be *conservative*.
+It represents what could *possibly* be affected, rather than any intuitive notion of what tasks would be useful to run for changes to a particular file.
+For example:
+
+* ``dom/url/URL.cpp`` schedules tasks on all platform and could potentially cause failures in any test suite
+
+* ``dom/system/mac/CoreLocationLocationProvider.mm`` could not possibly affect any platform but ``macosx``, but potentially any test suite
+
+* ``python/mozbuild/mozbuild/preprocessor.py`` could possibly affect any platform, and should also schedule Python lint tasks
+
+Exclusive and Inclusive
+-----------------------
+
+The first wrinkle in this "simple" plan is that there are a lot of files, and for the most part they all affect most components.
+But there are some components which are only affected by a well-defined set of files.
+For example, a Python lint component need only be scheduled when Python files are changed.
+
+We divide the components into "exclusive" and "inclusive" components.
+Absent any other configuration, any file in the repository is assumed to affect all of the exclusive components and none of the inclusive components.
+
+Exclusive components can be thought of as a series of families.
+For example, the platform (linux, windows, macosx, android) is a component family.
+The test suite (mochitest, reftest, xpcshell, etc.) is another.
+By default, source files are associated with every component in every family.
+This means tasks tagged with an exclusive component will *always* run, unless none of the modified source files are associated with that component.
+
+But what if we only want to run a particular task when a pre-determined file is modified?
+This is where inclusive components are used.
+Any task tagged with an inclusive component will *only* be run when a source file associated with that component is modified.
+Lint tasks and well separated unittest tasks are good examples of things you might want to schedule inclusively.
+
+A good way to keep this straight is to think of exclusive platform-family components (``macosx``, ``android``, ``windows``, ``linux``) and inclusive linting components (``py-lint``, ``js-lint``).
+An arbitrary file in the repository affects all platform families, but does not necessarily require a lint run.
+But we can configure mac-only files such as ``CoreLocationLocationProvider.mm`` to affect exclusively ``macosx``, and Python files like ``preprocessor.py`` to affect ``py-lint`` in addition to the exclusive components.
+
+It is also possible to define a file as affecting an inclusive component and nothing else.
+For example, the source code and configuration for the Python linting tasks does not affect any tasks other than linting.
+
+.. note:
+
+ Most unit test suite tasks are allocated to components for their platform family and for the test suite.
+ This indicates that if a platform family is affected (for example, ``android``) then the builds for that platform should execute as well as the full test suite.
+ If only a single suite is affected (for example, by a change to a reftest source file), then the reftests should execute for all platforms.
+
+ However, some test suites, for which the set of contributing files are well-defined, are represented as inclusive components.
+ These components will not be executed by default for any platform families, but only when one or more of the contributing files are changed.
+
+Specification
+-------------
+
+Components are defined as either inclusive or exclusive in :py:mod:`mozbuild.schedules`.
+
+File Annotation
+:::::::::::::::
+
+Files are annotated with their affected components in ``moz.build`` files with stanzas like ::
+
+
+ with Files('**/*.py'):
+ SCHEDULES.inclusive += ['py-lint']
+
+for inclusive components and ::
+
+ with Files('*gradle*'):
+ SCHEDULES.exclusive = ['android']
+
+for exclusive components.
+Note the use of ``+=`` for inclusive compoenents (as this is adding to the existing set of affected components) but ``=`` for exclusive components (as this is resetting the affected set to something smaller).
+For cases where an inclusive component is affected exclusively (such as the python-lint configuration in the example above), that component can be assigned to ``SCHEDULES.exclusive``::
+
+ with Files('**/pep8rc'):
+ SCHEDULES.exclusive = ['py-lint']
+
+Task Annotation
+:::::::::::::::
+
+Tasks are annotated with the components they belong to using the ``"skip-unless-schedules"`` optimization, which takes a list of components for this task::
+
+ task['optimization'] = {'skip-unless-schedules': ['windows', 'gtest']}
+
+For tests, this value is set automatically by the test transform based on the suite name and the platform family, doing the correct thing for inclusive test suites.
+Tests also use SETA via ``"skip-unless-schedules-or-seta"``, which skips a task if it is not affected *or* if SETA deems it unimportant.
diff --git a/taskcluster/docs/optimization.rst b/taskcluster/docs/optimization.rst
index 394e96f18b04..e6c705116f31 100644
--- a/taskcluster/docs/optimization.rst
+++ b/taskcluster/docs/optimization.rst
@@ -43,77 +43,10 @@ considered for optimization. This behavior is controlled with the
requested jobs are optimized away. If those jobs were actually necessary,
then a try push with ``try_task_config.json`` is the solution.
-Optimization Process
---------------------
+More Information
+----------------
-Optimization proceeds in three phases: removing tasks, replacing tasks,
-and finally generating a subgraph containing only the remaining tasks.
+.. toctree::
-Assume the following task graph as context for these examples::
-
- TC1 <--\ ,- UP1
- , B1 <--- T1a
- I1 <-| `- T1b
- ` B2 <--- T2a
- TC2 <--/ |- T2b
- `- UP2
-
-Removing Tasks
-::::::::::::::
-
-This phase begins with tasks on which nothing depends and follows the
-dependency graph backward from there -- right to left in the diagram above. If
-a task is not removed, then nothing it depends on will be removed either.
-Thus if T1a and T1b are both removed, B1 may be removed as well. But if T2b is
-not removed, then B2 may not be removed either.
-
-For each task with no remaining dependencies, the decision whether to remove is
-made by calling the optimization strategy's ``should_remove_task`` method. If
-this method returns True, the task is removed.
-
-The optimization process takes a ``do_not_optimize`` argument containing a list
-of tasks that cannot be removed under any circumstances. This is used to
-"force" running specific tasks.
-
-Replacing Tasks
-:::::::::::::::
-
-This phase begins with tasks having no dependencies and follows the reversed
-dependency graph from there -- left to right in the diagram above. If a task is
-not replaced, then anything depending on that task cannot be replaced.
-Replacement is generally done on the basis of some hash of the inputs to the
-task. In the diagram above, if both TC1 and I1 are replaced with existing
-tasks, then B1 is a candidate for replacement. But if TC2 has no replacement,
-then replacement of B2 will not be considered.
-
-It is possible to replace a task with nothing. This is similar to optimzing
-away, but is useful for utility tasks like UP1. If such a task is considered
-for replacement, then all of its dependencies (here, B1) have already been
-replaced and there is no utility in running the task and no need for a
-replacement task. It is an error for a task on which others depend to be
-replaced with nothing.
-
-The ``do_not_optimize`` set applies to task replacement, as does an additional
-``existing_tasks`` dictionary which allows the caller to supply as set of
-known, pre-existing tasks. This is used for action tasks, for example, where it
-contains the entire task-graph generated by the original decision task.
-
-Subgraph Generation
-:::::::::::::::::::
-
-The first two phases annotate each task in the existing taskgraph with their
-fate: removed, replaced, or retained. The tasks that are replaced also have a
-replacement taskId.
-
-The last phase constructs a subgraph containing the retained tasks, and
-simultaneously rewrites all dependencies to refer to taskIds instead of labels.
-To do so, it assigns a taskId to each retained task and uses the replacement
-taskId for all replaced tasks.
-
-The result is an optimized taskgraph with tasks named by taskId instead of
-label. At this phase, the edges in the task graph diverge from the
-``task.dependencies`` attributes, as the latter may contain dependencies
-outside of the taskgraph (for replacement tasks).
-
-As a side-effect, this phase also expands all ``{"task-reference": ".."}``
-objects within the task definitions.
+ optimization-process
+ optimization-schedules
diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py
index 99d8b7d99757..e929472e693d 100644
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -378,11 +378,6 @@ test_description_schema = Schema({
# the product name, defaults to firefox
Optional('product'): basestring,
- # conditional files to determine when these tests should be run
- Optional('when'): Any({
- Optional('files-changed'): [basestring],
- }),
-
Optional('worker-type'): optionally_keyed_by(
'test-platform',
Any(basestring, None),
@@ -671,7 +666,7 @@ def handle_suite_category(config, tests):
script = test['mozharness']['script']
category_arg = None
- if suite == 'test-verification':
+ if suite == 'test-verify':
pass
elif script == 'android_emulator_unittest.py':
category_arg = '--test-suite'
@@ -694,7 +689,6 @@ def enable_code_coverage(config, tests):
for test in tests:
if 'ccov' in test['build-platform'] and not test['test-name'].startswith('test-verify'):
test['mozharness'].setdefault('extra-options', []).append('--code-coverage')
- test['when'] = {}
test['instance-size'] = 'xlarge'
# Ensure we don't run on inbound/autoland/beta, but if the test is try only, ignore it
if 'mozilla-central' in test['run-on-projects'] or \
@@ -1008,24 +1002,21 @@ def make_job_description(config, tests):
'platform': test.get('treeherder-machine-platform', test['build-platform']),
}
- if test.get('when'):
- jobdesc['when'] = test['when']
+ suite = attributes['unittest_suite']
+ if suite in INCLUSIVE_COMPONENTS:
+ # if this is an "inclusive" test, then all files which might
+ # cause it to run are annotated with SCHEDULES in moz.build,
+ # so do not include the platform or any other components here
+ schedules = [suite]
else:
- suite = attributes['unittest_suite']
- if suite in INCLUSIVE_COMPONENTS:
- # if this is an "inclusive" test, then all files which might
- # cause it to run are annotated with SCHEDULES in moz.build,
- # so do not include the platform or any other components here
- schedules = [suite]
- else:
- schedules = [suite, platform_family(test['build-platform'])]
+ schedules = [suite, platform_family(test['build-platform'])]
- if config.params['project'] != 'try':
- # for non-try branches, include SETA
- jobdesc['optimization'] = {'skip-unless-schedules-or-seta': schedules}
- else:
- # otherwise just use skip-unless-schedules
- jobdesc['optimization'] = {'skip-unless-schedules': schedules}
+ if config.params['project'] != 'try':
+ # for non-try branches, include SETA
+ jobdesc['optimization'] = {'skip-unless-schedules-or-seta': schedules}
+ else:
+ # otherwise just use skip-unless-schedules
+ jobdesc['optimization'] = {'skip-unless-schedules': schedules}
run = jobdesc['run'] = {}
run['using'] = 'mozharness-test'
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
index 299497e15707..4d770b79af24 100644
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
@@ -95,9 +95,10 @@ function starttest(){
// Try some basic stuff with XHR.
var xhr2 = SpecialPowers.Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(SpecialPowers.Ci.nsIXMLHttpRequest);
is(xhr2.readyState, XMLHttpRequest.UNSENT, "Should be able to get props off privileged objects");
- var testURI = SpecialPowers.Cc['@mozilla.org/network/standard-url;1']
- .createInstance(SpecialPowers.Ci.nsIURI);
- testURI.spec = "http://www.foobar.org/";
+ var testURI = SpecialPowers.Cc['@mozilla.org/network/standard-url-mutator;1']
+ .createInstance(SpecialPowers.Ci.nsIURIMutator)
+ .setSpec("http://www.foobar.org/")
+ .finalize();
is(testURI.spec, "http://www.foobar.org/", "Getters/Setters should work correctly");
is(SpecialPowers.wrap(document).getElementsByTagName('details').length, 0, "Should work with proxy-based DOM bindings.");
diff --git a/testing/web-platform/moz.build b/testing/web-platform/moz.build
index d7309c107aa7..4422a1961365 100644
--- a/testing/web-platform/moz.build
+++ b/testing/web-platform/moz.build
@@ -99,6 +99,10 @@ with Files("*.py"):
with Files("tests/*.md"):
BUG_COMPONENT = ("Testing", "web-platform-tests")
+with Files("tests/**"):
+ BUG_COMPONENT = ("Testing", "web-platform-tests")
+ SCHEDULES.inclusive += ['test-verify-wpt']
+
with Files("tests/LICENSE"):
BUG_COMPONENT = ("Testing", "web-platform-tests")
diff --git a/third_party/python/blessings/MANIFEST.in b/third_party/python/blessings/MANIFEST.in
deleted file mode 100644
index 3f4fbd708437..000000000000
--- a/third_party/python/blessings/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include README.rst
-include LICENSE
-include tox.ini
diff --git a/third_party/python/blessings/PKG-INFO b/third_party/python/blessings/PKG-INFO
deleted file mode 100644
index c52ca3cf917e..000000000000
--- a/third_party/python/blessings/PKG-INFO
+++ /dev/null
@@ -1,426 +0,0 @@
-Metadata-Version: 1.0
-Name: blessings
-Version: 1.3
-Summary: A thin, practical wrapper around terminal formatting, positioning, and more
-Home-page: https://github.com/erikrose/blessings
-Author: Erik Rose
-Author-email: erikrose@grinchcentral.com
-License: MIT
-Description: =========
- Blessings
- =========
-
- Coding with Blessings looks like this... ::
-
- from blessings import Terminal
-
- t = Terminal()
-
- print t.bold('Hi there!')
- print t.bold_red_on_bright_green('It hurts my eyes!')
-
- with t.location(0, t.height - 1):
- print 'This is at the bottom.'
-
- Or, for byte-level control, you can drop down and play with raw terminal
- capabilities::
-
- print '{t.bold}All your {t.red}bold and red base{t.normal}'.format(t=t)
- print t.wingo(2)
-
- The Pitch
- =========
-
- Blessings lifts several of curses_' limiting assumptions, and it makes your
- code pretty, too:
-
- * Use styles, color, and maybe a little positioning without clearing the whole
- screen first.
- * Leave more than one screenful of scrollback in the buffer after your program
- exits, like a well-behaved command-line app should.
- * Get rid of all those noisy, C-like calls to ``tigetstr`` and ``tparm``, so
- your code doesn't get crowded out by terminal bookkeeping.
- * Act intelligently when somebody redirects your output to a file, omitting the
- terminal control codes the user doesn't want to see (optional).
-
- .. _curses: http://docs.python.org/library/curses.html
-
- Before And After
- ----------------
-
- Without Blessings, this is how you'd print some underlined text at the bottom
- of the screen::
-
- from curses import tigetstr, setupterm, tparm
- from fcntl import ioctl
- from os import isatty
- import struct
- import sys
- from termios import TIOCGWINSZ
-
- # If we want to tolerate having our output piped to other commands or
- # files without crashing, we need to do all this branching:
- if hasattr(sys.stdout, 'fileno') and isatty(sys.stdout.fileno()):
- setupterm()
- sc = tigetstr('sc')
- cup = tigetstr('cup')
- rc = tigetstr('rc')
- underline = tigetstr('smul')
- normal = tigetstr('sgr0')
- else:
- sc = cup = rc = underline = normal = ''
- print sc # Save cursor position.
- if cup:
- # tigetnum('lines') doesn't always update promptly, hence this:
- height = struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, '\000' * 8))[0]
- print tparm(cup, height - 1, 0) # Move cursor to bottom.
- print 'This is {under}underlined{normal}!'.format(under=underline,
- normal=normal)
- print rc # Restore cursor position.
-
- Phew! That was long and full of incomprehensible trash! Let's try it again,
- this time with Blessings::
-
- from blessings import Terminal
-
- term = Terminal()
- with term.location(0, term.height - 1):
- print 'This is', term.underline('pretty!')
-
- Much better.
-
- What It Provides
- ================
-
- Blessings provides just one top-level object: ``Terminal``. Instantiating a
- ``Terminal`` figures out whether you're on a terminal at all and, if so, does
- any necessary terminal setup. After that, you can proceed to ask it all sorts
- of things about the terminal. Terminal terminal terminal.
-
- Simple Formatting
- -----------------
-
- Lots of handy formatting codes ("capabilities" in low-level parlance) are
- available as attributes on a ``Terminal``. For example::
-
- from blessings import Terminal
-
- term = Terminal()
- print 'I am ' + term.bold + 'bold' + term.normal + '!'
-
- You can also use them as wrappers so you don't have to say ``normal``
- afterward::
-
- print 'I am', term.bold('bold') + '!'
-
- Or, if you want fine-grained control while maintaining some semblance of
- brevity, you can combine it with Python's string formatting, which makes
- attributes easy to access::
-
- print 'All your {t.red}base {t.underline}are belong to us{t.normal}'.format(t=term)
-
- Simple capabilities of interest include...
-
- * ``bold``
- * ``reverse``
- * ``underline``
- * ``no_underline`` (which turns off underlining)
- * ``blink``
- * ``normal`` (which turns off everything, even colors)
- * ``clear_eol`` (clear to the end of the line)
- * ``clear_bol`` (clear to beginning of line)
- * ``clear_eos`` (clear to end of screen)
-
- Here are a few more which are less likely to work on all terminals:
-
- * ``dim``
- * ``italic`` and ``no_italic``
- * ``shadow`` and ``no_shadow``
- * ``standout`` and ``no_standout``
- * ``subscript`` and ``no_subscript``
- * ``superscript`` and ``no_superscript``
- * ``flash`` (which flashes the screen once)
-
- Note that, while the inverse of ``underline`` is ``no_underline``, the only way
- to turn off ``bold`` or ``reverse`` is ``normal``, which also cancels any
- custom colors. This is because there's no way to tell the terminal to undo
- certain pieces of formatting, even at the lowest level.
-
- You might notice that the above aren't the typical incomprehensible terminfo
- capability names; we alias a few of the harder-to-remember ones for
- readability. However, you aren't limited to these: you can reference any
- string-returning capability listed on the `terminfo man page`_ by the name
- under the "Cap-name" column: for example, ``term.rum``.
-
- .. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
-
- Color
- -----
-
- 16 colors, both foreground and background, are available as easy-to-remember
- attributes::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.red + term.on_green + 'Red on green? Ick!' + term.normal
- print term.bright_red + term.on_bright_blue + 'This is even worse!' + term.normal
-
- You can also call them as wrappers, which sets everything back to normal at the
- end::
-
- print term.red_on_green('Red on green? Ick!')
- print term.yellow('I can barely see it.')
-
- The available colors are...
-
- * ``black``
- * ``red``
- * ``green``
- * ``yellow``
- * ``blue``
- * ``magenta``
- * ``cyan``
- * ``white``
-
- You can set the background color instead of the foreground by prepending
- ``on_``, as in ``on_blue``. There is also a ``bright`` version of each color:
- for example, ``on_bright_blue``.
-
- There is also a numerical interface to colors, which takes an integer from
- 0-15::
-
- term.color(5) + 'Hello' + term.normal
- term.on_color(3) + 'Hello' + term.normal
-
- term.color(5)('Hello')
- term.on_color(3)('Hello')
-
- If some color is unsupported (for instance, if only the normal colors are
- available, not the bright ones), trying to use it will, on most terminals, have
- no effect: the foreground and background colors will stay as they were. You can
- get fancy and do different things depending on the supported colors by checking
- `number_of_colors`_.
-
- .. _`number_of_colors`: http://packages.python.org/blessings/#blessings.Terminal.number_of_colors
-
- Compound Formatting
- -------------------
-
- If you want to do lots of crazy formatting all at once, you can just mash it
- all together::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.bold_underline_green_on_yellow + 'Woo' + term.normal
-
- Or you can use your newly coined attribute as a wrapper, which implicitly sets
- everything back to normal afterward::
-
- print term.bold_underline_green_on_yellow('Woo')
-
- This compound notation comes in handy if you want to allow users to customize
- the formatting of your app: just have them pass in a format specifier like
- "bold_green" on the command line, and do a quick ``getattr(term,
- that_option)('Your text')`` when you do your formatting.
-
- I'd be remiss if I didn't credit couleur_, where I probably got the idea for
- all this mashing.
-
- .. _couleur: http://pypi.python.org/pypi/couleur
-
- Parametrized Capabilities
- -------------------------
-
- Some capabilities take parameters. Rather than making you dig up ``tparm()``
- all the time, we simply make such capabilities into callable strings. You can
- pass the parameters right in::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.move(10, 1)
-
- Here are some of interest:
-
- ``move``
- Position the cursor elsewhere. Parameters are y coordinate, then x
- coordinate.
- ``move_x``
- Move the cursor to the given column.
- ``move_y``
- Move the cursor to the given row.
-
- You can also reference any other string-returning capability listed on the
- `terminfo man page`_ by its name under the "Cap-name" column.
-
- .. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
-
- Height and Width
- ----------------
-
- It's simple to get the height and width of the terminal, in characters::
-
- from blessings import Terminal
-
- term = Terminal()
- height = term.height
- width = term.width
-
- These are newly updated each time you ask for them, so they're safe to use from
- SIGWINCH handlers.
-
- Temporary Repositioning
- -----------------------
-
- Sometimes you need to flit to a certain location, print something, and then
- return: for example, when updating a progress bar at the bottom of the screen.
- ``Terminal`` provides a context manager for doing this concisely::
-
- from blessings import Terminal
-
- term = Terminal()
- with term.location(0, term.height - 1):
- print 'Here is the bottom.'
- print 'This is back where I came from.'
-
- Parameters to ``location()`` are ``x`` and then ``y``, but you can also pass
- just one of them, leaving the other alone. For example... ::
-
- with term.location(y=10):
- print 'We changed just the row.'
-
- If you want to reposition permanently, see ``move``, in an example above.
-
- Pipe Savvy
- ----------
-
- If your program isn't attached to a terminal, like if it's being piped to
- another command or redirected to a file, all the capability attributes on
- ``Terminal`` will return empty strings. You'll get a nice-looking file without
- any formatting codes gumming up the works.
-
- If you want to override this--like if you anticipate your program being piped
- through ``less -r``, which handles terminal escapes just fine--pass
- ``force_styling=True`` to the ``Terminal`` constructor.
-
- In any case, there is an ``is_a_tty`` attribute on ``Terminal`` that lets you
- see whether the attached stream seems to be a terminal. If it's false, you
- might refrain from drawing progress bars and other frippery, since you're
- apparently headed into a pipe::
-
- from blessings import Terminal
-
- term = Terminal()
- if term.is_a_tty:
- with term.location(0, term.height - 1):
- print 'Progress: [=======> ]'
- print term.bold('Important stuff')
-
- Shopping List
- =============
-
- There are decades of legacy tied up in terminal interaction, so attention to
- detail and behavior in edge cases make a difference. Here are some ways
- Blessings has your back:
-
- * Uses the terminfo database so it works with any terminal type
- * Provides up-to-the-moment terminal height and width, so you can respond to
- terminal size changes (SIGWINCH signals). (Most other libraries query the
- ``COLUMNS`` and ``LINES`` environment variables or the ``cols`` or ``lines``
- terminal capabilities, which don't update promptly, if at all.)
- * Avoids making a mess if the output gets piped to a non-terminal
- * Works great with standard Python string templating
- * Provides convenient access to all terminal capabilities, not just a sugared
- few
- * Outputs to any file-like object, not just stdout
- * Keeps a minimum of internal state, so you can feel free to mix and match with
- calls to curses or whatever other terminal libraries you like
-
- Blessings does not provide...
-
- * Native color support on the Windows command prompt. However, it should work
- when used in concert with colorama_.
-
- .. _colorama: http://pypi.python.org/pypi/colorama/0.2.4
-
- Bugs
- ====
-
- Bugs or suggestions? Visit the `issue tracker`_.
-
- .. _`issue tracker`: https://github.com/erikrose/blessings/issues/new
-
- License
- =======
-
- Blessings is under the MIT License. See the LICENSE file.
-
- Version History
- ===============
-
- 1.3
- * Add ``number_of_colors``, which tells you how many colors the terminal
- supports.
- * Made ``color(n)`` and ``on_color(n)`` callable to wrap a string, like the
- named colors can. Also, make them both fall back to the ``setf`` and
- ``setb`` capabilities (like the named colors do) if the ANSI ``setaf`` and
- ``setab`` aren't available.
- * Allow ``color`` attr to act as an unparametrized string, not just a
- callable.
- * Make ``height`` and ``width`` examine any passed-in stream before falling
- back to stdout. (This rarely if ever affects actual behavior; it's mostly
- philosophical.)
- * Make caching simpler and slightly more efficient.
- * Get rid of a reference cycle between Terminals and FormattingStrings.
- * Update docs to reflect that terminal addressing (as in ``location()``) is
- 0-based.
-
- 1.2
- * Added support for Python 3! We need 3.2.3 or greater, because the curses
- library couldn't decide whether to accept strs or bytes before that
- (http://bugs.python.org/issue10570).
- * Everything that comes out of the library is now unicode. This lets us
- support Python 3 without making a mess of the code, and Python 2 should
- continue to work unless you were testing types (and badly). Please file a
- bug if this causes trouble for you.
- * Changed to the MIT License for better world domination.
- * Added Sphinx docs.
-
- 1.1
- * Added nicely named attributes for colors.
- * Introduced compound formatting.
- * Added wrapper behavior for styling and colors.
- * Let you force capabilities to be non-empty, even if the output stream is
- not a terminal.
- * Added the ``is_a_tty`` attribute for telling whether the output stream is a
- terminal.
- * Sugared the remaining interesting string capabilities.
- * Let ``location()`` operate on just an x *or* y coordinate.
-
- 1.0
- * Extracted Blessings from nose-progressive, my `progress-bar-having,
- traceback-shortcutting, rootin', tootin' testrunner`_. It provided the
- tootin' functionality.
-
- .. _`progress-bar-having, traceback-shortcutting, rootin', tootin' testrunner`: http://pypi.python.org/pypi/nose-progressive/
-
-Keywords: terminal,tty,curses,ncurses,formatting,style,color,console
-Platform: UNKNOWN
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Console
-Classifier: Environment :: Console :: Curses
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.5
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Software Development :: User Interfaces
-Classifier: Topic :: Terminals
diff --git a/third_party/python/blessings/README.rst b/third_party/python/blessings/README.rst
deleted file mode 100644
index 59983de8600b..000000000000
--- a/third_party/python/blessings/README.rst
+++ /dev/null
@@ -1,399 +0,0 @@
-=========
-Blessings
-=========
-
-Coding with Blessings looks like this... ::
-
- from blessings import Terminal
-
- t = Terminal()
-
- print t.bold('Hi there!')
- print t.bold_red_on_bright_green('It hurts my eyes!')
-
- with t.location(0, t.height - 1):
- print 'This is at the bottom.'
-
-Or, for byte-level control, you can drop down and play with raw terminal
-capabilities::
-
- print '{t.bold}All your {t.red}bold and red base{t.normal}'.format(t=t)
- print t.wingo(2)
-
-The Pitch
-=========
-
-Blessings lifts several of curses_' limiting assumptions, and it makes your
-code pretty, too:
-
-* Use styles, color, and maybe a little positioning without clearing the whole
- screen first.
-* Leave more than one screenful of scrollback in the buffer after your program
- exits, like a well-behaved command-line app should.
-* Get rid of all those noisy, C-like calls to ``tigetstr`` and ``tparm``, so
- your code doesn't get crowded out by terminal bookkeeping.
-* Act intelligently when somebody redirects your output to a file, omitting the
- terminal control codes the user doesn't want to see (optional).
-
-.. _curses: http://docs.python.org/library/curses.html
-
-Before And After
-----------------
-
-Without Blessings, this is how you'd print some underlined text at the bottom
-of the screen::
-
- from curses import tigetstr, setupterm, tparm
- from fcntl import ioctl
- from os import isatty
- import struct
- import sys
- from termios import TIOCGWINSZ
-
- # If we want to tolerate having our output piped to other commands or
- # files without crashing, we need to do all this branching:
- if hasattr(sys.stdout, 'fileno') and isatty(sys.stdout.fileno()):
- setupterm()
- sc = tigetstr('sc')
- cup = tigetstr('cup')
- rc = tigetstr('rc')
- underline = tigetstr('smul')
- normal = tigetstr('sgr0')
- else:
- sc = cup = rc = underline = normal = ''
- print sc # Save cursor position.
- if cup:
- # tigetnum('lines') doesn't always update promptly, hence this:
- height = struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, '\000' * 8))[0]
- print tparm(cup, height - 1, 0) # Move cursor to bottom.
- print 'This is {under}underlined{normal}!'.format(under=underline,
- normal=normal)
- print rc # Restore cursor position.
-
-Phew! That was long and full of incomprehensible trash! Let's try it again,
-this time with Blessings::
-
- from blessings import Terminal
-
- term = Terminal()
- with term.location(0, term.height - 1):
- print 'This is', term.underline('pretty!')
-
-Much better.
-
-What It Provides
-================
-
-Blessings provides just one top-level object: ``Terminal``. Instantiating a
-``Terminal`` figures out whether you're on a terminal at all and, if so, does
-any necessary terminal setup. After that, you can proceed to ask it all sorts
-of things about the terminal. Terminal terminal terminal.
-
-Simple Formatting
------------------
-
-Lots of handy formatting codes ("capabilities" in low-level parlance) are
-available as attributes on a ``Terminal``. For example::
-
- from blessings import Terminal
-
- term = Terminal()
- print 'I am ' + term.bold + 'bold' + term.normal + '!'
-
-You can also use them as wrappers so you don't have to say ``normal``
-afterward::
-
- print 'I am', term.bold('bold') + '!'
-
-Or, if you want fine-grained control while maintaining some semblance of
-brevity, you can combine it with Python's string formatting, which makes
-attributes easy to access::
-
- print 'All your {t.red}base {t.underline}are belong to us{t.normal}'.format(t=term)
-
-Simple capabilities of interest include...
-
-* ``bold``
-* ``reverse``
-* ``underline``
-* ``no_underline`` (which turns off underlining)
-* ``blink``
-* ``normal`` (which turns off everything, even colors)
-* ``clear_eol`` (clear to the end of the line)
-* ``clear_bol`` (clear to beginning of line)
-* ``clear_eos`` (clear to end of screen)
-
-Here are a few more which are less likely to work on all terminals:
-
-* ``dim``
-* ``italic`` and ``no_italic``
-* ``shadow`` and ``no_shadow``
-* ``standout`` and ``no_standout``
-* ``subscript`` and ``no_subscript``
-* ``superscript`` and ``no_superscript``
-* ``flash`` (which flashes the screen once)
-
-Note that, while the inverse of ``underline`` is ``no_underline``, the only way
-to turn off ``bold`` or ``reverse`` is ``normal``, which also cancels any
-custom colors. This is because there's no way to tell the terminal to undo
-certain pieces of formatting, even at the lowest level.
-
-You might notice that the above aren't the typical incomprehensible terminfo
-capability names; we alias a few of the harder-to-remember ones for
-readability. However, you aren't limited to these: you can reference any
-string-returning capability listed on the `terminfo man page`_ by the name
-under the "Cap-name" column: for example, ``term.rum``.
-
-.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
-
-Color
------
-
-16 colors, both foreground and background, are available as easy-to-remember
-attributes::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.red + term.on_green + 'Red on green? Ick!' + term.normal
- print term.bright_red + term.on_bright_blue + 'This is even worse!' + term.normal
-
-You can also call them as wrappers, which sets everything back to normal at the
-end::
-
- print term.red_on_green('Red on green? Ick!')
- print term.yellow('I can barely see it.')
-
-The available colors are...
-
-* ``black``
-* ``red``
-* ``green``
-* ``yellow``
-* ``blue``
-* ``magenta``
-* ``cyan``
-* ``white``
-
-You can set the background color instead of the foreground by prepending
-``on_``, as in ``on_blue``. There is also a ``bright`` version of each color:
-for example, ``on_bright_blue``.
-
-There is also a numerical interface to colors, which takes an integer from
-0-15::
-
- term.color(5) + 'Hello' + term.normal
- term.on_color(3) + 'Hello' + term.normal
-
- term.color(5)('Hello')
- term.on_color(3)('Hello')
-
-If some color is unsupported (for instance, if only the normal colors are
-available, not the bright ones), trying to use it will, on most terminals, have
-no effect: the foreground and background colors will stay as they were. You can
-get fancy and do different things depending on the supported colors by checking
-`number_of_colors`_.
-
-.. _`number_of_colors`: http://packages.python.org/blessings/#blessings.Terminal.number_of_colors
-
-Compound Formatting
--------------------
-
-If you want to do lots of crazy formatting all at once, you can just mash it
-all together::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.bold_underline_green_on_yellow + 'Woo' + term.normal
-
-Or you can use your newly coined attribute as a wrapper, which implicitly sets
-everything back to normal afterward::
-
- print term.bold_underline_green_on_yellow('Woo')
-
-This compound notation comes in handy if you want to allow users to customize
-the formatting of your app: just have them pass in a format specifier like
-"bold_green" on the command line, and do a quick ``getattr(term,
-that_option)('Your text')`` when you do your formatting.
-
-I'd be remiss if I didn't credit couleur_, where I probably got the idea for
-all this mashing.
-
-.. _couleur: http://pypi.python.org/pypi/couleur
-
-Parametrized Capabilities
--------------------------
-
-Some capabilities take parameters. Rather than making you dig up ``tparm()``
-all the time, we simply make such capabilities into callable strings. You can
-pass the parameters right in::
-
- from blessings import Terminal
-
- term = Terminal()
- print term.move(10, 1)
-
-Here are some of interest:
-
-``move``
- Position the cursor elsewhere. Parameters are y coordinate, then x
- coordinate.
-``move_x``
- Move the cursor to the given column.
-``move_y``
- Move the cursor to the given row.
-
-You can also reference any other string-returning capability listed on the
-`terminfo man page`_ by its name under the "Cap-name" column.
-
-.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
-
-Height and Width
-----------------
-
-It's simple to get the height and width of the terminal, in characters::
-
- from blessings import Terminal
-
- term = Terminal()
- height = term.height
- width = term.width
-
-These are newly updated each time you ask for them, so they're safe to use from
-SIGWINCH handlers.
-
-Temporary Repositioning
------------------------
-
-Sometimes you need to flit to a certain location, print something, and then
-return: for example, when updating a progress bar at the bottom of the screen.
-``Terminal`` provides a context manager for doing this concisely::
-
- from blessings import Terminal
-
- term = Terminal()
- with term.location(0, term.height - 1):
- print 'Here is the bottom.'
- print 'This is back where I came from.'
-
-Parameters to ``location()`` are ``x`` and then ``y``, but you can also pass
-just one of them, leaving the other alone. For example... ::
-
- with term.location(y=10):
- print 'We changed just the row.'
-
-If you want to reposition permanently, see ``move``, in an example above.
-
-Pipe Savvy
-----------
-
-If your program isn't attached to a terminal, like if it's being piped to
-another command or redirected to a file, all the capability attributes on
-``Terminal`` will return empty strings. You'll get a nice-looking file without
-any formatting codes gumming up the works.
-
-If you want to override this--like if you anticipate your program being piped
-through ``less -r``, which handles terminal escapes just fine--pass
-``force_styling=True`` to the ``Terminal`` constructor.
-
-In any case, there is an ``is_a_tty`` attribute on ``Terminal`` that lets you
-see whether the attached stream seems to be a terminal. If it's false, you
-might refrain from drawing progress bars and other frippery, since you're
-apparently headed into a pipe::
-
- from blessings import Terminal
-
- term = Terminal()
- if term.is_a_tty:
- with term.location(0, term.height - 1):
- print 'Progress: [=======> ]'
- print term.bold('Important stuff')
-
-Shopping List
-=============
-
-There are decades of legacy tied up in terminal interaction, so attention to
-detail and behavior in edge cases make a difference. Here are some ways
-Blessings has your back:
-
-* Uses the terminfo database so it works with any terminal type
-* Provides up-to-the-moment terminal height and width, so you can respond to
- terminal size changes (SIGWINCH signals). (Most other libraries query the
- ``COLUMNS`` and ``LINES`` environment variables or the ``cols`` or ``lines``
- terminal capabilities, which don't update promptly, if at all.)
-* Avoids making a mess if the output gets piped to a non-terminal
-* Works great with standard Python string templating
-* Provides convenient access to all terminal capabilities, not just a sugared
- few
-* Outputs to any file-like object, not just stdout
-* Keeps a minimum of internal state, so you can feel free to mix and match with
- calls to curses or whatever other terminal libraries you like
-
-Blessings does not provide...
-
-* Native color support on the Windows command prompt. However, it should work
- when used in concert with colorama_.
-
-.. _colorama: http://pypi.python.org/pypi/colorama/0.2.4
-
-Bugs
-====
-
-Bugs or suggestions? Visit the `issue tracker`_.
-
-.. _`issue tracker`: https://github.com/erikrose/blessings/issues/new
-
-License
-=======
-
-Blessings is under the MIT License. See the LICENSE file.
-
-Version History
-===============
-
-1.3
- * Add ``number_of_colors``, which tells you how many colors the terminal
- supports.
- * Made ``color(n)`` and ``on_color(n)`` callable to wrap a string, like the
- named colors can. Also, make them both fall back to the ``setf`` and
- ``setb`` capabilities (like the named colors do) if the ANSI ``setaf`` and
- ``setab`` aren't available.
- * Allow ``color`` attr to act as an unparametrized string, not just a
- callable.
- * Make ``height`` and ``width`` examine any passed-in stream before falling
- back to stdout. (This rarely if ever affects actual behavior; it's mostly
- philosophical.)
- * Make caching simpler and slightly more efficient.
- * Get rid of a reference cycle between Terminals and FormattingStrings.
- * Update docs to reflect that terminal addressing (as in ``location()``) is
- 0-based.
-
-1.2
- * Added support for Python 3! We need 3.2.3 or greater, because the curses
- library couldn't decide whether to accept strs or bytes before that
- (http://bugs.python.org/issue10570).
- * Everything that comes out of the library is now unicode. This lets us
- support Python 3 without making a mess of the code, and Python 2 should
- continue to work unless you were testing types (and badly). Please file a
- bug if this causes trouble for you.
- * Changed to the MIT License for better world domination.
- * Added Sphinx docs.
-
-1.1
- * Added nicely named attributes for colors.
- * Introduced compound formatting.
- * Added wrapper behavior for styling and colors.
- * Let you force capabilities to be non-empty, even if the output stream is
- not a terminal.
- * Added the ``is_a_tty`` attribute for telling whether the output stream is a
- terminal.
- * Sugared the remaining interesting string capabilities.
- * Let ``location()`` operate on just an x *or* y coordinate.
-
-1.0
- * Extracted Blessings from nose-progressive, my `progress-bar-having,
- traceback-shortcutting, rootin', tootin' testrunner`_. It provided the
- tootin' functionality.
-
-.. _`progress-bar-having, traceback-shortcutting, rootin', tootin' testrunner`: http://pypi.python.org/pypi/nose-progressive/
diff --git a/third_party/python/blessings/blessings/__init__.py b/third_party/python/blessings/blessings/__init__.py
index 081288ba68b1..810d367bcf3f 100644
--- a/third_party/python/blessings/blessings/__init__.py
+++ b/third_party/python/blessings/blessings/__init__.py
@@ -1,14 +1,18 @@
-from collections import defaultdict
+"""A thin, practical wrapper around terminal coloring, styling, and
+positioning"""
+
+from contextlib import contextmanager
import curses
-from curses import tigetstr, tigetnum, setupterm, tparm
+from curses import setupterm, tigetnum, tigetstr, tparm
from fcntl import ioctl
+
try:
from io import UnsupportedOperation as IOUnsupportedOperation
except ImportError:
class IOUnsupportedOperation(Exception):
- """A dummy exception to take the place of Python 3's ``io.UnsupportedOperation`` in Python 2"""
- pass
-import os
+ """A dummy exception to take the place of Python 3's
+ ``io.UnsupportedOperation`` in Python 2"""
+
from os import isatty, environ
from platform import python_version_tuple
import struct
@@ -16,15 +20,16 @@ import sys
from termios import TIOCGWINSZ
-if ('3', '0', '0') <= python_version_tuple() < ('3', '2', '2+'): # Good till 3.2.10
+__all__ = ['Terminal']
+
+
+if ('3', '0', '0') <= python_version_tuple() < ('3', '2', '2+'): # Good till
+ # 3.2.10
# Python 3.x < 3.2.3 has a bug in which tparm() erroneously takes a string.
raise ImportError('Blessings needs Python 3.2.3 or greater for Python 3 '
'support due to http://bugs.python.org/issue10570.')
-__all__ = ['Terminal']
-
-
class Terminal(object):
"""An abstraction around terminal capabilities
@@ -40,9 +45,6 @@ class Terminal(object):
around with the terminal; it's almost always needed when the terminal
is and saves sticking lots of extra args on client functions in
practice.
- ``is_a_tty``
- Whether ``stream`` appears to be a terminal. You can examine this value
- to decide whether to draw progress bars or other frippery.
"""
def __init__(self, kind=None, stream=None, force_styling=False):
@@ -69,26 +71,31 @@ class Terminal(object):
somewhere, and stdout is probably where the output is ultimately
headed. If not, stderr is probably bound to the same terminal.)
+ If you want to force styling to not happen, pass
+ ``force_styling=None``.
+
"""
if stream is None:
stream = sys.__stdout__
try:
stream_descriptor = (stream.fileno() if hasattr(stream, 'fileno')
- and callable(stream.fileno)
+ and callable(stream.fileno)
else None)
except IOUnsupportedOperation:
stream_descriptor = None
- self.is_a_tty = stream_descriptor is not None and isatty(stream_descriptor)
- self._does_styling = self.is_a_tty or force_styling
+ self._is_a_tty = (stream_descriptor is not None and
+ isatty(stream_descriptor))
+ self._does_styling = ((self.is_a_tty or force_styling) and
+ force_styling is not None)
- # The desciptor to direct terminal initialization sequences to.
+ # The descriptor to direct terminal initialization sequences to.
# sys.__stdout__ seems to always have a descriptor of 1, even if output
# is redirected.
self._init_descriptor = (sys.__stdout__.fileno()
if stream_descriptor is None
else stream_descriptor)
- if self._does_styling:
+ if self.does_styling:
# Make things like tigetstr() work. Explicit args make setupterm()
# work even when -s is passed to nosetests. Lean toward sending
# init sequences to the stream if it has a file descriptor, and
@@ -111,10 +118,20 @@ class Terminal(object):
clear_eol='el',
clear_bol='el1',
clear_eos='ed',
+ # 'clear' clears the whole screen.
position='cup', # deprecated
+ enter_fullscreen='smcup',
+ exit_fullscreen='rmcup',
move='cup',
move_x='hpa',
move_y='vpa',
+ move_left='cub1',
+ move_right='cuf1',
+ move_up='cuu1',
+ move_down='cud1',
+
+ hide_cursor='civis',
+ normal_cursor='cnorm',
reset_colors='op', # oc doesn't work on my OS X terminal.
@@ -138,7 +155,7 @@ class Terminal(object):
no_underline='rmul')
def __getattr__(self, attr):
- """Return parametrized terminal capabilities, like bold.
+ """Return a terminal capability, like bold.
For example, you can say ``term.bold`` to get the string that turns on
bold formatting and ``term.normal`` to get the string that turns it off
@@ -154,10 +171,27 @@ class Terminal(object):
Return values are always Unicode.
"""
- resolution = self._resolve_formatter(attr) if self._does_styling else NullCallableString()
+ resolution = (self._resolve_formatter(attr) if self.does_styling
+ else NullCallableString())
setattr(self, attr, resolution) # Cache capability codes.
return resolution
+ @property
+ def does_styling(self):
+ """Whether attempt to emit capabilities
+
+ This is influenced by the ``is_a_tty`` property and by the
+ ``force_styling`` argument to the constructor. You can examine
+ this value to decide whether to draw progress bars or other frippery.
+
+ """
+ return self._does_styling
+
+ @property
+ def is_a_tty(self):
+ """Whether my ``stream`` appears to be associated with a terminal"""
+ return self._is_a_tty
+
@property
def height(self):
"""The height of the terminal in characters
@@ -167,8 +201,8 @@ class Terminal(object):
piping to things that eventually display on the terminal (like ``less
-R``) work. If a stream representing a terminal was passed in, return
the dimensions of that terminal. If there somehow is no controlling
- terminal, return ``None``. (Thus, you should check that ``is_a_tty`` is
- true before doing any math on the result.)
+ terminal, return ``None``. (Thus, you should check that the property
+ ``is_a_tty`` is true before doing any math on the result.)
"""
return self._height_and_width()[0]
@@ -183,16 +217,30 @@ class Terminal(object):
return self._height_and_width()[1]
def _height_and_width(self):
- """Return a tuple of (terminal height, terminal width)."""
+ """Return a tuple of (terminal height, terminal width).
+
+ Start by trying TIOCGWINSZ (Terminal I/O-Control: Get Window Size),
+ falling back to environment variables (LINES, COLUMNS), and returning
+ (None, None) if those are unavailable or invalid.
+
+ """
# tigetnum('lines') and tigetnum('cols') update only if we call
# setupterm() again.
for descriptor in self._init_descriptor, sys.__stdout__:
try:
- return struct.unpack('hhhh', ioctl(descriptor, TIOCGWINSZ, '\000' * 8))[0:2]
+ return struct.unpack(
+ 'hhhh', ioctl(descriptor, TIOCGWINSZ, '\000' * 8))[0:2]
except IOError:
+ # when the output stream or init descriptor is not a tty, such
+ # as when when stdout is piped to another program, fe. tee(1),
+ # these ioctls will raise IOError
pass
- return None, None # Should never get here
+ try:
+ return int(environ.get('LINES')), int(environ.get('COLUMNS'))
+ except TypeError:
+ return None, None
+ @contextmanager
def location(self, x=None, y=None):
"""Return a context manager for temporarily moving the cursor.
@@ -206,10 +254,45 @@ class Terminal(object):
print 'I can do it %i times!' % x
Specify ``x`` to move to a certain column, ``y`` to move to a certain
- row, or both.
+ row, both, or neither. If you specify neither, only the saving and
+ restoration of cursor position will happen. This can be useful if you
+ simply want to restore your place after doing some manual cursor
+ movement.
"""
- return Location(self, x, y)
+ # Save position and move to the requested column, row, or both:
+ self.stream.write(self.save)
+ if x is not None and y is not None:
+ self.stream.write(self.move(y, x))
+ elif x is not None:
+ self.stream.write(self.move_x(x))
+ elif y is not None:
+ self.stream.write(self.move_y(y))
+ try:
+ yield
+ finally:
+ # Restore original cursor position:
+ self.stream.write(self.restore)
+
+ @contextmanager
+ def fullscreen(self):
+ """Return a context manager that enters fullscreen mode while inside it
+ and restores normal mode on leaving."""
+ self.stream.write(self.enter_fullscreen)
+ try:
+ yield
+ finally:
+ self.stream.write(self.exit_fullscreen)
+
+ @contextmanager
+ def hidden_cursor(self):
+ """Return a context manager that hides the cursor while inside it and
+ makes it visible on leaving."""
+ self.stream.write(self.hide_cursor)
+ try:
+ yield
+ finally:
+ self.stream.write(self.normal_cursor)
@property
def color(self):
@@ -254,12 +337,22 @@ class Terminal(object):
# don't name it after the underlying capability, because we deviate
# slightly from its behavior, and we might someday wish to give direct
# access to it.
- colors = tigetnum('colors') # Returns -1 if no color support, -2 if no such cap.
- #self.__dict__['colors'] = ret # Cache it. It's not changing. (Doesn't work.)
+ if not self._does_styling:
+ return 0
+
+ colors = tigetnum('colors') # Returns -1 if no color support, -2 if no
+ # such cap.
+ # self.__dict__['colors'] = ret # Cache it. It's not changing.
+ # (Doesn't work.)
return colors if colors >= 0 else 0
def _resolve_formatter(self, attr):
- """Resolve a sugary or plain capability name, color, or compound formatting function name into a callable capability."""
+ """Resolve a sugary or plain capability name, color, or compound
+ formatting function name into a callable capability.
+
+ Return a ``ParametrizingString`` or a ``FormattingString``.
+
+ """
if attr in COLORS:
return self._resolve_color(attr)
elif attr in COMPOUNDABLES:
@@ -277,7 +370,8 @@ class Terminal(object):
return ParametrizingString(self._resolve_capability(attr))
def _resolve_capability(self, atom):
- """Return a terminal code for a capname or a sugary name, or an empty Unicode.
+ """Return a terminal code for a capname or a sugary name, or an empty
+ Unicode.
The return value is always Unicode, because otherwise it is clumsy
(especially in Python 3) to concatenate with real (Unicode) strings.
@@ -285,14 +379,13 @@ class Terminal(object):
"""
code = tigetstr(self._sugar.get(atom, atom))
if code:
- # We can encode escape sequences as UTF-8 because they never
- # contain chars > 127, and UTF-8 never changes anything within that
- # range..
- return code.decode('utf-8')
+ # See the comment in ParametrizingString for why this is latin1.
+ return code.decode('latin1')
return u''
def _resolve_color(self, color):
- """Resolve a color like red or on_bright_green into a callable capability."""
+ """Resolve a color like red or on_bright_green into a callable
+ capability."""
# TODO: Does curses automatically exchange red and blue and cyan and
# yellow when a terminal supports setf/setb rather than setaf/setab?
# I'll be blasted if I can find any documentation. The following
@@ -315,7 +408,8 @@ class Terminal(object):
return self.setab or self.setb
def _formatting_string(self, formatting):
- """Return a new ``FormattingString`` which implicitly receives my notion of "normal"."""
+ """Return a new ``FormattingString`` which implicitly receives my
+ notion of "normal"."""
return FormattingString(formatting, self.normal)
@@ -326,7 +420,8 @@ def derivative_colors(colors):
[('on_bright_' + c) for c in colors])
-COLORS = set(['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'])
+COLORS = set(['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
+ 'white'])
COLORS.update(derivative_colors(COLORS))
COMPOUNDABLES = (COLORS |
set(['bold', 'underline', 'reverse', 'blink', 'dim', 'italic',
@@ -334,7 +429,9 @@ COMPOUNDABLES = (COLORS |
class ParametrizingString(unicode):
- """A Unicode string which can be called to parametrize it as a terminal capability"""
+ """A Unicode string which can be called to parametrize it as a terminal
+ capability"""
+
def __new__(cls, formatting, normal=None):
"""Instantiate.
@@ -352,7 +449,15 @@ class ParametrizingString(unicode):
# Re-encode the cap, because tparm() takes a bytestring in Python
# 3. However, appear to be a plain Unicode string otherwise so
# concats work.
- parametrized = tparm(self.encode('utf-8'), *args).decode('utf-8')
+ #
+ # We use *latin1* encoding so that bytes emitted by tparm are
+ # encoded to their native value: some terminal kinds, such as
+ # 'avatar' or 'kermit', emit 8-bit bytes in range 0x7f to 0xff.
+ # latin1 leaves these values unmodified in their conversion to
+ # unicode byte values. The terminal emulator will "catch" and
+ # handle these values, even if emitting utf8-encoded text, where
+ # these bytes would otherwise be illegal utf8 start bytes.
+ parametrized = tparm(self.encode('latin1'), *args).decode('latin1')
return (parametrized if self._normal is None else
FormattingString(parametrized, self._normal))
except curses.error:
@@ -376,7 +481,9 @@ class ParametrizingString(unicode):
class FormattingString(unicode):
- """A Unicode string which can be called upon a piece of text to wrap it in formatting"""
+ """A Unicode string which can be called upon a piece of text to wrap it in
+ formatting"""
+
def __new__(cls, formatting, normal):
new = unicode.__new__(cls, formatting)
new._normal = normal
@@ -394,21 +501,48 @@ class FormattingString(unicode):
class NullCallableString(unicode):
- """A dummy class to stand in for ``FormattingString`` and ``ParametrizingString``
+ """A dummy callable Unicode to stand in for ``FormattingString`` and
+ ``ParametrizingString``
- A callable bytestring that returns an empty Unicode when called with an int
- and the arg otherwise. We use this when there is no tty and so all
- capabilities are blank.
+ We use this when there is no tty and thus all capabilities should be blank.
"""
def __new__(cls):
new = unicode.__new__(cls, u'')
return new
- def __call__(self, arg):
- if isinstance(arg, int):
+ def __call__(self, *args):
+ """Return a Unicode or whatever you passed in as the first arg
+ (hopefully a string of some kind).
+
+ When called with an int as the first arg, return an empty Unicode. An
+ int is a good hint that I am a ``ParametrizingString``, as there are
+ only about half a dozen string-returning capabilities on OS X's
+ terminfo man page which take any param that's not an int, and those are
+ seldom if ever used on modern terminal emulators. (Most have to do with
+ programming function keys. Blessings' story for supporting
+ non-string-returning caps is undeveloped.) And any parametrized
+ capability in a situation where all capabilities themselves are taken
+ to be blank are, of course, themselves blank.
+
+ When called with a non-int as the first arg (no no args at all), return
+ the first arg. I am acting as a ``FormattingString``.
+
+ """
+ if len(args) != 1 or isinstance(args[0], int):
+ # I am acting as a ParametrizingString.
+
+ # tparm can take not only ints but also (at least) strings as its
+ # second...nth args. But we don't support callably parametrizing
+ # caps that take non-ints yet, so we can cheap out here. TODO: Go
+ # through enough of the motions in the capability resolvers to
+ # determine which of 2 special-purpose classes,
+ # NullParametrizableString or NullFormattingString, to return, and
+ # retire this one.
return u''
- return arg # TODO: Force even strs in Python 2.x to be unicodes? Nah. How would I know what encoding to use to convert it?
+ return args[0] # Should we force even strs in Python 2.x to be
+ # unicodes? No. How would I know what encoding to use
+ # to convert it?
def split_into_formatters(compound):
@@ -427,24 +561,3 @@ def split_into_formatters(compound):
else:
merged_segs.append(s)
return merged_segs
-
-
-class Location(object):
- """Context manager for temporarily moving the cursor"""
- def __init__(self, term, x=None, y=None):
- self.x, self.y = x, y
- self.term = term
-
- def __enter__(self):
- """Save position and move to the requested column, row, or both."""
- self.term.stream.write(self.term.save) # save position
- if self.x and self.y:
- self.term.stream.write(self.term.move(self.y, self.x))
- elif self.x:
- self.term.stream.write(self.term.move_x(self.x))
- elif self.y:
- self.term.stream.write(self.term.move_y(self.y))
-
- def __exit__(self, type, value, tb):
- """Restore original cursor position."""
- self.term.stream.write(self.term.restore)
diff --git a/third_party/python/blessings/blessings/tests.py b/third_party/python/blessings/blessings/tests.py
index a02a3924a8fc..aff3a2f249c0 100644
--- a/third_party/python/blessings/blessings/tests.py
+++ b/third_party/python/blessings/blessings/tests.py
@@ -28,12 +28,12 @@ TestTerminal = partial(Terminal, kind='xterm-256color')
def unicode_cap(cap):
"""Return the result of ``tigetstr`` except as Unicode."""
- return tigetstr(cap).decode('utf-8')
+ return tigetstr(cap).decode('latin1')
def unicode_parm(cap, *parms):
"""Return the result of ``tparm(tigetstr())`` except as Unicode."""
- return tparm(tigetstr(cap), *parms).decode('utf-8')
+ return tparm(tigetstr(cap), *parms).decode('latin1')
def test_capability():
@@ -57,7 +57,8 @@ def test_capability_without_tty():
def test_capability_with_forced_tty():
- """If we force styling, capabilities had better not (generally) be empty."""
+ """If we force styling, capabilities had better not (generally) be
+ empty."""
t = TestTerminal(stream=StringIO(), force_styling=True)
eq_(t.save, unicode_cap('sc'))
@@ -67,15 +68,16 @@ def test_parametrization():
eq_(TestTerminal().cup(3, 4), unicode_parm('cup', 3, 4))
-def height_and_width():
+def test_height_and_width():
"""Assert that ``height_and_width()`` returns ints."""
t = TestTerminal() # kind shouldn't matter.
- assert isinstance(int, t.height)
- assert isinstance(int, t.width)
+ assert isinstance(t.height, int)
+ assert isinstance(t.width, int)
def test_stream_attr():
- """Make sure Terminal exposes a ``stream`` attribute that defaults to something sane."""
+ """Make sure Terminal exposes a ``stream`` attribute that defaults to
+ something sane."""
eq_(Terminal().stream, sys.__stdout__)
@@ -102,6 +104,25 @@ def test_horizontal_location():
unicode_cap('rc'))
+def test_null_location():
+ """Make sure ``location()`` with no args just does position restoration."""
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ with t.location():
+ pass
+ eq_(t.stream.getvalue(), unicode_cap('sc') +
+ unicode_cap('rc'))
+
+
+def test_zero_location():
+ """Make sure ``location()`` pays attention to 0-valued args."""
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ with t.location(0, 0):
+ pass
+ eq_(t.stream.getvalue(), unicode_cap('sc') +
+ unicode_parm('cup', 0, 0) +
+ unicode_cap('rc'))
+
+
def test_null_fileno():
"""Make sure ``Terminal`` works when ``fileno`` is ``None``.
@@ -178,7 +199,8 @@ def test_number_of_colors_with_tty():
def test_formatting_functions():
"""Test crazy-ass formatting wrappers, both simple and compound."""
t = TestTerminal()
- # By now, it should be safe to use sugared attributes. Other tests test those.
+ # By now, it should be safe to use sugared attributes. Other tests test
+ # those.
eq_(t.bold(u'hi'), t.bold + u'hi' + t.normal)
eq_(t.green('hi'), t.green + u'hi' + t.normal) # Plain strs for Python 2.x
# Test some non-ASCII chars, probably not necessary:
@@ -187,7 +209,8 @@ def test_formatting_functions():
t.bold + t.underline + t.green + t.on_red + u'boo' + t.normal)
# Don't spell things like this:
eq_(t.on_bright_red_bold_bright_green_underline('meh'),
- t.on_bright_red + t.bold + t.bright_green + t.underline + u'meh' + t.normal)
+ t.on_bright_red + t.bold + t.bright_green + t.underline + u'meh' +
+ t.normal)
def test_formatting_functions_without_tty():
@@ -229,3 +252,19 @@ def test_init_descriptor_always_initted():
"""We should be able to get a height and width even on no-tty Terminals."""
t = Terminal(stream=StringIO())
eq_(type(t.height), int)
+
+
+def test_force_styling_none():
+ """If ``force_styling=None`` is passed to the constructor, don't ever do
+ styling."""
+ t = TestTerminal(force_styling=None)
+ eq_(t.save, '')
+
+
+def test_null_callable_string():
+ """Make sure NullCallableString tolerates all numbers and kinds of args it
+ might receive."""
+ t = TestTerminal(stream=StringIO())
+ eq_(t.clear, '')
+ eq_(t.move(1, 2), '')
+ eq_(t.move_x(1), '')
diff --git a/third_party/python/blessings/setup.cfg b/third_party/python/blessings/setup.cfg
deleted file mode 100644
index 861a9f554263..000000000000
--- a/third_party/python/blessings/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[egg_info]
-tag_build =
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/third_party/python/blessings/setup.py b/third_party/python/blessings/setup.py
deleted file mode 100644
index 6af55452d867..000000000000
--- a/third_party/python/blessings/setup.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import sys
-
-from setuptools import setup, find_packages
-
-
-extra_setup = {}
-if sys.version_info >= (3,):
- extra_setup['use_2to3'] = True
-
-setup(
- name='blessings',
- version='1.3',
- description='A thin, practical wrapper around terminal formatting, positioning, and more',
- long_description=open('README.rst').read(),
- author='Erik Rose',
- author_email='erikrose@grinchcentral.com',
- license='MIT',
- packages=find_packages(exclude=['ez_setup']),
- tests_require=['Nose'],
- url='https://github.com/erikrose/blessings',
- include_package_data=True,
- classifiers=[
- 'Intended Audience :: Developers',
- 'Natural Language :: English',
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Console',
- 'Environment :: Console :: Curses',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: POSIX',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.5',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.2',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Software Development :: User Interfaces',
- 'Topic :: Terminals'
- ],
- keywords=['terminal', 'tty', 'curses', 'ncurses', 'formatting', 'style', 'color', 'console'],
- **extra_setup
-)
diff --git a/third_party/python/blessings/tox.ini b/third_party/python/blessings/tox.ini
deleted file mode 100644
index e1753f2614da..000000000000
--- a/third_party/python/blessings/tox.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[tox]
-envlist = py25, py26, py27, py32
-
-[testenv]
-commands = nosetests blessings
-deps = nose
-changedir = .tox # So Python 3 runs don't pick up incompatible, un-2to3'd source from the cwd
diff --git a/third_party/python/json-e/jsone/interpreter.py b/third_party/python/json-e/jsone/interpreter.py
index 4d15264e9e02..eb38a9c85bce 100644
--- a/third_party/python/json-e/jsone/interpreter.py
+++ b/third_party/python/json-e/jsone/interpreter.py
@@ -1,7 +1,7 @@
from __future__ import absolute_import, print_function, unicode_literals
from .prattparser import PrattParser, infix, prefix
-from .shared import TemplateError, string
+from .shared import TemplateError, InterpreterError, string
import operator
import json
@@ -21,8 +21,9 @@ OPERATORS = {
}
-def expectationError(operator, expected):
- return TemplateError('{} expected {}'.format(operator, expected))
+def infixExpectationError(operator, expected):
+ return InterpreterError('infix: {} expects {} {} {}'.
+ format(operator, expected, operator, expected))
class ExpressionEvaluator(PrattParser):
@@ -44,9 +45,9 @@ class ExpressionEvaluator(PrattParser):
'null', 'number', 'identifier', 'string',
]
precedence = [
- ['in'],
['||'],
['&&'],
+ ['in'],
['==', '!='],
['>=', '<=', '<', '>'],
['+', '-'],
@@ -80,14 +81,14 @@ class ExpressionEvaluator(PrattParser):
def uminus(self, token, pc):
v = pc.parse('unary')
if not isNumber(v):
- raise expectationError('unary -', 'number')
+ raise InterpreterError('{} expects {}'.format('unary -', 'number'))
return -v
@prefix("+")
def uplus(self, token, pc):
v = pc.parse('unary')
if not isNumber(v):
- raise expectationError('unary +', 'number')
+ raise InterpreterError('{} expects {}'.format('unary +', 'number'))
return v
@prefix("identifier")
@@ -95,8 +96,8 @@ class ExpressionEvaluator(PrattParser):
try:
return self.context[token.value]
except KeyError:
- raise TemplateError(
- 'no context value named "{}"'.format(token.value))
+ raise InterpreterError(
+ 'unknown context value {}'.format(token.value))
@prefix("null")
def null(self, token, pc):
@@ -131,23 +132,23 @@ class ExpressionEvaluator(PrattParser):
@infix("+")
def plus(self, left, token, pc):
if not isinstance(left, (string, int, float)) or isinstance(left, bool):
- raise expectationError('+', 'number or string')
+ raise infixExpectationError('+', 'number/string')
right = pc.parse(token.kind)
if not isinstance(right, (string, int, float)) or isinstance(right, bool):
- raise expectationError('+', 'number or string')
+ raise infixExpectationError('+', 'number/string')
if type(right) != type(left) and \
(isinstance(left, string) or isinstance(right, string)):
- raise expectationError('+', 'matching types')
+ raise infixExpectationError('+', 'numbers/strings')
return left + right
@infix('-', '*', '/', '**')
def arith(self, left, token, pc):
op = token.kind
if not isNumber(left):
- raise expectationError(op, 'number')
+ raise infixExpectationError(op, 'number')
right = pc.parse({'**': '**-right-associative'}.get(op))
if not isNumber(right):
- raise expectationError(op, 'number')
+ raise infixExpectationError(op, 'number')
return OPERATORS[op](left, right)
@infix("[")
@@ -175,7 +176,7 @@ class ExpressionEvaluator(PrattParser):
@infix(".")
def property_dot(self, left, token, pc):
if not isinstance(left, dict):
- raise expectationError('.', 'object')
+ raise infixExpectationError('.', 'object')
k = pc.require('identifier').value
try:
return left[k]
@@ -202,7 +203,7 @@ class ExpressionEvaluator(PrattParser):
right = pc.parse(op)
if type(left) != type(right) or \
not (isinstance(left, (int, float, string)) and not isinstance(left, bool)):
- raise expectationError(op, 'matching types')
+ raise infixExpectationError(op, 'numbers/strings')
return OPERATORS[op](left, right)
@infix("in")
@@ -210,17 +211,17 @@ class ExpressionEvaluator(PrattParser):
right = pc.parse(token.kind)
if isinstance(right, dict):
if not isinstance(left, string):
- raise expectationError('in-object', 'string on left side')
+ raise infixExpectationError('in-object', 'string on left side')
elif isinstance(right, string):
if not isinstance(left, string):
- raise expectationError('in-string', 'string on left side')
+ raise infixExpectationError('in-string', 'string on left side')
elif not isinstance(right, list):
- raise expectationError(
+ raise infixExpectationError(
'in', 'Array, string, or object on right side')
try:
return left in right
except TypeError:
- raise expectationError('in', 'scalar value, collection')
+ raise infixExpectationError('in', 'scalar value, collection')
def isNumber(v):
@@ -268,19 +269,19 @@ def accessProperty(value, a, b, is_interval):
try:
return value[a:b]
except TypeError:
- raise expectationError('[..]', 'integer')
+ raise infixExpectationError('[..]', 'integer')
else:
try:
return value[a]
except IndexError:
raise TemplateError('index out of bounds')
except TypeError:
- raise expectationError('[..]', 'integer')
+ raise infixExpectationError('[..]', 'integer')
if not isinstance(value, dict):
- raise expectationError('[..]', 'object, array, or string')
+ raise infixExpectationError('[..]', 'object, array, or string')
if not isinstance(a, string):
- raise expectationError('[..]', 'string index')
+ raise infixExpectationError('[..]', 'string index')
try:
return value[a]
diff --git a/third_party/python/json-e/jsone/prattparser.py b/third_party/python/json-e/jsone/prattparser.py
index 0c871abbadf8..5bf250a8161a 100644
--- a/third_party/python/json-e/jsone/prattparser.py
+++ b/third_party/python/json-e/jsone/prattparser.py
@@ -11,7 +11,7 @@ class SyntaxError(TemplateError):
@classmethod
def unexpected(cls, got, exp):
exp = ', '.join(sorted(exp))
- return cls('Found {}, expected {}'.format(got, exp))
+ return cls('Found {}, expected {}'.format(got.value, exp))
Token = namedtuple('Token', ['kind', 'value', 'start', 'end'])
@@ -92,7 +92,7 @@ class PrattParser(with_metaclass(PrattParserMeta, object)):
# if there are any tokens remaining, that's an error..
token = pc.attempt()
if token:
- raise SyntaxError.unexpected(token.kind, self.infix_rules)
+ raise SyntaxError.unexpected(token, self.infix_rules)
return result
def parseUntilTerminator(self, source, terminator):
@@ -100,7 +100,7 @@ class PrattParser(with_metaclass(PrattParserMeta, object)):
result = pc.parse()
token = pc.attempt()
if token.kind != terminator:
- raise SyntaxError.unexpected(token.kind, [terminator])
+ raise SyntaxError.unexpected(token, [terminator])
return (result, token.start)
def _generate_tokens(self, source):
@@ -169,7 +169,7 @@ class ParseContext(object):
if not token:
raise SyntaxError('Unexpected end of input')
if kinds and token.kind not in kinds:
- raise SyntaxError.unexpected(token.kind, kinds)
+ raise SyntaxError.unexpected(token, kinds)
return token
def parse(self, precedence=None):
@@ -178,7 +178,7 @@ class ParseContext(object):
token = self.require()
prefix_rule = parser.prefix_rules.get(token.kind)
if not prefix_rule:
- raise SyntaxError.unexpected(token.kind, parser.prefix_rules)
+ raise SyntaxError.unexpected(token, parser.prefix_rules)
left = prefix_rule(parser, token, self)
while self.next_token:
kind = self.next_token.kind
diff --git a/third_party/python/json-e/jsone/render.py b/third_party/python/json-e/jsone/render.py
index c48b96cb5098..5f46bbf5ae92 100644
--- a/third_party/python/json-e/jsone/render.py
+++ b/third_party/python/json-e/jsone/render.py
@@ -72,7 +72,10 @@ def checkUndefinedProperties(template, allowed):
@operator('$eval')
def eval(template, context):
- return evaluateExpression(renderValue(template['$eval'], context), context)
+ checkUndefinedProperties(template, ['\$eval'])
+ if not isinstance(template['$eval'], string):
+ raise TemplateError("$eval must be given a string expression")
+ return evaluateExpression(template['$eval'], context)
@operator('$flatten')
@@ -140,21 +143,21 @@ def ifConstruct(template, context):
def jsonConstruct(template, context):
checkUndefinedProperties(template, ['\$json'])
value = renderValue(template['$json'], context)
- return json.dumps(value, separators=(',', ':'))
+ return json.dumps(value, separators=(',', ':'), sort_keys=True)
@operator('$let')
def let(template, context):
checkUndefinedProperties(template, ['\$let', 'in'])
- variables = renderValue(template['$let'], context)
- if not isinstance(variables, dict):
- raise TemplateError("$let value must evaluate to an object")
- else:
- if not all(IDENTIFIER_RE.match(variableNames) for variableNames in variables.keys()):
- raise TemplateError('top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/')
+ if not isinstance(template['$let'], dict):
+ raise TemplateError("$let value must be an object")
subcontext = context.copy()
- subcontext.update(variables)
+ for k, v in template['$let'].items():
+ if not IDENTIFIER_RE.match(k):
+ raise TemplateError('top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/')
+ subcontext[k] = renderValue(v, context)
+
try:
in_expression = template['in']
except KeyError:
@@ -243,7 +246,7 @@ def reverse(template, context):
checkUndefinedProperties(template, ['\$reverse'])
value = renderValue(template['$reverse'], context)
if not isinstance(value, list):
- raise TemplateError("$reverse value must evaluate to an array")
+ raise TemplateError("$reverse value must evaluate to an array of objects")
return list(reversed(value))
@@ -253,7 +256,7 @@ def sort(template, context):
checkUndefinedProperties(template, ['\$sort', BY_RE])
value = renderValue(template['$sort'], context)
if not isinstance(value, list):
- raise TemplateError("$sort value must evaluate to an array")
+ raise TemplateError('$sorted values to be sorted must have the same type')
# handle by(..) if given, applying the schwartzian transform
by_keys = [k for k in template if k.startswith('by(')]
@@ -279,9 +282,9 @@ def sort(template, context):
except IndexError:
return []
if eltype in (list, dict, bool, type(None)):
- raise TemplateError('$sort values must be sortable')
+ raise TemplateError('$sorted values to be sorted must have the same type')
if not all(isinstance(e[0], eltype) for e in to_sort):
- raise TemplateError('$sorted values must all have the same type')
+ raise TemplateError('$sorted values to be sorted must have the same type')
# unzip the schwartzian transform
return list(e[1] for e in sorted(to_sort))
diff --git a/third_party/python/json-e/jsone/shared.py b/third_party/python/json-e/jsone/shared.py
index df6f08d5dfec..0e70e21f8163 100644
--- a/third_party/python/json-e/jsone/shared.py
+++ b/third_party/python/json-e/jsone/shared.py
@@ -28,6 +28,10 @@ class TemplateError(JSONTemplateError):
pass
+class InterpreterError(JSONTemplateError):
+ pass
+
+
# Regular expression matching: X days Y hours Z minutes
# todo: support hr, wk, yr
FROMNOW_RE = re.compile(''.join([
@@ -109,7 +113,13 @@ def to_str(v):
def stringDate(date):
# Convert to isoFormat
- string = date.isoformat()
+ try:
+ string = date.isoformat(timespec='microseconds')
+ # py2.7 to py3.5 does not have timespec
+ except TypeError as e:
+ string = date.isoformat()
+ if string.find('.') == -1:
+ string += '.000'
string = datefmt_re.sub(r'\1Z', string)
return string
diff --git a/third_party/rust/serde/.cargo-checksum.json b/third_party/rust/serde/.cargo-checksum.json
index 9d6c69eecd0e..961f58d249c3 100644
--- a/third_party/rust/serde/.cargo-checksum.json
+++ b/third_party/rust/serde/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"f98dcc6eedbfb307be98a2d8ca49d3cc10a76a08e7d7dfa2f6ec69987f0c679d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7dc6376a88195594cd6b43cb891aad10f57144ff737178b55d046aa04739b43a","src/de/from_primitive.rs":"28ec3ab1c430cf27d632b642ccfccb6d055eeb9fb576e7e446ba24c66f507fb4","src/de/ignored_any.rs":"1b5ee592f5ae58d69e321144d4397f149c047e327529d0b880e1a5285e781a35","src/de/impls.rs":"d220ded8bebc3367c3a0ac1360b59be1b435df40be65a5b688a6ba8108e96bea","src/de/mod.rs":"b8fd19e4a2d40369cb85e3ed6bc5197800c06b2271a846ed6e9b0cb7af41c5f0","src/de/utf8.rs":"956b124b7ce98353cb781b56e43a6fed2e67f1389d35b7a468d5be75b1485853","src/de/value.rs":"fe31174cc41035a1b53d4657b6cae6f3bcb660be39eccaae281a6e3705655578","src/export.rs":"7477f5bd345ca9e0b8d56bccdc62484e42a92fc6cd909bf17fb6e05cd1eb7946","src/lib.rs":"f549fbbd92b2e5e946a327eadf18216b9fe076f569bd623a32872797dc9eb4bc","src/macros.rs":"e1d542b1dac2c1d1f9d5ada7cc5b6639767fc67851421cc3adfb942a7cf750b6","src/private/de.rs":"15b82edcadaa60e748bf6ee0e0e14464cf945f39cb7493d3c1684cc34f6594b6","src/private/macros.rs":"6861a4f332ea24d0ed5db1c28fe3105d2716523902f045c0bbbd439ebf9e44de","src/private/mod.rs":"bcd7c54838e139475c23a323678e20eccbe88c0be93f7977f7675cead4d3b6ed","src/private/ser.rs":"eec5aecf077cebf4dc3fdbe83780f716bf33d311806ee59ce42a0c53c2f92211","src/ser/impls.rs":"dc5219e898d67c9422fef81d1f36d25ea92341c5ad3d976c0017886f371a8d51","src/ser/impossible.rs":"35bd09bb517b28eda0048b0622eb5a0313d5aebf37c03b5a44dbca200d0a9ac8","src/ser/mod.rs":"3d92794f13c8d90c2ebc33716bd6e4d4dde552c6f7de42dfaf9967d8a9ab7d10"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"b5e865ec105f4f546fecdf164172e670b2b82a2533700f556cc1c1c246a4ce14","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7dc6376a88195594cd6b43cb891aad10f57144ff737178b55d046aa04739b43a","src/de/from_primitive.rs":"28ec3ab1c430cf27d632b642ccfccb6d055eeb9fb576e7e446ba24c66f507fb4","src/de/ignored_any.rs":"864eaefef0aaae36daf0c2bdee08165dabbf60710f3217142d5e280c0a35c1fe","src/de/impls.rs":"e0d8b5255afb175daf720dd5b1072d2d1888fce6790de57fe51d8d2b51fc1603","src/de/mod.rs":"2984925d7844816cacc290067db049766912d1393732d9df280e6ba020582afc","src/de/utf8.rs":"956b124b7ce98353cb781b56e43a6fed2e67f1389d35b7a468d5be75b1485853","src/de/value.rs":"463e107e9ce9a56cc22901aeb60edbf0d10b144ca03dcdb78988d68f6c96022b","src/export.rs":"dd08253f225862aa5009b27900e04187480c96562c35205b71b36b2ac64c4cce","src/lib.rs":"d8411e8311ec29d8d7184556e014a11f03fe84af26f64daeaf34ad449310084d","src/macros.rs":"e1d542b1dac2c1d1f9d5ada7cc5b6639767fc67851421cc3adfb942a7cf750b6","src/private/de.rs":"55403af32b5b4112ab2203c1598bb033308f8c59ed5c4b5f4730dc2f26d3808d","src/private/macros.rs":"6861a4f332ea24d0ed5db1c28fe3105d2716523902f045c0bbbd439ebf9e44de","src/private/mod.rs":"bcd7c54838e139475c23a323678e20eccbe88c0be93f7977f7675cead4d3b6ed","src/private/ser.rs":"fed0c80a55a214c9bf411fe591f8f99562b01fcd27dfe968f6cc9d694a9c60b2","src/ser/impls.rs":"7eb99e5a74a3fcbb611a6ad762d1915a0ae1f5bb7a95f54115936db3bdde32ca","src/ser/impossible.rs":"009dce92e20bd25335db7d747c595111f5eb8d21dda0f6c75bccf0d0608c5751","src/ser/mod.rs":"b0b970c9e4987db7fbd7bc3bb9f32448e2de864c6596829922cf8fe131dae23d"},"package":"db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"}
\ No newline at end of file
diff --git a/third_party/rust/serde/Cargo.toml b/third_party/rust/serde/Cargo.toml
index 0e44a8db93f2..1741977aa944 100644
--- a/third_party/rust/serde/Cargo.toml
+++ b/third_party/rust/serde/Cargo.toml
@@ -1,68 +1,44 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
[package]
name = "serde"
-version = "1.0.23" # remember to update html_root_url
+version = "1.0.27"
authors = ["Erick Tryzelaar ", "David Tolnay "]
-license = "MIT/Apache-2.0"
+include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
description = "A generic serialization/deserialization framework"
homepage = "https://serde.rs"
-repository = "https://github.com/serde-rs/serde"
documentation = "https://docs.serde.rs/serde/"
+readme = "README.md"
keywords = ["serde", "serialization", "no_std"]
categories = ["encoding"]
-readme = "README.md"
-include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
-
-[badges]
-travis-ci = { repository = "serde-rs/serde" }
-appveyor = { repository = "serde-rs/serde" }
-
-[dependencies]
-serde_derive = { version = "1.0", optional = true, path = "../serde_derive" }
-
-[dev-dependencies]
-serde_derive = { version = "1.0", path = "../serde_derive" }
-
-
-### FEATURES #################################################################
+license = "MIT/Apache-2.0"
+repository = "https://github.com/serde-rs/serde"
+[dependencies.serde_derive]
+version = "1.0"
+optional = true
+[dev-dependencies.serde_derive]
+version = "1.0"
[features]
-default = ["std"]
-
-# Re-export the derive(Serialize, Deserialize) macros. This is specifically
-# intended for library crates that provide optional Serde impls behind a Cargo
-# cfg of their own. All other crates should depend on serde_derive directly.
-#
-# Please refer to the long comment above the line `pub use serde_derive::*` in
-# src/lib.rs before enabling this feature. If you think you need this feature
-# and your use case does not precisely match the one described in the comment,
-# please open an issue to let us know about your use case.
-derive = ["serde_derive"]
-
-# Provide impls for common standard library types like Vec and HashMap.
-# Requires a dependency on the Rust standard library.
-std = []
-
-# Provide impls for types that require unstable functionality. For tracking and
-# discussion of unstable functionality please refer to this issue:
-#
-# https://github.com/serde-rs/serde/issues/812
-unstable = []
-
-# Provide impls for types in the Rust core allocation and collections library
-# including String, Box, Vec, and Cow. This is a subset of std but may
-# be enabled without depending on all of std.
-#
-# Requires a dependency on the unstable core allocation library:
-#
-# https://doc.rust-lang.org/alloc/
alloc = ["unstable"]
-
-# Opt into impls for Rc and Arc. Serializing and deserializing these types
-# does not preserve identity and may result in multiple copies of the same data.
-# Be sure that this is what you want before enabling this feature.
-rc = []
-
-# Get serde_derive picked up by the Integer 32 playground. Not public API.
-#
-# http://play.integer32.com/
+default = ["std"]
+derive = ["serde_derive"]
playground = ["serde_derive"]
+rc = []
+std = []
+unstable = []
+[badges.appveyor]
+repository = "serde-rs/serde"
+
+[badges.travis-ci]
+repository = "serde-rs/serde"
diff --git a/third_party/rust/serde/src/de/ignored_any.rs b/third_party/rust/serde/src/de/ignored_any.rs
index a4f3abe26479..20b06b90bd58 100644
--- a/third_party/rust/serde/src/de/ignored_any.rs
+++ b/third_party/rust/serde/src/de/ignored_any.rs
@@ -8,7 +8,7 @@
use lib::*;
-use de::{Deserialize, Deserializer, Visitor, SeqAccess, MapAccess, Error};
+use de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor};
/// An efficient way of discarding data from a deserializer.
///
diff --git a/third_party/rust/serde/src/de/impls.rs b/third_party/rust/serde/src/de/impls.rs
index c7ea2ede2f44..d872cbe61e1c 100644
--- a/third_party/rust/serde/src/de/impls.rs
+++ b/third_party/rust/serde/src/de/impls.rs
@@ -15,7 +15,7 @@ use de::{Deserialize, Deserializer, EnumAccess, Error, SeqAccess, Unexpected, Va
use de::MapAccess;
use de::from_primitive::FromPrimitive;
-use private::de::DeserializeFromSeed;
+use private::de::InPlaceSeed;
#[cfg(any(feature = "std", feature = "alloc"))]
use private::de::size_hint;
@@ -52,7 +52,6 @@ impl<'de> Deserialize<'de> for () {
struct BoolVisitor;
-
impl<'de> Visitor<'de> for BoolVisitor {
type Value = bool;
@@ -213,7 +212,7 @@ impl<'de> Deserialize<'de> for char {
#[cfg(any(feature = "std", feature = "alloc"))]
struct StringVisitor;
#[cfg(any(feature = "std", feature = "alloc"))]
-struct StringFromVisitor<'a>(&'a mut String);
+struct StringInPlaceVisitor<'a>(&'a mut String);
#[cfg(any(feature = "std", feature = "alloc"))]
impl<'de> Visitor<'de> for StringVisitor {
@@ -253,13 +252,16 @@ impl<'de> Visitor<'de> for StringVisitor {
{
match String::from_utf8(v) {
Ok(s) => Ok(s),
- Err(e) => Err(Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self),),
+ Err(e) => Err(Error::invalid_value(
+ Unexpected::Bytes(&e.into_bytes()),
+ &self,
+ )),
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
-impl<'a, 'de> Visitor<'de> for StringFromVisitor<'a> {
+impl<'a, 'de> Visitor<'de> for StringInPlaceVisitor<'a> {
type Value = ();
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
@@ -306,7 +308,10 @@ impl<'a, 'de> Visitor<'de> for StringFromVisitor<'a> {
*self.0 = s;
Ok(())
}
- Err(e) => Err(Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self),),
+ Err(e) => Err(Error::invalid_value(
+ Unexpected::Bytes(&e.into_bytes()),
+ &self,
+ )),
}
}
}
@@ -320,11 +325,11 @@ impl<'de> Deserialize<'de> for String {
deserializer.deserialize_string(StringVisitor)
}
- fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error>
+ fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
- deserializer.deserialize_string(StringFromVisitor(self))
+ deserializer.deserialize_string(StringInPlaceVisitor(place))
}
}
@@ -485,7 +490,6 @@ forwarded_impl!((), Box, CString::into_boxed_c_str);
struct OptionVisitor {
marker: PhantomData,
}
-struct OptionFromVisitor<'a, T: 'a>(&'a mut Option);
impl<'de, T> Visitor<'de> for OptionVisitor
where
@@ -522,49 +526,6 @@ where
}
}
-impl<'a, 'de, T> Visitor<'de> for OptionFromVisitor<'a, T>
-where
- T: Deserialize<'de>,
-{
- type Value = ();
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("option")
- }
-
- #[inline]
- fn visit_unit(self) -> Result<(), E>
- where
- E: Error,
- {
- *self.0 = None;
- Ok(())
- }
-
- #[inline]
- fn visit_none(self) -> Result<(), E>
- where
- E: Error,
- {
- *self.0 = None;
- Ok(())
- }
-
- #[inline]
- fn visit_some(self, deserializer: D) -> Result<(), D::Error>
- where
- D: Deserializer<'de>,
- {
- // The some enum's repr is opaque, so we can't play cute tricks with
- // its tag to build this in place unconditionally.
- //
- // FIXME: investigate whether branching on the old value being Some to
- // deserialize_from the value is profitable (probably data-dependent?)
- *self.0 = try!(T::deserialize(deserializer).map(Some));
- Ok(())
- }
-}
-
impl<'de, T> Deserialize<'de> for Option
where
T: Deserialize<'de>,
@@ -573,24 +534,25 @@ where
where
D: Deserializer<'de>,
{
- deserializer.deserialize_option(OptionVisitor { marker: PhantomData })
+ deserializer.deserialize_option(OptionVisitor {
+ marker: PhantomData,
+ })
}
- fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error>
- where
- D: Deserializer<'de>,
- {
- deserializer.deserialize_option(OptionFromVisitor(self))
- }
+ // The Some variant's repr is opaque, so we can't play cute tricks with its
+ // tag to have deserialize_in_place build the content in place unconditionally.
+ //
+ // FIXME: investigate whether branching on the old value being Some to
+ // deserialize_in_place the value is profitable (probably data-dependent?)
}
////////////////////////////////////////////////////////////////////////////////
-struct PhantomDataVisitor {
+struct PhantomDataVisitor {
marker: PhantomData,
}
-impl<'de, T> Visitor<'de> for PhantomDataVisitor {
+impl<'de, T: ?Sized> Visitor<'de> for PhantomDataVisitor {
type Value = PhantomData;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
@@ -606,12 +568,14 @@ impl<'de, T> Visitor<'de> for PhantomDataVisitor {
}
}
-impl<'de, T> Deserialize<'de> for PhantomData {
+impl<'de, T: ?Sized> Deserialize<'de> for PhantomData