mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
This commit is contained in:
commit
36f8be9d4e
@ -2,3 +2,8 @@
|
||||
# https://hg.mozilla.org/mozilla-central/rev/0ceae9db9ec0be18daa1a279511ad305723185d4
|
||||
abd6d77c618998827e5ffc3dab12f1a34d6ed03d # cinnabar
|
||||
804b8b8883ba2a6795b0fcf65ebcb18306b6416b # gecko-dev
|
||||
|
||||
# Bug 1511181 - Reformat everything to the Google coding style r=ehsan
|
||||
# https://hg.mozilla.org/mozilla-central/rev/6f3709b3878117466168c40affa7bca0b60cf75b
|
||||
0e0308d10a5fd4a8dcf0601978776342a2abf2df # cinnabar
|
||||
265e6721798a455604328ed5262f430cfcc37c2f # gecko-dev
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1918,6 +1918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "profiler_helper"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"object 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -33,7 +33,7 @@
|
||||
<vbox id="separateProfileBox">
|
||||
<checkbox id="separateProfileMode"
|
||||
data-l10n-id="separate-profile-mode"/>
|
||||
<hbox id="sync-dev-edition-root" lign="center" class="indent" hidden="true">
|
||||
<hbox id="sync-dev-edition-root" align="center" class="indent" hidden="true">
|
||||
<label id="useFirefoxSync" data-l10n-id="use-firefox-sync"/>
|
||||
<deck id="getStarted">
|
||||
<label class="text-link" data-l10n-id="get-started-not-logged-in"/>
|
||||
|
@ -216,18 +216,30 @@ const reducers = {
|
||||
// Find the position of any added declaration which matches the incoming
|
||||
// declaration to be removed.
|
||||
const addIndex = rule.add.findIndex(addDecl => {
|
||||
return addDecl.index === decl.index;
|
||||
return addDecl.index === decl.index &&
|
||||
addDecl.property === decl.property &&
|
||||
addDecl.value === decl.value;
|
||||
});
|
||||
|
||||
// Find the position of any removed declaration which matches the incoming
|
||||
// declaration to be removed. It's possible to get duplicate remove operations
|
||||
// when, for example, disabling a declaration then deleting it.
|
||||
const removeIndex = rule.remove.findIndex(removeDecl => {
|
||||
return removeDecl.index === decl.index &&
|
||||
removeDecl.property === decl.property &&
|
||||
removeDecl.value === decl.value;
|
||||
});
|
||||
|
||||
// Track the remove operation only if the property was not previously introduced
|
||||
// by an add operation. This ensures repeated changes of the same property
|
||||
// register as a single remove operation of its original value.
|
||||
if (addIndex < 0) {
|
||||
// register as a single remove operation of its original value. Avoid tracking the
|
||||
// remove declaration if already tracked (happens on disable followed by delete).
|
||||
if (addIndex < 0 && removeIndex < 0) {
|
||||
rule.remove.push(decl);
|
||||
}
|
||||
|
||||
// Delete any previous add operation which would be canceled out by this remove.
|
||||
if (rule.add[addIndex] && rule.add[addIndex].value === decl.value) {
|
||||
if (rule.add[addIndex]) {
|
||||
rule.add.splice(addIndex, 1);
|
||||
}
|
||||
|
||||
@ -258,18 +270,19 @@ const reducers = {
|
||||
// Find the position of any removed declaration which matches the incoming
|
||||
// declaration to be added.
|
||||
const removeIndex = rule.remove.findIndex(removeDecl => {
|
||||
return removeDecl.index === decl.index;
|
||||
return removeDecl.index === decl.index &&
|
||||
removeDecl.value === decl.value &&
|
||||
removeDecl.property === decl.property;
|
||||
});
|
||||
|
||||
// Find the position of any added declaration which matches the incoming
|
||||
// declaration to be added in case we need to replace it.
|
||||
const addIndex = rule.add.findIndex(addDecl => {
|
||||
return addDecl.index === decl.index;
|
||||
return addDecl.index === decl.index &&
|
||||
addDecl.property === decl.property;
|
||||
});
|
||||
|
||||
if (rule.remove[removeIndex] &&
|
||||
rule.remove[removeIndex].value === decl.value &&
|
||||
rule.remove[removeIndex].property === decl.property) {
|
||||
if (rule.remove[removeIndex]) {
|
||||
// Delete any previous remove operation which would be canceled out by this add.
|
||||
rule.remove.splice(removeIndex, 1);
|
||||
} else if (rule.add[addIndex]) {
|
||||
|
@ -16,6 +16,7 @@ support-files =
|
||||
[browser_changes_declaration_duplicate.js]
|
||||
[browser_changes_declaration_edit_value.js]
|
||||
[browser_changes_declaration_remove_ahead.js]
|
||||
[browser_changes_declaration_remove_disabled.js]
|
||||
[browser_changes_declaration_remove.js]
|
||||
[browser_changes_declaration_rename.js]
|
||||
[browser_changes_rule_selector.js]
|
||||
|
@ -0,0 +1,99 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that disabling a CSS declaration and then removing it from the Rule view
|
||||
// is tracked as removed only once. Toggling leftover declarations should not introduce
|
||||
// duplicate changes.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type='text/css'>
|
||||
div {
|
||||
color: red;
|
||||
background: black;
|
||||
}
|
||||
</style>
|
||||
<div></div>
|
||||
`;
|
||||
|
||||
add_task(async function() {
|
||||
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
const { inspector, view: ruleView } = await openRuleView();
|
||||
const { document: doc, store } = selectChangesView(inspector);
|
||||
|
||||
await selectNode("div", inspector);
|
||||
const rule = getRuleViewRuleEditor(ruleView, 1).rule;
|
||||
|
||||
info("Using the second declaration");
|
||||
await testRemoveValue(ruleView, store, doc, rule.textProps[1]);
|
||||
info("Using the first declaration");
|
||||
await testToggle(ruleView, store, doc, rule.textProps[0]);
|
||||
info("Using the first declaration");
|
||||
await testRemoveName(ruleView, store, doc, rule.textProps[0]);
|
||||
});
|
||||
|
||||
async function testRemoveValue(ruleView, store, doc, prop) {
|
||||
info("Test removing disabled declaration by clearing its property value.");
|
||||
let onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Disable the declaration");
|
||||
await togglePropStatus(ruleView, prop);
|
||||
info("Wait for change to be tracked");
|
||||
await onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Remove the disabled declaration by clearing its value");
|
||||
await setProperty(ruleView, prop, null);
|
||||
await onTrackChange;
|
||||
|
||||
const removeDecl = getRemovedDeclarations(doc);
|
||||
is(removeDecl.length, 1, "Only one declaration tracked as removed");
|
||||
}
|
||||
|
||||
async function testToggle(ruleView, store, doc, prop) {
|
||||
info("Test toggling leftover declaration off and on will not track extra changes.");
|
||||
let onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Disable the declaration");
|
||||
await togglePropStatus(ruleView, prop);
|
||||
await onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Re-enable the declaration");
|
||||
await togglePropStatus(ruleView, prop);
|
||||
await onTrackChange;
|
||||
|
||||
const removeDecl = getRemovedDeclarations(doc);
|
||||
is(removeDecl.length, 1, "Still just one declaration tracked as removed");
|
||||
}
|
||||
|
||||
async function testRemoveName(ruleView, store, doc, prop) {
|
||||
info("Test removing disabled declaration by clearing its property name.");
|
||||
let onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Disable the declaration");
|
||||
await togglePropStatus(ruleView, prop);
|
||||
await onTrackChange;
|
||||
|
||||
onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
|
||||
info("Remove the disabled declaration by clearing its name");
|
||||
await removeProperty(ruleView, prop);
|
||||
await onTrackChange;
|
||||
|
||||
info(`Expecting two declarations removed:
|
||||
- one removed by its value in the other test
|
||||
- one removed by its name from this test
|
||||
`);
|
||||
|
||||
const removeDecl = getRemovedDeclarations(doc);
|
||||
is(removeDecl.length, 2, "Two declarations tracked as removed");
|
||||
is(removeDecl[0].property, "background", "First declaration name correct");
|
||||
is(removeDecl[0].value, "black", "First declaration value correct");
|
||||
is(removeDecl[1].property, "color", "Second declaration name correct");
|
||||
is(removeDecl[1].value, "red", "Second declaration value correct");
|
||||
}
|
@ -49,5 +49,6 @@ support-files =
|
||||
[browser_flexbox_sizing_info_updates_on_change.js]
|
||||
[browser_flexbox_sizing_wanted_to_grow_but_was_clamped.js]
|
||||
[browser_flexbox_text_nodes_are_listed.js]
|
||||
[browser_flexbox_text_nodes_are_not_inlined.js]
|
||||
[browser_flexbox_toggle_flexbox_highlighter_01.js]
|
||||
[browser_flexbox_toggle_flexbox_highlighter_02.js]
|
||||
|
@ -0,0 +1,40 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that single child text nodes that are also flex items can be selected in the
|
||||
// flexbox inspector.
|
||||
// This means that they are not inlined like normal single child text nodes, since
|
||||
// selecting them in the flexbox inspector also means selecting them in the markup view.
|
||||
|
||||
const TEST_URI = URL_ROOT + "doc_flexbox_text_nodes.html";
|
||||
|
||||
add_task(async function() {
|
||||
await addTab(TEST_URI);
|
||||
const { inspector, flexboxInspector } = await openLayoutView();
|
||||
const { document: doc } = flexboxInspector;
|
||||
|
||||
// Select the flex container in the inspector.
|
||||
const onItemsListRendered = waitForDOM(doc, ".layout-flexbox-wrapper .flex-item-list");
|
||||
await selectNode(".container.single-child", inspector);
|
||||
const [flexItemList] = await onItemsListRendered;
|
||||
|
||||
const items = [...flexItemList.querySelectorAll("button .objectBox")];
|
||||
is(items.length, 1, "There is 1 item displayed in the list");
|
||||
is(items[0].textContent, "#text", "The item in the list is a text node");
|
||||
|
||||
info("Click on the item to select it");
|
||||
const onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
|
||||
items[0].closest("button").click();
|
||||
const [flexOutlineContainer] = await onFlexItemOutlineRendered;
|
||||
ok(flexOutlineContainer,
|
||||
"The flex outline is displayed for a single child short text node too");
|
||||
|
||||
ok(inspector.selection.isTextNode(),
|
||||
"The current inspector selection is the text node");
|
||||
|
||||
const markupContainer = inspector.markup.getContainer(inspector.selection.nodeFront);
|
||||
is(markupContainer.elt.textContent, "short text", "This is the right text node");
|
||||
});
|
@ -17,3 +17,4 @@
|
||||
<div></div>
|
||||
Here is yet another text node
|
||||
</div>
|
||||
<div class="container single-child">short text</div>
|
||||
|
@ -20,17 +20,6 @@ const DATA = [
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "layoutview",
|
||||
newpanel: "changesview",
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamp: null,
|
||||
category: "devtools.main",
|
||||
method: "sidepanel_changed",
|
||||
object: "inspector",
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "changesview",
|
||||
newpanel: "animationinspector",
|
||||
},
|
||||
},
|
||||
@ -75,17 +64,6 @@ const DATA = [
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "computedview",
|
||||
newpanel: "changesview",
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamp: null,
|
||||
category: "devtools.main",
|
||||
method: "sidepanel_changed",
|
||||
object: "inspector",
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "changesview",
|
||||
newpanel: "animationinspector",
|
||||
},
|
||||
},
|
||||
@ -128,9 +106,6 @@ add_task(async function() {
|
||||
// Let's reset the counts.
|
||||
Services.telemetry.clearEvents();
|
||||
|
||||
// Ensure the Changes panel is enabled before running the tests.
|
||||
await pushPref("devtools.inspector.changes.enabled", true);
|
||||
|
||||
// Ensure no events have been logged
|
||||
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
|
||||
ok(!snapshot.parent, "No events have been logged for the main process");
|
||||
@ -155,7 +130,7 @@ function testSidebar(toolbox) {
|
||||
|
||||
const inspector = toolbox.getCurrentPanel();
|
||||
let sidebarTools = ["computedview", "layoutview", "fontinspector",
|
||||
"animationinspector", "changesview"];
|
||||
"animationinspector"];
|
||||
|
||||
// Concatenate the array with itself so that we can open each tool twice.
|
||||
sidebarTools = [...sidebarTools, ...sidebarTools];
|
||||
@ -184,11 +159,9 @@ function checkResults() {
|
||||
checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
|
||||
checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", {0: 3, 1: 0}, "array");
|
||||
checkTelemetry("DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
|
||||
checkTelemetry("devtools.changesview.opened_count", "", 2, "scalar");
|
||||
checkTelemetry("DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_CHANGESVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
}
|
||||
|
||||
function checkEventTelemetry() {
|
||||
|
@ -35,6 +35,7 @@ loader.lazyRequireGetter(this, "SKIP_TO_SIBLING", "devtools/server/actors/inspec
|
||||
loader.lazyRequireGetter(this, "NodeActor", "devtools/server/actors/inspector/node", true);
|
||||
loader.lazyRequireGetter(this, "NodeListActor", "devtools/server/actors/inspector/node", true);
|
||||
loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
|
||||
loader.lazyRequireGetter(this, "findFlexOrGridParentContainerForNode", "devtools/server/actors/layout", true);
|
||||
loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
|
||||
loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
|
||||
loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
|
||||
@ -509,13 +510,13 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
*
|
||||
* @param NodeActor node
|
||||
*/
|
||||
inlineTextChild: function(node) {
|
||||
inlineTextChild: function({ rawNode }) {
|
||||
// Quick checks to prevent creating a new walker if possible.
|
||||
if (isBeforePseudoElement(node.rawNode) ||
|
||||
isAfterPseudoElement(node.rawNode) ||
|
||||
isShadowHost(node.rawNode) ||
|
||||
node.rawNode.nodeType != Node.ELEMENT_NODE ||
|
||||
node.rawNode.children.length > 0) {
|
||||
if (isBeforePseudoElement(rawNode) ||
|
||||
isAfterPseudoElement(rawNode) ||
|
||||
isShadowHost(rawNode) ||
|
||||
rawNode.nodeType != Node.ELEMENT_NODE ||
|
||||
rawNode.children.length > 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -524,11 +525,11 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
// By default always try to use an anonymous walker. Even for DirectShadowHostChild,
|
||||
// children should be available through an anonymous walker (unless the child is not
|
||||
// slotted, see catch block).
|
||||
walker = this.getDocumentWalker(node.rawNode);
|
||||
walker = this.getDocumentWalker(rawNode);
|
||||
} catch (e) {
|
||||
// Using an anonymous walker might throw, for instance on unslotted shadow host
|
||||
// children.
|
||||
walker = this.getNonAnonymousWalker(node.rawNode);
|
||||
walker = this.getNonAnonymousWalker(rawNode);
|
||||
}
|
||||
|
||||
const firstChild = walker.firstChild();
|
||||
@ -539,17 +540,23 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
// - unique child is a text node, but is too long to be inlined
|
||||
// - we are a slot -> these are always represented on their own lines with
|
||||
// a link to the original node.
|
||||
// - we are a flex item -> these are always shown on their own lines so they can be
|
||||
// selected by the flexbox inspector.
|
||||
const isAssignedSlot =
|
||||
!!firstChild &&
|
||||
node.rawNode.nodeName === "SLOT" &&
|
||||
rawNode.nodeName === "SLOT" &&
|
||||
isDirectShadowHostChild(firstChild);
|
||||
|
||||
const isFlexItem =
|
||||
!!firstChild &&
|
||||
findFlexOrGridParentContainerForNode(firstChild, "flex", this);
|
||||
|
||||
if (!firstChild ||
|
||||
walker.nextSibling() ||
|
||||
firstChild.nodeType !== Node.TEXT_NODE ||
|
||||
firstChild.nodeValue.length > gValueSummaryLength ||
|
||||
isAssignedSlot
|
||||
) {
|
||||
isAssignedSlot ||
|
||||
isFlexItem) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,8 @@ const FlexboxActor = ActorClassWithSpec(flexboxSpec, {
|
||||
* Returns an array of FlexItemActor objects for all the flex item elements contained
|
||||
* in the flex container element.
|
||||
*
|
||||
* @return {Array} An array of FlexItemActor objects.
|
||||
* @return {Array}
|
||||
* An array of FlexItemActor objects.
|
||||
*/
|
||||
getFlexItems() {
|
||||
if (isNodeDead(this.containerEl)) {
|
||||
@ -335,12 +336,11 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, {
|
||||
* The node to start iterating at.
|
||||
* @param {String} type
|
||||
* Can be "grid" or "flex", the display type we are searching for.
|
||||
* @param {Node|null} container
|
||||
* The container of the current node.
|
||||
* @return {GridActor|FlexboxActor|null} The GridActor or FlexboxActor of the
|
||||
* grid/flex container of the give node. Otherwise, returns null.
|
||||
* @return {GridActor|FlexboxActor|null}
|
||||
* The GridActor or FlexboxActor of the grid/flex container of the given node.
|
||||
* Otherwise, returns null.
|
||||
*/
|
||||
getCurrentDisplay(node, type, container) {
|
||||
getCurrentDisplay(node, type) {
|
||||
if (isNodeDead(node)) {
|
||||
return null;
|
||||
}
|
||||
@ -350,33 +350,20 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, {
|
||||
node = node.rawNode;
|
||||
}
|
||||
|
||||
const treeWalker = this.walker.getDocumentWalker(node, SHOW_ELEMENT);
|
||||
let currentNode = treeWalker.currentNode;
|
||||
let displayType = this.walker.getNode(currentNode).displayType;
|
||||
const flexType = type === "flex";
|
||||
const gridType = type === "grid";
|
||||
const displayType = this.walker.getNode(node).displayType;
|
||||
|
||||
// If the node is an element, check first if it is itself a flex or a grid.
|
||||
if (currentNode.nodeType === currentNode.ELEMENT_NODE) {
|
||||
if (node.nodeType === node.ELEMENT_NODE) {
|
||||
if (!displayType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type == "flex") {
|
||||
// If only the current node is being considered when finding its display, then
|
||||
// return it as only a flex container.
|
||||
if ((displayType == "inline-flex" || displayType == "flex") &&
|
||||
!container) {
|
||||
return new FlexboxActor(this, currentNode);
|
||||
|
||||
// If considering the current node's container, then we are trying to determine
|
||||
// the current node's flex item status.
|
||||
} else if (container) {
|
||||
if (this.isNodeFlexItemInContainer(currentNode, container)) {
|
||||
return new FlexboxActor(this, container);
|
||||
}
|
||||
}
|
||||
} else if (type == "grid" &&
|
||||
(displayType == "inline-grid" || displayType == "grid")) {
|
||||
return new GridActor(this, currentNode);
|
||||
if (flexType && displayType.includes("flex")) {
|
||||
return new FlexboxActor(this, node);
|
||||
} else if (gridType && displayType.includes("grid")) {
|
||||
return new GridActor(this, node);
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,68 +372,53 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, {
|
||||
// Note that text nodes that are children of flex/grid containers are wrapped in
|
||||
// anonymous containers, so even if their displayType getter returns null we still
|
||||
// want to walk up the chain to find their container.
|
||||
while ((currentNode = treeWalker.parentNode())) {
|
||||
if (!currentNode) {
|
||||
break;
|
||||
}
|
||||
|
||||
displayType = this.walker.getNode(currentNode).displayType;
|
||||
|
||||
if (type == "flex" &&
|
||||
(displayType == "inline-flex" || displayType == "flex")) {
|
||||
if (this.isNodeFlexItemInContainer(node, currentNode)) {
|
||||
return new FlexboxActor(this, currentNode);
|
||||
}
|
||||
} else if (type == "grid" &&
|
||||
(displayType == "inline-grid" || displayType == "grid")) {
|
||||
return new GridActor(this, currentNode);
|
||||
} else if (displayType == "contents") {
|
||||
// Continue walking up the tree since the parent node is a content element.
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
const container = findFlexOrGridParentContainerForNode(node, type, this.walker);
|
||||
if (container && flexType) {
|
||||
return new FlexboxActor(this, container);
|
||||
}
|
||||
if (container && gridType) {
|
||||
return new GridActor(this, container);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the grid container found by iterating on the given selected node. The current
|
||||
* node can be a grid container or grid item. If it is a grid item, returns the parent
|
||||
* grid container. Otherwise, return null if the current or parent node is not a grid
|
||||
* Returns the grid container for a given selected node.
|
||||
* The node itself can be a container, but if not, walk up the DOM to find its
|
||||
* container.
|
||||
* Returns null if no container can be found.
|
||||
*
|
||||
* @param {Node|NodeActor} node
|
||||
* The node to start iterating at.
|
||||
* @return {GridActor|null} The GridActor of the grid container of the given node.
|
||||
* Otherwise, returns null.
|
||||
* @return {GridActor|null}
|
||||
* The GridActor of the grid container of the given node. Otherwise, returns
|
||||
* null.
|
||||
*/
|
||||
getCurrentGrid(node) {
|
||||
return this.getCurrentDisplay(node, "grid");
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the flex container found by iterating on the given selected node. The current
|
||||
* node can be a flex container or flex item. If it is a flex item, returns the parent
|
||||
* flex container. Otherwise, return null if the current or parent node is not a flex
|
||||
* Returns the flex container for a given selected node.
|
||||
* The node itself can be a container, but if not, walk up the DOM to find its
|
||||
* container.
|
||||
* Returns null if no container can be found.
|
||||
*
|
||||
* @param {Node|NodeActor} node
|
||||
* The node to start iterating at.
|
||||
* @param {Boolean|null} onlyLookAtParents
|
||||
* Whether or not to only consider the parent node of the given node.
|
||||
* @return {FlexboxActor|null} The FlexboxActor of the flex container of the given node.
|
||||
* Otherwise, returns null.
|
||||
* If true, skip the passed node and only start looking at its parent and up.
|
||||
* @return {FlexboxActor|null}
|
||||
* The FlexboxActor of the flex container of the given node. Otherwise, returns
|
||||
* null.
|
||||
*/
|
||||
getCurrentFlexbox(node, onlyLookAtParents) {
|
||||
let container = null;
|
||||
|
||||
if (onlyLookAtParents) {
|
||||
container = node.rawNode.parentNode;
|
||||
node = node.rawNode.parentNode;
|
||||
}
|
||||
|
||||
return this.getCurrentDisplay(node, "flex", container);
|
||||
return this.getCurrentDisplay(node, "flex");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -482,41 +454,93 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, {
|
||||
|
||||
return gridActors;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether or not the given node is actually considered a flex item by its
|
||||
* container.
|
||||
*
|
||||
* @param {Node|NodeActor} supposedItem
|
||||
* The node that might be a flex item of its container.
|
||||
* @param {Node} container
|
||||
* The node's container.
|
||||
* @return {Boolean} Whether or not the node we are looking at is a flex item of its
|
||||
* container.
|
||||
*/
|
||||
isNodeFlexItemInContainer(supposedItem, container) {
|
||||
const containerDisplayType = this.walker.getNode(container).displayType;
|
||||
|
||||
if (containerDisplayType == "inline-flex" || containerDisplayType == "flex") {
|
||||
const containerFlex = container.getAsFlexContainer();
|
||||
|
||||
for (const line of containerFlex.getLines()) {
|
||||
for (const item of line.getItems()) {
|
||||
if (item.node === supposedItem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
function isNodeDead(node) {
|
||||
return !node || (node.rawNode && Cu.isDeadWrapper(node.rawNode));
|
||||
}
|
||||
|
||||
/**
|
||||
* If the provided node is a grid of flex item, then return its parent grid or flex
|
||||
* container.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* The node that is supposedly a grid or flex item.
|
||||
* @param {String} type
|
||||
* The type of container/item to look for: "flex" or "grid".
|
||||
* @param {WalkerActor} walkerActor
|
||||
* The current instance of WalkerActor.
|
||||
* @return {DOMNode|null}
|
||||
* The parent grid or flex container if found, null otherwise.
|
||||
*/
|
||||
function findFlexOrGridParentContainerForNode(node, type, walker) {
|
||||
const treeWalker = walker.getDocumentWalker(node, SHOW_ELEMENT);
|
||||
let currentNode = treeWalker.currentNode;
|
||||
|
||||
const flexType = type === "flex";
|
||||
const gridType = type === "grid";
|
||||
|
||||
try {
|
||||
while ((currentNode = treeWalker.parentNode())) {
|
||||
if (!currentNode) {
|
||||
break;
|
||||
}
|
||||
|
||||
const displayType = walker.getNode(currentNode).displayType;
|
||||
if (!displayType) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (flexType && displayType.includes("flex")) {
|
||||
if (isNodeAFlexItemInContainer(node, currentNode, walker)) {
|
||||
return currentNode;
|
||||
}
|
||||
} else if (gridType && displayType.includes("grid")) {
|
||||
return currentNode;
|
||||
} else if (displayType === "contents") {
|
||||
// Continue walking up the tree since the parent node is a content element.
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// Getting the parentNode can fail when the supplied node is in shadow DOM.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given node is actually considered a flex item by its
|
||||
* container.
|
||||
*
|
||||
* @param {Node|NodeActor} supposedItem
|
||||
* The node that might be a flex item of its container.
|
||||
* @param {Node} container
|
||||
* The node's container.
|
||||
* @return {Boolean}
|
||||
* Whether or not the node we are looking at is a flex item of its container.
|
||||
*/
|
||||
function isNodeAFlexItemInContainer(supposedItem, container, walker) {
|
||||
const containerDisplayType = walker.getNode(container).displayType;
|
||||
|
||||
if (containerDisplayType.includes("flex")) {
|
||||
const containerFlex = container.getAsFlexContainer();
|
||||
|
||||
for (const line of containerFlex.getLines()) {
|
||||
for (const item of line.getItems()) {
|
||||
if (item.node === supposedItem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exports.findFlexOrGridParentContainerForNode = findFlexOrGridParentContainerForNode;
|
||||
exports.FlexboxActor = FlexboxActor;
|
||||
exports.FlexItemActor = FlexItemActor;
|
||||
exports.GridActor = GridActor;
|
||||
|
@ -35,9 +35,7 @@ support-files =
|
||||
|
||||
[test_addon_events.js]
|
||||
[test_addon_reload.js]
|
||||
skip-if = verify # verify mode causes AddonRepository shutdown errors
|
||||
[test_addons_actor.js]
|
||||
skip-if = (verify && !debug && (os == 'win'))
|
||||
[test_animation_name.js]
|
||||
[test_animation_type.js]
|
||||
[test_actor-registry-actor.js]
|
||||
|
@ -1 +1 @@
|
||||
a1758f0fc17a71c8d53387be5e9da42495ee11ae
|
||||
add1538234a214a371e471bff67734afd7c3ca95
|
||||
|
@ -586,7 +586,7 @@ impl AlphaBatchBuilder {
|
||||
};
|
||||
|
||||
let instance = PrimitiveInstanceData::from(BrushInstance {
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::all(),
|
||||
clip_task_address,
|
||||
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
|
||||
@ -862,7 +862,7 @@ impl AlphaBatchBuilder {
|
||||
};
|
||||
|
||||
let instance = PrimitiveInstanceData::from(BrushInstance {
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::all(),
|
||||
clip_task_address,
|
||||
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
|
||||
@ -1107,7 +1107,7 @@ impl AlphaBatchBuilder {
|
||||
|
||||
let instance = BrushInstance {
|
||||
prim_header_index,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
clip_task_address,
|
||||
@ -1189,7 +1189,7 @@ impl AlphaBatchBuilder {
|
||||
let shadow_instance = BrushInstance {
|
||||
prim_header_index: shadow_prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
user_data: shadow_uv_rect_address,
|
||||
@ -1198,7 +1198,7 @@ impl AlphaBatchBuilder {
|
||||
let content_instance = BrushInstance {
|
||||
prim_header_index: content_prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
user_data: content_uv_rect_address,
|
||||
@ -1283,7 +1283,7 @@ impl AlphaBatchBuilder {
|
||||
let instance = BrushInstance {
|
||||
prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
user_data: 0,
|
||||
@ -1328,7 +1328,7 @@ impl AlphaBatchBuilder {
|
||||
let instance = BrushInstance {
|
||||
prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
user_data: 0,
|
||||
@ -1368,7 +1368,7 @@ impl AlphaBatchBuilder {
|
||||
let instance = BrushInstance {
|
||||
prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: 0,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags: BrushFlags::empty(),
|
||||
user_data: uv_rect_address,
|
||||
|
@ -1017,7 +1017,7 @@ impl PrimitiveTemplateKind {
|
||||
fn write_prim_gpu_blocks(
|
||||
&self,
|
||||
request: &mut GpuDataRequest,
|
||||
prim_rect: LayoutRect,
|
||||
prim_size: LayoutSize,
|
||||
) {
|
||||
match *self {
|
||||
PrimitiveTemplateKind::Clear => {
|
||||
@ -1034,8 +1034,8 @@ impl PrimitiveTemplateKind {
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push([
|
||||
prim_rect.size.width,
|
||||
prim_rect.size.height,
|
||||
prim_size.width,
|
||||
prim_size.height,
|
||||
0.0,
|
||||
0.0,
|
||||
]);
|
||||
@ -1047,8 +1047,8 @@ impl PrimitiveTemplateKind {
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push([
|
||||
prim_rect.size.width,
|
||||
prim_rect.size.height,
|
||||
prim_size.width,
|
||||
prim_size.height,
|
||||
0.0,
|
||||
0.0,
|
||||
]);
|
||||
@ -1165,15 +1165,8 @@ impl PrimitiveTemplateKind {
|
||||
fn write_segment_gpu_blocks(
|
||||
&self,
|
||||
request: &mut GpuDataRequest,
|
||||
prim_rect: LayoutRect,
|
||||
) {
|
||||
match *self {
|
||||
PrimitiveTemplateKind::Clear => {
|
||||
request.write_segment(
|
||||
prim_rect,
|
||||
[0.0; 4],
|
||||
);
|
||||
}
|
||||
PrimitiveTemplateKind::NormalBorder { ref template, .. } => {
|
||||
for segment in &template.brush_segments {
|
||||
// has to match VECS_PER_SEGMENT
|
||||
@ -1192,12 +1185,6 @@ impl PrimitiveTemplateKind {
|
||||
);
|
||||
}
|
||||
}
|
||||
PrimitiveTemplateKind::LineDecoration { .. } => {
|
||||
request.write_segment(
|
||||
prim_rect,
|
||||
[0.0; 4],
|
||||
);
|
||||
}
|
||||
PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } |
|
||||
PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => {
|
||||
for segment in brush_segments {
|
||||
@ -1208,6 +1195,8 @@ impl PrimitiveTemplateKind {
|
||||
);
|
||||
}
|
||||
}
|
||||
PrimitiveTemplateKind::Clear |
|
||||
PrimitiveTemplateKind::LineDecoration { .. } |
|
||||
PrimitiveTemplateKind::Image { .. } |
|
||||
PrimitiveTemplateKind::Rectangle { .. } |
|
||||
PrimitiveTemplateKind::TextRun { .. } |
|
||||
@ -1233,8 +1222,11 @@ impl PrimitiveTemplate {
|
||||
frame_state: &mut FrameBuildingState,
|
||||
) {
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
|
||||
self.kind.write_prim_gpu_blocks(&mut request, self.prim_rect);
|
||||
self.kind.write_segment_gpu_blocks(&mut request, self.prim_rect);
|
||||
self.kind.write_prim_gpu_blocks(
|
||||
&mut request,
|
||||
self.prim_rect.size,
|
||||
);
|
||||
self.kind.write_segment_gpu_blocks(&mut request);
|
||||
}
|
||||
|
||||
self.opacity = match self.kind {
|
||||
@ -3097,10 +3089,6 @@ impl PrimitiveStore {
|
||||
0.0,
|
||||
0.0,
|
||||
]);
|
||||
request.write_segment(
|
||||
pic.local_rect,
|
||||
[0.0; 4],
|
||||
);
|
||||
}
|
||||
}
|
||||
PrimitiveInstanceKind::TextRun { .. } |
|
||||
@ -3604,7 +3592,10 @@ impl PrimitiveStore {
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) {
|
||||
let segments = &scratch.segments[segment_instance.segments_range];
|
||||
|
||||
prim_data.kind.write_prim_gpu_blocks(&mut request, prim_data.prim_rect);
|
||||
prim_data.kind.write_prim_gpu_blocks(
|
||||
&mut request,
|
||||
prim_data.prim_rect.size,
|
||||
);
|
||||
|
||||
for segment in segments {
|
||||
request.write_segment(
|
||||
|
@ -380,9 +380,9 @@ class Bootstrapper(object):
|
||||
sys.exit(1)
|
||||
|
||||
self.instance.state_dir = state_dir
|
||||
self.instance.ensure_clang_static_analysis_package(checkout_root)
|
||||
self.instance.ensure_stylo_packages(state_dir, checkout_root)
|
||||
self.instance.ensure_node_packages(state_dir, checkout_root)
|
||||
self.instance.ensure_clang_static_analysis_package(checkout_root)
|
||||
|
||||
def check_telemetry_opt_in(self, state_dir):
|
||||
# We can't prompt the user.
|
||||
|
@ -885,10 +885,15 @@ bool AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's check if this is a 3rd party context.
|
||||
if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
|
||||
LOG(("Our window isn't a third-party window"));
|
||||
return true;
|
||||
// As a performance optimization, we only perform this check for
|
||||
// BEHAVIOR_REJECT_FOREIGN and BEHAVIOR_LIMIT_FOREIGN. For
|
||||
// BEHAVIOR_REJECT_TRACKER, third-partiness is implicily checked later below.
|
||||
if (behavior != nsICookieService::BEHAVIOR_REJECT_TRACKER) {
|
||||
// Let's check if this is a 3rd party context.
|
||||
if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
|
||||
LOG(("Our window isn't a third-party window"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
|
||||
@ -909,6 +914,20 @@ bool AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
|
||||
if (thirdPartyUtil) {
|
||||
bool thirdParty = false;
|
||||
nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
|
||||
aURI, &thirdParty);
|
||||
// The result of this assertion depends on whether IsThirdPartyWindow
|
||||
// succeeds, because otherwise IsThirdPartyWindowOrChannel artificially
|
||||
// fails.
|
||||
MOZ_ASSERT(nsContentUtils::IsThirdPartyWindowOrChannel(
|
||||
aWindow, nullptr, aURI) == NS_SUCCEEDED(rv));
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIPrincipal> parentPrincipal;
|
||||
nsCOMPtr<nsIURI> parentPrincipalURI;
|
||||
nsCOMPtr<nsIURI> trackingURI;
|
||||
|
@ -1,61 +1,61 @@
|
||||
# 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/.
|
||||
|
||||
title = About Networking
|
||||
warning = This is very experimental. Do not use without adult supervision.
|
||||
show-next-time-checkbox = Show this warning next time
|
||||
ok = OK
|
||||
http = HTTP
|
||||
sockets = Sockets
|
||||
dns = DNS
|
||||
websockets = WebSockets
|
||||
refresh = Refresh
|
||||
auto-refresh = Autorefresh every 3 seconds
|
||||
hostname = Hostname
|
||||
port = Port
|
||||
http2 = HTTP/2
|
||||
ssl = SSL
|
||||
active = Active
|
||||
idle = Idle
|
||||
host = Host
|
||||
tcp = TCP
|
||||
sent = Sent
|
||||
received = Received
|
||||
family = Family
|
||||
trr = TRR
|
||||
addresses = Addresses
|
||||
expires = Expires (Seconds)
|
||||
messages-sent = Messages Sent
|
||||
messages-received = Messages Received
|
||||
bytes-sent = Bytes Sent
|
||||
bytes-received = Bytes Received
|
||||
logging = Logging
|
||||
log-tutorial =
|
||||
See <a data-l10n-name="logging">HTTP Logging</a>
|
||||
for instructions on how to use this tool.
|
||||
current-log-file = Current Log File:
|
||||
current-log-modules = Current Log Modules:
|
||||
set-log-file = Set Log File
|
||||
set-log-modules = Set Log Modules
|
||||
start-logging = Start Logging
|
||||
stop-logging = Stop Logging
|
||||
dns-lookup = DNS Lookup
|
||||
dns-lookup-button = Resolve
|
||||
dns-domain = Domain:
|
||||
dns-lookup-table-column = IPs
|
||||
rcwn = RCWN Stats
|
||||
rcwn-status = RCWN Status
|
||||
rcwn-cache-won-count = Cache won count
|
||||
rcwn-net-won-count = Net won count
|
||||
total-network-requests = Total network request count
|
||||
rcwn-operation = Cache Operation
|
||||
rcwn-perf-open = Open
|
||||
rcwn-perf-read = Read
|
||||
rcwn-perf-write = Write
|
||||
rcwn-perf-entry-open = Entry Open
|
||||
rcwn-avg-short = Short Average
|
||||
rcwn-avg-long = Long Average
|
||||
rcwn-std-dev-long = Long Standard Deviation
|
||||
rcwn-cache-slow = Cache slow count
|
||||
rcwn-cache-not-slow = Cache not slow count
|
||||
# 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/.
|
||||
|
||||
title = About Networking
|
||||
warning = This is very experimental. Do not use without adult supervision.
|
||||
show-next-time-checkbox = Show this warning next time
|
||||
ok = OK
|
||||
http = HTTP
|
||||
sockets = Sockets
|
||||
dns = DNS
|
||||
websockets = WebSockets
|
||||
refresh = Refresh
|
||||
auto-refresh = Autorefresh every 3 seconds
|
||||
hostname = Hostname
|
||||
port = Port
|
||||
http2 = HTTP/2
|
||||
ssl = SSL
|
||||
active = Active
|
||||
idle = Idle
|
||||
host = Host
|
||||
tcp = TCP
|
||||
sent = Sent
|
||||
received = Received
|
||||
family = Family
|
||||
trr = TRR
|
||||
addresses = Addresses
|
||||
expires = Expires (Seconds)
|
||||
messages-sent = Messages Sent
|
||||
messages-received = Messages Received
|
||||
bytes-sent = Bytes Sent
|
||||
bytes-received = Bytes Received
|
||||
logging = Logging
|
||||
log-tutorial =
|
||||
See <a data-l10n-name="logging">HTTP Logging</a>
|
||||
for instructions on how to use this tool.
|
||||
current-log-file = Current Log File:
|
||||
current-log-modules = Current Log Modules:
|
||||
set-log-file = Set Log File
|
||||
set-log-modules = Set Log Modules
|
||||
start-logging = Start Logging
|
||||
stop-logging = Stop Logging
|
||||
dns-lookup = DNS Lookup
|
||||
dns-lookup-button = Resolve
|
||||
dns-domain = Domain:
|
||||
dns-lookup-table-column = IPs
|
||||
rcwn = RCWN Stats
|
||||
rcwn-status = RCWN Status
|
||||
rcwn-cache-won-count = Cache won count
|
||||
rcwn-net-won-count = Net won count
|
||||
total-network-requests = Total network request count
|
||||
rcwn-operation = Cache Operation
|
||||
rcwn-perf-open = Open
|
||||
rcwn-perf-read = Read
|
||||
rcwn-perf-write = Write
|
||||
rcwn-perf-entry-open = Entry Open
|
||||
rcwn-avg-short = Short Average
|
||||
rcwn-avg-long = Long Average
|
||||
rcwn-std-dev-long = Long Standard Deviation
|
||||
rcwn-cache-slow = Cache slow count
|
||||
rcwn-cache-not-slow = Cache not slow count
|
||||
|
@ -12,9 +12,17 @@ optional = true
|
||||
default-features = false
|
||||
features = ["std"]
|
||||
|
||||
[dependencies.goblin]
|
||||
optional = true
|
||||
# The version and features of goblin need to match what's in object's Cargo.toml,
|
||||
# because we really want object's goblin and not another instance of goblin.
|
||||
version = "0.0.17"
|
||||
features = ["endian_fd", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive"]
|
||||
default-features = false
|
||||
|
||||
[dependencies.thin-vec]
|
||||
version = "0.1.0"
|
||||
features = ["gecko-ffi"]
|
||||
|
||||
[features]
|
||||
parse_elf = ["object"]
|
||||
parse_elf = ["object", "goblin"]
|
||||
|
@ -1,6 +1,11 @@
|
||||
use compact_symbol_table::CompactSymbolTable;
|
||||
use object::{ElfFile, Object, SymbolKind, Uuid};
|
||||
use std::collections::HashMap;
|
||||
use std::cmp;
|
||||
use goblin::elf;
|
||||
|
||||
const UUID_SIZE: usize = 16;
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
|
||||
fn get_symbol_map<'a, 'b, T>(object_file: &'b T) -> HashMap<u32, &'a str>
|
||||
where
|
||||
@ -16,9 +21,73 @@ where
|
||||
|
||||
pub fn get_compact_symbol_table(buffer: &[u8], breakpad_id: &str) -> Option<CompactSymbolTable> {
|
||||
let elf_file = ElfFile::parse(buffer).ok()?;
|
||||
let elf_id = Uuid::from_bytes(elf_file.build_id()?).ok()?;
|
||||
let elf_id = get_elf_id(&elf_file, buffer)?;
|
||||
if format!("{:X}0", elf_id.simple()) != breakpad_id {
|
||||
return None;
|
||||
}
|
||||
return Some(CompactSymbolTable::from_map(get_symbol_map(&elf_file)));
|
||||
}
|
||||
|
||||
fn create_elf_id(identifier: &[u8], little_endian: bool) -> Option<Uuid> {
|
||||
// Make sure that we have exactly UUID_SIZE bytes available
|
||||
let mut data = [0 as u8; UUID_SIZE];
|
||||
let len = cmp::min(identifier.len(), UUID_SIZE);
|
||||
data[0..len].copy_from_slice(&identifier[0..len]);
|
||||
|
||||
if little_endian {
|
||||
// The file ELF file targets a little endian architecture. Convert to
|
||||
// network byte order (big endian) to match the Breakpad processor's
|
||||
// expectations. For big endian object files, this is not needed.
|
||||
data[0..4].reverse(); // uuid field 1
|
||||
data[4..6].reverse(); // uuid field 2
|
||||
data[6..8].reverse(); // uuid field 3
|
||||
}
|
||||
|
||||
Uuid::from_bytes(&data).ok()
|
||||
}
|
||||
|
||||
/// Tries to obtain the object identifier of an ELF object.
|
||||
///
|
||||
/// As opposed to Mach-O, ELF does not specify a unique ID for object files in
|
||||
/// its header. Compilers and linkers usually add either `SHT_NOTE` sections or
|
||||
/// `PT_NOTE` program header elements for this purpose. If one of these notes
|
||||
/// is present, ElfFile's build_id() method will find it.
|
||||
///
|
||||
/// If neither of the above are present, this function will hash the first page
|
||||
/// of the `.text` section (program code). This matches what the Breakpad
|
||||
/// processor does.
|
||||
///
|
||||
/// If all of the above fails, this function will return `None`.
|
||||
pub fn get_elf_id(elf_file: &ElfFile, data: &[u8]) -> Option<Uuid> {
|
||||
if let Some(identifier) = elf_file.build_id() {
|
||||
return create_elf_id(identifier, elf_file.elf().little_endian);
|
||||
}
|
||||
|
||||
// We were not able to locate the build ID, so fall back to hashing the
|
||||
// first page of the ".text" (program code) section. This algorithm XORs
|
||||
// 16-byte chunks directly into a UUID buffer.
|
||||
if let Some(section_data) = find_text_section(elf_file.elf(), data) {
|
||||
let mut hash = [0; UUID_SIZE];
|
||||
for i in 0..cmp::min(section_data.len(), PAGE_SIZE) {
|
||||
hash[i % UUID_SIZE] ^= section_data[i];
|
||||
}
|
||||
|
||||
return create_elf_id(&hash, elf_file.elf().little_endian);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns a reference to the data of the the .text section in an ELF binary.
|
||||
fn find_text_section<'elf, 'data>(
|
||||
elf: &'elf elf::Elf,
|
||||
data: &'data [u8],
|
||||
) -> Option<&'data[u8]> {
|
||||
elf.section_headers.iter().find_map(|header| {
|
||||
match (header.sh_type, elf.shdr_strtab.get(header.sh_name)) {
|
||||
(elf::section_header::SHT_PROGBITS, Some(Ok(".text"))) =>
|
||||
Some(&data[header.sh_offset as usize..][..header.sh_size as usize]),
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ extern crate thin_vec;
|
||||
|
||||
#[cfg(feature = "parse_elf")]
|
||||
extern crate object;
|
||||
#[cfg(feature = "parse_elf")]
|
||||
extern crate goblin;
|
||||
|
||||
mod compact_symbol_table;
|
||||
|
||||
|
@ -128,15 +128,20 @@ nsMacUtilsImpl::GetIsTranslated(bool* aIsTranslated) {
|
||||
}
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
// Get the path to the .app directory for the parent process. When executing
|
||||
// in the child process, this is the outer .app (such as Firefox.app) and not
|
||||
// the inner .app containing the child process executable.
|
||||
// Get the path to the .app directory (aka bundle) for the parent process.
|
||||
// When executing in the child process, this is the outer .app (such as
|
||||
// Firefox.app) and not the inner .app containing the child process
|
||||
// executable. We don't rely on the actual .app extension to allow for the
|
||||
// bundle being renamed.
|
||||
bool nsMacUtilsImpl::GetAppPath(nsCString& aAppPath) {
|
||||
nsAutoCString appPath;
|
||||
nsAutoCString appBinaryPath(
|
||||
(CommandLine::ForCurrentProcess()->argv()[0]).c_str());
|
||||
|
||||
auto pattern = NS_LITERAL_CSTRING(".app/Contents/MacOS/");
|
||||
// The binary path resides within the .app dir in Contents/MacOS,
|
||||
// e.g., Firefox.app/Contents/MacOS/firefox. Search backwards in
|
||||
// the binary path for the end of .app path.
|
||||
auto pattern = NS_LITERAL_CSTRING("/Contents/MacOS/");
|
||||
nsAutoCString::const_iterator start, end;
|
||||
appBinaryPath.BeginReading(start);
|
||||
appBinaryPath.EndReading(end);
|
||||
@ -144,8 +149,9 @@ bool nsMacUtilsImpl::GetAppPath(nsCString& aAppPath) {
|
||||
end = start;
|
||||
appBinaryPath.BeginReading(start);
|
||||
|
||||
// If we're executing in a child process, get the
|
||||
// parent .app by searching right-to-left once more.
|
||||
// If we're executing in a child process, get the parent .app path
|
||||
// by searching backwards once more. The child executable resides
|
||||
// in Firefox.app/Contents/MacOS/plugin-container/Contents/MacOS.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
if (RFindInReadable(pattern, start, end)) {
|
||||
end = start;
|
||||
@ -155,10 +161,6 @@ bool nsMacUtilsImpl::GetAppPath(nsCString& aAppPath) {
|
||||
}
|
||||
}
|
||||
|
||||
++end;
|
||||
++end;
|
||||
++end;
|
||||
++end;
|
||||
appPath.Assign(Substring(start, end));
|
||||
} else {
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user