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: add webFrame.securityOrigin #35163

Closed
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
9 changes: 9 additions & 0 deletions docs/api/web-frame.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,12 @@ current renderer process.
An `Integer` representing the unique frame id in the current renderer process.
Distinct WebFrame instances that refer to the same underlying frame will have
the same `routingId`.

### `webFrame.securityOrigin` _Readonly_

A `string` representing the security origin assigned to this frame. A security
origin can be defined as a URL which is shared across one or more frames
with same-site synchronous script access. An `<iframe>` or window opened with
`open()` may share the same security origin if permitted by the browser's
same-site rules. Isolated resources without any parent frame, such as Data URIs
or `about:blank`, may return an empty string.
20 changes: 19 additions & 1 deletion shell/renderer/api/electron_api_web_frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/function_template_extensions.h"
Expand All @@ -37,6 +38,7 @@
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/common/web_cache/web_cache_resource_type_stats.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/web_cache.h"
#include "third_party/blink/public/platform/web_isolated_world_info.h"
#include "third_party/blink/public/web/web_custom_element.h"
Expand Down Expand Up @@ -395,7 +397,8 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
.SetProperty("top", &WebFrameRenderer::GetTop)
.SetProperty("firstChild", &WebFrameRenderer::GetFirstChild)
.SetProperty("nextSibling", &WebFrameRenderer::GetNextSibling)
.SetProperty("routingId", &WebFrameRenderer::GetRoutingId);
.SetProperty("routingId", &WebFrameRenderer::GetRoutingId)
.SetProperty("securityOrigin", &WebFrameRenderer::GetSecurityOrigin);
}

const char* GetTypeName() override { return "WebFrameRenderer"; }
Expand Down Expand Up @@ -885,6 +888,21 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,

return render_frame->GetRoutingID();
}

GURL GetSecurityOrigin(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "routingId", &render_frame))
return GURL::EmptyGURL();

blink::WebSecurityOrigin security_origin =
render_frame->GetWebFrame()->GetSecurityOrigin();

// Opaque origins are unique origins for isolated pages like data: and
// about:blank which aren't serializable.
return security_origin.IsOpaque()
? GURL::EmptyGURL()
: blink::WebStringToGURL(security_origin.ToString());
}
};

gin::WrapperInfo WebFrameRenderer::kWrapperInfo = {gin::kEmbedderNativeGin};
Expand Down
72 changes: 71 additions & 1 deletion spec-main/api-web-frame-spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'chai';
import * as http from 'http';
import * as path from 'path';
import { BrowserWindow, ipcMain, WebContents } from 'electron/main';
import { emittedOnce } from './events-helpers';
Expand Down Expand Up @@ -75,9 +76,14 @@ describe('webFrame module', () => {
describe('api', () => {
let w: WebContents;
before(async () => {
const win = new BrowserWindow({ show: false, webPreferences: { contextIsolation: false, nodeIntegration: true } });
const winOpts = { show: false, webPreferences: { contextIsolation: false, nodeIntegration: true } };
const win = new BrowserWindow(winOpts);
await win.loadURL('about:blank');
w = win.webContents;
w.setWindowOpenHandler(() => ({
action: 'allow',
overrideBrowserWindowOptions: winOpts
}));
await w.executeJavaScript('webFrame = require(\'electron\').webFrame; null');
});
it('top is self for top frame', async () => {
Expand Down Expand Up @@ -192,5 +198,69 @@ describe('webFrame module', () => {
expect(await w.executeJavaScript('webFrame.executeJavaScriptInIsolatedWorld(999, [{code: \'1 + 1\'}])')).to.equal(2);
});
});

describe('securityOrigin', () => {
let server: http.Server;
const getServerUrl = () => `http://127.0.0.1:${(server.address() as any).port}/`;
const getSecurityOrigin = (webContents: Electron.WebContents = w) => webContents.executeJavaScript('require("electron").webFrame.securityOrigin');

before(async () => {
const createServer = () => new Promise<http.Server>(resolve => {
server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<!doctype html>');
});
server.listen(0, '127.0.0.1', () => {
resolve(server);
});
});
await createServer();
});

after(() => {
server.close();
});

afterEach(async () => {
// Force new renderer process to clear out sticky security origin from
// previous test.
w.forcefullyCrashRenderer();
const p = emittedOnce(w, 'dom-ready');
w.reload();
await p;
await w.loadURL('about:blank');
});

it('results in origin for http', async () => {
await w.loadURL(getServerUrl());
expect(await getSecurityOrigin()).to.equal(getServerUrl());
});
it('results in same origin for child about:blank', async () => {
await w.loadURL(getServerUrl());
const p = emittedOnce(w, 'did-create-window');
w.executeJavaScript('window.open("about:blank"); void 0;');
const [childWindow] = await p;
expect(await getSecurityOrigin(childWindow.webContents)).to.equal(getServerUrl());
childWindow.close();
});
it('results in same origin for child iframe', async () => {
w.loadURL(getServerUrl());
const childFramePromise = emittedOnce(w, 'frame-created');
w.executeJavaScript(`(() => {
window.addEventListener('DOMContentLoaded', () => {
const iframe = document.createElement('iframe');
iframe.src = 'about:blank';
document.documentElement.appendChild(iframe);
});
})()`);
await childFramePromise;
expect(await w.executeJavaScript('require("electron").webFrame.firstChild.securityOrigin')).to.equal(getServerUrl());
});
it('results in an empty string for isolated pages', async () => {
expect(await getSecurityOrigin()).to.equal(''); // about:blank
await w.loadURL('data:text/html,howdy');
expect(await getSecurityOrigin()).to.equal('');
});
});
});
});