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 support for DOM elements going over the context bridge #26776

Merged
merged 2 commits into from Dec 10, 2020
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
1 change: 1 addition & 0 deletions docs/api/context-bridge.md
Expand Up @@ -106,6 +106,7 @@ has been included below for completeness:
| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are the return value or exact parameter. Promises nested in arrays or objects will be dropped. |
| `Function` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending classes or constructors will not work. |
| [Cloneable Types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | Simple | ✅ | ✅ | See the linked document on cloneable types |
| `Element` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending custom elements will not work. |
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |

If the type you care about is not in the above table, it is probably not supported.
9 changes: 9 additions & 0 deletions shell/renderer/api/electron_api_context_bridge.cc
Expand Up @@ -22,6 +22,7 @@
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"
#include "shell/common/world_ids.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame.h"

namespace electron {
Expand Down Expand Up @@ -319,6 +320,14 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
return v8::MaybeLocal<v8::Value>(cloned_arr);
}

// Custom logic to "clone" Element references
blink::WebElement elem = blink::WebElement::FromV8Value(value);
if (!elem.IsNull()) {
v8::Context::Scope destination_context_scope(destination_context);
return v8::MaybeLocal<v8::Value>(elem.ToV8Value(
destination_context->Global(), destination_context->GetIsolate()));
}

// Proxy all objects
if (IsPlainObject(value)) {
auto object_value = v8::Local<v8::Object>::Cast(value);
Expand Down
35 changes: 32 additions & 3 deletions spec-main/api-context-bridge-spec.ts
Expand Up @@ -357,7 +357,7 @@ describe('contextBridge', () => {
expect(result).to.deep.equal([true, true]);
});

it('it should handle recursive objects', async () => {
it('should handle recursive objects', async () => {
await makeBindingWindow(() => {
const o: any = { value: 135 };
o.o = o;
Expand All @@ -371,6 +371,33 @@ describe('contextBridge', () => {
expect(result).to.deep.equal([135, 135, 135]);
});

it('should handle DOM elements', async () => {
await makeBindingWindow(() => {
contextBridge.exposeInMainWorld('example', {
getElem: () => document.body
});
});
const result = await callWithBindings((root: any) => {
return [root.example.getElem().tagName, root.example.getElem().constructor.name, typeof root.example.getElem().querySelector];
});
expect(result).to.deep.equal(['BODY', 'HTMLBodyElement', 'function']);
});

it('should handle DOM elements going backwards over the bridge', async () => {
await makeBindingWindow(() => {
contextBridge.exposeInMainWorld('example', {
getElemInfo: (fn: Function) => {
const elem = fn();
return [elem.tagName, elem.constructor.name, typeof elem.querySelector];
}
});
});
const result = await callWithBindings((root: any) => {
return root.example.getElemInfo(() => document.body);
});
expect(result).to.deep.equal(['BODY', 'HTMLBodyElement', 'function']);
});

// Can only run tests which use the GCRunner in non-sandboxed environments
if (!useSandbox) {
it('should release the global hold on methods sent across contexts', async () => {
Expand Down Expand Up @@ -559,7 +586,8 @@ describe('contextBridge', () => {
receiveArguments: (fn: any) => fn({ key: 'value' }),
symbolKeyed: {
[Symbol('foo')]: 123
}
},
getBody: () => document.body
});
});
const result = await callWithBindings(async (root: any) => {
Expand Down Expand Up @@ -631,7 +659,8 @@ describe('contextBridge', () => {
[(await example.object.getPromise()).arr[3], Array],
[(await example.object.getPromise()).arr[3][0], String],
[arg, Object],
[arg.key, String]
[arg.key, String],
[example.getBody(), HTMLBodyElement]
];
return {
protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype)
Expand Down