author | Bogdan Tara <btara@mozilla.com> |
Sun, 20 Jan 2019 11:52:26 +0200 | |
changeset 454622 | 70857e6af679bed8ffcf7f4c9ae248bc26424c15 |
parent 454621 | 965622da5962b6cfd9e9e8b5332896e1634070e1 (current diff) |
parent 454619 | 219bc50b5cd5ac8b374cac6531ece44dddf4bfff (diff) |
child 454623 | fd43da99e9da96a448df3c0a21ed1e9e16482bcc |
push id | 35405 |
push user | dvarga@mozilla.com |
push date | Sun, 20 Jan 2019 21:36:32 +0000 |
treeherder | mozilla-central@666abafd77b1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 66.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
|
--- 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| {