mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Bug 1349275 - refactored moveInfobar
function; r=pbro
- Added `getViewportDimensions` - Added `getComputedStylePropertyValue` to `CanvasFrameAnonymousContentHelper` - Refactored totally `moveInfobar` to works with both APZ enabled and new positioned absolutely highlighters - Updated `AutoRefreshHighlighter` for having a `scrollUpdate` method. - Updated tests MozReview-Commit-ID: 5m31ZzRzLXr --HG-- extra : rebase_source : c5abac64217ee0b86413594461fb6a50d5df655e
This commit is contained in:
parent
c4f1f8e321
commit
c2450fbc4d
@ -12,8 +12,8 @@ const TEST_URL = "data:text/html;charset=utf-8,<div>zoom me</div>";
|
||||
// TEST_LEVELS entries should contain the zoom level to test.
|
||||
const TEST_LEVELS = [2, 1, .5];
|
||||
|
||||
// Returns the expected style attribute value to check for on the root highlighter
|
||||
// element, for the values given.
|
||||
// Returns the expected style attribute value to check for on the highlighter's elements
|
||||
// node, for the values given.
|
||||
const expectedStyle = (w, h, z) =>
|
||||
(z !== 1 ? `transform-origin:top left; transform:scale(${1 / z}); ` : "") +
|
||||
`position:absolute; width:${w * z}px;height:${h * z}px; ` +
|
||||
@ -40,7 +40,7 @@ add_task(function* () {
|
||||
|
||||
info("Check that the highlighter root wrapper node was scaled down");
|
||||
|
||||
let style = yield getRootNodeStyle(testActor);
|
||||
let style = yield getElementsNodeStyle(testActor);
|
||||
let { width, height } = yield testActor.getWindowDimensions();
|
||||
is(style, expectedStyle(width, height, level),
|
||||
"The style attribute of the root element is correct");
|
||||
@ -60,8 +60,8 @@ function* hoverContainer(container, inspector) {
|
||||
yield onHighlight;
|
||||
}
|
||||
|
||||
function* getRootNodeStyle(testActor) {
|
||||
function* getElementsNodeStyle(testActor) {
|
||||
let value = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-root", "style");
|
||||
"box-model-elements", "style");
|
||||
return value;
|
||||
}
|
||||
|
@ -18,14 +18,16 @@ add_task(function* () {
|
||||
tag: "div",
|
||||
id: "top",
|
||||
classes: ".class1.class2",
|
||||
dims: "500" + " \u00D7 " + "100"
|
||||
dims: "500" + " \u00D7 " + "100",
|
||||
arrowed: true
|
||||
},
|
||||
{
|
||||
selector: "#vertical",
|
||||
position: "overlap",
|
||||
position: "top",
|
||||
tag: "div",
|
||||
id: "vertical",
|
||||
classes: ""
|
||||
classes: "",
|
||||
arrowed: false
|
||||
// No dims as they will vary between computers
|
||||
},
|
||||
{
|
||||
@ -34,13 +36,15 @@ add_task(function* () {
|
||||
tag: "div",
|
||||
id: "bottom",
|
||||
classes: "",
|
||||
dims: "500" + " \u00D7 " + "100"
|
||||
dims: "500" + " \u00D7 " + "100",
|
||||
arrowed: true
|
||||
},
|
||||
{
|
||||
selector: "body",
|
||||
position: "bottom",
|
||||
tag: "body",
|
||||
classes: ""
|
||||
classes: "",
|
||||
arrowed: true
|
||||
// No dims as they will vary between computers
|
||||
},
|
||||
{
|
||||
@ -48,7 +52,8 @@ add_task(function* () {
|
||||
position: "bottom",
|
||||
tag: "clipPath",
|
||||
id: "clip",
|
||||
classes: ""
|
||||
classes: "",
|
||||
arrowed: false
|
||||
// No dims as element is not displayed and we just want to test tag name
|
||||
},
|
||||
];
|
||||
@ -81,6 +86,11 @@ function* testPosition(test, inspector, testActor) {
|
||||
"box-model-infobar-classes");
|
||||
is(classes, test.classes, "node " + test.selector + ": classes match.");
|
||||
|
||||
let arrowed = !(yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-infobar-container", "hide-arrow"));
|
||||
|
||||
is(arrowed, test.arrowed, "node " + test.selector + ": arrow visibility match.");
|
||||
|
||||
if (test.dims) {
|
||||
let dims = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-infobar-dimensions");
|
||||
|
@ -14,7 +14,7 @@ add_task(function* () {
|
||||
let testData = {
|
||||
selector: "body",
|
||||
position: "overlap",
|
||||
style: "top:0px",
|
||||
style: "position:fixed",
|
||||
};
|
||||
|
||||
yield testPositionAndStyle(testData, inspector, testActor);
|
||||
@ -28,7 +28,7 @@ function* testPositionAndStyle(test, inspector, testActor) {
|
||||
let style = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-infobar-container", "style");
|
||||
|
||||
is(style.split(";")[0], test.style,
|
||||
is(style.split(";")[0].trim(), test.style,
|
||||
"Infobar shows on top of the page when page isn't scrolled");
|
||||
|
||||
yield testActor.scrollWindow(0, 500);
|
||||
@ -36,6 +36,6 @@ function* testPositionAndStyle(test, inspector, testActor) {
|
||||
style = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-infobar-container", "style");
|
||||
|
||||
is(style.split(";")[0], test.style,
|
||||
is(style.split(";")[0].trim(), test.style,
|
||||
"Infobar shows on top of the page even if the page is scrolled");
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
--highlighter-bubble-text-color: hsl(216, 33%, 97%);
|
||||
--highlighter-bubble-background-color: hsl(214, 13%, 24%);
|
||||
--highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
|
||||
--highlighter-bubble-arrow-size: 8px;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,15 +142,11 @@
|
||||
border: 1px solid var(--highlighter-bubble-border-color);
|
||||
}
|
||||
|
||||
:-moz-native-anonymous [class$=infobar-container][hide-arrow] > [class$=infobar] {
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
/* Arrows */
|
||||
|
||||
:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before {
|
||||
left: calc(50% - 8px);
|
||||
border: 8px solid var(--highlighter-bubble-border-color);
|
||||
left: calc(50% - var(--highlighter-bubble-arrow-size));
|
||||
border: var(--highlighter-bubble-arrow-size) solid var(--highlighter-bubble-border-color);
|
||||
}
|
||||
|
||||
:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after {
|
||||
|
@ -72,6 +72,7 @@ function AutoRefreshHighlighter(highlighterEnv) {
|
||||
this.currentQuads = {};
|
||||
|
||||
this._winDimensions = getWindowDimensions(this.win);
|
||||
this._scroll = { x: this.win.pageXOffset, y: this.win.pageYOffset };
|
||||
|
||||
this.update = this.update.bind(this);
|
||||
}
|
||||
@ -192,6 +193,21 @@ AutoRefreshHighlighter.prototype = {
|
||||
return areQuadsDifferent(oldQuads, this.currentQuads, getCurrentZoom(this.win));
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the knowledge we have of the current window's scrolling offset, both
|
||||
* horizontal and vertical, and return `true` if they have changed since.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_hasWindowScrolled: function () {
|
||||
let { pageXOffset, pageYOffset } = this.win;
|
||||
let hasChanged = this._scroll.x !== pageXOffset ||
|
||||
this._scroll.y !== pageYOffset;
|
||||
|
||||
this._scroll = { x: pageXOffset, y: pageYOffset };
|
||||
|
||||
return hasChanged;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the knowledge we have of the current window's dimensions and return `true`
|
||||
* if they have changed since.
|
||||
@ -212,6 +228,12 @@ AutoRefreshHighlighter.prototype = {
|
||||
update: function () {
|
||||
if (!this._isNodeValid(this.currentNode) ||
|
||||
(!this._hasMoved() && !this._haveWindowDimensionsChanged())) {
|
||||
// At this point we're not calling the `_update` method. However, if the window has
|
||||
// scrolled, we want to invoke `_scrollUpdate`.
|
||||
if (this._hasWindowScrolled()) {
|
||||
this._scrollUpdate();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,10 +252,17 @@ AutoRefreshHighlighter.prototype = {
|
||||
// To be implemented by sub classes
|
||||
// When called, sub classes should update the highlighter shown for
|
||||
// this.currentNode
|
||||
// This is called as a result of a page scroll, zoom or repaint
|
||||
// This is called as a result of a page zoom or repaint
|
||||
throw new Error("Custom highlighter class had to implement _update method");
|
||||
},
|
||||
|
||||
_scrollUpdate: function () {
|
||||
// Can be implemented by sub classes
|
||||
// When called, sub classes can upate the highlighter shown for
|
||||
// this.currentNode
|
||||
// This is called as a result of a page scroll
|
||||
},
|
||||
|
||||
_hide: function () {
|
||||
// To be implemented by sub classes
|
||||
// When called, sub classes should actually hide the highlighter
|
||||
|
@ -352,6 +352,10 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
return shown;
|
||||
},
|
||||
|
||||
_scrollUpdate: function () {
|
||||
this._moveInfobar();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the highlighter, the outline and the infobar.
|
||||
*/
|
||||
@ -508,7 +512,7 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
}
|
||||
|
||||
// Un-zoom the root wrapper if the page was zoomed.
|
||||
let rootId = this.ID_CLASS_PREFIX + "root";
|
||||
let rootId = this.ID_CLASS_PREFIX + "elements";
|
||||
this.markup.scaleRootElement(this.currentNode, rootId);
|
||||
|
||||
return true;
|
||||
|
@ -5,7 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const { getCurrentZoom, getWindowDimensions,
|
||||
const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
|
||||
getRootBindingParent } = require("devtools/shared/layout/utils");
|
||||
const { on, emit } = require("sdk/event/core");
|
||||
|
||||
@ -38,10 +38,6 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const STYLESHEET_URI = "resource://devtools/server/actors/" +
|
||||
"highlighters.css";
|
||||
// How high is the infobar (px).
|
||||
const INFOBAR_HEIGHT = 34;
|
||||
// What's the size of the infobar arrow (px).
|
||||
const INFOBAR_ARROW_SIZE = 9;
|
||||
|
||||
const _tokens = Symbol("classList/tokens");
|
||||
|
||||
@ -249,10 +245,11 @@ function CanvasFrameAnonymousContentHelper(highlighterEnv, nodeBuilder) {
|
||||
this.highlighterEnv.on("window-ready", this._onWindowReady);
|
||||
|
||||
this.listeners = new Map();
|
||||
this.elements = new Map();
|
||||
}
|
||||
|
||||
CanvasFrameAnonymousContentHelper.prototype = {
|
||||
destroy: function () {
|
||||
destroy() {
|
||||
this._remove();
|
||||
this.highlighterEnv.off("window-ready", this._onWindowReady);
|
||||
this.highlighterEnv = this.nodeBuilder = this._content = null;
|
||||
@ -260,9 +257,10 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
this.anonymousContentGlobal = null;
|
||||
|
||||
this._removeAllListeners();
|
||||
this.elements.clear();
|
||||
},
|
||||
|
||||
_insert: function () {
|
||||
_insert() {
|
||||
let doc = this.highlighterEnv.document;
|
||||
// Wait for DOMContentLoaded before injecting the anonymous content.
|
||||
if (doc.readyState != "interactive" && doc.readyState != "complete") {
|
||||
@ -317,53 +315,52 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
* - when first attaching to a page
|
||||
* - when swapping frame loaders (moving tabs, toggling RDM)
|
||||
*/
|
||||
_onWindowReady: function (e, {isTopLevel}) {
|
||||
_onWindowReady(e, {isTopLevel}) {
|
||||
if (isTopLevel) {
|
||||
this._remove();
|
||||
this._removeAllListeners();
|
||||
this.elements.clear();
|
||||
this._insert();
|
||||
this.anonymousContentDocument = this.highlighterEnv.document;
|
||||
}
|
||||
},
|
||||
|
||||
getTextContentForElement: function (id) {
|
||||
if (!this.content) {
|
||||
return null;
|
||||
}
|
||||
return this.content.getTextContentForElement(id);
|
||||
getComputedStylePropertyValue(id, property) {
|
||||
return this.content && this.content.getComputedStylePropertyValue(id, property);
|
||||
},
|
||||
|
||||
setTextContentForElement: function (id, text) {
|
||||
getTextContentForElement(id) {
|
||||
return this.content && this.content.getTextContentForElement(id);
|
||||
},
|
||||
|
||||
setTextContentForElement(id, text) {
|
||||
if (this.content) {
|
||||
this.content.setTextContentForElement(id, text);
|
||||
}
|
||||
},
|
||||
|
||||
setAttributeForElement: function (id, name, value) {
|
||||
setAttributeForElement(id, name, value) {
|
||||
if (this.content) {
|
||||
this.content.setAttributeForElement(id, name, value);
|
||||
}
|
||||
},
|
||||
|
||||
getAttributeForElement: function (id, name) {
|
||||
if (!this.content) {
|
||||
return null;
|
||||
}
|
||||
return this.content.getAttributeForElement(id, name);
|
||||
getAttributeForElement(id, name) {
|
||||
return this.content && this.content.getAttributeForElement(id, name);
|
||||
},
|
||||
|
||||
removeAttributeForElement: function (id, name) {
|
||||
removeAttributeForElement(id, name) {
|
||||
if (this.content) {
|
||||
this.content.removeAttributeForElement(id, name);
|
||||
}
|
||||
},
|
||||
|
||||
hasAttributeForElement: function (id, name) {
|
||||
hasAttributeForElement(id, name) {
|
||||
return typeof this.getAttributeForElement(id, name) === "string";
|
||||
},
|
||||
|
||||
getCanvasContext: function (id, type = "2d") {
|
||||
return this.content ? this.content.getCanvasContext(id, type) : null;
|
||||
getCanvasContext(id, type = "2d") {
|
||||
return this.content && this.content.getCanvasContext(id, type);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -402,7 +399,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
* @param {String} type
|
||||
* @param {Function} handler
|
||||
*/
|
||||
addEventListenerForElement: function (id, type, handler) {
|
||||
addEventListenerForElement(id, type, handler) {
|
||||
if (typeof id !== "string") {
|
||||
throw new Error("Expected a string ID in addEventListenerForElement but" +
|
||||
" got: " + id);
|
||||
@ -426,7 +423,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
* @param {String} id
|
||||
* @param {String} type
|
||||
*/
|
||||
removeEventListenerForElement: function (id, type) {
|
||||
removeEventListenerForElement(id, type) {
|
||||
let listeners = this.listeners.get(type);
|
||||
if (!listeners) {
|
||||
return;
|
||||
@ -440,7 +437,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
handleEvent(event) {
|
||||
let listeners = this.listeners.get(event.type);
|
||||
if (!listeners) {
|
||||
return;
|
||||
@ -477,7 +474,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_removeAllListeners: function () {
|
||||
_removeAllListeners() {
|
||||
if (this.highlighterEnv && this.highlighterEnv.pageListenerTarget) {
|
||||
let target = this.highlighterEnv.pageListenerTarget;
|
||||
for (let [type] of this.listeners) {
|
||||
@ -487,14 +484,18 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
this.listeners.clear();
|
||||
},
|
||||
|
||||
getElement: function (id) {
|
||||
getElement(id) {
|
||||
if (this.elements.has(id)) {
|
||||
return this.elements.get(id);
|
||||
}
|
||||
|
||||
let classList = new ClassList(this.getAttributeForElement(id, "class"));
|
||||
|
||||
on(classList, "update", () => {
|
||||
this.setAttributeForElement(id, "class", classList.toString());
|
||||
});
|
||||
|
||||
return {
|
||||
let element = {
|
||||
getTextContent: () => this.getTextContentForElement(id),
|
||||
setTextContent: text => this.setTextContentForElement(id, text),
|
||||
setAttribute: (name, val) => this.setAttributeForElement(id, name, val),
|
||||
@ -508,8 +509,15 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
removeEventListener: (type, handler) => {
|
||||
return this.removeEventListenerForElement(id, type, handler);
|
||||
},
|
||||
computedStyle: {
|
||||
getPropertyValue: property => this.getComputedStylePropertyValue(id, property)
|
||||
},
|
||||
classList
|
||||
};
|
||||
|
||||
this.elements.set(id, element);
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
get content() {
|
||||
@ -540,7 +548,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
* should be used to read the current zoom value.
|
||||
* @param {String} id The ID of the root element inserted with this API.
|
||||
*/
|
||||
scaleRootElement: function (node, id) {
|
||||
scaleRootElement(node, id) {
|
||||
let boundaryWindow = this.highlighterEnv.window;
|
||||
let zoom = getCurrentZoom(node);
|
||||
// Hide the root element and force the reflow in order to get the proper window's
|
||||
@ -582,55 +590,91 @@ exports.CanvasFrameAnonymousContentHelper = CanvasFrameAnonymousContentHelper;
|
||||
* The window object.
|
||||
*/
|
||||
function moveInfobar(container, bounds, win) {
|
||||
let winHeight = win.innerHeight * getCurrentZoom(win);
|
||||
let winWidth = win.innerWidth * getCurrentZoom(win);
|
||||
let winScrollY = win.scrollY;
|
||||
let zoom = getCurrentZoom(win);
|
||||
let viewport = getViewportDimensions(win);
|
||||
|
||||
// Ensure that containerBottom and containerTop are at least zero to avoid
|
||||
// showing tooltips outside the viewport.
|
||||
let containerBottom = Math.max(0, bounds.bottom) + INFOBAR_ARROW_SIZE;
|
||||
let containerTop = Math.min(winHeight, bounds.top);
|
||||
let { computedStyle } = container;
|
||||
|
||||
// Can the bar be above the node?
|
||||
let top;
|
||||
if (containerTop < INFOBAR_HEIGHT) {
|
||||
// No. Can we move the bar under the node?
|
||||
if (containerBottom + INFOBAR_HEIGHT > winHeight) {
|
||||
// No. Let's move it inside. Can we show it at the top of the element?
|
||||
if (containerTop < winScrollY) {
|
||||
// No. Window is scrolled past the top of the element.
|
||||
top = 0;
|
||||
} else {
|
||||
// Yes. Show it at the top of the element
|
||||
top = containerTop;
|
||||
}
|
||||
container.setAttribute("position", "overlap");
|
||||
} else {
|
||||
// Yes. Let's move it under the node.
|
||||
top = containerBottom;
|
||||
container.setAttribute("position", "bottom");
|
||||
}
|
||||
} else {
|
||||
// Yes. Let's move it on top of the node.
|
||||
top = containerTop - INFOBAR_HEIGHT;
|
||||
container.setAttribute("position", "top");
|
||||
// To simplify, we use the same arrow's size value as margin's value for all four sides.
|
||||
let margin = parseFloat(computedStyle
|
||||
.getPropertyValue("--highlighter-bubble-arrow-size"));
|
||||
let containerHeight = parseFloat(computedStyle.getPropertyValue("height"));
|
||||
let containerWidth = parseFloat(computedStyle.getPropertyValue("width"));
|
||||
let containerHalfWidth = containerWidth / 2;
|
||||
|
||||
let viewportWidth = viewport.width * zoom;
|
||||
let viewportHeight = viewport.height * zoom;
|
||||
let { pageXOffset, pageYOffset } = win;
|
||||
|
||||
pageYOffset *= zoom;
|
||||
pageXOffset *= zoom;
|
||||
containerHeight += margin;
|
||||
|
||||
// Defines the boundaries for the infobar.
|
||||
let topBoundary = margin;
|
||||
let bottomBoundary = viewportHeight - containerHeight;
|
||||
let leftBoundary = containerHalfWidth + margin;
|
||||
let rightBoundary = viewportWidth - containerHalfWidth - margin;
|
||||
|
||||
// Set the default values.
|
||||
let top = bounds.y - containerHeight;
|
||||
let bottom = bounds.bottom + margin;
|
||||
let left = bounds.x + bounds.width / 2;
|
||||
let isOverlapTheNode = false;
|
||||
let positionAttribute = "top";
|
||||
let position = "absolute";
|
||||
|
||||
// Here we start the math.
|
||||
// We basically want to position absolutely the infobar, except when is pointing to a
|
||||
// node that is offscreen or partially offscreen, in a way that the infobar can't
|
||||
// be placed neither on top nor on bottom.
|
||||
// In such cases, the infobar will overlap the node, and to limit the latency given
|
||||
// by APZ (See Bug 1312103) it will be positioned as "fixed".
|
||||
// It's a sort of "position: sticky" (but positioned as absolute instead of relative).
|
||||
let canBePlacedOnTop = top >= pageYOffset;
|
||||
let canBePlacedOnBottom = bottomBoundary + pageYOffset - bottom > 0;
|
||||
|
||||
if (!canBePlacedOnTop && canBePlacedOnBottom) {
|
||||
top = bottom;
|
||||
positionAttribute = "bottom";
|
||||
}
|
||||
|
||||
// Align the bar with the box's center if possible.
|
||||
let left = bounds.right - bounds.width / 2;
|
||||
// Make sure the while infobar is visible.
|
||||
let buffer = 100;
|
||||
if (left < buffer) {
|
||||
left = buffer;
|
||||
container.setAttribute("hide-arrow", "true");
|
||||
} else if (left > winWidth - buffer) {
|
||||
left = winWidth - buffer;
|
||||
let isOffscreenOnTop = top < topBoundary + pageYOffset;
|
||||
let isOffscreenOnBottom = top > bottomBoundary + pageYOffset;
|
||||
let isOffscreenOnLeft = left < leftBoundary + pageXOffset;
|
||||
let isOffscreenOnRight = left > rightBoundary + pageXOffset;
|
||||
|
||||
if (isOffscreenOnTop) {
|
||||
top = topBoundary;
|
||||
isOverlapTheNode = true;
|
||||
} else if (isOffscreenOnBottom) {
|
||||
top = bottomBoundary;
|
||||
isOverlapTheNode = true;
|
||||
} else if (isOffscreenOnLeft || isOffscreenOnRight) {
|
||||
isOverlapTheNode = true;
|
||||
top -= pageYOffset;
|
||||
}
|
||||
|
||||
if (isOverlapTheNode) {
|
||||
left = Math.min(Math.max(leftBoundary, left - pageXOffset), rightBoundary);
|
||||
|
||||
position = "fixed";
|
||||
container.setAttribute("hide-arrow", "true");
|
||||
} else {
|
||||
position = "absolute";
|
||||
container.removeAttribute("hide-arrow");
|
||||
}
|
||||
|
||||
let style = "top:" + top + "px;left:" + left + "px;";
|
||||
container.setAttribute("style", style);
|
||||
// We need to scale the infobar Independently from the highlighter's container;
|
||||
// otherwise the `position: fixed` won't work, since "any value other than `none` for
|
||||
// the transform, results in the creation of both a stacking context and a containing
|
||||
// block. The object acts as a containing block for fixed positioned descendants."
|
||||
// (See https://www.w3.org/TR/css-transforms-1/#transform-rendering)
|
||||
container.setAttribute("style", `
|
||||
position:${position};
|
||||
transform-origin: 0 0;
|
||||
transform: scale(${1 / zoom}) translate(${left}px, ${top}px)`);
|
||||
|
||||
container.setAttribute("position", positionAttribute);
|
||||
}
|
||||
exports.moveInfobar = moveInfobar;
|
||||
|
@ -676,6 +676,26 @@ function getWindowDimensions(window) {
|
||||
}
|
||||
exports.getWindowDimensions = getWindowDimensions;
|
||||
|
||||
/**
|
||||
* Returns the viewport's dimensions for the `window` given.
|
||||
*
|
||||
* @return {Object} An object with `width` and `height` properties, representing the
|
||||
* number of pixels for the viewport's size.
|
||||
*/
|
||||
function getViewportDimensions(window) {
|
||||
let windowUtils = utilsFor(window);
|
||||
|
||||
let scrollbarHeight = {};
|
||||
let scrollbarWidth = {};
|
||||
windowUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
|
||||
|
||||
let width = window.innerWidth - scrollbarWidth.value;
|
||||
let height = window.innerHeight - scrollbarHeight.value;
|
||||
|
||||
return { width, height };
|
||||
}
|
||||
exports.getViewportDimensions = getViewportDimensions;
|
||||
|
||||
/**
|
||||
* Returns the max size allowed for a surface like textures or canvas.
|
||||
* If no `webgl` context is available, DEFAULT_MAX_SURFACE_SIZE is returned instead.
|
||||
|
Loading…
Reference in New Issue
Block a user