This repository has been archived by the owner on Mar 19, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Prevent infinitely recursing when injecting into iframes (#66)
This patch prevents `axe-webdriverjs` from infinitely recursing when injecting `axe-core` into `<iframe>`s. I've re-written the code we use for injecting which will only recurse as deep as the number of nested `<iframe>`s on the page. In order to keep the code "easy to read/write", I've used `async/await` rather than chaining `Promise`s together. Because we do not have a clear picture of what Node.js versions we need to support here, I've added Babel in order to ensure `async/await` works in older Nodes. It is currently setup use support Node v4. Closes #63.
- Loading branch information
1 parent
1586dbd
commit 591a701
Showing
11 changed files
with
3,115 additions
and
901 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"presets": [ | ||
["env", { | ||
"targets": { | ||
"node": "4" | ||
} | ||
}] | ||
], | ||
"plugins": [ | ||
["transform-runtime", { | ||
"helpers": false, | ||
"polyfill": false, | ||
"regenerator": true, | ||
"moduleName": "babel-runtime" | ||
}] | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ output | |
npm-shrinkwrap.json | ||
.nyc_output/ | ||
coverage/ | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,4 @@ | ||
node_modules | ||
output | ||
test | ||
Gruntfile.js | ||
typings | ||
.editorconfig | ||
.travis.yml | ||
.gitignore | ||
CONTRIBUTING.md | ||
.nyc_output/ | ||
coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
class AxeInjector { | ||
constructor({ driver, config, axeSource = null }) { | ||
this.driver = driver; | ||
this.axeSource = axeSource || require('axe-core').source; | ||
this.config = config ? JSON.stringify(config) : ''; | ||
|
||
this.didLogError = false; | ||
this.errorHandler = this.errorHandler.bind(this); | ||
} | ||
|
||
// Single-shot error handler. Ensures we don't log more than once. | ||
errorHandler() { | ||
// We've already "warned" the user. No need to do it again (mostly for backwards compatiability) | ||
if (this.didLogError) { | ||
return; | ||
} | ||
|
||
this.didLogError = true; | ||
// eslint-disable-next-line no-console | ||
console.log('Failed to inject axe-core into one of the iframes!'); | ||
} | ||
|
||
// Get axe-core source (and configuration) | ||
get script() { | ||
return ` | ||
${this.axeSource} | ||
${this.config ? `axe.configure(${this.config})` : ''} | ||
axe.configure({ branding: { application: 'webdriverjs' } }) | ||
`; | ||
} | ||
|
||
// Inject into the provided `frame` and its child `frames` | ||
async handleFrame(frame) { | ||
// Switch context to the frame and inject our `script` into it | ||
await this.driver.switchTo().frame(frame); | ||
await this.driver.executeScript(this.script); | ||
|
||
// Get all of <iframe>s at this level | ||
const frames = await this.driver.findElements({ tagName: 'iframe' }); | ||
|
||
// Inject into each frame. Handling errors to ensure an issue on a single frame won't stop the rest of the injections. | ||
return Promise.all( | ||
frames.map(childFrame => | ||
this.handleFrame(childFrame).catch(this.errorHandler) | ||
) | ||
); | ||
} | ||
|
||
// Inject into all frames. | ||
async injectIntoAllFrames() { | ||
// Ensure we're "starting" our loop at the top-most frame | ||
await this.driver.switchTo().defaultContent(); | ||
|
||
// Inject the script into the top-level | ||
// XXX: if this `executeScript` fails, we *want* to error, as we cannot run axe-core. | ||
await this.driver.executeScript(this.script); | ||
|
||
// Get all of <iframe>s at this level | ||
const frames = await this.driver.findElements({ tagName: 'iframe' }); | ||
|
||
// Inject the script into all child frames. Handle errors to ensure we don't stop execution if we fail to inject. | ||
await Promise.all( | ||
frames.map(childFrame => | ||
this.handleFrame(childFrame).catch(this.errorHandler) | ||
) | ||
); | ||
|
||
// Move back to the top-most frame | ||
return this.driver.switchTo().defaultContent(); | ||
} | ||
|
||
// Inject axe, invoking the provided callback when done | ||
inject(callback) { | ||
this.injectIntoAllFrames() | ||
.then(() => callback()) | ||
// For now, we intentionally ignore errors here, as | ||
// allowing them to bubble up would be a breaking change. | ||
.catch(() => callback()); | ||
} | ||
} | ||
|
||
module.exports = AxeInjector; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.