diff --git a/devtools/server/actors/accessibility/accessible.js b/devtools/server/actors/accessibility/accessible.js index 44d6317520be..da6e6cc90b72 100644 --- a/devtools/server/actors/accessibility/accessible.js +++ b/devtools/server/actors/accessibility/accessible.js @@ -241,7 +241,7 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, { let targetAcc; try { - targetAcc = this.walker.attachAccessible(target, doc); + targetAcc = this.walker.attachAccessible(target, doc.rawAccessible); } catch (e) { // Target is not available. } diff --git a/devtools/server/actors/accessibility/walker.js b/devtools/server/actors/accessibility/walker.js index 37916e0a413c..3343d5acacd1 100644 --- a/devtools/server/actors/accessibility/walker.js +++ b/devtools/server/actors/accessibility/walker.js @@ -13,12 +13,16 @@ loader.lazyRequireGetter(this, "AccessibleActor", "devtools/server/actors/access loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true); loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils"); loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter"); +loader.lazyRequireGetter(this, "getCurrentZoom", "devtools/shared/layout/utils", true); +loader.lazyRequireGetter(this, "InspectorUtils", "InspectorUtils"); loader.lazyRequireGetter(this, "isDefunct", "devtools/server/actors/utils/accessibility", true); loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true); loader.lazyRequireGetter(this, "isWindowIncluded", "devtools/shared/layout/utils", true); loader.lazyRequireGetter(this, "isXUL", "devtools/server/actors/highlighters/utils/markup", true); loader.lazyRequireGetter(this, "register", "devtools/server/actors/highlighters", true); +const kStateHover = 0x00000004; // NS_EVENT_STATE_HOVER + const nsIAccessibleEvent = Ci.nsIAccessibleEvent; const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent; const nsIAccessibleRole = Ci.nsIAccessibleRole; @@ -126,6 +130,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { this.setA11yServiceGetter(); this.onPick = this.onPick.bind(this); this.onHovered = this.onHovered.bind(this); + this._preventContentEvent = this._preventContentEvent.bind(this); this.onKey = this.onKey.bind(this); this.onHighlighterEvent = this.onHighlighterEvent.bind(this); }, @@ -513,6 +518,24 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { _preventContentEvent(event) { event.stopPropagation(); event.preventDefault(); + + const target = event.originalTarget || event.target; + if (target !== this._currentTarget) { + this._resetStateAndReleaseTarget(); + this._currentTarget = target; + // We use InspectorUtils to save the original hover content state of the target + // element (that includes its hover state). In order to not trigger any visual + // changes to the element that depend on its hover state we remove the state while + // the element is the most current target of the highlighter. + // + // TODO: This logic can be removed if/when we can use elementsAtPoint API for + // determining topmost DOMNode that corresponds to specific coordinates. We would + // then be able to use a highlighter overlay that would prevent all pointer events + // to content but still render highlighter for the node/element correctly. + this._currentTargetHoverState = + InspectorUtils.getContentState(target) & kStateHover; + InspectorUtils.removeContentState(target, kStateHover); + } }, /** @@ -521,7 +544,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { * @param {Object} event * Current click event. */ - async onPick(event) { + onPick(event) { if (!this._isPicking) { return; } @@ -535,16 +558,16 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { // the client, but don't stop picking. if (event.shiftKey) { if (!this._currentAccessible) { - this._currentAccessible = await this._findAndAttachAccessible(event); + this._currentAccessible = this._findAndAttachAccessible(event); } events.emit(this, "picker-accessible-previewed", this._currentAccessible); return; } - this._stopPickerListeners(); + this._unsetPickerEnvironment(); this._isPicking = false; if (!this._currentAccessible) { - this._currentAccessible = await this._findAndAttachAccessible(event); + this._currentAccessible = this._findAndAttachAccessible(event); } events.emit(this, "picker-accessible-picked", this._currentAccessible); }, @@ -555,7 +578,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { * @param {Object} event * Current hover event. */ - async onHovered(event) { + onHovered(event) { if (!this._isPicking) { return; } @@ -565,7 +588,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { return; } - const accessible = await this._findAndAttachAccessible(event); + const accessible = this._findAndAttachAccessible(event); if (!accessible) { return; } @@ -626,7 +649,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { pick: function() { if (!this._isPicking) { this._isPicking = true; - this._startPickerListeners(); + this._setPickerEnvironment(); } }, @@ -651,7 +674,7 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { // defunct and accessing it via parent property will throw. try { let parent = accessible; - while (parent && parent != accessibleDocument) { + while (parent && parent.rawAccessible != accessibleDocument) { parent = parent.parentAcc; } } catch (error) { @@ -662,39 +685,59 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, { }, /** - * Find accessible object that corresponds to a DOMNode and attach (lookup its - * ancestry to the root doc) to the AccessibilityWalker tree. + * When RDM is used, users can set custom DPR values that are different from the device + * they are using. Store true screenPixelsPerCSSPixel value to be able to use accessible + * highlighter features correctly. + */ + get pixelRatio() { + const { contentViewer } = this.targetActor.docShell; + const { windowUtils } = this.rootWin; + const overrideDPPX = contentViewer.overrideDPPX; + let ratio; + if (overrideDPPX) { + contentViewer.overrideDPPX = 0; + ratio = windowUtils.screenPixelsPerCSSPixel; + contentViewer.overrideDPPX = overrideDPPX; + } else { + ratio = windowUtils.screenPixelsPerCSSPixel; + } + + return ratio; + }, + + /** + * Find deepest accessible object that corresponds to the screen coordinates of the + * mouse pointer and attach it to the AccessibilityWalker tree. * * @param {Object} event * Correspoinding content event. * @return {null|Object} * Accessible object, if available, that corresponds to a DOM node. */ - async _findAndAttachAccessible(event) { - let target = event.originalTarget || event.target; - let rawAccessible; - // Find a first accessible object in the target's ancestry, including - // target. Note: not all DOM nodes have corresponding accessible objects - // (for example, a