fix double-freeing remote references
After the page does navigations, garbage collection can still happen in the old context. This commit changes to store references to remote objects by _pages_, instead of by _WebContents_.
This commit is contained in:
parent
9cbbb2a6c4
commit
4cdb1b8fc3
9 changed files with 139 additions and 113 deletions
|
@ -32,7 +32,7 @@ namespace std {
|
||||||
template <typename Type1, typename Type2>
|
template <typename Type1, typename Type2>
|
||||||
struct hash<std::pair<Type1, Type2>> {
|
struct hash<std::pair<Type1, Type2>> {
|
||||||
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
||||||
return base::HashInts<Type1, Type2>(value.first, value.second);
|
return base::HashInts(base::Hash(value.first), value.second);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,8 +137,9 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo);
|
dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo);
|
||||||
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
||||||
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
||||||
dict.SetMethod("createDoubleIDWeakMap",
|
dict.SetMethod(
|
||||||
&atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
|
"createDoubleIDWeakMap",
|
||||||
|
&atom::api::KeyWeakMap<std::pair<std::string, int32_t>>::Create);
|
||||||
dict.SetMethod("requestGarbageCollectionForTesting",
|
dict.SetMethod("requestGarbageCollectionForTesting",
|
||||||
&RequestGarbageCollectionForTesting);
|
&RequestGarbageCollectionForTesting);
|
||||||
dict.SetMethod("isSameOrigin", &IsSameOrigin);
|
dict.SetMethod("isSameOrigin", &IsSameOrigin);
|
||||||
|
|
|
@ -15,17 +15,20 @@ namespace atom {
|
||||||
// static
|
// static
|
||||||
void RemoteCallbackFreer::BindTo(v8::Isolate* isolate,
|
void RemoteCallbackFreer::BindTo(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id,
|
int object_id,
|
||||||
content::WebContents* web_contents) {
|
content::WebContents* web_contents) {
|
||||||
new RemoteCallbackFreer(isolate, target, object_id, web_contents);
|
new RemoteCallbackFreer(isolate, target, context_id, object_id, web_contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
|
RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id,
|
int object_id,
|
||||||
content::WebContents* web_contents)
|
content::WebContents* web_contents)
|
||||||
: ObjectLifeMonitor(isolate, target),
|
: ObjectLifeMonitor(isolate, target),
|
||||||
content::WebContentsObserver(web_contents),
|
content::WebContentsObserver(web_contents),
|
||||||
|
context_id_(context_id),
|
||||||
object_id_(object_id) {}
|
object_id_(object_id) {}
|
||||||
|
|
||||||
RemoteCallbackFreer::~RemoteCallbackFreer() {}
|
RemoteCallbackFreer::~RemoteCallbackFreer() {}
|
||||||
|
@ -34,6 +37,7 @@ void RemoteCallbackFreer::RunDestructor() {
|
||||||
base::string16 channel =
|
base::string16 channel =
|
||||||
base::ASCIIToUTF16("ELECTRON_RENDERER_RELEASE_CALLBACK");
|
base::ASCIIToUTF16("ELECTRON_RENDERER_RELEASE_CALLBACK");
|
||||||
base::ListValue args;
|
base::ListValue args;
|
||||||
|
args.AppendString(context_id_);
|
||||||
args.AppendInteger(object_id_);
|
args.AppendInteger(object_id_);
|
||||||
auto* frame_host = web_contents()->GetMainFrame();
|
auto* frame_host = web_contents()->GetMainFrame();
|
||||||
if (frame_host) {
|
if (frame_host) {
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||||
#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "atom/common/api/object_life_monitor.h"
|
#include "atom/common/api/object_life_monitor.h"
|
||||||
#include "content/public/browser/web_contents_observer.h"
|
#include "content/public/browser/web_contents_observer.h"
|
||||||
|
|
||||||
|
@ -14,12 +17,14 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||||
public:
|
public:
|
||||||
static void BindTo(v8::Isolate* isolate,
|
static void BindTo(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id,
|
int object_id,
|
||||||
content::WebContents* web_conents);
|
content::WebContents* web_conents);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RemoteCallbackFreer(v8::Isolate* isolate,
|
RemoteCallbackFreer(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id,
|
int object_id,
|
||||||
content::WebContents* web_conents);
|
content::WebContents* web_conents);
|
||||||
~RemoteCallbackFreer() override;
|
~RemoteCallbackFreer() override;
|
||||||
|
@ -30,6 +35,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||||
void RenderViewDeleted(content::RenderViewHost*) override;
|
void RenderViewDeleted(content::RenderViewHost*) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string context_id_;
|
||||||
int object_id_;
|
int object_id_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer);
|
DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer);
|
||||||
|
|
|
@ -29,14 +29,17 @@ content::RenderFrame* GetCurrentRenderFrame() {
|
||||||
// static
|
// static
|
||||||
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
|
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id) {
|
int object_id) {
|
||||||
new RemoteObjectFreer(isolate, target, object_id);
|
new RemoteObjectFreer(isolate, target, context_id, object_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteObjectFreer::RemoteObjectFreer(v8::Isolate* isolate,
|
RemoteObjectFreer::RemoteObjectFreer(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id)
|
int object_id)
|
||||||
: ObjectLifeMonitor(isolate, target),
|
: ObjectLifeMonitor(isolate, target),
|
||||||
|
context_id_(context_id),
|
||||||
object_id_(object_id),
|
object_id_(object_id),
|
||||||
routing_id_(MSG_ROUTING_NONE) {
|
routing_id_(MSG_ROUTING_NONE) {
|
||||||
content::RenderFrame* render_frame = GetCurrentRenderFrame();
|
content::RenderFrame* render_frame = GetCurrentRenderFrame();
|
||||||
|
@ -56,6 +59,7 @@ void RemoteObjectFreer::RunDestructor() {
|
||||||
base::string16 channel = base::ASCIIToUTF16("ipc-message");
|
base::string16 channel = base::ASCIIToUTF16("ipc-message");
|
||||||
base::ListValue args;
|
base::ListValue args;
|
||||||
args.AppendString("ELECTRON_BROWSER_DEREFERENCE");
|
args.AppendString("ELECTRON_BROWSER_DEREFERENCE");
|
||||||
|
args.AppendString(context_id_);
|
||||||
args.AppendInteger(object_id_);
|
args.AppendInteger(object_id_);
|
||||||
render_frame->Send(new AtomFrameHostMsg_Message(render_frame->GetRoutingID(),
|
render_frame->Send(new AtomFrameHostMsg_Message(render_frame->GetRoutingID(),
|
||||||
channel, args));
|
channel, args));
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||||
#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "atom/common/api/object_life_monitor.h"
|
#include "atom/common/api/object_life_monitor.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
@ -13,17 +15,20 @@ class RemoteObjectFreer : public ObjectLifeMonitor {
|
||||||
public:
|
public:
|
||||||
static void BindTo(v8::Isolate* isolate,
|
static void BindTo(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id);
|
int object_id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RemoteObjectFreer(v8::Isolate* isolate,
|
RemoteObjectFreer(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target,
|
v8::Local<v8::Object> target,
|
||||||
|
const std::string& context_id,
|
||||||
int object_id);
|
int object_id);
|
||||||
~RemoteObjectFreer() override;
|
~RemoteObjectFreer() override;
|
||||||
|
|
||||||
void RunDestructor() override;
|
void RunDestructor() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string context_id_;
|
||||||
int object_id_;
|
int object_id_;
|
||||||
int routing_id_;
|
int routing_id_;
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,15 @@ class ObjectsRegistry {
|
||||||
|
|
||||||
// Register a new object and return its assigned ID. If the object is already
|
// Register a new object and return its assigned ID. If the object is already
|
||||||
// registered then the already assigned ID would be returned.
|
// registered then the already assigned ID would be returned.
|
||||||
add (webContents, obj) {
|
add (webContents, contextId, obj) {
|
||||||
// Get or assign an ID to the object.
|
// Get or assign an ID to the object.
|
||||||
const id = this.saveToStorage(obj)
|
const id = this.saveToStorage(obj)
|
||||||
|
|
||||||
// Add object to the set of referenced objects.
|
// Add object to the set of referenced objects.
|
||||||
const webContentsId = webContents.getId()
|
let owner = this.owners[contextId]
|
||||||
let owner = this.owners[webContentsId]
|
|
||||||
if (!owner) {
|
if (!owner) {
|
||||||
owner = this.owners[webContentsId] = new Set()
|
owner = this.owners[contextId] = new Set()
|
||||||
this.registerDeleteListener(webContents, webContentsId)
|
this.registerDeleteListener(webContents, contextId)
|
||||||
}
|
}
|
||||||
if (!owner.has(id)) {
|
if (!owner.has(id)) {
|
||||||
owner.add(id)
|
owner.add(id)
|
||||||
|
@ -43,25 +42,26 @@ class ObjectsRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dereference an object according to its ID.
|
// Dereference an object according to its ID.
|
||||||
remove (webContentsId, id) {
|
// Note that an object may be double-freed (cleared when page is reloaded, and
|
||||||
|
// then garbage collected in old page).
|
||||||
|
remove (contextId, id) {
|
||||||
|
let owner = this.owners[contextId]
|
||||||
|
if (owner) {
|
||||||
|
// Remove the reference in owner.
|
||||||
|
owner.delete(id)
|
||||||
// Dereference from the storage.
|
// Dereference from the storage.
|
||||||
this.dereference(id)
|
this.dereference(id)
|
||||||
|
|
||||||
// Also remove the reference in owner.
|
|
||||||
let owner = this.owners[webContentsId]
|
|
||||||
if (owner) {
|
|
||||||
owner.delete(id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear all references to objects refrenced by the WebContents.
|
// Clear all references to objects refrenced by the WebContents.
|
||||||
clear (webContentsId) {
|
clear (contextId) {
|
||||||
let owner = this.owners[webContentsId]
|
let owner = this.owners[contextId]
|
||||||
if (!owner) return
|
if (!owner) return
|
||||||
|
|
||||||
for (let id of owner) this.dereference(id)
|
for (let id of owner) this.dereference(id)
|
||||||
|
|
||||||
delete this.owners[webContentsId]
|
delete this.owners[contextId]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private: Saves the object into storage and assigns an ID for it.
|
// Private: Saves the object into storage and assigns an ID for it.
|
||||||
|
@ -92,12 +92,12 @@ class ObjectsRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private: Clear the storage when webContents is reloaded/navigated.
|
// Private: Clear the storage when webContents is reloaded/navigated.
|
||||||
registerDeleteListener (webContents, webContentsId) {
|
registerDeleteListener (webContents, contextId) {
|
||||||
const processId = webContents.getProcessId()
|
const processId = webContents.getProcessId()
|
||||||
const listener = (event, deletedProcessId) => {
|
const listener = (event, deletedProcessId) => {
|
||||||
if (deletedProcessId === processId) {
|
if (deletedProcessId === processId) {
|
||||||
webContents.removeListener('render-view-deleted', listener)
|
webContents.removeListener('render-view-deleted', listener)
|
||||||
this.clear(webContentsId)
|
this.clear(contextId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
webContents.on('render-view-deleted', listener)
|
webContents.on('render-view-deleted', listener)
|
||||||
|
|
|
@ -56,7 +56,7 @@ let getObjectPrototype = function (object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a real value into meta data.
|
// Convert a real value into meta data.
|
||||||
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
let valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
|
||||||
// Determine the type of value.
|
// Determine the type of value.
|
||||||
const meta = { type: typeof value }
|
const meta = { type: typeof value }
|
||||||
if (meta.type === 'object') {
|
if (meta.type === 'object') {
|
||||||
|
@ -84,14 +84,14 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||||
|
|
||||||
// Fill the meta object according to value's type.
|
// Fill the meta object according to value's type.
|
||||||
if (meta.type === 'array') {
|
if (meta.type === 'array') {
|
||||||
meta.members = value.map((el) => valueToMeta(sender, el, optimizeSimpleObject))
|
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||||
meta.name = value.constructor ? value.constructor.name : ''
|
meta.name = value.constructor ? value.constructor.name : ''
|
||||||
|
|
||||||
// Reference the original value if it's an object, because when it's
|
// Reference the original value if it's an object, because when it's
|
||||||
// passed to renderer we would assume the renderer keeps a reference of
|
// passed to renderer we would assume the renderer keeps a reference of
|
||||||
// it.
|
// it.
|
||||||
meta.id = objectsRegistry.add(sender, value)
|
meta.id = objectsRegistry.add(sender, contextId, value)
|
||||||
meta.members = getObjectMembers(value)
|
meta.members = getObjectMembers(value)
|
||||||
meta.proto = getObjectPrototype(value)
|
meta.proto = getObjectPrototype(value)
|
||||||
} else if (meta.type === 'buffer') {
|
} else if (meta.type === 'buffer') {
|
||||||
|
@ -101,7 +101,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||||
// Instead they should appear in the renderer process
|
// Instead they should appear in the renderer process
|
||||||
value.then(function () {}, function () {})
|
value.then(function () {}, function () {})
|
||||||
|
|
||||||
meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
|
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
|
||||||
value.then(onFulfilled, onRejected)
|
value.then(onFulfilled, onRejected)
|
||||||
})
|
})
|
||||||
} else if (meta.type === 'error') {
|
} else if (meta.type === 'error') {
|
||||||
|
@ -132,12 +132,12 @@ const plainObjectToMeta = function (obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Error into meta data.
|
// Convert Error into meta data.
|
||||||
const exceptionToMeta = function (sender, error) {
|
const exceptionToMeta = function (sender, contextId, error) {
|
||||||
return {
|
return {
|
||||||
type: 'exception',
|
type: 'exception',
|
||||||
message: error.message,
|
message: error.message,
|
||||||
stack: error.stack || error,
|
stack: error.stack || error,
|
||||||
cause: valueToMeta(sender, error.cause)
|
cause: valueToMeta(sender, contextId, error.cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ const removeRemoteListenersAndLogWarning = (sender, meta, callIntoRenderer) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert array of meta data from renderer into array of real values.
|
// Convert array of meta data from renderer into array of real values.
|
||||||
const unwrapArgs = function (sender, args) {
|
const unwrapArgs = function (sender, contextId, args) {
|
||||||
const metaToValue = function (meta) {
|
const metaToValue = function (meta) {
|
||||||
switch (meta.type) {
|
switch (meta.type) {
|
||||||
case 'value':
|
case 'value':
|
||||||
|
@ -177,7 +177,7 @@ const unwrapArgs = function (sender, args) {
|
||||||
case 'remote-object':
|
case 'remote-object':
|
||||||
return objectsRegistry.get(meta.id)
|
return objectsRegistry.get(meta.id)
|
||||||
case 'array':
|
case 'array':
|
||||||
return unwrapArgs(sender, meta.value)
|
return unwrapArgs(sender, contextId, meta.value)
|
||||||
case 'buffer':
|
case 'buffer':
|
||||||
return bufferUtils.metaToBuffer(meta.value)
|
return bufferUtils.metaToBuffer(meta.value)
|
||||||
case 'date':
|
case 'date':
|
||||||
|
@ -201,26 +201,26 @@ const unwrapArgs = function (sender, args) {
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
case 'function': {
|
case 'function': {
|
||||||
// Merge webContentsId and meta.id, since meta.id can be the same in
|
// Merge contextId and meta.id, since meta.id can be the same in
|
||||||
// different webContents.
|
// different webContents.
|
||||||
const webContentsId = sender.getId()
|
const objectId = [contextId, meta.id]
|
||||||
const objectId = [webContentsId, meta.id]
|
|
||||||
|
|
||||||
// Cache the callbacks in renderer.
|
// Cache the callbacks in renderer.
|
||||||
if (rendererFunctions.has(objectId)) {
|
if (rendererFunctions.has(objectId)) {
|
||||||
return rendererFunctions.get(objectId)
|
return rendererFunctions.get(objectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const webContentsId = sender.getId()
|
||||||
let callIntoRenderer = function (...args) {
|
let callIntoRenderer = function (...args) {
|
||||||
if (!sender.isDestroyed() && webContentsId === sender.getId()) {
|
if (!sender.isDestroyed() && webContentsId === sender.getId()) {
|
||||||
sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
|
sender.send('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||||
} else {
|
} else {
|
||||||
removeRemoteListenersAndLogWarning(this, meta, callIntoRenderer)
|
removeRemoteListenersAndLogWarning(this, meta, callIntoRenderer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
||||||
|
|
||||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
|
v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
|
||||||
rendererFunctions.set(objectId, callIntoRenderer)
|
rendererFunctions.set(objectId, callIntoRenderer)
|
||||||
return callIntoRenderer
|
return callIntoRenderer
|
||||||
}
|
}
|
||||||
|
@ -233,18 +233,18 @@ const unwrapArgs = function (sender, args) {
|
||||||
|
|
||||||
// Call a function and send reply asynchronously if it's a an asynchronous
|
// Call a function and send reply asynchronously if it's a an asynchronous
|
||||||
// style function and the caller didn't pass a callback.
|
// style function and the caller didn't pass a callback.
|
||||||
const callFunction = function (event, func, caller, args) {
|
const callFunction = function (event, contextId, func, caller, args) {
|
||||||
const funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
const funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||||
const funcPassedCallback = typeof args[args.length - 1] === 'function'
|
const funcPassedCallback = typeof args[args.length - 1] === 'function'
|
||||||
try {
|
try {
|
||||||
if (funcMarkedAsync && !funcPassedCallback) {
|
if (funcMarkedAsync && !funcPassedCallback) {
|
||||||
args.push(function (ret) {
|
args.push(function (ret) {
|
||||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||||
})
|
})
|
||||||
func.apply(caller, args)
|
func.apply(caller, args)
|
||||||
} else {
|
} else {
|
||||||
const ret = func.apply(caller, args)
|
const ret = func.apply(caller, args)
|
||||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Catch functions thrown further down in function invocation and wrap
|
// Catch functions thrown further down in function invocation and wrap
|
||||||
|
@ -257,105 +257,105 @@ const callFunction = function (event, func, caller, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
|
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, contextId, module) {
|
||||||
try {
|
try {
|
||||||
event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
|
event.returnValue = valueToMeta(event.sender, contextId, process.mainModule.require(module))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) {
|
||||||
try {
|
try {
|
||||||
event.returnValue = valueToMeta(event.sender, electron[module])
|
event.returnValue = valueToMeta(event.sender, contextId, electron[module])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
|
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, contextId, name) {
|
||||||
try {
|
try {
|
||||||
event.returnValue = valueToMeta(event.sender, global[name])
|
event.returnValue = valueToMeta(event.sender, contextId, global[name])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
|
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
|
||||||
try {
|
try {
|
||||||
event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
|
event.returnValue = valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow())
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
|
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
|
||||||
event.returnValue = valueToMeta(event.sender, event.sender)
|
event.returnValue = valueToMeta(event.sender, contextId, event.sender)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
|
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||||
try {
|
try {
|
||||||
args = unwrapArgs(event.sender, args)
|
args = unwrapArgs(event.sender, contextId, args)
|
||||||
let constructor = objectsRegistry.get(id)
|
let constructor = objectsRegistry.get(id)
|
||||||
|
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
|
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
event.returnValue = valueToMeta(event.sender, new constructor(...args))
|
event.returnValue = valueToMeta(event.sender, contextId, new constructor(...args))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
|
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||||
try {
|
try {
|
||||||
args = unwrapArgs(event.sender, args)
|
args = unwrapArgs(event.sender, contextId, args)
|
||||||
let func = objectsRegistry.get(id)
|
let func = objectsRegistry.get(id)
|
||||||
|
|
||||||
if (func == null) {
|
if (func == null) {
|
||||||
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
callFunction(event, func, global, args)
|
callFunction(event, contextId, func, global, args)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
|
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||||
try {
|
try {
|
||||||
args = unwrapArgs(event.sender, args)
|
args = unwrapArgs(event.sender, contextId, args)
|
||||||
let object = objectsRegistry.get(id)
|
let object = objectsRegistry.get(id)
|
||||||
|
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
|
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
event.returnValue = valueToMeta(event.sender, new object[method](...args))
|
event.returnValue = valueToMeta(event.sender, contextId, new object[method](...args))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
|
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||||
try {
|
try {
|
||||||
args = unwrapArgs(event.sender, args)
|
args = unwrapArgs(event.sender, contextId, args)
|
||||||
let obj = objectsRegistry.get(id)
|
let obj = objectsRegistry.get(id)
|
||||||
|
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
|
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
callFunction(event, obj[method], obj, args)
|
callFunction(event, contextId, obj[method], obj, args)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||||
try {
|
try {
|
||||||
args = unwrapArgs(event.sender, args)
|
args = unwrapArgs(event.sender, contextId, args)
|
||||||
let obj = objectsRegistry.get(id)
|
let obj = objectsRegistry.get(id)
|
||||||
|
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
|
@ -365,11 +365,11 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
||||||
obj[name] = args[0]
|
obj[name] = args[0]
|
||||||
event.returnValue = null
|
event.returnValue = null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
|
||||||
try {
|
try {
|
||||||
let obj = objectsRegistry.get(id)
|
let obj = objectsRegistry.get(id)
|
||||||
|
|
||||||
|
@ -377,14 +377,14 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
||||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
event.returnValue = valueToMeta(event.sender, obj[name])
|
event.returnValue = valueToMeta(event.sender, contextId, obj[name])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
|
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id) {
|
||||||
objectsRegistry.remove(event.sender.getId(), id)
|
objectsRegistry.remove(contextId, id)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||||
|
@ -392,16 +392,16 @@ ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||||
e.returnValue = null
|
e.returnValue = null
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
|
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
|
||||||
try {
|
try {
|
||||||
let guestViewManager = require('./guest-view-manager')
|
let guestViewManager = require('./guest-view-manager')
|
||||||
event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
|
event.returnValue = valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
|
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, contextId, requestId, guestInstanceId, method, ...args) {
|
||||||
try {
|
try {
|
||||||
let guestViewManager = require('./guest-view-manager')
|
let guestViewManager = require('./guest-view-manager')
|
||||||
let guest = guestViewManager.getGuest(guestInstanceId)
|
let guest = guestViewManager.getGuest(guestInstanceId)
|
||||||
|
@ -413,7 +413,7 @@ ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, request
|
||||||
}
|
}
|
||||||
guest[method].apply(guest, args)
|
guest[method].apply(guest, args)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.returnValue = exceptionToMeta(event.sender, error)
|
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,18 @@ const bufferUtils = require('../../common/buffer-utils')
|
||||||
const callbacksRegistry = new CallbacksRegistry()
|
const callbacksRegistry = new CallbacksRegistry()
|
||||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||||
|
|
||||||
|
// An unique ID that can represent current context.
|
||||||
|
const contextId = v8Util.getContextId()
|
||||||
|
|
||||||
|
// Notify the main process when current context is going to be released.
|
||||||
|
// Note that when the renderer process is destroyed, the message may not be
|
||||||
|
// sent, we also listen to the "render-view-deleted" event in the main process
|
||||||
|
// to guard that situation.
|
||||||
|
process.on('exit', () => {
|
||||||
|
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||||
|
ipcRenderer.sendSync(command, contextId)
|
||||||
|
})
|
||||||
|
|
||||||
// Convert the arguments object into an array of meta data.
|
// Convert the arguments object into an array of meta data.
|
||||||
function wrapArgs (args, visited = new Set()) {
|
function wrapArgs (args, visited = new Set()) {
|
||||||
const valueToMeta = (value) => {
|
const valueToMeta = (value) => {
|
||||||
|
@ -107,7 +119,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||||
} else {
|
} else {
|
||||||
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
||||||
}
|
}
|
||||||
const ret = ipcRenderer.sendSync(command, metaId, member.name, wrapArgs(args))
|
const ret = ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
|
||||||
return metaToValue(ret)
|
return metaToValue(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +138,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||||
} else if (member.type === 'get') {
|
} else if (member.type === 'get') {
|
||||||
descriptor.get = () => {
|
descriptor.get = () => {
|
||||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||||
const meta = ipcRenderer.sendSync(command, metaId, member.name)
|
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +146,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||||
descriptor.set = (value) => {
|
descriptor.set = (value) => {
|
||||||
const args = wrapArgs([value])
|
const args = wrapArgs([value])
|
||||||
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
||||||
const meta = ipcRenderer.sendSync(command, metaId, member.name, args)
|
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name, args)
|
||||||
if (meta != null) metaToValue(meta)
|
if (meta != null) metaToValue(meta)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -164,7 +176,7 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
|
||||||
if (loaded) return
|
if (loaded) return
|
||||||
loaded = true
|
loaded = true
|
||||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||||
const meta = ipcRenderer.sendSync(command, metaId, name)
|
const meta = ipcRenderer.sendSync(command, contextId, metaId, name)
|
||||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +236,7 @@ function metaToValue (meta) {
|
||||||
} else {
|
} else {
|
||||||
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
||||||
}
|
}
|
||||||
const obj = ipcRenderer.sendSync(command, meta.id, wrapArgs(args))
|
const obj = ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args))
|
||||||
return metaToValue(obj)
|
return metaToValue(obj)
|
||||||
}
|
}
|
||||||
ret = remoteFunction
|
ret = remoteFunction
|
||||||
|
@ -237,7 +249,7 @@ function metaToValue (meta) {
|
||||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||||
|
|
||||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||||
v8Util.setRemoteObjectFreer(ret, meta.id)
|
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
|
||||||
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
||||||
remoteObjectCache.set(meta.id, ret)
|
remoteObjectCache.set(meta.id, ret)
|
||||||
return ret
|
return ret
|
||||||
|
@ -264,60 +276,51 @@ function metaToException (meta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browser calls a callback in renderer.
|
// Browser calls a callback in renderer.
|
||||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => {
|
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, passedContextId, id, args) => {
|
||||||
|
if (passedContextId !== contextId) {
|
||||||
|
// The invoked callback belongs to an old page in this renderer.
|
||||||
|
return
|
||||||
|
}
|
||||||
callbacksRegistry.apply(id, metaToValue(args))
|
callbacksRegistry.apply(id, metaToValue(args))
|
||||||
})
|
})
|
||||||
|
|
||||||
// A callback in browser is released.
|
// A callback in browser is released.
|
||||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => {
|
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, passedContextId, id) => {
|
||||||
|
if (passedContextId !== contextId) {
|
||||||
|
// The freed callback belongs to an old page in this renderer.
|
||||||
|
return
|
||||||
|
}
|
||||||
callbacksRegistry.remove(id)
|
callbacksRegistry.remove(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on('exit', () => {
|
|
||||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
|
||||||
ipcRenderer.sendSync(command, initialContext)
|
|
||||||
})
|
|
||||||
|
|
||||||
exports.require = (module) => {
|
exports.require = (module) => {
|
||||||
const command = 'ELECTRON_BROWSER_REQUIRE'
|
const command = 'ELECTRON_BROWSER_REQUIRE'
|
||||||
const meta = ipcRenderer.sendSync(command, module)
|
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias to remote.require('electron').xxx.
|
// Alias to remote.require('electron').xxx.
|
||||||
exports.getBuiltin = (module) => {
|
exports.getBuiltin = (module) => {
|
||||||
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
||||||
const meta = ipcRenderer.sendSync(command, module)
|
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getCurrentWindow = () => {
|
exports.getCurrentWindow = () => {
|
||||||
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
||||||
const meta = ipcRenderer.sendSync(command)
|
const meta = ipcRenderer.sendSync(command, contextId)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current WebContents object.
|
// Get current WebContents object.
|
||||||
exports.getCurrentWebContents = () => {
|
exports.getCurrentWebContents = () => {
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
|
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
|
||||||
}
|
|
||||||
|
|
||||||
const CONTEXT_ARG = '--context-id='
|
|
||||||
let initialContext = process.argv.find(arg => arg.startsWith(CONTEXT_ARG))
|
|
||||||
if (process.webContentsId) {
|
|
||||||
// set by sandbox renderer init script
|
|
||||||
initialContext = process.webContentsId
|
|
||||||
} else if (initialContext) {
|
|
||||||
initialContext = parseInt(initialContext.substr(CONTEXT_ARG.length), 10)
|
|
||||||
} else {
|
|
||||||
// if not available, pull from remote
|
|
||||||
initialContext = exports.getCurrentWebContents().getId()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a global object in browser.
|
// Get a global object in browser.
|
||||||
exports.getGlobal = (name) => {
|
exports.getGlobal = (name) => {
|
||||||
const command = 'ELECTRON_BROWSER_GLOBAL'
|
const command = 'ELECTRON_BROWSER_GLOBAL'
|
||||||
const meta = ipcRenderer.sendSync(command, name)
|
const meta = ipcRenderer.sendSync(command, contextId, name)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +337,7 @@ exports.createFunctionWithReturnValue = (returnValue) => {
|
||||||
// Get the guest WebContents from guestInstanceId.
|
// Get the guest WebContents from guestInstanceId.
|
||||||
exports.getGuestWebContents = (guestInstanceId) => {
|
exports.getGuestWebContents = (guestInstanceId) => {
|
||||||
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
|
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
|
||||||
const meta = ipcRenderer.sendSync(command, guestInstanceId)
|
const meta = ipcRenderer.sendSync(command, contextId, guestInstanceId)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ const webViewConstants = require('./web-view-constants')
|
||||||
|
|
||||||
const hasProp = {}.hasOwnProperty
|
const hasProp = {}.hasOwnProperty
|
||||||
|
|
||||||
|
// An unique ID that can represent current context.
|
||||||
|
const contextId = v8Util.getContextId()
|
||||||
|
|
||||||
// ID generator.
|
// ID generator.
|
||||||
let nextId = 0
|
let nextId = 0
|
||||||
|
|
||||||
|
@ -396,7 +399,7 @@ const registerWebViewElement = function () {
|
||||||
const createNonBlockHandler = function (m) {
|
const createNonBlockHandler = function (m) {
|
||||||
return function (...args) {
|
return function (...args) {
|
||||||
const internal = v8Util.getHiddenValue(this, 'internal')
|
const internal = v8Util.getHiddenValue(this, 'internal')
|
||||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m, ...args)
|
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, null, internal.guestInstanceId, m, ...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const method of nonblockMethods) {
|
for (const method of nonblockMethods) {
|
||||||
|
@ -410,7 +413,7 @@ const registerWebViewElement = function () {
|
||||||
hasUserGesture = false
|
hasUserGesture = false
|
||||||
}
|
}
|
||||||
const requestId = getNextId()
|
const requestId = getNextId()
|
||||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
||||||
ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) {
|
ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) {
|
||||||
if (callback) callback(result)
|
if (callback) callback(result)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue