-
Notifications
You must be signed in to change notification settings - Fork 9.6k
core(iframe-elements): Include new IFrameElements gatherer #8979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 19 commits
89d81ef
499c599
4abd4dc
3ee62ee
ce2e280
2642fa9
76e3e1d
e07610e
aa8e553
861e3ed
0c8ba7f
c9887c6
0ca3b25
d2223fd
371bcc9
a1a6f21
a4bacac
54f9793
13e6f66
e674bc9
4121d17
8d9354e
82ddb3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,8 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Where is my iframe?</title> | ||
| </head> | ||
| <body> | ||
| <h1>Hello frames</h1> | ||
| <iframe name="oopif" src="https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/" style="width: 100vw; height: 100vh;"></iframe> | ||
| <iframe id="oopif" name="oopif" src="https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/" style="width: 100vw; height: 110vh;"></iframe> | ||
| <iframe id="outer-iframe" src="http://localhost:10200/online-only.html" style="position: fixed"></iframe> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,9 +10,4 @@ | |
| */ | ||
| module.exports = { | ||
| extends: 'lighthouse:default', | ||
| settings: { | ||
| onlyAudits: [ | ||
| 'network-requests', | ||
| ], | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| /** | ||
| * @license Copyright 2019 Google Inc. All Rights Reserved. | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. | ||
| */ | ||
| 'use strict'; | ||
|
|
||
| const Gatherer = require('./gatherer.js'); | ||
| const pageFunctions = require('../../lib/page-functions.js'); | ||
|
|
||
| /* eslint-env browser, node */ | ||
|
|
||
| /** | ||
| * @return {LH.Artifacts['IFrameElements']} | ||
| */ | ||
jburger424 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /* istanbul ignore next */ | ||
| function collectIFrameElements() { | ||
| // @ts-ignore - put into scope via stringification | ||
| const iFrameElements = getElementsInDocument('iframe'); // eslint-disable-line no-undef | ||
| return iFrameElements.map(/** @param {HTMLIFrameElement} node */ (node) => { | ||
| const clientRect = node.getBoundingClientRect(); | ||
| const {top, bottom, left, right, width, height} = clientRect; | ||
| return { | ||
| id: node.id, | ||
| src: node.src, | ||
| clientRect: {top, bottom, left, right, width, height}, | ||
jburger424 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // @ts-ignore - put into scope via stringification | ||
| isPositionFixed: isPositionFixed(node), // eslint-disable-line no-undef | ||
| }; | ||
| }); | ||
| } | ||
|
|
||
| class IFrameElements extends Gatherer { | ||
| /** | ||
| * @param {LH.Gatherer.PassContext} passContext | ||
| * @return {Promise<LH.Artifacts['IFrameElements']>} | ||
| * @override | ||
| */ | ||
| async afterPass(passContext) { | ||
| const driver = passContext.driver; | ||
|
|
||
| const {frameTree} = await driver.sendCommand('Page.getFrameTree'); | ||
| let toVisit = [frameTree]; | ||
| /** @type {Map<string | undefined, LH.Crdp.Page.Frame | undefined>} */ | ||
| const framesByDomId = new Map(); | ||
jburger424 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| while (toVisit.length) { | ||
| const frameTree = toVisit.shift(); | ||
| // Should never be undefined, but needed for tsc. | ||
| if (!frameTree) continue; | ||
| if (framesByDomId.has(frameTree.frame.name)) { | ||
| // DOM ID collision, mark as undefined. | ||
| framesByDomId.set(frameTree.frame.name, undefined); | ||
| } else { | ||
| framesByDomId.set(frameTree.frame.name, frameTree.frame); | ||
jburger424 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // Add children to queue. | ||
| if (frameTree.childFrames) { | ||
| toVisit = toVisit.concat(frameTree.childFrames); | ||
| } | ||
| } | ||
|
|
||
| const expression = `(() => { | ||
| ${pageFunctions.getOuterHTMLSnippetString}; | ||
| ${pageFunctions.getElementsInDocumentString}; | ||
| ${pageFunctions.isPositionFixedString}; | ||
| return (${collectIFrameElements})(); | ||
| })()`; | ||
|
|
||
| /** @type {LH.Artifacts['IFrameElements']} */ | ||
| const iframeElements = await driver.evaluateAsync(expression, {useIsolation: true}); | ||
| for (const el of iframeElements) { | ||
| el.frame = framesByDomId.get(el.id); | ||
jburger424 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| return iframeElements; | ||
| } | ||
| } | ||
|
|
||
| module.exports = IFrameElements; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,6 +98,10 @@ function getElementsInDocument(selector) { | |
| if (el.shadowRoot) { | ||
| _findAllElements(el.shadowRoot.querySelectorAll('*')); | ||
| } | ||
| // If the element has a contentDocument (IFrame), dig deeper. | ||
|
||
| if (el.contentDocument) { | ||
| _findAllElements(el.contentDocument.querySelectorAll('*')); | ||
| } | ||
| } | ||
| }; | ||
| _findAllElements(document.querySelectorAll('*')); | ||
|
|
@@ -225,6 +229,43 @@ function getNodeSelector(node) { | |
| return parts.join(' > '); | ||
| } | ||
|
|
||
| /** | ||
jburger424 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * This function checks if an element or an ancestor of an element is `position:fixed`. | ||
| * In addition we ensure that the element is capable of behaving as a `position:fixed` | ||
| * element, checking that it lives within a scrollable ancestor. | ||
| * @param {HTMLElement} element | ||
| * @return {boolean} | ||
| */ | ||
| /* istanbul ignore next */ | ||
| function isPositionFixed(element) { | ||
jburger424 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * @param {HTMLElement} element | ||
| * @param {string} attr | ||
| * @return {string} | ||
| */ | ||
| function getStyleAttrValue(element, attr) { | ||
| // Check style before computedStyle as computedStyle is expensive. | ||
| return element.style[attr] || window.getComputedStyle(element)[attr]; | ||
| } | ||
|
|
||
| // Position fixed/sticky has no effect in case when document does not scroll. | ||
jburger424 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const htmlEl = document.querySelector('html'); | ||
| if (htmlEl.scrollHeight <= htmlEl.clientHeight || | ||
| !['scroll', 'auto', 'visible'].includes(getStyleAttrValue(htmlEl, 'overflowY'))) { | ||
| return false; | ||
| } | ||
|
|
||
| let currentEl = element; | ||
| while (currentEl) { | ||
| const position = getStyleAttrValue(currentEl, 'position'); | ||
| if ((position === 'fixed' || position === 'sticky')) { | ||
| return true; | ||
| } | ||
| currentEl = currentEl.parentElement; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Generate a human-readable label for the given element, based on end-user facing | ||
| * strings like the innerText or alt attribute. | ||
|
|
@@ -280,4 +321,5 @@ module.exports = { | |
| getNodeSelector: getNodeSelector, | ||
| getNodeLabel: getNodeLabel, | ||
| getNodeLabelString: getNodeLabel.toString(), | ||
| isPositionFixedString: isPositionFixed.toString(), | ||
| }; | ||

Uh oh!
There was an error while loading. Please reload this page.