From 3d157eda4e795be92e67477865c44be631377dd3 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Mon, 10 Sep 2018 14:55:27 -0400 Subject: [PATCH 01/44] Bug 1317102 - Part 1: Add a pref to enable displaying multiple grid containers in the CSS Grid Inspector. r=jdescottes --HG-- rename : devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js => devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_01.js rename : devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js => devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js --- .../inspector/grids/components/GridItem.js | 8 +- .../client/inspector/grids/grid-inspector.js | 84 ++++--- .../client/inspector/grids/reducers/grids.js | 41 +++- .../client/inspector/grids/test/browser.ini | 4 +- .../test/browser_grids_grid-list-no-grids.js | 3 +- ...wser_grids_grid-list-on-iframe-reloaded.js | 7 +- ...ids_grid-list-on-mutation-element-added.js | 15 +- ...s_grid-list-on-mutation-element-removed.js | 10 +- ...rowser_grids_grid-list-toggle-grids_01.js} | 10 +- ...browser_grids_grid-list-toggle-grids_02.js | 80 ++++++ ...r_grids_grid-list-toggle-multiple-grids.js | 116 +++++++-- .../browser_grids_restored-after-reload.js | 14 +- ...ds_restored-multiple-grids-after-reload.js | 106 ++++++++ devtools/client/inspector/grids/test/head.js | 1 - devtools/client/inspector/grids/types.js | 4 + .../test/browser_markup_grid_display_badge.js | 10 +- .../markup/views/element-container.js | 2 +- .../inspector/markup/views/element-editor.js | 2 +- ...rowser_rules_edit-display-grid-property.js | 2 +- ...wser_rules_grid-highlighter-on-mutation.js | 4 +- ...wser_rules_grid-highlighter-on-navigate.js | 4 +- ...rowser_rules_grid-highlighter-on-reload.js | 2 +- ..._grid-highlighter-restored-after-reload.js | 6 +- .../test/browser_rules_grid-toggle_01.js | 12 +- .../test/browser_rules_grid-toggle_01b.js | 12 +- .../test/browser_rules_grid-toggle_02.js | 12 +- .../test/browser_rules_grid-toggle_03.js | 16 +- .../test/browser_rules_grid-toggle_04.js | 12 +- .../rules/views/text-property-editor.js | 9 +- .../inspector/shared/highlighters-overlay.js | 229 +++++++++++++----- .../client/preferences/devtools-client.js | 2 + 31 files changed, 597 insertions(+), 242 deletions(-) rename devtools/client/inspector/grids/test/{browser_grids_grid-list-toggle-single-grid.js => browser_grids_grid-list-toggle-grids_01.js} (80%) create mode 100644 devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js create mode 100644 devtools/client/inspector/grids/test/browser_grids_restored-multiple-grids-after-reload.js diff --git a/devtools/client/inspector/grids/components/GridItem.js b/devtools/client/inspector/grids/components/GridItem.js index 686f0770060c..1cc363c40c76 100644 --- a/devtools/client/inspector/grids/components/GridItem.js +++ b/devtools/client/inspector/grids/components/GridItem.js @@ -96,7 +96,6 @@ class GridItem extends PureComponent { onHideBoxModelHighlighter, onShowBoxModelHighlighterForNode, } = this.props; - const { nodeFront } = grid; return ( dom.li({}, @@ -104,6 +103,7 @@ class GridItem extends PureComponent { dom.input( { checked: grid.highlighted, + disabled: grid.disabled, type: "checkbox", value: grid.id, onChange: this.onGridCheckboxClick, @@ -112,10 +112,10 @@ class GridItem extends PureComponent { Rep({ defaultRep: ElementNode, mode: MODE.TINY, - object: translateNodeFrontToGrip(nodeFront), + object: translateNodeFrontToGrip(grid.nodeFront), onDOMNodeMouseOut: () => onHideBoxModelHighlighter(), - onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront), - onInspectIconClick: () => this.onGridInspectIconClick(nodeFront), + onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(grid.nodeFront), + onInspectIconClick: () => this.onGridInspectIconClick(grid.nodeFront), }) ), dom.div( diff --git a/devtools/client/inspector/grids/grid-inspector.js b/devtools/client/inspector/grids/grid-inspector.js index a76ec8ca3091..4d501b8ee8ad 100644 --- a/devtools/client/inspector/grids/grid-inspector.js +++ b/devtools/client/inspector/grids/grid-inspector.js @@ -56,6 +56,9 @@ class GridInspector { this.store = inspector.store; this.telemetry = inspector.telemetry; this.walker = this.inspector.walker; + // Maximum number of grid highlighters that can be displayed. + this.maxHighlighters = + Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); this.onHighlighterShown = this.onHighlighterShown.bind(this); this.onHighlighterHidden = this.onHighlighterHidden.bind(this); @@ -163,15 +166,15 @@ class GridInspector { * The color to use. */ getInitialGridColor(nodeFront, customColor, fallbackColor) { - const highlighted = nodeFront == this.highlighters.gridHighlighterShown; + const highlighted = this.highlighters.gridHighlighters.has(nodeFront); let color; if (customColor) { color = customColor; - } else if (highlighted && this.highlighters.state.grid.options) { + } else if (highlighted && this.highlighters.state.grids.has(nodeFront.actorID)) { // If the node front is currently highlighted, use the color from the highlighter // options. - color = this.highlighters.state.grid.options.color; + color = this.highlighters.state.grids.get(nodeFront.actorID).options.color; } else { // Otherwise use the color defined in the store for this node front. color = this.getGridColorForNodeFront(nodeFront); @@ -200,29 +203,40 @@ class GridInspector { } /** - * Given a list of new grid fronts, and if we have a currently highlighted grid, check - * if its fragments have changed. + * Given a list of new grid fronts, and if there are highlighted grids, check + * if their fragments have changed. * * @param {Array} newGridFronts * A list of GridFront objects. * @return {Boolean} */ haveCurrentFragmentsChanged(newGridFronts) { - const currentNode = this.highlighters.gridHighlighterShown; - if (!currentNode) { + const gridHighlighters = this.highlighters.gridHighlighters; + + if (!gridHighlighters.size) { return false; } - const newGridFront = newGridFronts.find(g => g.containerNodeFront === currentNode); - if (!newGridFront) { + const gridFronts = newGridFronts.filter(g => + gridHighlighters.has(g.containerNodeFront)); + if (!gridFronts.length) { return false; } const { grids } = this.store.getState(); - const oldFragments = grids.find(g => g.nodeFront === currentNode).gridFragments; - const newFragments = newGridFront.gridFragments; - return !compareFragmentsGeometry(oldFragments, newFragments); + for (const node of gridHighlighters.keys()) { + const oldFragments = grids + .find(g => g.nodeFront === node).gridFragments; + const newFragments = newGridFronts + .find(g => g.containerNodeFront === node).gridFragments; + + if (!compareFragmentsGeometry(oldFragments, newFragments)) { + return true; + } + } + + return false; } /** @@ -300,12 +314,16 @@ class GridInspector { const colorForHost = customColors[hostname] ? customColors[hostname][i] : null; const fallbackColor = GRID_COLORS[i % GRID_COLORS.length]; const color = this.getInitialGridColor(nodeFront, colorForHost, fallbackColor); - const highlighted = nodeFront == this.highlighters.gridHighlighterShown; + const highlighted = this.highlighters.gridHighlighters.has(nodeFront); + const disabled = !highlighted && + this.maxHighlighters > 1 && + this.highlighters.gridHighlighters.size === this.maxHighlighters; grids.push({ id: i, actorID: grid.actorID, color, + disabled, direction: grid.direction, gridFragments: grid.gridFragments, highlighted, @@ -325,11 +343,9 @@ class GridInspector { * @param {NodeFront} nodeFront * The NodeFront of the grid container element for which the grid * highlighter is shown for. - * @param {Object} options - * The highlighter options used for the highlighter being shown/hidden. */ - onHighlighterShown(nodeFront, options) { - this.onHighlighterChange(nodeFront, true, options); + onHighlighterShown(nodeFront) { + this.onHighlighterChange(nodeFront, true); } /** @@ -340,11 +356,9 @@ class GridInspector { * @param {NodeFront} nodeFront * The NodeFront of the grid container element for which the grid highlighter * is hidden for. - * @param {Object} options - * The highlighter options used for the highlighter being shown/hidden. */ - onHighlighterHidden(nodeFront, options) { - this.onHighlighterChange(nodeFront, false, options); + onHighlighterHidden(nodeFront) { + this.onHighlighterChange(nodeFront, false); } /** @@ -356,10 +370,8 @@ class GridInspector { * is shown for. * @param {Boolean} highlighted * If the grid should be updated to highlight or hide. - * @param {Object} options - * The highlighter options used for the highlighter being shown/hidden. */ - onHighlighterChange(nodeFront, highlighted, options = {}) { + onHighlighterChange(nodeFront, highlighted) { if (!this.isPanelVisible()) { return; } @@ -415,10 +427,6 @@ class GridInspector { return; } - // Get the node front(s) from the current grid(s) so we can compare them to them to - // node(s) of the new grids. - const oldNodeFronts = grids.map(grid => grid.nodeFront.actorID); - // In some cases, the nodes for current grids may have been removed from the DOM in // which case we need to update. if (grids.length && grids.some(grid => !grid.nodeFront.actorID)) { @@ -426,18 +434,18 @@ class GridInspector { return; } - // Otherwise, continue comparing with the new grids. - const newNodeFronts = newGridFronts.filter(grid => grid.containerNodeFront) + // Get the node front(s) from the current grid(s) so we can compare them to them to + // the node(s) of the new grids. + const oldNodeFronts = grids.map(grid => grid.nodeFront.actorID); + const newNodeFronts = newGridFronts.filter(grid => grid.containerNode) .map(grid => grid.containerNodeFront.actorID); + if (grids.length === newGridFronts.length && - oldNodeFronts.sort().join(",") == newNodeFronts.sort().join(",")) { - // Same list of containers, but let's check if the geometry of the current grid has - // changed, if it hasn't we can safely abort. - if (!this.highlighters.gridHighlighterShown || - (this.highlighters.gridHighlighterShown && - !this.haveCurrentFragmentsChanged(newGridFronts))) { - return; - } + oldNodeFronts.sort().join(",") == newNodeFronts.sort().join(",") && + !this.haveCurrentFragmentsChanged(newGridFronts)) { + // Same list of containers and the geometry of all the displayed grids remained the + // same, we can safely abort. + return; } // Either the list of containers or the current fragments have changed, do update. diff --git a/devtools/client/inspector/grids/reducers/grids.js b/devtools/client/inspector/grids/reducers/grids.js index 6c62554f5601..d53b5afd17e1 100644 --- a/devtools/client/inspector/grids/reducers/grids.js +++ b/devtools/client/inspector/grids/reducers/grids.js @@ -4,6 +4,8 @@ "use strict"; +const Services = require("Services"); + const { UPDATE_GRID_COLOR, UPDATE_GRID_HIGHLIGHTED, @@ -16,7 +18,7 @@ const reducers = { [UPDATE_GRID_COLOR](grids, { nodeFront, color }) { const newGrids = grids.map(g => { - if (g.nodeFront == nodeFront) { + if (g.nodeFront === nodeFront) { g = Object.assign({}, g, { color }); } @@ -27,11 +29,44 @@ const reducers = { }, [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) { + const maxHighlighters = + Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); + const highlightedNodeFronts = grids.filter(g => g.highlighted) + .map(g => g.nodeFront); + let numHighlighted = highlightedNodeFronts.length; + + // Get the total number of highlighted grids including the one that will be + // highlighted/unhighlighted. + if (!highlightedNodeFronts.includes(nodeFront) && highlighted) { + numHighlighted += 1; + } else if (highlightedNodeFronts.includes(nodeFront) && !highlighted) { + numHighlighted -= 1; + } + return grids.map(g => { - const isUpdatedNode = g.nodeFront === nodeFront; + if (maxHighlighters === 1) { + // When there is only one grid highlighter available, only the given grid + // container nodeFront can be highlighted, and all the other grid containers + // are unhighlighted. + return Object.assign({}, g, { + highlighted: g.nodeFront === nodeFront && highlighted, + }); + } else if (numHighlighted === maxHighlighters && g.nodeFront !== nodeFront) { + // The maximum number of highlighted grids have been reached. Disable all the + // other non-highlighted grids. + return Object.assign({}, g, { + disabled: !g.highlighted, + }); + } else if (g.nodeFront === nodeFront) { + // This is the provided grid nodeFront to highlight/unhighlight. + return Object.assign({}, g, { + disabled: false, + highlighted, + }); + } return Object.assign({}, g, { - highlighted: isUpdatedNode && highlighted + disabled: false, }); }); }, diff --git a/devtools/client/inspector/grids/test/browser.ini b/devtools/client/inspector/grids/test/browser.ini index 73d717bedf92..475ea377a5ff 100644 --- a/devtools/client/inspector/grids/test/browser.ini +++ b/devtools/client/inspector/grids/test/browser.ini @@ -25,8 +25,9 @@ support-files = skip-if = (verify && (os == 'win' || os == 'linux')) [browser_grids_grid-list-on-mutation-element-added.js] [browser_grids_grid-list-on-mutation-element-removed.js] +[browser_grids_grid-list-toggle-grids_01.js] +[browser_grids_grid-list-toggle-grids_02.js] [browser_grids_grid-list-toggle-multiple-grids.js] -[browser_grids_grid-list-toggle-single-grid.js] [browser_grids_grid-outline-cannot-show-outline.js] [browser_grids_grid-outline-highlight-area.js] skip-if = (verify && (os == 'win')) @@ -40,3 +41,4 @@ skip-if = (verify && (os == 'win')) [browser_grids_number-of-css-grids-telemetry.js] [browser_grids_persist-color-palette.js] [browser_grids_restored-after-reload.js] +[browser_grids_restored-multiple-grids-after-reload.js] diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js index 0a4d107d3137..4fd0d999049c 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js @@ -28,7 +28,6 @@ add_task(async function() { info("Checking the initial state of the Grid Inspector."); ok(noGridList, "The message no grid containers is displayed."); ok(!gridList, "No grid containers are listed."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter exists in the highlighters overlay."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-iframe-reloaded.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-iframe-reloaded.js index 249c8106dd22..5eeb0e75ddc5 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-iframe-reloaded.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-iframe-reloaded.js @@ -23,7 +23,8 @@ add_task(async function() { is(gridList.childNodes.length, 1, "There's one grid in the list"); const checkbox = gridList.querySelector("input"); ok(checkbox.checked, "The checkbox is checked"); - ok(highlighters.gridHighlighterShown, "There's a highlighter shown"); + is(highlighters.gridHighlighters.size, 1, "There's a highlighter shown"); + is(highlighters.state.grids.size, 1, "There's a saved grid state to be restored."); info("Reload the iframe in content and expect the grid list to update"); const oldGrid = store.getState().grids[0]; @@ -37,12 +38,14 @@ add_task(async function() { await onHighlighterHidden; is(gridList.childNodes.length, 1, "There's still one grid in the list"); + ok(!highlighters.state.grids.size, "No grids to be restored on page reload."); info("Highlight the first grid again to make sure this still works"); await enableTheFirstGrid(doc, inspector); is(gridList.childNodes.length, 1, "There's again one grid in the list"); - ok(highlighters.gridHighlighterShown, "There's a highlighter shown"); + is(highlighters.gridHighlighters.size, 1, "There's a highlighter shown"); + is(highlighters.state.grids.size, 1, "There's a saved grid state to be restored."); }); async function enableTheFirstGrid(doc, { highlighters, store }) { diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js index ab18d0b45eb0..602d1635e210 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js @@ -33,9 +33,8 @@ add_task(async function() { info("Checking the initial state of the Grid Inspector."); is(gridList.childNodes.length, 1, "One grid container is listed."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter exists in the highlighters overlay."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the layout panel."); let onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -43,9 +42,7 @@ add_task(async function() { await onHighlighterShown; info("Checking the CSS grid highlighter is created."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter is created in the highlighters overlay."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Adding the #grid2 container in the content page."); const onGridListUpdate = waitUntilState(store, state => @@ -59,9 +56,7 @@ add_task(async function() { info("Checking the new Grid Inspector state."); is(gridList.childNodes.length, 2, "Two grid containers are listed."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter is created in the highlighters overlay."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); const checkbox2 = gridList.children[1].querySelector("input"); @@ -76,7 +71,7 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is still shown."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the layout panel."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -89,5 +84,5 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is not shown."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js index b36810e7125c..b9b9512490ac 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js @@ -30,9 +30,7 @@ add_task(async function() { info("Checking the initial state of the Grid Inspector."); is(gridList.childNodes.length, 1, "One grid container is listed."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the highlighters overlay."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the layout panel."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -43,9 +41,7 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is created."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter is created in the highlighters overlay."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Removing the #grid container in the content page."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -57,7 +53,7 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is not shown."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); const noGridList = doc.querySelector(".grid-pane .devtools-sidepanel-no-result"); ok(noGridList, "The message no grid containers is displayed."); }); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_01.js similarity index 80% rename from devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js rename to devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_01.js index 5edbc68d331a..5fbcec872a22 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_01.js @@ -30,9 +30,7 @@ add_task(async function() { info("Checking the initial state of the Grid Inspector."); is(gridList.childNodes.length, 1, "One grid container is listed."); ok(!checkbox.checked, `Grid item ${checkbox.value} is unchecked in the grid list.`); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the highlighters overlay."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the layout panel."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -44,9 +42,7 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is created."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter is created in the highlighters overlay."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the layout panel."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -58,5 +54,5 @@ add_task(async function() { await onCheckboxChange; info("Checking the CSS grid highlighter is not shown."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js new file mode 100644 index 000000000000..1a463eea98b9 --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test toggling the grid highlighter in the grid inspector panel with multiple grids in +// the page. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+`; + +add_task(async function() { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, gridInspector } = await openLayoutView(); + const { document: doc } = gridInspector; + const { highlighters, store } = inspector; + + await selectNode("#grid1", inspector); + const gridList = doc.getElementById("grid-list"); + const checkbox1 = gridList.children[0].querySelector("input"); + const checkbox2 = gridList.children[1].querySelector("input"); + + info("Checking the initial state of the Grid Inspector."); + is(gridList.childNodes.length, 2, "2 grid containers are listed."); + ok(!checkbox1.checked, `Grid item ${checkbox1.value} is unchecked in the grid list.`); + ok(!checkbox2.checked, `Grid item ${checkbox2.value} is unchecked in the grid list.`); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid1."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + state.grids[0].highlighted && + !state.grids[1].highlighted); + checkbox1.click(); + await onHighlighterShown; + await onCheckboxChange; + + info("Checking the CSS grid highlighter is created."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid2."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + state.grids[1].highlighted); + checkbox2.click(); + await onHighlighterShown; + await onCheckboxChange; + + info("Checking the CSS grid highlighter is still shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + + info("Toggling OFF the CSS grid highlighter from the layout panel."); + const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + !state.grids[1].highlighted); + checkbox2.click(); + await onHighlighterHidden; + await onCheckboxChange; + + info("Checking the CSS grid highlighter is not shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js index f67fff38e767..0284c4f742ad 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js @@ -3,8 +3,7 @@ "use strict"; -// Test toggling the grid highlighter in the grid inspector panel with multiple grids in -// the page. +// Test toggling the multiple grid highlighters in the grid inspector panel. const TEST_URI = ` +
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+`; + +add_task(async function() { + await pushPref("devtools.gridinspector.maxHighlighters", 3); + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { gridInspector, inspector } = await openLayoutView(); + const { document: doc } = gridInspector; + const { highlighters, store } = inspector; + + await selectNode("#grid1", inspector); + const gridList = doc.getElementById("grid-list"); + const checkbox1 = gridList.children[0].querySelector("input"); + const checkbox2 = gridList.children[1].querySelector("input"); + const checkbox3 = gridList.children[2].querySelector("input"); + + info("Toggling ON the CSS grid highlighter for #grid1."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 4 && + state.grids[0].highlighted && !state.grids[0].disabled && + !state.grids[1].highlighted && !state.grids[1].disabled && + !state.grids[2].highlighted && !state.grids[2].disabled && + !state.grids[3].highlighted && !state.grids[3].disabled); + checkbox1.click(); + await onHighlighterShown; + await onCheckboxChange; + + info("Toggling ON the CSS grid highlighter for #grid2."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 4 && + state.grids[0].highlighted && !state.grids[0].disabled && + state.grids[1].highlighted && !state.grids[1].disabled && + !state.grids[2].highlighted && !state.grids[2].disabled && + !state.grids[3].highlighted && !state.grids[3].disabled); + checkbox2.click(); + await onHighlighterShown; + await onCheckboxChange; + + info("Toggling ON the CSS grid highlighter for #grid3."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 4 && + state.grids[0].highlighted && !state.grids[0].disabled && + state.grids[1].highlighted && !state.grids[1].disabled && + state.grids[2].highlighted && !state.grids[2].disabled && + !state.grids[3].highlighted && state.grids[3].disabled); + checkbox3.click(); + await onHighlighterShown; + await onCheckboxChange; + + info("Check that the CSS grid highlighters are created and the saved grid state."); + is(highlighters.gridHighlighters.size, 3, + "Got expected number of grid highlighters shown."); + is(highlighters.state.grids.size, 3, "Got expected number of grids in the saved state"); + + info("Reload the page, expect the highlighters to be displayed once again and " + + "grids are checked"); + const onStateRestored = waitForNEvents(highlighters, "grid-state-restored", 3); + const onGridListRestored = waitUntilState(store, state => + state.grids.length == 4 && + state.grids[0].highlighted && !state.grids[0].disabled && + state.grids[1].highlighted && !state.grids[1].disabled && + state.grids[2].highlighted && !state.grids[2].disabled && + !state.grids[3].highlighted && state.grids[3].disabled); + await refreshTab(); + const { restored } = await onStateRestored; + await onGridListRestored; + + info("Check that the grid highlighters can be displayed after reloading the page"); + ok(restored, "The highlighter state was restored"); + is(highlighters.gridHighlighters.size, 3, + "Got expected number of grid highlighters shown."); + is(highlighters.state.grids.size, 3, "Got expected number of grids in the saved state"); +}); diff --git a/devtools/client/inspector/grids/test/head.js b/devtools/client/inspector/grids/test/head.js index 29cb4fe9c06e..6c2412cbee8c 100644 --- a/devtools/client/inspector/grids/test/head.js +++ b/devtools/client/inspector/grids/test/head.js @@ -23,7 +23,6 @@ registerCleanupFunction(() => { }); const asyncStorage = require("devtools/shared/async-storage"); -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; /** * Simulate a color change in a given color picker tooltip. diff --git a/devtools/client/inspector/grids/types.js b/devtools/client/inspector/grids/types.js index d55fedc86029..124ed7059316 100644 --- a/devtools/client/inspector/grids/types.js +++ b/devtools/client/inspector/grids/types.js @@ -19,6 +19,10 @@ exports.grid = { // The text direction of the grid container direction: PropTypes.string, + // Whether or not the grid checkbox is disabled as a result of hitting the + // maximum number of grid highlighters shown. + disabled: PropTypes.bool, + // The grid fragment object of the grid container gridFragments: PropTypes.array, diff --git a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js index 353131e3b379..afdde035d7f8 100644 --- a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js +++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js @@ -14,8 +14,6 @@ const TEST_URI = `
`; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const { inspector } = await openLayoutView(); @@ -28,9 +26,8 @@ add_task(async function() { ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active."); info("Check the initial state of the grid highlighter."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter exists in the highlighters overlay."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the grid display badge."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -41,10 +38,9 @@ add_task(async function() { await onHighlighterShown; await onCheckboxChange; - info("Check the CSS grid highlighter is created and grid display badge state."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + info("Check that the CSS grid highlighter is created and the display badge state."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is created in the highlighters overlay."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); ok(gridDisplayBadge.classList.contains("active"), "grid display badge is active."); info("Toggling OFF the CSS grid highlighter from the grid display badge."); diff --git a/devtools/client/inspector/markup/views/element-container.js b/devtools/client/inspector/markup/views/element-container.js index 66db51582db9..1b6c5544b8d1 100644 --- a/devtools/client/inspector/markup/views/element-container.js +++ b/devtools/client/inspector/markup/views/element-container.js @@ -96,7 +96,7 @@ MarkupElementContainer.prototype = extend(MarkupContainer.prototype, { return; } this.editor._displayBadge.classList.toggle("active", - this.markup.highlighters.gridHighlighterShown === this.node); + this.markup.highlighters.gridHighlighters.has(this.node)); }, async _buildEventTooltipContent(target) { diff --git a/devtools/client/inspector/markup/views/element-editor.js b/devtools/client/inspector/markup/views/element-editor.js index fd87c683b218..e62c64a43241 100644 --- a/devtools/client/inspector/markup/views/element-editor.js +++ b/devtools/client/inspector/markup/views/element-editor.js @@ -326,7 +326,7 @@ ElementEditor.prototype = { this._displayBadge.title = DISPLAY_TYPES[this.node.displayType]; this._displayBadge.classList.toggle("active", this.highlighters.flexboxHighlighterShown === this.node || - this.highlighters.gridHighlighterShown === this.node); + this.highlighters.gridHighlighters.has(this.node)); this._displayBadge.classList.toggle("interactive", Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled") && (this.node.displayType === "flex" || this.node.displayType === "inline-flex")); diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js b/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js index d665bd04a81c..c5745b69d2ab 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js @@ -45,5 +45,5 @@ add_task(async function() { info("Check the grid highlighter and grid toggle button are hidden."); gridToggle = container.querySelector(".ruleview-grid"); ok(!gridToggle, "Grid highlighter toggle is not visible."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js index a4028f6b1cbf..c1db983125eb 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js @@ -32,7 +32,7 @@ add_task(async function() { const onHighlighterShown = highlighters.once("grid-highlighter-shown"); gridToggle.click(); await onHighlighterShown; - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); info("Remove the #grid container in the content page"); @@ -40,5 +40,5 @@ add_task(async function() { document.querySelector("#grid").remove(); `); await onHighlighterHidden; - ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js index b83b20155d4c..688450d4f250 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js @@ -34,8 +34,8 @@ add_task(async function() { gridToggle.click(); await onHighlighterShown; - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); await navigateTo(inspector, TEST_URI_2); - ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js index 565280a96be7..2263da654b72 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js @@ -49,5 +49,5 @@ async function checkGridHighlighter() { gridToggle.click(); await onHighlighterShown; - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); } diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js index 1147fb25f61d..ae3235f47055 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js @@ -47,7 +47,7 @@ add_task(async function() { gridToggle.click(); await onHighlighterShown; - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Reload the page, expect the highlighter to be displayed once again"); let onStateRestored = highlighters.once("grid-state-restored"); @@ -56,7 +56,7 @@ add_task(async function() { ok(restored, "The highlighter state was restored"); info("Check that the grid highlighter can be displayed after reloading the page"); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Navigate to another URL, and check that the highlighter is hidden"); const otherUri = "data:text/html;charset=utf-8," + encodeURIComponent(OTHER_URI); @@ -64,5 +64,5 @@ add_task(async function() { await navigateTo(inspector, otherUri); ({ restored } = await onStateRestored); ok(!restored, "The highlighter state was not restored"); - ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js index 08496bf142c4..34f7fb55a6f0 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js @@ -19,8 +19,6 @@ const TEST_URI = ` `; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -34,9 +32,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the rule-view."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -47,9 +43,7 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter created in the rule-view."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -60,5 +54,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js index 65803569caef..f0f7324f27d5 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js @@ -19,8 +19,6 @@ const TEST_URI = ` `; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -34,9 +32,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the rule-view."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -47,9 +43,7 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter created in the rule-view."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -60,5 +54,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js index 98f94008d1a0..58c6e6b37782 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js @@ -22,8 +22,6 @@ const TEST_URI = ` `; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -40,9 +38,7 @@ add_task(async function() { ok(!gridToggle.classList.contains("active") && !overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle buttons are not active."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the rule-view."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the overridden rule in the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -54,9 +50,7 @@ add_task(async function() { ok(gridToggle.classList.contains("active") && overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter created in the rule-view."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling off the CSS grid highlighter from the normal grid declaration in the " + "rule-view."); @@ -69,5 +63,5 @@ add_task(async function() { ok(!gridToggle.classList.contains("active") && !overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle buttons are not active."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js index 1ee3cf3c4d8f..d143afcb2143 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js @@ -22,8 +22,6 @@ const TEST_URI = ` `; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -39,9 +37,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the rule-view."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter for the first grid container from the " + "rule-view."); @@ -53,13 +49,11 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter created in the rule-view."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Selecting the second grid container."); await selectNode("#grid2", inspector); - const firstGridHighterShown = highlighters.gridHighlighterShown; + const firstGridHighterShown = highlighters.gridHighlighters.keys().next().value; container = getRuleViewProperty(view, ".grid", "display").valueSpan; gridToggle = container.querySelector(".ruleview-grid"); @@ -68,7 +62,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is still shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is still shown."); info("Toggling ON the CSS grid highlighter for the second grid container from the " + "rule-view."); @@ -80,7 +74,7 @@ add_task(async function() { "toggle button is active in the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.gridHighlighterShown != firstGridHighterShown, + ok(highlighters.gridHighlighters.keys().next().value != firstGridHighterShown, "Grid highlighter for the second grid container is shown."); info("Selecting the first grid container."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js index 037af70ecd05..8f45bfbd0ccd 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js @@ -19,8 +19,6 @@ const TEST_URI = ` `; -const HIGHLIGHTER_TYPE = "CssGridHighlighter"; - add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -34,9 +32,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], - "No CSS grid highlighter exists in the rule-view."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -47,9 +43,7 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.highlighters[HIGHLIGHTER_TYPE], - "CSS grid highlighter created in the rule-view."); - ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -60,5 +54,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js index 761b6805cb3b..fc177b452e87 100644 --- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -538,8 +538,8 @@ TextPropertyEditor.prototype = { const gridToggle = this.valueSpan.querySelector(".ruleview-grid"); if (gridToggle) { gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip")); - if (this.ruleView.highlighters.gridHighlighterShown === - this.ruleView.inspector.selection.nodeFront) { + if (this.ruleView.highlighters.gridHighlighters.has( + this.ruleView.inspector.selection.nodeFront)) { gridToggle.classList.add("active"); } } @@ -926,14 +926,15 @@ TextPropertyEditor.prototype = { "session_id": this.toolbox.sessionId }); - // Since the value was changed, check if the original propertywas a flex or grid + // Since the value was changed, check if the original property was a flex or grid // display declaration and hide their respective highlighters. if (this.isDisplayFlex()) { this.ruleView.highlighters.hideFlexboxHighlighter(); } if (this.isDisplayGrid()) { - this.ruleView.highlighters.hideGridHighlighter(); + this.ruleView.highlighters.hideGridHighlighter( + this.ruleView.inspector.selection.nodeFront); } // First, set this property value (common case, only modified a property) diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js index 947588d8c97d..7c5dfc453550 100644 --- a/devtools/client/inspector/shared/highlighters-overlay.js +++ b/devtools/client/inspector/shared/highlighters-overlay.js @@ -6,6 +6,7 @@ "use strict"; +const Services = require("Services"); const EventEmitter = require("devtools/shared/event-emitter"); const { VIEW_NODE_VALUE_TYPE, @@ -23,42 +24,45 @@ class HighlightersOverlay { * Inspector toolbox panel. */ constructor(inspector) { - /* - * Collection of instantiated highlighter actors like FlexboxHighlighter, - * CssGridHighlighter, ShapesHighlighter and GeometryEditorHighlighter. - */ - this.highlighters = {}; - /* - * Collection of instantiated in-context editors, like ShapesInContextEditor, which - * behave like highlighters but with added editing capabilities that need to map value - * changes to properties in the Rule view. - */ - this.editors = {}; this.inspector = inspector; this.highlighterUtils = this.inspector.toolbox.highlighterUtils; this.store = this.inspector.store; this.telemetry = inspector.telemetry; + // Collection of instantiated highlighter actors like FlexboxHighlighter, + // ShapesHighlighter and GeometryEditorHighlighter. + this.highlighters = {}; + // Map of grid container NodeFront to their instantiated grid highlighter actors. + this.gridHighlighters = new Map(); + // Array of reusable grid highlighters that have been instantiated and are not + // associated with any NodeFront. + this.extraGridHighlighterPool = []; + + // Collection of instantiated in-context editors, like ShapesInContextEditor, which + // behave like highlighters but with added editing capabilities that need to map value + // changes to properties in the Rule view. + this.editors = {}; + + // Saved state to be restore on page navigation. + this.state = { + flexbox: {}, + // Map of grid container NodeFront to the their stored grid options + grids: new Map(), + shapes: {}, + }; + // NodeFront of the flexbox container that is highlighted. this.flexboxHighlighterShown = null; // NodeFront of the flex item that is highlighted. this.flexItemHighlighterShown = null; // NodeFront of element that is highlighted by the geometry editor. this.geometryEditorHighlighterShown = null; - // NodeFront of the grid container that is highlighted. - this.gridHighlighterShown = null; // Name of the highlighter shown on mouse hover. this.hoveredHighlighterShown = null; // Name of the selector highlighter shown. this.selectorHighlighterShown = null; // NodeFront of the shape that is highlighted this.shapesHighlighterShown = null; - // Saved state to be restore on page navigation. - this.state = { - flexbox: {}, - grid: {}, - shapes: {}, - }; this.onClick = this.onClick.bind(this); this.onMarkupMutation = this.onMarkupMutation.bind(this); @@ -126,7 +130,7 @@ class HighlightersOverlay { /** * Toggle the shapes highlighter for the given node. - + * * @param {NodeFront} node * The NodeFront of the element with a shape to highlight. * @param {Object} options @@ -389,7 +393,7 @@ class HighlightersOverlay { * "rule" represents the rule view. */ async toggleGridHighlighter(node, trigger) { - if (node == this.gridHighlighterShown) { + if (this.gridHighlighters.has(node)) { await this.hideGridHighlighter(node); return; } @@ -410,7 +414,26 @@ class HighlightersOverlay { * "rule" represents the rule view. */ async showGridHighlighter(node, options, trigger) { - const highlighter = await this._getHighlighter("CssGridHighlighter"); + const maxHighlighters = + Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); + + // When the grid highlighter has the given node, it is probably called with new + // highlighting options, so skip any extra grid highlighter handling. + if (!this.gridHighlighters.has(node)) { + if (maxHighlighters === 1) { + // Only one grid highlighter can be shown at a time. Hides any instantiated + // grid highlighters. + for (const nodeFront of this.gridHighlighters.keys()) { + await this.hideGridHighlighter(nodeFront); + } + } else if (this.gridHighlighters.size === maxHighlighters) { + // The maximum number of grid highlighters shown have been reached. Don't show + // any additional grid highlighters. + return; + } + } + + const highlighter = await this._getGridHighlighter(node); if (!highlighter) { return; } @@ -434,10 +457,10 @@ class HighlightersOverlay { // Save grid highlighter state. const { url } = this.inspector.target; const selector = await node.getUniqueSelector(); - this.state.grid = { selector, options, url }; - this.gridHighlighterShown = node; + this.state.grids.set(node, { selector, options, url }); + // Emit the NodeFront of the grid container element that the grid highlighter was - // shown for. + // shown for, and its options for testing the highlighter setting options. this.emit("grid-highlighter-shown", node, options); } catch (e) { this._handleRejection(e); @@ -451,22 +474,24 @@ class HighlightersOverlay { * The NodeFront of the grid container element to unhighlight. */ async hideGridHighlighter(node) { - if (!this.gridHighlighterShown || !this.highlighters.CssGridHighlighter) { + if (!this.gridHighlighters.has(node)) { return; } this._toggleRuleViewIcon(node, false, ".ruleview-grid"); - await this.highlighters.CssGridHighlighter.hide(); + // Hide the highlighter and put it in the pool of extra grid highlighters + // so that it can be reused. + const highlighter = this.gridHighlighters.get(node); + await highlighter.hide(); + this.extraGridHighlighterPool.push(highlighter); + + this.state.grids.delete(node); + this.gridHighlighters.delete(node); // Emit the NodeFront of the grid container element that the grid highlighter was // hidden for. - const nodeFront = this.gridHighlighterShown; - this.gridHighlighterShown = null; - this.emit("grid-highlighter-hidden", nodeFront, this.state.grid.options); - - // Erase grid highlighter state. - this.state.grid = {}; + this.emit("grid-highlighter-hidden", node); } /** @@ -572,8 +597,16 @@ class HighlightersOverlay { * Restores the saved grid highlighter state. */ async restoreGridState() { + // The NodeFronts that are used as the keys in the grid state Map are no longer in the + // tree after a reload. To clean up the grid state, we create a copy of the values of + // the grid state before restoring and clear it. + const values = [...this.state.grids.values()]; + this.state.grids.clear(); + try { - await this.restoreState("grid", this.state.grid, this.showGridHighlighter); + for (const gridState of values) { + await this.restoreState("grid", gridState, this.showGridHighlighter); + } } catch (e) { this._handleRejection(e); } @@ -613,24 +646,23 @@ class HighlightersOverlay { await showFunction(nodeFront, options); this.emit(`${name}-state-restored`, { restored: true }); + } else { + this.emit(`${name}-state-restored`, { restored: false }); } - - this.emit(`${name}-state-restored`, { restored: false }); } /** - * Get an instance of an in-context editor for the given type. - * - * In-context editors behave like highlighters but with added editing capabilities which - * need to write value changes back to something, like to properties in the Rule view. - * They typically exist in the context of the page, like the ShapesInContextEditor. - * - * @param {String} type - * Type of in-context editor. Currently supported: "shapesEditor" - * - * @return {Object|null} - * Reference to instance for given type of in-context editor or null. - */ + * Get an instance of an in-context editor for the given type. + * + * In-context editors behave like highlighters but with added editing capabilities which + * need to write value changes back to something, like to properties in the Rule view. + * They typically exist in the context of the page, like the ShapesInContextEditor. + * + * @param {String} type + * Type of in-context editor. Currently supported: "shapesEditor" + * @return {Object|null} + * Reference to instance for given type of in-context editor or null. + */ async getInContextEditor(type) { if (this.editors[type]) { return this.editors[type]; @@ -667,8 +699,6 @@ class HighlightersOverlay { * @return {Promise} that resolves to the highlighter */ async _getHighlighter(type) { - const utils = this.highlighterUtils; - if (this.highlighters[type]) { return this.highlighters[type]; } @@ -676,9 +706,9 @@ class HighlightersOverlay { let highlighter; try { - highlighter = await utils.getHighlighterByType(type); + highlighter = await this.highlighterUtils.getHighlighterByType(type); } catch (e) { - // Ignore any error + this._handleRejection(e); } if (!highlighter) { @@ -689,6 +719,41 @@ class HighlightersOverlay { return highlighter; } + /** + * Get a grid highlighter front for a given node. + * + * @param {NodeFront} node + * The NodeFront of the grid container element to highlight. + * @return {Promise} that resolves to the grid highlighter front. + */ + async _getGridHighlighter(node) { + if (this.gridHighlighters.has(node)) { + return this.gridHighlighters.get(node); + } + + let highlighter; + + // Attempt to use any grid highlighters already instantiated in the extra pool, + // otherwise, initialize a new grid highlighter. + if (this.extraGridHighlighterPool.length > 0) { + highlighter = this.extraGridHighlighterPool.pop(); + } else { + try { + highlighter = await this.highlighterUtils.getHighlighterByType( + "CssGridHighlighter"); + } catch (e) { + this._handleRejection(e); + } + } + + if (!highlighter) { + return null; + } + + this.gridHighlighters.set(node, highlighter); + return highlighter; + } + _handleRejection(error) { if (!this.destroyed) { console.error(error); @@ -778,7 +843,7 @@ class HighlightersOverlay { hideHighlighter(node); } } catch (e) { - console.error(e); + this._handleRejection(e); } } @@ -968,35 +1033,44 @@ class HighlightersOverlay { return; } - this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown, + for (const node of this.gridHighlighters.keys()) { + await this._hideHighlighterIfDeadNode(node, this.hideGridHighlighter); + } + + await this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown, this.hideFlexboxHighlighter); - this._hideHighlighterIfDeadNode(this.flexItemHighlighterShown, + await this._hideHighlighterIfDeadNode(this.flexItemHighlighterShown, this.hideFlexItemHighlighter); - this._hideHighlighterIfDeadNode(this.gridHighlighterShown, - this.hideGridHighlighter); - this._hideHighlighterIfDeadNode(this.shapesHighlighterShown, + await this._hideHighlighterIfDeadNode(this.shapesHighlighterShown, this.hideShapesHighlighter); } /** * Clear saved highlighter shown properties on will-navigate. */ - onWillNavigate() { + async onWillNavigate() { this.destroyEditors(); + // Store all the grid highlighters into the pool of reusable grid highlighters, and + // clear the Map of grid highlighters. + for (const highlighter of this.gridHighlighters.values()) { + this.extraGridHighlighterPool.push(highlighter); + } + + this.gridHighlighters.clear(); + this.boxModelHighlighterShown = null; this.flexboxHighlighterShown = null; this.flexItemHighlighterShown = null; this.geometryEditorHighlighterShown = null; - this.gridHighlighterShown = null; this.hoveredHighlighterShown = null; this.selectorHighlighterShown = null; this.shapesHighlighterShown = null; } /** - * Destroy and clean-up all instances of in-context editors. - */ + * Destroy and clean-up all instances of in-context editors. + */ destroyEditors() { for (const type in this.editors) { this.editors[type].off("show"); @@ -1007,6 +1081,27 @@ class HighlightersOverlay { this.editors = {}; } + /** + * Destroy all instances of the grid highlighters. + */ + destroyGridHighlighters() { + for (const highlighter of this.gridHighlighters.values()) { + highlighter.finalize(); + } + + for (const highlighter of this.extraGridHighlighterPool) { + highlighter.finalize(); + } + + this.gridHighlighters.clear(); + + this.gridHighlighters = null; + this.extraGridHighlighterPool = null; + } + + /** + * Destroy and clean-up all instances of highlighters. + */ destroyHighlighters() { for (const type in this.highlighters) { if (this.highlighters[type]) { @@ -1014,6 +1109,7 @@ class HighlightersOverlay { this.highlighters[type] = null; } } + this.highlighters = null; } @@ -1022,13 +1118,13 @@ class HighlightersOverlay { * all initialized highlighters. */ destroy() { - this.destroyHighlighters(); - this.destroyEditors(); - - // Remove inspector events. this.inspector.off("markupmutation", this.onMarkupMutation); this.inspector.target.off("will-navigate", this.onWillNavigate); + this.destroyEditors(); + this.destroyGridHighlighters(); + this.destroyHighlighters(); + this._lastHovered = null; this.inspector = null; @@ -1040,7 +1136,6 @@ class HighlightersOverlay { this.flexboxHighlighterShown = null; this.flexItemHighlighterShown = null; this.geometryEditorHighlighterShown = null; - this.gridHighlighterShown = null; this.hoveredHighlighterShown = null; this.selectorHighlighterShown = null; this.shapesHighlighterShown = null; diff --git a/devtools/client/preferences/devtools-client.js b/devtools/client/preferences/devtools-client.js index ec8791c05125..a65e6fe83e37 100644 --- a/devtools/client/preferences/devtools-client.js +++ b/devtools/client/preferences/devtools-client.js @@ -82,6 +82,8 @@ pref("devtools.gridinspector.gridOutlineMaxRows", 50); pref("devtools.gridinspector.showGridAreas", false); pref("devtools.gridinspector.showGridLineNumbers", false); pref("devtools.gridinspector.showInfiniteLines", false); +// Max number of grid highlighters that can be displayed +pref("devtools.gridinspector.maxHighlighters", 1); // Whether or not the box model panel is opened in the layout view pref("devtools.layout.boxmodel.opened", true); From 43af6ced9a73bc1298bf5ebfc326c221dd2d7e65 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 10 Sep 2018 14:59:13 -0400 Subject: [PATCH 02/44] Bug 1489721. Use Reader to read the fonts. r=mattwoodrow It gets rid of the unaligned accesses and is easier to reason about. --- gfx/webrender_bindings/Moz2DImageRenderer.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/gfx/webrender_bindings/Moz2DImageRenderer.cpp b/gfx/webrender_bindings/Moz2DImageRenderer.cpp index 8bf7ac46115c..b6e0cb00ff0a 100644 --- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp +++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp @@ -400,6 +400,14 @@ static bool Moz2DRenderCallback(const Range aBlob, return IntRectAbsolute(x1, y1, x2, y2); } + layers::BlobFont ReadBlobFont() { + MOZ_RELEASE_ASSERT(pos + sizeof(layers::BlobFont) <= len); + layers::BlobFont ret; + memcpy(&ret, buf + pos, sizeof(ret)); + pos += sizeof(ret); + return ret; + } + }; MOZ_RELEASE_ASSERT(aBlob.length() > sizeof(size_t)); @@ -421,10 +429,12 @@ static bool Moz2DRenderCallback(const Range aBlob, layers::WebRenderTranslator translator(dt); - size_t count = *(size_t*)(aBlob.begin().get() + end); + MOZ_RELEASE_ASSERT(extra_end >= end); + MOZ_RELEASE_ASSERT(extra_end < aBlob.length()); + Reader fontReader(aBlob.begin().get() + end, extra_end - end); + size_t count = fontReader.ReadSize(); for (size_t i = 0; i < count; i++) { - layers::BlobFont blobFont = - *(layers::BlobFont*)(aBlob.begin() + end + sizeof(count) + sizeof(layers::BlobFont)*i).get(); + layers::BlobFont blobFont = fontReader.ReadBlobFont(); RefPtr scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey); translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont); } From c293ba6c93a05d856d0b948b863053097c84f9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Mon, 10 Sep 2018 21:43:46 +0200 Subject: [PATCH 03/44] Bug 1489504 - Show WebExtension API activity in about:performance, r=felipe,tarek. --- .../content/aboutPerformance.js | 41 +++++++++++++++++-- .../components/extensions/ExtensionParent.jsm | 7 ++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index 79e852414766..8f851f2f7e3b 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -9,6 +9,7 @@ const { PerformanceStats } = ChromeUtils.import("resource://gre/modules/PerformanceStats.jsm", {}); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {}); const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm", {}); +const { ExtensionParent } = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm", {}); const {WebExtensionPolicy} = Cu.getGlobalForObject(Services); @@ -72,6 +73,10 @@ function performanceCountersEnabled() { return Services.prefs.getBoolPref("dom.performance.enable_scheduler_timing", false); } +function extensionCountersEnabled() { + return Services.prefs.getBoolPref("extensions.webextensions.enablePerformanceCounters", false); +} + let tabFinder = { update() { this._map = new Map(); @@ -400,6 +405,11 @@ var State = { return this._monitor.promiseSnapshot(); } + let addons = WebExtensionPolicy.getActiveExtensions(); + let addonHosts = new Map(); + for (let addon of addons) + addonHosts.set(addon.mozExtensionHostname, addon.id); + let counters = await ChromeUtils.requestPerformanceMetrics(); let tabs = {}; for (let counter of counters) { @@ -414,11 +424,15 @@ var State = { } let tab; - if (windowId in tabs) { - tab = tabs[windowId]; + let id = windowId; + if (addonHosts.has(host)) { + id = addonHosts.get(host); + } + if (id in tabs) { + tab = tabs[id]; } else { tab = {windowId, host, dispatchCount: 0, duration: 0, children: []}; - tabs[windowId] = tab; + tabs[id] = tab; } tab.dispatchCount += dispatchCount; tab.duration += duration; @@ -427,6 +441,27 @@ var State = { } } + if (extensionCountersEnabled()) { + let extCounters = ExtensionParent.ParentAPIManager.performanceCounters; + for (let [id, apiMap] of extCounters) { + let dispatchCount = 0, duration = 0; + for (let [, counter] of apiMap) { + dispatchCount += counter.calls; + duration += counter.duration; + } + + let tab; + if (id in tabs) { + tab = tabs[id]; + } else { + tab = {windowId: 0, host: id, dispatchCount: 0, duration: 0, children: []}; + tabs[id] = tab; + } + tab.dispatchCount += dispatchCount; + tab.duration += duration; + } + } + return {tabs, date: Cu.now()}; }, diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index b628a2b13068..0d2ddeae9ce2 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -899,12 +899,13 @@ ParentAPIManager = { if (!this._timingEnabled) { return callable(); } - let webExtId = data.childId.split(".")[0]; - let start = Cu.now(); + let childId = data.childId; + let webExtId = childId.slice(0, childId.lastIndexOf(".")); + let start = Cu.now() * 1000; try { return callable(); } finally { - let end = Cu.now(); + let end = Cu.now() * 1000; this.storeExecutionTime(webExtId, data.path, end - start); } }, From 1973f4a4547aeac7e4394fec1a04fe54679b5c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Mon, 10 Sep 2018 21:43:46 +0200 Subject: [PATCH 04/44] Bug 1489505 - about:performance items should all have a name, r=felipe. --- .../components/aboutperformance/content/aboutPerformance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index 8f851f2f7e3b..f17e974a939b 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -612,11 +612,11 @@ var State = { let image = "chrome://mozapps/skin/places/defaultFavicon.svg"; let found = tabFinder.get(parseInt(id)); if (found) { - name = found.tab.linkedBrowser.contentTitle; if (found.tabbrowser) { + name = found.tab.getAttribute("label"); image = found.tab.getAttribute("image"); } else { - name = "Preloaded: " + name; + name = "Preloaded: " + found.tab.linkedBrowser.contentTitle; } } else if (id == 1) { name = BRAND_NAME; From aa03e83d410fce73d99cfa5a15ea0cdb3c915fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Mon, 10 Sep 2018 21:43:46 +0200 Subject: [PATCH 05/44] Bug 1489512 - about:performance should use the trackers icon instead of the tracking protection one, r=johannh. --- .../components/aboutperformance/content/aboutPerformance.xhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml index e398cb9618f8..f8ad7ca9027a 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml +++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml @@ -149,7 +149,7 @@ padding-inline-start: 62px; } #dispatch-tbody > tr > td.tracking { - background: url(chrome://browser/skin/tracking-protection-disabled.svg) no-repeat 41px center; + background: url(chrome://browser/skin/controlcenter/trackers.svg) no-repeat 41px center; -moz-context-properties: fill; fill: rgb(224, 41, 29); } From 58da26473a4c12f70ba45e9d4ab37540a44d56de Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Wed, 25 Oct 2017 11:33:03 -0700 Subject: [PATCH 06/44] bug 1411458 - add a test for bug 1411458 r=jcj MozReview-Commit-ID: LrZN4DATEVP --HG-- extra : rebase_source : f71a1278484a97e38a7c44fdf47cc0c690a0716b --- js/xpconnect/src/xpc.msg | 3 ++- .../manager/ssl/tests/unit/test_signed_apps.js | 6 ++++++ .../tests/unit/test_signed_apps/bug_1411458.zip | Bin 0 -> 2698 bytes 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip diff --git a/js/xpconnect/src/xpc.msg b/js/xpconnect/src/xpc.msg index ba2a4f79a484..1cc6c031cc70 100644 --- a/js/xpconnect/src/xpc.msg +++ b/js/xpconnect/src/xpc.msg @@ -204,6 +204,7 @@ XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE , "The JAR's signature is wr XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE , "An entry in the JAR is too large.") XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_INVALID , "An entry in the JAR is invalid.") XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_MANIFEST_INVALID , "The JAR's manifest or signature file is invalid.") +XPC_MSG_DEF(NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO , "The PKCS#7 signature is malformed or invalid.") XPC_MSG_DEF(NS_ERROR_CMS_VERIFY_NOT_SIGNED , "The PKCS#7 information is not signed.") /* Codes related to signed manifests */ @@ -236,4 +237,4 @@ XPC_MSG_DEF(NS_ERROR_PHISHING_URI , "The URI is phishing") XPC_MSG_DEF(NS_ERROR_TRACKING_URI , "The URI is tracking") XPC_MSG_DEF(NS_ERROR_UNWANTED_URI , "The URI is unwanted") XPC_MSG_DEF(NS_ERROR_BLOCKED_URI , "The URI is blocked") -XPC_MSG_DEF(NS_ERROR_HARMFUL_URI , "The URI is harmful") \ No newline at end of file +XPC_MSG_DEF(NS_ERROR_HARMFUL_URI , "The URI is harmful") diff --git a/security/manager/ssl/tests/unit/test_signed_apps.js b/security/manager/ssl/tests/unit/test_signed_apps.js index 8c0101b61468..31940dccaaa6 100644 --- a/security/manager/ssl/tests/unit/test_signed_apps.js +++ b/security/manager/ssl/tests/unit/test_signed_apps.js @@ -683,6 +683,12 @@ add_signature_test(PKCS7WithSHA1OrSHA256, function () { Cr.NS_OK)); }); +add_test(function () { + certdb.openSignedAppFileAsync( + Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("bug_1411458"), + check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO)); +}); + // TODO: tampered MF, tampered SF // TODO: too-large MF, too-large RSA, too-large SF // TODO: MF and SF that end immediately after the last main header diff --git a/security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip b/security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip new file mode 100644 index 0000000000000000000000000000000000000000..0b296945ab6b030376713a84381b305fb4a5fcce GIT binary patch literal 2698 zcmWIWW@Zs#00D#OAaAJ{jZJ<)HVE?qac*K>W?E`-iC$K5eqJ?~f`SrEB)%-Qs5mn} zPsvKbNCza5mzbLhDS7Fx1foS_r~y zKpf=i=;G@dl95@g07NAjsR~7@i7C0M3TgR8U~UP>npBuIdT7SYZn9(-V`5+cVLqU7 zDTyVC`kA?j>8W}JdFh=2e(t*?YcQZeW4 zxs99*1_DPm+^&ClUWl=|aADl-ca#2qaF{cShHMD*$R!jgRqc}z-UEzp22WQ%mvv4F zO|S&X!EmKH!h4x;aGwzi1H&dD=3|gy047F9T~9wZeMh~ZV8_r9P6lTAv+pB8xU_Y)UUI%*>kDIEiKd z#Kwgy7cQLG$|&K|$g0EB!{f7%i7lv2h%HIr?H_};2bX$At~{ygkgg&5LFr9WN@_x0 z(i8To{)ycyJ10&|40nIPHQjDwc3=JHZKWG#a^ASbalhv4kt^A!XRnuhXsLCTy?^z( ziUQ}l_s1-AHh*4n%P!SENVU}a@?IKwslosX4AgVtq#nyPf5<4^D=w6s#|`& ziSG-Cl@F8Vb(U|R^Xp&w+BL6I&waN(`g2;k&7GLF>!-~BS>b*Cip$ONtm`4q*UdZe zFug3wt<^Psf5i5Gm;3)3ALT92`S*O~pVb;4Iv$2TGu^JT!{`1tBST{&gGEQ!j5Q)2 z&EMb`vM=@A8>uskYkY27U%Km5TJ@>#QtQ(*m*)LWvicfj6H~XRc&^0FZ%z|0{%W|f z@zL|bV(Zu{|E;I=W?4^9UD^ zTwDIzdGYEOm&@&E^>fGnv3}0;eDdmZQI^Z6FpDhtbU8{b-^1IgI_cBnw`v z*8SUYATR3kAse~-nMd~&slQ*7z4yZQxPmqY_oF+%t=9df%g6KRc)i=%x|eUhEL!-$ zuW#{{cTMNnc$WLW`Dv5>sPOmn<1$9`b4_!Sa+sG*R0~*UaAdMw;gUR;XAujM6&Uzq z{KTBo#N61JIa@e3D;B+T^j@Xo+r#s6{mtbOGrHDQtA*5@ul%%SzPQktdmPt{UcZjt z(p_Wn_uA7#CpA;MoMK-848Fhg^lUR}wGD4>?BDJ4uK9x_omZ|@3=Gi^Ju+7=HbjRJV+s}Ou+q>_}k;(e|&+dH`QD!mi)EtYDjlq9!#uQt- z?riRVKR^8Uy~|rGy1q{Dwf!Ey>%QH&8k>LLTi(grZrUF=+nS{|@!_=jT6ZhIU0yWX z*D3s7{%0QzAKfi;uRQx1I=}knJgd(Cx0XhKzV5p-tJu_b6udR}5lX;G?f7^MESQZUpr;0pF|G}Lv; zOb1qqy1vjl+e*R9)yy;4#mO`|E6K?)$|SNdIXyBhFv`d!)WjC8!emR{cJM6FG7tte z2jG_aI{JCKxdw;m`MSXjMzxyD4_NVADZttV;Fbcid#n_q5<{HQ3Zeqsb9{_TGBZ6( zgR%?^+_IC*^UH0ypxPnzJ*qypl28xFEQ^Tz!VFXAsFLJx3k#335bgZJOkcP@)QTNd zzj3Bho>{3|rg^Hnw|-ELmtTcJXh3C1cCdLq$oq^;BFwm3KEN;lg9b(rh2lGOt>|qR zh(-p62F6Mt6XG*;J?IS-gq{;XCaSMMdgZ`Q#a1;*z_c=KY2*UdLGYM^R#dEziVCfg yLU#{(qJtUQz}Ui$VrH-#x>oe0gV6dDSu0XP3h-tHwX}c@JvN3KVEF#v0Pz5-|6w2i literal 0 HcmV?d00001 From 5d4baca1a6131e37b7c9c4343d19336ace3ab7e8 Mon Sep 17 00:00:00 2001 From: Michael Kaply Date: Thu, 6 Sep 2018 16:14:25 -0500 Subject: [PATCH 07/44] Bug 1482158 - Add telemetry for other major engines. r=mikedeboer --HG-- extra : amend_source : 0097cbe78a73cba3250a98978e1094d20c5024be --- browser/modules/BrowserUsageTelemetry.jsm | 49 +--------- netwerk/base/nsIBrowserSearchService.idl | 10 ++ toolkit/components/search/nsSearchService.js | 91 +++++++++++++++++++ .../tests/xpcshell/test_urltelemetry.js | 83 +++++++++++++++++ .../search/tests/xpcshell/xpcshell.ini | 1 + 5 files changed, 186 insertions(+), 48 deletions(-) create mode 100644 toolkit/components/search/tests/xpcshell/test_urltelemetry.js diff --git a/browser/modules/BrowserUsageTelemetry.jsm b/browser/modules/BrowserUsageTelemetry.jsm index d8fb6fb2d8c5..52a11d770843 100644 --- a/browser/modules/BrowserUsageTelemetry.jsm +++ b/browser/modules/BrowserUsageTelemetry.jsm @@ -202,29 +202,7 @@ let URICountListener = { return; } - let parseURLResult = Services.search.parseSubmissionURL(uriSpec); - if (parseURLResult.engine) { - this._recordSearchTelemetry(uriSpec, parseURLResult); - } else if (this._urlsQueuedForParsing) { - if (Services.search.isInitialized) { - this._urlsQueuedForParsing = null; - } else { - this._urlsQueuedForParsing.push(uriSpec); - if (this._urlsQueuedForParsing.length == 1) { - Services.search.init(rv => { - if (Components.isSuccessCode(rv)) { - for (let url of this._urlsQueuedForParsing) { - let innerParseURLResult = Services.search.parseSubmissionURL(url); - if (innerParseURLResult.engine) { - this._recordSearchTelemetry(url, innerParseURLResult); - } - } - } - this._urlsQueuedForParsing = null; - }); - } - } - } + Services.search.recordSearchURLTelemetry(uriSpec); if (!shouldCountURI) { return; @@ -262,31 +240,6 @@ let URICountListener = { this._domainSet.clear(); }, - _urlsQueuedForParsing: [], - - _recordSearchTelemetry(url, parseURLResult) { - switch (parseURLResult.engine.identifier) { - case "google": - case "google-2018": - let type; - let queries = new URLSearchParams(url.split("#")[0].split("?")[1]); - let code = queries.get("client"); - if (code) { - // Detecting follow-on searches for sap is a little tricky. - // There are a few parameters that only show up - // with follow-ons, so we look for those. (oq/ved/ei) - type = queries.has("oq") || queries.has("ved") || queries.has("ei") ? "sap-follow-on" : "sap"; - } else { - type = "organic"; - } - let payload = `google.in-content:${type}:${code || "none"}`; - - let histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS"); - histogram.add(payload); - break; - } - }, - QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), }; diff --git a/netwerk/base/nsIBrowserSearchService.idl b/netwerk/base/nsIBrowserSearchService.idl index 8bc9bebb6395..8fdceec48a21 100644 --- a/netwerk/base/nsIBrowserSearchService.idl +++ b/netwerk/base/nsIBrowserSearchService.idl @@ -519,6 +519,16 @@ interface nsIBrowserSearchService : nsISupports * "https://www.google.com/search?q=terms". */ nsISearchParseSubmissionResult parseSubmissionURL(in AString url); + + /** + * Determines if a URL is related to search and if so, records the + * appropriate telemetry. + * + * @param url + * String containing the URL to parse, for example + * "https://www.google.com/search?q=terms". + */ + boolean recordSearchURLTelemetry(in AString url); }; %{ C++ diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 864aba011797..2ca53cc3ca12 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -188,6 +188,45 @@ const SEARCH_DEFAULT_UPDATE_INTERVAL = 7; // from the server doesn't specify an interval. const SEARCH_GEO_DEFAULT_UPDATE_INTERVAL = 2592000; // 30 days. +// Regular expressions used to identify common search URLS. +const SEARCH_URL_REGEX = new RegExp([ + /^https:\/\/www\.(google)\.(?:.+)\/search/, + /^https:\/\/(?:.*)search\.(yahoo)\.com\/search/, + /^https:\/\/www\.(bing)\.com\/search/, + /^https:\/\/(duckduckgo)\.com\//, + /^https:\/\/www\.(baidu)\.com\/(?:s|baidu)/, +].map(regex => regex.source).join("|")); + +// Used to identify various parameters (query, code, etc.) in search URLS. +const SEARCH_PROVIDER_INFO = { + "google": { + "queryParam": "q", + "codeParam": "client", + "codePrefixes": ["firefox"], + "followonParams": ["oq", "ved", "ei"], + }, + "duckduckgo": { + "queryParam": "q", + "codeParam": "t", + "codePrefixes": ["ff"], + }, + "yahoo": { + "queryParam": "p", + }, + "baidu": { + "queryParam": "wd", + "codeParam": "tn", + "codePrefixes": ["monline_dg"], + "followonParams": ["oq"], + }, + "bing": { + "queryParam": "q", + "codeParam": "pc", + "codePrefixes": ["MOZ", "MZ"], + }, +}; + +const SEARCH_COUNTS_HISTOGRAM_KEY = "SEARCH_COUNTS"; /** * Prefixed to all search debug output. */ @@ -4420,6 +4459,58 @@ SearchService.prototype = { return new ParseSubmissionResult(mapEntry.engine, terms, offset, length); }, + recordSearchURLTelemetry(url) { + let matches = url.match(SEARCH_URL_REGEX); + if (!matches) { + return; + } + let provider = matches.filter((e, i) => e && i != 0)[0]; + let searchProviderInfo = SEARCH_PROVIDER_INFO[provider]; + let queries = new URLSearchParams(url.split("#")[0].split("?")[1]); + if (!queries.get(searchProviderInfo.queryParam)) { + return; + } + // Default to organic to simplify things. + // We override type in the sap cases. + let type = "organic"; + let code; + if (searchProviderInfo.codeParam) { + code = queries.get(searchProviderInfo.codeParam); + if (code && + searchProviderInfo.codePrefixes.some(p => code.startsWith(p))) { + if (searchProviderInfo.followonParams && + searchProviderInfo.followonParams.some(p => queries.has(p))) { + type = "sap-follow-on"; + } else { + type = "sap"; + } + } else if (provider == "bing") { + // Bing requires lots of extra work related to cookies. + let secondaryCode = queries.get("form"); + // This code is used for all Bing follow-on searches. + if (secondaryCode == "QBRE") { + for (let cookie of Services.cookies.getCookiesFromHost("www.bing.com")) { + if (cookie.name == "SRCHS") { + // If this cookie is present, it's probably an SAP follow-on. + // This might be an organic follow-on in the same session, + // but there is no way to tell the difference. + if (searchProviderInfo.codePrefixes.some(p => cookie.value.startsWith("PC=" + p))) { + type = "sap-follow-on"; + code = cookie.value.split("=")[1]; + break; + } + } + } + } + } + } + + let payload = `${provider}.in-content:${type}:${code || "none"}`; + let histogram = Services.telemetry.getKeyedHistogramById(SEARCH_COUNTS_HISTOGRAM_KEY); + histogram.add(payload); + LOG("nsSearchService::recordSearchURLTelemetry: " + payload); + }, + // nsIObserver observe: function SRCH_SVC_observe(aEngine, aTopic, aVerb) { switch (aTopic) { diff --git a/toolkit/components/search/tests/xpcshell/test_urltelemetry.js b/toolkit/components/search/tests/xpcshell/test_urltelemetry.js new file mode 100644 index 000000000000..c36c600c962d --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test_urltelemetry.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_parsing_search_urls() { + let hs; + // Google search access point. + Services.search.recordSearchURLTelemetry("https://www.google.com/search?q=test&ie=utf-8&oe=utf-8&client=firefox-b-1-ab"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("google.in-content:sap:firefox-b-1-ab" in hs, "The histogram must contain the correct key"); + + // Google search access point follow-on. + Services.search.recordSearchURLTelemetry("https://www.google.com/search?client=firefox-b-1-ab&ei=EI_VALUE&q=test2&oq=test2&gs_l=GS_L_VALUE"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("google.in-content:sap-follow-on:firefox-b-1-ab" in hs, "The histogram must contain the correct key"); + + // Google organic. + Services.search.recordSearchURLTelemetry("https://www.google.com/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("google.in-content:organic:none" in hs, "The histogram must contain the correct key"); + + // Google organic UK. + Services.search.recordSearchURLTelemetry("https://www.google.co.uk/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("google.in-content:organic:none" in hs, "The histogram must contain the correct key"); + + // Yahoo organic. + Services.search.recordSearchURLTelemetry("https://search.yahoo.com/search?p=test&fr=yfp-t&fp=1&toggle=1&cop=mss&ei=UTF-8"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("yahoo.in-content:organic:none" in hs, "The histogram must contain the correct key"); + + // Yahoo organic UK. + Services.search.recordSearchURLTelemetry("https://uk.search.yahoo.com/search?p=test&fr=yfp-t&fp=1&toggle=1&cop=mss&ei=UTF-8"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("yahoo.in-content:organic:none" in hs, "The histogram must contain the correct key"); + + // Bing search access point. + Services.search.recordSearchURLTelemetry("https://www.bing.com/search?q=test&pc=MOZI&form=MOZLBR"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("bing.in-content:sap:MOZI" in hs, "The histogram must contain the correct key"); + + // Bing organic. + Services.search.recordSearchURLTelemetry("https://www.bing.com/search?q=test&qs=n&form=QBLH&sp=-1&pq=&sc=0-0&sk=&cvid=CVID_VALUE"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("bing.in-content:organic:none" in hs, "The histogram must contain the correct key"); + + // DuckDuckGo search access point. + Services.search.recordSearchURLTelemetry("https://duckduckgo.com/?q=test&t=ffab"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("duckduckgo.in-content:sap:ffab" in hs, "The histogram must contain the correct key"); + + // DuckDuckGo organic. + Services.search.recordSearchURLTelemetry("https://duckduckgo.com/?q=test&t=hi&ia=news"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("duckduckgo.in-content:organic:hi" in hs, "The histogram must contain the correct key"); + + // Baidu search access point. + Services.search.recordSearchURLTelemetry("https://www.baidu.com/baidu?wd=test&tn=monline_dg&ie=utf-8"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("baidu.in-content:sap:monline_dg" in hs, "The histogram must contain the correct key"); + + // Baidu search access point follow-on. + Services.search.recordSearchURLTelemetry("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=monline_dg&wd=test2&oq=test&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn&rsv_enter=1&rsv_sug3=2&rsv_sug2=0&inputT=227&rsv_sug4=397"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("baidu.in-content:sap-follow-on:monline_dg" in hs, "The histogram must contain the correct key"); + + // Baidu organic. + Services.search.recordSearchURLTelemetry("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&tn=baidu&bar=&wd=test&rn=&oq=&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn"); + hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(hs); + Assert.ok("baidu.in-content:organic:baidu" in hs, "The histogram must contain the correct key"); +}); diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.ini b/toolkit/components/search/tests/xpcshell/xpcshell.ini index d921936bacbe..7c099075de77 100644 --- a/toolkit/components/search/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini @@ -102,3 +102,4 @@ skip-if = (verify && !debug && (os == 'linux')) [test_paramSubstitution.js] [test_migrateWebExtensionEngine.js] [test_sendSubmissionURL.js] +[test_urltelemetry.js] From 87b1e322e265f6e8b45d63a72a87f845e994d92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 10 Sep 2018 22:27:38 +0200 Subject: [PATCH 08/44] Bug 1489552 - Let BrowserWindowTracker.getTopWindow use the module's internal window list instead of nsIWindowMediator::getZOrderDOMWindowEnumerator / getMostRecentWindow. r=nhnt11 --- browser/base/content/browser-captivePortal.js | 97 +++++++++---------- browser/base/content/browser.js | 2 + .../browser_CaptivePortalWatcher.js | 12 +-- .../browser_CaptivePortalWatcher_1.js | 4 +- .../base/content/test/captivePortal/head.js | 37 ++++--- .../test/browser_restore_reversed_z_order.js | 2 +- browser/modules/BrowserWindowTracker.jsm | 76 +++++---------- .../browser/browser_BrowserWindowTracker.js | 8 +- 8 files changed, 111 insertions(+), 127 deletions(-) diff --git a/browser/base/content/browser-captivePortal.js b/browser/base/content/browser-captivePortal.js index b8bfca9cc05b..ac3edf75708d 100644 --- a/browser/base/content/browser-captivePortal.js +++ b/browser/base/content/browser-captivePortal.js @@ -58,15 +58,13 @@ var CaptivePortalWatcher = { this._captivePortalDetected(); // Automatically open a captive portal tab if there's no other browser window. - let windows = Services.wm.getEnumerator("navigator:browser"); - if (windows.getNext() == window && !windows.hasMoreElements()) { + if (BrowserWindowTracker.windowCount == 1) { this.ensureCaptivePortalTab(); } } else if (cps.state == cps.UNKNOWN) { // We trigger a portal check after delayed startup to avoid doing a network // request before first paint. this._delayedRecheckPending = true; - Services.obs.addObserver(this, "browser-delayed-startup-finished"); } XPCOMUtils.defineLazyPreferenceGetter(this, "PORTAL_RECHECK_DELAY_MS", @@ -78,22 +76,18 @@ var CaptivePortalWatcher = { Services.obs.removeObserver(this, "captive-portal-login-abort"); Services.obs.removeObserver(this, "captive-portal-login-success"); - if (this._delayedRecheckPending) { - Services.obs.removeObserver(this, "browser-delayed-startup-finished"); - } + this._cancelDelayedCaptivePortal(); + }, - if (this._delayedCaptivePortalDetectedInProgress) { - Services.obs.removeObserver(this, "xul-window-visible"); + delayedStartup() { + if (this._delayedRecheckPending) { + delete this._delayedRecheckPending; + cps.recheckCaptivePortal(); } }, observe(aSubject, aTopic, aData) { switch (aTopic) { - case "browser-delayed-startup-finished": - Services.obs.removeObserver(this, "browser-delayed-startup-finished"); - delete this._delayedRecheckPending; - cps.recheckCaptivePortal(); - break; case "captive-portal-login": this._captivePortalDetected(); break; @@ -101,8 +95,8 @@ var CaptivePortalWatcher = { case "captive-portal-login-success": this._captivePortalGone(); break; - case "xul-window-visible": - this._delayedCaptivePortalDetected(); + case "delayed-captive-portal-handled": + this._cancelDelayedCaptivePortal(); break; } }, @@ -113,9 +107,10 @@ var CaptivePortalWatcher = { } let win = BrowserWindowTracker.getTopWindow(); + // Used by tests: ignore the main test window in order to enable testing of // the case where we have no open windows. - if (win && win.document.documentElement.getAttribute("ignorecaptiveportal")) { + if (win.document.documentElement.getAttribute("ignorecaptiveportal")) { win = null; } @@ -123,9 +118,10 @@ var CaptivePortalWatcher = { // This is so that if a different application was focused, when the user // (re-)focuses a browser window, we open the tab immediately in that window // so they can log in before continuing to browse. - if (win != Services.ww.activeWindow) { + if (win != Services.focus.activeWindow) { this._delayedCaptivePortalDetectedInProgress = true; - Services.obs.addObserver(this, "xul-window-visible"); + window.addEventListener("activate", this, { once: true }); + Services.obs.addObserver(this, "delayed-captive-portal-handled"); } this._showNotification(); @@ -141,24 +137,14 @@ var CaptivePortalWatcher = { return; } - let win = BrowserWindowTracker.getTopWindow(); // Used by tests: ignore the main test window in order to enable testing of // the case where we have no open windows. - if (win && win.document.documentElement.getAttribute("ignorecaptiveportal")) { - win = null; - } - - if (win != Services.ww.activeWindow) { - // The window that got focused was not a browser window. + if (window.document.documentElement.getAttribute("ignorecaptiveportal")) { return; } - Services.obs.removeObserver(this, "xul-window-visible"); - this._delayedCaptivePortalDetectedInProgress = false; - if (win != window) { - // Some other browser window got focus, we don't have to do anything. - return; - } + Services.obs.notifyObservers(null, "delayed-captive-portal-handled"); + // Trigger a portal recheck. The user may have logged into the portal via // another client, or changed networks. cps.recheckCaptivePortal(); @@ -185,31 +171,42 @@ var CaptivePortalWatcher = { }, _captivePortalGone() { - if (this._delayedCaptivePortalDetectedInProgress) { - Services.obs.removeObserver(this, "xul-window-visible"); - this._delayedCaptivePortalDetectedInProgress = false; - } - + this._cancelDelayedCaptivePortal(); this._removeNotification(); }, + _cancelDelayedCaptivePortal() { + if (this._delayedCaptivePortalDetectedInProgress) { + this._delayedCaptivePortalDetectedInProgress = false; + Services.obs.removeObserver(this, "delayed-captive-portal-handled"); + window.removeEventListener("activate", this); + } + }, + handleEvent(aEvent) { - if (aEvent.type != "TabSelect" || !this._captivePortalTab || !this._captivePortalNotification) { - return; - } + switch (aEvent.type) { + case "activate": + this._delayedCaptivePortalDetected(); + break; + case "TabSelect": + if (!this._captivePortalTab || !this._captivePortalNotification) { + break; + } - let tab = this._captivePortalTab.get(); - let n = this._captivePortalNotification; - if (!tab || !n) { - return; - } + let tab = this._captivePortalTab.get(); + let n = this._captivePortalNotification; + if (!tab || !n) { + break; + } - let doc = tab.ownerDocument; - let button = n.querySelector("button.notification-button"); - if (doc.defaultView.gBrowser.selectedTab == tab) { - button.style.visibility = "hidden"; - } else { - button.style.visibility = "visible"; + let doc = tab.ownerDocument; + let button = n.querySelector("button.notification-button"); + if (doc.defaultView.gBrowser.selectedTab == tab) { + button.style.visibility = "hidden"; + } else { + button.style.visibility = "visible"; + } + break; } }, diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 8fafc9dcc28d..a212f85a00cc 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1559,6 +1559,8 @@ var gBrowserInit = { } }); + CaptivePortalWatcher.delayedStartup(); + this.delayedStartupFinished = true; _resolveDelayedStartup(); diff --git a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js index 8290571b3f19..ebc43bd91961 100644 --- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js +++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js @@ -24,7 +24,7 @@ let testCasesForBothSuccessAndAbort = [ await freePortal(aSuccess); ensureNoPortalTab(win); ensureNoPortalNotification(win); - await closeWindowAndWaitForXulWindowVisible(win); + await closeWindowAndWaitForWindowActivate(win); }, /** @@ -57,7 +57,7 @@ let testCasesForBothSuccessAndAbort = [ ensureNoPortalTab(win2); ensureNoPortalNotification(win2); - await closeWindowAndWaitForXulWindowVisible(win2); + await closeWindowAndWaitForWindowActivate(win2); // No need to wait for xul-window-visible: after win2 is closed, focus // is restored to the default window and win1 remains in the background. await BrowserTestUtils.closeWindow(win1); @@ -77,7 +77,7 @@ let testCasesForBothSuccessAndAbort = [ await freePortal(aSuccess); ensureNoPortalTab(win); ensureNoPortalNotification(win); - await closeWindowAndWaitForXulWindowVisible(win); + await closeWindowAndWaitForWindowActivate(win); }, /** @@ -95,7 +95,7 @@ let testCasesForBothSuccessAndAbort = [ }); ensureNoPortalTab(win); ensureNoPortalNotification(win); - await closeWindowAndWaitForXulWindowVisible(win); + await closeWindowAndWaitForWindowActivate(win); }, /** @@ -113,8 +113,8 @@ let testCasesForBothSuccessAndAbort = [ await freePortal(aSuccess); ensureNoPortalNotification(win1); ensureNoPortalNotification(win2); - await closeWindowAndWaitForXulWindowVisible(win2); - await closeWindowAndWaitForXulWindowVisible(win1); + await closeWindowAndWaitForWindowActivate(win2); + await closeWindowAndWaitForWindowActivate(win1); }, ]; diff --git a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js index 38c1d5d94130..a1735052541e 100644 --- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js +++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js @@ -21,7 +21,7 @@ let testcases = [ await freePortal(true); ensurePortalTab(win); ensureNoPortalNotification(win); - await closeWindowAndWaitForXulWindowVisible(win); + await closeWindowAndWaitForWindowActivate(win); }, /** @@ -82,7 +82,7 @@ let testcases = [ await freePortal(true); ensureNoPortalTab(win); ensureNoPortalNotification(win); - await closeWindowAndWaitForXulWindowVisible(win); + await closeWindowAndWaitForWindowActivate(win); }, ]; diff --git a/browser/base/content/test/captivePortal/head.js b/browser/base/content/test/captivePortal/head.js index dab6cb1ce037..544304f38026 100644 --- a/browser/base/content/test/captivePortal/head.js +++ b/browser/base/content/test/captivePortal/head.js @@ -60,7 +60,9 @@ async function focusWindowAndWaitForPortalUI(aLongRecheck, win) { if (!win) { win = await BrowserTestUtils.openNewBrowserWindow(); } - await SimpleTest.promiseFocus(win); + let windowActivePromise = waitForBrowserWindowActive(win); + win.focus(); + await windowActivePromise; // After a new window is opened, CaptivePortalWatcher asks for a recheck, and // waits for it to complete. We need to manually tell it a recheck completed. @@ -128,26 +130,35 @@ function ensureNoPortalNotification(win) { /** * Some tests open a new window and close it later. When the window is closed, - * the original window opened by mochitest gains focus, generating a - * xul-window-visible notification. If the next test also opens a new window - * before this notification has a chance to fire, CaptivePortalWatcher picks + * the original window opened by mochitest gains focus, generating an + * activate event. If the next test also opens a new window + * before this event has a chance to fire, CaptivePortalWatcher picks * up the first one instead of the one from the new window. To avoid this - * unfortunate intermittent timing issue, we wait for the notification from + * unfortunate intermittent timing issue, we wait for the event from * the original window every time we close a window that we opened. */ -function waitForXulWindowVisible() { +function waitForBrowserWindowActive(win) { return new Promise(resolve => { - Services.obs.addObserver(function observe() { - Services.obs.removeObserver(observe, "xul-window-visible"); + if (Services.focus.activeWindow == win) { resolve(); - }, "xul-window-visible"); + } else { + win.addEventListener("activate", () => { + resolve(); + }, { once: true }); + } }); } -async function closeWindowAndWaitForXulWindowVisible(win) { - let p = waitForXulWindowVisible(); +async function closeWindowAndWaitForWindowActivate(win) { + let activationPromises = []; + for (let w of BrowserWindowTracker.orderedWindows) { + if (w != win && + !win.document.documentElement.getAttribute("ignorecaptiveportal")) { + activationPromises.push(waitForBrowserWindowActive(win)); + } + } await BrowserTestUtils.closeWindow(win); - await p; + await Promise.race(activationPromises); } /** @@ -157,6 +168,6 @@ async function closeWindowAndWaitForXulWindowVisible(win) { */ async function openWindowAndWaitForFocus() { let win = await BrowserTestUtils.openNewBrowserWindow(); - await SimpleTest.promiseFocus(win); + await waitForBrowserWindowActive(win); return win; } diff --git a/browser/components/sessionstore/test/browser_restore_reversed_z_order.js b/browser/components/sessionstore/test/browser_restore_reversed_z_order.js index 325b4b451de9..0aae3c3a5cb6 100644 --- a/browser/components/sessionstore/test/browser_restore_reversed_z_order.js +++ b/browser/components/sessionstore/test/browser_restore_reversed_z_order.js @@ -66,7 +66,7 @@ add_task(async function test_windows_are_restored_in_reversed_z_order() { await promiseBrowserState(gBrowserState); let indexedTabLabels = [...gTestURLsMap.values()]; - let tabsRestoredLabels = [...BrowserWindowTracker.orderedWindows].map(window => window.gBrowser.selectedTab.label); + let tabsRestoredLabels = BrowserWindowTracker.orderedWindows.map(window => window.gBrowser.selectedTab.label); Assert.equal(tabsRestoredLabels[0], indexedTabLabels[2], "First restored tab should be last used tab"); Assert.equal(tabsRestoredLabels[1], indexedTabLabels[1], "Second restored tab is correct"); diff --git a/browser/modules/BrowserWindowTracker.jsm b/browser/modules/BrowserWindowTracker.jsm index dc083db33605..a5642c4fb848 100644 --- a/browser/modules/BrowserWindowTracker.jsm +++ b/browser/modules/BrowserWindowTracker.jsm @@ -14,7 +14,6 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); // Lazy getters XPCOMUtils.defineLazyModuleGetters(this, { - AppConstants: "resource://gre/modules/AppConstants.jsm", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", }); @@ -145,44 +144,6 @@ var WindowHelper = { _trackedWindows.push(window); } }, - - getTopWindow(options) { - let checkPrivacy = typeof options == "object" && - "private" in options; - - let allowPopups = typeof options == "object" && !!options.allowPopups; - - function isSuitableBrowserWindow(win) { - return (!win.closed && - (allowPopups || win.toolbar.visible) && - (!checkPrivacy || - PrivateBrowsingUtils.permanentPrivateBrowsing || - PrivateBrowsingUtils.isWindowPrivate(win) == options.private)); - } - - let broken_wm_z_order = - AppConstants.platform != "macosx" && AppConstants.platform != "win"; - - if (broken_wm_z_order) { - let win = Services.wm.getMostRecentWindow("navigator:browser"); - - // if we're lucky, this isn't a popup, and we can just return this - if (win && !isSuitableBrowserWindow(win)) { - win = null; - // this is oldest to newest, so this gets a bit ugly - for (let nextWin of Services.wm.getEnumerator("navigator:browser")) { - if (isSuitableBrowserWindow(nextWin)) - win = nextWin; - } - } - return win; - } - for (let win of Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true)) { - if (isSuitableBrowserWindow(win)) - return win; - } - return null; - }, }; this.BrowserWindowTracker = { @@ -195,23 +156,34 @@ this.BrowserWindowTracker = { * Omit the property to search in both groups. * * allowPopups: true if popup windows are permissable. */ - getTopWindow(options) { - return WindowHelper.getTopWindow(options); + getTopWindow(options = {}) { + for (let win of _trackedWindows) { + if (!win.closed && + (options.allowPopups || win.toolbar.visible) && + (!("private" in options) || + PrivateBrowsingUtils.permanentPrivateBrowsing || + PrivateBrowsingUtils.isWindowPrivate(win) == options.private)) { + return win; + } + } + return null; }, /** - * Iterator property that yields window objects by z-index, in reverse order. - * This means that the lastly focused window will the first item that is yielded. - * Note: we only know the order of windows we're actively tracking, which - * basically means _only_ browser windows. + * Number of currently open browser windows. */ - orderedWindows: { - * [Symbol.iterator]() { - // Clone the windows array immediately as it may change during iteration, - // we'd rather have an outdated order than skip/revisit windows. - for (let window of [..._trackedWindows]) - yield window; - }, + get windowCount() { + return _trackedWindows.length; + }, + + /** + * Array of browser windows ordered by z-index, in reverse order. + * This means that the top-most browser window will be the first item. + */ + get orderedWindows() { + // Clone the windows array immediately as it may change during iteration, + // we'd rather have an outdated order than skip/revisit windows. + return [..._trackedWindows]; }, track(window) { diff --git a/browser/modules/test/browser/browser_BrowserWindowTracker.js b/browser/modules/test/browser/browser_BrowserWindowTracker.js index 7414ea882ace..551e828c9ccf 100644 --- a/browser/modules/test/browser/browser_BrowserWindowTracker.js +++ b/browser/modules/test/browser/browser_BrowserWindowTracker.js @@ -79,7 +79,9 @@ add_task(async function test_getTopWindow() { add_task(async function test_orderedWindows() { await withOpenWindows(10, async function(windows) { - let ordered = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW); + Assert.equal(BrowserWindowTracker.windowCount, 11, + "Number of tracked windows, including the test window"); + let ordered = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW); Assert.deepEqual([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], ordered.map(w => windows.indexOf(w)), "Order of opened windows should be as opened."); @@ -90,7 +92,7 @@ add_task(async function test_orderedWindows() { await promise; } - let ordered2 = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW); + let ordered2 = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW); // After the shuffle, we expect window '1' to be the top-most window, because // it was the last one we called focus on. Then '6', the window we focused // before-last, followed by '4'. The order of the other windows remains @@ -104,7 +106,7 @@ add_task(async function test_orderedWindows() { windows[9].minimize(); await promise; - let ordered3 = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW); + let ordered3 = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW); // Test the end of the array of window indices, because Windows Debug builds // mysteriously swap the order of the first two windows. Assert.deepEqual([8, 7, 5, 3, 2, 0, 9], ordered3.map(w => windows.indexOf(w)).slice(3), From 9df3c85d934385313ed67c9d9339e95c3a049bbe Mon Sep 17 00:00:00 2001 From: Tiberius Oros Date: Mon, 10 Sep 2018 23:37:11 +0300 Subject: [PATCH 09/44] Backed out changeset b38e10e9dcf8 (bug 1482158) for eslint failure at builds/worker/checkouts/gecko/browser/modules/BrowserUsageTelemetry.jsm on a CLOSED TREE --- browser/modules/BrowserUsageTelemetry.jsm | 49 +++++++++- netwerk/base/nsIBrowserSearchService.idl | 10 -- toolkit/components/search/nsSearchService.js | 91 ------------------- .../tests/xpcshell/test_urltelemetry.js | 83 ----------------- .../search/tests/xpcshell/xpcshell.ini | 1 - 5 files changed, 48 insertions(+), 186 deletions(-) delete mode 100644 toolkit/components/search/tests/xpcshell/test_urltelemetry.js diff --git a/browser/modules/BrowserUsageTelemetry.jsm b/browser/modules/BrowserUsageTelemetry.jsm index 52a11d770843..d8fb6fb2d8c5 100644 --- a/browser/modules/BrowserUsageTelemetry.jsm +++ b/browser/modules/BrowserUsageTelemetry.jsm @@ -202,7 +202,29 @@ let URICountListener = { return; } - Services.search.recordSearchURLTelemetry(uriSpec); + let parseURLResult = Services.search.parseSubmissionURL(uriSpec); + if (parseURLResult.engine) { + this._recordSearchTelemetry(uriSpec, parseURLResult); + } else if (this._urlsQueuedForParsing) { + if (Services.search.isInitialized) { + this._urlsQueuedForParsing = null; + } else { + this._urlsQueuedForParsing.push(uriSpec); + if (this._urlsQueuedForParsing.length == 1) { + Services.search.init(rv => { + if (Components.isSuccessCode(rv)) { + for (let url of this._urlsQueuedForParsing) { + let innerParseURLResult = Services.search.parseSubmissionURL(url); + if (innerParseURLResult.engine) { + this._recordSearchTelemetry(url, innerParseURLResult); + } + } + } + this._urlsQueuedForParsing = null; + }); + } + } + } if (!shouldCountURI) { return; @@ -240,6 +262,31 @@ let URICountListener = { this._domainSet.clear(); }, + _urlsQueuedForParsing: [], + + _recordSearchTelemetry(url, parseURLResult) { + switch (parseURLResult.engine.identifier) { + case "google": + case "google-2018": + let type; + let queries = new URLSearchParams(url.split("#")[0].split("?")[1]); + let code = queries.get("client"); + if (code) { + // Detecting follow-on searches for sap is a little tricky. + // There are a few parameters that only show up + // with follow-ons, so we look for those. (oq/ved/ei) + type = queries.has("oq") || queries.has("ved") || queries.has("ei") ? "sap-follow-on" : "sap"; + } else { + type = "organic"; + } + let payload = `google.in-content:${type}:${code || "none"}`; + + let histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS"); + histogram.add(payload); + break; + } + }, + QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), }; diff --git a/netwerk/base/nsIBrowserSearchService.idl b/netwerk/base/nsIBrowserSearchService.idl index 8fdceec48a21..8bc9bebb6395 100644 --- a/netwerk/base/nsIBrowserSearchService.idl +++ b/netwerk/base/nsIBrowserSearchService.idl @@ -519,16 +519,6 @@ interface nsIBrowserSearchService : nsISupports * "https://www.google.com/search?q=terms". */ nsISearchParseSubmissionResult parseSubmissionURL(in AString url); - - /** - * Determines if a URL is related to search and if so, records the - * appropriate telemetry. - * - * @param url - * String containing the URL to parse, for example - * "https://www.google.com/search?q=terms". - */ - boolean recordSearchURLTelemetry(in AString url); }; %{ C++ diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 2ca53cc3ca12..864aba011797 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -188,45 +188,6 @@ const SEARCH_DEFAULT_UPDATE_INTERVAL = 7; // from the server doesn't specify an interval. const SEARCH_GEO_DEFAULT_UPDATE_INTERVAL = 2592000; // 30 days. -// Regular expressions used to identify common search URLS. -const SEARCH_URL_REGEX = new RegExp([ - /^https:\/\/www\.(google)\.(?:.+)\/search/, - /^https:\/\/(?:.*)search\.(yahoo)\.com\/search/, - /^https:\/\/www\.(bing)\.com\/search/, - /^https:\/\/(duckduckgo)\.com\//, - /^https:\/\/www\.(baidu)\.com\/(?:s|baidu)/, -].map(regex => regex.source).join("|")); - -// Used to identify various parameters (query, code, etc.) in search URLS. -const SEARCH_PROVIDER_INFO = { - "google": { - "queryParam": "q", - "codeParam": "client", - "codePrefixes": ["firefox"], - "followonParams": ["oq", "ved", "ei"], - }, - "duckduckgo": { - "queryParam": "q", - "codeParam": "t", - "codePrefixes": ["ff"], - }, - "yahoo": { - "queryParam": "p", - }, - "baidu": { - "queryParam": "wd", - "codeParam": "tn", - "codePrefixes": ["monline_dg"], - "followonParams": ["oq"], - }, - "bing": { - "queryParam": "q", - "codeParam": "pc", - "codePrefixes": ["MOZ", "MZ"], - }, -}; - -const SEARCH_COUNTS_HISTOGRAM_KEY = "SEARCH_COUNTS"; /** * Prefixed to all search debug output. */ @@ -4459,58 +4420,6 @@ SearchService.prototype = { return new ParseSubmissionResult(mapEntry.engine, terms, offset, length); }, - recordSearchURLTelemetry(url) { - let matches = url.match(SEARCH_URL_REGEX); - if (!matches) { - return; - } - let provider = matches.filter((e, i) => e && i != 0)[0]; - let searchProviderInfo = SEARCH_PROVIDER_INFO[provider]; - let queries = new URLSearchParams(url.split("#")[0].split("?")[1]); - if (!queries.get(searchProviderInfo.queryParam)) { - return; - } - // Default to organic to simplify things. - // We override type in the sap cases. - let type = "organic"; - let code; - if (searchProviderInfo.codeParam) { - code = queries.get(searchProviderInfo.codeParam); - if (code && - searchProviderInfo.codePrefixes.some(p => code.startsWith(p))) { - if (searchProviderInfo.followonParams && - searchProviderInfo.followonParams.some(p => queries.has(p))) { - type = "sap-follow-on"; - } else { - type = "sap"; - } - } else if (provider == "bing") { - // Bing requires lots of extra work related to cookies. - let secondaryCode = queries.get("form"); - // This code is used for all Bing follow-on searches. - if (secondaryCode == "QBRE") { - for (let cookie of Services.cookies.getCookiesFromHost("www.bing.com")) { - if (cookie.name == "SRCHS") { - // If this cookie is present, it's probably an SAP follow-on. - // This might be an organic follow-on in the same session, - // but there is no way to tell the difference. - if (searchProviderInfo.codePrefixes.some(p => cookie.value.startsWith("PC=" + p))) { - type = "sap-follow-on"; - code = cookie.value.split("=")[1]; - break; - } - } - } - } - } - } - - let payload = `${provider}.in-content:${type}:${code || "none"}`; - let histogram = Services.telemetry.getKeyedHistogramById(SEARCH_COUNTS_HISTOGRAM_KEY); - histogram.add(payload); - LOG("nsSearchService::recordSearchURLTelemetry: " + payload); - }, - // nsIObserver observe: function SRCH_SVC_observe(aEngine, aTopic, aVerb) { switch (aTopic) { diff --git a/toolkit/components/search/tests/xpcshell/test_urltelemetry.js b/toolkit/components/search/tests/xpcshell/test_urltelemetry.js deleted file mode 100644 index c36c600c962d..000000000000 --- a/toolkit/components/search/tests/xpcshell/test_urltelemetry.js +++ /dev/null @@ -1,83 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -add_task(async function test_parsing_search_urls() { - let hs; - // Google search access point. - Services.search.recordSearchURLTelemetry("https://www.google.com/search?q=test&ie=utf-8&oe=utf-8&client=firefox-b-1-ab"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("google.in-content:sap:firefox-b-1-ab" in hs, "The histogram must contain the correct key"); - - // Google search access point follow-on. - Services.search.recordSearchURLTelemetry("https://www.google.com/search?client=firefox-b-1-ab&ei=EI_VALUE&q=test2&oq=test2&gs_l=GS_L_VALUE"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("google.in-content:sap-follow-on:firefox-b-1-ab" in hs, "The histogram must contain the correct key"); - - // Google organic. - Services.search.recordSearchURLTelemetry("https://www.google.com/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("google.in-content:organic:none" in hs, "The histogram must contain the correct key"); - - // Google organic UK. - Services.search.recordSearchURLTelemetry("https://www.google.co.uk/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("google.in-content:organic:none" in hs, "The histogram must contain the correct key"); - - // Yahoo organic. - Services.search.recordSearchURLTelemetry("https://search.yahoo.com/search?p=test&fr=yfp-t&fp=1&toggle=1&cop=mss&ei=UTF-8"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("yahoo.in-content:organic:none" in hs, "The histogram must contain the correct key"); - - // Yahoo organic UK. - Services.search.recordSearchURLTelemetry("https://uk.search.yahoo.com/search?p=test&fr=yfp-t&fp=1&toggle=1&cop=mss&ei=UTF-8"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("yahoo.in-content:organic:none" in hs, "The histogram must contain the correct key"); - - // Bing search access point. - Services.search.recordSearchURLTelemetry("https://www.bing.com/search?q=test&pc=MOZI&form=MOZLBR"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("bing.in-content:sap:MOZI" in hs, "The histogram must contain the correct key"); - - // Bing organic. - Services.search.recordSearchURLTelemetry("https://www.bing.com/search?q=test&qs=n&form=QBLH&sp=-1&pq=&sc=0-0&sk=&cvid=CVID_VALUE"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("bing.in-content:organic:none" in hs, "The histogram must contain the correct key"); - - // DuckDuckGo search access point. - Services.search.recordSearchURLTelemetry("https://duckduckgo.com/?q=test&t=ffab"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("duckduckgo.in-content:sap:ffab" in hs, "The histogram must contain the correct key"); - - // DuckDuckGo organic. - Services.search.recordSearchURLTelemetry("https://duckduckgo.com/?q=test&t=hi&ia=news"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("duckduckgo.in-content:organic:hi" in hs, "The histogram must contain the correct key"); - - // Baidu search access point. - Services.search.recordSearchURLTelemetry("https://www.baidu.com/baidu?wd=test&tn=monline_dg&ie=utf-8"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("baidu.in-content:sap:monline_dg" in hs, "The histogram must contain the correct key"); - - // Baidu search access point follow-on. - Services.search.recordSearchURLTelemetry("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=monline_dg&wd=test2&oq=test&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn&rsv_enter=1&rsv_sug3=2&rsv_sug2=0&inputT=227&rsv_sug4=397"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("baidu.in-content:sap-follow-on:monline_dg" in hs, "The histogram must contain the correct key"); - - // Baidu organic. - Services.search.recordSearchURLTelemetry("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&tn=baidu&bar=&wd=test&rn=&oq=&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn"); - hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(hs); - Assert.ok("baidu.in-content:organic:baidu" in hs, "The histogram must contain the correct key"); -}); diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.ini b/toolkit/components/search/tests/xpcshell/xpcshell.ini index 7c099075de77..d921936bacbe 100644 --- a/toolkit/components/search/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini @@ -102,4 +102,3 @@ skip-if = (verify && !debug && (os == 'linux')) [test_paramSubstitution.js] [test_migrateWebExtensionEngine.js] [test_sendSubmissionURL.js] -[test_urltelemetry.js] From 60ed9855dbd638eef6a557c6af5703ab8419a481 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 10 Sep 2018 21:53:40 +0100 Subject: [PATCH 10/44] Bug 1489848 - SVG elements don't implement the click method r=heycam --- .../meta/svg/linking/scripted/a-download-click.svg.ini | 4 ---- .../tests/svg/linking/scripted/a-download-click.svg | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini diff --git a/testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini b/testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini deleted file mode 100644 index c222c822e260..000000000000 --- a/testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini +++ /dev/null @@ -1,4 +0,0 @@ -[a-download-click.svg] - [Clicking on an element with a download attribute must not throw an exception] - expected: FAIL - diff --git a/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg b/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg index 406d0e6ba566..b728603d5475 100644 --- a/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg +++ b/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg @@ -22,7 +22,7 @@ const link = frame.contentDocument.querySelector("#blob-url"); link.href.baseVal = URL.createObjectURL(blob); - link.click(); + link.dispatchEvent(new Event('click')); t.step_timeout(() => t.done(), 1000); })); @@ -30,4 +30,4 @@ root.appendChild(frame); }, "Clicking on an element with a download attribute must not throw an exception"); ]]> - \ No newline at end of file + From 1251117de44a665872ff62cec73aeaf0ad82d488 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 10 Sep 2018 13:50:42 -0700 Subject: [PATCH 11/44] Bug 1490111: Fix typo in reference case link-tag for reftest contain-layout-formatting-context-margin-001.html. (no review; just a trivial test metadata tweak) This patch fixes a trivial typo and brings the tag into alignment with what we've already got in our reftest.list file. Landing as DONTBUILD (skipping builds/tests) because this won't impact test runs, since this tag is inactive in this local copy of the test. --HG-- extra : rebase_source : 1b0f6f79d671416fb6e617804158925a10499f17 --- .../contain/contain-layout-formatting-context-margin-001.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html b/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html index c346a529284b..a8520ee2d1c8 100644 --- a/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html +++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html @@ -6,7 +6,7 @@ - + -
-
cell1
-
cell2
-
-
-
cell1
-
cell2
-
-`; - -add_task(async function() { - await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); - const { inspector, gridInspector } = await openLayoutView(); - const { document: doc } = gridInspector; - const { highlighters, store } = inspector; - - await selectNode("#grid1", inspector); - const gridList = doc.getElementById("grid-list"); - const checkbox1 = gridList.children[0].querySelector("input"); - const checkbox2 = gridList.children[1].querySelector("input"); - - info("Checking the initial state of the Grid Inspector."); - is(gridList.childNodes.length, 2, "2 grid containers are listed."); - ok(!checkbox1.checked, `Grid item ${checkbox1.value} is unchecked in the grid list.`); - ok(!checkbox2.checked, `Grid item ${checkbox2.value} is unchecked in the grid list.`); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); - - info("Toggling ON the CSS grid highlighter for #grid1."); - let onHighlighterShown = highlighters.once("grid-highlighter-shown"); - let onCheckboxChange = waitUntilState(store, state => - state.grids.length == 2 && - state.grids[0].highlighted && - !state.grids[1].highlighted); - checkbox1.click(); - await onHighlighterShown; - await onCheckboxChange; - - info("Checking the CSS grid highlighter is created."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); - - info("Toggling ON the CSS grid highlighter for #grid2."); - onHighlighterShown = highlighters.once("grid-highlighter-shown"); - onCheckboxChange = waitUntilState(store, state => - state.grids.length == 2 && - !state.grids[0].highlighted && - state.grids[1].highlighted); - checkbox2.click(); - await onHighlighterShown; - await onCheckboxChange; - - info("Checking the CSS grid highlighter is still shown."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); - - info("Toggling OFF the CSS grid highlighter from the layout panel."); - const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); - onCheckboxChange = waitUntilState(store, state => - state.grids.length == 2 && - !state.grids[0].highlighted && - !state.grids[1].highlighted); - checkbox2.click(); - await onHighlighterHidden; - await onCheckboxChange; - - info("Checking the CSS grid highlighter is not shown."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); -}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js index 0284c4f742ad..f67fff38e767 100644 --- a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js @@ -3,7 +3,8 @@ "use strict"; -// Test toggling the multiple grid highlighters in the grid inspector panel. +// Test toggling the grid highlighter in the grid inspector panel with multiple grids in +// the page. const TEST_URI = ` -
-
cell1
-
cell2
-
-
-
cell1
-
cell2
-
-
-
cell1
-
cell2
-
-
-
cell1
-
cell2
-
-`; - -add_task(async function() { - await pushPref("devtools.gridinspector.maxHighlighters", 3); - await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); - const { gridInspector, inspector } = await openLayoutView(); - const { document: doc } = gridInspector; - const { highlighters, store } = inspector; - - await selectNode("#grid1", inspector); - const gridList = doc.getElementById("grid-list"); - const checkbox1 = gridList.children[0].querySelector("input"); - const checkbox2 = gridList.children[1].querySelector("input"); - const checkbox3 = gridList.children[2].querySelector("input"); - - info("Toggling ON the CSS grid highlighter for #grid1."); - let onHighlighterShown = highlighters.once("grid-highlighter-shown"); - let onCheckboxChange = waitUntilState(store, state => - state.grids.length == 4 && - state.grids[0].highlighted && !state.grids[0].disabled && - !state.grids[1].highlighted && !state.grids[1].disabled && - !state.grids[2].highlighted && !state.grids[2].disabled && - !state.grids[3].highlighted && !state.grids[3].disabled); - checkbox1.click(); - await onHighlighterShown; - await onCheckboxChange; - - info("Toggling ON the CSS grid highlighter for #grid2."); - onHighlighterShown = highlighters.once("grid-highlighter-shown"); - onCheckboxChange = waitUntilState(store, state => - state.grids.length == 4 && - state.grids[0].highlighted && !state.grids[0].disabled && - state.grids[1].highlighted && !state.grids[1].disabled && - !state.grids[2].highlighted && !state.grids[2].disabled && - !state.grids[3].highlighted && !state.grids[3].disabled); - checkbox2.click(); - await onHighlighterShown; - await onCheckboxChange; - - info("Toggling ON the CSS grid highlighter for #grid3."); - onHighlighterShown = highlighters.once("grid-highlighter-shown"); - onCheckboxChange = waitUntilState(store, state => - state.grids.length == 4 && - state.grids[0].highlighted && !state.grids[0].disabled && - state.grids[1].highlighted && !state.grids[1].disabled && - state.grids[2].highlighted && !state.grids[2].disabled && - !state.grids[3].highlighted && state.grids[3].disabled); - checkbox3.click(); - await onHighlighterShown; - await onCheckboxChange; - - info("Check that the CSS grid highlighters are created and the saved grid state."); - is(highlighters.gridHighlighters.size, 3, - "Got expected number of grid highlighters shown."); - is(highlighters.state.grids.size, 3, "Got expected number of grids in the saved state"); - - info("Reload the page, expect the highlighters to be displayed once again and " + - "grids are checked"); - const onStateRestored = waitForNEvents(highlighters, "grid-state-restored", 3); - const onGridListRestored = waitUntilState(store, state => - state.grids.length == 4 && - state.grids[0].highlighted && !state.grids[0].disabled && - state.grids[1].highlighted && !state.grids[1].disabled && - state.grids[2].highlighted && !state.grids[2].disabled && - !state.grids[3].highlighted && state.grids[3].disabled); - await refreshTab(); - const { restored } = await onStateRestored; - await onGridListRestored; - - info("Check that the grid highlighters can be displayed after reloading the page"); - ok(restored, "The highlighter state was restored"); - is(highlighters.gridHighlighters.size, 3, - "Got expected number of grid highlighters shown."); - is(highlighters.state.grids.size, 3, "Got expected number of grids in the saved state"); -}); diff --git a/devtools/client/inspector/grids/test/head.js b/devtools/client/inspector/grids/test/head.js index 6c2412cbee8c..29cb4fe9c06e 100644 --- a/devtools/client/inspector/grids/test/head.js +++ b/devtools/client/inspector/grids/test/head.js @@ -23,6 +23,7 @@ registerCleanupFunction(() => { }); const asyncStorage = require("devtools/shared/async-storage"); +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; /** * Simulate a color change in a given color picker tooltip. diff --git a/devtools/client/inspector/grids/types.js b/devtools/client/inspector/grids/types.js index 124ed7059316..d55fedc86029 100644 --- a/devtools/client/inspector/grids/types.js +++ b/devtools/client/inspector/grids/types.js @@ -19,10 +19,6 @@ exports.grid = { // The text direction of the grid container direction: PropTypes.string, - // Whether or not the grid checkbox is disabled as a result of hitting the - // maximum number of grid highlighters shown. - disabled: PropTypes.bool, - // The grid fragment object of the grid container gridFragments: PropTypes.array, diff --git a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js index afdde035d7f8..353131e3b379 100644 --- a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js +++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js @@ -14,6 +14,8 @@ const TEST_URI = `
`; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const { inspector } = await openLayoutView(); @@ -26,8 +28,9 @@ add_task(async function() { ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active."); info("Check the initial state of the grid highlighter."); - ok(!highlighters.gridHighlighters.size, + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the grid display badge."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -38,9 +41,10 @@ add_task(async function() { await onHighlighterShown; await onCheckboxChange; - info("Check that the CSS grid highlighter is created and the display badge state."); - is(highlighters.gridHighlighters.size, 1, + info("Check the CSS grid highlighter is created and grid display badge state."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); ok(gridDisplayBadge.classList.contains("active"), "grid display badge is active."); info("Toggling OFF the CSS grid highlighter from the grid display badge."); diff --git a/devtools/client/inspector/markup/views/element-container.js b/devtools/client/inspector/markup/views/element-container.js index 1b6c5544b8d1..66db51582db9 100644 --- a/devtools/client/inspector/markup/views/element-container.js +++ b/devtools/client/inspector/markup/views/element-container.js @@ -96,7 +96,7 @@ MarkupElementContainer.prototype = extend(MarkupContainer.prototype, { return; } this.editor._displayBadge.classList.toggle("active", - this.markup.highlighters.gridHighlighters.has(this.node)); + this.markup.highlighters.gridHighlighterShown === this.node); }, async _buildEventTooltipContent(target) { diff --git a/devtools/client/inspector/markup/views/element-editor.js b/devtools/client/inspector/markup/views/element-editor.js index e62c64a43241..fd87c683b218 100644 --- a/devtools/client/inspector/markup/views/element-editor.js +++ b/devtools/client/inspector/markup/views/element-editor.js @@ -326,7 +326,7 @@ ElementEditor.prototype = { this._displayBadge.title = DISPLAY_TYPES[this.node.displayType]; this._displayBadge.classList.toggle("active", this.highlighters.flexboxHighlighterShown === this.node || - this.highlighters.gridHighlighters.has(this.node)); + this.highlighters.gridHighlighterShown === this.node); this._displayBadge.classList.toggle("interactive", Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled") && (this.node.displayType === "flex" || this.node.displayType === "inline-flex")); diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js b/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js index c5745b69d2ab..d665bd04a81c 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-display-grid-property.js @@ -45,5 +45,5 @@ add_task(async function() { info("Check the grid highlighter and grid toggle button are hidden."); gridToggle = container.querySelector(".ruleview-grid"); ok(!gridToggle, "Grid highlighter toggle is not visible."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js index c1db983125eb..a4028f6b1cbf 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js @@ -32,7 +32,7 @@ add_task(async function() { const onHighlighterShown = highlighters.once("grid-highlighter-shown"); gridToggle.click(); await onHighlighterShown; - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); info("Remove the #grid container in the content page"); @@ -40,5 +40,5 @@ add_task(async function() { document.querySelector("#grid").remove(); `); await onHighlighterHidden; - ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js index 688450d4f250..b83b20155d4c 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-navigate.js @@ -34,8 +34,8 @@ add_task(async function() { gridToggle.click(); await onHighlighterShown; - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); await navigateTo(inspector, TEST_URI_2); - ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js index 2263da654b72..565280a96be7 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js @@ -49,5 +49,5 @@ async function checkGridHighlighter() { gridToggle.click(); await onHighlighterShown; - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); } diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js index ae3235f47055..1147fb25f61d 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-restored-after-reload.js @@ -47,7 +47,7 @@ add_task(async function() { gridToggle.click(); await onHighlighterShown; - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Reload the page, expect the highlighter to be displayed once again"); let onStateRestored = highlighters.once("grid-state-restored"); @@ -56,7 +56,7 @@ add_task(async function() { ok(restored, "The highlighter state was restored"); info("Check that the grid highlighter can be displayed after reloading the page"); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Navigate to another URL, and check that the highlighter is hidden"); const otherUri = "data:text/html;charset=utf-8," + encodeURIComponent(OTHER_URI); @@ -64,5 +64,5 @@ add_task(async function() { await navigateTo(inspector, otherUri); ({ restored } = await onStateRestored); ok(!restored, "The highlighter state was not restored"); - ok(!highlighters.gridHighlighters.size, "CSS grid highlighter is hidden."); + ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js index 34f7fb55a6f0..08496bf142c4 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js @@ -19,6 +19,8 @@ const TEST_URI = ` `; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -32,7 +34,9 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the rule-view."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -43,7 +47,9 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter created in the rule-view."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -54,5 +60,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js index f0f7324f27d5..65803569caef 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js @@ -19,6 +19,8 @@ const TEST_URI = ` `; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -32,7 +34,9 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the rule-view."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -43,7 +47,9 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter created in the rule-view."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -54,5 +60,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js index 58c6e6b37782..98f94008d1a0 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js @@ -22,6 +22,8 @@ const TEST_URI = ` `; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -38,7 +40,9 @@ add_task(async function() { ok(!gridToggle.classList.contains("active") && !overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle buttons are not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the rule-view."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the overridden rule in the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -50,7 +54,9 @@ add_task(async function() { ok(gridToggle.classList.contains("active") && overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter created in the rule-view."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Toggling off the CSS grid highlighter from the normal grid declaration in the " + "rule-view."); @@ -63,5 +69,5 @@ add_task(async function() { ok(!gridToggle.classList.contains("active") && !overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle buttons are not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js index d143afcb2143..1ee3cf3c4d8f 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js @@ -22,6 +22,8 @@ const TEST_URI = ` `; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -37,7 +39,9 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the rule-view."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter for the first grid container from the " + "rule-view."); @@ -49,11 +53,13 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter created in the rule-view."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Selecting the second grid container."); await selectNode("#grid2", inspector); - const firstGridHighterShown = highlighters.gridHighlighters.keys().next().value; + const firstGridHighterShown = highlighters.gridHighlighterShown; container = getRuleViewProperty(view, ".grid", "display").valueSpan; gridToggle = container.querySelector(".ruleview-grid"); @@ -62,7 +68,7 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is still shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is still shown."); info("Toggling ON the CSS grid highlighter for the second grid container from the " + "rule-view."); @@ -74,7 +80,7 @@ add_task(async function() { "toggle button is active in the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - ok(highlighters.gridHighlighters.keys().next().value != firstGridHighterShown, + ok(highlighters.gridHighlighterShown != firstGridHighterShown, "Grid highlighter for the second grid container is shown."); info("Selecting the first grid container."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js index 8f45bfbd0ccd..037af70ecd05 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js @@ -19,6 +19,8 @@ const TEST_URI = ` `; +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -32,7 +34,9 @@ add_task(async function() { ok(gridToggle, "Grid highlighter toggle is visible."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the rule-view."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); info("Toggling ON the CSS grid highlighter from the rule-view."); const onHighlighterShown = highlighters.once("grid-highlighter-shown"); @@ -43,7 +47,9 @@ add_task(async function() { "the rule-view."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); - is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter created in the rule-view."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); info("Toggling OFF the CSS grid highlighter from the rule-view."); const onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); @@ -54,5 +60,5 @@ add_task(async function() { "in the rule-view."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); - ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); }); diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js index fc177b452e87..761b6805cb3b 100644 --- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -538,8 +538,8 @@ TextPropertyEditor.prototype = { const gridToggle = this.valueSpan.querySelector(".ruleview-grid"); if (gridToggle) { gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip")); - if (this.ruleView.highlighters.gridHighlighters.has( - this.ruleView.inspector.selection.nodeFront)) { + if (this.ruleView.highlighters.gridHighlighterShown === + this.ruleView.inspector.selection.nodeFront) { gridToggle.classList.add("active"); } } @@ -926,15 +926,14 @@ TextPropertyEditor.prototype = { "session_id": this.toolbox.sessionId }); - // Since the value was changed, check if the original property was a flex or grid + // Since the value was changed, check if the original propertywas a flex or grid // display declaration and hide their respective highlighters. if (this.isDisplayFlex()) { this.ruleView.highlighters.hideFlexboxHighlighter(); } if (this.isDisplayGrid()) { - this.ruleView.highlighters.hideGridHighlighter( - this.ruleView.inspector.selection.nodeFront); + this.ruleView.highlighters.hideGridHighlighter(); } // First, set this property value (common case, only modified a property) diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js index 7c5dfc453550..947588d8c97d 100644 --- a/devtools/client/inspector/shared/highlighters-overlay.js +++ b/devtools/client/inspector/shared/highlighters-overlay.js @@ -6,7 +6,6 @@ "use strict"; -const Services = require("Services"); const EventEmitter = require("devtools/shared/event-emitter"); const { VIEW_NODE_VALUE_TYPE, @@ -24,45 +23,42 @@ class HighlightersOverlay { * Inspector toolbox panel. */ constructor(inspector) { + /* + * Collection of instantiated highlighter actors like FlexboxHighlighter, + * CssGridHighlighter, ShapesHighlighter and GeometryEditorHighlighter. + */ + this.highlighters = {}; + /* + * Collection of instantiated in-context editors, like ShapesInContextEditor, which + * behave like highlighters but with added editing capabilities that need to map value + * changes to properties in the Rule view. + */ + this.editors = {}; this.inspector = inspector; this.highlighterUtils = this.inspector.toolbox.highlighterUtils; this.store = this.inspector.store; this.telemetry = inspector.telemetry; - // Collection of instantiated highlighter actors like FlexboxHighlighter, - // ShapesHighlighter and GeometryEditorHighlighter. - this.highlighters = {}; - // Map of grid container NodeFront to their instantiated grid highlighter actors. - this.gridHighlighters = new Map(); - // Array of reusable grid highlighters that have been instantiated and are not - // associated with any NodeFront. - this.extraGridHighlighterPool = []; - - // Collection of instantiated in-context editors, like ShapesInContextEditor, which - // behave like highlighters but with added editing capabilities that need to map value - // changes to properties in the Rule view. - this.editors = {}; - - // Saved state to be restore on page navigation. - this.state = { - flexbox: {}, - // Map of grid container NodeFront to the their stored grid options - grids: new Map(), - shapes: {}, - }; - // NodeFront of the flexbox container that is highlighted. this.flexboxHighlighterShown = null; // NodeFront of the flex item that is highlighted. this.flexItemHighlighterShown = null; // NodeFront of element that is highlighted by the geometry editor. this.geometryEditorHighlighterShown = null; + // NodeFront of the grid container that is highlighted. + this.gridHighlighterShown = null; // Name of the highlighter shown on mouse hover. this.hoveredHighlighterShown = null; // Name of the selector highlighter shown. this.selectorHighlighterShown = null; // NodeFront of the shape that is highlighted this.shapesHighlighterShown = null; + // Saved state to be restore on page navigation. + this.state = { + flexbox: {}, + grid: {}, + shapes: {}, + }; this.onClick = this.onClick.bind(this); this.onMarkupMutation = this.onMarkupMutation.bind(this); @@ -130,7 +126,7 @@ class HighlightersOverlay { /** * Toggle the shapes highlighter for the given node. - * + * @param {NodeFront} node * The NodeFront of the element with a shape to highlight. * @param {Object} options @@ -393,7 +389,7 @@ class HighlightersOverlay { * "rule" represents the rule view. */ async toggleGridHighlighter(node, trigger) { - if (this.gridHighlighters.has(node)) { + if (node == this.gridHighlighterShown) { await this.hideGridHighlighter(node); return; } @@ -414,26 +410,7 @@ class HighlightersOverlay { * "rule" represents the rule view. */ async showGridHighlighter(node, options, trigger) { - const maxHighlighters = - Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); - - // When the grid highlighter has the given node, it is probably called with new - // highlighting options, so skip any extra grid highlighter handling. - if (!this.gridHighlighters.has(node)) { - if (maxHighlighters === 1) { - // Only one grid highlighter can be shown at a time. Hides any instantiated - // grid highlighters. - for (const nodeFront of this.gridHighlighters.keys()) { - await this.hideGridHighlighter(nodeFront); - } - } else if (this.gridHighlighters.size === maxHighlighters) { - // The maximum number of grid highlighters shown have been reached. Don't show - // any additional grid highlighters. - return; - } - } - - const highlighter = await this._getGridHighlighter(node); + const highlighter = await this._getHighlighter("CssGridHighlighter"); if (!highlighter) { return; } @@ -457,10 +434,10 @@ class HighlightersOverlay { // Save grid highlighter state. const { url } = this.inspector.target; const selector = await node.getUniqueSelector(); - this.state.grids.set(node, { selector, options, url }); - + this.state.grid = { selector, options, url }; + this.gridHighlighterShown = node; // Emit the NodeFront of the grid container element that the grid highlighter was - // shown for, and its options for testing the highlighter setting options. + // shown for. this.emit("grid-highlighter-shown", node, options); } catch (e) { this._handleRejection(e); @@ -474,24 +451,22 @@ class HighlightersOverlay { * The NodeFront of the grid container element to unhighlight. */ async hideGridHighlighter(node) { - if (!this.gridHighlighters.has(node)) { + if (!this.gridHighlighterShown || !this.highlighters.CssGridHighlighter) { return; } this._toggleRuleViewIcon(node, false, ".ruleview-grid"); - // Hide the highlighter and put it in the pool of extra grid highlighters - // so that it can be reused. - const highlighter = this.gridHighlighters.get(node); - await highlighter.hide(); - this.extraGridHighlighterPool.push(highlighter); - - this.state.grids.delete(node); - this.gridHighlighters.delete(node); + await this.highlighters.CssGridHighlighter.hide(); // Emit the NodeFront of the grid container element that the grid highlighter was // hidden for. - this.emit("grid-highlighter-hidden", node); + const nodeFront = this.gridHighlighterShown; + this.gridHighlighterShown = null; + this.emit("grid-highlighter-hidden", nodeFront, this.state.grid.options); + + // Erase grid highlighter state. + this.state.grid = {}; } /** @@ -597,16 +572,8 @@ class HighlightersOverlay { * Restores the saved grid highlighter state. */ async restoreGridState() { - // The NodeFronts that are used as the keys in the grid state Map are no longer in the - // tree after a reload. To clean up the grid state, we create a copy of the values of - // the grid state before restoring and clear it. - const values = [...this.state.grids.values()]; - this.state.grids.clear(); - try { - for (const gridState of values) { - await this.restoreState("grid", gridState, this.showGridHighlighter); - } + await this.restoreState("grid", this.state.grid, this.showGridHighlighter); } catch (e) { this._handleRejection(e); } @@ -646,23 +613,24 @@ class HighlightersOverlay { await showFunction(nodeFront, options); this.emit(`${name}-state-restored`, { restored: true }); - } else { - this.emit(`${name}-state-restored`, { restored: false }); } + + this.emit(`${name}-state-restored`, { restored: false }); } /** - * Get an instance of an in-context editor for the given type. - * - * In-context editors behave like highlighters but with added editing capabilities which - * need to write value changes back to something, like to properties in the Rule view. - * They typically exist in the context of the page, like the ShapesInContextEditor. - * - * @param {String} type - * Type of in-context editor. Currently supported: "shapesEditor" - * @return {Object|null} - * Reference to instance for given type of in-context editor or null. - */ + * Get an instance of an in-context editor for the given type. + * + * In-context editors behave like highlighters but with added editing capabilities which + * need to write value changes back to something, like to properties in the Rule view. + * They typically exist in the context of the page, like the ShapesInContextEditor. + * + * @param {String} type + * Type of in-context editor. Currently supported: "shapesEditor" + * + * @return {Object|null} + * Reference to instance for given type of in-context editor or null. + */ async getInContextEditor(type) { if (this.editors[type]) { return this.editors[type]; @@ -699,6 +667,8 @@ class HighlightersOverlay { * @return {Promise} that resolves to the highlighter */ async _getHighlighter(type) { + const utils = this.highlighterUtils; + if (this.highlighters[type]) { return this.highlighters[type]; } @@ -706,9 +676,9 @@ class HighlightersOverlay { let highlighter; try { - highlighter = await this.highlighterUtils.getHighlighterByType(type); + highlighter = await utils.getHighlighterByType(type); } catch (e) { - this._handleRejection(e); + // Ignore any error } if (!highlighter) { @@ -719,41 +689,6 @@ class HighlightersOverlay { return highlighter; } - /** - * Get a grid highlighter front for a given node. - * - * @param {NodeFront} node - * The NodeFront of the grid container element to highlight. - * @return {Promise} that resolves to the grid highlighter front. - */ - async _getGridHighlighter(node) { - if (this.gridHighlighters.has(node)) { - return this.gridHighlighters.get(node); - } - - let highlighter; - - // Attempt to use any grid highlighters already instantiated in the extra pool, - // otherwise, initialize a new grid highlighter. - if (this.extraGridHighlighterPool.length > 0) { - highlighter = this.extraGridHighlighterPool.pop(); - } else { - try { - highlighter = await this.highlighterUtils.getHighlighterByType( - "CssGridHighlighter"); - } catch (e) { - this._handleRejection(e); - } - } - - if (!highlighter) { - return null; - } - - this.gridHighlighters.set(node, highlighter); - return highlighter; - } - _handleRejection(error) { if (!this.destroyed) { console.error(error); @@ -843,7 +778,7 @@ class HighlightersOverlay { hideHighlighter(node); } } catch (e) { - this._handleRejection(e); + console.error(e); } } @@ -1033,44 +968,35 @@ class HighlightersOverlay { return; } - for (const node of this.gridHighlighters.keys()) { - await this._hideHighlighterIfDeadNode(node, this.hideGridHighlighter); - } - - await this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown, + this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown, this.hideFlexboxHighlighter); - await this._hideHighlighterIfDeadNode(this.flexItemHighlighterShown, + this._hideHighlighterIfDeadNode(this.flexItemHighlighterShown, this.hideFlexItemHighlighter); - await this._hideHighlighterIfDeadNode(this.shapesHighlighterShown, + this._hideHighlighterIfDeadNode(this.gridHighlighterShown, + this.hideGridHighlighter); + this._hideHighlighterIfDeadNode(this.shapesHighlighterShown, this.hideShapesHighlighter); } /** * Clear saved highlighter shown properties on will-navigate. */ - async onWillNavigate() { + onWillNavigate() { this.destroyEditors(); - // Store all the grid highlighters into the pool of reusable grid highlighters, and - // clear the Map of grid highlighters. - for (const highlighter of this.gridHighlighters.values()) { - this.extraGridHighlighterPool.push(highlighter); - } - - this.gridHighlighters.clear(); - this.boxModelHighlighterShown = null; this.flexboxHighlighterShown = null; this.flexItemHighlighterShown = null; this.geometryEditorHighlighterShown = null; + this.gridHighlighterShown = null; this.hoveredHighlighterShown = null; this.selectorHighlighterShown = null; this.shapesHighlighterShown = null; } /** - * Destroy and clean-up all instances of in-context editors. - */ + * Destroy and clean-up all instances of in-context editors. + */ destroyEditors() { for (const type in this.editors) { this.editors[type].off("show"); @@ -1081,27 +1007,6 @@ class HighlightersOverlay { this.editors = {}; } - /** - * Destroy all instances of the grid highlighters. - */ - destroyGridHighlighters() { - for (const highlighter of this.gridHighlighters.values()) { - highlighter.finalize(); - } - - for (const highlighter of this.extraGridHighlighterPool) { - highlighter.finalize(); - } - - this.gridHighlighters.clear(); - - this.gridHighlighters = null; - this.extraGridHighlighterPool = null; - } - - /** - * Destroy and clean-up all instances of highlighters. - */ destroyHighlighters() { for (const type in this.highlighters) { if (this.highlighters[type]) { @@ -1109,7 +1014,6 @@ class HighlightersOverlay { this.highlighters[type] = null; } } - this.highlighters = null; } @@ -1118,13 +1022,13 @@ class HighlightersOverlay { * all initialized highlighters. */ destroy() { + this.destroyHighlighters(); + this.destroyEditors(); + + // Remove inspector events. this.inspector.off("markupmutation", this.onMarkupMutation); this.inspector.target.off("will-navigate", this.onWillNavigate); - this.destroyEditors(); - this.destroyGridHighlighters(); - this.destroyHighlighters(); - this._lastHovered = null; this.inspector = null; @@ -1136,6 +1040,7 @@ class HighlightersOverlay { this.flexboxHighlighterShown = null; this.flexItemHighlighterShown = null; this.geometryEditorHighlighterShown = null; + this.gridHighlighterShown = null; this.hoveredHighlighterShown = null; this.selectorHighlighterShown = null; this.shapesHighlighterShown = null; diff --git a/devtools/client/preferences/devtools-client.js b/devtools/client/preferences/devtools-client.js index a65e6fe83e37..ec8791c05125 100644 --- a/devtools/client/preferences/devtools-client.js +++ b/devtools/client/preferences/devtools-client.js @@ -82,8 +82,6 @@ pref("devtools.gridinspector.gridOutlineMaxRows", 50); pref("devtools.gridinspector.showGridAreas", false); pref("devtools.gridinspector.showGridLineNumbers", false); pref("devtools.gridinspector.showInfiniteLines", false); -// Max number of grid highlighters that can be displayed -pref("devtools.gridinspector.maxHighlighters", 1); // Whether or not the box model panel is opened in the layout view pref("devtools.layout.boxmodel.opened", true); From b560625580a81e5d5f41aaae287f2083bf418e8e Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Mon, 10 Sep 2018 18:47:32 -0400 Subject: [PATCH 14/44] Bug 1490130 - fix embedjs.py script to use relative paths to source files; r=froydnj --- js/src/builtin/embedjs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/builtin/embedjs.py b/js/src/builtin/embedjs.py index afc334f7029f..fdec789e9367 100644 --- a/js/src/builtin/embedjs.py +++ b/js/src/builtin/embedjs.py @@ -40,6 +40,7 @@ from __future__ import with_statement import re import sys import os +import mozpack.path as mozpath import subprocess import shlex import which @@ -86,8 +87,11 @@ namespace %(namespace)s { def embed(cxx, preprocessorOption, cppflags, msgs, sources, c_out, js_out, namespace, env): + objdir = os.getcwd() + # Use relative pathnames to avoid path translation issues in WSL. combinedSources = '\n'.join([msgs] + ['#include "%(s)s"' % - {'s': source} for source in sources]) + {'s': mozpath.relpath(source, objdir)} + for source in sources]) args = cppflags + ['-D%(k)s=%(v)s' % {'k': k, 'v': env[k]} for k in env] preprocessed = preprocess(cxx, preprocessorOption, combinedSources, args) processed = '\n'.join([line for line in preprocessed.splitlines() if From 6e443c9a8684f22a6cc3cf93d8199a56ba26dbde Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Mon, 10 Sep 2018 18:57:40 -0400 Subject: [PATCH 15/44] Bug 1414060 - move NONASCII to moz.configure; r=mshal --- build/moz.configure/windows.configure | 4 ++++ js/src/old-configure.in | 6 ------ old-configure.in | 6 ------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure index 517c68e3fa5d..1f57db66fbec 100644 --- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -483,3 +483,7 @@ def alter_path(sdk_bin_path): set_config('PATH', alter_path) check_prog('MAKECAB', ('makecab.exe',)) + +# Make sure that the build system can handle non-ASCII characters in +# environment variables to prevent silent breakage on non-English systems. +set_config('NONASCII', b'\241\241') diff --git a/js/src/old-configure.in b/js/src/old-configure.in index ba875c359653..9b6cea06c1b6 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -1662,12 +1662,6 @@ else fi AC_SUBST(CL_INCLUDES_PREFIX) rm -f dummy-hello.c - - dnl Make sure that the build system can handle non-ASCII characters - dnl in environment variables to prevent it from breaking silently on - dnl non-English systems. - NONASCII=$'\241\241' - AC_SUBST(NONASCII) fi fi diff --git a/old-configure.in b/old-configure.in index 2ce37d88c3fb..2fd070540b38 100644 --- a/old-configure.in +++ b/old-configure.in @@ -3805,12 +3805,6 @@ else fi AC_SUBST(CL_INCLUDES_PREFIX) rm -f dummy-hello.c - - dnl Make sure that the build system can handle non-ASCII characters - dnl in environment variables to prevent it from breaking silently on - dnl non-English systems. - NONASCII=$'\241\241' - AC_SUBST(NONASCII) fi fi fi # COMPILE_ENVIRONMENT From 30ed2d528b68e8cdc6caf137d00c4e4e165bbce9 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Mon, 10 Sep 2018 18:57:40 -0400 Subject: [PATCH 16/44] Bug 1414060 - give json.dump the correct encoding; r=mshal --- python/mozbuild/mozbuild/backend/configenvironment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index aefca071e4de..76eec5889e42 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -241,9 +241,10 @@ class PartialConfigDict(object): return existing_files def _write_file(self, key, value): + encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8' filename = mozpath.join(self._datadir, key) with FileAvoidWrite(filename) as fh: - json.dump(value, fh, indent=4) + json.dump(value, fh, indent=4, encoding=encoding) return filename def _fill_group(self, values): From 373eb37d44ee305f386a4643ae68969c3d33f47a Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 11 Sep 2018 00:57:53 +0200 Subject: [PATCH 17/44] Bug 1478485 - Use the stored CB on abs.pos. grid items when calculating used offset values. r=dholbert --- layout/generic/nsGridContainerFrame.h | 3 +-- layout/style/nsComputedDOMStyle.cpp | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index 835a789dae32..c998557d5acd 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -159,8 +159,7 @@ public: /** * Return the containing block for aChild which MUST be an abs.pos. child - * of a grid container. This is just a helper method for - * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere. + * of a grid container and that container must have been reflowed. */ static const nsRect& GridItemCB(nsIFrame* aChild); diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index ad2ed4ee1b3d..53cc570b17ea 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -4162,6 +4162,10 @@ nsComputedDOMStyle::GetAbsoluteOffset(mozilla::Side aSide) if (scrollFrame) { scrollbarSizes = scrollFrame->GetActualScrollbarSizes(); } + } else if (container->IsGridContainerFrame() && + (mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) { + containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame); + rect.MoveBy(-containerRect.x, -containerRect.y); } nscoord offset = 0; From 2e13ef79fbcfcc4dabec1c8bebe39154c51b3e93 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 11 Sep 2018 09:36:07 +1000 Subject: [PATCH 18/44] Bug 1489744 - Fix a bounds violation crash in the prefs parser. r=glandium Currently, if a get_char() call returns EOF, the index moves beyond the buffer's bounds and get_char() cannot be called again without triggering a panic. As a result, everywhere that encounters an EOF and then does subsequent parsing ungets the EOF... except there was one place that failed to do that: the match case for CharKind::Slash in get_token(). This meant that a single '/' at the end of the input could trigger a bounds violation (but only if it is the start of a new token). This EOF-unget requirement is subtle and easy to get wrong, so this patch eliminates it. get_char() now can be called repeatedly after an EOF, and will return EOF on each subsequent call. This means that some of the existing unget_char() calls can be removed. Some others are still necessary to get line numbers correct in error messages, but the outcome of mishandled cases is now much less drastic -- an incorrect line number in an error message instead of a panic. The patch also clarifies a couple of related comments. --HG-- extra : rebase_source : 62a3f07bb83b95495b2975724876b619a33b5c9d --- modules/libpref/parser/src/lib.rs | 70 +++++++++++++++------------ modules/libpref/test/gtest/Parser.cpp | 9 ++-- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/modules/libpref/parser/src/lib.rs b/modules/libpref/parser/src/lib.rs index 8d8b9a6125e4..5ea7650252f5 100644 --- a/modules/libpref/parser/src/lib.rs +++ b/modules/libpref/parser/src/lib.rs @@ -54,16 +54,10 @@ // This parser uses several important optimizations. // -// - Because "'\0' means EOF" is part of the grammar (see above) we can match -// EOF as a normal char/token, which means we can avoid a typical "do we -// still have chars remaining?" test in get_char(), which gives a speedup -// because get_char() is a very hot function. (Actually, Rust would -// bounds-check this function anyway, so we have get_char_unchecked() which -// is used for the two hottest call sites.) -// -// This also means EOF is representable by a u8. If EOF was represented by an -// out-of-band value such as -1 or 256, we'd have to return a larger type -// such as u16 or i16 from get_char(). +// - Because "'\0' means EOF" is part of the grammar (see above), EOF is +// representable by a u8. If EOF was represented by an out-of-band value such +// as -1 or 256, we'd have to return a larger type such as u16 or i16 from +// get_char(). // // - When starting a new token, it uses a lookup table with the first char, // which quickly identifies what kind of token it will be. Furthermore, if @@ -192,7 +186,7 @@ enum Token { #[derive(Clone, Copy, PartialEq)] enum CharKind { // These are ordered by frequency. See the comment in GetToken(). - SingleChar, // Unambiguous single-char tokens: [()+,-] + SingleChar, // Unambiguous single-char tokens: [()+,-] or EOF SpaceNL, // [\t\v\f \n] Keyword, // [A-Za-z_] Quote, // ["'] @@ -535,13 +529,21 @@ impl<'t> Parser<'t> { #[inline(always)] fn get_char(&mut self) -> u8 { - let c = self.buf[self.i]; - self.i += 1; - c + // We do the bounds check ourselves so we can return EOF on failure. + // (Although the buffer is guaranteed to end in an EOF char, we might + // go one char past that, whereupon we must return EOF again.) + if self.i < self.buf.len() { + let c = unsafe { *self.buf.get_unchecked(self.i) }; + self.i += 1; + c + } else { + debug_assert!(self.i == self.buf.len()); + EOF + } } - // This function skips the bounds check in non-optimized builds. Using it - // at the hottest two call sites gives a ~15% parsing speed boost. + // This function skips the bounds check in optimized builds. Using it at + // the hottest two call sites gives a ~15% parsing speed boost. #[inline(always)] unsafe fn get_char_unchecked(&mut self) -> u8 { debug_assert!(self.i < self.buf.len()); @@ -568,9 +570,11 @@ impl<'t> Parser<'t> { #[inline(always)] fn match_single_line_comment(&mut self) { loop { - // To reach here, the previous char must have been '/', and - // assertions elsewhere ensure that there must be at least one - // subsequent char (the '\0' for EOF). + // To reach here, the previous char must have been '/' (if this is + // the first loop iteration) or non-special (if this is the second + // or subsequent iteration), and assertions elsewhere ensure that + // there must be at least one subsequent char after those chars + // (the '\0' for EOF). let c = unsafe { self.get_char_unchecked() }; // All the special chars have value <= b'\r'. @@ -588,8 +592,6 @@ impl<'t> Parser<'t> { break; } EOF => { - // Unget EOF so subsequent calls to get_char() are safe. - self.unget_char(); break; } _ => continue @@ -615,8 +617,6 @@ impl<'t> Parser<'t> { self.match_char(b'\n'); } EOF => { - // Unget EOF so subsequent calls to get_char() are safe. - self.unget_char(); return false } _ => continue @@ -630,11 +630,10 @@ impl<'t> Parser<'t> { for _ in 0..ndigits { value = value << 4; match self.get_char() { - c @ b'0'... b'9' => value += (c - b'0') as u16, + c @ b'0'...b'9' => value += (c - b'0') as u16, c @ b'A'...b'F' => value += (c - b'A') as u16 + 10, c @ b'a'...b'f' => value += (c - b'a') as u16 + 10, _ => { - // Unget in case the char was a closing quote or EOF. self.unget_char(); return None; } @@ -716,7 +715,15 @@ impl<'t> Parser<'t> { return Token::Error("unterminated /* comment"); } } - _ => return Token::Error("expected '/' or '*' after '/'") + c @ _ => { + if c == b'\n' || c == b'\r' { + // Unget the newline char; the outer loop will + // reget it and adjust self.line_num + // appropriately. + self.unget_char(); + } + return Token::Error("expected '/' or '*' after '/'"); + } } continue; } @@ -871,9 +878,12 @@ impl<'t> Parser<'t> { } continue; // We don't want to str_buf.push(c2) below. } - _ => { - // Unget in case the char is an EOF. - self.unget_char(); + c @ _ => { + if c == b'\n' || c == b'\r' { + // Unget the newline char; the outer loop will + // reget it and adjust self.line_num appropriately. + self.unget_char(); + } self.string_error_token( &mut token, "unexpected escape sequence character after '\\'"); continue; @@ -894,8 +904,6 @@ impl<'t> Parser<'t> { } } else if c == EOF { - // Unget EOF so subsequent calls to get_char() are safe. - self.unget_char(); self.string_error_token(&mut token, "unterminated string literal"); break; diff --git a/modules/libpref/test/gtest/Parser.cpp b/modules/libpref/test/gtest/Parser.cpp index 8fe94a885b16..4a6cefa74ded 100644 --- a/modules/libpref/test/gtest/Parser.cpp +++ b/modules/libpref/test/gtest/Parser.cpp @@ -293,11 +293,14 @@ pref("string.bad-keyword", TRUE); "test:3: prefs parse error: unterminated /* comment\n" ); - // Malformed comment. + // Malformed comments (single slashes), followed by whitespace, newline, EOF. DEFAULT(R"( -/ comment - )", +/ comment; +/ +; /)", "test:2: prefs parse error: expected '/' or '*' after '/'\n" + "test:3: prefs parse error: expected '/' or '*' after '/'\n" + "test:4: prefs parse error: expected '/' or '*' after '/'\n" ); // C++-style comment ending in EOF (1). From 5826bb91b98416a952319e6783be6e2af7487fab Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 10 Sep 2018 20:02:01 -0400 Subject: [PATCH 19/44] Bug 1489996 - Update pdf.js to version 2.0.843. r=bdahl --- browser/extensions/pdfjs/README.mozilla | 4 +- browser/extensions/pdfjs/content/build/pdf.js | 36 +- .../pdfjs/content/build/pdf.worker.js | 284 ++--- .../extensions/pdfjs/content/web/viewer.html | 6 +- .../extensions/pdfjs/content/web/viewer.js | 986 ++++++++++-------- browser/extensions/pdfjs/moz.yaml | 2 +- .../locales/en-US/pdfviewer/viewer.properties | 8 + 7 files changed, 726 insertions(+), 600 deletions(-) diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 3fbbb6c55850..59dcbe84cd11 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ This is the PDF.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 2.0.815 +Current extension version is: 2.0.843 -Taken from upstream commit: d6927376 +Taken from upstream commit: bf368f3a diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index d386500ce8a3..b7b3ab6092a0 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; -var pdfjsVersion = '2.0.815'; -var pdfjsBuild = 'd6927376'; +var pdfjsVersion = '2.0.843'; +var pdfjsBuild = 'bf368f3a'; var pdfjsSharedUtil = __w_pdfjs_require__(1); var pdfjsDisplayAPI = __w_pdfjs_require__(7); var pdfjsDisplayTextLayer = __w_pdfjs_require__(19); @@ -4226,7 +4226,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { } return worker.messageHandler.sendWithPromise('GetDocRequest', { docId, - apiVersion: '2.0.815', + apiVersion: '2.0.843', source: { data: source.data, url: source.url, @@ -5553,8 +5553,8 @@ var InternalRenderTask = function InternalRenderTaskClosure() { }(); var version, build; { - exports.version = version = '2.0.815'; - exports.build = build = 'd6927376'; + exports.version = version = '2.0.843'; + exports.build = build = 'bf368f3a'; } exports.getDocument = getDocument; exports.LoopbackPort = LoopbackPort; @@ -6354,7 +6354,7 @@ var CanvasGraphics = function CanvasGraphicsClosure() { if (canvasCtx) { addContextCurrentTransform(canvasCtx); } - this.cachedGetSinglePixelWidth = null; + this._cachedGetSinglePixelWidth = null; } function putBinaryImageData(ctx, imgData) { if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) { @@ -6829,12 +6829,12 @@ var CanvasGraphics = function CanvasGraphicsClosure() { this.current = this.stateStack.pop(); this.ctx.restore(); this.pendingClip = null; - this.cachedGetSinglePixelWidth = null; + this._cachedGetSinglePixelWidth = null; } }, transform: function CanvasGraphics_transform(a, b, c, d, e, f) { this.ctx.transform(a, b, c, d, e, f); - this.cachedGetSinglePixelWidth = null; + this._cachedGetSinglePixelWidth = null; }, constructPath: function CanvasGraphics_constructPath(ops, args) { var ctx = this.ctx; @@ -7177,7 +7177,7 @@ var CanvasGraphics = function CanvasGraphicsClosure() { if (scale === 0 || lineWidth === 0) { var fillStrokeMode = current.textRenderingMode & _util.TextRenderingMode.FILL_STROKE_MASK; if (fillStrokeMode === _util.TextRenderingMode.STROKE || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) { - this.cachedGetSinglePixelWidth = null; + this._cachedGetSinglePixelWidth = null; lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR; } } else { @@ -7269,7 +7269,7 @@ var CanvasGraphics = function CanvasGraphicsClosure() { if (isTextInvisible || fontSize === 0) { return; } - this.cachedGetSinglePixelWidth = null; + this._cachedGetSinglePixelWidth = null; ctx.save(); ctx.transform.apply(ctx, current.textMatrix); ctx.translate(current.x, current.y); @@ -7737,14 +7737,12 @@ var CanvasGraphics = function CanvasGraphicsClosure() { } ctx.beginPath(); }, - getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) { - if (this.cachedGetSinglePixelWidth === null) { - this.ctx.save(); - var inverse = this.ctx.mozCurrentTransformInverse; - this.ctx.restore(); - this.cachedGetSinglePixelWidth = Math.sqrt(Math.max(inverse[0] * inverse[0] + inverse[1] * inverse[1], inverse[2] * inverse[2] + inverse[3] * inverse[3])); + getSinglePixelWidth(scale) { + if (this._cachedGetSinglePixelWidth === null) { + const inverse = this.ctx.mozCurrentTransformInverse; + this._cachedGetSinglePixelWidth = Math.sqrt(Math.max(inverse[0] * inverse[0] + inverse[1] * inverse[1], inverse[2] * inverse[2] + inverse[3] * inverse[3])); } - return this.cachedGetSinglePixelWidth; + return this._cachedGetSinglePixelWidth; }, getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) { var transform = this.ctx.mozCurrentTransform; @@ -10015,8 +10013,8 @@ var renderTextLayer = function renderTextLayerClosure() { let fontFamily = textDiv.style.fontFamily; if (fontSize !== this._layoutTextLastFontSize || fontFamily !== this._layoutTextLastFontFamily) { this._layoutTextCtx.font = fontSize + ' ' + fontFamily; - this._lastFontSize = fontSize; - this._lastFontFamily = fontFamily; + this._layoutTextLastFontSize = fontSize; + this._layoutTextLastFontFamily = fontFamily; } let width = this._layoutTextCtx.measureText(textDiv.textContent).width; let transform = ''; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 903de55cd2f8..8b0b00eb5bef 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; -var pdfjsVersion = '2.0.815'; -var pdfjsBuild = 'd6927376'; +var pdfjsVersion = '2.0.843'; +var pdfjsBuild = 'bf368f3a'; var pdfjsCoreWorker = __w_pdfjs_require__(1); exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; @@ -327,7 +327,7 @@ var WorkerMessageHandler = { var cancelXHRs = null; var WorkerTasks = []; let apiVersion = docParams.apiVersion; - let workerVersion = '2.0.815'; + let workerVersion = '2.0.843'; if (apiVersion !== workerVersion) { throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); } @@ -22285,7 +22285,7 @@ exports.CMapFactory = CMapFactory; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getFontType = exports.ProblematicCharRanges = exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.PRIVATE_USE_OFFSET_END = exports.PRIVATE_USE_OFFSET_START = exports.SEAC_ANALYSIS_ENABLED = undefined; +exports.getFontType = exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.SEAC_ANALYSIS_ENABLED = undefined; var _util = __w_pdfjs_require__(2); @@ -22307,11 +22307,9 @@ var _stream = __w_pdfjs_require__(14); var _type1_parser = __w_pdfjs_require__(38); -var PRIVATE_USE_OFFSET_START = 0xE000; -var PRIVATE_USE_OFFSET_END = 0xF8FF; -var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false; +const PRIVATE_USE_AREAS = [[0xE000, 0xF8FF], [0x100000, 0x10FFFD]]; var PDF_GLYPH_SPACE_UNITS = 1000; -var SEAC_ANALYSIS_ENABLED = false; +var SEAC_ANALYSIS_ENABLED = true; var FontFlags = { FixedPitch: 1, Serif: 2, @@ -22590,7 +22588,6 @@ var OpenTypeFileBuilder = function OpenTypeFileBuilderClosure() { }; return OpenTypeFileBuilder; }(); -var ProblematicCharRanges = new Int32Array([0x0000, 0x0020, 0x007F, 0x00A1, 0x00AD, 0x00AE, 0x0600, 0x0780, 0x08A0, 0x10A0, 0x1780, 0x1800, 0x1C00, 0x1C50, 0x2000, 0x2010, 0x2011, 0x2012, 0x2028, 0x2030, 0x205F, 0x2070, 0x25CC, 0x25CD, 0x3000, 0x3001, 0x3164, 0x3165, 0xAA60, 0xAA80, 0xD800, 0xE000, 0xFFF0, 0x10000]); var Font = function FontClosure() { function Font(name, file, properties) { var charCode; @@ -22794,58 +22791,33 @@ var Font = function FontClosure() { } return toFontChar; } - function isProblematicUnicodeLocation(code) { - var i = 0, - j = ProblematicCharRanges.length - 1; - while (i < j) { - var c = i + j + 1 >> 1; - if (code < ProblematicCharRanges[c]) { - j = c - 1; - } else { - i = c; - } - } - return !(i & 1); - } - function adjustMapping(charCodeToGlyphId, properties, missingGlyphs) { - var toUnicode = properties.toUnicode; - var isSymbolic = !!(properties.flags & FontFlags.Symbolic); - var isIdentityUnicode = properties.toUnicode instanceof IdentityToUnicodeMap; + function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) { var newMap = Object.create(null); var toFontChar = []; - var usedFontCharCodes = []; - var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START; + var privateUseAreaIndex = 0; + var nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0]; + var privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1]; for (var originalCharCode in charCodeToGlyphId) { originalCharCode |= 0; var glyphId = charCodeToGlyphId[originalCharCode]; - if (missingGlyphs[glyphId]) { + if (!hasGlyph(glyphId)) { continue; } - var fontCharCode = originalCharCode; - var hasUnicodeValue = false; - if (!isIdentityUnicode && toUnicode.has(originalCharCode)) { - hasUnicodeValue = true; - var unicode = toUnicode.get(fontCharCode); - if (unicode.length === 1) { - fontCharCode = unicode.charCodeAt(0); + if (nextAvailableFontCharCode > privateUseOffetEnd) { + privateUseAreaIndex++; + if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) { + (0, _util.warn)('Ran out of space in font private use area.'); + break; } + nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0]; + privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1]; } - if (usedFontCharCodes[fontCharCode] !== undefined || isProblematicUnicodeLocation(fontCharCode) || isSymbolic && !hasUnicodeValue) { - do { - if (nextAvailableFontCharCode > PRIVATE_USE_OFFSET_END) { - (0, _util.warn)('Ran out of space in font private use area.'); - break; - } - fontCharCode = nextAvailableFontCharCode++; - if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) { - fontCharCode = 0xF020; - nextAvailableFontCharCode = fontCharCode + 1; - } - } while (usedFontCharCodes[fontCharCode] !== undefined); + var fontCharCode = nextAvailableFontCharCode++; + if (glyphId === 0) { + glyphId = newGlyphZeroId; } newMap[fontCharCode] = glyphId; toFontChar[originalCharCode] = fontCharCode; - usedFontCharCodes[fontCharCode] = true; } return { toFontChar, @@ -23032,6 +23004,9 @@ var Font = function FontClosure() { throw new _util.FormatError('Unicode ranges Bits > 123 are reserved for internal usage'); } } + if (lastCharIndex > 0xFFFF) { + lastCharIndex = 0xFFFF; + } } else { firstCharIndex = 0; lastCharIndex = 255; @@ -23585,13 +23560,12 @@ var Font = function FontClosure() { data[offset + 1] = value >> 1 & 0xFF; }; } + var numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs; var locaData = loca.data; - var locaDataSize = itemSize * (1 + numGlyphs); - if (locaData.length !== locaDataSize) { - locaData = new Uint8Array(locaDataSize); - locaData.set(loca.data.subarray(0, locaDataSize)); - loca.data = locaData; - } + var locaDataSize = itemSize * (1 + numGlyphsOut); + locaData = new Uint8Array(locaDataSize); + locaData.set(loca.data.subarray(0, locaDataSize)); + loca.data = locaData; var oldGlyfData = glyf.data; var oldGlyfDataLength = oldGlyfData.length; var newGlyfData = new Uint8Array(oldGlyfDataLength); @@ -23600,8 +23574,7 @@ var Font = function FontClosure() { var missingGlyphs = Object.create(null); itemEncode(locaData, 0, writeOffset); var i, j; - var locaCount = dupFirstEntry ? numGlyphs - 1 : numGlyphs; - for (i = 0, j = itemSize; i < locaCount; i++, j += itemSize) { + for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { var endOffset = itemDecode(locaData, j); if (endOffset === 0) { endOffset = startOffset; @@ -23626,7 +23599,7 @@ var Font = function FontClosure() { } if (writeOffset === 0) { var simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]); - for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { + for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) { itemEncode(locaData, j, simpleGlyph.length); } glyf.data = simpleGlyph; @@ -24045,7 +24018,14 @@ var Font = function FontClosure() { } font.pos = (font.start || 0) + tables['maxp'].offset; var version = font.getInt32(); - var numGlyphs = font.getUint16(); + const numGlyphs = font.getUint16(); + let numGlyphsOut = numGlyphs + 1; + let dupFirstEntry = true; + if (numGlyphsOut > 0xFFFF) { + dupFirstEntry = false; + numGlyphsOut = numGlyphs; + (0, _util.warn)('Not enough space in glyfs to duplicate first glyph.'); + } var maxFunctionDefs = 0; var maxSizeOfInstructions = 0; if (version >= 0x00010000 && tables['maxp'].length >= 22) { @@ -24060,20 +24040,15 @@ var Font = function FontClosure() { font.pos += 4; maxSizeOfInstructions = font.getUint16(); } - var dupFirstEntry = false; - if (properties.type === 'CIDFontType2' && properties.toUnicode && properties.toUnicode.get(0) > '\u0000') { - dupFirstEntry = true; - numGlyphs++; - tables['maxp'].data[4] = numGlyphs >> 8; - tables['maxp'].data[5] = numGlyphs & 255; - } + tables['maxp'].data[4] = numGlyphsOut >> 8; + tables['maxp'].data[5] = numGlyphsOut & 255; var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'], tables['cvt '], maxFunctionDefs); if (!hintsValid) { delete tables['fpgm']; delete tables['prep']; delete tables['cvt ']; } - sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphs); + sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut); if (!tables['head']) { throw new _util.FormatError('Required "head" table is not found'); } @@ -24105,11 +24080,12 @@ var Font = function FontClosure() { this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm; this.descent = metricsOverride.descent / metricsOverride.unitsPerEm; if (tables['post']) { - var valid = readPostScriptTable(tables['post'], properties, numGlyphs); - if (!valid) { - tables['post'] = null; - } + readPostScriptTable(tables['post'], properties, numGlyphs); } + tables['post'] = { + tag: 'post', + data: createPostTable(properties) + }; var charCodeToGlyphId = [], charCode; function hasGlyph(glyphId) { @@ -24132,9 +24108,6 @@ var Font = function FontClosure() { charCodeToGlyphId[charCode] = glyphId; } }); - if (dupFirstEntry && (isCidToGidMapEmpty || !charCodeToGlyphId[0])) { - charCodeToGlyphId[0] = numGlyphs - 1; - } } else { var cmapTable = readCmapTable(tables['cmap'], font, this.isSymbolicFont, properties.hasEncoding); var cmapPlatformId = cmapTable.platformId; @@ -24202,11 +24175,15 @@ var Font = function FontClosure() { if (charCodeToGlyphId.length === 0) { charCodeToGlyphId[0] = 0; } - var newMapping = adjustMapping(charCodeToGlyphId, properties, missingGlyphs); + let glyphZeroId = numGlyphsOut - 1; + if (!dupFirstEntry) { + glyphZeroId = 0; + } + var newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId); this.toFontChar = newMapping.toFontChar; tables['cmap'] = { tag: 'cmap', - data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphs) + data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut) }; if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) { tables['OS/2'] = { @@ -24214,17 +24191,12 @@ var Font = function FontClosure() { data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride) }; } - if (!tables['post']) { - tables['post'] = { - tag: 'post', - data: createPostTable(properties) - }; - } if (!isTrueType) { try { cffFile = new _stream.Stream(tables['CFF '].data); var parser = new _cff_parser.CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED); cff = parser.parse(); + cff.duplicateFirstGlyph(); var compiler = new _cff_parser.CFFCompiler(cff); tables['CFF '].data = compiler.compile(); } catch (e) { @@ -24251,8 +24223,12 @@ var Font = function FontClosure() { if (properties.builtInEncoding) { adjustToUnicode(properties, properties.builtInEncoding); } + let glyphZeroId = 1; + if (font instanceof CFFFont) { + glyphZeroId = font.numGlyphs - 1; + } var mapping = font.getGlyphMapping(properties); - var newMapping = adjustMapping(mapping, properties, Object.create(null)); + var newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font), glyphZeroId); this.toFontChar = newMapping.toFontChar; var numGlyphs = font.numGlyphs; function getCharCodes(charCodeToGlyphId, glyphId) { @@ -24403,11 +24379,11 @@ var Font = function FontClosure() { var seac = this.seacMap[charcode]; fontCharCode = seac.baseFontCharCode; accent = { - fontChar: String.fromCharCode(seac.accentFontCharCode), + fontChar: String.fromCodePoint(seac.accentFontCharCode), offset: seac.accentOffset }; } - var fontChar = String.fromCharCode(fontCharCode); + var fontChar = typeof fontCharCode === 'number' ? String.fromCodePoint(fontCharCode) : ''; var glyph = this.glyphCache[charcode]; if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) { glyph = new Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont); @@ -24669,6 +24645,16 @@ var Type1Font = function Type1FontClosure() { } return type1FontGlyphMapping(properties, builtInEncoding, glyphNames); }, + hasGlyphId: function Type1Font_hasGlyphID(id) { + if (id < 0 || id >= this.numGlyphs) { + return false; + } + if (id === 0) { + return true; + } + var glyph = this.charstrings[id - 1]; + return glyph.charstring.length > 0; + }, getSeacs: function Type1Font_getSeacs(charstrings) { var i, ii; var seacMap = []; @@ -24746,12 +24732,7 @@ var Type1Font = function Type1FontClosure() { var charStringsIndex = new _cff_parser.CFFIndex(); charStringsIndex.add([0x8B, 0x0E]); for (i = 0; i < count; i++) { - var glyph = glyphs[i]; - if (glyph.length === 0) { - charStringsIndex.add([0x8B, 0x0E]); - continue; - } - charStringsIndex.add(glyph); + charStringsIndex.add(glyphs[i]); } cff.charStrings = charStringsIndex; var privateDict = new _cff_parser.CFFPrivateDict(); @@ -24787,6 +24768,7 @@ var CFFFont = function CFFFontClosure() { this.properties = properties; var parser = new _cff_parser.CFFParser(file, properties, SEAC_ANALYSIS_ENABLED); this.cff = parser.parse(); + this.cff.duplicateFirstGlyph(); var compiler = new _cff_parser.CFFCompiler(this.cff); this.seacs = this.cff.seacs; try { @@ -24827,29 +24809,19 @@ var CFFFont = function CFFFontClosure() { var encoding = cff.encoding ? cff.encoding.encoding : null; charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets); return charCodeToGlyphId; + }, + hasGlyphId: function CFFFont_hasGlyphID(id) { + return this.cff.hasGlyphId(id); } }; return CFFFont; }(); -(function checkSeacSupport() { - if (typeof navigator !== 'undefined' && /Windows/.test(navigator.userAgent)) { - exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED = true; - } -})(); -(function checkChromeWindows() { - if (typeof navigator !== 'undefined' && /Windows.*Chrome/.test(navigator.userAgent)) { - SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = true; - } -})(); exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED; -exports.PRIVATE_USE_OFFSET_START = PRIVATE_USE_OFFSET_START; -exports.PRIVATE_USE_OFFSET_END = PRIVATE_USE_OFFSET_END; exports.ErrorFont = ErrorFont; exports.Font = Font; exports.FontFlags = FontFlags; exports.ToUnicodeMap = ToUnicodeMap; exports.IdentityToUnicodeMap = IdentityToUnicodeMap; -exports.ProblematicCharRanges = ProblematicCharRanges; exports.getFontType = getFontType; /***/ }), @@ -24862,7 +24834,7 @@ exports.getFontType = getFontType; Object.defineProperty(exports, "__esModule", { value: true }); -exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined; +exports.CFFFDSelect = exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined; var _util = __w_pdfjs_require__(2); @@ -25626,20 +25598,16 @@ var CFFParser = function CFFParserClosure() { return new CFFEncoding(predefined, format, encoding, raw); }, parseFDSelect: function CFFParser_parseFDSelect(pos, length) { - var start = pos; var bytes = this.bytes; var format = bytes[pos++]; - var fdSelect = [], - rawBytes; - var i, - invalidFirstGID = false; + var fdSelect = []; + var i; switch (format) { case 0: for (i = 0; i < length; ++i) { var id = bytes[pos++]; fdSelect.push(id); } - rawBytes = bytes.subarray(start, pos); break; case 3: var rangesCount = bytes[pos++] << 8 | bytes[pos++]; @@ -25647,7 +25615,6 @@ var CFFParser = function CFFParserClosure() { var first = bytes[pos++] << 8 | bytes[pos++]; if (i === 0 && first !== 0) { (0, _util.warn)('parseFDSelect: The first range must have a first GID of 0' + ' -- trying to recover.'); - invalidFirstGID = true; first = 0; } var fdIndex = bytes[pos++]; @@ -25657,10 +25624,6 @@ var CFFParser = function CFFParserClosure() { } } pos += 2; - rawBytes = bytes.subarray(start, pos); - if (invalidFirstGID) { - rawBytes[3] = rawBytes[4] = 0; - } break; default: throw new _util.FormatError(`parseFDSelect: Unknown format "${format}".`); @@ -25668,7 +25631,7 @@ var CFFParser = function CFFParserClosure() { if (fdSelect.length !== length) { throw new _util.FormatError('parseFDSelect: Invalid font data.'); } - return new CFFFDSelect(fdSelect, rawBytes); + return new CFFFDSelect(format, fdSelect); } }; return CFFParser; @@ -25687,6 +25650,26 @@ var CFF = function CFFClosure() { this.fdSelect = null; this.isCIDFont = false; } + CFF.prototype = { + duplicateFirstGlyph: function CFF_duplicateFirstGlyph() { + if (this.charStrings.count >= 65535) { + (0, _util.warn)('Not enough space in charstrings to duplicate first glyph.'); + return; + } + var glyphZero = this.charStrings.get(0); + this.charStrings.add(glyphZero); + if (this.isCIDFont) { + this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]); + } + }, + hasGlyphId: function CFF_hasGlyphID(id) { + if (id < 0 || id >= this.charStrings.count) { + return false; + } + var glyph = this.charStrings.get(id); + return glyph.length > 0; + } + }; return CFF; }(); var CFFHeader = function CFFHeaderClosure() { @@ -25873,9 +25856,9 @@ var CFFEncoding = function CFFEncodingClosure() { return CFFEncoding; }(); var CFFFDSelect = function CFFFDSelectClosure() { - function CFFFDSelect(fdSelect, raw) { + function CFFFDSelect(format, fdSelect) { + this.format = format; this.fdSelect = fdSelect; - this.raw = raw; } CFFFDSelect.prototype = { getFDIndex: function CFFFDSelect_get(glyphIndex) { @@ -25966,6 +25949,7 @@ var CFFCompiler = function CFFCompilerClosure() { } } } + cff.topDict.setByName('charset', 0); var compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont); output.add(compiled.output); var topDictTracker = compiled.trackers[0]; @@ -25982,21 +25966,15 @@ var CFFCompiler = function CFFCompilerClosure() { output.add(encoding); } } - if (cff.charset && cff.topDict.hasName('charset')) { - if (cff.charset.predefined) { - topDictTracker.setEntryLocation('charset', [cff.charset.format], output); - } else { - var charset = this.compileCharset(cff.charset); - topDictTracker.setEntryLocation('charset', [output.length], output); - output.add(charset); - } - } + var charset = this.compileCharset(cff.charset); + topDictTracker.setEntryLocation('charset', [output.length], output); + output.add(charset); var charStrings = this.compileCharStrings(cff.charStrings); topDictTracker.setEntryLocation('CharStrings', [output.length], output); output.add(charStrings); if (cff.isCIDFont) { topDictTracker.setEntryLocation('FDSelect', [output.length], output); - var fdSelect = this.compileFDSelect(cff.fdSelect.raw); + var fdSelect = this.compileFDSelect(cff.fdSelect); output.add(fdSelect); compiled = this.compileTopDicts(cff.fdArray, output.length, true); topDictTracker.setEntryLocation('FDArray', [output.length], output); @@ -26191,16 +26169,55 @@ var CFFCompiler = function CFFCompilerClosure() { this.out.writeByteArray(this.compileIndex(globalSubrIndex)); }, compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) { - return this.compileIndex(charStrings); + var charStringsIndex = new CFFIndex(); + for (var i = 0; i < charStrings.count; i++) { + var glyph = charStrings.get(i); + if (glyph.length === 0) { + charStringsIndex.add(new Uint8Array([0x8B, 0x0E])); + continue; + } + charStringsIndex.add(glyph); + } + return this.compileIndex(charStringsIndex); }, compileCharset: function CFFCompiler_compileCharset(charset) { - return this.compileTypedArray(charset.raw); + let length = 1 + (this.cff.charStrings.count - 1) * 2; + let out = new Uint8Array(length); + return this.compileTypedArray(out); }, compileEncoding: function CFFCompiler_compileEncoding(encoding) { return this.compileTypedArray(encoding.raw); }, compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) { - return this.compileTypedArray(fdSelect); + let format = fdSelect.format; + let out, i; + switch (format) { + case 0: + out = new Uint8Array(1 + fdSelect.fdSelect.length); + out[0] = format; + for (i = 0; i < fdSelect.fdSelect.length; i++) { + out[i + 1] = fdSelect.fdSelect[i]; + } + break; + case 3: + let start = 0; + let lastFD = fdSelect.fdSelect[0]; + let ranges = [format, 0, 0, start >> 8 & 0xFF, start & 0xFF, lastFD]; + for (i = 1; i < fdSelect.fdSelect.length; i++) { + let currentFD = fdSelect.fdSelect[i]; + if (currentFD !== lastFD) { + ranges.push(i >> 8 & 0xFF, i & 0xFF, currentFD); + lastFD = currentFD; + } + } + let numRanges = (ranges.length - 3) / 3; + ranges[1] = numRanges >> 8 & 0xFF; + ranges[2] = numRanges & 0xFF; + ranges.push(i >> 8 & 0xFF, i & 0xFF); + out = new Uint8Array(ranges); + break; + } + return this.compileTypedArray(out); }, compileTypedArray: function CFFCompiler_compileTypedArray(data) { var out = []; @@ -26271,6 +26288,7 @@ exports.CFFCharset = CFFCharset; exports.CFFTopDict = CFFTopDict; exports.CFFPrivateDict = CFFPrivateDict; exports.CFFCompiler = CFFCompiler; +exports.CFFFDSelect = CFFFDSelect; /***/ }), /* 32 */ @@ -33703,7 +33721,7 @@ var FontRendererFactory = function FontRendererFactoryClosure() { return glyphs; } function lookupCmap(ranges, unicode) { - var code = unicode.charCodeAt(0), + var code = unicode.codePointAt(0), gid = 0; var l = 0, r = ranges.length - 1; diff --git a/browser/extensions/pdfjs/content/web/viewer.html b/browser/extensions/pdfjs/content/web/viewer.html index 2b8a5104edcb..11fc1b7a7193 100644 --- a/browser/extensions/pdfjs/content/web/viewer.html +++ b/browser/extensions/pdfjs/content/web/viewer.html @@ -82,11 +82,15 @@ See https://github.com/adobe-type-tools/cmap-resources -
+
+
+
+ +
diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index 4258538ee4aa..3037bc1aa76b 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -120,8 +120,8 @@ let pdfjsWebApp, pdfjsWebAppOptions; pdfjsWebAppOptions = __webpack_require__(8); } { - __webpack_require__(33); - __webpack_require__(36); + __webpack_require__(34); + __webpack_require__(37); } ; ; @@ -200,9 +200,9 @@ function getViewerConfiguration() { findField: document.getElementById('findInput'), highlightAllCheckbox: document.getElementById('findHighlightAll'), caseSensitiveCheckbox: document.getElementById('findMatchCase'), + entireWordCheckbox: document.getElementById('findEntireWord'), findMsg: document.getElementById('findMsg'), findResultsCount: document.getElementById('findResultsCount'), - findStatusIcon: document.getElementById('findStatusIcon'), findPreviousButton: document.getElementById('findPrevious'), findNextButton: document.getElementById('findNext') }, @@ -298,31 +298,32 @@ var _pdf_find_bar = __webpack_require__(15); var _pdf_find_controller = __webpack_require__(16); -var _pdf_history = __webpack_require__(17); +var _pdf_history = __webpack_require__(18); -var _pdf_link_service = __webpack_require__(18); +var _pdf_link_service = __webpack_require__(19); -var _pdf_outline_viewer = __webpack_require__(19); +var _pdf_outline_viewer = __webpack_require__(20); -var _pdf_presentation_mode = __webpack_require__(20); +var _pdf_presentation_mode = __webpack_require__(21); -var _pdf_sidebar_resizer = __webpack_require__(21); +var _pdf_sidebar_resizer = __webpack_require__(22); -var _pdf_thumbnail_viewer = __webpack_require__(22); +var _pdf_thumbnail_viewer = __webpack_require__(23); -var _pdf_viewer = __webpack_require__(24); +var _pdf_viewer = __webpack_require__(25); -var _secondary_toolbar = __webpack_require__(29); +var _secondary_toolbar = __webpack_require__(30); -var _toolbar = __webpack_require__(31); +var _toolbar = __webpack_require__(32); -var _view_history = __webpack_require__(32); +var _view_history = __webpack_require__(33); const DEFAULT_SCALE_DELTA = 1.1; const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000; const FORCE_PAGES_LOADED_TIMEOUT = 10000; const DefaultExternalServices = { updateFindControlState(data) {}, + updateFindMatchesCount(data) {}, initPassiveLoading(callbacks) {}, fallback(data, callback) {}, reportTelemetry(data) {}, @@ -379,218 +380,214 @@ let PDFViewerApplication = { externalServices: DefaultExternalServices, _boundEvents: {}, contentDispositionFilename: null, - initialize(appConfig) { + async initialize(appConfig) { this.preferences = this.externalServices.createPreferences(); this.appConfig = appConfig; - return this._readPreferences().then(() => { - return this._parseHashParameters(); - }).then(() => { - return this._initializeL10n(); - }).then(() => { - if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) { - _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP); - } - return this._initializeViewerComponents(); - }).then(() => { - this.bindEvents(); - this.bindWindowEvents(); - let appContainer = appConfig.appContainer || document.documentElement; - this.l10n.translate(appContainer).then(() => { - this.eventBus.dispatch('localized', { source: this }); - }); - this.initialized = true; + await this._readPreferences(); + await this._parseHashParameters(); + await this._initializeL10n(); + if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) { + _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP); + } + await this._initializeViewerComponents(); + this.bindEvents(); + this.bindWindowEvents(); + let appContainer = appConfig.appContainer || document.documentElement; + this.l10n.translate(appContainer).then(() => { + this.eventBus.dispatch('localized', { source: this }); }); + this.initialized = true; }, - _readPreferences() { + async _readPreferences() { const OVERRIDES = { disableFontFace: true, disableRange: true, disableStream: true, textLayerMode: _ui_utils.TextLayerMode.DISABLE }; - return this.preferences.getAll().then(function (prefs) { + try { + const prefs = await this.preferences.getAll(); for (let name in prefs) { if (name in OVERRIDES && _app_options.AppOptions.get(name) === OVERRIDES[name]) { continue; } _app_options.AppOptions.set(name, prefs[name]); } - }, function (reason) {}); + } catch (reason) {} }, - _parseHashParameters() { + async _parseHashParameters() { + if (!_app_options.AppOptions.get('pdfBugEnabled')) { + return; + } const waitOn = []; - if (_app_options.AppOptions.get('pdfBugEnabled')) { - let hash = document.location.hash.substring(1); - let hashParams = (0, _ui_utils.parseQueryString)(hash); - if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') { - waitOn.push(loadFakeWorker()); - } - if ('disablerange' in hashParams) { - _app_options.AppOptions.set('disableRange', hashParams['disablerange'] === 'true'); - } - if ('disablestream' in hashParams) { - _app_options.AppOptions.set('disableStream', hashParams['disablestream'] === 'true'); - } - if ('disableautofetch' in hashParams) { - _app_options.AppOptions.set('disableAutoFetch', hashParams['disableautofetch'] === 'true'); - } - if ('disablefontface' in hashParams) { - _app_options.AppOptions.set('disableFontFace', hashParams['disablefontface'] === 'true'); - } - if ('disablehistory' in hashParams) { - _app_options.AppOptions.set('disableHistory', hashParams['disablehistory'] === 'true'); - } - if ('webgl' in hashParams) { - _app_options.AppOptions.set('enableWebGL', hashParams['webgl'] === 'true'); - } - if ('useonlycsszoom' in hashParams) { - _app_options.AppOptions.set('useOnlyCssZoom', hashParams['useonlycsszoom'] === 'true'); - } - if ('verbosity' in hashParams) { - _app_options.AppOptions.set('verbosity', hashParams['verbosity'] | 0); - } - if ('textlayer' in hashParams) { - switch (hashParams['textlayer']) { - case 'off': - _app_options.AppOptions.set('textLayerMode', _ui_utils.TextLayerMode.DISABLE); - break; - case 'visible': - case 'shadow': - case 'hover': - let viewer = this.appConfig.viewerContainer; - viewer.classList.add('textLayer-' + hashParams['textlayer']); - break; - } - } - if ('pdfbug' in hashParams) { - _app_options.AppOptions.set('pdfBug', true); - let enabled = hashParams['pdfbug'].split(','); - waitOn.push(loadAndEnablePDFBug(enabled)); + let hash = document.location.hash.substring(1); + let hashParams = (0, _ui_utils.parseQueryString)(hash); + if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') { + waitOn.push(loadFakeWorker()); + } + if ('disablerange' in hashParams) { + _app_options.AppOptions.set('disableRange', hashParams['disablerange'] === 'true'); + } + if ('disablestream' in hashParams) { + _app_options.AppOptions.set('disableStream', hashParams['disablestream'] === 'true'); + } + if ('disableautofetch' in hashParams) { + _app_options.AppOptions.set('disableAutoFetch', hashParams['disableautofetch'] === 'true'); + } + if ('disablefontface' in hashParams) { + _app_options.AppOptions.set('disableFontFace', hashParams['disablefontface'] === 'true'); + } + if ('disablehistory' in hashParams) { + _app_options.AppOptions.set('disableHistory', hashParams['disablehistory'] === 'true'); + } + if ('webgl' in hashParams) { + _app_options.AppOptions.set('enableWebGL', hashParams['webgl'] === 'true'); + } + if ('useonlycsszoom' in hashParams) { + _app_options.AppOptions.set('useOnlyCssZoom', hashParams['useonlycsszoom'] === 'true'); + } + if ('verbosity' in hashParams) { + _app_options.AppOptions.set('verbosity', hashParams['verbosity'] | 0); + } + if ('textlayer' in hashParams) { + switch (hashParams['textlayer']) { + case 'off': + _app_options.AppOptions.set('textLayerMode', _ui_utils.TextLayerMode.DISABLE); + break; + case 'visible': + case 'shadow': + case 'hover': + let viewer = this.appConfig.viewerContainer; + viewer.classList.add('textLayer-' + hashParams['textlayer']); + break; } } + if ('pdfbug' in hashParams) { + _app_options.AppOptions.set('pdfBug', true); + let enabled = hashParams['pdfbug'].split(','); + waitOn.push(loadAndEnablePDFBug(enabled)); + } return Promise.all(waitOn); }, - _initializeL10n() { + async _initializeL10n() { this.l10n = this.externalServices.createL10n({ locale: _app_options.AppOptions.get('locale') }); - return this.l10n.getDirection().then(dir => { - document.getElementsByTagName('html')[0].dir = dir; - }); + const dir = await this.l10n.getDirection(); + document.getElementsByTagName('html')[0].dir = dir; }, - _initializeViewerComponents() { - let { appConfig } = this; - return new Promise((resolve, reject) => { - this.overlayManager = new _overlay_manager.OverlayManager(); - const dispatchToDOM = _app_options.AppOptions.get('eventBusDispatchToDOM'); - let eventBus = appConfig.eventBus || (0, _dom_events.getGlobalEventBus)(dispatchToDOM); - this.eventBus = eventBus; - let pdfRenderingQueue = new _pdf_rendering_queue.PDFRenderingQueue(); - pdfRenderingQueue.onIdle = this.cleanup.bind(this); - this.pdfRenderingQueue = pdfRenderingQueue; - let pdfLinkService = new _pdf_link_service.PDFLinkService({ - eventBus, - externalLinkTarget: _app_options.AppOptions.get('externalLinkTarget'), - externalLinkRel: _app_options.AppOptions.get('externalLinkRel') - }); - this.pdfLinkService = pdfLinkService; - let downloadManager = this.externalServices.createDownloadManager({ disableCreateObjectURL: _app_options.AppOptions.get('disableCreateObjectURL') }); - this.downloadManager = downloadManager; - let container = appConfig.mainContainer; - let viewer = appConfig.viewerContainer; - this.pdfViewer = new _pdf_viewer.PDFViewer({ + async _initializeViewerComponents() { + const appConfig = this.appConfig; + this.overlayManager = new _overlay_manager.OverlayManager(); + const dispatchToDOM = _app_options.AppOptions.get('eventBusDispatchToDOM'); + let eventBus = appConfig.eventBus || (0, _dom_events.getGlobalEventBus)(dispatchToDOM); + this.eventBus = eventBus; + let pdfRenderingQueue = new _pdf_rendering_queue.PDFRenderingQueue(); + pdfRenderingQueue.onIdle = this.cleanup.bind(this); + this.pdfRenderingQueue = pdfRenderingQueue; + let pdfLinkService = new _pdf_link_service.PDFLinkService({ + eventBus, + externalLinkTarget: _app_options.AppOptions.get('externalLinkTarget'), + externalLinkRel: _app_options.AppOptions.get('externalLinkRel') + }); + this.pdfLinkService = pdfLinkService; + let downloadManager = this.externalServices.createDownloadManager({ disableCreateObjectURL: _app_options.AppOptions.get('disableCreateObjectURL') }); + this.downloadManager = downloadManager; + let container = appConfig.mainContainer; + let viewer = appConfig.viewerContainer; + this.pdfViewer = new _pdf_viewer.PDFViewer({ + container, + viewer, + eventBus, + renderingQueue: pdfRenderingQueue, + linkService: pdfLinkService, + downloadManager, + renderer: _app_options.AppOptions.get('renderer'), + enableWebGL: _app_options.AppOptions.get('enableWebGL'), + l10n: this.l10n, + textLayerMode: _app_options.AppOptions.get('textLayerMode'), + imageResourcesPath: _app_options.AppOptions.get('imageResourcesPath'), + renderInteractiveForms: _app_options.AppOptions.get('renderInteractiveForms'), + enablePrintAutoRotate: _app_options.AppOptions.get('enablePrintAutoRotate'), + useOnlyCssZoom: _app_options.AppOptions.get('useOnlyCssZoom'), + maxCanvasPixels: _app_options.AppOptions.get('maxCanvasPixels') + }); + pdfRenderingQueue.setViewer(this.pdfViewer); + pdfLinkService.setViewer(this.pdfViewer); + let thumbnailContainer = appConfig.sidebar.thumbnailView; + this.pdfThumbnailViewer = new _pdf_thumbnail_viewer.PDFThumbnailViewer({ + container: thumbnailContainer, + renderingQueue: pdfRenderingQueue, + linkService: pdfLinkService, + l10n: this.l10n + }); + pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + this.pdfHistory = new _pdf_history.PDFHistory({ + linkService: pdfLinkService, + eventBus + }); + pdfLinkService.setHistory(this.pdfHistory); + this.findController = new _pdf_find_controller.PDFFindController({ + pdfViewer: this.pdfViewer, + eventBus + }); + this.findController.onUpdateResultsCount = matchesCount => { + if (this.supportsIntegratedFind) { + this.externalServices.updateFindMatchesCount(matchesCount); + } else { + this.findBar.updateResultsCount(matchesCount); + } + }; + this.findController.onUpdateState = (state, previous, matchesCount) => { + if (this.supportsIntegratedFind) { + this.externalServices.updateFindControlState({ + result: state, + findPrevious: previous, + matchesCount + }); + } else { + this.findBar.updateUIState(state, previous, matchesCount); + } + }; + this.pdfViewer.setFindController(this.findController); + let findBarConfig = Object.create(appConfig.findBar); + findBarConfig.findController = this.findController; + findBarConfig.eventBus = eventBus; + this.findBar = new _pdf_find_bar.PDFFindBar(findBarConfig, this.l10n); + this.pdfDocumentProperties = new _pdf_document_properties.PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, this.l10n); + this.pdfCursorTools = new _pdf_cursor_tools.PDFCursorTools({ + container, + eventBus, + cursorToolOnLoad: _app_options.AppOptions.get('cursorToolOnLoad') + }); + this.toolbar = new _toolbar.Toolbar(appConfig.toolbar, container, eventBus, this.l10n); + this.secondaryToolbar = new _secondary_toolbar.SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus); + if (this.supportsFullscreen) { + this.pdfPresentationMode = new _pdf_presentation_mode.PDFPresentationMode({ container, viewer, - eventBus, - renderingQueue: pdfRenderingQueue, - linkService: pdfLinkService, - downloadManager, - renderer: _app_options.AppOptions.get('renderer'), - enableWebGL: _app_options.AppOptions.get('enableWebGL'), - l10n: this.l10n, - textLayerMode: _app_options.AppOptions.get('textLayerMode'), - imageResourcesPath: _app_options.AppOptions.get('imageResourcesPath'), - renderInteractiveForms: _app_options.AppOptions.get('renderInteractiveForms'), - enablePrintAutoRotate: _app_options.AppOptions.get('enablePrintAutoRotate'), - useOnlyCssZoom: _app_options.AppOptions.get('useOnlyCssZoom'), - maxCanvasPixels: _app_options.AppOptions.get('maxCanvasPixels') - }); - pdfRenderingQueue.setViewer(this.pdfViewer); - pdfLinkService.setViewer(this.pdfViewer); - let thumbnailContainer = appConfig.sidebar.thumbnailView; - this.pdfThumbnailViewer = new _pdf_thumbnail_viewer.PDFThumbnailViewer({ - container: thumbnailContainer, - renderingQueue: pdfRenderingQueue, - linkService: pdfLinkService, - l10n: this.l10n - }); - pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); - this.pdfHistory = new _pdf_history.PDFHistory({ - linkService: pdfLinkService, - eventBus - }); - pdfLinkService.setHistory(this.pdfHistory); - this.findController = new _pdf_find_controller.PDFFindController({ pdfViewer: this.pdfViewer, - eventBus - }); - this.findController.onUpdateResultsCount = matchCount => { - if (this.supportsIntegratedFind) { - return; - } - this.findBar.updateResultsCount(matchCount); - }; - this.findController.onUpdateState = (state, previous, matchCount) => { - if (this.supportsIntegratedFind) { - this.externalServices.updateFindControlState({ - result: state, - findPrevious: previous - }); - } else { - this.findBar.updateUIState(state, previous, matchCount); - } - }; - this.pdfViewer.setFindController(this.findController); - let findBarConfig = Object.create(appConfig.findBar); - findBarConfig.findController = this.findController; - findBarConfig.eventBus = eventBus; - this.findBar = new _pdf_find_bar.PDFFindBar(findBarConfig, this.l10n); - this.pdfDocumentProperties = new _pdf_document_properties.PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, this.l10n); - this.pdfCursorTools = new _pdf_cursor_tools.PDFCursorTools({ - container, eventBus, - cursorToolOnLoad: _app_options.AppOptions.get('cursorToolOnLoad') + contextMenuItems: appConfig.fullscreen }); - this.toolbar = new _toolbar.Toolbar(appConfig.toolbar, container, eventBus, this.l10n); - this.secondaryToolbar = new _secondary_toolbar.SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus); - if (this.supportsFullscreen) { - this.pdfPresentationMode = new _pdf_presentation_mode.PDFPresentationMode({ - container, - viewer, - pdfViewer: this.pdfViewer, - eventBus, - contextMenuItems: appConfig.fullscreen - }); - } - this.passwordPrompt = new _password_prompt.PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.l10n); - this.pdfOutlineViewer = new _pdf_outline_viewer.PDFOutlineViewer({ - container: appConfig.sidebar.outlineView, - eventBus, - linkService: pdfLinkService - }); - this.pdfAttachmentViewer = new _pdf_attachment_viewer.PDFAttachmentViewer({ - container: appConfig.sidebar.attachmentsView, - eventBus, - downloadManager - }); - let sidebarConfig = Object.create(appConfig.sidebar); - sidebarConfig.pdfViewer = this.pdfViewer; - sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer; - sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer; - sidebarConfig.eventBus = eventBus; - this.pdfSidebar = new _pdf_sidebar.PDFSidebar(sidebarConfig, this.l10n); - this.pdfSidebar.onToggled = this.forceRendering.bind(this); - this.pdfSidebarResizer = new _pdf_sidebar_resizer.PDFSidebarResizer(appConfig.sidebarResizer, eventBus, this.l10n); - resolve(undefined); + } + this.passwordPrompt = new _password_prompt.PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.l10n); + this.pdfOutlineViewer = new _pdf_outline_viewer.PDFOutlineViewer({ + container: appConfig.sidebar.outlineView, + eventBus, + linkService: pdfLinkService }); + this.pdfAttachmentViewer = new _pdf_attachment_viewer.PDFAttachmentViewer({ + container: appConfig.sidebar.attachmentsView, + eventBus, + downloadManager + }); + let sidebarConfig = Object.create(appConfig.sidebar); + sidebarConfig.pdfViewer = this.pdfViewer; + sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer; + sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer; + sidebarConfig.eventBus = eventBus; + this.pdfSidebar = new _pdf_sidebar.PDFSidebar(sidebarConfig, this.l10n); + this.pdfSidebar.onToggled = this.forceRendering.bind(this); + this.pdfSidebarResizer = new _pdf_sidebar_resizer.PDFSidebarResizer(appConfig.sidebarResizer, eventBus, this.l10n); }, run(config) { this.initialize(config).then(webViewerInitialized); @@ -703,11 +700,11 @@ let PDFViewerApplication = { } document.title = title; }, - close() { + async close() { let errorWrapper = this.appConfig.errorWrapper.container; errorWrapper.setAttribute('hidden', 'true'); if (!this.pdfLoadingTask) { - return Promise.resolve(); + return; } let promise = this.pdfLoadingTask.destroy(); this.pdfLoadingTask = null; @@ -736,11 +733,9 @@ let PDFViewerApplication = { } return promise; }, - open(file, args) { + async open(file, args) { if (this.pdfLoadingTask) { - return this.close().then(() => { - return this.open(file, args); - }); + await this.close(); } const workerParameters = _app_options.AppOptions.getAll('worker'); for (let key in workerParameters) { @@ -915,10 +910,6 @@ let PDFViewerApplication = { this.initialRotation = this.pdfHistory.initialRotation; } } - let initialParams = { - bookmark: null, - hash: null - }; let storePromise = store.getMultiple({ page: null, zoom: _ui_utils.DEFAULT_SCALE_VALUE, @@ -929,7 +920,8 @@ let PDFViewerApplication = { scrollMode: null, spreadMode: null }).catch(() => {}); - Promise.all([storePromise, pageModePromise]).then(([values = {}, pageMode]) => { + Promise.all([storePromise, pageModePromise]).then(async ([values = {}, pageMode]) => { + const initialBookmark = this.initialBookmark; const zoom = _app_options.AppOptions.get('defaultZoomValue'); let hash = zoom ? `zoom=${zoom}` : null; let rotation = null; @@ -946,16 +938,6 @@ let PDFViewerApplication = { if (pageMode && !_app_options.AppOptions.get('disablePageMode')) { sidebarView = sidebarView || apiPageModeToSidebarView(pageMode); } - return { - hash, - rotation, - sidebarView, - scrollMode, - spreadMode - }; - }).then(({ hash, rotation, sidebarView, scrollMode, spreadMode }) => { - initialParams.bookmark = this.initialBookmark; - initialParams.hash = hash; this.setInitialView(hash, { rotation, sidebarView, @@ -966,19 +948,18 @@ let PDFViewerApplication = { if (!this.isViewerEmbedded) { pdfViewer.focus(); } - return Promise.race([pagesPromise, new Promise(resolve => { + await Promise.race([pagesPromise, new Promise(resolve => { setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT); })]); - }).then(() => { - if (!initialParams.bookmark && !initialParams.hash) { + if (!initialBookmark && !hash) { return; } if (pdfViewer.hasEqualPageSizes) { return; } - this.initialBookmark = initialParams.bookmark; + this.initialBookmark = initialBookmark; pdfViewer.currentScaleValue = pdfViewer.currentScaleValue; - this.setInitialView(initialParams.hash); + this.setInitialView(hash); }).then(function () { pdfViewer.update(); }); @@ -1345,13 +1326,13 @@ function webViewerInitialized() { appConfig.sidebar.toggleButton.addEventListener('click', function () { PDFViewerApplication.pdfSidebar.toggle(); }); - Promise.resolve().then(function () { + try { webViewerOpenFileViaURL(file); - }).catch(function (reason) { + } catch (reason) { PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(msg => { PDFViewerApplication.error(msg, reason); }); - }); + } } let webViewerOpenFileViaURL; { @@ -1562,6 +1543,7 @@ function webViewerFind(evt) { query: evt.query, phraseSearch: evt.phraseSearch, caseSensitive: evt.caseSensitive, + entireWord: evt.entireWord, highlightAll: evt.highlightAll, findPrevious: evt.findPrevious }); @@ -1571,6 +1553,7 @@ function webViewerFindFromUrlHash(evt) { query: evt.query, phraseSearch: evt.phraseSearch, caseSensitive: false, + entireWord: false, highlightAll: true, findPrevious: false }); @@ -1674,6 +1657,7 @@ function webViewerKeyDown(evt) { query: findState.query, phraseSearch: findState.phraseSearch, caseSensitive: findState.caseSensitive, + entireWord: findState.entireWord, highlightAll: findState.highlightAll, findPrevious: cmd === 5 || cmd === 12 }); @@ -1941,18 +1925,16 @@ function formatL10nValue(text, args) { }); } let NullL10n = { - getLanguage() { - return Promise.resolve('en-us'); + async getLanguage() { + return 'en-us'; }, - getDirection() { - return Promise.resolve('ltr'); + async getDirection() { + return 'ltr'; }, - get(property, args, fallback) { - return Promise.resolve(formatL10nValue(fallback, args)); + async get(property, args, fallback) { + return formatL10nValue(fallback, args); }, - translate(element) { - return Promise.resolve(); - } + async translate(element) {} }; function getOutputScale(ctx) { let devicePixelRatio = window.devicePixelRatio || 1; @@ -3480,69 +3462,57 @@ class OverlayManager { get active() { return this._active; } - register(name, element, callerCloseMethod = null, canForceClose = false) { - return new Promise(resolve => { - let container; - if (!name || !element || !(container = element.parentNode)) { - throw new Error('Not enough parameters.'); - } else if (this._overlays[name]) { - throw new Error('The overlay is already registered.'); - } - this._overlays[name] = { - element, - container, - callerCloseMethod, - canForceClose - }; - resolve(); - }); + async register(name, element, callerCloseMethod = null, canForceClose = false) { + let container; + if (!name || !element || !(container = element.parentNode)) { + throw new Error('Not enough parameters.'); + } else if (this._overlays[name]) { + throw new Error('The overlay is already registered.'); + } + this._overlays[name] = { + element, + container, + callerCloseMethod, + canForceClose + }; } - unregister(name) { - return new Promise(resolve => { - if (!this._overlays[name]) { - throw new Error('The overlay does not exist.'); + async unregister(name) { + if (!this._overlays[name]) { + throw new Error('The overlay does not exist.'); + } else if (this._active === name) { + throw new Error('The overlay cannot be removed while it is active.'); + } + delete this._overlays[name]; + } + async open(name) { + if (!this._overlays[name]) { + throw new Error('The overlay does not exist.'); + } else if (this._active) { + if (this._overlays[name].canForceClose) { + this._closeThroughCaller(); } else if (this._active === name) { - throw new Error('The overlay cannot be removed while it is active.'); - } - delete this._overlays[name]; - resolve(); - }); - } - open(name) { - return new Promise(resolve => { - if (!this._overlays[name]) { - throw new Error('The overlay does not exist.'); - } else if (this._active) { - if (this._overlays[name].canForceClose) { - this._closeThroughCaller(); - } else if (this._active === name) { - throw new Error('The overlay is already active.'); - } else { - throw new Error('Another overlay is currently active.'); - } - } - this._active = name; - this._overlays[this._active].element.classList.remove('hidden'); - this._overlays[this._active].container.classList.remove('hidden'); - window.addEventListener('keydown', this._keyDownBound); - resolve(); - }); - } - close(name) { - return new Promise(resolve => { - if (!this._overlays[name]) { - throw new Error('The overlay does not exist.'); - } else if (!this._active) { - throw new Error('The overlay is currently not active.'); - } else if (this._active !== name) { + throw new Error('The overlay is already active.'); + } else { throw new Error('Another overlay is currently active.'); } - this._overlays[this._active].container.classList.add('hidden'); - this._overlays[this._active].element.classList.add('hidden'); - this._active = null; - window.removeEventListener('keydown', this._keyDownBound); - resolve(); - }); + } + this._active = name; + this._overlays[this._active].element.classList.remove('hidden'); + this._overlays[this._active].container.classList.remove('hidden'); + window.addEventListener('keydown', this._keyDownBound); + } + async close(name) { + if (!this._overlays[name]) { + throw new Error('The overlay does not exist.'); + } else if (!this._active) { + throw new Error('The overlay is currently not active.'); + } else if (this._active !== name) { + throw new Error('Another overlay is currently active.'); + } + this._overlays[this._active].container.classList.add('hidden'); + this._overlays[this._active].element.classList.add('hidden'); + this._active = null; + window.removeEventListener('keydown', this._keyDownBound); } _keyDown(evt) { if (this._active && evt.keyCode === 27) { @@ -4022,6 +3992,7 @@ var _pdf_find_controller = __webpack_require__(16); var _ui_utils = __webpack_require__(2); +const MATCHES_COUNT_LIMIT = 1000; class PDFFindBar { constructor(options, l10n = _ui_utils.NullL10n) { this.opened = false; @@ -4030,9 +4001,9 @@ class PDFFindBar { this.findField = options.findField || null; this.highlightAll = options.highlightAllCheckbox || null; this.caseSensitive = options.caseSensitiveCheckbox || null; + this.entireWord = options.entireWordCheckbox || null; this.findMsg = options.findMsg || null; this.findResultsCount = options.findResultsCount || null; - this.findStatusIcon = options.findStatusIcon || null; this.findPreviousButton = options.findPreviousButton || null; this.findNextButton = options.findNextButton || null; this.findController = options.findController || null; @@ -4071,6 +4042,9 @@ class PDFFindBar { this.caseSensitive.addEventListener('click', () => { this.dispatchEvent('casesensitivitychange'); }); + this.entireWord.addEventListener('click', () => { + this.dispatchEvent('entirewordchange'); + }); this.eventBus.on('resize', this._adjustWidth.bind(this)); } reset() { @@ -4081,13 +4055,14 @@ class PDFFindBar { source: this, type, query: this.findField.value, - caseSensitive: this.caseSensitive.checked, phraseSearch: true, + caseSensitive: this.caseSensitive.checked, + entireWord: this.entireWord.checked, highlightAll: this.highlightAll.checked, findPrevious: findPrev }); } - updateUIState(state, previous, matchCount) { + updateUIState(state, previous, matchesCount) { let notFound = false; let findMsg = ''; let status = ''; @@ -4119,20 +4094,28 @@ class PDFFindBar { this.findMsg.textContent = msg; this._adjustWidth(); }); - this.updateResultsCount(matchCount); + this.updateResultsCount(matchesCount); } - updateResultsCount(matchCount) { + updateResultsCount({ current = 0, total = 0 } = {}) { if (!this.findResultsCount) { return; } - if (!matchCount) { - this.findResultsCount.classList.add('hidden'); - this.findResultsCount.textContent = ''; - } else { - this.findResultsCount.textContent = matchCount.toLocaleString(); - this.findResultsCount.classList.remove('hidden'); + let matchesCountMsg = ''; + if (total) { + if (total > MATCHES_COUNT_LIMIT) { + matchesCountMsg = this.l10n.get('find_matches_count_limit', { limit: MATCHES_COUNT_LIMIT.toLocaleString() }, 'More than {{limit}} matches'); + } else { + matchesCountMsg = this.l10n.get('find_matches_count', { + current: current.toLocaleString(), + total: total.toLocaleString() + }, '{{current}} of {{total}} matches'); + } } - this._adjustWidth(); + Promise.resolve(matchesCountMsg).then(msg => { + this.findResultsCount.textContent = msg; + this.findResultsCount.classList[!total ? 'add' : 'remove']('hidden'); + this._adjustWidth(); + }); } open() { if (!this.opened) { @@ -4188,6 +4171,8 @@ exports.PDFFindController = exports.FindState = undefined; var _pdfjsLib = __webpack_require__(3); +var _pdf_find_utils = __webpack_require__(17); + var _dom_events = __webpack_require__(10); var _ui_utils = __webpack_require__(2); @@ -4232,7 +4217,7 @@ class PDFFindController { this.pageContents = []; this.pageMatches = []; this.pageMatchesLength = null; - this.matchCount = 0; + this.matchesCountTotal = 0; this.selected = { pageIdx: -1, matchIdx: -1 @@ -4318,7 +4303,25 @@ class PDFFindController { matchesLength.push(matchesWithLength[i].matchLength); } } - _calculatePhraseMatch(query, pageIndex, pageContent) { + _isEntireWord(content, startIdx, length) { + if (startIdx > 0) { + const first = content.charCodeAt(startIdx); + const limit = content.charCodeAt(startIdx - 1); + if ((0, _pdf_find_utils.getCharacterType)(first) === (0, _pdf_find_utils.getCharacterType)(limit)) { + return false; + } + } + const endIdx = startIdx + length - 1; + if (endIdx < content.length - 1) { + const last = content.charCodeAt(endIdx); + const limit = content.charCodeAt(endIdx + 1); + if ((0, _pdf_find_utils.getCharacterType)(last) === (0, _pdf_find_utils.getCharacterType)(limit)) { + return false; + } + } + return true; + } + _calculatePhraseMatch(query, pageIndex, pageContent, entireWord) { let matches = []; let queryLen = query.length; let matchIdx = -queryLen; @@ -4327,11 +4330,14 @@ class PDFFindController { if (matchIdx === -1) { break; } + if (entireWord && !this._isEntireWord(pageContent, matchIdx, queryLen)) { + continue; + } matches.push(matchIdx); } this.pageMatches[pageIndex] = matches; } - _calculateWordMatch(query, pageIndex, pageContent) { + _calculateWordMatch(query, pageIndex, pageContent, entireWord) { let matchesWithLength = []; let queryArray = query.match(/\S+/g); for (let i = 0, len = queryArray.length; i < len; i++) { @@ -4343,6 +4349,9 @@ class PDFFindController { if (matchIdx === -1) { break; } + if (entireWord && !this._isEntireWord(pageContent, matchIdx, subqueryLen)) { + continue; + } matchesWithLength.push({ match: matchIdx, matchLength: subqueryLen, @@ -4362,6 +4371,7 @@ class PDFFindController { let query = this._normalize(this.state.query); let caseSensitive = this.state.caseSensitive; let phraseSearch = this.state.phraseSearch; + const entireWord = this.state.entireWord; let queryLen = query.length; if (queryLen === 0) { return; @@ -4371,17 +4381,18 @@ class PDFFindController { query = query.toLowerCase(); } if (phraseSearch) { - this._calculatePhraseMatch(query, pageIndex, pageContent); + this._calculatePhraseMatch(query, pageIndex, pageContent, entireWord); } else { - this._calculateWordMatch(query, pageIndex, pageContent); + this._calculateWordMatch(query, pageIndex, pageContent, entireWord); } this._updatePage(pageIndex); if (this.resumePageIdx === pageIndex) { this.resumePageIdx = null; this._nextPageMatch(); } - if (this.pageMatches[pageIndex].length > 0) { - this.matchCount += this.pageMatches[pageIndex].length; + const pageMatchesCount = this.pageMatches[pageIndex].length; + if (pageMatchesCount > 0) { + this.matchesCountTotal += pageMatchesCount; this._updateUIResultsCount(); } } @@ -4434,7 +4445,7 @@ class PDFFindController { this.hadMatch = false; this.resumePageIdx = null; this.pageMatches = []; - this.matchCount = 0; + this.matchesCountTotal = 0; this.pageMatchesLength = null; for (let i = 0; i < numPages; i++) { this._updatePage(i); @@ -4531,15 +4542,33 @@ class PDFFindController { this._updatePage(this.selected.pageIdx); } } - _updateUIResultsCount() { - if (this.onUpdateResultsCount) { - this.onUpdateResultsCount(this.matchCount); + _requestMatchesCount() { + const { pageIdx, matchIdx } = this.selected; + let current = 0; + if (matchIdx !== -1) { + for (let i = 0; i < pageIdx; i++) { + current += this.pageMatches[i] && this.pageMatches[i].length || 0; + } + current += matchIdx + 1; } + return { + current, + total: this.matchesCountTotal + }; + } + _updateUIResultsCount() { + if (!this.onUpdateResultsCount) { + return; + } + const matchesCount = this._requestMatchesCount(); + this.onUpdateResultsCount(matchesCount); } _updateUIState(state, previous) { - if (this.onUpdateState) { - this.onUpdateState(state, previous, this.matchCount); + if (!this.onUpdateState) { + return; } + const matchesCount = this._requestMatchesCount(); + this.onUpdateState(state, previous, matchesCount); } } exports.FindState = FindState; @@ -4552,6 +4581,86 @@ exports.PDFFindController = PDFFindController; "use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +const CharacterType = { + SPACE: 0, + ALPHA_LETTER: 1, + PUNCT: 2, + HAN_LETTER: 3, + KATAKANA_LETTER: 4, + HIRAGANA_LETTER: 5, + HALFWIDTH_KATAKANA_LETTER: 6, + THAI_LETTER: 7 +}; +function isAlphabeticalScript(charCode) { + return charCode < 0x2E80; +} +function isAscii(charCode) { + return (charCode & 0xFF80) === 0; +} +function isAsciiAlpha(charCode) { + return charCode >= 0x61 && charCode <= 0x7A || charCode >= 0x41 && charCode <= 0x5A; +} +function isAsciiDigit(charCode) { + return charCode >= 0x30 && charCode <= 0x39; +} +function isAsciiSpace(charCode) { + return charCode === 0x20 || charCode === 0x09 || charCode === 0x0D || charCode === 0x0A; +} +function isHan(charCode) { + return charCode >= 0x3400 && charCode <= 0x9FFF || charCode >= 0xF900 && charCode <= 0xFAFF; +} +function isKatakana(charCode) { + return charCode >= 0x30A0 && charCode <= 0x30FF; +} +function isHiragana(charCode) { + return charCode >= 0x3040 && charCode <= 0x309F; +} +function isHalfwidthKatakana(charCode) { + return charCode >= 0xFF60 && charCode <= 0xFF9F; +} +function isThai(charCode) { + return (charCode & 0xFF80) === 0x0E00; +} +function getCharacterType(charCode) { + if (isAlphabeticalScript(charCode)) { + if (isAscii(charCode)) { + if (isAsciiSpace(charCode)) { + return CharacterType.SPACE; + } else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) || charCode === 0x5F) { + return CharacterType.ALPHA_LETTER; + } + return CharacterType.PUNCT; + } else if (isThai(charCode)) { + return CharacterType.THAI_LETTER; + } else if (charCode === 0xA0) { + return CharacterType.SPACE; + } + return CharacterType.ALPHA_LETTER; + } + if (isHan(charCode)) { + return CharacterType.HAN_LETTER; + } else if (isKatakana(charCode)) { + return CharacterType.KATAKANA_LETTER; + } else if (isHiragana(charCode)) { + return CharacterType.HIRAGANA_LETTER; + } else if (isHalfwidthKatakana(charCode)) { + return CharacterType.HALFWIDTH_KATAKANA_LETTER; + } + return CharacterType.ALPHA_LETTER; +} +exports.CharacterType = CharacterType; +exports.getCharacterType = getCharacterType; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + Object.defineProperty(exports, "__esModule", { value: true }); @@ -4923,7 +5032,7 @@ exports.isDestHashesEqual = isDestHashesEqual; exports.isDestArraysEqual = isDestArraysEqual; /***/ }), -/* 18 */ +/* 19 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5257,7 +5366,7 @@ exports.PDFLinkService = PDFLinkService; exports.SimpleLinkService = SimpleLinkService; /***/ }), -/* 19 */ +/* 20 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5398,7 +5507,7 @@ class PDFOutlineViewer { exports.PDFOutlineViewer = PDFOutlineViewer; /***/ }), -/* 20 */ +/* 21 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5715,7 +5824,7 @@ class PDFPresentationMode { exports.PDFPresentationMode = PDFPresentationMode; /***/ }), -/* 21 */ +/* 22 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5835,7 +5944,7 @@ class PDFSidebarResizer { exports.PDFSidebarResizer = PDFSidebarResizer; /***/ }), -/* 22 */ +/* 23 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5848,7 +5957,7 @@ exports.PDFThumbnailViewer = undefined; var _ui_utils = __webpack_require__(2); -var _pdf_thumbnail_view = __webpack_require__(23); +var _pdf_thumbnail_view = __webpack_require__(24); const THUMBNAIL_SCROLL_MARGIN = -19; const THUMBNAIL_SELECTED_CLASS = 'selected'; @@ -6024,7 +6133,7 @@ class PDFThumbnailViewer { exports.PDFThumbnailViewer = PDFThumbnailViewer; /***/ }), -/* 23 */ +/* 24 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6332,7 +6441,7 @@ class PDFThumbnailView { exports.PDFThumbnailView = PDFThumbnailView; /***/ }), -/* 24 */ +/* 25 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6343,7 +6452,7 @@ Object.defineProperty(exports, "__esModule", { }); exports.PDFViewer = undefined; -var _base_viewer = __webpack_require__(25); +var _base_viewer = __webpack_require__(26); var _ui_utils = __webpack_require__(2); @@ -6422,7 +6531,7 @@ class PDFViewer extends _base_viewer.BaseViewer { exports.PDFViewer = PDFViewer; /***/ }), -/* 25 */ +/* 26 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6437,17 +6546,17 @@ var _ui_utils = __webpack_require__(2); var _pdf_rendering_queue = __webpack_require__(6); -var _annotation_layer_builder = __webpack_require__(26); +var _annotation_layer_builder = __webpack_require__(27); var _pdfjsLib = __webpack_require__(3); var _dom_events = __webpack_require__(10); -var _pdf_page_view = __webpack_require__(27); +var _pdf_page_view = __webpack_require__(28); -var _pdf_link_service = __webpack_require__(18); +var _pdf_link_service = __webpack_require__(19); -var _text_layer_builder = __webpack_require__(28); +var _text_layer_builder = __webpack_require__(29); const DEFAULT_CACHE_SIZE = 10; const ScrollMode = { @@ -7230,7 +7339,7 @@ exports.ScrollMode = ScrollMode; exports.SpreadMode = SpreadMode; /***/ }), -/* 26 */ +/* 27 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -7245,7 +7354,7 @@ var _pdfjsLib = __webpack_require__(3); var _ui_utils = __webpack_require__(2); -var _pdf_link_service = __webpack_require__(18); +var _pdf_link_service = __webpack_require__(19); class AnnotationLayerBuilder { constructor({ pageDiv, pdfPage, linkService, downloadManager, imageResourcesPath = '', renderInteractiveForms = false, l10n = _ui_utils.NullL10n }) { @@ -7315,7 +7424,7 @@ exports.AnnotationLayerBuilder = AnnotationLayerBuilder; exports.DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory; /***/ }), -/* 27 */ +/* 28 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -7772,7 +7881,7 @@ class PDFPageView { exports.PDFPageView = PDFPageView; /***/ }), -/* 28 */ +/* 29 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8041,7 +8150,7 @@ exports.TextLayerBuilder = TextLayerBuilder; exports.DefaultTextLayerFactory = DefaultTextLayerFactory; /***/ }), -/* 29 */ +/* 30 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8052,11 +8161,11 @@ Object.defineProperty(exports, "__esModule", { }); exports.SecondaryToolbar = undefined; -var _base_viewer = __webpack_require__(25); +var _base_viewer = __webpack_require__(26); var _pdf_cursor_tools = __webpack_require__(4); -var _pdf_single_page_viewer = __webpack_require__(30); +var _pdf_single_page_viewer = __webpack_require__(31); var _ui_utils = __webpack_require__(2); @@ -8319,7 +8428,7 @@ class SecondaryToolbar { exports.SecondaryToolbar = SecondaryToolbar; /***/ }), -/* 30 */ +/* 31 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8330,7 +8439,7 @@ Object.defineProperty(exports, "__esModule", { }); exports.PDFSinglePageViewer = undefined; -var _base_viewer = __webpack_require__(25); +var _base_viewer = __webpack_require__(26); var _ui_utils = __webpack_require__(2); @@ -8444,7 +8553,7 @@ class PDFSinglePageViewer extends _base_viewer.BaseViewer { exports.PDFSinglePageViewer = PDFSinglePageViewer; /***/ }), -/* 31 */ +/* 32 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8629,7 +8738,7 @@ class Toolbar { exports.Toolbar = Toolbar; /***/ }), -/* 32 */ +/* 33 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8667,53 +8776,44 @@ class ViewHistory { this.database = database; }); } - _writeToStorage() { - return new Promise(resolve => { - let databaseStr = JSON.stringify(this.database); - sessionStorage.setItem('pdfjs.history', databaseStr); - resolve(); - }); + async _writeToStorage() { + let databaseStr = JSON.stringify(this.database); + sessionStorage.setItem('pdfjs.history', databaseStr); } - _readFromStorage() { - return new Promise(function (resolve) { - resolve(sessionStorage.getItem('pdfjs.history')); - }); + async _readFromStorage() { + return sessionStorage.getItem('pdfjs.history'); } - set(name, val) { - return this._initializedPromise.then(() => { - this.file[name] = val; - return this._writeToStorage(); - }); + async set(name, val) { + await this._initializedPromise; + this.file[name] = val; + return this._writeToStorage(); } - setMultiple(properties) { - return this._initializedPromise.then(() => { - for (let name in properties) { - this.file[name] = properties[name]; - } - return this._writeToStorage(); - }); + async setMultiple(properties) { + await this._initializedPromise; + for (let name in properties) { + this.file[name] = properties[name]; + } + return this._writeToStorage(); } - get(name, defaultValue) { - return this._initializedPromise.then(() => { + async get(name, defaultValue) { + await this._initializedPromise; + let val = this.file[name]; + return val !== undefined ? val : defaultValue; + } + async getMultiple(properties) { + await this._initializedPromise; + let values = Object.create(null); + for (let name in properties) { let val = this.file[name]; - return val !== undefined ? val : defaultValue; - }); - } - getMultiple(properties) { - return this._initializedPromise.then(() => { - let values = Object.create(null); - for (let name in properties) { - let val = this.file[name]; - values[name] = val !== undefined ? val : properties[name]; - } - return values; - }); + values[name] = val !== undefined ? val : properties[name]; + } + return values; } } exports.ViewHistory = ViewHistory; /***/ }), -/* 33 */ +/* 34 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8724,11 +8824,11 @@ Object.defineProperty(exports, "__esModule", { }); exports.FirefoxCom = exports.DownloadManager = undefined; -__webpack_require__(34); +__webpack_require__(35); var _pdfjsLib = __webpack_require__(3); -var _preferences = __webpack_require__(35); +var _preferences = __webpack_require__(36); var _app = __webpack_require__(1); @@ -8807,12 +8907,12 @@ class DownloadManager { } } class FirefoxPreferences extends _preferences.BasePreferences { - _writeToStorage(prefObj) { + async _writeToStorage(prefObj) { return new Promise(function (resolve) { FirefoxCom.request('setPreferences', prefObj, resolve); }); } - _readFromStorage(prefObj) { + async _readFromStorage(prefObj) { return new Promise(function (resolve) { FirefoxCom.request('getPreferences', prefObj, function (prefStr) { let readPrefs = JSON.parse(prefStr); @@ -8825,22 +8925,21 @@ class MozL10n { constructor(mozL10n) { this.mozL10n = mozL10n; } - getLanguage() { - return Promise.resolve(this.mozL10n.getLanguage()); + async getLanguage() { + return this.mozL10n.getLanguage(); } - getDirection() { - return Promise.resolve(this.mozL10n.getDirection()); + async getDirection() { + return this.mozL10n.getDirection(); } - get(property, args, fallback) { - return Promise.resolve(this.mozL10n.get(property, args, fallback)); + async get(property, args, fallback) { + return this.mozL10n.get(property, args, fallback); } - translate(element) { + async translate(element) { this.mozL10n.translate(element); - return Promise.resolve(); } } (function listenFindEvents() { - const events = ['find', 'findagain', 'findhighlightallchange', 'findcasesensitivitychange']; + const events = ['find', 'findagain', 'findhighlightallchange', 'findcasesensitivitychange', 'findentirewordchange']; let handleEvent = function (evt) { if (!_app.PDFViewerApplication.initialized) { return; @@ -8851,12 +8950,13 @@ class MozL10n { query: evt.detail.query, phraseSearch: true, caseSensitive: !!evt.detail.caseSensitive, + entireWord: !!evt.detail.entireWord, highlightAll: !!evt.detail.highlightAll, findPrevious: !!evt.detail.findPrevious }); }; - for (let i = 0, len = events.length; i < len; i++) { - window.addEventListener(events[i], handleEvent); + for (let event of events) { + window.addEventListener(event, handleEvent); } })(); function FirefoxComDataRangeTransport(length, initialData) { @@ -8876,6 +8976,7 @@ _app.PDFViewerApplication.externalServices = { updateFindControlState(data) { FirefoxCom.request('updateFindControlState', data); }, + updateFindMatchesCount(data) {}, initPassiveLoading(callbacks) { let pdfDataRangeTransport; window.addEventListener('message', function windowMessage(e) { @@ -8960,7 +9061,7 @@ exports.DownloadManager = DownloadManager; exports.FirefoxCom = FirefoxCom; /***/ }), -/* 34 */ +/* 35 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -9058,7 +9159,7 @@ exports.FirefoxCom = FirefoxCom; })(undefined); /***/ }), -/* 35 */ +/* 36 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -9125,66 +9226,63 @@ class BasePreferences { } }); } - _writeToStorage(prefObj) { - return Promise.reject(new Error('Not implemented: _writeToStorage')); + async _writeToStorage(prefObj) { + throw new Error('Not implemented: _writeToStorage'); } - _readFromStorage(prefObj) { - return Promise.reject(new Error('Not implemented: _readFromStorage')); + async _readFromStorage(prefObj) { + throw new Error('Not implemented: _readFromStorage'); } - reset() { - return this._initializedPromise.then(() => { - this.prefs = Object.assign(Object.create(null), this.defaults); - return this._writeToStorage(this.defaults); - }); + async reset() { + await this._initializedPromise; + this.prefs = Object.assign(Object.create(null), this.defaults); + return this._writeToStorage(this.defaults); } - set(name, value) { - return this._initializedPromise.then(() => { - if (this.defaults[name] === undefined) { - throw new Error(`Set preference: "${name}" is undefined.`); - } else if (value === undefined) { - throw new Error('Set preference: no value is specified.'); - } - let valueType = typeof value; - let defaultType = typeof this.defaults[name]; - if (valueType !== defaultType) { - if (valueType === 'number' && defaultType === 'string') { - value = value.toString(); - } else { - throw new Error(`Set preference: "${value}" is a ${valueType}, ` + `expected a ${defaultType}.`); - } + async set(name, value) { + await this._initializedPromise; + let defaultValue = this.defaults[name]; + if (defaultValue === undefined) { + throw new Error(`Set preference: "${name}" is undefined.`); + } else if (value === undefined) { + throw new Error('Set preference: no value is specified.'); + } + let valueType = typeof value; + let defaultType = typeof defaultValue; + if (valueType !== defaultType) { + if (valueType === 'number' && defaultType === 'string') { + value = value.toString(); } else { - if (valueType === 'number' && !Number.isInteger(value)) { - throw new Error(`Set preference: "${value}" must be an integer.`); - } + throw new Error(`Set preference: "${value}" is a ${valueType}, ` + `expected a ${defaultType}.`); } - this.prefs[name] = value; - return this._writeToStorage(this.prefs); - }); - } - get(name) { - return this._initializedPromise.then(() => { - let defaultValue = this.defaults[name]; - if (defaultValue === undefined) { - throw new Error(`Get preference: "${name}" is undefined.`); - } else { - let prefValue = this.prefs[name]; - if (prefValue !== undefined) { - return prefValue; - } + } else { + if (valueType === 'number' && !Number.isInteger(value)) { + throw new Error(`Set preference: "${value}" must be an integer.`); } - return defaultValue; - }); + } + this.prefs[name] = value; + return this._writeToStorage(this.prefs); } - getAll() { - return this._initializedPromise.then(() => { - return Object.assign(Object.create(null), this.defaults, this.prefs); - }); + async get(name) { + await this._initializedPromise; + let defaultValue = this.defaults[name]; + if (defaultValue === undefined) { + throw new Error(`Get preference: "${name}" is undefined.`); + } else { + let prefValue = this.prefs[name]; + if (prefValue !== undefined) { + return prefValue; + } + } + return defaultValue; + } + async getAll() { + await this._initializedPromise; + return Object.assign(Object.create(null), this.defaults, this.prefs); } } exports.BasePreferences = BasePreferences; /***/ }), -/* 36 */ +/* 37 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; diff --git a/browser/extensions/pdfjs/moz.yaml b/browser/extensions/pdfjs/moz.yaml index db7aa89e255d..4ac61ba11197 100644 --- a/browser/extensions/pdfjs/moz.yaml +++ b/browser/extensions/pdfjs/moz.yaml @@ -20,7 +20,7 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: version 2.0.815 + release: version 2.0.843 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ diff --git a/browser/locales/en-US/pdfviewer/viewer.properties b/browser/locales/en-US/pdfviewer/viewer.properties index 3929482459c4..39f1a99bcf66 100644 --- a/browser/locales/en-US/pdfviewer/viewer.properties +++ b/browser/locales/en-US/pdfviewer/viewer.properties @@ -165,8 +165,16 @@ find_next.title=Find the next occurrence of the phrase find_next_label=Next find_highlight=Highlight all find_match_case_label=Match case +find_entire_word_label=Whole words find_reached_top=Reached top of document, continued from bottom find_reached_bottom=Reached end of document, continued from top +# LOCALIZATION NOTE (find_matches_count): "{{current}}" and "{{total}}" will be +# replaced by a number representing the index of the currently active find result, +# respectively a number representing the total number of matches in the document. +find_matches_count={{current}} of {{total}} matches +# LOCALIZATION NOTE (find_matches_count_limit): "{{limit}}" will be replaced by +# a numerical value. +find_matches_count_limit=More than {{limit}} matches find_not_found=Phrase not found # Error panel labels From ebc62f01e49061c711b07aaeb0e248c74c05782e Mon Sep 17 00:00:00 2001 From: Doug Thayer Date: Tue, 11 Sep 2018 04:29:38 +0300 Subject: [PATCH 20/44] Bug 1489588 - Disallow nsDisplayList copies and implement moves r=mattwoodrow Summary: nsDisplayLists are currently a little bit error prone, since the empty state of an nsDisplayList requires mTop == &mSentinel. since &mSentinel will change when copied, while mTop won't, this naturally creates an invalid state. Additionally, copies don't quite make sense, since there is a requirement in the destructor that destructed nsDisplayLists are empty - in which case we would have to empty both the copied and the original nsDisplayList - something which is unlikely to happen naturally. Moves however are a natural operation - we just need to implement the correct move behavior accounting for this mTop == &mSentinel requirement. Reviewers: mattwoodrow Reviewed By: mattwoodrow Bug #: 1489588 Differential Revision: https://phabricator.services.mozilla.com/D5293 --HG-- extra : rebase_source : e8889903c6cd03a8ec0ff90f189dbdcc79201eb3 extra : histedit_source : 9e0e18702e6e4b4feb5bea0548dc42250905f897 --- layout/painting/nsDisplayList.h | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 595fd959df7d..6ebd21c3b490 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -3254,6 +3254,40 @@ public: } } + nsDisplayList(nsDisplayList&& aOther) + { + mIsOpaque = aOther.mIsOpaque; + mForceTransparentSurface = aOther.mForceTransparentSurface; + + if (aOther.mSentinel.mAbove) { + AppendToTop(&aOther); + } else { + mTop = &mSentinel; + mLength = 0; + } + } + + nsDisplayList& operator=(nsDisplayList&& aOther) + { + if (this != &aOther) { + if (aOther.mSentinel.mAbove) { + nsDisplayList tmp; + tmp.AppendToTop(&aOther); + aOther.AppendToTop(this); + AppendToTop(&tmp); + } else { + mTop = &mSentinel; + mLength = 0; + } + mIsOpaque = aOther.mIsOpaque; + mForceTransparentSurface = aOther.mForceTransparentSurface; + } + return *this; + } + + nsDisplayList(const nsDisplayList&) = delete; + nsDisplayList& operator=(const nsDisplayList& aOther) = delete; + /** * Append an item to the top of the list. The item must not currently * be in a list and cannot be null. From 3241d9c80cb1cb638db45ff3398a9ddf8b3862c0 Mon Sep 17 00:00:00 2001 From: Doug Thayer Date: Tue, 11 Sep 2018 04:31:40 +0300 Subject: [PATCH 21/44] Bug 1489588 - Allocate TextOverflows inline r=mattwoodrow Summary: Once the nsDisplayList difficulties are sorted out, this becomes rather trivial. Depends on D5293 Reviewers: mattwoodrow Reviewed By: mattwoodrow Bug #: 1489588 Differential Revision: https://phabricator.services.mozilla.com/D5294 --HG-- extra : histedit_source : 9eae7ebf1455acae8a9ffaff90ce8f92e1e62566 --- layout/generic/TextOverflow.cpp | 8 ++++---- layout/generic/TextOverflow.h | 11 +++++++++-- layout/generic/nsBlockFrame.cpp | 11 +++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp index 0ba53a9918a8..0062981f28cb 100644 --- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -370,7 +370,7 @@ TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder, // has overflow on that side. } -/* static */ UniquePtr +/* static */ Maybe TextOverflow::WillProcessLines(nsDisplayListBuilder* aBuilder, nsIFrame* aBlockFrame) { @@ -378,14 +378,14 @@ TextOverflow::WillProcessLines(nsDisplayListBuilder* aBuilder, if (aBuilder->IsForEventDelivery() || aBuilder->IsForFrameVisibility() || !CanHaveTextOverflow(aBlockFrame)) { - return nullptr; + return Nothing(); } nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetScrollableFrameFor(aBlockFrame); if (scrollableFrame && scrollableFrame->IsTransformingByAPZ()) { // If the APZ is actively scrolling this, don't bother with markers. - return nullptr; + return Nothing(); } - return MakeUnique(aBuilder, aBlockFrame); + return Some(TextOverflow(aBuilder, aBlockFrame)); } void diff --git a/layout/generic/TextOverflow.h b/layout/generic/TextOverflow.h index 534efb7dc823..b41fa171bead 100644 --- a/layout/generic/TextOverflow.h +++ b/layout/generic/TextOverflow.h @@ -27,13 +27,13 @@ namespace css { * 1. allocate an object using WillProcessLines * 2. then call ProcessLine for each line you are building display lists for */ -class MOZ_HEAP_CLASS TextOverflow final { +class TextOverflow final { public: /** * Allocate an object for text-overflow processing. * @return nullptr if no processing is necessary. The caller owns the object. */ - static UniquePtr + static Maybe WillProcessLines(nsDisplayListBuilder* aBuilder, nsIFrame* aBlockFrame); @@ -45,6 +45,13 @@ class MOZ_HEAP_CLASS TextOverflow final { TextOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aBlockFrame); + TextOverflow() = default; + ~TextOverflow() = default; + TextOverflow(TextOverflow&&) = default; + TextOverflow(const TextOverflow&) = delete; + TextOverflow& operator=(TextOverflow&&) = default; + TextOverflow& operator=(const TextOverflow&) = delete; + /** * Analyze the display lists for text overflow and what kind of item is at * the content edges. Add display items for text-overflow markers as needed diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 0c5d2a92a8c8..8242d1769d36 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -6812,8 +6812,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, aBuilder->MarkFramesForDisplayList(this, mFloats); // Prepare for text-overflow processing. - UniquePtr textOverflow = - TextOverflow::WillProcessLines(aBuilder, this); + Maybe textOverflow = TextOverflow::WillProcessLines(aBuilder, this); // We'll collect our lines' display items here, & then append this to aLists. nsDisplayListCollection linesDisplayListCollection(aBuilder); @@ -6828,7 +6827,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // Also skip the cursor if we're creating text overflow markers, // since we need to know what line number we're up to in order // to generate unique display item keys. - nsLineBox* cursor = (aBuilder->ShouldDescendIntoFrame(this, true) || textOverflow) ? + nsLineBox* cursor = (aBuilder->ShouldDescendIntoFrame(this, true) || textOverflow.isSome()) ? nullptr : GetFirstLineContaining(aBuilder->GetDirtyRect().y); LineIterator line_end = LinesEnd(); @@ -6843,7 +6842,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (lineArea.y >= aBuilder->GetDirtyRect().YMost()) { break; } - MOZ_ASSERT(!textOverflow); + MOZ_ASSERT(textOverflow.isNothing()); DisplayLine(aBuilder, lineArea, line, depth, drawnLines, linesDisplayListCollection, this, nullptr, 0); } @@ -6858,7 +6857,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, ++line) { nsRect lineArea = line->GetVisualOverflowArea(); DisplayLine(aBuilder, lineArea, line, depth, drawnLines, - linesDisplayListCollection, this, textOverflow.get(), lineCount); + linesDisplayListCollection, this, textOverflow.ptrOr(nullptr), lineCount); if (!lineArea.IsEmpty()) { if (lineArea.y < lastY || lineArea.YMost() < lastYMost) { @@ -6879,7 +6878,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // PositionedDescendants just before we append the lines' display items, // so that our text-overflow markers will appear on top of this block's // normal content but below any of its its' positioned children. - if (textOverflow) { + if (textOverflow.isSome()) { aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers()); } linesDisplayListCollection.MoveTo(aLists); From 129854b17ee702e7d86b306933ef4d2f21cbc8a3 Mon Sep 17 00:00:00 2001 From: Ted Campbell Date: Mon, 10 Sep 2018 21:35:20 -0400 Subject: [PATCH 22/44] Bug 1463375 - Increase arm64 simulator stack protection size. r=jandem The simulator uses a protection area instead of guard pages. Increase the size of this protection to be larger than worst-case frame size for EnterJit trampoline. --HG-- extra : rebase_source : fe05738f67cc25530953d6eb16e5fc1221470691 --- js/src/jit-test/tests/baseline/bug1463375.js | 5 +++++ js/src/jit/arm64/vixl/Simulator-vixl.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/baseline/bug1463375.js diff --git a/js/src/jit-test/tests/baseline/bug1463375.js b/js/src/jit-test/tests/baseline/bug1463375.js new file mode 100644 index 000000000000..8b9030b7fc70 --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1463375.js @@ -0,0 +1,5 @@ +// |jit-test| allow-overrecursed +function f() { + f.apply(null, new Array(20000)); +} +f() diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.h b/js/src/jit/arm64/vixl/Simulator-vixl.h index 70adeedad1d1..0e2f3c1f1ed2 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.h +++ b/js/src/jit/arm64/vixl/Simulator-vixl.h @@ -2572,7 +2572,7 @@ class Simulator : public DecoderVisitor { // Stack byte* stack_; - static const int stack_protection_size_ = 128 * KBytes; + static const int stack_protection_size_ = 512 * KBytes; static const int stack_size_ = (2 * MBytes) + (2 * stack_protection_size_); byte* stack_limit_; From eafdf2d611f2a16af2c779a2e9e09712471361e1 Mon Sep 17 00:00:00 2001 From: Andrei Ciure Date: Mon, 10 Sep 2018 12:08:00 +0300 Subject: [PATCH 23/44] Bug 1486165 - disable browser_connection_dnsoverhttps.js for frequent failures. r=jmaher --HG-- extra : rebase_source : 40539a5092e203c690a5dbee8129e773bdc1441c --- browser/components/preferences/in-content/tests/browser.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/components/preferences/in-content/tests/browser.ini b/browser/components/preferences/in-content/tests/browser.ini index 7d6e21649403..84b9d9cc9e16 100644 --- a/browser/components/preferences/in-content/tests/browser.ini +++ b/browser/components/preferences/in-content/tests/browser.ini @@ -43,6 +43,7 @@ skip-if = os != "win" # Windows-specific handler application selection dialog [browser_connection_bug1445991.js] skip-if = (verify && debug && (os == 'linux' || os == 'mac')) [browser_connection_dnsoverhttps.js] +skip-if = ccov # Skipping test on all ccov platforms [browser_contentblocking.js] [browser_cookies_exceptions.js] [browser_defaultbrowser_alwayscheck.js] From 46c70d27ee632838926d5f1e1a10d1868ba3fb66 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 11 Sep 2018 09:41:37 +1000 Subject: [PATCH 24/44] Bug 1490115 - Handle unaccompanied low surrogate pairs in the prefs parser. r=glandium Currently they cause the `String::from_utf16()` call to return an error result, and then the subsequent `unwrap()` on that result aborts. --HG-- extra : rebase_source : 6be81d4d1e618444f762a1ba4e93b5ce648dd45b --- modules/libpref/parser/src/lib.rs | 10 +++++++--- modules/libpref/test/gtest/Parser.cpp | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/libpref/parser/src/lib.rs b/modules/libpref/parser/src/lib.rs index 5ea7650252f5..ca253e2b0190 100644 --- a/modules/libpref/parser/src/lib.rs +++ b/modules/libpref/parser/src/lib.rs @@ -600,8 +600,7 @@ impl<'t> Parser<'t> { } // Returns false if we hit EOF without closing the comment. - fn match_multi_line_comment(&mut self) -> bool - { + fn match_multi_line_comment(&mut self) -> bool { loop { match self.get_char() { b'*' => { @@ -854,7 +853,7 @@ impl<'t> Parser<'t> { } else { self.string_error_token( &mut token, - "invalid low surrogate value after high surrogate"); + "invalid low surrogate after high surrogate"); continue; } } @@ -864,6 +863,11 @@ impl<'t> Parser<'t> { &mut token, "expected low surrogate after high surrogate"); continue; } + } else if 0xdc00 == (0xfc00 & value) { + // Unaccompanied low surrogate value. + self.string_error_token( + &mut token, "expected high surrogate before low surrogate"); + continue; } else if value == 0 { self.string_error_token(&mut token, "\\u0000 is not allowed"); continue; diff --git a/modules/libpref/test/gtest/Parser.cpp b/modules/libpref/test/gtest/Parser.cpp index 4a6cefa74ded..adad5d4ce702 100644 --- a/modules/libpref/test/gtest/Parser.cpp +++ b/modules/libpref/test/gtest/Parser.cpp @@ -196,14 +196,24 @@ pref("int.ok", 0); "test:2: prefs parse error: expected low surrogate after high surrogate\n" ); - // High surrogate followed by invalid low surrogate value. + // High surrogate followed by invalid low surrogate. // (The string literal is broken in two so that MSVC doesn't complain about // an invalid universal-character-name.) DEFAULT(R"( pref("string.bad-u-surrogate", "foo\)" R"(ud83c\u1234"); pref("int.ok", 0); )", - "test:2: prefs parse error: invalid low surrogate value after high surrogate\n" + "test:2: prefs parse error: invalid low surrogate after high surrogate\n" + ); + + // Low surrogate not preceded by high surrogate. + // (The string literal is broken in two so that MSVC doesn't complain about + // an invalid universal-character-name.) + DEFAULT(R"( +pref("string.bad-u-surrogate", "foo\)" R"(udc00"); +pref("int.ok", 0); + )", + "test:2: prefs parse error: expected high surrogate before low surrogate\n" ); // Unlike in JavaScript, \b, \f, \t, \v aren't allowed. From fb4191fda8dfbd97733427f9c915951e0c3e0b58 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 11 Sep 2018 09:41:40 +1000 Subject: [PATCH 25/44] Bug 1490115 - Add extra tests for comment-only inputs. r=glandium --HG-- extra : rebase_source : 5ef9accd9c6d7501ff2e54a4a81b3360f0bbc040 --- modules/libpref/test/gtest/Parser.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/libpref/test/gtest/Parser.cpp b/modules/libpref/test/gtest/Parser.cpp index adad5d4ce702..4ac98d49bb87 100644 --- a/modules/libpref/test/gtest/Parser.cpp +++ b/modules/libpref/test/gtest/Parser.cpp @@ -51,9 +51,7 @@ user_pref("string", "value"); ); // Totally empty input. - DEFAULT("", - "" - ); + DEFAULT("", ""); // Whitespace-only input. DEFAULT(R"( @@ -62,6 +60,11 @@ user_pref("string", "value"); "" ); + // Comment-only inputs. + DEFAULT(R"(// blah)", ""); + DEFAULT(R"(# blah)", ""); + DEFAULT(R"(/* blah */)", ""); + //------------------------------------------------------------------------- // All the lexing errors. (To be pedantic, some of the integer literal // overflows are triggered in the parser, but put them all here so they're all From f9b40a12b29ab6f66a84a74480144c29083ccfe5 Mon Sep 17 00:00:00 2001 From: Andrew Swan Date: Thu, 6 Sep 2018 16:31:33 -0700 Subject: [PATCH 26/44] Bug 1451503 Allow addonStartup.registerChrome() to register contentaccesible content packages r=kmag Differential Revision: https://phabricator.services.mozilla.com/D5231 --HG-- extra : rebase_source : 59d3ca4a98955eb2d6e968a9d05f481b08c7c599 extra : histedit_source : b3f0500b11626f78d6be5ed3a9c759c65fc2fef3 --- .../extensions/AddonManagerStartup.cpp | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/toolkit/mozapps/extensions/AddonManagerStartup.cpp b/toolkit/mozapps/extensions/AddonManagerStartup.cpp index b9f3a18f0103..dc1b6ade9396 100644 --- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -663,6 +663,30 @@ AddonManagerStartup::InitializeURLPreloader() namespace { static bool sObserverRegistered; +struct ContentEntry final +{ + explicit ContentEntry(nsTArray& aArgs, uint8_t aFlags=0) + : mArgs(aArgs) + , mFlags(aFlags) + {} + + ContentEntry(const ContentEntry& other) + : mArgs(other.mArgs) + , mFlags(other.mFlags) + {} + + AutoTArray mArgs; + uint8_t mFlags; +}; + +}; // anonymous namespace +}; // namespace mozilla + +DECLARE_USE_COPY_CONSTRUCTORS(mozilla::ContentEntry); + +namespace mozilla { +namespace { + class RegistryEntries final : public nsIJSRAIIHelper , public LinkedListElement { @@ -671,10 +695,9 @@ public: NS_DECL_NSIJSRAIIHELPER using Override = AutoTArray; - using Content = AutoTArray; using Locale = AutoTArray; - RegistryEntries(FileLocation& location, nsTArray&& overrides, nsTArray&& content, nsTArray&& locales) + RegistryEntries(FileLocation& location, nsTArray&& overrides, nsTArray&& content, nsTArray&& locales) : mLocation(location) , mOverrides(std::move(overrides)) , mContent(std::move(content)) @@ -692,7 +715,7 @@ protected: private: FileLocation mLocation; const nsTArray mOverrides; - const nsTArray mContent; + const nsTArray mContent; const nsTArray mLocales; }; @@ -710,9 +733,9 @@ RegistryEntries::Register() cr->ManifestOverride(context, 0, const_cast(args), 0); } - for (auto& content: mContent) { - const char* args[] = {content[0].get(), content[1].get()}; - cr->ManifestContent(context, 0, const_cast(args), 0); + for (auto& content : mContent) { + const char* args[] = {content.mArgs[0].get(), content.mArgs[1].get()}; + cr->ManifestContent(context, 0, const_cast(args), content.mFlags); } for (auto& locale : mLocales) { @@ -760,7 +783,7 @@ AddonManagerStartup::RegisterChrome(nsIURI* manifestURI, JS::HandleValue locatio nsTArray locales; - nsTArray content; + nsTArray content; nsTArray overrides; JS::RootedObject locs(cx, &locations.toObject()); @@ -789,8 +812,14 @@ AddonManagerStartup::RegisterChrome(nsIURI* manifestURI, JS::HandleValue locatio NS_ENSURE_TRUE(vals.Length() == 2, NS_ERROR_INVALID_ARG); overrides.AppendElement(vals); } else if (type.EqualsLiteral("content")) { - NS_ENSURE_TRUE(vals.Length() == 2, NS_ERROR_INVALID_ARG); - content.AppendElement(vals); + if (vals.Length() == 3 && vals[2].EqualsLiteral("contentaccessible=yes")) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), NS_ERROR_INVALID_ARG); + vals.RemoveElementAt(2); + content.AppendElement(ContentEntry(vals, nsChromeRegistry::CONTENT_ACCESSIBLE)); + } else { + NS_ENSURE_TRUE(vals.Length() == 2, NS_ERROR_INVALID_ARG); + content.AppendElement(ContentEntry(vals)); + } } else if (type.EqualsLiteral("locale")) { NS_ENSURE_TRUE(vals.Length() == 3, NS_ERROR_INVALID_ARG); locales.AppendElement(vals); From 62bcb25a7c5dda799e294af67a93ddf660c38c0a Mon Sep 17 00:00:00 2001 From: Andrew Swan Date: Thu, 6 Sep 2018 16:01:39 -0700 Subject: [PATCH 27/44] Bug 1451503 Move most reftest resources from chrome: to resource: r=kmag Differential Revision: https://phabricator.services.mozilla.com/D5232 --HG-- extra : rebase_source : 00223dacf6cfdfc4bb8505405844f66c7134e2c0 extra : histedit_source : 2d1f6e353e394520038c05b07bcd08ce06908cf2 --- editor/libeditor/crashtests/1444630.html | 3 +-- editor/reftests/xul/autocomplete-ref.xul | 2 +- editor/reftests/xul/empty-ref.xul | 2 +- editor/reftests/xul/emptyautocomplete-ref.xul | 2 +- editor/reftests/xul/emptymultiline-1.xul | 2 +- editor/reftests/xul/emptymultiline-2.xul | 2 +- editor/reftests/xul/emptymultiline-ref.xul | 2 +- editor/reftests/xul/emptytextbox-ref.xul | 2 +- editor/reftests/xul/number-ref.xul | 2 +- editor/reftests/xul/numberwithvalue-ref.xul | 2 +- editor/reftests/xul/passwd-ref.xul | 2 +- editor/reftests/xul/plain-ref.xul | 2 +- editor/reftests/xul/textbox-ref.xul | 2 +- .../progress/bar-pseudo-element-ref.html | 2 +- .../progress/bar-pseudo-element-rtl-ref.html | 2 +- .../bar-pseudo-element-vertical-ref.html | 2 +- .../bar-pseudo-element-vertical-rtl-ref.html | 2 +- .../forms/progress/block-invalidate-ref.html | 2 +- .../reftests/forms/progress/in-cells-ref.html | 2 +- .../indeterminate-style-height-ref.html | 2 +- .../indeterminate-style-width-ref.html | 2 +- .../forms/progress/margin-padding-ref.html | 2 +- .../progress/margin-padding-rtl-ref.html | 2 +- .../progress/margin-padding-vertical-ref.html | 2 +- .../margin-padding-vertical-rtl-ref.html | 2 +- .../forms/progress/transformations-ref.html | 2 +- .../reftests/forms/progress/values-ref.html | 2 +- .../forms/progress/values-rtl-ref.html | 2 +- .../forms/progress/values-vertical-ref.html | 2 +- .../progress/values-vertical-rtl-ref.html | 2 +- layout/tools/reftest/bootstrap.js | 11 +++++++--- layout/tools/reftest/jar.mn | 21 ++++++++++--------- layout/tools/reftest/manifest.jsm | 4 ++-- layout/tools/reftest/reftest-content.js | 2 +- layout/tools/reftest/reftest.jsm | 12 +++++------ layout/tools/reftest/reftest.xul | 2 +- ...rogressbar-fallback-default-style-ref.html | 2 +- 37 files changed, 60 insertions(+), 55 deletions(-) diff --git a/editor/libeditor/crashtests/1444630.html b/editor/libeditor/crashtests/1444630.html index eb0e707fb1cd..4c86cf39a66a 100644 --- a/editor/libeditor/crashtests/1444630.html +++ b/editor/libeditor/crashtests/1444630.html @@ -7,8 +7,7 @@ function load() let textarea = document.getElementById("editor"); textarea.focus(); - SpecialPowers.Cu.import( - "chrome://reftest/content/AsyncSpellCheckTestHelper.jsm") + SpecialPowers.Cu.import("resource://reftest/AsyncSpellCheckTestHelper.jsm") .onSpellCheck(textarea, () => { let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false); let sc = isc.spellChecker; diff --git a/editor/reftests/xul/autocomplete-ref.xul b/editor/reftests/xul/autocomplete-ref.xul index 92c9997b5cf9..7c3afec3a6d4 100644 --- a/editor/reftests/xul/autocomplete-ref.xul +++ b/editor/reftests/xul/autocomplete-ref.xul @@ -1,6 +1,6 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/layout/reftests/forms/progress/in-cells-ref.html b/layout/reftests/forms/progress/in-cells-ref.html index db8ace4387ab..08917e43f4a0 100644 --- a/layout/reftests/forms/progress/in-cells-ref.html +++ b/layout/reftests/forms/progress/in-cells-ref.html @@ -1,6 +1,6 @@ - +