Skip to content
This repository was archived by the owner on Apr 11, 2023. It is now read-only.

iframe Late injection #1043

Merged
merged 6 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions e2e/lib/Page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export default class Page {
private constructor(private webdriver: WebDriver) {}

static async currentContext(webdriver: WebDriver): Promise<Page> {
await Page.waitForConsoleLoaded(webdriver);
await Page.waitForPageCompleted(webdriver);
return new Page(webdriver);
}

static async navigateTo(webdriver: WebDriver, url: string): Promise<Page> {
await webdriver.navigate().to(url);
await Page.waitForConsoleLoaded(webdriver);
await Page.waitForPageCompleted(webdriver);
return new Page(webdriver);
}

Expand All @@ -34,18 +34,19 @@ export default class Page {

async navigateTo(url: string): Promise<Page> {
await this.webdriver.navigate().to(url);
await Page.waitForConsoleLoaded(this.webdriver);
await Page.waitForPageCompleted(this.webdriver);

await new Promise((resolve) => setTimeout(resolve, 200));

return new Page(this.webdriver);
}

async showConsole(): Promise<Console> {
const iframe = this.webdriver.findElement(
By.css("#vimvixen-console-frame")
);

await this.sendKeys(":");
const iframe = this.webdriver.findElement(By.id("vimvixen-console-frame"));
await this.webdriver.wait(until.elementIsVisible(iframe));
await this.webdriver.switchTo().frame(0);

await this.webdriver.switchTo().frame(iframe);
await this.webdriver.wait(until.elementLocated(By.css("input")));
return new Console(this.webdriver);
}
Expand All @@ -54,9 +55,8 @@ export default class Page {
const iframe = this.webdriver.findElement(
By.css("#vimvixen-console-frame")
);

await this.webdriver.wait(until.elementIsVisible(iframe));
await this.webdriver.switchTo().frame(0);
await this.webdriver.switchTo().frame(iframe);
return new Console(this.webdriver);
}

Expand Down Expand Up @@ -113,14 +113,35 @@ export default class Page {
return hints;
}

private static async waitForConsoleLoaded(webdriver: WebDriver) {
private static async waitForPageCompleted(
webdriver: WebDriver
): Promise<void> {
this.waitForDocumentCompleted(webdriver);

const topFrame = await webdriver.executeScript(() => window.top === window);
if (!topFrame) {
return;
}
// style tag is injected at end of add-on loading
await webdriver.wait(until.elementLocated(By.tagName("style")));

const iframe = await webdriver.findElements(
By.id("vimvixen-console-frame")
);
if (iframe.length === 0) {
return;
}

await webdriver.switchTo().frame(iframe[0]);
await Page.waitForDocumentCompleted(webdriver);
await webdriver.switchTo().parentFrame();
}

private static async waitForDocumentCompleted(webdriver: WebDriver) {
await webdriver.wait(
until.elementLocated(By.css("iframe.vimvixen-console-frame"))
async () =>
(await webdriver.executeScript("return document.readyState")) ===
"complete"
);
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"all_frames": true,
"matches": [ "<all_urls>" ],
"js": [ "build/content.js" ],
"run_at": "document_end",
"run_at": "document_start",
"match_about_blank": true
}
],
Expand Down
2 changes: 1 addition & 1 deletion src/console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ReactDOM from "react-dom";

const store = createStore(reducers, applyMiddleware(promise));

window.addEventListener("load", () => {
window.addEventListener("DOMContentLoaded", () => {
const wrapper = document.getElementById("vimvixen-console");
ReactDOM.render(
<Provider store={store}>
Expand Down
8 changes: 4 additions & 4 deletions src/content/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ export default class Application {
private navigateController: NavigateController
) {}

run() {
this.routeCommonComponents();
init(): Promise<void> {
this.routeFocusEvents();
if (window.self === window.top) {
this.routeMasterComponents();
}
return this.routeCommonComponents();
}

private routeMasterComponents() {
Expand Down Expand Up @@ -76,7 +76,7 @@ export default class Application {
});
}

private routeCommonComponents() {
private routeCommonComponents(): Promise<void> {
this.messageListener.onWebMessage((msg: Message) => {
switch (msg.type) {
case messages.FOLLOW_REQUEST_COUNT_TARGETS:
Expand Down Expand Up @@ -117,7 +117,7 @@ export default class Application {
inputDriver.onKey((key) => this.markKeyController.press(key));
inputDriver.onKey((key) => this.keymapController.press(key));

this.settingController.initSettings();
return this.settingController.initSettings();
}

private routeFocusEvents() {
Expand Down
27 changes: 27 additions & 0 deletions src/content/Bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type Callback = () => void;

export default class Bootstrap {
constructor() {}

isReady(): boolean {
return document.body !== null;
}

waitForReady(callback: Callback): void {
const observer = new MutationObserver(() => {
if (document.body != null) {
observer.disconnect();
callback();
}
});

observer.observe(document, {
attributes: false,
attributeOldValue: false,
characterData: false,
characterDataOldValue: false,
childList: true,
subtree: true,
});
}
}
33 changes: 20 additions & 13 deletions src/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import "reflect-metadata";

import Application from "./Application";
import Bootstrap from "./Bootstrap";
import consoleFrameStyle from "./site-style";
import { ConsoleFramePresenterImpl } from "./presenters/ConsoleFramePresenter";
import { container } from "tsyringe";
import "./di";

if (window.self === window.top) {
new ConsoleFramePresenterImpl().initialize();
}
const initDom = () => {
(async () => {
try {
const app = container.resolve(Application);
await app.init();
} catch (e) {
console.error(e);
}
})();

try {
const app = container.resolve(Application);
app.run();
} catch (e) {
console.error(e);
}
const style = window.document.createElement("style");
style.textContent = consoleFrameStyle;
window.document.head.appendChild(style);
};

const style = window.document.createElement("style");
style.textContent = consoleFrameStyle;
window.document.head.appendChild(style);
const bootstrap = new Bootstrap();
if (bootstrap.isReady()) {
initDom();
} else {
bootstrap.waitForReady(() => initDom());
}
14 changes: 10 additions & 4 deletions src/content/operators/impls/AddonOperatorFactoryChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,38 @@ import Operator from "../Operator";
import EnableAddonOperator from "./EnableAddonOperator";
import DisableAddonOperator from "./DisableAddonOperator";
import ToggleAddonOperator from "./ToggleAddonOperator";
import ConsoleFramePresenter from "../../presenters/ConsoleFramePresenter";

@injectable()
export default class AddonOperatorFactoryChain implements OperatorFactoryChain {
constructor(
@inject("AddonIndicatorClient")
private readonly addonIndicatorClient: AddonIndicatorClient,
@inject("AddonEnabledRepository")
private readonly addonEnabledRepository: AddonEnabledRepository
private readonly addonEnabledRepository: AddonEnabledRepository,
@inject("ConsoleFramePresenter")
private readonly consoleFramePresenter: ConsoleFramePresenter
) {}

create(op: operations.Operation, _repeat: number): Operator | null {
switch (op.type) {
case operations.ADDON_ENABLE:
return new EnableAddonOperator(
this.addonIndicatorClient,
this.addonEnabledRepository
this.addonEnabledRepository,
this.consoleFramePresenter
);
case operations.ADDON_DISABLE:
return new DisableAddonOperator(
this.addonIndicatorClient,
this.addonEnabledRepository
this.addonEnabledRepository,
this.consoleFramePresenter
);
case operations.ADDON_TOGGLE_ENABLED:
return new ToggleAddonOperator(
this.addonIndicatorClient,
this.addonEnabledRepository
this.addonEnabledRepository,
this.consoleFramePresenter
);
}
return null;
Expand Down
5 changes: 4 additions & 1 deletion src/content/operators/impls/DisableAddonOperator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import Operator from "../Operator";
import AddonIndicatorClient from "../../client/AddonIndicatorClient";
import AddonEnabledRepository from "../../repositories/AddonEnabledRepository";
import ConsoleFramePresenter from "../../presenters/ConsoleFramePresenter";

export default class DisableAddonOperator implements Operator {
constructor(
private readonly indicator: AddonIndicatorClient,
private readonly repository: AddonEnabledRepository
private readonly repository: AddonEnabledRepository,
private readonly consoleFramePresenter: ConsoleFramePresenter
) {}

async run(): Promise<void> {
this.repository.set(false);
this.consoleFramePresenter.detach();
await this.indicator.setEnabled(false);
}
}
5 changes: 4 additions & 1 deletion src/content/operators/impls/EnableAddonOperator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import Operator from "../Operator";
import AddonIndicatorClient from "../../client/AddonIndicatorClient";
import AddonEnabledRepository from "../../repositories/AddonEnabledRepository";
import ConsoleFramePresenter from "../../presenters/ConsoleFramePresenter";

export default class EnableAddonOperator implements Operator {
constructor(
private readonly indicator: AddonIndicatorClient,
private readonly repository: AddonEnabledRepository
private readonly repository: AddonEnabledRepository,
private readonly consoleFramePresenter: ConsoleFramePresenter
) {}

async run(): Promise<void> {
this.repository.set(true);
this.consoleFramePresenter.attach();
await this.indicator.setEnabled(true);
}
}
15 changes: 11 additions & 4 deletions src/content/operators/impls/ToggleAddonOperator.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import Operator from "../Operator";
import AddonIndicatorClient from "../../client/AddonIndicatorClient";
import AddonEnabledRepository from "../../repositories/AddonEnabledRepository";
import ConsoleFramePresenter from "../../presenters/ConsoleFramePresenter";

export default class ToggleAddonOperator implements Operator {
constructor(
private readonly indicator: AddonIndicatorClient,
private readonly repository: AddonEnabledRepository
private readonly repository: AddonEnabledRepository,
private readonly consoleFramePresenter: ConsoleFramePresenter
) {}

async run(): Promise<void> {
const current = this.repository.get();
this.repository.set(!current);
await this.indicator.setEnabled(!current);
const enabled = !this.repository.get();
this.repository.set(enabled);
if (enabled) {
this.consoleFramePresenter.attach();
} else {
this.consoleFramePresenter.detach();
}
await this.indicator.setEnabled(enabled);
}
}
31 changes: 27 additions & 4 deletions src/content/presenters/ConsoleFramePresenter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
export default interface ConsoleFramePresenter {
initialize(): void;
attach(): void;

detach(): void;

blur(): void;

resize(width: number, height: number): void;

isTopWindow(): boolean;
}

export class ConsoleFramePresenterImpl implements ConsoleFramePresenter {
initialize(): void {
private static readonly IframeId = "vimvixen-console-frame" as const;

attach(): void {
const ele = document.getElementById("vimvixen-console-frame");
if (ele) {
return;
}

const iframe = document.createElement("iframe");
iframe.src = browser.runtime.getURL("build/console.html");
iframe.id = "vimvixen-console-frame";
iframe.id = ConsoleFramePresenterImpl.IframeId;
iframe.className = "vimvixen-console-frame";
document.body.append(iframe);
}

detach(): void {
const ele = document.getElementById(ConsoleFramePresenterImpl.IframeId);
if (!ele) {
return;
}
ele.remove();
}

blur(): void {
const ele = document.getElementById("vimvixen-console-frame");
if (!ele) {
throw new Error("console frame not created");
return;
}
ele.blur();
}
Expand All @@ -30,4 +49,8 @@ export class ConsoleFramePresenterImpl implements ConsoleFramePresenter {
}
ele.style.height = `${height}px`;
}

isTopWindow(): boolean {
return window.top === window;
}
}
Loading