Merge autoland to central, a=merge
MozReview-Commit-ID: 4jAMhgCDoPO
|
@ -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");
|
||||
|
|
После Ширина: | Высота: | Размер: 580 B |
После Ширина: | Высота: | Размер: 580 B |
После Ширина: | Высота: | Размер: 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 |
Двоичные данные
mobile/android/branding/nightly-old-id/res/drawable-nodpi/firstrun_welcome.png
Executable file
После Ширина: | Высота: | Размер: 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 |