Skip to content
This repository has been archived by the owner on Jan 4, 2019. It is now read-only.

Commit

Permalink
add webcontents/webview clone support
Browse files Browse the repository at this point in the history
auditors: @bbondy
  • Loading branch information
bridiver committed Jul 19, 2016
1 parent 975dcac commit 683ea44
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 21 deletions.
54 changes: 52 additions & 2 deletions atom/browser/api/atom_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -947,8 +947,12 @@ void WebContents::Reload(bool ignore_cache) {

void WebContents::ResumeLoadingCreatedWebContents() {
if (delayed_open_url_params_.get()) {
OpenURLFromTab(web_contents(), *delayed_open_url_params_.get());
delayed_open_url_params_.reset(nullptr);
if (delayed_open_url_params_->url == web_contents()->GetLastCommittedURL()) {
web_contents()->GetController().LoadIfNecessary();
} else {
OpenURLFromTab(web_contents(), *delayed_open_url_params_.get());
delayed_open_url_params_.reset(nullptr);
}
return;
}
GetWebContents()->ResumeLoadingCreatedWebContents();
Expand Down Expand Up @@ -1386,6 +1390,51 @@ void WebContents::Focus() {
web_contents()->Focus();
}

mate::Handle<WebContents> WebContents::Clone(const mate::Dictionary& options) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());

mate::Dictionary cloneOptions(options);

int guest_instance_id = -1;
if (IsGuest()) {
cloneOptions.Set("isGuest", true);
cloneOptions.Set("embedder", embedder_);
guest_instance_id = guest_delegate_->GetNextInstanceId();
cloneOptions.Set(options::kGuestInstanceID, guest_instance_id);
}

mate::Handle<api::Session> session;
if (!cloneOptions.Get("session", &session)) {
session = atom::api::Session::CreateFrom(isolate(),
static_cast<AtomBrowserContext*>(
web_contents()->GetBrowserContext()));
cloneOptions.Set("session", session);
}

GURL delayed_load_url;
if (!cloneOptions.Get("delayedLoadUrl", &delayed_load_url)) {
cloneOptions.Set("delayedLoadUrl", GetURL());
}

content::WebContents::CreateParams create_params(
session->browser_context(),
web_contents()->GetSiteInstance());
auto clone = new WebContents(isolate(), cloneOptions, &create_params);
clone->web_contents()->GetController().CopyStateFrom(
web_contents()->GetController());

auto handle = mate::CreateHandle(
isolate(), clone);
g_wrap_web_contents.Run(handle.ToV8());

if (IsGuest()) {
guest_delegate_->RegisterGuest(handle, guest_instance_id);
}

return handle;
}

void WebContents::SetActive(bool active) {
if (Emit("set-active", active))
return;
Expand Down Expand Up @@ -1675,6 +1724,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("findInPage", &WebContents::FindInPage)
.SetMethod("stopFindInPage", &WebContents::StopFindInPage)
.SetMethod("focus", &WebContents::Focus)
.SetMethod("clone", &WebContents::Clone)
.SetMethod("tabTraverse", &WebContents::TabTraverse)
.SetMethod("_send", &WebContents::SendIPCMessage)
.SetMethod("sendInputEvent", &WebContents::SendInputEvent)
Expand Down
1 change: 1 addition & 0 deletions atom/browser/api/atom_api_web_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
uint32_t FindInPage(mate::Arguments* args);
void StopFindInPage(content::StopFindAction action);
void ShowDefinitionForSelection();
mate::Handle<WebContents> Clone(const mate::Dictionary& options);

// Focus.
void Focus();
Expand Down
40 changes: 27 additions & 13 deletions atom/browser/web_view_guest_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,7 @@ content::WebContents* WebViewGuestDelegate::CreateNewGuestWindow(
mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
options.Set("isGuest", true);

// get the next guest id and assign it to options and webPreferences
node::Environment* env = node::Environment::GetCurrent(isolate);
auto next_instance_id_event = v8::Local<v8::Object>::Cast(
mate::Event::Create(isolate).ToV8());
mate::EmitEvent(isolate,
env->process_object(),
"ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID",
next_instance_id_event);
int guest_instance_id = next_instance_id_event->Get(
mate::StringToV8(isolate, "returnValue"))->NumberValue();
int guest_instance_id = GetNextInstanceId();
options.Set(options::kGuestInstanceID, guest_instance_id);

// TODO(bridiver) should we create a new site instance
Expand All @@ -93,18 +84,41 @@ content::WebContents* WebViewGuestDelegate::CreateNewGuestWindow(
mate::Handle<api::WebContents> new_api_web_contents =
api::WebContents::CreateWithParams(isolate, options, params);
content::WebContents* web_contents = new_api_web_contents->GetWebContents();
RegisterGuest(new_api_web_contents, guest_instance_id);

return web_contents;
}

void WebViewGuestDelegate::RegisterGuest(mate::Handle<api::WebContents> guest,
int guest_instance_id) {
auto isolate = api_web_contents_->isolate();
v8::HandleScope handle_scope(isolate);

// register the guest so we can find it in the new window
auto add_guest_event =
v8::Local<v8::Object>::Cast(mate::Event::Create(isolate).ToV8());
node::Environment* env = node::Environment::GetCurrent(isolate);
mate::EmitEvent(isolate,
env->process_object(),
"ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST",
add_guest_event,
new_api_web_contents,
guest,
guest_instance_id);
}

return web_contents;
int WebViewGuestDelegate::GetNextInstanceId() {
auto isolate = api_web_contents_->isolate();
v8::HandleScope handle_scope(isolate);

// get the next guest id and assign it to options and webPreferences
auto next_instance_id_event = v8::Local<v8::Object>::Cast(
mate::Event::Create(isolate).ToV8());
node::Environment* env = node::Environment::GetCurrent(isolate);
mate::EmitEvent(isolate,
env->process_object(),
"ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID",
next_instance_id_event);
return next_instance_id_event->Get(
mate::StringToV8(isolate, "returnValue"))->NumberValue();
}

void WebViewGuestDelegate::SetSize(const SetSizeParams& params) {
Expand Down
3 changes: 3 additions & 0 deletions atom/browser/web_view_guest_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class WebViewGuestDelegate : public content::BrowserPluginGuestDelegate,

bool IsAttached();

void RegisterGuest(mate::Handle<api::WebContents> guest,
int guest_instance_id);
int GetNextInstanceId();
protected:
// content::WebContentsObserver:
void DidStartProvisionalLoadForFrame(
Expand Down
38 changes: 32 additions & 6 deletions lib/renderer/web-view/web-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ var WebViewImpl = (function () {
}

// Resets some state upon reattaching <webview> element to the DOM.
WebViewImpl.prototype.reset = function () {
WebViewImpl.prototype.reset = function (destroyGuest = true) {
// Unlisten the zoom-level-changed event.
webFrame.removeListener('zoom-level-changed', this.onZoomLevelChanged)
ipcRenderer.removeListener('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', this.onVisibilityChanged)
Expand All @@ -84,7 +84,9 @@ var WebViewImpl = (function () {
// heard back from createGuest yet. We will not reset the flag in this case so
// that we don't end up allocating a second guest.
if (this.guestInstanceId) {
guestViewInternal.destroyGuest(this.guestInstanceId)
if (destroyGuest) {
guestViewInternal.destroyGuest(this.guestInstanceId)
}
this.webContents = null
this.guestInstanceId = void 0
this.beforeFirstNavigation = true
Expand Down Expand Up @@ -338,15 +340,30 @@ var registerWebViewElement = function () {
}
return internal.handleWebviewAttributeMutation(name, oldValue, newValue)
}
proto.detachedCallback = function () {
proto.__defineGetter__('guestInstanceId', function () {
var internal
internal = v8Util.getHiddenValue(this, 'internal')
if (!internal) {
return
}
guestViewInternal.deregisterEvents(internal.viewInstanceId)
internal.elementAttached = false
return internal.reset()
return internal.guestInstanceId
})
proto.detach = function (destroyGuest = false) {
var internal
internal = v8Util.getHiddenValue(this, 'internal')
if (!internal) {
return
}
if (internal.elementAttached) {
guestViewInternal.deregisterEvents(internal.viewInstanceId)
internal.elementAttached = false
let guestInstanceId = internal.guestInstanceId
internal.reset(destroyGuest)
return guestInstanceId
}
}
proto.detachedCallback = function () {
this.detach(true)
}
proto.attachedCallback = function () {
var internal
Expand All @@ -360,6 +377,15 @@ var registerWebViewElement = function () {
return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
}
}
proto.clone = function (options) {
const internal = v8Util.getHiddenValue(this, 'internal')
if (internal.webContents) {
const options = Object.assign({}, internal.webContents.getWebPreferences())
return internal.webContents.clone(options)
} else {
throw new Error(`Cannot call clone because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.`)
}
}

// Public-facing API methods.
methods = [
Expand Down

1 comment on commit 683ea44

@bbondy
Copy link
Member

@bbondy bbondy commented on 683ea44 Jul 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

Please sign in to comment.