Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Sun, 20 Jan 2019 11:52:26 +0200
changeset 511725 70857e6af679bed8ffcf7f4c9ae248bc26424c15
parent 511724 965622da5962b6cfd9e9e8b5332896e1634070e1 (current diff)
parent 511722 219bc50b5cd5ac8b374cac6531ece44dddf4bfff (diff)
child 511726 fd43da99e9da96a448df3c0a21ed1e9e16482bcc
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
--- a/devtools/client/accessibility/accessibility.css
+++ b/devtools/client/accessibility/accessibility.css
@@ -223,40 +223,38 @@ body {
 .treeTable .treeRow.highlighted:not(.selected) {
   background-color: var(--theme-selection-background-hover);
 }
 
 .treeTable .treeLabelCell {
   min-width: 50%;
 }
 
-.treeTable:focus,
-.treeTable > tbody:focus {
+.treeTable:focus {
   outline: 0;
 }
 
-.treeTable::-moz-focus-inner,
-.treeTable > tbody::-moz-focus-inner {
+.treeTable::-moz-focus-inner {
   border: 0;
 }
 
-.treeTable:not(:focus) tbody:not(:focus) .treeRow.selected {
+.treeTable:not(:focus) .treeRow.selected {
   background-color: var(--accessibility-unfocused-tree-focused-node-background);
 }
 
-.treeTable:not(:focus) tbody:not(:focus) .treeRow.selected .theme-twisty {
+.treeTable:not(:focus) .treeRow.selected .theme-twisty {
   fill: var(--accessibility-unfocused-tree-focused-node-twisty-fill);
 }
 
-.treeTable:not(:focus) tbody:not(:focus) .treeRow.selected *,
-.treeTable:not(:focus) tbody:not(:focus) .treeRow.selected .treeLabelCell:after {
+.treeTable:not(:focus) .treeRow.selected *,
+.treeTable:not(:focus) .treeRow.selected .treeLabelCell:after {
   color: inherit;
 }
 
-.treeTable:not(:focus) tbody:not(:focus) .treeRow.selected .objectBox-string {
+.treeTable:not(:focus) .treeRow.selected .objectBox-string {
   color: var(--string-color);
 }
 
 .treeTable > thead {
   pointer-events: none;
 }
 
 .treeTable > tbody tr {
--- a/devtools/client/accessibility/components/AccessibilityRow.js
+++ b/devtools/client/accessibility/components/AccessibilityRow.js
@@ -56,16 +56,17 @@ class HighlightableTreeRowClass extends 
 const HighlightableTreeRow = createFactory(HighlightableTreeRowClass);
 
 // Component that expands TreeView's own TreeRow and is responsible for
 // rendering an accessible object.
 class AccessibilityRow extends Component {
   static get propTypes() {
     return {
       ...TreeRow.propTypes,
+      hasContextMenu: PropTypes.bool.isRequired,
       dispatch: PropTypes.func.isRequired,
       walker: PropTypes.object,
     };
   }
 
   componentDidMount() {
     const { selected, object } = this.props.member;
     if (selected) {
@@ -188,34 +189,30 @@ class AccessibilityRow extends Component
 
     menu.popup(e.screenX, e.screenY, gToolbox);
 
     if (gTelemetry) {
       gTelemetry.scalarAdd(TELEMETRY_ACCESSIBLE_CONTEXT_MENU_OPENED, 1);
     }
   }
 
-  get hasContextMenu() {
-    const { supports } = this.props;
-    return supports.snapshot;
-  }
-
   /**
    * Render accessible row component.
    * @returns acecssible-row React component.
    */
   render() {
     const { object } = this.props.member;
     const props = Object.assign({}, this.props, {
-      onContextMenu: this.hasContextMenu && (e => this.onContextMenu(e)),
+      onContextMenu: this.props.hasContextMenu && (e => this.onContextMenu(e)),
       onMouseOver: () => this.highlight(object),
       onMouseOut: () => this.unhighlight(),
     });
 
     return (HighlightableTreeRow(props));
   }
 }
 
 const mapStateToProps = ({ ui }) => ({
   supports: ui.supports,
 });
 
-module.exports = connect(mapStateToProps)(AccessibilityRow);
+module.exports =
+  connect(mapStateToProps, null, null, { withRef: true })(AccessibilityRow);
--- a/devtools/client/accessibility/components/AccessibilityTree.js
+++ b/devtools/client/accessibility/components/AccessibilityTree.js
@@ -29,16 +29,17 @@ class AccessibilityTree extends Componen
   static get propTypes() {
     return {
       walker: PropTypes.object,
       dispatch: PropTypes.func.isRequired,
       accessibles: PropTypes.object,
       expanded: PropTypes.object,
       selected: PropTypes.string,
       highlighted: PropTypes.object,
+      supports: PropTypes.object,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.onNameChange = this.onNameChange.bind(this);
     this.onReorder = this.onReorder.bind(this);
@@ -132,31 +133,37 @@ class AccessibilityTree extends Componen
     }];
 
     const {
       accessibles,
       dispatch,
       expanded,
       selected,
       highlighted: highlightedItem,
+      supports,
       walker,
     } = this.props;
 
+    // Historically, the first context menu item is snapshot function and it is available
+    // for all accessible object.
+    const hasContextMenu = supports.snapshot;
+
     const renderValue = props => {
       return Rep(Object.assign({}, props, {
         defaultRep: Grip,
         cropLimit: 50,
       }));
     };
 
     const renderRow = rowProps => {
       const { object } = rowProps.member;
       const highlighted = object === highlightedItem;
       return AccessibilityRow(Object.assign({}, rowProps, {
         walker,
+        hasContextMenu,
         highlighted,
         decorator: {
           getRowClass: function() {
             return highlighted ? ["highlighted"] : [];
           },
         },
       }));
     };
@@ -175,21 +182,33 @@ class AccessibilityTree extends Componen
         selected,
         onClickRow(nodePath, event) {
           event.stopPropagation();
           if (event.target.classList.contains("theme-twisty")) {
             this.toggle(nodePath);
           }
           this.selectRow(event.currentTarget);
         },
+        onContextMenuTree: hasContextMenu && function(e) {
+          // If context menu event is triggered on (or bubbled to) the TreeView, it was
+          // done via keyboard. Open context menu for currently selected row.
+          let row = this.getSelectedRow();
+          if (!row) {
+            return;
+          }
+
+          row = row.getWrappedInstance();
+          row.onContextMenu(e);
+        },
       })
     );
   }
 }
 
 const mapStateToProps = ({ accessibles, ui }) => ({
   accessibles,
   expanded: ui.expanded,
   selected: ui.selected,
+  supports: ui.supports,
   highlighted: ui.highlighted,
 });
 // Exports from this module
 module.exports = connect(mapStateToProps)(AccessibilityTree);
--- a/devtools/client/accessibility/test/mochitest/test_accessible_row_context_menu.html
+++ b/devtools/client/accessibility/test/mochitest/test_accessible_row_context_menu.html
@@ -57,19 +57,16 @@ window.onload = async function() {
       const provider = createElement(Provider, { store: mockStore }, accRow);
 
       container = document.createElement("div");
       container.id = "container";
       document.body.appendChild(container);
       return ReactDOM.render(provider, container);
     }
 
-    const checker = Symbol();
-    let isCalled;
-
     const ROW_ID = "test-row";
     const JSON_URL_PREFIX = "data:application/json;charset=UTF-8,";
     const SNAPSHOT = { "snapshot": true };
     const defaultProps = {
       id: ROW_ID,
       member: {
         object: {
           name: "test",
@@ -82,23 +79,22 @@ window.onload = async function() {
       },
       columns: [
         { "id": "default", "title": "role" },
         { "id": "value", "title": "name" },
       ],
       provider: {
         getValue: (object, id) => object[id],
       },
+      hasContextMenu: true,
     };
 
     const mockProps = {
       ...defaultProps,
-      onContextMenu: () => {
-        isCalled = true;
-      },
+      hasContextMenu: null,
     };
 
     const defaultState = { ui: { supports: { snapshot: true }}};
     const mockState = { ui: { supports: {}}};
 
     info("Check contextmenu default behaviour.");
     renderAccessibilityRow(defaultProps, defaultState);
     let row = document.getElementById(ROW_ID);
@@ -128,35 +124,35 @@ window.onload = async function() {
 
       const [ url, where ] = await openWebLinkInPromise;
       is(url, `${JSON_URL_PREFIX}${encodeURIComponent(JSON.stringify(SNAPSHOT))}`,
          "Correct URL is opened");
       is(where, "tab", "URL was opened correctly");
 
       // Reset @openWebLinkIn to default.
       browserWindow.openWebLinkIn = defaultOpenWebLinkIn;
+      menu.remove();
     });
 
     info("Check accessibility row when context menu is not supported.");
-    renderAccessibilityRow(mockProps, mockState);
+    renderAccessibilityRow(defaultProps, mockState);
     row = document.getElementById(ROW_ID);
 
     info("Check contextmenu listener is not called when context menu is not supported.");
-    isCalled = checker;
     Simulate.contextMenu(row);
-    ok(isCalled === checker, "contextmenu event handler was never called.");
+    let menu = document.getElementById("accessibility-row-contextmenu");
+    ok(!menu, "contextmenu event handler was never called.");
 
-    info("Check accessibility row when context menu is supported.");
+    info("Check accessibility row when no context menu is available.");
     renderAccessibilityRow(mockProps, defaultState);
     row = document.getElementById(ROW_ID);
 
-    info("Check contextmenu listener is called when context menu is supported.");
-    isCalled = checker;
     Simulate.contextMenu(row);
-    ok(isCalled, "contextmenu event handler was called correctly.");
+    menu = document.getElementById("accessibility-row-contextmenu");
+    ok(!menu, "contextmenu event handler was never called.");
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 };
 </script>
 </pre>
--- a/devtools/client/shared/components/tree/TreeView.js
+++ b/devtools/client/shared/components/tree/TreeView.js
@@ -112,16 +112,18 @@ define(function(require, exports, module
         // Selected node
         selected: PropTypes.string,
         // Custom filtering callback
         onFilter: PropTypes.func,
         // Custom sorting callback
         onSort: PropTypes.func,
         // Custom row click callback
         onClickRow: PropTypes.func,
+        // Tree context menu event handler
+        onContextMenuTree: PropTypes.func,
         // A header is displayed if set to true
         header: PropTypes.bool,
         // Long string is expandable by a toggle button
         expandableStrings: PropTypes.bool,
         // Array of columns
         columns: PropTypes.arrayOf(PropTypes.shape({
           id: PropTypes.string.isRequired,
           title: PropTypes.string,
@@ -507,18 +509,18 @@ define(function(require, exports, module
       return rows;
     }
 
     render() {
       const root = this.props.object;
       const classNames = ["treeTable"];
       this.rows = [];
 
+      const { className, onContextMenuTree } = this.props;
       // Use custom class name from props.
-      const className = this.props.className;
       if (className) {
         classNames.push(...className.split(" "));
       }
 
       // Alright, let's render all tree rows. The tree is one big <table>.
       let rows = this.renderRows(root, 0, "");
 
       // This happens when the view needs to do initial asynchronous
@@ -536,24 +538,24 @@ define(function(require, exports, module
         dom.table({
           className: classNames.join(" "),
           role: "tree",
           ref: tree => {
             this.tree = tree;
           },
           tabIndex: 0,
           onKeyDown: this.onKeyDown,
+          onContextMenu: onContextMenuTree && onContextMenuTree.bind(this),
           "aria-label": this.props.label || "",
           "aria-activedescendant": this.state.selected,
           cellPadding: 0,
           cellSpacing: 0},
           TreeHeader(props),
           dom.tbody({
             role: "presentation",
-            tabIndex: -1,
           }, rows)
         )
       );
     }
   }
 
   // Helpers
 
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -116,16 +116,20 @@ const MAX_SURFACE_SIZE: f32 = 4096.0;
 /// The maximum number of primitives to look for in a display
 /// list, trying to find unique primitives.
 const MAX_PRIMS_TO_SEARCH: usize = 128;
 
 /// Used to get unique tile IDs, even when the tile cache is
 /// destroyed between display lists / scenes.
 static NEXT_TILE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
 
+fn clamp(value: i32, low: i32, high: i32) -> i32 {
+    value.max(low).min(high)
+}
+
 /// Information about the state of an opacity binding.
 #[derive(Debug)]
 pub struct OpacityBindingInfo {
     /// The current value retrieved from dynamic scene properties.
     value: f32,
     /// True if it was changed (or is new) since the last frame build.
     changed: bool,
 }
@@ -693,26 +697,32 @@ impl TileCache {
     fn get_tile_coords_for_rect(
         &self,
         rect: &WorldRect,
     ) -> (TileOffset, TileOffset) {
         // Translate the rectangle into the virtual tile space
         let origin = rect.origin - self.world_origin;
 
         // Get the tile coordinates in the picture space.
-        let p0 = TileOffset::new(
+        let mut p0 = TileOffset::new(
             (origin.x / self.world_tile_size.width).floor() as i32,
             (origin.y / self.world_tile_size.height).floor() as i32,
         );
 
-        let p1 = TileOffset::new(
+        let mut p1 = TileOffset::new(
             ((origin.x + rect.size.width) / self.world_tile_size.width).ceil() as i32,
             ((origin.y + rect.size.height) / self.world_tile_size.height).ceil() as i32,
         );
 
+        // Clamp the tile coordinates here to avoid looping over irrelevant tiles later on.
+        p0.x = clamp(p0.x, 0, self.tile_count.width);
+        p0.y = clamp(p0.y, 0, self.tile_count.height);
+        p1.x = clamp(p1.x, 0, self.tile_count.width);
+        p1.y = clamp(p1.y, 0, self.tile_count.height);
+
         (p0, p1)
     }
 
     /// Update transforms, opacity bindings and tile rects.
     pub fn pre_update(
         &mut self,
         pic_rect: LayoutRect,
         frame_context: &FrameVisibilityContext,
@@ -1228,22 +1238,16 @@ impl TileCache {
             .map_local_to_world
             .map(&culling_rect)
             .expect("bug: unable to map local clip rect");
 
         // Normalize the tile coordinates before adding to tile dependencies.
         // For each affected tile, mark any of the primitive dependencies.
         for y in p0.y .. p1.y {
             for x in p0.x .. p1.x {
-                // If the primitive exists on tiles outside the selected tile cache
-                // area, just ignore those.
-                if x < 0 || x >= self.tile_count.width || y < 0 || y >= self.tile_count.height {
-                    continue;
-                }
-
                 let index = (y * self.tile_count.width + x) as usize;
                 let tile = &mut self.tiles[index];
 
                 // Store the local clip rect by calculating what portion
                 // of the tile it covers.
                 let world_culling_rect = world_culling_rect
                     .intersection(&tile.world_rect)
                     .map(|rect| {