Skip to content
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

feat: webFrameMain.origin #35535

Merged
merged 5 commits into from Sep 15, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/api/web-frame-main.md
Expand Up @@ -144,6 +144,16 @@ ipcRenderer.on('port', (e, msg) => {

A `string` representing the current URL of the frame.

#### `frame.origin` _Readonly_

A `string` representing the current origin of the frame, serialized according
to [RFC 6454](https://www.rfc-editor.org/rfc/rfc6454). This may be different
from the URL. For instance, if the frame is a child window opened to
`about:blank`, then `frame.origin` will return the parent frame's origin, while
`frame.url` will return the empty string. Pages without a scheme/host/port
triple origin will have the serialized origin of `"null"` (that is, the string
containing the letters n, u, l, l).

#### `frame.top` _Readonly_

A `WebFrameMain | null` representing top frame in the frame hierarchy to which `frame`
Expand Down
7 changes: 7 additions & 0 deletions shell/browser/api/electron_api_web_frame_main.cc
Expand Up @@ -298,6 +298,12 @@ GURL WebFrameMain::URL() const {
return render_frame_->GetLastCommittedURL();
}

std::string WebFrameMain::Origin() const {
if (!CheckRenderFrame())
return std::string();
return render_frame_->GetLastCommittedOrigin().Serialize();
}

blink::mojom::PageVisibilityState WebFrameMain::VisibilityState() const {
if (!CheckRenderFrame())
return blink::mojom::PageVisibilityState::kHidden;
Expand Down Expand Up @@ -387,6 +393,7 @@ v8::Local<v8::ObjectTemplate> WebFrameMain::FillObjectTemplate(
.SetProperty("processId", &WebFrameMain::ProcessID)
.SetProperty("routingId", &WebFrameMain::RoutingID)
.SetProperty("url", &WebFrameMain::URL)
.SetProperty("origin", &WebFrameMain::Origin)
.SetProperty("visibilityState", &WebFrameMain::VisibilityState)
.SetProperty("top", &WebFrameMain::Top)
.SetProperty("parent", &WebFrameMain::Parent)
Expand Down
1 change: 1 addition & 0 deletions shell/browser/api/electron_api_web_frame_main.h
Expand Up @@ -108,6 +108,7 @@ class WebFrameMain : public gin::Wrappable<WebFrameMain>,
int ProcessID() const;
int RoutingID() const;
GURL URL() const;
std::string Origin() const;
blink::mojom::PageVisibilityState VisibilityState() const;

content::RenderFrameHost* Top() const;
Expand Down
78 changes: 64 additions & 14 deletions spec-main/api-web-frame-main-spec.ts
Expand Up @@ -2,11 +2,11 @@ import { expect } from 'chai';
import * as http from 'http';
import * as path from 'path';
import * as url from 'url';
import { BrowserWindow, WebFrameMain, webFrameMain, ipcMain } from 'electron/main';
import { BrowserWindow, WebFrameMain, webFrameMain, ipcMain, app, WebContents } from 'electron/main';
import { closeAllWindows } from './window-helpers';
import { emittedOnce, emittedNTimes } from './events-helpers';
import { AddressInfo } from 'net';
import { ifit, waitUntil } from './spec-helpers';
import { defer, ifit, waitUntil } from './spec-helpers';

describe('webFrameMain module', () => {
const fixtures = path.resolve(__dirname, '..', 'spec-main', 'fixtures');
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('webFrameMain module', () => {
let webFrame: WebFrameMain;

beforeEach(async () => {
w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame-with-frame-container.html'));
webFrame = w.webContents.mainFrame;
});
Expand Down Expand Up @@ -88,8 +88,8 @@ describe('webFrameMain module', () => {
});

describe('cross-origin', () => {
let serverA = null as unknown as Server;
let serverB = null as unknown as Server;
let serverA: Server;
let serverB: Server;

before(async () => {
serverA = await createServer();
Expand All @@ -112,7 +112,7 @@ describe('webFrameMain module', () => {

describe('WebFrame.url', () => {
it('should report correct address for each subframe', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame-with-frame-container.html'));
const webFrame = w.webContents.mainFrame;

Expand All @@ -122,9 +122,59 @@ describe('webFrameMain module', () => {
});
});

describe('WebFrame.origin', () => {
it('should be null for a fresh WebContents', () => {
const w = new BrowserWindow({ show: false });
expect(w.webContents.mainFrame.origin).to.equal('null');
});

it('should be file:// for file frames', async () => {
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(fixtures, 'blank.html'));
expect(w.webContents.mainFrame.origin).to.equal('file://');
});

it('should be http:// for an http frame', async () => {
const w = new BrowserWindow({ show: false });
const s = await createServer();
defer(() => s.server.close());
await w.loadURL(s.url);
expect(w.webContents.mainFrame.origin).to.equal(s.url.replace(/\/$/, ''));
});

it('should show parent origin when child page is about:blank', async () => {
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(fixtures, 'blank.html'));
const webContentsCreated: Promise<[unknown, WebContents]> = emittedOnce(app, 'web-contents-created') as any;
expect(w.webContents.mainFrame.origin).to.equal('file://');
await w.webContents.executeJavaScript('window.open("", null, "show=false"), null');
const [, childWebContents] = await webContentsCreated;
expect(childWebContents.mainFrame.origin).to.equal('file://');
});

it('should show parent frame\'s origin when about:blank child window opened through cross-origin subframe', async () => {
const w = new BrowserWindow({ show: false });
const serverA = await createServer();
const serverB = await createServer();
defer(() => {
serverA.server.close();
serverB.server.close();
});
await w.loadURL(serverA.url + '?frameSrc=' + encodeURIComponent(serverB.url));
const { mainFrame } = w.webContents;
expect(mainFrame.origin).to.equal(serverA.url.replace(/\/$/, ''));
const [childFrame] = mainFrame.frames;
expect(childFrame.origin).to.equal(serverB.url.replace(/\/$/, ''));
const webContentsCreated: Promise<[unknown, WebContents]> = emittedOnce(app, 'web-contents-created') as any;
await childFrame.executeJavaScript('window.open("", null, "show=false"), null');
const [, childWebContents] = await webContentsCreated;
expect(childWebContents.mainFrame.origin).to.equal(childFrame.origin);
});
});

describe('WebFrame IDs', () => {
it('has properties for various identifiers', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame.html'));
const webFrame = w.webContents.mainFrame;
expect(webFrame).to.have.ownProperty('url').that.is.a('string');
Expand Down Expand Up @@ -154,7 +204,7 @@ describe('webFrameMain module', () => {

describe('WebFrame.executeJavaScript', () => {
it('can inject code into any subframe', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame-with-frame-container.html'));
const webFrame = w.webContents.mainFrame;

Expand All @@ -165,7 +215,7 @@ describe('webFrameMain module', () => {
});

it('can resolve promise', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame.html'));
const webFrame = w.webContents.mainFrame;
const p = () => webFrame.executeJavaScript('new Promise(resolve => setTimeout(resolve(42), 2000));');
Expand All @@ -174,7 +224,7 @@ describe('webFrameMain module', () => {
});

it('can reject with error', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame.html'));
const webFrame = w.webContents.mainFrame;
const p = () => webFrame.executeJavaScript('new Promise((r,e) => setTimeout(e("error!"), 500));');
Expand All @@ -195,7 +245,7 @@ describe('webFrameMain module', () => {
});

it('can reject when script execution fails', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame.html'));
const webFrame = w.webContents.mainFrame;
const p = () => webFrame.executeJavaScript('console.log(test)');
Expand All @@ -205,7 +255,7 @@ describe('webFrameMain module', () => {

describe('WebFrame.reload', () => {
it('reloads a frame', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });
await w.loadFile(path.join(subframesPath, 'frame.html'));
const webFrame = w.webContents.mainFrame;

Expand Down Expand Up @@ -238,7 +288,7 @@ describe('webFrameMain module', () => {
let w: BrowserWindow;

beforeEach(async () => {
w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
w = new BrowserWindow({ show: false });
});

// TODO(jkleinsc) fix this flaky test on linux
Expand Down Expand Up @@ -301,7 +351,7 @@ describe('webFrameMain module', () => {
});

it('can find each frame from navigation events', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
const w = new BrowserWindow({ show: false });

// frame-with-frame-container.html, frame-with-frame.html, frame.html
const didFrameFinishLoad = emittedNTimes(w.webContents, 'did-frame-finish-load', 3);
Expand Down