Merge autoland to central, a=merge

MozReview-Commit-ID: 4jAMhgCDoPO
This commit is contained in:
Wes Kocher 2017-10-03 13:25:44 -07:00
Родитель 6068998290 e1c96e5c8e
Коммит 7b3327cb2e
168 изменённых файлов: 2575 добавлений и 978 удалений

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

@ -8,7 +8,6 @@ obj*/**
# If you are enabling a directory, please add directory specific exclusions
# below.
chrome/**
config/**
docshell/**
editor/**
embedding/**
@ -36,6 +35,7 @@ xpcom/**
# We currently have no js files in these directories, so we ignore them by
# default to aid ESLint's performance.
build/**
config/**
db/**
gradle/**
hal/**

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

@ -461,7 +461,7 @@ var BrowserPageActions = {
},
doCommandForAction(action, event, buttonNode) {
if (event.type == "click" && event.button != 0) {
if (event && event.type == "click" && event.button != 0) {
return;
}
PageActions.logTelemetry("used", action, buttonNode);

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

@ -245,7 +245,6 @@ window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-but
%endif
%ifndef MOZ_WIDGET_COCOA
#main-window:not([sizemode=normal]) .titlebar-placeholder[type="pre-tabs"],
#main-window:not([sizemode=normal]) .titlebar-placeholder[type="post-tabs"],
%endif
#main-window:not([chromemargin]) > #titlebar,
#main-window[inFullscreen] > #titlebar,

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

@ -2214,6 +2214,7 @@ function BrowserGoHome(aEvent) {
switch (where) {
case "current":
loadOneOrMoreURIs(homePage, Services.scriptSecurityManager.getSystemPrincipal());
gBrowser.selectedBrowser.focus();
break;
case "tabshifted":
case "tab":

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

@ -2,5 +2,12 @@
support-files =
head.js
discovery.html
file_rich_icon.html
file_mask_icon.html
moz.png
rich_moz_1.png
rich_moz_2.png
[browser_multiple_icons_in_short_timeframe.js]
[browser_rich_icons.js]
[browser_icon_discovery.js]

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

@ -0,0 +1,96 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
add_task(async function() {
let url = "http://mochi.test:8888/browser/browser/base/content/test/favicons/discovery.html";
info("Test icons discovery");
// First we need to clear the failed favicons cache, since previous tests
// likely added this non-existing icon, and useDefaultIcon would skip it.
PlacesUtils.favicons.removeFailedFavicon(makeURI("http://mochi.test:8888/favicon.ico"));
await BrowserTestUtils.withNewTab(url, iconDiscovery);
});
let iconDiscoveryTests = [
{ text: "rel icon discovered" },
{ rel: "abcdefg icon qwerty", text: "rel may contain additional rels separated by spaces" },
{ rel: "ICON", text: "rel is case insensitive" },
{ rel: "shortcut-icon", pass: false, text: "rel shortcut-icon not discovered" },
{ href: "moz.png", text: "relative href works" },
{ href: "notthere.png", text: "404'd icon is removed properly" },
{ href: "data:image/x-icon,%00", type: "image/x-icon", text: "data: URIs work" },
{ type: "image/png; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
{ richIcon: true, rel: "apple-touch-icon", text: "apple-touch-icon discovered" },
{ richIcon: true, rel: "apple-touch-icon-precomposed", text: "apple-touch-icon-precomposed discovered" },
{ richIcon: true, rel: "fluid-icon", text: "fluid-icon discovered" },
{ richIcon: true, rel: "unknown-icon", pass: false, text: "unknown icon not discovered" }
];
async function iconDiscovery() {
// Since the page doesn't have an icon, we should try using the root domain
// icon.
await BrowserTestUtils.waitForCondition(() => {
return gBrowser.getIcon() == "http://mochi.test:8888/favicon.ico";
}, "wait for default icon load to finish");
for (let testCase of iconDiscoveryTests) {
if (testCase.pass == undefined)
testCase.pass = true;
// Clear the current icon.
gBrowser.setIcon(gBrowser.selectedTab, null);
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, null, true);
let promiseMessage = new Promise(resolve => {
let mm = window.messageManager;
mm.addMessageListener("Link:SetIcon", function listenForIcon(msg) {
mm.removeMessageListener("Link:SetIcon", listenForIcon);
resolve(msg.data);
});
});
await ContentTask.spawn(gBrowser.selectedBrowser, testCase, test => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = test.rel || "icon";
link.href = test.href || "http://mochi.test:8888/browser/browser/base/content/test/favicons/moz.png";
link.type = test.type || "image/png";
head.appendChild(link);
});
await promiseLinkAdded;
if (!testCase.richIcon) {
// Because there is debounce logic in ContentLinkHandler.jsm to reduce the
// favicon loads, we have to wait some time before checking that icon was
// stored properly.
try {
await BrowserTestUtils.waitForCondition(() => {
return gBrowser.getIcon() != null;
}, "wait for icon load to finish", 100, 20);
ok(testCase.pass, testCase.text);
} catch (ex) {
ok(!testCase.pass, testCase.text);
}
} else {
// Rich icons are not set as tab icons, so just check for the SetIcon message.
try {
let data = await Promise.race([promiseMessage,
new Promise((resolve, reject) => setTimeout(reject, 2000))]);
is(data.canUseForTab, false, "Rich icons cannot be used for tabs");
ok(testCase.pass, testCase.text);
} catch (ex) {
ok(!testCase.pass, testCase.text);
}
}
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
let head = content.document.getElementById("linkparent");
head.removeChild(head.getElementsByTagName("link")[0]);
});
}
}

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

@ -0,0 +1,69 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
const ROOT = "http://mochi.test:8888/browser/browser/base/content/test/favicons/"
add_task(async function test_richIcons() {
const URL = ROOT + "file_rich_icon.html";
const EXPECTED_ICON = ROOT + "moz.png";
const EXPECTED_RICH_ICON = ROOT + "rich_moz_2.png";
// One regular icon and one rich icon. Note that ContentLinkHandler will
// choose the best rich icon if there are multiple candidates available
// in the page.
const EXPECTED_ICON_LOADS = 2;
let loadCount = 0;
let tabIconUri;
let richIconUri;
const promiseMessage = new Promise(resolve => {
const mm = window.messageManager;
mm.addMessageListener("Link:SetIcon", function listenForSetIcon(msg) {
// Ignore the chrome favicon sets on the tab before the actual page load.
if (msg.data.url === "chrome://branding/content/icon32.png")
return;
if (!msg.data.canUseForTab)
richIconUri = msg.data.url;
if (++loadCount === EXPECTED_ICON_LOADS) {
mm.removeMessageListener("Link:SetIcon", listenForSetIcon);
resolve();
}
});
});
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
await promiseMessage;
is(richIconUri, EXPECTED_RICH_ICON, "should choose the largest rich icon");
// Because there is debounce logic in ContentLinkHandler.jsm to reduce the
// favicon loads, we have to wait some time before checking that icon was
// stored properly.
await BrowserTestUtils.waitForCondition(() => {
tabIconUri = gBrowser.getIcon();
return tabIconUri != null;
}, "wait for icon load to finish", 100, 20);
is(tabIconUri, EXPECTED_ICON, "should use the non-rich icon for the tab");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_maskIcons() {
const URL = ROOT + "file_mask_icon.html";
// First we need to clear the failed favicons cache, since previous tests
// likely added this non-existing icon, and useDefaultIcon would skip it.
PlacesUtils.favicons.removeFailedFavicon(makeURI("http://mochi.test:8888/favicon.ico"));
const EXPECTED_ICON = "http://mochi.test:8888/favicon.ico";
let tabIconUri;
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
await BrowserTestUtils.waitForCondition(() => {
tabIconUri = gBrowser.getIcon();
return tabIconUri != null;
}, "wait for icon load to finish", 100, 20);
is(tabIconUri, EXPECTED_ICON, "should ignore the mask icons and load the root favicon");
await BrowserTestUtils.removeTab(tab);
});

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

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8" />
<title>Mask Icon</title>
<link rel="icon" mask href="moz.png" type="image/png" />
<link rel="mask-icon" href="moz.png" type="image/png" />
</head>
<body>
</body>
</html>

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

@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8" />
<title>Rich Icons</title>
<link rel="icon" href="moz.png" type="image/png" />
<link rel="apple-touch-icon" sizes="96x96" href="rich_moz_1.png" type="image/png" />
<link rel="apple-touch-icon" sizes="256x256" href="rich_moz_2.png" type="image/png" />
</head>
<body>
</body>
</html>

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

@ -7,3 +7,5 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
"resource://testing-common/BrowserTestUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentTask",
"resource://testing-common/ContentTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");

Двоичные данные
browser/base/content/test/favicons/moz.png Normal file

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

После

Ширина:  |  Высота:  |  Размер: 580 B

Двоичные данные
browser/base/content/test/favicons/rich_moz_1.png Normal file

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

После

Ширина:  |  Высота:  |  Размер: 580 B

Двоичные данные
browser/base/content/test/favicons/rich_moz_2.png Normal file

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

После

Ширина:  |  Высота:  |  Размер: 580 B

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

@ -326,7 +326,7 @@ skip-if = !datareporting
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_decoderDoctor.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_discovery.js]
[browser_search_discovery.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_double_close_tab.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.

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

@ -1,181 +0,0 @@
/* eslint-disable mozilla/no-arbitrary-setTimeout */
add_task(async function() {
let url = "http://mochi.test:8888/browser/browser/base/content/test/general/discovery.html";
info("Test icons discovery");
// First we need to clear the failed favicons cache, since previous tests
// likely added this non-existing icon, and useDefaultIcon would skip it.
PlacesUtils.favicons.removeFailedFavicon(makeURI("http://mochi.test:8888/favicon.ico"));
await BrowserTestUtils.withNewTab(url, iconDiscovery);
info("Test search discovery");
await BrowserTestUtils.withNewTab(url, searchDiscovery);
});
let iconDiscoveryTests = [
{ text: "rel icon discovered" },
{ rel: "abcdefg icon qwerty", text: "rel may contain additional rels separated by spaces" },
{ rel: "ICON", text: "rel is case insensitive" },
{ rel: "shortcut-icon", pass: false, text: "rel shortcut-icon not discovered" },
{ href: "moz.png", text: "relative href works" },
{ href: "notthere.png", text: "404'd icon is removed properly" },
{ href: "data:image/x-icon,%00", type: "image/x-icon", text: "data: URIs work" },
{ type: "image/png; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
// These rich icon tests are temporarily disabled due to intermittent failures.
/*
{ richIcon: true, rel: "apple-touch-icon", text: "apple-touch-icon discovered" },
{ richIcon: true, rel: "apple-touch-icon-precomposed", text: "apple-touch-icon-precomposed discovered" },
{ richIcon: true, rel: "fluid-icon", text: "fluid-icon discovered" },
*/
{ richIcon: true, rel: "unknown-icon", pass: false, text: "unknown icon not discovered" }
];
async function iconDiscovery() {
// Since the page doesn't have an icon, we should try using the root domain
// icon.
await BrowserTestUtils.waitForCondition(() => {
return gBrowser.getIcon() == "http://mochi.test:8888/favicon.ico";
}, "wait for default icon load to finish");
for (let testCase of iconDiscoveryTests) {
if (testCase.pass == undefined)
testCase.pass = true;
testCase.rootDir = getRootDirectory(gTestPath);
// Clear the current icon.
gBrowser.setIcon(gBrowser.selectedTab, null);
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, null, true);
let promiseMessage = new Promise(resolve => {
let mm = window.messageManager;
mm.addMessageListener("Link:SetIcon", function listenForIcon(msg) {
mm.removeMessageListener("Link:SetIcon", listenForIcon);
resolve(msg.data);
});
});
await ContentTask.spawn(gBrowser.selectedBrowser, testCase, test => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = test.rel || "icon";
link.href = test.href || test.rootDir + "moz.png";
link.type = test.type || "image/png";
head.appendChild(link);
});
await promiseLinkAdded;
if (!testCase.richIcon) {
// Because there is debounce logic in ContentLinkHandler.jsm to reduce the
// favicon loads, we have to wait some time before checking that icon was
// stored properly.
try {
await BrowserTestUtils.waitForCondition(() => {
return gBrowser.getIcon() != null;
}, "wait for icon load to finish", 100, 20);
ok(testCase.pass, testCase.text);
} catch (ex) {
ok(!testCase.pass, testCase.text);
}
} else {
// Rich icons are not set as tab icons, so just check for the SetIcon message.
try {
let data = await Promise.race([promiseMessage,
new Promise((resolve, reject) => setTimeout(reject, 2000))]);
is(data.canUseForTab, false, "Rich icons cannot be used for tabs");
ok(testCase.pass, testCase.text);
} catch (ex) {
ok(!testCase.pass, testCase.text);
}
}
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
let head = content.document.getElementById("linkparent");
head.removeChild(head.getElementsByTagName("link")[0]);
});
}
}
let searchDiscoveryTests = [
{ text: "rel search discovered" },
{ rel: "SEARCH", text: "rel is case insensitive" },
{ rel: "-search-", pass: false, text: "rel -search- not discovered" },
{ rel: "foo bar baz search quux", text: "rel may contain additional rels separated by spaces" },
{ href: "https://not.mozilla.com", text: "HTTPS ok" },
{ href: "ftp://not.mozilla.com", text: "FTP ok" },
{ href: "data:text/foo,foo", pass: false, text: "data URI not permitted" },
{ href: "javascript:alert(0)", pass: false, text: "JS URI not permitted" },
{ type: "APPLICATION/OPENSEARCHDESCRIPTION+XML", text: "type is case insensitve" },
{ type: " application/opensearchdescription+xml ", text: "type may contain extra whitespace" },
{ type: "application/opensearchdescription+xml; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
{ type: "aapplication/opensearchdescription+xml", pass: false, text: "type should not be loosely matched" },
{ rel: "search search search", count: 1, text: "only one engine should be added" }
];
async function searchDiscovery() {
let browser = gBrowser.selectedBrowser;
for (let testCase of searchDiscoveryTests) {
if (testCase.pass == undefined)
testCase.pass = true;
testCase.title = testCase.title || searchDiscoveryTests.indexOf(testCase);
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, null, true);
await ContentTask.spawn(gBrowser.selectedBrowser, testCase, test => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = test.rel || "search";
link.href = test.href || "http://so.not.here.mozilla.com/search.xml";
link.type = test.type || "application/opensearchdescription+xml";
link.title = test.title;
head.appendChild(link);
});
await promiseLinkAdded;
await new Promise(resolve => executeSoon(resolve));
if (browser.engines) {
info(`Found ${browser.engines.length} engines`);
info(`First engine title: ${browser.engines[0].title}`);
let hasEngine = testCase.count ?
(browser.engines[0].title == testCase.title && browser.engines.length == testCase.count) :
(browser.engines[0].title == testCase.title);
ok(hasEngine, testCase.text);
browser.engines = null;
} else {
ok(!testCase.pass, testCase.text);
}
}
info("Test multiple engines with the same title");
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, e => e.target.href == "http://second.mozilla.com/search.xml", true);
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = "search";
link.href = "http://first.mozilla.com/search.xml";
link.type = "application/opensearchdescription+xml";
link.title = "Test Engine";
let link2 = link.cloneNode(false);
link2.href = "http://second.mozilla.com/search.xml";
head.appendChild(link);
head.appendChild(link2);
});
await promiseLinkAdded;
await new Promise(resolve => executeSoon(resolve));
ok(browser.engines, "has engines");
is(browser.engines.length, 1, "only one engine");
is(browser.engines[0].uri, "http://first.mozilla.com/search.xml", "first engine wins");
browser.engines = null;
}

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

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function() {
let url = "http://mochi.test:8888/browser/browser/base/content/test/general/discovery.html";
info("Test search discovery");
await BrowserTestUtils.withNewTab(url, searchDiscovery);
});
let searchDiscoveryTests = [
{ text: "rel search discovered" },
{ rel: "SEARCH", text: "rel is case insensitive" },
{ rel: "-search-", pass: false, text: "rel -search- not discovered" },
{ rel: "foo bar baz search quux", text: "rel may contain additional rels separated by spaces" },
{ href: "https://not.mozilla.com", text: "HTTPS ok" },
{ href: "ftp://not.mozilla.com", text: "FTP ok" },
{ href: "data:text/foo,foo", pass: false, text: "data URI not permitted" },
{ href: "javascript:alert(0)", pass: false, text: "JS URI not permitted" },
{ type: "APPLICATION/OPENSEARCHDESCRIPTION+XML", text: "type is case insensitve" },
{ type: " application/opensearchdescription+xml ", text: "type may contain extra whitespace" },
{ type: "application/opensearchdescription+xml; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
{ type: "aapplication/opensearchdescription+xml", pass: false, text: "type should not be loosely matched" },
{ rel: "search search search", count: 1, text: "only one engine should be added" }
];
async function searchDiscovery() {
let browser = gBrowser.selectedBrowser;
for (let testCase of searchDiscoveryTests) {
if (testCase.pass == undefined)
testCase.pass = true;
testCase.title = testCase.title || searchDiscoveryTests.indexOf(testCase);
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, null, true);
await ContentTask.spawn(gBrowser.selectedBrowser, testCase, test => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = test.rel || "search";
link.href = test.href || "http://so.not.here.mozilla.com/search.xml";
link.type = test.type || "application/opensearchdescription+xml";
link.title = test.title;
head.appendChild(link);
});
await promiseLinkAdded;
await new Promise(resolve => executeSoon(resolve));
if (browser.engines) {
info(`Found ${browser.engines.length} engines`);
info(`First engine title: ${browser.engines[0].title}`);
let hasEngine = testCase.count ?
(browser.engines[0].title == testCase.title && browser.engines.length == testCase.count) :
(browser.engines[0].title == testCase.title);
ok(hasEngine, testCase.text);
browser.engines = null;
} else {
ok(!testCase.pass, testCase.text);
}
}
info("Test multiple engines with the same title");
let promiseLinkAdded =
BrowserTestUtils.waitForContentEvent(gBrowser.selectedBrowser, "DOMLinkAdded",
false, e => e.target.href == "http://second.mozilla.com/search.xml", true);
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
let doc = content.document;
let head = doc.getElementById("linkparent");
let link = doc.createElement("link");
link.rel = "search";
link.href = "http://first.mozilla.com/search.xml";
link.type = "application/opensearchdescription+xml";
link.title = "Test Engine";
let link2 = link.cloneNode(false);
link2.href = "http://second.mozilla.com/search.xml";
head.appendChild(link);
head.appendChild(link2);
});
await promiseLinkAdded;
await new Promise(resolve => executeSoon(resolve));
ok(browser.engines, "has engines");
is(browser.engines.length, 1, "only one engine");
is(browser.engines[0].uri, "http://first.mozilla.com/search.xml", "first engine wins");
browser.engines = null;
}

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

@ -224,21 +224,27 @@ function promiseNewSearchEngine(basename) {
}
function promisePageActionPanelOpen() {
if (BrowserPageActions.panelNode.state == "open") {
return Promise.resolve();
}
// The main page action button is hidden for some URIs, so make sure it's
// visible before trying to click it.
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
// Wait for the main page action button to become visible. It's hidden for
// some URIs, so depending on when this is called, it may not yet be quite
// visible. It's up to the caller to make sure it will be visible.
info("Waiting for main page action button to have non-0 size");
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
return bounds.width > 0 && bounds.height > 0;
}).then(() => {
// Wait for the panel to become open, by clicking the button if necessary.
info("Waiting for main page action panel to be open");
if (BrowserPageActions.panelNode.state == "open") {
return Promise.resolve();
}
let shownPromise = promisePageActionPanelShown();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
return shownPromise;
}).then(() => {
// Wait for items in the panel to become visible.
return promisePageActionViewChildrenVisible(BrowserPageActions.mainViewNode);
});
}
@ -275,28 +281,30 @@ function promisePanelEvent(panelIDOrNode, eventType) {
}
function promisePageActionViewShown() {
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
info("promisePageActionViewShown waiting for ViewShown");
return BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "ViewShown").then(async event => {
let panelViewNode = event.originalTarget;
// Wait for the subview to be really truly shown by making sure there's at
// least one child with non-zero bounds.
info("promisePageActionViewShown waiting for a child node to be visible");
await BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
await promisePageActionViewChildrenVisible(panelViewNode);
return panelViewNode;
});
}
function promisePageActionViewChildrenVisible(panelViewNode) {
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
}
function promiseSpeculativeConnection(httpserver) {
return BrowserTestUtils.waitForCondition(() => {
if (httpserver) {

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

@ -1648,7 +1648,8 @@ CustomizeMode.prototype = {
__dumpDragData(aEvent);
let item = aEvent.target;
while (item && item.localName != "toolbarpaletteitem") {
if (item.localName == "toolbar") {
if (item.localName == "toolbar" || item.id == kPaletteId ||
item.id == "customization-panelHolder") {
return;
}
item = item.parentNode;
@ -1669,6 +1670,12 @@ CustomizeMode.prototype = {
this._dragOffset = {x: aEvent.clientX - itemCenter.x,
y: aEvent.clientY - itemCenter.y};
let toolbarParent = draggedItem.closest("toolbar");
if (toolbarParent) {
let toolbarRect = this._dwu.getBoundsWithoutFlushing(toolbarParent);
toolbarParent.style.minHeight = toolbarRect.height + "px";
}
gDraggingInToolbars = new Set();
// Hack needed so that the dragimage will still show the
@ -1688,6 +1695,8 @@ CustomizeMode.prototype = {
this._setDragActive(item.previousSibling, "after", draggedItem.id, placeForItem);
this._dragOverItem = item.previousSibling;
}
let currentArea = this._getCustomizableParent(item);
currentArea.setAttribute("draggingover", "true");
}
this._initializeDragAfterMove = null;
this.window.clearTimeout(this._dragInitializeTimeout);
@ -1796,6 +1805,7 @@ CustomizeMode.prototype = {
this._setDragActive(dragOverItem, dragValue, draggedItemId, targetAreaType);
}
this._dragOverItem = dragOverItem;
targetArea.setAttribute("draggingover", "true");
}
aEvent.preventDefault();
@ -1860,6 +1870,11 @@ CustomizeMode.prototype = {
draggedItem.hidden = false;
draggedItem.removeAttribute("mousedown");
let toolbarParent = draggedItem.closest("toolbar");
if (toolbarParent) {
toolbarParent.style.removeProperty("min-height");
}
// Do nothing if the target was dropped onto itself (ie, no change in area
// or position).
if (draggedItem == aTargetNode) {
@ -2035,6 +2050,11 @@ CustomizeMode.prototype = {
if (draggedWrapper) {
draggedWrapper.hidden = false;
draggedWrapper.removeAttribute("mousedown");
let toolbarParent = draggedWrapper.closest("toolbar");
if (toolbarParent) {
toolbarParent.style.removeProperty("min-height");
}
}
if (this._dragOverItem) {
@ -2119,6 +2139,10 @@ CustomizeMode.prototype = {
if (!currentArea) {
return;
}
let nextArea = aNextItem ? this._getCustomizableParent(aNextItem) : null;
if (currentArea != nextArea) {
currentArea.removeAttribute("draggingover");
}
let areaType = CustomizableUI.getAreaType(currentArea.id);
if (areaType) {
if (aNoTransition) {
@ -2139,7 +2163,6 @@ CustomizeMode.prototype = {
} else {
aItem.removeAttribute("dragover");
if (aNextItem) {
let nextArea = this._getCustomizableParent(aNextItem);
if (nextArea == currentArea) {
// No need to do anything if we're still dragging in this area:
return;
@ -2227,7 +2250,7 @@ CustomizeMode.prototype = {
// Deal with drag/drop on the padding of the panel.
let containingPanelHolder = aElement.closest("#customization-panelHolder");
if (containingPanelHolder) {
return containingPanelHolder.firstChild;
return containingPanelHolder.querySelector("#widget-overflow-fixed-list");
}
}

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

@ -26,7 +26,10 @@
<image class="panel-arrow" side="top"/>
</box>
<box class="panel-arrowcontent" side="top" flex="1">
<hbox id="customization-panelHolder"/>
<vbox id="customization-panelHolder">
<description id="customization-panelHeader">&customizeMode.overflowList.title;</description>
<description id="customization-panelDescription">&customizeMode.overflowList.description;</description>
</vbox>
<box class="panel-inner-arrowcontentfooter" hidden="true"/>
</box>
</vbox>

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

@ -339,8 +339,7 @@
<vbox id="widget-overflow-list" class="widget-overflow-list"
overflowfortoolbar="nav-bar"/>
<toolbarseparator id="widget-overflow-fixed-separator" hidden="true"/>
<vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true"
emptylabel="&customizeMode.emptyOverflowList.description;"/>
<vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true" />
</vbox>
<toolbarbutton command="cmd_CustomizeToolbars"
id="overflowMenu-customize-button"

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

@ -118,9 +118,19 @@ this.browserAction = class extends ExtensionAPI {
this.tabContext = new TabContext(tab => Object.create(this.defaults),
extension);
// eslint-disable-next-line mozilla/balanced-listeners
this.tabContext.on("location-change", this.handleLocationChange.bind(this));
this.build();
}
handleLocationChange(eventType, tab, fromBrowse) {
if (fromBrowse) {
this.tabContext.clear(tab);
this.updateOnChange(tab);
}
}
onShutdown(reason) {
browserActionMap.delete(this.extension);

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

@ -214,6 +214,12 @@ add_task(async function testTabSwitchContext() {
"title": "Default Title 2",
"badge": "d2",
"badgeBackgroundColor": [0, 0xff, 0, 0xff]},
{"icon": browser.runtime.getURL("default-2.png"),
"popup": browser.runtime.getURL("default-2.html"),
"title": "Default Title 2",
"badge": "d2",
"badgeBackgroundColor": [0, 0xff, 0, 0xff],
"disabled": false},
];
return [
@ -251,20 +257,6 @@ add_task(async function testTabSwitchContext() {
await expectDefaults(details[0]);
expect(details[2]);
},
expect => {
browser.test.log("Navigate to a new page. Expect no changes.");
// TODO: This listener should not be necessary, but the |tabs.update|
// callback currently fires too early in e10s windows.
browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
if (tabId == tabs[1] && changed.url) {
browser.tabs.onUpdated.removeListener(listener);
expect(details[2]);
}
});
browser.tabs.update(tabs[1], {url: "about:blank?1"});
},
async expect => {
browser.test.log("Switch back to the first tab. Expect previously set properties.");
await browser.tabs.update(tabs[0], {active: true});
@ -296,6 +288,18 @@ add_task(async function testTabSwitchContext() {
await expectDefaults(details[3]);
expect(details[2]);
},
expect => {
browser.test.log("Navigate to a new page. Expect defaults.");
browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
if (tabId == tabs[1] && changed.url) {
browser.tabs.onUpdated.removeListener(listener);
expect(details[6]);
}
});
browser.tabs.update(tabs[1], {url: "about:blank?1"});
},
async expect => {
browser.test.log("Delete tab, switch back to tab 1. Expect previous results again.");
await browser.tabs.remove(tabs[1]);

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

@ -514,7 +514,7 @@
<checkbox id="trackingProtectionPBM"
preference="privacy.trackingprotection.pbmode.enabled"
accesskey="&trackingProtectionPBM6.accesskey;"/>
<label flex="1">&trackingProtectionPBM6.label;<label id="trackingProtectionPBMLearnMore"
<label flex="1">&trackingProtectionPBM6.label;<spacer class="tail-with-learn-more" /><label id="trackingProtectionPBMLearnMore"
class="learnMore text-link">&trackingProtectionPBMLearnMore.label;</label>
</label>
</hbox>

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

@ -32,10 +32,6 @@ var Pocket = {
/**
* Functions related to the Pocket panel UI.
*/
onBeforeCommand(event) {
BrowserUtils.setToolbarButtonHeightProperty(event.target);
},
onShownInPhotonPageActionPanel(panel, iframe) {
let window = panel.ownerGlobal;
window.pktUI.setPhotonPageActionPanelFrame(iframe);

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

@ -855,7 +855,8 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
<!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
<!ENTITY customizeMode.emptyOverflowList.description "Drag and drop items here to keep them within reach but out of your toolbar…">
<!ENTITY customizeMode.overflowList.title "Overflow Panel">
<!ENTITY customizeMode.overflowList.description "Drag and drop items here to keep them within reach but out of your toolbar…">
<!ENTITY customizeMode.uidensity "Density">
<!-- LOCALIZATION NOTE (customizeMode.uidensity.menuNormal.*):
Normal is displayed in the Customize screen, under the Density menu. -->

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

@ -974,21 +974,28 @@ add_task(async function migrate1() {
PageActions.actionForID("copyURL")._shownInUrlbar = false;
});
function promisePageActionPanelOpen() {
let button = document.getElementById("pageActionButton");
// The main page action button is hidden for some URIs, so make sure it's
// visible before trying to click it.
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
// Wait for the main page action button to become visible. It's hidden for
// some URIs, so depending on when this is called, it may not yet be quite
// visible. It's up to the caller to make sure it will be visible.
info("Waiting for main page action button to have non-0 size");
let bounds = dwu.getBoundsWithoutFlushing(button);
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
return bounds.width > 0 && bounds.height > 0;
}).then(() => {
// Wait for the panel to become open, by clicking the button if necessary.
info("Waiting for main page action panel to be open");
if (BrowserPageActions.panelNode.state == "open") {
return Promise.resolve();
}
let shownPromise = promisePageActionPanelShown();
EventUtils.synthesizeMouseAtCenter(button, {});
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
return shownPromise;
}).then(() => {
// Wait for items in the panel to become visible.
return promisePageActionViewChildrenVisible(BrowserPageActions.mainViewNode);
});
}
@ -1025,24 +1032,26 @@ function promisePanelEvent(panelIDOrNode, eventType) {
}
function promisePageActionViewShown() {
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
info("promisePageActionViewShown waiting for ViewShown");
return BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "ViewShown").then(async event => {
let panelViewNode = event.originalTarget;
// Wait for the subview to be really truly shown by making sure there's at
// least one child with non-zero bounds.
info("promisePageActionViewShown waiting for a child node to be visible");
await BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
await promisePageActionViewChildrenVisible(panelViewNode);
return panelViewNode;
});
}
function promisePageActionViewChildrenVisible(panelViewNode) {
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
}

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

@ -438,6 +438,7 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
border: 1px solid var(--arrowpanel-border-color);
box-shadow: 0 0 4px hsla(0,0%,0%,.2);
%endif
max-width: 29em;
}
#customization-panelWrapper > .panel-arrowbox {
@ -495,30 +496,61 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
}
%endif
#customization-panelDescription {
font-size: 1.1em;
padding: 2px 12px 10px;
margin: 0;
}
#customization-panelHeader {
font-size: 1.3em;
font-weight: 500;
padding: 2px 12px;
margin: 0;
}
#customization-panelHolder > #widget-overflow-fixed-list {
padding-top: 10px;
min-height: 225px;
}
/**
* We create a ::before pseudoelement that contains a background image to show the
* drop dragon. This element fades in and out depending on whether the containing
* panel list is empty and unhovered, or not.
*/
#customization-panelHolder > #widget-overflow-fixed-list:not(:empty) {
padding-bottom: 50px; /* Make sure there's always space to drop stuff. */
border-top: 1px solid rgba(0,0,0,0.1);
}
#customization-panelHolder > #widget-overflow-fixed-list:empty {
#customization-panelHolder > #widget-overflow-fixed-list::before {
display: block;
content: "";
background-image: url("chrome://browser/skin/customizableui/empty-overflow-panel.png");
background-position: center top 10px;
background-position: center bottom 10px;
background-size: 218px 134px;
background-repeat: no-repeat;
}
#customization-panelHolder > #widget-overflow-fixed-list:empty::after {
content: attr(emptylabel);
padding: 154px 10px 10px; /* 154 = 134 for the image, 10px space on either side. */
text-align: center;
display: block;
opacity: 0;
transition: opacity 300ms ease-out;
padding-bottom: 154px; /* 154 = 134 for the image, 10px space on either side. */
margin-bottom: -154px; /* don't affect positioning of the actual contents */
pointer-events: none;
}
@media (min-resolution: 1.1dppx) {
#customization-panelHolder > #widget-overflow-fixed-list:empty {
#customization-panelHolder > #widget-overflow-fixed-list::before {
background-image: url("chrome://browser/skin/customizableui/empty-overflow-panel@2x.png");
}
}
#customization-panelHolder > #widget-overflow-fixed-list:empty::before {
opacity: 1;
}
#customization-panelHolder > #widget-overflow-fixed-list[draggingover]:empty::before {
opacity: 0;
}
#downloads-button-autohide-panel > .panel-arrowcontainer > .panel-arrowcontent {
padding: 5px 12px;
}

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

@ -1,65 +0,0 @@
/* 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/. */
String.prototype.format = function string_format() {
// there are two modes of operation... unnamed indices are read in order;
// named indices using %(name)s. The two styles cannot be mixed.
// Unnamed indices can be passed as either a single argument to this function,
// multiple arguments to this function, or as a single array argument
let curindex = 0;
let d;
if (arguments.length > 1) {
d = arguments;
}
else
d = arguments[0];
function r(s, key, type) {
if (type == '%')
return '%';
let v;
if (key == "") {
if (curindex == -1)
throw Error("Cannot mix named and positional indices in string formatting.");
if (curindex == 0 && (!(d instanceof Object) || !(0 in d))) {
v = d;
}
else if (!(curindex in d))
throw Error("Insufficient number of items in format, requesting item %i".format(curindex));
else {
v = d[curindex];
}
++curindex;
}
else {
key = key.slice(1, -1);
if (curindex > 0)
throw Error("Cannot mix named and positional indices in string formatting.");
curindex = -1;
if (!(key in d))
throw Error("Key '%s' not present during string substitution.".format(key));
v = d[key];
}
switch (type) {
case "s":
if (v === undefined)
return "<undefined>";
return v.toString();
case "r":
return uneval(v);
case "i":
return parseInt(v);
case "f":
return Number(v);
default:
throw Error("Unexpected format character '%s'.".format(type));
}
}
return this.replace(/%(\([^)]+\))?(.)/g, r);
};

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

@ -13,7 +13,7 @@ function handleRequest(request, response) {
response.setHeader("Access-Control-Allow-Origin", "*", false);
// Redirect to a different file each time.
let counter = 1 + +getState("counter");
let counter = 1 + (+getState("counter") % 2);
let index = request.path.lastIndexOf("/");
let newPath = request.path.substr(0, index + 1) +

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

@ -13,7 +13,7 @@ function handleRequest(request, response) {
response.setHeader("Access-Control-Allow-Origin", "*", false);
// Redirect to a different file each time.
let counter = 1 + +getState("counter");
let counter = 1 + (+getState("counter") % 2);
let index = request.path.lastIndexOf("/");
let newPath = request.path.substr(0, index + 1) +

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

@ -313,16 +313,7 @@ BoxModel.prototype = {
return;
}
if (!this.inspector) {
return;
}
let node = this.inspector.selection.nodeFront;
this.inspector.pageStyle.getLayout(node, {
autoMargins: true,
}).then(layout => {
this.store.dispatch(updateLayout(layout));
}, console.error);
this.updateBoxModel("editable-value-change");
},
cssProperties: getCssProperties(this.inspector.toolbox)
}, event);

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

@ -14,6 +14,7 @@ support-files =
[browser_boxmodel.js]
[browser_boxmodel_computed-accordion-state.js]
[browser_boxmodel_edit-position-visible-position-change.js]
[browser_boxmodel_editablemodel.js]
[browser_boxmodel_editablemodel_allproperties.js]
disabled=too many intermittent failures (bug 1009322)

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

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the 'Edit Position' button is still visible after
// layout is changed.
// see bug 1398722
const TEST_URI = `
<div id="mydiv" style="background:tomato;
position:absolute;
top:10px;
left:10px;
width:100px;
height:100px">
`;
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openBoxModelView();
yield selectNode("#mydiv", inspector);
let editPositionButton = view.document.querySelector(".layout-geometry-editor");
ok(isNodeVisible(editPositionButton), "Edit Position button is visible initially");
let positionLeftTextbox = view.document.querySelector(
".boxmodel-editable[title=position-left]"
);
ok(isNodeVisible(positionLeftTextbox), "Position-left edit box exists");
info("Change the value of position-left and submit");
let onUpdate = waitForUpdate(inspector);
EventUtils.synthesizeMouseAtCenter(positionLeftTextbox, {}, view.document.defaultView);
EventUtils.synthesizeKey("8", {}, view.document.defaultView);
EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
yield onUpdate;
editPositionButton = view.document.querySelector(".layout-geometry-editor");
ok(isNodeVisible(editPositionButton),
"Edit Position button is still visible after layout change");
});

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

@ -36,6 +36,15 @@ function* selectAndHighlightNode(selectorOrNodeFront, inspector) {
yield updated;
}
/**
* Is the given node visible in the page (rendered in the frame tree).
* @param {DOMNode}
* @return {Boolean}
*/
function isNodeVisible(node) {
return !!node.getClientRects().length;
}
/**
* Open the toolbox, with the inspector tool visible, and the computed view
* sidebar tab selected to display the box model view.

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

@ -30,7 +30,8 @@ const {
} = require("devtools/client/inspector/shared/node-types");
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
const {createChild, promiseWarn, debounce} = require("devtools/client/inspector/shared/utils");
const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
const {debounce} = require("devtools/shared/debounce");
const EventEmitter = require("devtools/shared/old-event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const clipboardHelper = require("devtools/shared/platform/clipboard");

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

@ -99,36 +99,6 @@ function advanceValidate(keyCode, value, insertionPoint) {
exports.advanceValidate = advanceValidate;
/**
* Create a debouncing function wrapper to only call the target function after a certain
* amount of time has passed without it being called.
*
* @param {Function} func
* The function to debounce
* @param {number} wait
* The wait period
* @param {Object} scope
* The scope to use for func
* @return {Function} The debounced function
*/
function debounce(func, wait, scope) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
let args = arguments;
timer = setTimeout(function () {
timer = null;
func.apply(scope, args);
}, wait);
};
}
exports.debounce = debounce;
/**
* From underscore's `_.throttle`
* http://underscorejs.org

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

@ -24,7 +24,7 @@
}
.headersPanelBox .netInfoHeadersGroup {
color: var(--theme-body-color-alt);
color: var(--theme-toolbar-color);
margin-bottom: 10px;
border-bottom: 1px solid var(--theme-splitter-color);
padding-top: 8px;

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

@ -903,7 +903,7 @@ body,
.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel,
.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel:hover,
.tree-container .treeTable .treeRow.tree-section > .treeValueCell:not(:hover) * {
color: var(--theme-body-color-alt);
color: var(--theme-toolbar-color);
}
.tree-container .treeTable .treeValueCell {

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

@ -57,7 +57,7 @@
margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
cursor: n-resize;
cursor: ns-resize;
}
.devtools-side-splitter {
@ -70,7 +70,7 @@
margin-inline-start: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
margin-inline-end: calc(-1 * var(--devtools-splitter-inline-end-width));
cursor: e-resize;
cursor: ew-resize;
}
.devtools-horizontal-splitter.disabled,

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

@ -974,7 +974,6 @@ a.learn-more-link.webconsole-learn-more-link {
background-color: var(--repeat-bubble-background-color);
font-weight: normal;
font-size: 0.8em;
height: auto;
}
/* Network Messages */

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

@ -94,8 +94,8 @@
margin-inline-end: 0;
margin-inline-start: 0;
/* In some edge case the cursor is not changed to n-resize */
cursor: n-resize;
/* In some edge case the cursor is not changed to ns-resize */
cursor: ns-resize;
}
.devtools-responsive-container > .devtools-sidebar-tabs:not(.pane-collapsed) {

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

@ -19,7 +19,7 @@ const {
getVisibleMessages,
getAllRepeatById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/MessageContainer").MessageContainer);
const {
MESSAGE_TYPE,
} = require("devtools/client/webconsole/new-console-output/constants");

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

@ -13,7 +13,7 @@ const ObjectClient = require("devtools/shared/client/object-client");
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { MODE } = require("devtools/client/shared/components/reps/reps");
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/GripMessageBody"));
const TABLE_ROW_MAX_ITEMS = 1000;
const TABLE_COLUMN_MAX_ITEMS = 10;

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

@ -20,8 +20,8 @@ const {
FILTERS,
} = require("../constants");
const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
const FilterCheckbox = require("devtools/client/webconsole/new-console-output/components/filter-checkbox");
const FilterButton = require("devtools/client/webconsole/new-console-output/components/FilterButton");
const FilterCheckbox = require("devtools/client/webconsole/new-console-output/components/FilterCheckbox");
const FilterBar = createClass({

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

@ -16,10 +16,10 @@ const {
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const {MESSAGE_SOURCE} = require("devtools/client/webconsole/new-console-output/constants");
const CollapseButton = require("devtools/client/webconsole/new-console-output/components/collapse-button");
const MessageIndent = require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent;
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
const CollapseButton = require("devtools/client/webconsole/new-console-output/components/CollapseButton");
const MessageIndent = require("devtools/client/webconsole/new-console-output/components/MessageIndent").MessageIndent;
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/MessageIcon");
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/MessageRepeat");
const FrameView = createFactory(require("devtools/client/shared/components/Frame"));
const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace"));

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

@ -18,12 +18,12 @@ const {
} = require("devtools/client/webconsole/new-console-output/constants");
const componentMap = new Map([
["ConsoleApiCall", require("./message-types/console-api-call")],
["ConsoleCommand", require("./message-types/console-command")],
["DefaultRenderer", require("./message-types/default-renderer")],
["EvaluationResult", require("./message-types/evaluation-result")],
["NetworkEventMessage", require("./message-types/network-event-message")],
["PageError", require("./message-types/page-error")]
["ConsoleApiCall", require("./message-types/ConsoleApiCall")],
["ConsoleCommand", require("./message-types/ConsoleCommand")],
["DefaultRenderer", require("./message-types/DefaultRenderer")],
["EvaluationResult", require("./message-types/EvaluationResult")],
["NetworkEventMessage", require("./message-types/NetworkEventMessage")],
["PageError", require("./message-types/PageError")]
]);
const MessageContainer = createClass({

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

@ -12,11 +12,11 @@ const {
DOM: dom,
PropTypes
} = require("devtools/client/shared/vendor/react");
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/grip-message-body");
const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table"));
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/GripMessageBody");
const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/ConsoleTable"));
const {isGroupType, l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
ConsoleApiCall.displayName = "ConsoleApiCall";

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

@ -11,7 +11,7 @@ const {
createFactory,
PropTypes
} = require("devtools/client/shared/vendor/react");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
ConsoleCommand.displayName = "ConsoleCommand";

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

@ -11,8 +11,8 @@ const {
createFactory,
PropTypes
} = require("devtools/client/shared/vendor/react");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/grip-message-body");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/GripMessageBody");
EvaluationResult.displayName = "EvaluationResult";

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

@ -12,7 +12,7 @@ const {
DOM: dom,
PropTypes
} = require("devtools/client/shared/vendor/react");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const TabboxPanel = createFactory(require("devtools/client/netmonitor/src/components/tabbox-panel"));

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

@ -11,7 +11,7 @@ const {
createFactory,
PropTypes
} = require("devtools/client/shared/vendor/react");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/Message"));
PageError.displayName = "PageError";

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

@ -4,10 +4,10 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'console-api-call.js',
'console-command.js',
'default-renderer.js',
'evaluation-result.js',
'network-event-message.js',
'page-error.js',
'ConsoleApiCall.js',
'ConsoleCommand.js',
'DefaultRenderer.js',
'EvaluationResult.js',
'NetworkEventMessage.js',
'PageError.js',
)

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

@ -8,16 +8,16 @@ DIRS += [
]
DevToolsModules(
'collapse-button.js',
'console-output.js',
'console-table.js',
'filter-bar.js',
'filter-button.js',
'filter-checkbox.js',
'grip-message-body.js',
'message-container.js',
'message-icon.js',
'message-indent.js',
'message-repeat.js',
'message.js'
'CollapseButton.js',
'ConsoleOutput.js',
'ConsoleTable.js',
'FilterBar.js',
'FilterButton.js',
'FilterCheckbox.js',
'GripMessageBody.js',
'Message.js',
'MessageContainer.js',
'MessageIcon.js',
'MessageIndent.js',
'MessageRepeat.js'
)

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

@ -13,8 +13,8 @@ const { createContextMenu } = require("devtools/client/webconsole/new-console-ou
const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
const EventEmitter = require("devtools/shared/old-event-emitter");
const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output"));
const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/ConsoleOutput"));
const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/FilterBar"));
let store = null;
@ -266,24 +266,25 @@ NewConsoleOutputWrapper.prototype = {
if (this.queuedMessageUpdates.length > 0) {
this.queuedMessageUpdates.forEach(({ message, res }) => {
actions.networkMessageUpdate(message);
store.dispatch(actions.networkMessageUpdate(message));
this.jsterm.hud.emit("network-message-updated", res);
});
this.queuedMessageUpdates = [];
}
if (this.queuedRequestUpdates.length > 0) {
this.queuedRequestUpdates.forEach(({ id, data}) => {
actions.networkUpdateRequest(id, data);
// Fire an event indicating that all data fetched from
// the backend has been received. This is based on
// 'FirefoxDataProvider.isQueuePayloadReady', see more
// comments in that method.
// (netmonitor/src/connector/firefox-data-provider).
// This event might be utilized in tests to find the right
// time when to finish.
this.jsterm.hud.emit("network-request-payload-ready", {id, data});
store.dispatch(actions.networkUpdateRequest(id, data));
});
this.queuedRequestUpdates = [];
// Fire an event indicating that all data fetched from
// the backend has been received. This is based on
// 'FirefoxDataProvider.isQueuePayloadReady', see more
// comments in that method.
// (netmonitor/src/connector/firefox-data-provider).
// This event might be utilized in tests to find the right
// time when to finish.
this.jsterm.hud.emit("network-request-payload-ready");
}
}, 50);
},

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

@ -10,7 +10,7 @@ const { render } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
// Components under test.
const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"));
const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/ConsoleApiCall"));
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");

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

@ -13,12 +13,12 @@ const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"));
const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/ConsoleApiCall"));
const {
MESSAGE_OPEN,
MESSAGE_CLOSE,
} = require("devtools/client/webconsole/new-console-output/constants");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");

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

@ -13,8 +13,8 @@ const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/EvaluationResult"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");

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

@ -10,8 +10,8 @@ const { createFactory, DOM } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
const FilterButton = require("devtools/client/webconsole/new-console-output/components/FilterButton");
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/FilterBar"));
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const {

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

@ -7,7 +7,7 @@ const { render } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/FilterButton"));
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
describe("FilterButton component:", () => {

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

@ -7,7 +7,7 @@ const { render } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const FilterCheckbox = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-checkbox"));
const FilterCheckbox = createFactory(require("devtools/client/webconsole/new-console-output/components/FilterCheckbox"));
describe("FilterCheckbox component:", () => {
const props = {

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

@ -9,10 +9,10 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const { MessageContainer, getMessageComponent } = require("devtools/client/webconsole/new-console-output/components/message-container");
const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
const { MessageContainer, getMessageComponent } = require("devtools/client/webconsole/new-console-output/components/MessageContainer");
const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/ConsoleApiCall");
const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/EvaluationResult");
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/PageError");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");

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

@ -5,7 +5,7 @@
const {
MESSAGE_LEVEL,
} = require("devtools/client/webconsole/new-console-output/constants");
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/MessageIcon");
const expect = require("expect");

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

@ -2,7 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/MessageRepeat");
const expect = require("expect");

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

@ -10,8 +10,8 @@ const { render } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
// Components under test.
const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");

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

@ -13,12 +13,12 @@ const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/PageError");
const {
MESSAGE_OPEN,
MESSAGE_CLOSE,
} = require("devtools/client/webconsole/new-console-output/constants");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");

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

@ -9,7 +9,7 @@
// behave as expected.
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-group.html";
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");

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

@ -14,10 +14,6 @@ const { assert, dumpn } = DevToolsUtils;
loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
"Uint32Array", "Int8Array", "Int16Array", "Int32Array",
"Float32Array", "Float64Array"];
// Number of items to preview in objects, arrays, maps, sets, lists,
// collections, etc.
const OBJECT_PREVIEW_MAX_ITEMS = 10;
@ -120,7 +116,7 @@ ObjectActor.prototype = {
// FF40+: Allow to know how many properties an object has to lazily display them
// when there is a bunch.
if (TYPED_ARRAY_CLASSES.indexOf(g.class) != -1) {
if (isTypedArray(g)) {
// Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
let length = DevToolsUtils.getProperty(this.obj, "length");
g.ownPropertyLength = length;
@ -349,8 +345,7 @@ ObjectActor.prototype = {
// prototype. Avoid calling getOwnPropertyNames on objects that may have
// many properties like Array, strings or js objects. That to avoid
// freezing firefox when doing so.
if (TYPED_ARRAY_CLASSES.includes(this.obj.class) ||
["Array", "Object", "String"].includes(this.obj.class)) {
if (isArray(this.obj) || ["Object", "String"].includes(this.obj.class)) {
obj = obj.proto;
level++;
}
@ -820,7 +815,11 @@ function PropertyIteratorActor(objectActor, options) {
} else {
throw new Error("Unsupported class to enumerate entries from: " + cls);
}
} else if (options.ignoreNonIndexedProperties && !options.query) {
} else if (
isArray(objectActor.obj)
&& options.ignoreNonIndexedProperties
&& !options.query
) {
this.iterator = enumArrayProperties(objectActor, options);
} else {
this.iterator = enumObjectProperties(objectActor, options);
@ -872,13 +871,13 @@ PropertyIteratorActor.prototype.requestTypes = {
function enumArrayProperties(objectActor, options) {
let length = DevToolsUtils.getProperty(objectActor.obj, "length");
if (typeof length !== "number") {
if (!isSafePositiveInteger(length)) {
// Pseudo arrays are flagged as ArrayLike if they have
// subsequent indexed properties without having any length attribute.
length = 0;
let names = objectActor.obj.getOwnPropertyNames();
for (let key of names) {
if (isNaN(key) || key != length++) {
if (!isSafeIndex(key) || key != length++) {
break;
}
}
@ -906,15 +905,32 @@ function enumObjectProperties(objectActor, options) {
if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
let length = DevToolsUtils.getProperty(objectActor.obj, "length");
if (typeof length !== "number") {
// Pseudo arrays are flagged as ArrayLike if they have
// subsequent indexed properties without having any length attribute.
length = 0;
for (let key of names) {
if (isNaN(key) || key != length++) {
break;
let sliceIndex;
const isLengthTrustworthy =
isSafePositiveInteger(length)
&& (length > 0 && isSafeIndex(names[length - 1]))
&& !isSafeIndex(names[length]);
if (!isLengthTrustworthy) {
// The length property may not reflect what the object looks like, let's find
// where indexed properties end.
if (!isSafeIndex(names[0])) {
// If the first item is not a number, this means there is no indexed properties
// in this object.
sliceIndex = 0;
} else {
sliceIndex = names.length;
while (sliceIndex > 0) {
if (isSafeIndex(names[sliceIndex - 1])) {
break;
}
sliceIndex--;
}
}
} else {
sliceIndex = length;
}
// It appears that getOwnPropertyNames always returns indexed properties
@ -922,11 +938,11 @@ function enumObjectProperties(objectActor, options) {
// We do such clever operation to optimize very large array inspection,
// like webaudio buffers.
if (options.ignoreIndexedProperties) {
// Keep items after `length` index
names = names.slice(length);
// Keep items after `sliceIndex` index
names = names.slice(sliceIndex);
} else if (options.ignoreNonIndexedProperties) {
// Remove `length` first items
names.splice(length);
// Keep `sliceIndex` first items
names.length = sliceIndex;
}
}
@ -1616,7 +1632,7 @@ function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false)
// Preview functions that do not rely on the object class.
DebuggerServer.ObjectActorPreviewers.Object = [
function TypedArray({obj, hooks}, grip) {
if (TYPED_ARRAY_CLASSES.indexOf(obj.class) == -1) {
if (!isTypedArray(obj)) {
return false;
}
@ -2489,6 +2505,57 @@ function unwrap(obj) {
return unwrap(unwrapped);
}
const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
"Uint32Array", "Int8Array", "Int16Array", "Int32Array",
"Float32Array", "Float64Array"];
/**
* Returns true if a debuggee object is a typed array.
*
* @param obj Debugger.Object
* The debuggee object to test.
* @return Boolean
*/
function isTypedArray(object) {
return TYPED_ARRAY_CLASSES.includes(object.class);
}
/**
* Returns true if a debuggee object is an array, including a typed array.
*
* @param obj Debugger.Object
* The debuggee object to test.
* @return Boolean
*/
function isArray(object) {
return isTypedArray(object) || object.class === "Array";
}
/**
* Returns true if the parameter is a safe positive integer.
*
* @param num Number
* The number to test.
* @return Boolean
*/
function isSafePositiveInteger(num) {
return Number.isSafeInteger(num) && 1 / num > 0;
}
/**
* Returns true if the parameter is suitable to be an array index.
*
* @param num Any
* @return Boolean
*/
function isSafeIndex(str) {
// Transform the parameter to a number using the Unary operator.
let num = +str;
return isSafePositiveInteger(num) &&
// Check the string since unary can transform non number (boolean, null, …).
num + "" === str;
}
exports.ObjectActor = ObjectActor;
exports.PropertyIteratorActor = PropertyIteratorActor;
exports.LongStringActor = LongStringActor;

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

@ -0,0 +1,237 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
// Test that onEnumProperties returns the expected data
// when passing `ignoreNonIndexedProperties` and `ignoreIndexedProperties` options
// with various objects. (See Bug 1403065)
async function run_test() {
do_test_pending();
await run_test_with_server(DebuggerServer);
await run_test_with_server(WorkerDebuggerServer);
do_test_finished();
}
const DO_NOT_CHECK_VALUE = Symbol();
async function run_test_with_server(server) {
initTestDebuggerServer(server);
const debuggee = addTestGlobal("test-grips", server);
debuggee.eval(function stopMe(arg1) {
debugger;
}.toString());
const dbgClient = new DebuggerClient(server.connectPipe());
await dbgClient.connect();
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
[{
evaledObject: { a: 10 },
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["a", 10]],
}, {
evaledObject: { length: 10 },
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["length", 10]],
}, {
evaledObject: { a: 10, 0: "indexed" },
expectedIndexedProperties: [["0", "indexed"]],
expectedNonIndexedProperties: [["a", 10]],
}, {
evaledObject: { 1: 1, length: 42, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", 42], ["a", 10]],
}, {
evaledObject: { 1: 1, length: 2.34, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", 2.34], ["a", 10]],
}, {
evaledObject: { 1: 1, length: -0, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", -0], ["a", 10]],
}, {
evaledObject: { 1: 1, length: -10, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", -10], ["a", 10]],
}, {
evaledObject: { 1: 1, length: true, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", true], ["a", 10]],
}, {
evaledObject: { 1: 1, length: null, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", DO_NOT_CHECK_VALUE], ["a", 10]],
}, {
evaledObject: { 1: 1, length: Math.pow(2, 53), a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", 9007199254740992], ["a", 10]],
}, {
evaledObject: { 1: 1, length: "fake", a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", "fake"], ["a", 10]],
}, {
evaledObject: { 1: 1, length: Infinity, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [["length", DO_NOT_CHECK_VALUE], ["a", 10]],
}, {
evaledObject: { 0: 0, length: 0},
expectedIndexedProperties: [["0", 0]],
expectedNonIndexedProperties: [["length", 0]],
}, {
evaledObject: { 0: 0, 1: 1, length: 1},
expectedIndexedProperties: [["0", 0], ["1", 1]],
expectedNonIndexedProperties: [["length", 1]],
}, {
evaledObject: { length: 0},
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["length", 0]],
}, {
evaledObject: { 1: 1 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [],
}, {
evaledObject: `(() => {
x = [12, 42];
x.foo = 90;
return x;
})()`,
expectedIndexedProperties: [["0", 12], ["1", 42]],
expectedNonIndexedProperties: [["length", 2], ["foo", 90]],
}, {
evaledObject: `(() => {
x = [12, 42];
x.length = 3;
return x;
})()`,
expectedIndexedProperties: [["0", 12], ["1", 42], ["2", undefined]],
expectedNonIndexedProperties: [["length", 3]],
}, {
evaledObject: `(() => {
x = [12, 42];
x.length = 1;
return x;
})()`,
expectedIndexedProperties: [["0", 12]],
expectedNonIndexedProperties: [["length", 1]],
}, {
evaledObject: `(() => {
x = [, 42,,];
x.foo = 90;
return x;
})()`,
expectedIndexedProperties: [["0", undefined], ["1", 42], ["2", undefined]],
expectedNonIndexedProperties: [["length", 3], ["foo", 90]],
}, {
evaledObject: `(() => {
x = Array(2);
x.foo = "bar";
x.bar = "foo";
return x;
})()`,
expectedIndexedProperties: [["0", undefined], ["1", undefined]],
expectedNonIndexedProperties: [["length", 2], ["foo", "bar"], ["bar", "foo"]],
}, {
evaledObject: `(() => {
x = new Int8Array(new ArrayBuffer(2));
x.foo = "bar";
x.bar = "foo";
return x;
})()`,
expectedIndexedProperties: [["0", 0], ["1", 0]],
expectedNonIndexedProperties: [
["foo", "bar"],
["bar", "foo"],
["length", 2],
["buffer", DO_NOT_CHECK_VALUE],
["byteLength", 2],
["byteOffset", 0],
],
}].forEach(async (testData) => {
await test_object_grip(debuggee, dbgClient, threadClient, testData);
});
await dbgClient.close();
}
async function test_object_grip(debuggee, dbgClient, threadClient, testData = {}) {
const {
evaledObject,
expectedIndexedProperties,
expectedNonIndexedProperties,
} = testData;
return new Promise((resolve, reject) => {
threadClient.addOneTimeListener("paused", async function (event, packet) {
let [grip] = packet.frame.arguments;
let objClient = threadClient.pauseGrip(grip);
do_print(`
Check enumProperties response for
${
typeof evaledObject === "string"
? evaledObject
: JSON.stringify(evaledObject)
}
`);
// Checks the result of enumProperties.
let response = await objClient.enumProperties({ ignoreNonIndexedProperties: true });
await check_enum_properties(response, expectedIndexedProperties);
response = await objClient.enumProperties({ ignoreIndexedProperties: true });
await check_enum_properties(response, expectedNonIndexedProperties);
await threadClient.resume();
resolve();
});
debuggee.eval(`
stopMe(${
typeof evaledObject === "string"
? evaledObject
: JSON.stringify(evaledObject)
});
`);
});
}
async function check_enum_properties(response, expected = []) {
ok(response && Object.getOwnPropertyNames(response).includes("iterator"),
"The response object has an iterator property");
const {iterator} = response;
equal(iterator.count, expected.length, "iterator.count has the expected value");
do_print("Check iterator.slice response for all properties");
let sliceResponse = await iterator.slice(0, iterator.count);
ok(sliceResponse && Object.getOwnPropertyNames(sliceResponse).includes("ownProperties"),
"The response object has an ownProperties property");
let {ownProperties} = sliceResponse;
let names = Object.getOwnPropertyNames(ownProperties);
equal(names.length, expected.length,
"The response has the expected number of properties");
for (let i = 0; i < names.length; i++) {
const name = names[i];
const [key, value] = expected[i];
equal(name, key, "Property has the expected name");
const property = ownProperties[name];
if (value === DO_NOT_CHECK_VALUE) {
return;
}
if (value === undefined) {
equal(property, undefined, `Response has no value for the "${key}" property`);
} else {
const propValue = property.hasOwnProperty("value")
? property.value
: property.getterValue;
equal(propValue, value, `Property "${key}" has the expected value`);
}
}
}

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

@ -175,6 +175,7 @@ reason = only ran on B2G
[test_objectgrips-17.js]
[test_objectgrips-18.js]
[test_objectgrips-19.js]
[test_objectgrips-20.js]
[test_promise_state-01.js]
[test_promise_state-02.js]
[test_promise_state-03.js]

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

@ -5,35 +5,29 @@
"use strict";
/**
* From underscore's `_.debounce`
* http://underscorejs.org
* (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
* Create a debouncing function wrapper to only call the target function after a certain
* amount of time has passed without it being called.
*
* [and in turn extracted from the SDK's "lang/functional/concurrent.js"]
* @param {Function} func
* The function to debounce
* @param {number} wait
* The wait period
* @param {Object} scope
* The scope to use for func
* @return {Function} The debounced function
*/
exports.debounce = function (fn, wait) {
let timeout, args, context, timestamp, result;
exports.debounce = function (func, wait, scope) {
let timer = null;
let later = function () {
let last = Date.now() - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
result = fn.apply(context, args);
context = args = null;
}
};
return function (...aArgs) {
context = this;
args = aArgs;
timestamp = Date.now();
if (!timeout) {
timeout = setTimeout(later, wait);
return function () {
if (timer) {
clearTimeout(timer);
}
return result;
let args = arguments;
timer = setTimeout(function () {
timer = null;
func.apply(scope, args);
}, wait);
};
};

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

@ -521,15 +521,25 @@ XMLHttpRequestMainThread::DetectCharset()
}
mResponseCharset = encoding;
mDecoder = encoding->NewDecoderWithBOMRemoval();
// Only sniff the BOM for non-JSON responseTypes
if (mResponseType == XMLHttpRequestResponseType::Json) {
mDecoder = encoding->NewDecoderWithBOMRemoval();
} else {
mDecoder = encoding->NewDecoder();
}
return NS_OK;
}
nsresult
XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
uint32_t aSrcBufferLen)
uint32_t aSrcBufferLen,
bool aLast)
{
// Call this with an empty buffer to send the decoder the signal
// that we have hit the end of the stream.
NS_ENSURE_STATE(mDecoder);
CheckedInt<size_t> destBufferLen =
@ -550,7 +560,6 @@ XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
return NS_ERROR_OUT_OF_MEMORY;
}
// XXX there's no handling for incomplete byte sequences on EOF!
uint32_t result;
size_t read;
size_t written;
@ -558,7 +567,7 @@ XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
AsBytes(MakeSpan(aSrcBuffer, aSrcBufferLen)),
MakeSpan(helper.EndOfExistingData(), destBufferLen.value()),
false);
aLast);
MOZ_ASSERT(result == kInputEmpty);
MOZ_ASSERT(read == aSrcBufferLen);
MOZ_ASSERT(written <= destBufferLen.value());
@ -2189,6 +2198,12 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
return NS_OK;
}
// send the decoder the signal that we've hit the end of the stream,
// but only when parsing text (not XML, which does this already).
if (mDecoder && !mFlagParseBody) {
AppendToResponseText(nullptr, 0, true);
}
mWaitingForOnStopRequest = false;
if (mRequestObserver) {
@ -2364,7 +2379,7 @@ XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument()
mResponseCharset = mResponseXML->GetDocumentCharacterSet();
TruncateResponseText();
mResponseBodyDecodedPos = 0;
mDecoder = mResponseCharset->NewDecoderWithBOMRemoval();
mDecoder = mResponseCharset->NewDecoder();
}
}

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

@ -520,7 +520,8 @@ protected:
};
nsresult DetectCharset();
nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
nsresult AppendToResponseText(const char* aBuffer, uint32_t aBufferLen,
bool aLast = false);
static nsresult StreamReaderFunc(nsIInputStream* in,
void* closure,
const char* fromRawSegment,

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

@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc
the need to run the cargo update command in js/src as well. Hopefully this will
be resolved soon.
Latest Commit: 9c5f8682e75839ad8c26480b89f87bbb37aa7894
Latest Commit: aa81aebba2c1b8ff8af5d40796154d66349fd131

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

@ -35,6 +35,7 @@ serde_derive = { optional = true, version = "1.0" }
[dev-dependencies]
angle = {git = "https://github.com/servo/angle", branch = "servo"}
env_logger = "0.4"
rand = "0.3" # for the benchmarks
servo-glutin = "0.12" # for the example apps

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

@ -247,18 +247,18 @@ impl Example for App {
let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
builder.push_border(&info, border_widths, border_details);
builder.pop_clip_id();
if false {
// draw text?
let font_key = api.generate_font_key();
let font_bytes = load_file("res/FreeSans.ttf");
let font_bytes = load_file("../wrench/reftest/text/FreeSans.ttf");
resources.add_raw_font(font_key, font_bytes, 0);
let font_instance_key = api.generate_font_instance_key();
resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
let text_bounds = (100, 200).by(700, 300);
let text_bounds = (100, 50).by(700, 200);
let glyphs = vec![
GlyphInstance {
index: 48,
@ -344,7 +344,6 @@ impl Example for App {
);
}
builder.pop_clip_id();
builder.pop_stacking_context();
}

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

@ -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/. */
extern crate env_logger;
use gleam::gl;
use glutin;
use std::env;
@ -77,6 +79,8 @@ pub trait Example {
}
pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOptions>) {
env_logger::init().unwrap();
let args: Vec<String> = env::args().collect();
let res_path = if args.len() > 1 {
Some(PathBuf::from(&args[1]))

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

@ -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, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{ClipId, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo};
use api::WorldPoint;
@ -11,7 +11,7 @@ use clip_scroll_tree::TransformUpdateState;
use geometry::ray_intersects_rect;
use spring::{Spring, DAMPING, STIFFNESS};
use tiling::PackedLayerIndex;
use util::{MatrixHelpers, TransformedRectKind};
use util::MatrixHelpers;
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@ -27,15 +27,6 @@ pub struct ClipInfo {
/// The packed layer index for this node, which is used to render a clip mask
/// for it, if necessary.
pub packed_layer_index: PackedLayerIndex,
/// The final transformed rectangle of this clipping region for this node,
/// which depends on the screen rectangle and the transformation of all of
/// the parents.
pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
/// A rectangle which defines the rough boundaries of this clip in reference
/// frame relative coordinates (with no scroll offsets).
pub clip_rect: LayerRect,
}
impl ClipInfo {
@ -44,12 +35,9 @@ impl ClipInfo {
packed_layer_index: PackedLayerIndex,
clip_store: &mut ClipStore,
) -> ClipInfo {
let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
ClipInfo {
clip_sources: clip_store.insert(ClipSources::from(clip_region)),
packed_layer_index,
screen_bounding_rect: None,
clip_rect: clip_rect,
}
}
}
@ -144,10 +132,15 @@ impl ClipScrollNode {
}
}
pub fn new(pipeline_id: PipelineId, parent_id: ClipId, clip_info: ClipInfo) -> ClipScrollNode {
pub fn new_clip_node(
pipeline_id: PipelineId,
parent_id: ClipId,
clip_info: ClipInfo,
clip_rect: LayerRect,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: clip_info.clip_rect,
local_clip_rect: clip_info.clip_rect,
local_viewport_rect: clip_rect,
local_clip_rect: clip_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),

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

@ -166,7 +166,7 @@ impl ClipScrollTree {
return false;
}
let point_in_clips = transformed_point - clip_info.clip_rect.origin.to_vector();
let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
for &(ref clip, _) in clip_store.get(&clip_info.clip_sources).clips() {
if !clip.contains(&point_in_clips) {
cache.insert(*node_id, None);
@ -463,10 +463,6 @@ impl ClipScrollTree {
match node.node_type {
NodeType::Clip(ref info) => {
pt.new_level("Clip".to_owned());
pt.add_item(format!(
"screen_bounding_rect: {:?}",
info.screen_bounding_rect
));
let clips = clip_store.get(&info.clip_sources).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));

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

@ -2,9 +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::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, ClipAndScrollInfo};
use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect};
use api::{DeviceUintSize, ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
use api::{ClipAndScrollInfo, ClipId, ColorF};
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
use api::{ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
@ -24,7 +25,7 @@ use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, TextRunMode};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextShadowPrimitiveCpu};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_task::{AlphaRenderItem, ClipWorkItem, RenderTask};
@ -94,7 +95,9 @@ pub struct FrameBuilder {
stacking_context_store: Vec<StackingContext>,
clip_scroll_group_store: Vec<ClipScrollGroup>,
clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo, ClipScrollGroupIndex>,
// Note: value here is meant to be `ClipScrollGroupIndex`,
// but we already have `ClipAndScrollInfo` in the key
clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo, usize>,
packed_layers: Vec<PackedLayer>,
// A stack of the current text-shadow primitives.
@ -126,6 +129,8 @@ pub struct PrimitiveContext<'a> {
pub current_clip_stack: Vec<ClipWorkItem>,
pub clip_bounds: DeviceIntRect,
pub clip_id: ClipId,
pub display_list: &'a BuiltDisplayList,
}
impl<'a> PrimitiveContext<'a> {
@ -136,7 +141,9 @@ impl<'a> PrimitiveContext<'a> {
screen_rect: &DeviceIntRect,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore,
device_pixel_ratio: f32) -> Option<Self> {
device_pixel_ratio: f32,
display_list: &'a BuiltDisplayList,
) -> Option<Self> {
let mut current_clip_stack = Vec::new();
let mut clip_bounds = *screen_rect;
@ -165,15 +172,6 @@ impl<'a> PrimitiveContext<'a> {
continue;
}
// apply the screen bounds of the clip node
//Note: these are based on the local combined viewport, so can be tighter
if let Some((_kind, ref screen_rect)) = clip.screen_bounding_rect {
clip_bounds = match clip_bounds.intersection(screen_rect) {
Some(rect) => rect,
None => return None,
}
}
// apply the outer device bounds of the clip stack
if let Some(ref outer) = clip_sources.bounds.outer {
clip_bounds = match clip_bounds.intersection(&outer.device_rect) {
@ -200,6 +198,7 @@ impl<'a> PrimitiveContext<'a> {
clip_bounds,
device_pixel_ratio,
clip_id,
display_list,
})
}
}
@ -251,15 +250,6 @@ impl FrameBuilder {
}
}
pub fn create_clip_scroll_group_if_necessary(&mut self, info: ClipAndScrollInfo) {
if self.clip_scroll_group_indices.contains_key(&info) {
return;
}
let group_index = self.create_clip_scroll_group(info);
self.clip_scroll_group_indices.insert(info, group_index);
}
/// Create a primitive and add it to the prim store. This method doesn't
/// add the primitive to the draw list, so can be used for creating
/// sub-primitives.
@ -270,7 +260,10 @@ impl FrameBuilder {
mut clip_sources: Vec<ClipSource>,
container: PrimitiveContainer,
) -> PrimitiveIndex {
self.create_clip_scroll_group_if_necessary(clip_and_scroll);
if !self.clip_scroll_group_indices.contains_key(&clip_and_scroll) {
let group_id = self.create_clip_scroll_group(&clip_and_scroll);
self.clip_scroll_group_indices.insert(clip_and_scroll, group_id);
}
if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
clip_sources.push(ClipSource::Rectangle(main));
@ -361,10 +354,11 @@ impl FrameBuilder {
prim_index
}
pub fn create_clip_scroll_group(&mut self, info: ClipAndScrollInfo) -> ClipScrollGroupIndex {
fn create_clip_scroll_group(&mut self, info: &ClipAndScrollInfo) -> usize {
let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
self.packed_layers.push(PackedLayer::empty());
let group_id = self.clip_scroll_group_store.len();
self.clip_scroll_group_store.push(ClipScrollGroup {
scroll_node_id: info.scroll_node_id,
clip_node_id: info.clip_node_id(),
@ -372,7 +366,7 @@ impl FrameBuilder {
screen_bounding_rect: None,
});
ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, info)
group_id
}
pub fn notify_waiting_for_root_stacking_context(&mut self) {
@ -548,12 +542,13 @@ impl FrameBuilder {
clip_region: ClipRegion,
clip_scroll_tree: &mut ClipScrollTree,
) {
let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
let clip_info = ClipInfo::new(
clip_region,
PackedLayerIndex(self.packed_layers.len()),
&mut self.clip_store,
);
let node = ClipScrollNode::new(pipeline_id, parent_id, clip_info);
let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, clip_info, clip_rect);
clip_scroll_tree.add_node(node, new_node_id);
self.packed_layers.push(PackedLayer::empty());
}
@ -809,15 +804,15 @@ impl FrameBuilder {
let rect = LayerRect::new(origin, size);
// Calculate the local texel coords of the slices.
let px0 = 0;
let px1 = border.patch.slice.left;
let px2 = border.patch.width - border.patch.slice.right;
let px3 = border.patch.width;
let px0 = 0.0;
let px1 = border.patch.slice.left as f32;
let px2 = border.patch.width as f32 - border.patch.slice.right as f32;
let px3 = border.patch.width as f32;
let py0 = 0;
let py1 = border.patch.slice.top;
let py2 = border.patch.height - border.patch.slice.bottom;
let py3 = border.patch.height;
let py0 = 0.0;
let py1 = border.patch.slice.top as f32;
let py2 = border.patch.height as f32 - border.patch.slice.bottom as f32;
let py3 = border.patch.height as f32;
let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
@ -834,81 +829,104 @@ impl FrameBuilder {
);
let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
fn add_segment(
segments: &mut Vec<ImageBorderSegment>,
rect: LayerRect,
uv_rect: TexelRect,
repeat_horizontal: RepeatMode,
repeat_vertical: RepeatMode) {
if uv_rect.uv1.x > uv_rect.uv0.x &&
uv_rect.uv1.y > uv_rect.uv0.y {
segments.push(ImageBorderSegment::new(
rect,
uv_rect,
repeat_horizontal,
repeat_vertical,
));
}
}
// Build the list of image segments
let mut segments = vec![
// Top left
ImageBorderSegment::new(
LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
TexelRect::new(px0, py0, px1, py1),
RepeatMode::Stretch,
RepeatMode::Stretch,
),
// Top right
ImageBorderSegment::new(
LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
TexelRect::new(px2, py0, px3, py1),
RepeatMode::Stretch,
RepeatMode::Stretch,
),
// Bottom right
ImageBorderSegment::new(
LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
TexelRect::new(px2, py2, px3, py3),
RepeatMode::Stretch,
RepeatMode::Stretch,
),
// Bottom left
ImageBorderSegment::new(
LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
TexelRect::new(px0, py2, px1, py3),
RepeatMode::Stretch,
RepeatMode::Stretch,
),
];
let mut segments = vec![];
// Top left
add_segment(
&mut segments,
LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
TexelRect::new(px0, py0, px1, py1),
RepeatMode::Stretch,
RepeatMode::Stretch
);
// Top right
add_segment(
&mut segments,
LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
TexelRect::new(px2, py0, px3, py1),
RepeatMode::Stretch,
RepeatMode::Stretch
);
// Bottom right
add_segment(
&mut segments,
LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
TexelRect::new(px2, py2, px3, py3),
RepeatMode::Stretch,
RepeatMode::Stretch
);
// Bottom left
add_segment(
&mut segments,
LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
TexelRect::new(px0, py2, px1, py3),
RepeatMode::Stretch,
RepeatMode::Stretch
);
// Center
if border.fill {
segments.push(ImageBorderSegment::new(
add_segment(
&mut segments,
LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
TexelRect::new(px1, py1, px2, py2),
border.repeat_horizontal,
border.repeat_vertical,
))
border.repeat_vertical
);
}
// Add edge segments if valid size.
if px1 < px2 && py1 < py2 {
segments.extend_from_slice(&[
// Top
ImageBorderSegment::new(
LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
TexelRect::new(px1, py0, px2, py1),
border.repeat_horizontal,
RepeatMode::Stretch,
),
// Bottom
ImageBorderSegment::new(
LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
TexelRect::new(px1, py2, px2, py3),
border.repeat_horizontal,
RepeatMode::Stretch,
),
// Left
ImageBorderSegment::new(
LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
TexelRect::new(px0, py1, px1, py2),
RepeatMode::Stretch,
border.repeat_vertical,
),
// Right
ImageBorderSegment::new(
LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
TexelRect::new(px2, py1, px3, py2),
RepeatMode::Stretch,
border.repeat_vertical,
),
]);
}
// Add edge segments.
// Top
add_segment(
&mut segments,
LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
TexelRect::new(px1, py0, px2, py1),
border.repeat_horizontal,
RepeatMode::Stretch,
);
// Bottom
add_segment(
&mut segments,
LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
TexelRect::new(px1, py2, px2, py3),
border.repeat_horizontal,
RepeatMode::Stretch,
);
// Left
add_segment(
&mut segments,
LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
TexelRect::new(px0, py1, px1, py2),
RepeatMode::Stretch,
border.repeat_vertical,
);
// Right
add_segment(
&mut segments,
LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
TexelRect::new(px2, py1, px3, py2),
RepeatMode::Stretch,
border.repeat_vertical,
);
for segment in segments {
let mut info = info.clone();
@ -1559,8 +1577,8 @@ impl FrameBuilder {
&self,
clip_and_scroll: &ClipAndScrollInfo
) -> Option<PackedLayerIndex> {
let group_index = self.clip_scroll_group_indices.get(&clip_and_scroll).unwrap();
let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
let clip_scroll_group = &self.clip_scroll_group_store[group_id];
if clip_scroll_group.is_visible() {
Some(clip_scroll_group.packed_layer_index)
} else {
@ -1650,14 +1668,14 @@ impl FrameBuilder {
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
profile_counters: &mut FrameProfileCounters,
) {
) -> bool {
let stacking_context_index = *self.stacking_context_stack.last().unwrap();
let packed_layer_index =
match self.get_packed_layer_index_if_visible(&clip_and_scroll) {
Some(index) => index,
None => {
debug!("{:?} of invisible {:?}", base_prim_index, stacking_context_index);
return;
return false;
}
};
@ -1665,7 +1683,7 @@ impl FrameBuilder {
let stacking_context =
&mut self.stacking_context_store[stacking_context_index.0];
if !stacking_context.can_contribute_to_scene() {
return;
return false;
}
// At least one primitive in this stacking context is visible, so the stacking
@ -1690,7 +1708,7 @@ impl FrameBuilder {
.display_list;
if !stacking_context.is_backface_visible && packed_layer.transform.is_backface_visible() {
return;
return false;
}
let prim_context = PrimitiveContext::new(
@ -1701,11 +1719,16 @@ impl FrameBuilder {
clip_scroll_tree,
&self.clip_store,
device_pixel_ratio,
display_list,
);
let prim_context = match prim_context {
Some(prim_context) => prim_context,
None => return,
None => {
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
self.clip_scroll_group_store[group_id].screen_bounding_rect = None;
return false
},
};
debug!(
@ -1722,8 +1745,6 @@ impl FrameBuilder {
&prim_context,
resource_cache,
gpu_cache,
display_list,
TextRunMode::Normal,
render_tasks,
&mut self.clip_store,
) {
@ -1737,6 +1758,8 @@ impl FrameBuilder {
profile_counters.visible_primitives.inc();
}
}
true //visible
}
fn handle_pop_stacking_context(&mut self, screen_rect: &DeviceIntRect) {
@ -1797,7 +1820,7 @@ impl FrameBuilder {
let transform = node.world_viewport_transform
.pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
node_clip_info.screen_bounding_rect = if packed_layer.set_transform(transform) {
if packed_layer.set_transform(transform) {
// Meanwhile, the combined viewport rect is relative to the reference frame, so
// we move it into the local coordinate system of the node.
let local_viewport_rect = node.combined_local_viewport_rect
@ -1807,10 +1830,8 @@ impl FrameBuilder {
&local_viewport_rect,
screen_rect,
device_pixel_ratio,
)
} else {
None
};
);
}
let clip_sources = self.clip_store.get_mut(&node_clip_info.clip_sources);
clip_sources.update(
@ -2241,10 +2262,10 @@ impl FrameBuilder {
continue;
}
let group_index = *self.clip_scroll_group_indices
.get(&clip_and_scroll)
.unwrap();
if self.clip_scroll_group_store[group_index.0]
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
let group_index = ClipScrollGroupIndex(group_id, clip_and_scroll);
if self.clip_scroll_group_store[group_id]
.screen_bounding_rect
.is_none()
{

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

@ -18,7 +18,7 @@ use render_task::{ClipWorkItem, RenderTask, RenderTaskId, RenderTaskTree};
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use resource_cache::{ImageProperties, ResourceCache};
use std::{mem, usize};
use util::{pack_as_float, recycle_vec, TransformedRect};
use util::{MatrixHelpers, pack_as_float, recycle_vec, TransformedRect};
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveOpacity {
@ -57,10 +57,10 @@ pub struct TexelRect {
}
impl TexelRect {
pub fn new(u0: u32, v0: u32, u1: u32, v1: u32) -> TexelRect {
pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> TexelRect {
TexelRect {
uv0: DevicePoint::new(u0 as f32, v0 as f32),
uv1: DevicePoint::new(u1 as f32, v1 as f32),
uv0: DevicePoint::new(u0, v0),
uv1: DevicePoint::new(u1, v1),
}
}
@ -1060,126 +1060,19 @@ impl PrimitiveStore {
}
/// Returns true if the bounding box needs to be updated.
pub fn prepare_prim_for_render(
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
prim_context: &PrimitiveContext,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
display_list: &BuiltDisplayList,
// For some primitives, we need to mark dependencies as needed for rendering
// without spawning new tasks, since there will be another call to
// `prepare_prim_for_render_inner` specifically for this primitive later on.
render_tasks: Option<&mut RenderTaskTree>,
text_run_mode: TextRunMode,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
) -> Option<Geometry> {
let (prim_local_rect, prim_screen_rect, prim_kind, cpu_prim_index) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.screen_rect = None;
if !metadata.is_backface_visible &&
prim_context.packed_layer.transform.is_backface_visible() {
return None;
}
let local_rect = metadata
.local_rect
.intersection(&metadata.local_clip_rect)
.and_then(|rect| rect.intersection(&prim_context.packed_layer.local_clip_rect));
let local_rect = match local_rect {
Some(local_rect) => local_rect,
None => return None,
};
let xf_rect = TransformedRect::new(
&local_rect,
&prim_context.packed_layer.transform,
prim_context.device_pixel_ratio
);
metadata.screen_rect = xf_rect
.bounding_rect
.intersection(&prim_context.clip_bounds);
match metadata.screen_rect {
Some(screen_rect) => (local_rect, screen_rect, metadata.prim_kind, metadata.cpu_prim_index),
None => return None,
}
};
// Recurse into any sub primitives and prepare them for rendering first.
// TODO(gw): This code is a bit hacky to work around the borrow checker.
// Specifically, the clone() below on the primitive list for
// text shadow primitives. Consider restructuring this code to
// avoid borrow checker issues.
if prim_kind == PrimitiveKind::TextShadow {
for sub_prim_index in self.cpu_text_shadows[cpu_prim_index.0].primitives.clone() {
self.prepare_prim_for_render(
sub_prim_index,
prim_context,
resource_cache,
gpu_cache,
display_list,
TextRunMode::Shadow,
render_tasks,
clip_store,
);
}
}
) {
let metadata = &mut self.cpu_metadata[prim_index.0];
clip_store.get_mut(&metadata.clip_sources).update(
&prim_context.packed_layer.transform,
gpu_cache,
resource_cache,
prim_context.device_pixel_ratio,
);
// Try to create a mask if we may need to.
let prim_clips = clip_store.get(&metadata.clip_sources);
let clip_task = if prim_clips.is_masking() {
// Take into account the actual clip info of the primitive, and
// mutate the current bounds accordingly.
let mask_rect = match prim_clips.bounds.outer {
Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
Some(rect) => rect,
None => return None,
},
_ => prim_screen_rect,
};
let extra = ClipWorkItem {
layer_index: prim_context.packed_layer_index,
clip_sources: metadata.clip_sources.weak(),
apply_rectangles: false,
};
RenderTask::new_mask(
None,
mask_rect,
&prim_context.current_clip_stack,
Some(extra),
prim_screen_rect,
clip_store,
)
} else if !prim_context.current_clip_stack.is_empty() {
// If the primitive doesn't have a specific clip, key the task ID off the
// stacking context. This means that two primitives which are only clipped
// by the stacking context stack can share clip masks during render task
// assignment to targets.
RenderTask::new_mask(
Some(prim_context.clip_id),
prim_context.clip_bounds,
&prim_context.current_clip_stack,
None,
prim_screen_rect,
clip_store,
)
} else {
None
};
metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
match metadata.prim_kind {
PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {}
PrimitiveKind::BoxShadow => {
@ -1190,7 +1083,7 @@ impl PrimitiveStore {
// in device space. The shader adds a 1-pixel border around
// the patch, in order to prevent bilinear filter artifacts as
// the patch is clamped / mirrored across the box shadow rect.
let box_shadow = &mut self.cpu_box_shadows[cpu_prim_index.0];
let box_shadow = &mut self.cpu_box_shadows[metadata.cpu_prim_index.0];
let edge_size = box_shadow.edge_size.ceil() * prim_context.device_pixel_ratio;
let edge_size = edge_size as i32 + 2; // Account for bilinear filtering
let cache_size = DeviceIntSize::new(edge_size, edge_size);
@ -1217,12 +1110,12 @@ impl PrimitiveStore {
cache_size,
prim_index
);
let render_task_id = render_tasks.add(render_task);
box_shadow.render_task_id = Some(render_task_id);
// ignore the new task if we are in a dependency context
box_shadow.render_task_id = render_tasks.map(|rt| rt.add(render_task));
}
PrimitiveKind::TextShadow => {
let shadow = &mut self.cpu_text_shadows[cpu_prim_index.0];
let shadow = &mut self.cpu_text_shadows[metadata.cpu_prim_index.0];
// This is a text-shadow element. Create a render task that will
// render the text run to a target, and then apply a gaussian
@ -1234,24 +1127,28 @@ impl PrimitiveStore {
(metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
let cache_size = DeviceIntSize::new(cache_width, cache_height);
let blur_radius = device_length(shadow.shadow.blur_radius, prim_context.device_pixel_ratio);
let prim_cache_task = RenderTask::new_prim_cache(cache_size, prim_index);
let prim_cache_task_id = render_tasks.add(prim_cache_task);
let render_task =
RenderTask::new_blur(blur_radius, prim_cache_task_id, render_tasks);
shadow.render_task_id = Some(render_tasks.add(render_task));
// ignore new tasks if we are in a dependency context
shadow.render_task_id = render_tasks.map(|rt| {
let prim_cache_task = RenderTask::new_prim_cache(cache_size, prim_index);
let prim_cache_task_id = rt.add(prim_cache_task);
let render_task =
RenderTask::new_blur(blur_radius, prim_cache_task_id, rt);
rt.add(render_task)
});
}
PrimitiveKind::TextRun => {
let text = &mut self.cpu_text_runs[cpu_prim_index.0];
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
text.prepare_for_render(
resource_cache,
prim_context.device_pixel_ratio,
display_list,
prim_context.display_list,
text_run_mode,
gpu_cache,
);
}
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[cpu_prim_index.0];
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
resource_cache.request_image(
image_cpu.image_key,
@ -1273,7 +1170,7 @@ impl PrimitiveStore {
}
}
PrimitiveKind::YuvImage => {
let image_cpu = &mut self.cpu_yuv_images[cpu_prim_index.0];
let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
let channel_num = image_cpu.format.get_plane_num();
debug_assert!(channel_num <= 3);
@ -1298,7 +1195,7 @@ impl PrimitiveStore {
match metadata.prim_kind {
PrimitiveKind::Rectangle => {
let rect = &self.cpu_rectangles[cpu_prim_index.0];
let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
rect.write_gpu_blocks(request);
}
PrimitiveKind::Line => {
@ -1306,39 +1203,39 @@ impl PrimitiveStore {
line.write_gpu_blocks(request);
}
PrimitiveKind::Border => {
let border = &self.cpu_borders[cpu_prim_index.0];
let border = &self.cpu_borders[metadata.cpu_prim_index.0];
border.write_gpu_blocks(request);
}
PrimitiveKind::BoxShadow => {
let box_shadow = &self.cpu_box_shadows[cpu_prim_index.0];
let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
box_shadow.write_gpu_blocks(request);
}
PrimitiveKind::Image => {
let image = &self.cpu_images[cpu_prim_index.0];
let image = &self.cpu_images[metadata.cpu_prim_index.0];
image.write_gpu_blocks(request);
}
PrimitiveKind::YuvImage => {
let yuv_image = &self.cpu_yuv_images[cpu_prim_index.0];
let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
yuv_image.write_gpu_blocks(request);
}
PrimitiveKind::AlignedGradient => {
let gradient = &self.cpu_gradients[cpu_prim_index.0];
metadata.opacity = gradient.build_gpu_blocks_for_aligned(display_list, request);
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
metadata.opacity = gradient.build_gpu_blocks_for_aligned(prim_context.display_list, request);
}
PrimitiveKind::AngleGradient => {
let gradient = &self.cpu_gradients[cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(display_list, request);
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
}
PrimitiveKind::RadialGradient => {
let gradient = &self.cpu_radial_gradients[cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(display_list, request);
let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
}
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[cpu_prim_index.0];
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::TextShadow => {
let prim = &self.cpu_text_shadows[cpu_prim_index.0];
let prim = &self.cpu_text_shadows[metadata.cpu_prim_index.0];
request.push(prim.shadow.color);
request.push([
prim.shadow.offset.x,
@ -1349,11 +1246,179 @@ impl PrimitiveStore {
}
}
}
}
Some(Geometry {
local_rect: prim_local_rect,
device_rect: prim_screen_rect,
})
fn update_clip_task(
&mut self,
prim_index: PrimitiveIndex,
prim_context: &PrimitiveContext,
prim_screen_rect: DeviceIntRect,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
) -> bool {
let metadata = &mut self.cpu_metadata[prim_index.0];
clip_store.get_mut(&metadata.clip_sources).update(
&prim_context.packed_layer.transform,
gpu_cache,
resource_cache,
prim_context.device_pixel_ratio,
);
// Try to create a mask if we may need to.
let prim_clips = clip_store.get(&metadata.clip_sources);
let is_axis_aligned = prim_context.packed_layer.transform.preserves_2d_axis_alignment();
let clip_task = if prim_clips.is_masking() {
// Take into account the actual clip info of the primitive, and
// mutate the current bounds accordingly.
let mask_rect = match prim_clips.bounds.outer {
Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
Some(rect) => rect,
None => {
metadata.screen_rect = None;
return false;
}
},
_ => prim_screen_rect,
};
let extra = ClipWorkItem {
layer_index: prim_context.packed_layer_index,
clip_sources: metadata.clip_sources.weak(),
apply_rectangles: false,
};
RenderTask::new_mask(
None,
mask_rect,
&prim_context.current_clip_stack,
Some(extra),
prim_screen_rect,
clip_store,
is_axis_aligned,
)
} else if !prim_context.current_clip_stack.is_empty() {
// If the primitive doesn't have a specific clip, key the task ID off the
// stacking context. This means that two primitives which are only clipped
// by the stacking context stack can share clip masks during render task
// assignment to targets.
RenderTask::new_mask(
Some(prim_context.clip_id),
prim_context.clip_bounds,
&prim_context.current_clip_stack,
None,
prim_screen_rect,
clip_store,
is_axis_aligned,
)
} else {
None
};
metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
true
}
/// Returns true if the bounding box needs to be updated.
pub fn prepare_prim_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_context: &PrimitiveContext,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
) -> Option<Geometry> {
let (geometry, dependent_primitives) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.screen_rect = None;
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
warn!("invalid primitive rect {:?}", metadata.local_rect);
return None;
}
if !metadata.is_backface_visible &&
prim_context.packed_layer.transform.is_backface_visible() {
return None;
}
let local_rect = metadata
.local_rect
.intersection(&metadata.local_clip_rect)
.and_then(|rect| rect.intersection(&prim_context.packed_layer.local_clip_rect));
let local_rect = match local_rect {
Some(local_rect) => local_rect,
None => return None,
};
let xf_rect = TransformedRect::new(
&local_rect,
&prim_context.packed_layer.transform,
prim_context.device_pixel_ratio
);
metadata.screen_rect = xf_rect
.bounding_rect
.intersection(&prim_context.clip_bounds);
let geometry = match metadata.screen_rect {
Some(device_rect) => Geometry {
local_rect,
device_rect,
},
None => return None,
};
let dependencies = match metadata.prim_kind {
PrimitiveKind::TextShadow =>
self.cpu_text_shadows[metadata.cpu_prim_index.0].primitives.clone(),
_ => Vec::new(),
};
(geometry, dependencies)
};
// Recurse into any sub primitives and prepare them for rendering first.
// TODO(gw): This code is a bit hacky to work around the borrow checker.
// Specifically, the clone() below on the primitive list for
// text shadow primitives. Consider restructuring this code to
// avoid borrow checker issues.
for sub_prim_index in dependent_primitives {
self.prepare_prim_for_render_inner(
sub_prim_index,
prim_context,
resource_cache,
gpu_cache,
None,
TextRunMode::Shadow,
);
}
if !self.update_clip_task(
prim_index,
prim_context,
geometry.device_rect,
resource_cache,
gpu_cache,
render_tasks,
clip_store,
) {
return None;
}
self.prepare_prim_for_render_inner(
prim_index,
prim_context,
resource_cache,
gpu_cache,
Some(render_tasks),
TextRunMode::Normal,
);
Some(geometry)
}
}

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

@ -307,6 +307,7 @@ impl RenderTask {
extra_clip: Option<ClipWorkItem>,
prim_rect: DeviceIntRect,
clip_store: &ClipStore,
is_axis_aligned: bool,
) -> Option<RenderTask> {
// Filter out all the clip instances that don't contribute to the result
let mut inner_rect = Some(task_rect);
@ -358,7 +359,7 @@ impl RenderTask {
if inner_rect.contains_rect(&prim_rect) {
return None;
}
if clips.len() == 1 {
if is_axis_aligned && clips.len() == 1 {
geometry_kind = clips[0].get_geometry_kind(clip_store);
}
}

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

@ -558,6 +558,7 @@ impl LocalClip {
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ComplexClipRegion {
/// The boundaries of the rectangle.

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

@ -1051,6 +1051,8 @@ description =
description =
[PHandlerService::FillHandlerInfo]
description =
[PHandlerService::ExistsForProtocol]
description = bug 1382323
[PHandlerService::Exists]
description =
[PHandlerService::GetTypeFromExtension]

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

@ -676,6 +676,11 @@ static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr)
ABORT(R_NOT_FOUND);
}
if (nr_transport_addr_check_compatibility(addr, &cand->base)) {
r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Skipping STUN server because of link local mis-match for candidate %s",cand->ctx->label,cand->label);
ABORT(R_NOT_FOUND);
}
/* Copy the address */
if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr))
ABORT(r);

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

@ -249,6 +249,15 @@ static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_compon
if(ctx->stun_servers[j].transport!=IPPROTO_UDP)
continue;
if (ctx->stun_servers[j].type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
if (nr_transport_addr_check_compatibility(
&addrs[i].addr,
&ctx->stun_servers[j].u.addr)) {
r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping STUN server because of link local mis-match",ctx->label);
continue;
}
}
/* Ensure id is set (nr_ice_ctx_set_stun_servers does not) */
ctx->stun_servers[j].id = j;
if(r=nr_ice_candidate_create(ctx,component,
@ -279,6 +288,15 @@ static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_compon
if (ctx->turn_servers[j].turn_server.transport != IPPROTO_UDP)
continue;
if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
if (nr_transport_addr_check_compatibility(
&addrs[i].addr,
&ctx->turn_servers[j].turn_server.u.addr)) {
r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of link local mis-match",ctx->label);
continue;
}
}
if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
/* Ensure id is set with a unique value */
ctx->turn_servers[j].turn_server.id = j + ctx->stun_server_ct;
@ -514,12 +532,13 @@ static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_compon
if (ctx->turn_servers[j].turn_server.transport != IPPROTO_TCP)
continue;
if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR &&
nr_transport_addr_cmp(&ctx->turn_servers[j].turn_server.u.addr,
&addrs[i].addr,
NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) {
r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of IP version mis-match (%u - %u)",ctx->label,addrs[i].addr.ip_version,ctx->turn_servers[j].turn_server.u.addr.ip_version);
continue;
if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
if (nr_transport_addr_check_compatibility(
&addrs[i].addr,
&ctx->turn_servers[j].turn_server.u.addr)) {
r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of link local mis-match",ctx->label);
continue;
}
}
if (!ice_tcp_disabled) {

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

@ -436,6 +436,21 @@ int nr_transport_addr_is_link_local(nr_transport_addr *addr)
return(0);
}
int nr_transport_addr_check_compatibility(nr_transport_addr *addr1, nr_transport_addr *addr2)
{
// first make sure we're comparing the same ip versions and protocols
if ((addr1->ip_version != addr2->ip_version) ||
(addr1->protocol != addr2->protocol)) {
return(1);
}
// now make sure the link local status matches
if (nr_transport_addr_is_link_local(addr1) !=
nr_transport_addr_is_link_local(addr2)) {
return(1);
}
return(0);
}
int nr_transport_addr_is_wildcard(nr_transport_addr *addr)
{
switch(addr->ip_version){

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

@ -93,6 +93,7 @@ int nr_transport_addr_is_wildcard(nr_transport_addr *addr);
int nr_transport_addr_is_loopback(nr_transport_addr *addr);
int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr);
int nr_transport_addr_is_link_local(nr_transport_addr *addr);
int nr_transport_addr_check_compatibility(nr_transport_addr *addr1, nr_transport_addr *addr2);
int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from);
int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from);
int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr);

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

До

Ширина:  |  Высота:  |  Размер: 12 KiB

Двоичные данные
mobile/android/branding/beta/res/drawable-nodpi/firstrun_welcome.png Executable file

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

После

Ширина:  |  Высота:  |  Размер: 22 KiB

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

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
mobile/android/branding/nightly/res/drawable-nodpi/firstrun_welcome.png Executable file

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

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
mobile/android/branding/official/res/drawable-nodpi/firstrun_welcome.png Executable file

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

После

Ширина:  |  Высота:  |  Размер: 21 KiB

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