Merge pull request #740 from atom/webview

Implement <webview> tag
This commit is contained in:
Cheng Zhao 2014-10-26 19:43:52 +08:00
commit 0839915538
56 changed files with 2303 additions and 602 deletions

View file

@ -29,6 +29,7 @@
'atom/browser/api/lib/tray.coffee', 'atom/browser/api/lib/tray.coffee',
'atom/browser/api/lib/web-contents.coffee', 'atom/browser/api/lib/web-contents.coffee',
'atom/browser/lib/chrome-extension.coffee', 'atom/browser/lib/chrome-extension.coffee',
'atom/browser/lib/guest-view-manager.coffee',
'atom/browser/lib/init.coffee', 'atom/browser/lib/init.coffee',
'atom/browser/lib/objects-registry.coffee', 'atom/browser/lib/objects-registry.coffee',
'atom/browser/lib/rpc-server.coffee', 'atom/browser/lib/rpc-server.coffee',
@ -41,12 +42,14 @@
'atom/common/lib/init.coffee', 'atom/common/lib/init.coffee',
'atom/common/lib/asar.coffee', 'atom/common/lib/asar.coffee',
'atom/renderer/lib/chrome-api.coffee', 'atom/renderer/lib/chrome-api.coffee',
'atom/renderer/lib/guest-view-internal.coffee',
'atom/renderer/lib/init.coffee', 'atom/renderer/lib/init.coffee',
'atom/renderer/lib/inspector.coffee', 'atom/renderer/lib/inspector.coffee',
'atom/renderer/lib/override.coffee', 'atom/renderer/lib/override.coffee',
'atom/renderer/lib/web-view.coffee',
'atom/renderer/api/lib/ipc.coffee', 'atom/renderer/api/lib/ipc.coffee',
'atom/renderer/api/lib/remote.coffee', 'atom/renderer/api/lib/remote.coffee',
'atom/renderer/api/lib/web-view.coffee', 'atom/renderer/api/lib/web-frame.coffee',
], ],
'lib_sources': [ 'lib_sources': [
'atom/app/atom_content_client.cc', 'atom/app/atom_content_client.cc',
@ -100,8 +103,6 @@
'atom/browser/atom_browser_main_parts_mac.mm', 'atom/browser/atom_browser_main_parts_mac.mm',
'atom/browser/atom_javascript_dialog_manager.cc', 'atom/browser/atom_javascript_dialog_manager.cc',
'atom/browser/atom_javascript_dialog_manager.h', 'atom/browser/atom_javascript_dialog_manager.h',
'atom/browser/atom_resource_dispatcher_host_delegate.cc',
'atom/browser/atom_resource_dispatcher_host_delegate.h',
'atom/browser/atom_speech_recognition_manager_delegate.cc', 'atom/browser/atom_speech_recognition_manager_delegate.cc',
'atom/browser/atom_speech_recognition_manager_delegate.h', 'atom/browser/atom_speech_recognition_manager_delegate.h',
'atom/browser/browser.cc', 'atom/browser/browser.cc',
@ -178,6 +179,10 @@
'atom/browser/ui/win/notify_icon.h', 'atom/browser/ui/win/notify_icon.h',
'atom/browser/ui/x/x_window_utils.cc', 'atom/browser/ui/x/x_window_utils.cc',
'atom/browser/ui/x/x_window_utils.h', 'atom/browser/ui/x/x_window_utils.h',
'atom/browser/web_view/web_view_manager.cc',
'atom/browser/web_view/web_view_manager.h',
'atom/browser/web_view/web_view_renderer_state.cc',
'atom/browser/web_view/web_view_renderer_state.h',
'atom/browser/window_list.cc', 'atom/browser/window_list.cc',
'atom/browser/window_list.h', 'atom/browser/window_list.h',
'atom/browser/window_list_observer.h', 'atom/browser/window_list_observer.h',
@ -222,6 +227,7 @@
'atom/common/native_mate_converters/accelerator_converter.cc', 'atom/common/native_mate_converters/accelerator_converter.cc',
'atom/common/native_mate_converters/accelerator_converter.h', 'atom/common/native_mate_converters/accelerator_converter.h',
'atom/common/native_mate_converters/file_path_converter.h', 'atom/common/native_mate_converters/file_path_converter.h',
'atom/common/native_mate_converters/gfx_converter.h',
'atom/common/native_mate_converters/gurl_converter.h', 'atom/common/native_mate_converters/gurl_converter.h',
'atom/common/native_mate_converters/image_converter.cc', 'atom/common/native_mate_converters/image_converter.cc',
'atom/common/native_mate_converters/image_converter.h', 'atom/common/native_mate_converters/image_converter.h',
@ -248,8 +254,8 @@
'atom/renderer/api/atom_api_renderer_ipc.cc', 'atom/renderer/api/atom_api_renderer_ipc.cc',
'atom/renderer/api/atom_renderer_bindings.cc', 'atom/renderer/api/atom_renderer_bindings.cc',
'atom/renderer/api/atom_renderer_bindings.h', 'atom/renderer/api/atom_renderer_bindings.h',
'atom/renderer/api/atom_api_web_view.cc', 'atom/renderer/api/atom_api_web_frame.cc',
'atom/renderer/api/atom_api_web_view.h', 'atom/renderer/api/atom_api_web_frame.h',
'atom/renderer/atom_render_view_observer.cc', 'atom/renderer/atom_render_view_observer.cc',
'atom/renderer/atom_render_view_observer.h', 'atom/renderer/atom_render_view_observer.h',
'atom/renderer/atom_renderer_client.cc', 'atom/renderer/atom_renderer_client.cc',

View file

@ -4,7 +4,9 @@
#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/common/api/api_messages.h" #include "atom/common/api/api_messages.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/native_mate_converters/value_converter.h"
@ -12,16 +14,118 @@
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h" #include "native_mate/object_template_builder.h"
#include "atom/common/node_includes.h"
namespace atom { namespace atom {
namespace api { namespace api {
namespace {
v8::Persistent<v8::ObjectTemplate> template_;
} // namespace
WebContents::WebContents(content::WebContents* web_contents) WebContents::WebContents(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents), : content::WebContentsObserver(web_contents),
web_contents_(web_contents) { guest_instance_id_(-1),
guest_opaque_(true),
auto_size_enabled_(false) {
}
WebContents::WebContents(const mate::Dictionary& options)
: guest_instance_id_(-1),
guest_opaque_(true),
auto_size_enabled_(false) {
options.Get("guestInstanceId", &guest_instance_id_);
auto browser_context = AtomBrowserContext::Get();
content::SiteInstance* site_instance = content::SiteInstance::CreateForURL(
browser_context, GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params(browser_context, site_instance);
bool is_guest;
if (options.Get("isGuest", &is_guest) && is_guest)
params.guest_delegate = this;
storage_.reset(content::WebContents::Create(params));
storage_->SetDelegate(this);
Observe(storage_.get());
}
WebContents::~WebContents() {
Destroy();
}
bool WebContents::AddMessageToConsole(content::WebContents* source,
int32 level,
const base::string16& message,
int32 line_no,
const base::string16& source_id) {
base::ListValue args;
args.AppendInteger(level);
args.AppendString(message);
args.AppendInteger(line_no);
args.AppendString(source_id);
Emit("console-message", args);
return true;
}
bool WebContents::ShouldCreateWebContents(
content::WebContents* web_contents,
int route_id,
WindowContainerType window_container_type,
const base::string16& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) {
base::ListValue args;
args.AppendString(target_url.spec());
args.AppendString(partition_id);
Emit("new-window", args);
return false;
}
void WebContents::CloseContents(content::WebContents* source) {
Emit("close");
}
content::WebContents* WebContents::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
if (params.disposition != CURRENT_TAB)
return NULL;
content::NavigationController::LoadURLParams load_url_params(params.url);
load_url_params.referrer = params.referrer;
load_url_params.transition_type = params.transition;
load_url_params.extra_headers = params.extra_headers;
load_url_params.should_replace_current_entry =
params.should_replace_current_entry;
load_url_params.is_renderer_initiated = params.is_renderer_initiated;
load_url_params.transferred_global_request_id =
params.transferred_global_request_id;
web_contents()->GetController().LoadURLWithParams(load_url_params);
return web_contents();
}
void WebContents::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (!attached())
return;
// Send the unhandled keyboard events back to the embedder to reprocess them.
embedder_web_contents_->GetDelegate()->HandleKeyboardEvent(
web_contents(), event);
} }
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) { void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
@ -47,6 +151,16 @@ void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
Emit("did-finish-load"); Emit("did-finish-load");
} }
void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) {
base::ListValue args;
args.AppendInteger(error_code);
args.AppendString(error_description);
Emit("did-fail-load", args);
}
void WebContents::DidStartLoading(content::RenderViewHost* render_view_host) { void WebContents::DidStartLoading(content::RenderViewHost* render_view_host) {
Emit("did-start-loading"); Emit("did-start-loading");
} }
@ -55,6 +169,17 @@ void WebContents::DidStopLoading(content::RenderViewHost* render_view_host) {
Emit("did-stop-loading"); Emit("did-stop-loading");
} }
void WebContents::DidGetRedirectForResourceRequest(
content::RenderViewHost* render_view_host,
const content::ResourceRedirectDetails& details) {
base::ListValue args;
args.AppendString(details.url.spec());
args.AppendString(details.new_url.spec());
args.AppendBoolean(
details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME);
Emit("did-get-redirect-request", args);
}
bool WebContents::OnMessageReceived(const IPC::Message& message) { bool WebContents::OnMessageReceived(const IPC::Message& message) {
bool handled = true; bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WebContents, message) IPC_BEGIN_MESSAGE_MAP(WebContents, message)
@ -67,12 +192,88 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
return handled; return handled;
} }
void WebContents::RenderViewReady() {
if (!is_guest())
return;
// We don't want to accidentally set the opacity of an interstitial page.
// WebContents::GetRenderWidgetHostView will return the RWHV of an
// interstitial page if one is showing at this time. We only want opacity
// to apply to web pages.
web_contents()->GetRenderViewHost()->GetView()->
SetBackgroundOpaque(guest_opaque_);
content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
if (auto_size_enabled_) {
rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
} else {
rvh->DisableAutoResize(element_size_);
}
}
void WebContents::WebContentsDestroyed() { void WebContents::WebContentsDestroyed() {
// The RenderViewDeleted was not called when the WebContents is destroyed. // The RenderViewDeleted was not called when the WebContents is destroyed.
RenderViewDeleted(web_contents_->GetRenderViewHost()); RenderViewDeleted(web_contents()->GetRenderViewHost());
Emit("destroyed"); Emit("destroyed");
} }
void WebContents::WillAttach(content::WebContents* embedder_web_contents,
const base::DictionaryValue& extra_params) {
embedder_web_contents_ = embedder_web_contents;
extra_params_.reset(extra_params.DeepCopy());
}
content::WebContents* WebContents::CreateNewGuestWindow(
const content::WebContents::CreateParams& create_params) {
NOTREACHED() << "Should not create new window from guest";
return nullptr;
}
void WebContents::DidAttach() {
base::ListValue args;
args.Append(extra_params_.release());
Emit("did-attach", args);
}
int WebContents::GetGuestInstanceID() const {
return guest_instance_id_;
}
void WebContents::ElementSizeChanged(const gfx::Size& old_size,
const gfx::Size& new_size) {
element_size_ = new_size;
}
void WebContents::GuestSizeChanged(const gfx::Size& old_size,
const gfx::Size& new_size) {
if (!auto_size_enabled_)
return;
guest_size_ = new_size;
GuestSizeChangedDueToAutoSize(old_size, new_size);
}
void WebContents::RequestPointerLockPermission(
bool user_gesture,
bool last_unlocked_by_target,
const base::Callback<void(bool enabled)>& callback) {
callback.Run(true);
}
void WebContents::RegisterDestructionCallback(
const DestructionCallback& callback) {
destruction_callback_ = callback;
}
void WebContents::Destroy() {
if (storage_) {
if (!destruction_callback_.is_null())
destruction_callback_.Run();
Observe(nullptr);
storage_.reset();
}
}
bool WebContents::IsAlive() const { bool WebContents::IsAlive() const {
return web_contents() != NULL; return web_contents() != NULL;
} }
@ -155,6 +356,14 @@ bool WebContents::IsCrashed() const {
return web_contents()->IsCrashed(); return web_contents()->IsCrashed();
} }
void WebContents::SetUserAgent(const std::string& user_agent) {
web_contents()->SetUserAgentOverride(user_agent);
}
void WebContents::InsertCSS(const std::string& css) {
web_contents()->InsertCSS(css);
}
void WebContents::ExecuteJavaScript(const base::string16& code) { void WebContents::ExecuteJavaScript(const base::string16& code) {
web_contents()->GetMainFrame()->ExecuteJavaScript(code); web_contents()->GetMainFrame()->ExecuteJavaScript(code);
} }
@ -164,36 +373,84 @@ bool WebContents::SendIPCMessage(const base::string16& channel,
return Send(new AtomViewMsg_Message(routing_id(), channel, args)); return Send(new AtomViewMsg_Message(routing_id(), channel, args));
} }
void WebContents::SetAutoSize(bool enabled,
const gfx::Size& min_size,
const gfx::Size& max_size) {
min_auto_size_ = min_size;
min_auto_size_.SetToMin(max_size);
max_auto_size_ = max_size;
max_auto_size_.SetToMax(min_size);
enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty();
if (!enabled && !auto_size_enabled_)
return;
auto_size_enabled_ = enabled;
if (!attached())
return;
content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
if (auto_size_enabled_) {
rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
} else {
rvh->DisableAutoResize(element_size_);
guest_size_ = element_size_;
GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
}
}
void WebContents::SetAllowTransparency(bool allow) {
if (guest_opaque_ != allow)
return;
guest_opaque_ = !allow;
if (!web_contents()->GetRenderViewHost()->GetView())
return;
web_contents()->GetRenderViewHost()->GetView()->SetBackgroundOpaque(!allow);
}
mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
v8::Isolate* isolate) { v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate) if (template_.IsEmpty())
.SetMethod("isAlive", &WebContents::IsAlive) template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate)
.SetMethod("loadUrl", &WebContents::LoadURL) .SetMethod("destroy", &WebContents::Destroy)
.SetMethod("getUrl", &WebContents::GetURL) .SetMethod("isAlive", &WebContents::IsAlive)
.SetMethod("getTitle", &WebContents::GetTitle) .SetMethod("loadUrl", &WebContents::LoadURL)
.SetMethod("isLoading", &WebContents::IsLoading) .SetMethod("getUrl", &WebContents::GetURL)
.SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) .SetMethod("getTitle", &WebContents::GetTitle)
.SetMethod("stop", &WebContents::Stop) .SetMethod("isLoading", &WebContents::IsLoading)
.SetMethod("reload", &WebContents::Reload) .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
.SetMethod("reloadIgnoringCache", &WebContents::ReloadIgnoringCache) .SetMethod("stop", &WebContents::Stop)
.SetMethod("canGoBack", &WebContents::CanGoBack) .SetMethod("reload", &WebContents::Reload)
.SetMethod("canGoForward", &WebContents::CanGoForward) .SetMethod("reloadIgnoringCache", &WebContents::ReloadIgnoringCache)
.SetMethod("canGoToOffset", &WebContents::CanGoToOffset) .SetMethod("canGoBack", &WebContents::CanGoBack)
.SetMethod("goBack", &WebContents::GoBack) .SetMethod("canGoForward", &WebContents::CanGoForward)
.SetMethod("goForward", &WebContents::GoForward) .SetMethod("canGoToOffset", &WebContents::CanGoToOffset)
.SetMethod("goToIndex", &WebContents::GoToIndex) .SetMethod("goBack", &WebContents::GoBack)
.SetMethod("goToOffset", &WebContents::GoToOffset) .SetMethod("goForward", &WebContents::GoForward)
.SetMethod("getRoutingId", &WebContents::GetRoutingID) .SetMethod("goToIndex", &WebContents::GoToIndex)
.SetMethod("getProcessId", &WebContents::GetProcessID) .SetMethod("goToOffset", &WebContents::GoToOffset)
.SetMethod("isCrashed", &WebContents::IsCrashed) .SetMethod("getRoutingId", &WebContents::GetRoutingID)
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) .SetMethod("getProcessId", &WebContents::GetProcessID)
.SetMethod("_send", &WebContents::SendIPCMessage); .SetMethod("isCrashed", &WebContents::IsCrashed)
.SetMethod("setUserAgent", &WebContents::SetUserAgent)
.SetMethod("insertCSS", &WebContents::InsertCSS)
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript)
.SetMethod("_send", &WebContents::SendIPCMessage)
.SetMethod("setAutoSize", &WebContents::SetAutoSize)
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
.Build());
return mate::ObjectTemplateBuilder(
isolate, v8::Local<v8::ObjectTemplate>::New(isolate, template_));
} }
void WebContents::OnRendererMessage(const base::string16& channel, void WebContents::OnRendererMessage(const base::string16& channel,
const base::ListValue& args) { const base::ListValue& args) {
// webContents.emit(channel, new Event(), args...); // webContents.emit(channel, new Event(), args...);
Emit(base::UTF16ToUTF8(channel), args, web_contents(), NULL); Emit(base::UTF16ToUTF8(channel), args);
} }
void WebContents::OnRendererMessageSync(const base::string16& channel, void WebContents::OnRendererMessageSync(const base::string16& channel,
@ -203,12 +460,42 @@ void WebContents::OnRendererMessageSync(const base::string16& channel,
Emit(base::UTF16ToUTF8(channel), args, web_contents(), message); Emit(base::UTF16ToUTF8(channel), args, web_contents(), message);
} }
void WebContents::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
const gfx::Size& new_size) {
base::ListValue args;
args.AppendInteger(old_size.width());
args.AppendInteger(old_size.height());
args.AppendInteger(new_size.width());
args.AppendInteger(new_size.height());
Emit("size-changed", args);
}
// static // static
mate::Handle<WebContents> WebContents::Create( mate::Handle<WebContents> WebContents::CreateFrom(
v8::Isolate* isolate, content::WebContents* web_contents) { v8::Isolate* isolate, content::WebContents* web_contents) {
return mate::CreateHandle(isolate, new WebContents(web_contents)); return mate::CreateHandle(isolate, new WebContents(web_contents));
} }
// static
mate::Handle<WebContents> WebContents::Create(
v8::Isolate* isolate, const mate::Dictionary& options) {
return mate::CreateHandle(isolate, new WebContents(options));
}
} // namespace api } // namespace api
} // namespace atom } // namespace atom
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("create", &atom::api::WebContents::Create);
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_contents, Initialize)

View file

@ -5,20 +5,36 @@
#ifndef ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ #ifndef ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_
#define ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ #define ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_
#include <string>
#include "atom/browser/api/event_emitter.h" #include "atom/browser/api/event_emitter.h"
#include "content/public/browser/browser_plugin_guest_delegate.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "native_mate/handle.h" #include "native_mate/handle.h"
namespace mate {
class Dictionary;
}
namespace atom { namespace atom {
namespace api { namespace api {
class WebContents : public mate::EventEmitter, class WebContents : public mate::EventEmitter,
public content::BrowserPluginGuestDelegate,
public content::WebContentsDelegate,
public content::WebContentsObserver { public content::WebContentsObserver {
public: public:
static mate::Handle<WebContents> Create(v8::Isolate* isolate, // Create from an existing WebContents.
content::WebContents* web_contents); static mate::Handle<WebContents> CreateFrom(
v8::Isolate* isolate, content::WebContents* web_contents);
// Create a new WebContents.
static mate::Handle<WebContents> Create(
v8::Isolate* isolate, const mate::Dictionary& options);
void Destroy();
bool IsAlive() const; bool IsAlive() const;
void LoadURL(const GURL& url); void LoadURL(const GURL& url);
GURL GetURL() const; GURL GetURL() const;
@ -38,28 +54,98 @@ class WebContents : public mate::EventEmitter,
int GetRoutingID() const; int GetRoutingID() const;
int GetProcessID() const; int GetProcessID() const;
bool IsCrashed() const; bool IsCrashed() const;
void SetUserAgent(const std::string& user_agent);
void InsertCSS(const std::string& css);
void ExecuteJavaScript(const base::string16& code); void ExecuteJavaScript(const base::string16& code);
bool SendIPCMessage(const base::string16& channel, bool SendIPCMessage(const base::string16& channel,
const base::ListValue& args); const base::ListValue& args);
// Toggles autosize mode for corresponding <webview>.
void SetAutoSize(bool enabled,
const gfx::Size& min_size,
const gfx::Size& max_size);
// Sets the transparency of the guest.
void SetAllowTransparency(bool allow);
// Returns whether this is a guest view.
bool is_guest() const { return guest_instance_id_ != -1; }
// Returns whether this guest has an associated embedder.
bool attached() const { return !!embedder_web_contents_; }
content::WebContents* web_contents() const {
return content::WebContentsObserver::web_contents();
}
protected: protected:
explicit WebContents(content::WebContents* web_contents); explicit WebContents(content::WebContents* web_contents);
explicit WebContents(const mate::Dictionary& options);
~WebContents();
// mate::Wrappable implementations: // mate::Wrappable:
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) OVERRIDE; v8::Isolate* isolate) override;
// content::WebContentsObserver implementations: // content::WebContentsDelegate:
virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE; bool AddMessageToConsole(content::WebContents* source,
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; int32 level,
const base::string16& message,
int32 line_no,
const base::string16& source_id) override;
bool ShouldCreateWebContents(
content::WebContents* web_contents,
int route_id,
WindowContainerType window_container_type,
const base::string16& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) override;
void CloseContents(content::WebContents* source) override;
content::WebContents* OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) override;
void HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;
// content::WebContentsObserver:
virtual void RenderViewDeleted(content::RenderViewHost*) override;
virtual void RenderProcessGone(base::TerminationStatus status) override;
virtual void DidFinishLoad(content::RenderFrameHost* render_frame_host, virtual void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) OVERRIDE; const GURL& validated_url) override;
virtual void DidFailLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) override;
virtual void DidStartLoading( virtual void DidStartLoading(
content::RenderViewHost* render_view_host) OVERRIDE; content::RenderViewHost* render_view_host) override;
virtual void DidStopLoading( virtual void DidStopLoading(
content::RenderViewHost* render_view_host) OVERRIDE; content::RenderViewHost* render_view_host) override;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void DidGetRedirectForResourceRequest(
virtual void WebContentsDestroyed() OVERRIDE; content::RenderViewHost* render_view_host,
const content::ResourceRedirectDetails& details) override;
virtual bool OnMessageReceived(const IPC::Message& message) override;
virtual void RenderViewReady() override;
virtual void WebContentsDestroyed() override;
// content::BrowserPluginGuestDelegate:
virtual void WillAttach(content::WebContents* embedder_web_contents,
const base::DictionaryValue& extra_params) override;
virtual content::WebContents* CreateNewGuestWindow(
const content::WebContents::CreateParams& create_params) override;
virtual void DidAttach() override;
virtual int GetGuestInstanceID() const override;
virtual void ElementSizeChanged(const gfx::Size& old_size,
const gfx::Size& new_size) override;
virtual void GuestSizeChanged(const gfx::Size& old_size,
const gfx::Size& new_size) override;
virtual void RequestPointerLockPermission(
bool user_gesture,
bool last_unlocked_by_target,
const base::Callback<void(bool enabled)>& callback) override;
virtual void RegisterDestructionCallback(
const DestructionCallback& callback) override;
private: private:
// Called when received a message from renderer. // Called when received a message from renderer.
@ -71,7 +157,44 @@ class WebContents : public mate::EventEmitter,
const base::ListValue& args, const base::ListValue& args,
IPC::Message* message); IPC::Message* message);
content::WebContents* web_contents_; // Weak. void GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
const gfx::Size& new_size);
// Unique ID for a guest WebContents.
int guest_instance_id_;
DestructionCallback destruction_callback_;
// Stores whether the contents of the guest can be transparent.
bool guest_opaque_;
// The extra parameters associated with this guest view passed
// in from JavaScript. This will typically be the view instance ID,
// the API to use, and view-specific parameters. These parameters
// are passed along to new guests that are created from this guest.
scoped_ptr<base::DictionaryValue> extra_params_;
// Stores the WebContents that managed by this class.
scoped_ptr<content::WebContents> storage_;
// The WebContents that attaches this guest view.
content::WebContents* embedder_web_contents_;
// The size of the container element.
gfx::Size element_size_;
// The size of the guest content. Note: In autosize mode, the container
// element may not match the size of the guest.
gfx::Size guest_size_;
// Indicates whether autosize mode is enabled or not.
bool auto_size_enabled_;
// The maximum size constraints of the container element in autosize mode.
gfx::Size max_auto_size_;
// The minimum size constraints of the container element in autosize mode.
gfx::Size min_auto_size_;
DISALLOW_COPY_AND_ASSIGN(WebContents); DISALLOW_COPY_AND_ASSIGN(WebContents);
}; };

View file

@ -6,13 +6,11 @@
#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "native_mate/callback.h" #include "native_mate/callback.h"
#include "native_mate/constructor.h" #include "native_mate/constructor.h"
#include "native_mate/dictionary.h" #include "native_mate/dictionary.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
@ -40,23 +38,6 @@ struct Converter<PrintSettings> {
} }
}; };
template<>
struct Converter<gfx::Rect> {
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Rect* out) {
if (!val->IsObject())
return false;
mate::Dictionary dict(isolate, val->ToObject());
int x, y, width, height;
if (!dict.Get("x", &x) || !dict.Get("y", &y) ||
!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Rect(x, y, width, height);
return true;
}
};
} // namespace mate } // namespace mate
namespace atom { namespace atom {
@ -375,12 +356,12 @@ void Window::SetProgressBar(double progress) {
} }
mate::Handle<WebContents> Window::GetWebContents(v8::Isolate* isolate) const { mate::Handle<WebContents> Window::GetWebContents(v8::Isolate* isolate) const {
return WebContents::Create(isolate, window_->GetWebContents()); return WebContents::CreateFrom(isolate, window_->GetWebContents());
} }
mate::Handle<WebContents> Window::GetDevToolsWebContents( mate::Handle<WebContents> Window::GetDevToolsWebContents(
v8::Isolate* isolate) const { v8::Isolate* isolate) const {
return WebContents::Create(isolate, window_->GetDevToolsWebContents()); return WebContents::CreateFrom(isolate, window_->GetDevToolsWebContents());
} }
// static // static

View file

@ -4,8 +4,6 @@
#include "atom/browser/api/event_emitter.h" #include "atom/browser/api/event_emitter.h"
#include <vector>
#include "atom/browser/api/event.h" #include "atom/browser/api/event.h"
#include "atom/common/native_mate_converters/v8_value_converter.h" #include "atom/common/native_mate_converters/v8_value_converter.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
@ -64,6 +62,23 @@ bool EventEmitter::Emit(const base::StringPiece& name,
v8::Handle<v8::Context> context = isolate->GetCurrentContext(); v8::Handle<v8::Context> context = isolate->GetCurrentContext();
scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter); scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter);
// v8_args = [args...];
Arguments v8_args;
v8_args.reserve(args.GetSize());
for (size_t i = 0; i < args.GetSize(); i++) {
const base::Value* value(NULL);
if (args.Get(i, &value))
v8_args.push_back(converter->ToV8Value(value, context));
}
return Emit(isolate, name, v8_args, sender, message);
}
bool EventEmitter::Emit(v8::Isolate* isolate,
const base::StringPiece& name,
Arguments args,
content::WebContents* sender,
IPC::Message* message) {
v8::Handle<v8::Object> event; v8::Handle<v8::Object> event;
bool use_native_event = sender && message; bool use_native_event = sender && message;
@ -75,20 +90,13 @@ bool EventEmitter::Emit(const base::StringPiece& name,
event = CreateEventObject(isolate); event = CreateEventObject(isolate);
} }
// v8_args = [name, event, args...]; // args = [name, event, args...];
std::vector<v8::Handle<v8::Value>> v8_args; args.insert(args.begin(), event);
v8_args.reserve(args.GetSize() + 2); args.insert(args.begin(), mate::StringToV8(isolate, name));
v8_args.push_back(mate::StringToV8(isolate, name));
v8_args.push_back(event);
for (size_t i = 0; i < args.GetSize(); i++) {
const base::Value* value(NULL);
if (args.Get(i, &value))
v8_args.push_back(converter->ToV8Value(value, context));
}
// this.emit.apply(this, v8_args); // this.emit.apply(this, args);
node::MakeCallback(isolate, GetWrapper(isolate), "emit", v8_args.size(), node::MakeCallback(isolate, GetWrapper(isolate), "emit", args.size(),
&v8_args[0]); &args[0]);
if (use_native_event) { if (use_native_event) {
Handle<Event> native_event; Handle<Event> native_event;

View file

@ -5,6 +5,8 @@
#ifndef ATOM_BROWSER_API_EVENT_EMITTER_H_ #ifndef ATOM_BROWSER_API_EVENT_EMITTER_H_
#define ATOM_BROWSER_API_EVENT_EMITTER_H_ #define ATOM_BROWSER_API_EVENT_EMITTER_H_
#include <vector>
#include "native_mate/wrappable.h" #include "native_mate/wrappable.h"
namespace base { namespace base {
@ -23,6 +25,9 @@ namespace mate {
// Provide helperers to emit event in JavaScript. // Provide helperers to emit event in JavaScript.
class EventEmitter : public Wrappable { class EventEmitter : public Wrappable {
public:
typedef std::vector<v8::Handle<v8::Value>> Arguments;
protected: protected:
EventEmitter(); EventEmitter();
@ -36,6 +41,13 @@ class EventEmitter : public Wrappable {
bool Emit(const base::StringPiece& name, const base::ListValue& args, bool Emit(const base::StringPiece& name, const base::ListValue& args,
content::WebContents* sender, IPC::Message* message); content::WebContents* sender, IPC::Message* message);
// Lower level implementations.
bool Emit(v8::Isolate* isolate,
const base::StringPiece& name,
Arguments args,
content::WebContents* sender = nullptr,
IPC::Message* message = nullptr);
private: private:
DISALLOW_COPY_AND_ASSIGN(EventEmitter); DISALLOW_COPY_AND_ASSIGN(EventEmitter);
}; };

View file

@ -1,4 +1,5 @@
EventEmitter = require('events').EventEmitter EventEmitter = require('events').EventEmitter
binding = process.atomBinding 'web_contents'
ipc = require 'ipc' ipc = require 'ipc'
module.exports.wrap = (webContents) -> module.exports.wrap = (webContents) ->
@ -31,12 +32,15 @@ module.exports.wrap = (webContents) ->
process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', "#{processId}-#{routingId}" process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', "#{processId}-#{routingId}"
# Dispatch IPC messages to the ipc module. # Dispatch IPC messages to the ipc module.
webContents.on 'ipc-message', (event, channel, args...) => webContents.on 'ipc-message', (event, channel, args...) ->
Object.defineProperty event, 'sender', value: webContents Object.defineProperty event, 'sender', value: webContents
ipc.emit channel, event, args... ipc.emit channel, event, args...
webContents.on 'ipc-message-sync', (event, channel, args...) => webContents.on 'ipc-message-sync', (event, channel, args...) ->
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value) Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
Object.defineProperty event, 'sender', value: webContents Object.defineProperty event, 'sender', value: webContents
ipc.emit channel, event, args... ipc.emit channel, event, args...
webContents webContents
module.exports.create = (options={}) ->
@wrap binding.create(options)

View file

@ -7,15 +7,17 @@
#include "atom/browser/atom_access_token_store.h" #include "atom/browser/atom_access_token_store.h"
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
#include "atom/browser/atom_speech_recognition_manager_delegate.h" #include "atom/browser/atom_speech_recognition_manager_delegate.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/browser/web_view/web_view_renderer_state.h"
#include "atom/browser/window_list.h" #include "atom/browser/window_list.h"
#include "atom/common/options_switches.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/printing/printing_message_filter.h" #include "chrome/browser/printing/printing_message_filter.h"
#include "chrome/browser/speech/tts_message_filter.h" #include "chrome/browser/speech/tts_message_filter.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/site_instance.h" #include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/web_preferences.h" #include "content/public/common/web_preferences.h"
@ -58,12 +60,6 @@ void AtomBrowserClient::RenderProcessWillLaunch(
host->AddFilter(new TtsMessageFilter(id, host->GetBrowserContext())); host->AddFilter(new TtsMessageFilter(id, host->GetBrowserContext()));
} }
void AtomBrowserClient::ResourceDispatcherHostCreated() {
resource_dispatcher_delegate_.reset(new AtomResourceDispatcherHostDelegate);
content::ResourceDispatcherHost::Get()->SetDelegate(
resource_dispatcher_delegate_.get());
}
content::SpeechRecognitionManagerDelegate* content::SpeechRecognitionManagerDelegate*
AtomBrowserClient::GetSpeechRecognitionManagerDelegate() { AtomBrowserClient::GetSpeechRecognitionManagerDelegate() {
return new AtomSpeechRecognitionManagerDelegate; return new AtomSpeechRecognitionManagerDelegate;
@ -80,7 +76,7 @@ void AtomBrowserClient::OverrideWebkitPrefs(
prefs->javascript_enabled = true; prefs->javascript_enabled = true;
prefs->web_security_enabled = true; prefs->web_security_enabled = true;
prefs->javascript_can_open_windows_automatically = true; prefs->javascript_can_open_windows_automatically = true;
prefs->plugins_enabled = false; prefs->plugins_enabled = true;
prefs->dom_paste_enabled = true; prefs->dom_paste_enabled = true;
prefs->java_enabled = false; prefs->java_enabled = false;
prefs->allow_scripts_to_close_windows = true; prefs->allow_scripts_to_close_windows = true;
@ -146,8 +142,20 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
window = *iter; window = *iter;
} }
if (window != NULL) if (window != NULL) {
window->AppendExtraCommandLineSwitches(command_line, child_process_id); window->AppendExtraCommandLineSwitches(command_line, child_process_id);
} else {
// Append commnad line arguments for guest web view.
WebViewRendererState::WebViewInfo info;
if (WebViewRendererState::GetInstance()->GetInfo(child_process_id, &info)) {
command_line->AppendSwitchASCII(
switches::kGuestInstanceID,
base::IntToString(info.guest_instance_id));
command_line->AppendSwitchASCII(
switches::kNodeIntegration,
info.node_integration ? "true" : "false");
}
}
dying_render_process_ = NULL; dying_render_process_ = NULL;
} }

View file

@ -11,8 +11,6 @@
namespace atom { namespace atom {
class AtomResourceDispatcherHostDelegate;
class AtomBrowserClient : public brightray::BrowserClient { class AtomBrowserClient : public brightray::BrowserClient {
public: public:
AtomBrowserClient(); AtomBrowserClient();
@ -22,7 +20,6 @@ class AtomBrowserClient : public brightray::BrowserClient {
// content::ContentBrowserClient: // content::ContentBrowserClient:
virtual void RenderProcessWillLaunch( virtual void RenderProcessWillLaunch(
content::RenderProcessHost* host) OVERRIDE; content::RenderProcessHost* host) OVERRIDE;
virtual void ResourceDispatcherHostCreated() OVERRIDE;
virtual content::SpeechRecognitionManagerDelegate* virtual content::SpeechRecognitionManagerDelegate*
GetSpeechRecognitionManagerDelegate() override; GetSpeechRecognitionManagerDelegate() override;
virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE; virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE;
@ -41,8 +38,6 @@ class AtomBrowserClient : public brightray::BrowserClient {
virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts( virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
const content::MainFunctionParams&) OVERRIDE; const content::MainFunctionParams&) OVERRIDE;
scoped_ptr<AtomResourceDispatcherHostDelegate> resource_dispatcher_delegate_;
// The render process which would be swapped out soon. // The render process which would be swapped out soon.
content::RenderProcessHost* dying_render_process_; content::RenderProcessHost* dying_render_process_;

View file

@ -7,6 +7,7 @@
#include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/atom_url_request_job_factory.h"
#include "atom/browser/net/asar/asar_protocol_handler.h" #include "atom/browser/net/asar/asar_protocol_handler.h"
#include "atom/browser/web_view/web_view_manager.h"
#include "base/threading/sequenced_worker_pool.h" #include "base/threading/sequenced_worker_pool.h"
#include "base/threading/worker_pool.h" #include "base/threading/worker_pool.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
@ -68,6 +69,12 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
return top_job_factory.release(); return top_job_factory.release();
} }
content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() {
if (!guest_manager_)
guest_manager_.reset(new WebViewManager(this));
return guest_manager_.get();
}
// static // static
AtomBrowserContext* AtomBrowserContext::Get() { AtomBrowserContext* AtomBrowserContext::Get() {
return static_cast<AtomBrowserContext*>( return static_cast<AtomBrowserContext*>(

View file

@ -12,6 +12,7 @@ class BrowserProcess;
namespace atom { namespace atom {
class AtomURLRequestJobFactory; class AtomURLRequestJobFactory;
class WebViewManager;
class AtomBrowserContext : public brightray::BrowserContext { class AtomBrowserContext : public brightray::BrowserContext {
public: public:
@ -21,17 +22,20 @@ class AtomBrowserContext : public brightray::BrowserContext {
// Returns the browser context singleton. // Returns the browser context singleton.
static AtomBrowserContext* Get(); static AtomBrowserContext* Get();
AtomURLRequestJobFactory* job_factory() const { return job_factory_; }
protected:
// brightray::URLRequestContextGetter::Delegate: // brightray::URLRequestContextGetter::Delegate:
virtual net::URLRequestJobFactory* CreateURLRequestJobFactory( virtual net::URLRequestJobFactory* CreateURLRequestJobFactory(
content::ProtocolHandlerMap* handlers, content::ProtocolHandlerMap* handlers,
content::URLRequestInterceptorScopedVector* interceptors) OVERRIDE; content::URLRequestInterceptorScopedVector* interceptors) override;
// content::BrowserContext:
virtual content::BrowserPluginGuestManager* GetGuestManager() override;
AtomURLRequestJobFactory* job_factory() const { return job_factory_; }
private: private:
// A fake BrowserProcess object that used to feed the source code from chrome. // A fake BrowserProcess object that used to feed the source code from chrome.
scoped_ptr<BrowserProcess> fake_browser_process_; scoped_ptr<BrowserProcess> fake_browser_process_;
scoped_ptr<WebViewManager> guest_manager_;
AtomURLRequestJobFactory* job_factory_; // Weak reference. AtomURLRequestJobFactory* job_factory_; // Weak reference.

View file

@ -1,50 +0,0 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
#include <string>
#include "base/logging.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
namespace atom {
namespace {
const char* kDisableXFrameOptions = "disable-x-frame-options";
} // namespace
AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
}
AtomResourceDispatcherHostDelegate::~AtomResourceDispatcherHostDelegate() {
}
void AtomResourceDispatcherHostDelegate::OnResponseStarted(
net::URLRequest* request,
content::ResourceContext* resource_context,
content::ResourceResponse* response,
IPC::Sender* sender) {
// Check if frame's name contains "disable-x-frame-options"
int p, f;
if (!content::ResourceRequestInfo::GetRenderFrameForRequest(request, &p, &f))
return;
content::RenderFrameHost* frame = content::RenderFrameHost::FromID(p, f);
if (!frame)
return;
if (frame->GetFrameName().find(kDisableXFrameOptions) == std::string::npos)
return;
// Remove the "X-Frame-Options" from response headers.
net::HttpResponseHeaders* response_headers = request->response_headers();
if (response_headers && response_headers->HasHeader("x-frame-options"))
response_headers->RemoveHeader("x-frame-options");
}
} // namespace atom

View file

@ -1,31 +0,0 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
#define ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
#include "base/compiler_specific.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h"
namespace atom {
class AtomResourceDispatcherHostDelegate
: public content::ResourceDispatcherHostDelegate {
public:
AtomResourceDispatcherHostDelegate();
virtual ~AtomResourceDispatcherHostDelegate();
// content::ResourceDispatcherHostDelegate:
virtual void OnResponseStarted(net::URLRequest* request,
content::ResourceContext* resource_context,
content::ResourceResponse* response,
IPC::Sender* sender) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(AtomResourceDispatcherHostDelegate);
};
} // namespace atom
#endif // ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_

View file

@ -0,0 +1,89 @@
ipc = require 'ipc'
webContents = require 'web-contents'
webViewManager = null # Doesn't exist in early initialization.
supportedWebViewEvents = [
'did-finish-load'
'did-fail-load'
'did-frame-finish-load'
'did-start-loading'
'did-stop-loading'
'did-get-redirect-request'
'console-message'
'new-window'
'close'
'crashed'
'destroyed'
]
nextInstanceId = 0
guestInstances = {}
# Generate guestInstanceId.
getNextInstanceId = (webContents) ->
++nextInstanceId
# Create a new guest instance.
createGuest = (embedder, params) ->
webViewManager ?= process.atomBinding 'web_view_manager'
id = getNextInstanceId embedder
guest = webContents.create
isGuest: true
guestInstanceId: id
storagePartitionId: params.storagePartitionId
guestInstances[id] = {guest, embedder}
webViewManager.addGuest id, embedder, guest, params.nodeIntegration
# Destroy guest when the embedder is gone.
embedder.once 'render-view-deleted', ->
destroyGuest id if guestInstances[id]?
# Init guest web view after attached.
guest.once 'did-attach', (event, params) ->
@viewInstanceId = params.instanceId
min = width: params.minwidth, height: params.minheight
max = width: params.maxwidth, height: params.maxheight
@setAutoSize params.autosize, min, max
if params.src
@loadUrl params.src
if params.allowtransparency?
@setAllowTransparency params.allowtransparency
# Dispatch events to embedder.
for event in supportedWebViewEvents
do (event) ->
guest.on event, (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args...
# Autosize.
guest.on 'size-changed', (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED", args...
id
# Destroy an existing guest instance.
destroyGuest = (id) ->
webViewManager.removeGuest id
guestInstances[id].guest.destroy()
delete guestInstances[id]
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, type, params, requestId) ->
event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
destroyGuest id
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_AUTO_SIZE', (event, id, params) ->
guestInstances[id]?.guest.setAutoSize params.enableAutoSize, params.min, params.max
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
guestInstances[id]?.guest.setAllowTransparency allowtransparency
# Returns WebContents from its guest id.
exports.getGuest = (id) ->
guestInstances[id]?.guest
# Returns the embedder of the guest.
exports.getEmbedder = (id) ->
guestInstances[id]?.embedder

View file

@ -23,9 +23,8 @@ process.argv.splice startMark, endMark - startMark + 1
globalPaths = module.globalPaths globalPaths = module.globalPaths
globalPaths.push path.join process.resourcesPath, 'atom', 'browser', 'api', 'lib' globalPaths.push path.join process.resourcesPath, 'atom', 'browser', 'api', 'lib'
# Do loading in next tick since we still need some initialize work before # Following operations need extra bindings by AtomBindings.
# native bindings can work. process.once 'BIND_DONE', ->
setImmediate ->
# Import common settings. # Import common settings.
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
@ -61,6 +60,9 @@ setImmediate ->
# Load the RPC server. # Load the RPC server.
require './rpc-server.js' require './rpc-server.js'
# Load the guest view manager.
require './guest-view-manager.js'
# Now we try to load app's package.json. # Now we try to load app's package.json.
packageJson = null packageJson = null

View file

@ -94,11 +94,15 @@ ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
catch e catch e
event.returnValue = errorToMeta e event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) -> ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, guestInstanceId) ->
try try
BrowserWindow = require 'browser-window' BrowserWindow = require 'browser-window'
window = BrowserWindow.fromWebContents event.sender if guestInstanceId?
window = BrowserWindow.fromDevToolsWebContents event.sender unless window? guestViewManager = require './guest-view-manager'
window = BrowserWindow.fromWebContents guestViewManager.getEmbedder(guestInstanceId)
else
window = BrowserWindow.fromWebContents event.sender
window = BrowserWindow.fromDevToolsWebContents event.sender unless window?
event.returnValue = valueToMeta event.sender, window event.returnValue = valueToMeta event.sender, window
catch e catch e
event.returnValue = errorToMeta e event.returnValue = errorToMeta e
@ -157,3 +161,10 @@ ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, storeId) -> ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, storeId) ->
objectsRegistry.remove event.sender.getId(), storeId objectsRegistry.remove event.sender.getId(), storeId
ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
try
guestViewManager = require './guest-view-manager'
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
catch e
event.returnValue = errorToMeta e

View file

@ -84,7 +84,7 @@ NativeWindow::NativeWindow(content::WebContents* web_contents,
has_frame_(true), has_frame_(true),
enable_larger_than_screen_(false), enable_larger_than_screen_(false),
is_closed_(false), is_closed_(false),
node_integration_("except-iframe"), node_integration_(true),
has_dialog_attached_(false), has_dialog_attached_(false),
zoom_factor_(1.0), zoom_factor_(1.0),
weak_factory_(this), weak_factory_(this),
@ -94,12 +94,16 @@ NativeWindow::NativeWindow(content::WebContents* web_contents,
options.Get(switches::kFrame, &has_frame_); options.Get(switches::kFrame, &has_frame_);
options.Get(switches::kEnableLargerThanScreen, &enable_larger_than_screen_); options.Get(switches::kEnableLargerThanScreen, &enable_larger_than_screen_);
options.Get(switches::kNodeIntegration, &node_integration_);
// Read icon before window is created. // Read icon before window is created.
options.Get(switches::kIcon, &icon_); options.Get(switches::kIcon, &icon_);
// Read iframe security before any navigation. // Be compatible with old API of "node-integration" option.
options.Get(switches::kNodeIntegration, &node_integration_); std::string old_string_token;
if (options.Get(switches::kNodeIntegration, &old_string_token) &&
old_string_token != "disable")
node_integration_ = true;
// Read the web preferences. // Read the web preferences.
options.Get(switches::kWebPreferences, &web_preferences_); options.Get(switches::kWebPreferences, &web_preferences_);
@ -341,7 +345,7 @@ void NativeWindow::AppendExtraCommandLineSwitches(
base::CommandLine* command_line, int child_process_id) { base::CommandLine* command_line, int child_process_id) {
// Append --node-integration to renderer process. // Append --node-integration to renderer process.
command_line->AppendSwitchASCII(switches::kNodeIntegration, command_line->AppendSwitchASCII(switches::kNodeIntegration,
node_integration_); node_integration_ ? "true" : "false");
// Append --zoom-factor. // Append --zoom-factor.
if (zoom_factor_ != 1.0) if (zoom_factor_ != 1.0)
@ -358,6 +362,10 @@ void NativeWindow::AppendExtraCommandLineSwitches(
command_line->AppendSwitch(::switches::kDisableDirectWrite); command_line->AppendSwitch(::switches::kDisableDirectWrite);
#endif #endif
// Check if plugins are enabled.
if (web_preferences_.Get("plugins", &b) && b)
command_line->AppendSwitch(switches::kEnablePlugins);
// This set of options are not availabe in WebPreferences, so we have to pass // This set of options are not availabe in WebPreferences, so we have to pass
// them via command line and enable them in renderer procss. // them via command line and enable them in renderer procss.
for (size_t i = 0; i < arraysize(kWebRuntimeFeatures); ++i) { for (size_t i = 0; i < arraysize(kWebRuntimeFeatures); ++i) {
@ -388,8 +396,6 @@ void NativeWindow::OverrideWebkitPrefs(const GURL& url,
prefs->experimental_webgl_enabled = b; prefs->experimental_webgl_enabled = b;
if (web_preferences_.Get("webaudio", &b)) if (web_preferences_.Get("webaudio", &b))
prefs->webaudio_enabled = b; prefs->webaudio_enabled = b;
if (web_preferences_.Get("plugins", &b))
prefs->plugins_enabled = b;
if (web_preferences_.Get("extra-plugin-dirs", &list)) if (web_preferences_.Get("extra-plugin-dirs", &list))
for (size_t i = 0; i < list.size(); ++i) for (size_t i = 0; i < list.size(); ++i)
content::PluginService::GetInstance()->AddExtraPluginDir(list[i]); content::PluginService::GetInstance()->AddExtraPluginDir(list[i]);

View file

@ -288,8 +288,8 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
// The windows has been closed. // The windows has been closed.
bool is_closed_; bool is_closed_;
// The security token of iframe. // Whether node integration is enabled.
std::string node_integration_; bool node_integration_;
// There is a dialog that has been attached to window. // There is a dialog that has been attached to window.
bool has_dialog_attached_; bool has_dialog_attached_;

View file

@ -0,0 +1,112 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/web_view/web_view_manager.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/web_view/web_view_renderer_state.h"
#include "base/bind.h"
#include "base/stl_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<content::WebContents*> {
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
content::WebContents** out) {
atom::api::WebContents* contents;
if (!Converter<atom::api::WebContents*>::FromV8(isolate, val, &contents))
return false;
*out = contents->web_contents();
return true;
}
};
} // namespace mate
namespace atom {
WebViewManager::WebViewManager(content::BrowserContext* context) {
}
WebViewManager::~WebViewManager() {
}
void WebViewManager::AddGuest(int guest_instance_id,
content::WebContents* embedder,
content::WebContents* web_contents,
bool node_integration) {
web_contents_map_[guest_instance_id] = { web_contents, embedder };
WebViewRendererState::WebViewInfo web_view_info = {
guest_instance_id, node_integration
};
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&WebViewRendererState::AddGuest,
base::Unretained(WebViewRendererState::GetInstance()),
web_contents->GetRenderProcessHost()->GetID(),
web_view_info));
}
void WebViewManager::RemoveGuest(int guest_instance_id) {
auto web_contents = web_contents_map_[guest_instance_id].web_contents;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(
&WebViewRendererState::RemoveGuest,
base::Unretained(WebViewRendererState::GetInstance()),
web_contents->GetRenderProcessHost()->GetID()));
web_contents_map_.erase(guest_instance_id);
}
void WebViewManager::MaybeGetGuestByInstanceIDOrKill(
int guest_instance_id,
int embedder_render_process_id,
const GuestByInstanceIDCallback& callback) {
if (ContainsKey(web_contents_map_, guest_instance_id))
callback.Run(web_contents_map_[guest_instance_id].web_contents);
else
callback.Run(nullptr);
}
bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents,
const GuestCallback& callback) {
for (auto& item : web_contents_map_)
if (item.second.embedder == embedder_web_contents &&
callback.Run(item.second.web_contents))
return true;
return false;
}
} // namespace atom
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
using atom::WebViewManager;
auto manager = static_cast<WebViewManager*>(
atom::AtomBrowserContext::Get()->GetGuestManager());
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("addGuest",
base::Bind(&WebViewManager::AddGuest,
base::Unretained(manager)));
dict.SetMethod("removeGuest",
base::Bind(&WebViewManager::RemoveGuest,
base::Unretained(manager)));
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_view_manager, Initialize)

View file

@ -0,0 +1,50 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_
#define ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_
#include <map>
#include "content/public/browser/browser_plugin_guest_manager.h"
namespace content {
class BrowserContext;
}
namespace atom {
class WebViewManager : public content::BrowserPluginGuestManager {
public:
explicit WebViewManager(content::BrowserContext* context);
virtual ~WebViewManager();
void AddGuest(int guest_instance_id,
content::WebContents* embedder,
content::WebContents* web_contents,
bool node_integration);
void RemoveGuest(int guest_instance_id);
protected:
// content::BrowserPluginGuestManager:
virtual void MaybeGetGuestByInstanceIDOrKill(
int guest_instance_id,
int embedder_render_process_id,
const GuestByInstanceIDCallback& callback) override;
virtual bool ForEachGuest(content::WebContents* embedder_web_contents,
const GuestCallback& callback) override;
private:
struct WebContentsWithEmbedder {
content::WebContents* web_contents; // Weak ref.
content::WebContents* embedder;
};
std::map<int, WebContentsWithEmbedder> web_contents_map_;
DISALLOW_COPY_AND_ASSIGN(WebViewManager);
};
} // namespace atom
#endif // ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_

View file

@ -0,0 +1,46 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/web_view/web_view_renderer_state.h"
#include "content/public/browser/browser_thread.h"
namespace atom {
// static
WebViewRendererState* WebViewRendererState::GetInstance() {
return Singleton<WebViewRendererState>::get();
}
WebViewRendererState::WebViewRendererState() {
}
WebViewRendererState::~WebViewRendererState() {
}
bool WebViewRendererState::IsGuest(int render_process_id) {
return webview_info_map_.find(render_process_id) !=
webview_info_map_.end();
}
void WebViewRendererState::AddGuest(int guest_process_id,
const WebViewInfo& webview_info) {
webview_info_map_[guest_process_id] = webview_info;
}
void WebViewRendererState::RemoveGuest(int guest_process_id) {
webview_info_map_.erase(guest_process_id);
}
bool WebViewRendererState::GetInfo(int guest_process_id,
WebViewInfo* webview_info) {
WebViewInfoMap::iterator iter = webview_info_map_.find(guest_process_id);
if (iter != webview_info_map_.end()) {
*webview_info = iter->second;
return true;
}
return false;
}
} // namespace atom

View file

@ -0,0 +1,56 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_WEB_VIEW_WEB_VIEW_RENDERER_STATE_H_
#define ATOM_BROWSER_WEB_VIEW_WEB_VIEW_RENDERER_STATE_H_
#include <map>
#include <string>
#include <utility>
#include "base/memory/singleton.h"
namespace atom {
class WebViewManager;
// This class keeps track of <webview> renderer state for use on the IO thread.
// All methods should be called on the IO thread.
class WebViewRendererState {
public:
struct WebViewInfo {
int guest_instance_id;
bool node_integration;
};
static WebViewRendererState* GetInstance();
// Looks up the information for the embedder <webview> for a given render
// view, if one exists. Called on the IO thread.
bool GetInfo(int guest_process_id, WebViewInfo* webview_info);
// Returns true if the given renderer is used by webviews.
bool IsGuest(int render_process_id);
private:
friend class WebViewManager;
friend struct DefaultSingletonTraits<WebViewRendererState>;
typedef std::map<int, WebViewInfo> WebViewInfoMap;
WebViewRendererState();
~WebViewRendererState();
// Adds or removes a <webview> guest render process from the set.
void AddGuest(int render_process_id, const WebViewInfo& webview_info);
void RemoveGuest(int render_process_id);
WebViewInfoMap webview_info_map_;
DISALLOW_COPY_AND_ASSIGN(WebViewRendererState);
};
} // namespace atom
#endif // ATOM_BROWSER_WEB_VIEW_WEB_VIEW_RENDERER_STATE_H_

View file

@ -2,61 +2,9 @@
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "native_mate/dictionary.h" #include "atom/common/native_mate_converters/gfx_converter.h"
#include "ui/gfx/screen.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<gfx::Point> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Point& val) {
return mate::ObjectTemplateBuilder(isolate).SetValue("x", val.x())
.SetValue("y", val.y())
.Build()->NewInstance();
}
};
template<>
struct Converter<gfx::Size> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Size& val) {
return mate::ObjectTemplateBuilder(isolate).SetValue("width", val.width())
.SetValue("height", val.height())
.Build()->NewInstance();
}
};
template<>
struct Converter<gfx::Rect> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Rect& val) {
return mate::ObjectTemplateBuilder(isolate).SetValue("x", val.x())
.SetValue("y", val.y())
.SetValue("width", val.width())
.SetValue("height", val.height())
.Build()->NewInstance();
}
};
template<>
struct Converter<gfx::Display> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Display& display) {
return mate::ObjectTemplateBuilder(isolate)
.SetValue("bounds", display.bounds())
.SetValue("workArea", display.work_area())
.SetValue("size", display.size())
.SetValue("workAreaSize", display.work_area_size())
.SetValue("scaleFactor", display.device_scale_factor())
.Build()->NewInstance();
}
};
} // namespace mate
namespace { namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused, void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,

View file

@ -85,6 +85,9 @@ void AtomBindings::BindTo(v8::Isolate* isolate,
versions->Set(mate::StringToV8(isolate, "chrome"), versions->Set(mate::StringToV8(isolate, "chrome"),
mate::StringToV8(isolate, CHROME_VERSION_STRING)); mate::StringToV8(isolate, CHROME_VERSION_STRING));
} }
v8::Handle<v8::Value> event = mate::StringToV8(isolate, "BIND_DONE");
node::MakeCallback(isolate, process, "emit", 1, &event);
} }
void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) { void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) {

View file

@ -0,0 +1,101 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#include "native_mate/dictionary.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size.h"
namespace mate {
template<>
struct Converter<gfx::Point> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Point& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Point* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
*out = gfx::Point(x, y);
return true;
}
};
template<>
struct Converter<gfx::Size> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Size& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Size* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int width, height;
if (!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Size(width, height);
return true;
}
};
template<>
struct Converter<gfx::Rect> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Rect& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Rect* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y, width, height;
if (!dict.Get("x", &x) || !dict.Get("y", &y) ||
!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Rect(x, y, width, height);
return true;
}
};
template<>
struct Converter<gfx::Display> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Display& display) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("bounds", display.bounds());
dict.Set("workArea", display.work_area());
dict.Set("size", display.size());
dict.Set("workAreaSize", display.work_area_size());
dict.Set("scaleFactor", display.device_scale_factor());
return dict.GetHandle();
}
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_

View file

@ -68,6 +68,7 @@ REFERENCE_MODULE(atom_browser_power_monitor);
REFERENCE_MODULE(atom_browser_protocol); REFERENCE_MODULE(atom_browser_protocol);
REFERENCE_MODULE(atom_browser_global_shortcut); REFERENCE_MODULE(atom_browser_global_shortcut);
REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_tray);
REFERENCE_MODULE(atom_browser_web_contents);
REFERENCE_MODULE(atom_browser_window); REFERENCE_MODULE(atom_browser_window);
REFERENCE_MODULE(atom_common_asar); REFERENCE_MODULE(atom_common_asar);
REFERENCE_MODULE(atom_common_clipboard); REFERENCE_MODULE(atom_common_clipboard);
@ -77,7 +78,7 @@ REFERENCE_MODULE(atom_common_screen);
REFERENCE_MODULE(atom_common_shell); REFERENCE_MODULE(atom_common_shell);
REFERENCE_MODULE(atom_common_v8_util); REFERENCE_MODULE(atom_common_v8_util);
REFERENCE_MODULE(atom_renderer_ipc); REFERENCE_MODULE(atom_renderer_ipc);
REFERENCE_MODULE(atom_renderer_web_view); REFERENCE_MODULE(atom_renderer_web_frame);
#undef REFERENCE_MODULE #undef REFERENCE_MODULE
namespace atom { namespace atom {

View file

@ -60,6 +60,12 @@ const char kDarkTheme[] = "dark-theme";
// Enable DirectWrite on Windows. // Enable DirectWrite on Windows.
const char kDirectWrite[] = "direct-write"; const char kDirectWrite[] = "direct-write";
// Enable plugins.
const char kEnablePlugins[] = "enable-plugins";
// Instancd ID of guest WebContents.
const char kGuestInstanceID[] = "guest-instance-id";
// Web runtime features. // Web runtime features.
const char kExperimentalFeatures[] = "experimental-features"; const char kExperimentalFeatures[] = "experimental-features";
const char kExperimentalCanvasFeatures[] = "experimental-canvas-features"; const char kExperimentalCanvasFeatures[] = "experimental-canvas-features";

View file

@ -36,6 +36,8 @@ extern const char kAutoHideMenuBar[];
extern const char kEnableLargerThanScreen[]; extern const char kEnableLargerThanScreen[];
extern const char kDarkTheme[]; extern const char kDarkTheme[];
extern const char kDirectWrite[]; extern const char kDirectWrite[];
extern const char kEnablePlugins[];
extern const char kGuestInstanceID[];
extern const char kExperimentalFeatures[]; extern const char kExperimentalFeatures[];
extern const char kExperimentalCanvasFeatures[]; extern const char kExperimentalCanvasFeatures[];

View file

@ -0,0 +1,86 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/renderer/api/atom_api_web_frame.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
WebFrame::WebFrame()
: web_frame_(blink::WebLocalFrame::frameForCurrentContext()) {
}
WebFrame::~WebFrame() {
}
void WebFrame::SetName(const std::string& name) {
web_frame_->setName(blink::WebString::fromUTF8(name));
}
double WebFrame::SetZoomLevel(double level) {
return web_frame_->view()->setZoomLevel(level);
}
double WebFrame::GetZoomLevel() const {
return web_frame_->view()->zoomLevel();
}
double WebFrame::SetZoomFactor(double factor) {
return blink::WebView::zoomLevelToZoomFactor(SetZoomLevel(
blink::WebView::zoomFactorToZoomLevel(factor)));
}
double WebFrame::GetZoomFactor() const {
return blink::WebView::zoomLevelToZoomFactor(GetZoomLevel());
}
v8::Handle<v8::Value> WebFrame::RegisterEmbedderCustomElement(
const base::string16& name, v8::Handle<v8::Object> options) {
blink::WebExceptionCode c = 0;
return web_frame_->document().registerEmbedderCustomElement(name, options, c);
}
mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("setName", &WebFrame::SetName)
.SetMethod("setZoomLevel", &WebFrame::SetZoomLevel)
.SetMethod("getZoomLevel", &WebFrame::GetZoomLevel)
.SetMethod("setZoomFactor", &WebFrame::SetZoomFactor)
.SetMethod("getZoomFactor", &WebFrame::GetZoomFactor)
.SetMethod("registerEmbedderCustomElement",
&WebFrame::RegisterEmbedderCustomElement);
}
// static
mate::Handle<WebFrame> WebFrame::Create(v8::Isolate* isolate) {
return CreateHandle(isolate, new WebFrame);
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.Set("webFrame", atom::api::WebFrame::Create(isolate));
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_web_frame, Initialize)

View file

@ -2,44 +2,51 @@
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ #ifndef ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_
#define ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ #define ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_
#include <string>
#include "native_mate/handle.h" #include "native_mate/handle.h"
#include "native_mate/wrappable.h" #include "native_mate/wrappable.h"
namespace blink { namespace blink {
class WebView; class WebLocalFrame;
} }
namespace atom { namespace atom {
namespace api { namespace api {
class WebView : public mate::Wrappable { class WebFrame : public mate::Wrappable {
public: public:
static mate::Handle<WebView> Create(v8::Isolate* isolate); static mate::Handle<WebFrame> Create(v8::Isolate* isolate);
private: private:
WebView(); WebFrame();
virtual ~WebView(); virtual ~WebFrame();
void SetName(const std::string& name);
double SetZoomLevel(double level); double SetZoomLevel(double level);
double GetZoomLevel() const; double GetZoomLevel() const;
double SetZoomFactor(double factor); double SetZoomFactor(double factor);
double GetZoomFactor() const; double GetZoomFactor() const;
v8::Handle<v8::Value> RegisterEmbedderCustomElement(
const base::string16& name, v8::Handle<v8::Object> options);
// mate::Wrappable: // mate::Wrappable:
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate); v8::Isolate* isolate);
blink::WebView* web_view_; blink::WebLocalFrame* web_frame_;
DISALLOW_COPY_AND_ASSIGN(WebView); DISALLOW_COPY_AND_ASSIGN(WebFrame);
}; };
} // namespace api } // namespace api
} // namespace atom } // namespace atom
#endif // ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ #endif // ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_

View file

@ -1,81 +0,0 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/renderer/api/atom_api_web_view.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
namespace {
blink::WebView* GetCurrentWebView() {
blink::WebLocalFrame* frame = blink::WebLocalFrame::frameForCurrentContext();
if (!frame)
return NULL;
return frame->view();
}
} // namespace
WebView::WebView() : web_view_(GetCurrentWebView()) {
}
WebView::~WebView() {
}
double WebView::SetZoomLevel(double level) {
return web_view_->setZoomLevel(level);
}
double WebView::GetZoomLevel() const {
return web_view_->zoomLevel();
}
double WebView::SetZoomFactor(double factor) {
return blink::WebView::zoomLevelToZoomFactor(SetZoomLevel(
blink::WebView::zoomFactorToZoomLevel(factor)));
}
double WebView::GetZoomFactor() const {
return blink::WebView::zoomLevelToZoomFactor(GetZoomLevel());
}
mate::ObjectTemplateBuilder WebView::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("setZoomLevel", &WebView::SetZoomLevel)
.SetMethod("getZoomLevel", &WebView::GetZoomLevel)
.SetMethod("setZoomFactor", &WebView::SetZoomFactor)
.SetMethod("getZoomFactor", &WebView::GetZoomFactor);
}
// static
mate::Handle<WebView> WebView::Create(v8::Isolate* isolate) {
return CreateHandle(isolate, new WebView);
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.Set("webView", atom::api::WebView::Create(isolate));
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_web_view, Initialize)

View file

@ -1,4 +1,5 @@
EventEmitter = require('events').EventEmitter EventEmitter = require('events').EventEmitter
process = global.process
ipc = process.atomBinding('ipc') ipc = process.atomBinding('ipc')
class Ipc extends EventEmitter class Ipc extends EventEmitter

View file

@ -1,6 +1,7 @@
process = global.process
ipc = require 'ipc' ipc = require 'ipc'
CallbacksRegistry = require 'callbacks-registry'
v8Util = process.atomBinding 'v8_util' v8Util = process.atomBinding 'v8_util'
CallbacksRegistry = require 'callbacks-registry'
callbacksRegistry = new CallbacksRegistry callbacksRegistry = new CallbacksRegistry
@ -110,7 +111,7 @@ exports.require = (module) ->
windowCache = null windowCache = null
exports.getCurrentWindow = -> exports.getCurrentWindow = ->
return windowCache if windowCache? return windowCache if windowCache?
meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW' meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId
windowCache = metaToValue meta windowCache = metaToValue meta
# Get a global object in browser. # Get a global object in browser.
@ -129,3 +130,8 @@ exports.createFunctionWithReturnValue = (returnValue) ->
func = -> returnValue func = -> returnValue
v8Util.setHiddenValue func, 'returnValue', true v8Util.setHiddenValue func, 'returnValue', true
func func
# Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = (guestInstanceId) ->
meta = ipc.sendChannelSync 'ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId
metaToValue meta

View file

@ -0,0 +1 @@
module.exports = process.atomBinding('web_frame').webFrame

View file

@ -1 +0,0 @@
module.exports = process.atomBinding('web_view').webView

View file

@ -76,13 +76,6 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
void AtomRenderViewObserver::OnBrowserMessage(const base::string16& channel, void AtomRenderViewObserver::OnBrowserMessage(const base::string16& channel,
const base::ListValue& args) { const base::ListValue& args) {
if (!render_view()->GetWebView())
return;
blink::WebFrame* frame = render_view()->GetWebView()->mainFrame();
if (!renderer_client_->IsNodeBindingEnabled(frame))
return;
renderer_client_->atom_bindings()->OnBrowserMessage( renderer_client_->atom_bindings()->OnBrowserMessage(
render_view(), channel, args); render_view(), channel, args);
} }

View file

@ -13,13 +13,15 @@
#include "atom/renderer/atom_render_view_observer.h" #include "atom/renderer/atom_render_view_observer.h"
#include "chrome/renderer/printing/print_web_view_helper.h" #include "chrome/renderer/printing/print_web_view_helper.h"
#include "chrome/renderer/tts_dispatcher.h" #include "chrome/renderer/tts_dispatcher.h"
#include "content/public/common/content_constants.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_thread.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "native_mate/converter.h" #include "native_mate/converter.h"
#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebCustomElement.h"
#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebPluginParams.h"
#include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
@ -29,13 +31,6 @@ namespace atom {
namespace { namespace {
// Security tokens.
const char* kSecurityAll = "all";
const char* kSecurityExceptIframe = "except-iframe";
const char* kSecurityManualEnableIframe = "manual-enable-iframe";
const char* kSecurityDisable = "disable";
const char* kSecurityEnableNodeIntegration = "enable-node-integration";
bool IsSwitchEnabled(base::CommandLine* command_line, bool IsSwitchEnabled(base::CommandLine* command_line,
const char* switch_string, const char* switch_string,
bool* enabled) { bool* enabled) {
@ -73,24 +68,9 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
} // namespace } // namespace
AtomRendererClient::AtomRendererClient() AtomRendererClient::AtomRendererClient()
: node_integration_(EXCEPT_IFRAME), : node_bindings_(NodeBindings::Create(false)),
atom_bindings_(new AtomRendererBindings),
main_frame_(NULL) { main_frame_(NULL) {
// Translate the token.
std::string token = CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switches::kNodeIntegration);
if (token == kSecurityExceptIframe)
node_integration_ = EXCEPT_IFRAME;
else if (token == kSecurityManualEnableIframe)
node_integration_ = MANUAL_ENABLE_IFRAME;
else if (token == kSecurityDisable)
node_integration_ = DISABLE;
else if (token == kSecurityAll)
node_integration_ = ALL;
if (IsNodeBindingEnabled()) {
node_bindings_.reset(NodeBindings::Create(false));
atom_bindings_.reset(new AtomRendererBindings);
}
} }
AtomRendererClient::~AtomRendererClient() { AtomRendererClient::~AtomRendererClient() {
@ -99,8 +79,8 @@ AtomRendererClient::~AtomRendererClient() {
void AtomRendererClient::WebKitInitialized() { void AtomRendererClient::WebKitInitialized() {
EnableWebRuntimeFeatures(); EnableWebRuntimeFeatures();
if (!IsNodeBindingEnabled()) blink::WebCustomElement::addEmbedderCustomElementName("webview");
return; blink::WebCustomElement::addEmbedderCustomElementName("browserplugin");
node_bindings_->Initialize(); node_bindings_->Initialize();
node_bindings_->PrepareMessageLoop(); node_bindings_->PrepareMessageLoop();
@ -134,6 +114,20 @@ blink::WebSpeechSynthesizer* AtomRendererClient::OverrideSpeechSynthesizer(
return new TtsDispatcher(client); return new TtsDispatcher(client);
} }
bool AtomRendererClient::OverrideCreatePlugin(
content::RenderFrame* render_frame,
blink::WebLocalFrame* frame,
const blink::WebPluginParams& params,
blink::WebPlugin** plugin) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (params.mimeType.utf8() == content::kBrowserPluginMimeType ||
command_line->HasSwitch(switches::kEnablePlugins))
return false;
*plugin = nullptr;
return true;
}
void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame, void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame,
v8::Handle<v8::Context> context, v8::Handle<v8::Context> context,
int extension_group, int extension_group,
@ -142,9 +136,6 @@ void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame,
if (main_frame_ == NULL) if (main_frame_ == NULL)
main_frame_ = frame; main_frame_ = frame;
if (!IsNodeBindingEnabled(frame))
return;
v8::Context::Scope scope(context); v8::Context::Scope scope(context);
// Check the existance of process object to prevent duplicate initialization. // Check the existance of process object to prevent duplicate initialization.
@ -173,9 +164,6 @@ void AtomRendererClient::WillReleaseScriptContext(
blink::WebFrame* frame, blink::WebFrame* frame,
v8::Handle<v8::Context> context, v8::Handle<v8::Context> context,
int world_id) { int world_id) {
if (!IsNodeBindingEnabled(frame))
return;
node::Environment* env = node::Environment::GetCurrent(context); node::Environment* env = node::Environment::GetCurrent(context);
if (env == NULL) { if (env == NULL) {
LOG(ERROR) << "Encounter a non-node context when releasing script context"; LOG(ERROR) << "Encounter a non-node context when releasing script context";
@ -210,6 +198,10 @@ bool AtomRendererClient::ShouldFork(blink::WebFrame* frame,
bool is_initial_navigation, bool is_initial_navigation,
bool is_server_redirect, bool is_server_redirect,
bool* send_referrer) { bool* send_referrer) {
// Never fork renderer process for guests.
if (frame->uniqueName().utf8() == "ATOM_SHELL_GUEST_WEB_VIEW")
return false;
// Handle all the navigations and reloads in browser. // Handle all the navigations and reloads in browser.
// FIXME We only support GET here because http method will be ignored when // FIXME We only support GET here because http method will be ignored when
// the OpenURLFromTab is triggered, which means form posting would not work, // the OpenURLFromTab is triggered, which means form posting would not work,
@ -217,27 +209,6 @@ bool AtomRendererClient::ShouldFork(blink::WebFrame* frame,
return http_method == "GET"; return http_method == "GET";
} }
bool AtomRendererClient::IsNodeBindingEnabled(blink::WebFrame* frame) {
if (node_integration_ == DISABLE)
return false;
// Node integration is enabled in main frame unless explictly disabled.
else if (frame == main_frame_)
return true;
// Enable node integration in chrome extensions.
else if (frame != NULL &&
GURL(frame->document().url()).SchemeIs("chrome-extension"))
return true;
else if (node_integration_ == MANUAL_ENABLE_IFRAME &&
frame != NULL &&
frame->uniqueName().utf8().find(kSecurityEnableNodeIntegration)
== std::string::npos)
return false;
else if (node_integration_ == EXCEPT_IFRAME && frame != NULL)
return false;
else
return true;
}
void AtomRendererClient::EnableWebRuntimeFeatures() { void AtomRendererClient::EnableWebRuntimeFeatures() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool b; bool b;

View file

@ -26,8 +26,6 @@ class AtomRendererClient : public content::ContentRendererClient,
AtomRendererClient(); AtomRendererClient();
virtual ~AtomRendererClient(); virtual ~AtomRendererClient();
bool IsNodeBindingEnabled(blink::WebFrame* frame = NULL);
// Forwarded by RenderFrameObserver. // Forwarded by RenderFrameObserver.
void WillReleaseScriptContext(blink::WebFrame* frame, void WillReleaseScriptContext(blink::WebFrame* frame,
v8::Handle<v8::Context> context, v8::Handle<v8::Context> context,
@ -47,21 +45,25 @@ class AtomRendererClient : public content::ContentRendererClient,
virtual void WebKitInitialized() OVERRIDE; virtual void WebKitInitialized() OVERRIDE;
// content::ContentRendererClient: // content::ContentRendererClient:
virtual void RenderThreadStarted() OVERRIDE; void RenderThreadStarted() override;
virtual void RenderFrameCreated(content::RenderFrame* render_frame) OVERRIDE; void RenderFrameCreated(content::RenderFrame* render_frame) override;
virtual void RenderViewCreated(content::RenderView*) OVERRIDE; void RenderViewCreated(content::RenderView*) override;
virtual blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer( blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client); blink::WebSpeechSynthesizerClient* client) override;
virtual void DidCreateScriptContext(blink::WebFrame* frame, bool OverrideCreatePlugin(content::RenderFrame* render_frame,
v8::Handle<v8::Context> context, blink::WebLocalFrame* frame,
int extension_group, const blink::WebPluginParams& params,
int world_id) OVERRIDE; blink::WebPlugin** plugin) override;
virtual bool ShouldFork(blink::WebFrame* frame, void DidCreateScriptContext(blink::WebFrame* frame,
const GURL& url, v8::Handle<v8::Context> context,
const std::string& http_method, int extension_group,
bool is_initial_navigation, int world_id) override;
bool is_server_redirect, bool ShouldFork(blink::WebFrame* frame,
bool* send_referrer) OVERRIDE; const GURL& url,
const std::string& http_method,
bool is_initial_navigation,
bool is_server_redirect,
bool* send_referrer) override;
void EnableWebRuntimeFeatures(); void EnableWebRuntimeFeatures();
@ -70,9 +72,6 @@ class AtomRendererClient : public content::ContentRendererClient,
scoped_ptr<NodeBindings> node_bindings_; scoped_ptr<NodeBindings> node_bindings_;
scoped_ptr<AtomRendererBindings> atom_bindings_; scoped_ptr<AtomRendererBindings> atom_bindings_;
// The level of node integration we should support.
NodeIntegration node_integration_;
// The main frame. // The main frame.
blink::WebFrame* main_frame_; blink::WebFrame* main_frame_;

View file

@ -0,0 +1,48 @@
ipc = require 'ipc'
requestId = 0
WEB_VIEW_EVENTS =
'did-finish-load': []
'did-fail-load': ['errorCode', 'errorDescription']
'did-frame-finish-load': ['isMainFrame']
'did-start-loading': []
'did-stop-loading': []
'did-get-redirect-request': ['oldUrl', 'newUrl', 'isMainFrame']
'console-message': ['level', 'message', 'line', 'sourceId']
'new-window': ['url', 'partitionId']
'close': []
'crashed': []
'destroyed': []
dispatchEvent = (webView, event, args...) ->
throw new Error("Unkown event #{event}") unless WEB_VIEW_EVENTS[event]?
domEvent = new Event(event)
for f, i in WEB_VIEW_EVENTS[event]
domEvent[f] = args[i]
webView.dispatchEvent domEvent
module.exports =
registerEvents: (webView, viewInstanceId) ->
ipc.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}", (event, args...) ->
dispatchEvent webView, event, args...
ipc.on 'ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED', (args...) ->
domEvent = new Event('size-changed')
for f, i in ['oldWidth', 'oldHeight', 'newWidth', 'newHeight']
domEvent[f] = args[i]
webView.onSizeChanged domEvent
createGuest: (type, params, callback) ->
requestId++
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', type, params, requestId
ipc.on "ATOM_SHELL_RESPONSE_#{requestId}", callback
destroyGuest: (guestInstanceId) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId
setAutoSize: (guestInstanceId, params) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_AUTO_SIZE', guestInstanceId, params
setAllowTransparency: (guestInstanceId, allowtransparency) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', guestInstanceId, allowtransparency

View file

@ -20,32 +20,16 @@ globalPaths.push path.join(process.resourcesPath, 'app')
# Import common settings. # Import common settings.
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
# Expose global variables. # Process command line arguments.
global.require = require nodeIntegration = 'false'
global.module = module for arg in process.argv
if arg.indexOf('--guest-instance-id=') == 0
# Emit the 'exit' event when page is unloading. # This is a guest web view.
window.addEventListener 'unload', -> process.guestInstanceId = parseInt arg.substr(arg.indexOf('=') + 1)
process.emit 'exit' # Set the frame name to make AtomRendererClient recognize this guest.
require('web-frame').setName 'ATOM_SHELL_GUEST_WEB_VIEW'
# Set the __filename to the path of html file if it's file: or asar: protocol. else if arg.indexOf('--node-integration=') == 0
if window.location.protocol in ['file:', 'asar:'] nodeIntegration = arg.substr arg.indexOf('=') + 1
pathname =
if process.platform is 'win32' and window.location.pathname[0] is '/'
window.location.pathname.substr 1
else
window.location.pathname
global.__filename = path.normalize decodeURIComponent(pathname)
global.__dirname = path.dirname global.__filename
# Set module's filename so relative require can work as expected.
module.filename = global.__filename
# Also search for module under the html file.
module.paths = module.paths.concat Module._nodeModulePaths(global.__dirname)
else
global.__filename = __filename
global.__dirname = __dirname
if location.protocol is 'chrome-devtools:' if location.protocol is 'chrome-devtools:'
# Override some inspector APIs. # Override some inspector APIs.
@ -56,3 +40,46 @@ else if location.protocol is 'chrome-extension:'
else else
# Override default web functions. # Override default web functions.
require path.join(__dirname, 'override') require path.join(__dirname, 'override')
# Load webview tag implementation.
require path.join(__dirname, 'web-view') unless process.guestInstanceId?
if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe']
# Export node bindings to global.
global.require = require
global.module = module
# Set the __filename to the path of html file if it's file: or asar: protocol.
if window.location.protocol in ['file:', 'asar:']
pathname =
if process.platform is 'win32' and window.location.pathname[0] is '/'
window.location.pathname.substr 1
else
window.location.pathname
global.__filename = path.normalize decodeURIComponent(pathname)
global.__dirname = path.dirname global.__filename
# Set module's filename so relative require can work as expected.
module.filename = global.__filename
# Also search for module under the html file.
module.paths = module.paths.concat Module._nodeModulePaths(global.__dirname)
else
global.__filename = __filename
global.__dirname = __dirname
# Redirect window.onerror to uncaughtException.
window.onerror = (error) ->
if global.process.listeners('uncaughtException').length > 0
global.process.emit 'uncaughtException', error
true
else
false
# Emit the 'exit' event when page is unloading.
window.addEventListener 'unload', ->
process.emit 'exit'
else
# There still some native initialization codes needs "process", delete the
# global reference after they are done.
process.once 'BIND_DONE', ->
delete global.process

View file

@ -1,50 +1,43 @@
# Redirect window.onerror to uncaughtException. process = global.process
window.onerror = (error) -> remote = require 'remote'
if global.process.listeners('uncaughtException').length > 0
global.process.emit 'uncaughtException', error
true
else
false
# Override default window.close, see: unless process.guestInstanceId?
# https://github.com/atom/atom-shell/issues/70 # Override default window.close, see:
window.close = -> window.close = ->
require('remote').getCurrentWindow().close() remote.getCurrentWindow().close()
# Override default window.open. # Override default window.open.
window.open = (url, name, features) -> window.open = (url, name, features) ->
options = {} options = {}
for feature in features.split ',' for feature in features.split ','
[name, value] = feature.split '=' [name, value] = feature.split '='
options[name] = options[name] =
if value is 'yes' if value is 'yes'
true true
else if value is 'no' else if value is 'no'
false false
else else
value value
options.x ?= options.left options.x ?= options.left
options.y ?= options.top options.y ?= options.top
options.title ?= name options.title ?= name
options.width ?= 800 options.width ?= 800
options.height ?= 600 options.height ?= 600
BrowserWindow = require('remote').require 'browser-window' BrowserWindow = require('remote').require 'browser-window'
browser = new BrowserWindow options browser = new BrowserWindow options
browser.loadUrl url browser.loadUrl url
browser browser
# Use the dialog API to implement alert(). # Use the dialog API to implement alert().
window.alert = (message, title='') -> window.alert = (message, title='') ->
remote = require 'remote'
dialog = remote.require 'dialog' dialog = remote.require 'dialog'
buttons = ['OK'] buttons = ['OK']
dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons} dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}
# And the confirm(). # And the confirm().
window.confirm = (message, title='') -> window.confirm = (message, title='') ->
remote = require 'remote'
dialog = remote.require 'dialog' dialog = remote.require 'dialog'
buttons = ['OK', 'Cancel'] buttons = ['OK', 'Cancel']
not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons} not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}

View file

@ -0,0 +1,547 @@
v8Util = process.atomBinding 'v8_util'
guestViewInternal = require './guest-view-internal'
webFrame = require 'web-frame'
remote = require 'remote'
# ID generator.
nextId = 0
getNextId = -> ++nextId
# FIXME
# Discarded after Chrome 39
PLUGIN_METHOD_ATTACH = '-internal-attach'
# Attributes.
WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY = 'allowtransparency'
WEB_VIEW_ATTRIBUTE_AUTOSIZE = 'autosize'
WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'
WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'
WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'
WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'
WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'
WEB_VIEW_ATTRIBUTE_NODEINTEGRATION = 'nodeintegration'
AUTO_SIZE_ATTRIBUTES = [
WEB_VIEW_ATTRIBUTE_AUTOSIZE,
WEB_VIEW_ATTRIBUTE_MAXHEIGHT,
WEB_VIEW_ATTRIBUTE_MAXWIDTH,
WEB_VIEW_ATTRIBUTE_MINHEIGHT,
WEB_VIEW_ATTRIBUTE_MINWIDTH,
]
# Error messages.
ERROR_MSG_ALREADY_NAVIGATED =
'The object has already navigated, so its partition cannot be changed.'
ERROR_MSG_CANNOT_INJECT_SCRIPT = '<webview>: ' +
'Script cannot be injected into content until the page has loaded.'
ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE = '<webview>: ' +
'contentWindow is not available at this time. It will become available ' +
'when the page has finished loading.'
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'
# Represents the state of the storage partition.
class Partition
constructor: ->
@validPartitionId = true
@persistStorage = false
@storagePartitionId = ''
toAttribute: ->
return '' unless @validPartitionId
(if @persistStorage then 'persist:' else '') + @storagePartitionId
fromAttribute: (value, hasNavigated) ->
result = {}
if hasNavigated
result.error = ERROR_MSG_ALREADY_NAVIGATED
return result
value ?= ''
LEN = 'persist:'.length
if value.substr(0, LEN) == 'persist:'
value = value.substr LEN
unless value
@validPartitionId = false
result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
return result
@persistStorage = true
else
@persistStorage = false
@storagePartitionId = value
result
# Represents the internal state of the WebView node.
class WebView
constructor: (@webviewNode) ->
v8Util.setHiddenValue @webviewNode, 'internal', this
@attached = false
@pendingGuestCreation = false
@elementAttached = false
@beforeFirstNavigation = true
@contentWindow = null
@validPartitionId = true
# Used to save some state upon deferred attachment.
# If <object> bindings is not available, we defer attachment.
# This state contains whether or not the attachment request was for
# newwindow.
@deferredAttachState = null
# on* Event handlers.
@on = {}
@browserPluginNode = @createBrowserPluginNode()
shadowRoot = @webviewNode.createShadowRoot()
@partition = new Partition()
@setupWebViewSrcAttributeMutationObserver()
@setupFocusPropagation()
@setupWebviewNodeProperties()
@viewInstanceId = getNextId()
guestViewInternal.registerEvents this, @viewInstanceId
shadowRoot.appendChild @browserPluginNode
createBrowserPluginNode: ->
# We create BrowserPlugin as a custom element in order to observe changes
# to attributes synchronously.
browserPluginNode = new WebView.BrowserPlugin()
v8Util.setHiddenValue browserPluginNode, 'internal', this
browserPluginNode
getGuestInstanceId: ->
@guestInstanceId
# Resets some state upon reattaching <webview> element to the DOM.
reset: ->
# If guestInstanceId is defined then the <webview> has navigated and has
# already picked up a partition ID. Thus, we need to reset the initialization
# state. However, it may be the case that beforeFirstNavigation is false BUT
# guestInstanceId has yet to be initialized. This means that we have not
# 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 @guestInstanceId
guestViewInternal.destroyGuest @guestInstanceId
@guestInstanceId = undefined
@beforeFirstNavigation = true
@validPartitionId = true
@partition.validPartitionId = true
@contentWindow = null
@internalInstanceId = 0
# Sets the <webview>.request property.
setRequestPropertyOnWebViewNode: (request) ->
Object.defineProperty @webviewNode, 'request', value: request, enumerable: true
setupFocusPropagation: ->
unless @webviewNode.hasAttribute 'tabIndex'
# <webview> needs a tabIndex in order to be focusable.
# TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
# to allow <webview> to be focusable.
# See http://crbug.com/231664.
@webviewNode.setAttribute 'tabIndex', -1
@webviewNode.addEventListener 'focus', (e) =>
# Focus the BrowserPlugin when the <webview> takes focus.
@browserPluginNode.focus()
@webviewNode.addEventListener 'blur', (e) =>
# Blur the BrowserPlugin when the <webview> loses focus.
@browserPluginNode.blur()
# Validation helper function for executeScript() and insertCSS().
validateExecuteCodeCall: ->
throw new Error(ERROR_MSG_CANNOT_INJECT_SCRIPT) unless @guestInstanceId
setupAutoSizeProperties: ->
for attributeName in AUTO_SIZE_ATTRIBUTES
this[attributeName] = @webviewNode.getAttribute attributeName
Object.defineProperty @webviewNode, attributeName,
get: => this[attributeName]
set: (value) => @webviewNode.setAttribute attributeName, value
enumerable: true
setupWebviewNodeProperties: ->
@setupAutoSizeProperties()
Object.defineProperty @webviewNode, WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY,
get: => @allowtransparency
set: (value) =>
@webviewNode.setAttribute WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY, value
enumerable: true
# We cannot use {writable: true} property descriptor because we want a
# dynamic getter value.
Object.defineProperty @webviewNode, 'contentWindow',
get: =>
return @contentWindow if @contentWindow?
window.console.error ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE
# No setter.
enumerable: true
Object.defineProperty @webviewNode, 'partition',
get: => @partition.toAttribute()
set: (value) =>
result = @partition.fromAttribute value, @hasNavigated()
throw result.error if result.error?
@webviewNode.setAttribute 'partition', value
enumerable: true
@src = @webviewNode.getAttribute 'src'
Object.defineProperty @webviewNode, 'src',
get: => @src
set: (value) => @webviewNode.setAttribute 'src', value
# No setter.
enumerable: true
# The purpose of this mutation observer is to catch assignment to the src
# attribute without any changes to its value. This is useful in the case
# where the webview guest has crashed and navigating to the same address
# spawns off a new process.
setupWebViewSrcAttributeMutationObserver: ->
@srcAndPartitionObserver = new MutationObserver (mutations) =>
for mutation in mutations
oldValue = mutation.oldValue
newValue = @webviewNode.getAttribute mutation.attributeName
return if oldValue isnt newValue
@handleWebviewAttributeMutation mutation.attributeName, oldValue, newValue
params =
attributes: true,
attributeOldValue: true,
attributeFilter: ['src', 'partition']
@srcAndPartitionObserver.observe @webviewNode, params
# This observer monitors mutations to attributes of the <webview> and
# updates the BrowserPlugin properties accordingly. In turn, updating
# a BrowserPlugin property will update the corresponding BrowserPlugin
# attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
# details.
handleWebviewAttributeMutation: (name, oldValue, newValue) ->
if name in AUTO_SIZE_ATTRIBUTES
this[name] = newValue
return unless @guestInstanceId
# Convert autosize attribute to boolean.
autosize = @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE
guestViewInternal.setAutoSize @guestInstanceId,
enableAutoSize: autosize,
min:
width: parseInt @minwidth || 0
height: parseInt @minheight || 0
max:
width: parseInt @maxwidth || 0
height: parseInt @maxheight || 0
else if name is WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY
# We treat null attribute (attribute removed) and the empty string as
# one case.
oldValue ?= ''
newValue ?= ''
return if oldValue is newValue
@allowtransparency = newValue != ''
return unless @guestInstanceId
guestViewInternal.setAllowTransparency @guestInstanceId, @allowtransparency
else if name is 'src'
# We treat null attribute (attribute removed) and the empty string as
# one case.
oldValue ?= ''
newValue ?= ''
# Once we have navigated, we don't allow clearing the src attribute.
# Once <webview> enters a navigated state, it cannot be return back to a
# placeholder state.
if newValue == '' and oldValue != ''
# src attribute changes normally initiate a navigation. We suppress
# the next src attribute handler call to avoid reloading the page
# on every guest-initiated navigation.
@ignoreNextSrcAttributeChange = true
@webviewNode.setAttribute 'src', oldValue
@src = newValue
if @ignoreNextSrcAttributeChange
# Don't allow the src mutation observer to see this change.
@srcAndPartitionObserver.takeRecords()
@ignoreNextSrcAttributeChange = false
return
result = {}
@parseSrcAttribute result
throw result.error if result.error?
else if name is 'partition'
# Note that throwing error here won't synchronously propagate.
@partition.fromAttribute newValue, @hasNavigated()
handleBrowserPluginAttributeMutation: (name, oldValue, newValue) ->
# FIXME
# internalbindings => internalInstanceid after Chrome 39
if name is 'internalbindings' and !oldValue and !!newValue
@browserPluginNode.removeAttribute 'internalbindings'
# FIXME
# @internalInstanceId = parseInt newValue
if !!@guestInstanceId and @guestInstanceId != 0
isNewWindow = if @deferredAttachState then @deferredAttachState.isNewWindow else false
params = @buildAttachParams isNewWindow
# FIXME
# guestViewInternalNatives.AttachGuest
# @internalInstanceId,
# @guestInstanceId,
# params,
# (w) => @contentWindow = w
@browserPluginNode[PLUGIN_METHOD_ATTACH] @guestInstanceId, params
onSizeChanged: (webViewEvent) ->
newWidth = webViewEvent.newWidth
newHeight = webViewEvent.newHeight
node = @webviewNode
width = node.offsetWidth
height = node.offsetHeight
# Check the current bounds to make sure we do not resize <webview>
# outside of current constraints.
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) and
node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]
maxWidth = node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]
else
maxWidth = width
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINWIDTH) and
node[WEB_VIEW_ATTRIBUTE_MINWIDTH]
minWidth = node[WEB_VIEW_ATTRIBUTE_MINWIDTH]
else
minWidth = width
minWidth = maxWidth if minWidth > maxWidth
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXHEIGHT) and
node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT]
maxHeight = node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT]
else
maxHeight = height
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) and
node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]
minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]
else
minHeight = height
minHeight = maxHeight if minHeight > maxHeight
if not @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE or
(newWidth >= minWidth and
newWidth <= maxWidth and
newHeight >= minHeight and
newHeight <= maxHeight)
node.style.width = newWidth + 'px'
node.style.height = newHeight + 'px'
# Only fire the DOM event if the size of the <webview> has actually
# changed.
@dispatchEvent webViewEvent
# Returns if <object> is in the render tree.
isPluginInRenderTree: ->
# FIXME
# !!@internalInstanceId && @internalInstanceId != 0
'function' == typeof this.browserPluginNode[PLUGIN_METHOD_ATTACH]
hasNavigated: ->
not @beforeFirstNavigation
parseSrcAttribute: (result) ->
unless @partition.validPartitionId
result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
return
@src = @webviewNode.getAttribute 'src'
return unless @src
unless @guestInstanceId?
if @beforeFirstNavigation
@beforeFirstNavigation = false
@createGuest()
return
# Navigate to |this.src|.
remote.getGuestWebContents(@guestInstanceId).loadUrl @src
parseAttributes: ->
return unless @elementAttached
hasNavigated = @hasNavigated()
attributeValue = @webviewNode.getAttribute 'partition'
result = @partition.fromAttribute attributeValue, hasNavigated
@parseSrcAttribute result
createGuest: ->
return if @pendingGuestCreation
storagePartitionId =
@webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) or
@webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]
params =
storagePartitionId: storagePartitionId
nodeIntegration: @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_NODEINTEGRATION
guestViewInternal.createGuest 'webview', params, (guestInstanceId) =>
@pendingGuestCreation = false
unless @elementAttached
guestViewInternal.destroyGuest guestInstanceId
return
@attachWindow guestInstanceId, false
@pendingGuestCreation = true
dispatchEvent: (webViewEvent) ->
@webviewNode.dispatchEvent webViewEvent
# Adds an 'on<event>' property on the webview, which can be used to set/unset
# an event handler.
setupEventProperty: (eventName) ->
propertyName = 'on' + eventName.toLowerCase()
Object.defineProperty @webviewNode, propertyName,
get: => @on[propertyName]
set: (value) =>
if @on[propertyName]
@webviewNode.removeEventListener eventName, @on[propertyName]
@on[propertyName] = value
if value
@webviewNode.addEventListener eventName, value
enumerable: true
# Updates state upon loadcommit.
onLoadCommit: (@baseUrlForDataUrl, @currentEntryIndex, @entryCount, @processId, url, isTopLevel) ->
oldValue = @webviewNode.getAttribute 'src'
newValue = url
if isTopLevel and (oldValue != newValue)
# Touching the src attribute triggers a navigation. To avoid
# triggering a page reload on every guest-initiated navigation,
# we use the flag ignoreNextSrcAttributeChange here.
this.ignoreNextSrcAttributeChange = true
this.webviewNode.setAttribute 'src', newValue
onAttach: (storagePartitionId) ->
@webviewNode.setAttribute 'partition', storagePartitionId
@partition.fromAttribute storagePartitionId, this.hasNavigated()
buildAttachParams: (isNewWindow) ->
allowtransparency: @allowtransparency || false
autosize: @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE
instanceId: @viewInstanceId
maxheight: parseInt @maxheight || 0
maxwidth: parseInt @maxwidth || 0
minheight: parseInt @minheight || 0
minwidth: parseInt @minwidth || 0
# We don't need to navigate new window from here.
src: if isNewWindow then undefined else @src
# If we have a partition from the opener, that will also be already
# set via this.onAttach().
storagePartitionId: @partition.toAttribute()
userAgentOverride: @userAgentOverride
attachWindow: (guestInstanceId, isNewWindow) ->
@guestInstanceId = guestInstanceId
params = @buildAttachParams isNewWindow
unless @isPluginInRenderTree()
@deferredAttachState = isNewWindow: isNewWindow
return true
@deferredAttachState = null
# FIXME
# guestViewInternalNatives.AttachGuest @internalInstanceId, @guestInstanceId, params, (w) => @contentWindow = w
@browserPluginNode[PLUGIN_METHOD_ATTACH] @guestInstanceId, params
# Registers browser plugin <object> custom element.
registerBrowserPluginElement = ->
proto = Object.create HTMLObjectElement.prototype
proto.createdCallback = ->
@setAttribute 'type', 'application/browser-plugin'
@setAttribute 'id', 'browser-plugin-' + getNextId()
# The <object> node fills in the <webview> container.
@style.width = '100%'
@style.height = '100%'
proto.attributeChangedCallback = (name, oldValue, newValue) ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
internal.handleBrowserPluginAttributeMutation name, oldValue, newValue
proto.attachedCallback = ->
# Load the plugin immediately.
unused = this.nonExistentAttribute
WebView.BrowserPlugin = webFrame.registerEmbedderCustomElement 'browserplugin',
extends: 'object', prototype: proto
delete proto.createdCallback
delete proto.attachedCallback
delete proto.detachedCallback
delete proto.attributeChangedCallback
# Registers <webview> custom element.
registerWebViewElement = ->
proto = Object.create HTMLObjectElement.prototype
proto.createdCallback = ->
new WebView(this)
proto.attributeChangedCallback = (name, oldValue, newValue) ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
internal.handleWebviewAttributeMutation name, oldValue, newValue
proto.detachedCallback = ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
internal.elementAttached = false
internal.reset()
proto.attachedCallback = ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
unless internal.elementAttached
internal.elementAttached = true
internal.parseAttributes()
# Public-facing API methods.
methods = [
"getUrl"
"getTitle"
"isLoading"
"isWaitingForResponse"
"stop"
"reload"
"reloadIngoringCache"
"canGoBack"
"canGoForward"
"canGoToOffset"
"goBack"
"goForward"
"goToIndex"
"goToOffset"
"isCrashed"
"setUserAgent"
"executeJavaScript"
"insertCSS"
"send"
]
# Forward proto.foo* method calls to WebView.foo*.
createHandler = (m) ->
(args...) ->
internal = v8Util.getHiddenValue this, 'internal'
remote.getGuestWebContents(internal.guestInstanceId)[m] args...
proto[m] = createHandler m for m in methods
window.WebView = webFrame.registerEmbedderCustomElement 'webview',
prototype: proto
# Delete the callbacks so developers cannot call them and produce unexpected
# behavior.
delete proto.createdCallback
delete proto.attachedCallback
delete proto.detachedCallback
delete proto.attributeChangedCallback
useCapture = true
listener = (event) ->
return if document.readyState == 'loading'
registerBrowserPluginElement()
registerWebViewElement()
window.removeEventListener event.type, listener, useCapture
window.addEventListener 'readystatechange', listener, true

View file

@ -14,6 +14,11 @@
* [Process object](api/process.md) * [Process object](api/process.md)
* [Supported Chrome command line switches](api/chrome-command-line-switches.md) * [Supported Chrome command line switches](api/chrome-command-line-switches.md)
Custom DOM elements:
* [`File` object](app/file-object.md)
* [`<webview>` tag](app/web-view-tag.md)
Modules for browser side: Modules for browser side:
* [app](api/app.md) * [app](api/app.md)
@ -33,7 +38,7 @@ Modules for web page:
* [ipc (renderer)](api/ipc-renderer.md) * [ipc (renderer)](api/ipc-renderer.md)
* [remote](api/remote.md) * [remote](api/remote.md)
* [web-view](api/web-view.md) * [web-frame](api/web-frame.md)
Modules for both sides: Modules for both sides:
@ -47,8 +52,6 @@ Modules for both sides:
* [Coding style](development/coding-style.md) * [Coding style](development/coding-style.md)
* [Source code directory structure](development/source-code-directory-structure.md) * [Source code directory structure](development/source-code-directory-structure.md)
* [Technical differences to node-webkit](development/atom-shell-vs-node-webkit.md) * [Technical differences to node-webkit](development/atom-shell-vs-node-webkit.md)
* [How node.js is integrated into atom-shell](https://speakerdeck.com/zcbenz/practice-on-embedding-node-dot-js-into-atom-editor) (slides) ([中文版](http://2014.jsconf.cn/slides/Practice%20on%20embedding%20Node.js%20into%20Atom%20Editor.pdf
))
* [Build instructions (Mac)](development/build-instructions-mac.md) * [Build instructions (Mac)](development/build-instructions-mac.md)
* [Build instructions (Windows)](development/build-instructions-windows.md) * [Build instructions (Windows)](development/build-instructions-windows.md)
* [Build instructions (Linux)](development/build-instructions-linux.md) * [Build instructions (Linux)](development/build-instructions-linux.md)

View file

@ -18,9 +18,6 @@ win.show();
You can also create a window without chrome by using You can also create a window without chrome by using
[Frameless Window](frameless-window.md) API. [Frameless Window](frameless-window.md) API.
Security strategy of web pages showed by `BrowserWindow` is a bit different from
normal browsers, see [Web Security](web-security.md) for more.
## Class: BrowserWindow ## Class: BrowserWindow
`BrowserWindow` is an `BrowserWindow` is an
@ -54,9 +51,8 @@ normal browsers, see [Web Security](web-security.md) for more.
* `show` Boolean - Whether window should be shown when created * `show` Boolean - Whether window should be shown when created
* `frame` Boolean - Specify `false` to create a * `frame` Boolean - Specify `false` to create a
[Frameless Window](frameless-window.md) [Frameless Window](frameless-window.md)
* `node-integration` String - Default value is `except-iframe`, can also be * `node-integration` Boolean - Whether node integration is enabled, default
`all`, `manual-enable-iframe` or `disable`, see is `true`
[Web Security](web-security.md) for more informations.
* `accept-first-mouse` Boolean - Whether the web view accepts a single * `accept-first-mouse` Boolean - Whether the web view accepts a single
mouse-down event that simultaneously activates the window mouse-down event that simultaneously activates the window
* `auto-hide-menu-bar` Boolean - Auto hide the menu bar unless the `Alt` * `auto-hide-menu-bar` Boolean - Auto hide the menu bar unless the `Alt`
@ -512,14 +508,19 @@ A `WebContents` is responsible for rendering and controlling a web page.
`WebContents` is an `WebContents` is an
[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter).
### Event: 'crashed'
Emitted when the renderer process is crashed.
### Event: 'did-finish-load' ### Event: 'did-finish-load'
Emitted when the navigation is done, i.e. the spinner of the tab will stop Emitted when the navigation is done, i.e. the spinner of the tab will stop
spinning, and the onload event was dispatched. spinning, and the `onload` event was dispatched.
### Event: 'did-fail-load'
* `event` Event
* `errorCode` Integer
* `errorDescription` String
This event is like `did-finish-load`, but emitted when the load failed or was
cancelled, e.g. `window.stop()` is invoked.
### Event: 'did-frame-finish-load' ### Event: 'did-frame-finish-load'
@ -530,8 +531,29 @@ Emitted when a frame has done navigation.
### Event: 'did-start-loading' ### Event: 'did-start-loading'
Corresponds to the points in time when the spinner of the tab starts spinning.
### Event: 'did-stop-loading' ### Event: 'did-stop-loading'
Corresponds to the points in time when the spinner of the tab stops spinning.
### Event: 'did-get-redirect-request'
* `event` Event
* `oldUrl` String
* `newUrl` String
* `isMainFrame` Boolean
Emitted when a redirect was received while requesting a resource.
### Event: 'crashed'
Emitted when the renderer process is crashed.
### Event: 'destroyed'
Emitted when the WebContents is destroyed.
### WebContents.loadUrl(url) ### WebContents.loadUrl(url)
* `url` URL * `url` URL
@ -602,10 +624,22 @@ Navigates to the specified absolute index.
Navigates to the specified offset from the "current entry". Navigates to the specified offset from the "current entry".
### WebContents.IsCrashed() ### WebContents.isCrashed()
Whether the renderer process has crashed. Whether the renderer process has crashed.
### WebContents.setUserAgent(userAgent)
* `userAgent` String
Overrides the user agent for this page.
### WebContents.insertCSS(css)
* `css` String
Injects CSS into this page.
### WebContents.executeJavaScript(code) ### WebContents.executeJavaScript(code)
* `code` String * `code` String

30
docs/api/file-object.md Normal file
View file

@ -0,0 +1,30 @@
# `File` object
The DOM's File interface provides abstraction around native files, in order to
let users work on native files directly with HTML5 file API, atom-shell has
added a `path` attribute to `File` interface which exposes the file's real path
on filesystem.
Example on getting real path of a dragged file:
```html
<div id="holder">
Drag your file here
</div>
<script>
var holder = document.getElementById('holder');
holder.ondragover = function () {
return false;
};
holder.ondragleave = holder.ondragend = function () {
return false;
};
holder.ondrop = function (e) {
e.preventDefault();
var file = e.dataTransfer.files[0];
console.log('File you dragged here is', file.path);
return false;
};
</script>
```

View file

@ -1,26 +1,26 @@
# web-view # web-frame
The `web-view` module can custom the rendering of current web page. The `web-frame` module can custom the rendering of current web page.
An example of zooming current page to 200%. An example of zooming current page to 200%.
```javascript ```javascript
var webView = require('web-view'); var webFrame = require('web-frame');
webView.setZoomFactor(2); webFrame.setZoomFactor(2);
``` ```
## webView.setZoomFactor(factor) ## webFrame.setZoomFactor(factor)
* `factor` Number - Zoom factor * `factor` Number - Zoom factor
Changes the zoom factor to the specified factor, zoom factor is Changes the zoom factor to the specified factor, zoom factor is
zoom percent / 100, so 300% = 3.0. zoom percent / 100, so 300% = 3.0.
## webView.getZoomFactor() ## webFrame.getZoomFactor()
Returns the current zoom factor. Returns the current zoom factor.
## webView.setZoomLevel(level) ## webFrame.setZoomLevel(level)
* `level` Number - Zoom level * `level` Number - Zoom level
@ -28,6 +28,6 @@ Changes the zoom level to the specified level, 0 is "original size", and each
increment above or below represents zooming 20% larger or smaller to default increment above or below represents zooming 20% larger or smaller to default
limits of 300% and 50% of original size, respectively. limits of 300% and 50% of original size, respectively.
## webView.getZoomLevel() ## webFrame.getZoomLevel()
Returns the current zoom level. Returns the current zoom level.

View file

@ -1,62 +0,0 @@
# Web Security
Because atom-shell has added node integration to normal web pages, there are
some security adjustments that made atom-shell both more safe and more
convenient.
## Overriding `X-Frame-Options` header
May websites (including Google and Youtube) use the
[X-Frame-Options][x-frame-options] header to disable access to their websites
in `iframe`s. In atom-shell you can add a `disable-x-frame-options` string in
the `iframe`'s name to disable this:
```html
<!-- Refused to display -->
<iframe name="google" src="https://google.com"></iframe>
<!-- Loads as expected -->
<iframe name="google-disable-x-frame-options" src="https://google.com"></iframe>
```
## Frames are sandboxed by default
In normal browsers, `iframe`s are not sandboxed by default, which means a remote
page in `iframe` can easily access its parent's JavaScript context.
In atom-shell because the parent frame may have the power to access native
resources, this could cause security problems. In order to fix it, `iframe`s
in atom-shell are sandboxed with all permissions except the `allow-same-origin`
by default.
If you want to enable things like `parent.window.process.exit()` in `iframe`s,
you need to explicitly add `allow-same-origin` to the `sandbox` attribute, or
just set `sandbox` to `none`:
```html
<iframe sandbox="none" src="https://github.com"></iframe>
```
## Node integration in frames
The `node-integration` option of [BrowserWindow](browser-window.md) controls
whether node integration is enabled in web page and its `iframe`s.
By default the `node-integration` option is `except-iframe`, which means node
integration is disabled in all `iframe`s. You can also set it to `all`, with
which node integration is available to the main page and all its `iframe`s, or
`manual-enable-iframe`, which is like `except-iframe`, but enables `iframe`s
whose name contains string `enable-node-integration`. And setting to `disable`
would disable the node integration in both the main page and its `iframe`s.
An example of enable node integration in `iframe` with `node-integration` set to
`manual-enable-iframe`:
```html
<!-- iframe with node integration enabled -->
<iframe name="gh-enable-node-integration" src="https://github.com"></iframe>
<!-- iframe with node integration disabled -->
<iframe src="http://jandan.net"></iframe>
```
[x-frame-options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options

271
docs/api/web-view-tag.md Normal file
View file

@ -0,0 +1,271 @@
# `<webview>` tag
Use the `webview` tag to embed 'guest' content (such as web pages) in your
atom-shell app. The guest content is contained within the `webview` container;
an embedder page within your app controls how the guest content is laid out and
rendered.
Different from the `iframe`, the `webview` runs in a separate process than your
app; it doesn't have the same permissions as your web page and all interactions
between your app and embedded content will be asynchronous. This keeps your app
safe from the embedded content.
## Example
To embed a web page in your app, add the `webview` tag to your app's embedder
page (this is the app page that will display the guest content). In its simplest
form, the `webview` tag includes the `src` of the web page and css styles that
control the appearance of the `webview` container:
```html
<webview id="foo" src="https://www.github.com/" style="width:640px; height:480px"></webview>
```
If you want to control the guest content in any way, you can write JavaScript
that listens for `webview` events and responds to those events using the
`webview` methods. Here's sample code with two event listeners: one that listens
for the web page to start loading, the other for the web page to stop loading,
and displays a "loading..." message during the load time:
```html
<script>
onload = function() {
var webview = document.getElementById("foo");
var indicator = document.querySelector(".indicator");
var loadstart = function() {
indicator.innerText = "loading...";
}
var loadstop = function() {
indicator.innerText = "";
}
webview.addEventListener("did-start-loading", loadstart);
webview.addEventListener("id-stop-loading", loadstop);
}
</script>
```
## Tag attributes
### src
```html
<webview src="http://www.google.com/"></webview>
```
Returns the visible URL. Writing to this attribute initiates top-level
navigation.
Assigning `src` its own value will reload the current page.
The `src` attribute can also accept data URLs, such as
`data:text/plain,Hello, world!`.
### autosize
```html
<webview src="http://www.google.com/" autosize="on" minwidth="576" minheight="432"></webview>
```
If "on", the `webview` will container will automatically resize within the
bounds specified by the attributes `minwidth`, `minheight`, `maxwidth`, and
`maxheight`. These contraints do not impact the `webview` UNLESS `autosize` is
enabled. When `autosize` is enabled, the `webview` container size cannot be less
than the minimum values or greater than the maximum.
### nodeintegration
```html
<webview src="http://www.google.com/" nodeintegration></webview>
```
If "on", the guest page in `webview` will have node integration and can use node
APIs like `require` and `process` to access low level system resources.
## Methods
### `<webview>`.getUrl()
Returns URL of guest page.
### `<webview>`.getTitle()
Returns the title of guest page.
### `<webview>`.isLoading()
Returns whether guest page is still loading resources.
### `<webview>`.isWaitingForResponse()
Returns whether guest page is waiting for a first-response for the main resource
of the page.
### `<webview>`.stop()
Stops any pending navigation.
### `<webview>`.reload()
Reloads guest page.
### `<webview>`.reloadIgnoringCache()
Reloads guest page and ignores cache.
### `<webview>`.canGoBack()
Returns whether guest page can go back.
### `<webview>`.canGoForward()
Returns whether guest page can go forward.
### `<webview>`.canGoToOffset(offset)
* `offset` Integer
Returns whether guest page can go to `offset`.
### `<webview>`.goBack()
Makes guest page go back.
### `<webview>`.goForward()
Makes guest page go forward.
### `<webview>`.goToIndex(index)
* `index` Integer
Navigates to the specified absolute index.
### `<webview>`.goToOffset(offset)
* `offset` Integer
Navigates to the specified offset from the "current entry".
### `<webview>`.isCrashed()
Whether the renderer process has crashed.
### `<webview>`.setUserAgent(userAgent)
* `userAgent` String
Overrides the user agent for guest page.
### `<webview>`.insertCSS(css)
* `css` String
Injects CSS into guest page.
### `<webview>`.executeJavaScript(code)
* `code` String
Evaluate `code` in guest page.
### `<webview>`.send(channel[, args...])
* `channel` String
Send `args..` to guest page via `channel` in asynchronous message, the guest
page can handle it by listening to the `channel` event of `ipc` module.
See [WebContents.send](browser-window.md#webcontentssendchannel-args) for
examples.
## DOM events
### did-finish-load
Fired when the navigation is done, i.e. the spinner of the tab will stop
spinning, and the `onload` event was dispatched.
### did-fail-load
* `errorCode` Integer
* `errorDescription` String
This event is like `did-finish-load`, but fired when the load failed or was
cancelled, e.g. `window.stop()` is invoked.
### did-frame-finish-load
* `isMainFrame` Boolean
Fired when a frame has done navigation.
### did-start-loading
Corresponds to the points in time when the spinner of the tab starts spinning.
### did-stop-loading
Corresponds to the points in time when the spinner of the tab stops spinning.
### did-get-redirect-request
* `oldUrl` String
* `newUrl` String
* `isMainFrame` Boolean
Fired when a redirect was received while requesting a resource.
### console-message
* `level` Integer
* `message` String
* `line` Integer
* `sourceId` String
Fired when the guest window logs a console message.
The following example code forwards all log messages to the embedder's console
without regard for log level or other properties.
```javascript
webview.addEventListener('console-message', function(e) {
console.log('Guest page logged a message: ', e.message);
});
```
### new-window
* `url` String
* `partitionId` String
Fired when the guest page attempts to open a new browser window.
The following example code opens the new url in system's default browser.
```javascript
webview.addEventListener('new-window', function(e) {
require('shell').openExternal(e.url);
});
```
### close
Fired when the guest window attempts to close itself.
The following example code navigates the `webview` to `about:blank` when the
guest attempts to close itself.
```javascript
webview.addEventListener('close', function() {
webview.src = 'about:blank';
});
```
### crashed
Fired when the renderer process is crashed.
### destroyed
Fired when the WebContents is destroyed.

View file

@ -4,7 +4,7 @@ import platform
import sys import sys
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = '56984fa0e4c3c745652510f342c0fb2724d846c2' LIBCHROMIUMCONTENT_COMMIT = '2dfdf169b582e3f051e1fec3dd7df2bc179e1aa6'
ARCH = { ARCH = {
'cygwin': '32bit', 'cygwin': '32bit',

View file

@ -387,7 +387,6 @@ describe 'asar package', ->
w = new BrowserWindow(show: false, width: 400, height: 400) w = new BrowserWindow(show: false, width: 400, height: 400)
p = path.resolve fixtures, 'asar', 'web.asar', 'index.html' p = path.resolve fixtures, 'asar', 'web.asar', 'index.html'
u = url.format protocol: 'asar', slashed: false, pathname: p u = url.format protocol: 'asar', slashed: false, pathname: p
console.log u
w.loadUrl u w.loadUrl u
ipc.on 'dirname', (event, dirname) -> ipc.on 'dirname', (event, dirname) ->
assert.equal dirname, path.dirname(p) assert.equal dirname, path.dirname(p)

View file

@ -37,34 +37,6 @@ describe 'chromium feature', ->
assert.equal b.constructor.name, 'BrowserWindow' assert.equal b.constructor.name, 'BrowserWindow'
b.destroy() b.destroy()
describe 'iframe', ->
page = path.join fixtures, 'pages', 'change-parent.html'
beforeEach ->
global.changedByIframe = false
it 'can not modify parent by default', (done) ->
iframe = $('<iframe>')
iframe.hide()
iframe.attr 'src', "file://#{page}"
iframe.appendTo 'body'
isChanged = ->
iframe.remove()
assert.equal global.changedByIframe, false
done()
setTimeout isChanged, 30
it 'can modify parent when sanbox is set to none', (done) ->
iframe = $('<iframe sandbox="none">')
iframe.hide()
iframe.attr 'src', "file://#{page}"
iframe.appendTo 'body'
isChanged = ->
iframe.remove()
assert.equal global.changedByIframe, true
done()
setTimeout isChanged, 30
describe 'creating a Uint8Array under browser side', -> describe 'creating a Uint8Array under browser side', ->
it 'does not crash', -> it 'does not crash', ->
RUint8Array = require('remote').getGlobal 'Uint8Array' RUint8Array = require('remote').getGlobal 'Uint8Array'

View file

@ -1,7 +1,7 @@
<html> <html>
<body> <body>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
window.parent.changedByIframe = true; console.log('a');
</script> </script>
</body> </body>
</html> </html>

8
spec/fixtures/pages/b.html vendored Normal file
View file

@ -0,0 +1,8 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
console.log('b');
</script>
</body>
</html>

7
spec/fixtures/pages/c.html vendored Normal file
View file

@ -0,0 +1,7 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
console.log([typeof require, typeof module, typeof process].join(' '));
</script>
</body>
</html>

7
spec/fixtures/pages/d.html vendored Normal file
View file

@ -0,0 +1,7 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
console.log([typeof require, typeof module, typeof process].join(' '));
</script>
</body>
</html>

48
spec/webview-spec.coffee Normal file
View file

@ -0,0 +1,48 @@
assert = require 'assert'
path = require 'path'
describe '<webview> tag', ->
fixtures = path.join __dirname, 'fixtures'
webview = null
beforeEach ->
webview = new WebView
afterEach ->
document.body.removeChild webview
describe 'src attribute', ->
it 'specifies the page to load', (done) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'a'
done()
webview.src = "file://#{fixtures}/pages/a.html"
document.body.appendChild webview
it 'navigates to new page when changed', (done) ->
listener = (e) ->
webview.src = "file://#{fixtures}/pages/b.html"
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'b'
done()
webview.removeEventListener 'did-finish-load', listener
webview.addEventListener 'did-finish-load', listener
webview.src = "file://#{fixtures}/pages/a.html"
document.body.appendChild webview
describe 'nodeintegration attribute', ->
it 'inserts no node symbols when not set', (done) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'undefined undefined undefined'
done()
webview.src = "file://#{fixtures}/pages/c.html"
document.body.appendChild webview
it 'inserts node symbols when set', (done) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'function object object'
done()
webview.setAttribute 'nodeintegration', 'on'
webview.src = "file://#{fixtures}/pages/d.html"
document.body.appendChild webview