diff --git a/atom.gyp b/atom.gyp index 7b439cbbfe1..01993ad8f15 100644 --- a/atom.gyp +++ b/atom.gyp @@ -29,6 +29,7 @@ 'atom/browser/api/lib/tray.coffee', 'atom/browser/api/lib/web-contents.coffee', 'atom/browser/lib/chrome-extension.coffee', + 'atom/browser/lib/guest-view-manager.coffee', 'atom/browser/lib/init.coffee', 'atom/browser/lib/objects-registry.coffee', 'atom/browser/lib/rpc-server.coffee', @@ -41,12 +42,14 @@ 'atom/common/lib/init.coffee', 'atom/common/lib/asar.coffee', 'atom/renderer/lib/chrome-api.coffee', + 'atom/renderer/lib/guest-view-internal.coffee', 'atom/renderer/lib/init.coffee', 'atom/renderer/lib/inspector.coffee', 'atom/renderer/lib/override.coffee', + 'atom/renderer/lib/web-view.coffee', 'atom/renderer/api/lib/ipc.coffee', 'atom/renderer/api/lib/remote.coffee', - 'atom/renderer/api/lib/web-view.coffee', + 'atom/renderer/api/lib/web-frame.coffee', ], 'lib_sources': [ 'atom/app/atom_content_client.cc', @@ -100,8 +103,6 @@ 'atom/browser/atom_browser_main_parts_mac.mm', 'atom/browser/atom_javascript_dialog_manager.cc', '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.h', 'atom/browser/browser.cc', @@ -178,6 +179,10 @@ 'atom/browser/ui/win/notify_icon.h', 'atom/browser/ui/x/x_window_utils.cc', '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.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.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/image_converter.cc', 'atom/common/native_mate_converters/image_converter.h', @@ -248,8 +254,8 @@ 'atom/renderer/api/atom_api_renderer_ipc.cc', 'atom/renderer/api/atom_renderer_bindings.cc', 'atom/renderer/api/atom_renderer_bindings.h', - 'atom/renderer/api/atom_api_web_view.cc', - 'atom/renderer/api/atom_api_web_view.h', + 'atom/renderer/api/atom_api_web_frame.cc', + 'atom/renderer/api/atom_api_web_frame.h', 'atom/renderer/atom_render_view_observer.cc', 'atom/renderer/atom_render_view_observer.h', 'atom/renderer/atom_renderer_client.cc', diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index a56688c616d..5487234eb14 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -4,7 +4,9 @@ #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/native_mate_converters/gfx_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/value_converter.h" @@ -12,16 +14,118 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_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 "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" +#include "atom/common/node_includes.h" + namespace atom { namespace api { +namespace { + +v8::Persistent template_; + +} // namespace + WebContents::WebContents(content::WebContents* 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) { @@ -47,6 +151,16 @@ void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host, 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) { Emit("did-start-loading"); } @@ -55,6 +169,17 @@ void WebContents::DidStopLoading(content::RenderViewHost* render_view_host) { 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 handled = true; IPC_BEGIN_MESSAGE_MAP(WebContents, message) @@ -67,12 +192,88 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) { 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() { // The RenderViewDeleted was not called when the WebContents is destroyed. - RenderViewDeleted(web_contents_->GetRenderViewHost()); + RenderViewDeleted(web_contents()->GetRenderViewHost()); 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& 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 { return web_contents() != NULL; } @@ -155,6 +356,14 @@ bool WebContents::IsCrashed() const { 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) { 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)); } +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( v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("isAlive", &WebContents::IsAlive) - .SetMethod("loadUrl", &WebContents::LoadURL) - .SetMethod("getUrl", &WebContents::GetURL) - .SetMethod("getTitle", &WebContents::GetTitle) - .SetMethod("isLoading", &WebContents::IsLoading) - .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) - .SetMethod("stop", &WebContents::Stop) - .SetMethod("reload", &WebContents::Reload) - .SetMethod("reloadIgnoringCache", &WebContents::ReloadIgnoringCache) - .SetMethod("canGoBack", &WebContents::CanGoBack) - .SetMethod("canGoForward", &WebContents::CanGoForward) - .SetMethod("canGoToOffset", &WebContents::CanGoToOffset) - .SetMethod("goBack", &WebContents::GoBack) - .SetMethod("goForward", &WebContents::GoForward) - .SetMethod("goToIndex", &WebContents::GoToIndex) - .SetMethod("goToOffset", &WebContents::GoToOffset) - .SetMethod("getRoutingId", &WebContents::GetRoutingID) - .SetMethod("getProcessId", &WebContents::GetProcessID) - .SetMethod("isCrashed", &WebContents::IsCrashed) - .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) - .SetMethod("_send", &WebContents::SendIPCMessage); + if (template_.IsEmpty()) + template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) + .SetMethod("destroy", &WebContents::Destroy) + .SetMethod("isAlive", &WebContents::IsAlive) + .SetMethod("loadUrl", &WebContents::LoadURL) + .SetMethod("getUrl", &WebContents::GetURL) + .SetMethod("getTitle", &WebContents::GetTitle) + .SetMethod("isLoading", &WebContents::IsLoading) + .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) + .SetMethod("stop", &WebContents::Stop) + .SetMethod("reload", &WebContents::Reload) + .SetMethod("reloadIgnoringCache", &WebContents::ReloadIgnoringCache) + .SetMethod("canGoBack", &WebContents::CanGoBack) + .SetMethod("canGoForward", &WebContents::CanGoForward) + .SetMethod("canGoToOffset", &WebContents::CanGoToOffset) + .SetMethod("goBack", &WebContents::GoBack) + .SetMethod("goForward", &WebContents::GoForward) + .SetMethod("goToIndex", &WebContents::GoToIndex) + .SetMethod("goToOffset", &WebContents::GoToOffset) + .SetMethod("getRoutingId", &WebContents::GetRoutingID) + .SetMethod("getProcessId", &WebContents::GetProcessID) + .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::New(isolate, template_)); } void WebContents::OnRendererMessage(const base::string16& channel, const base::ListValue& 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, @@ -203,12 +460,42 @@ void WebContents::OnRendererMessageSync(const base::string16& channel, 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 -mate::Handle WebContents::Create( +mate::Handle WebContents::CreateFrom( v8::Isolate* isolate, content::WebContents* web_contents) { return mate::CreateHandle(isolate, new WebContents(web_contents)); } +// static +mate::Handle WebContents::Create( + v8::Isolate* isolate, const mate::Dictionary& options) { + return mate::CreateHandle(isolate, new WebContents(options)); +} + } // namespace api } // namespace atom + + +namespace { + +void Initialize(v8::Handle exports, v8::Handle unused, + v8::Handle 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) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 145cd5aba9a..1604d7db331 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -5,20 +5,36 @@ #ifndef ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ #define ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ +#include + #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 "native_mate/handle.h" +namespace mate { +class Dictionary; +} + namespace atom { namespace api { class WebContents : public mate::EventEmitter, + public content::BrowserPluginGuestDelegate, + public content::WebContentsDelegate, public content::WebContentsObserver { public: - static mate::Handle Create(v8::Isolate* isolate, - content::WebContents* web_contents); + // Create from an existing WebContents. + static mate::Handle CreateFrom( + v8::Isolate* isolate, content::WebContents* web_contents); + // Create a new WebContents. + static mate::Handle Create( + v8::Isolate* isolate, const mate::Dictionary& options); + + void Destroy(); bool IsAlive() const; void LoadURL(const GURL& url); GURL GetURL() const; @@ -38,28 +54,98 @@ class WebContents : public mate::EventEmitter, int GetRoutingID() const; int GetProcessID() const; bool IsCrashed() const; + void SetUserAgent(const std::string& user_agent); + void InsertCSS(const std::string& css); void ExecuteJavaScript(const base::string16& code); bool SendIPCMessage(const base::string16& channel, const base::ListValue& args); + // Toggles autosize mode for corresponding . + 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: explicit WebContents(content::WebContents* web_contents); + explicit WebContents(const mate::Dictionary& options); + ~WebContents(); - // mate::Wrappable implementations: + // mate::Wrappable: virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) OVERRIDE; + v8::Isolate* isolate) override; - // content::WebContentsObserver implementations: - virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE; - virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + // content::WebContentsDelegate: + bool AddMessageToConsole(content::WebContents* source, + 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, - 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( - content::RenderViewHost* render_view_host) OVERRIDE; + content::RenderViewHost* render_view_host) override; virtual void DidStopLoading( - content::RenderViewHost* render_view_host) OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - virtual void WebContentsDestroyed() OVERRIDE; + content::RenderViewHost* render_view_host) override; + virtual void DidGetRedirectForResourceRequest( + 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& callback) override; + virtual void RegisterDestructionCallback( + const DestructionCallback& callback) override; private: // Called when received a message from renderer. @@ -71,7 +157,44 @@ class WebContents : public mate::EventEmitter, const base::ListValue& args, 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 extra_params_; + + // Stores the WebContents that managed by this class. + scoped_ptr 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); }; diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 7bfffff1a5b..9be2862c190 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -6,13 +6,11 @@ #include "atom/browser/api/atom_api_web_contents.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 "native_mate/callback.h" #include "native_mate/constructor.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" @@ -40,23 +38,6 @@ struct Converter { } }; -template<> -struct Converter { - static bool FromV8(v8::Isolate* isolate, - v8::Handle 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 atom { @@ -375,12 +356,12 @@ void Window::SetProgressBar(double progress) { } mate::Handle Window::GetWebContents(v8::Isolate* isolate) const { - return WebContents::Create(isolate, window_->GetWebContents()); + return WebContents::CreateFrom(isolate, window_->GetWebContents()); } mate::Handle Window::GetDevToolsWebContents( v8::Isolate* isolate) const { - return WebContents::Create(isolate, window_->GetDevToolsWebContents()); + return WebContents::CreateFrom(isolate, window_->GetDevToolsWebContents()); } // static diff --git a/atom/browser/api/event_emitter.cc b/atom/browser/api/event_emitter.cc index 89a95bb212d..768ab5f5876 100644 --- a/atom/browser/api/event_emitter.cc +++ b/atom/browser/api/event_emitter.cc @@ -4,8 +4,6 @@ #include "atom/browser/api/event_emitter.h" -#include - #include "atom/browser/api/event.h" #include "atom/common/native_mate_converters/v8_value_converter.h" #include "base/memory/scoped_ptr.h" @@ -64,6 +62,23 @@ bool EventEmitter::Emit(const base::StringPiece& name, v8::Handle context = isolate->GetCurrentContext(); scoped_ptr 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 event; bool use_native_event = sender && message; @@ -75,20 +90,13 @@ bool EventEmitter::Emit(const base::StringPiece& name, event = CreateEventObject(isolate); } - // v8_args = [name, event, args...]; - std::vector> v8_args; - v8_args.reserve(args.GetSize() + 2); - 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)); - } + // args = [name, event, args...]; + args.insert(args.begin(), event); + args.insert(args.begin(), mate::StringToV8(isolate, name)); - // this.emit.apply(this, v8_args); - node::MakeCallback(isolate, GetWrapper(isolate), "emit", v8_args.size(), - &v8_args[0]); + // this.emit.apply(this, args); + node::MakeCallback(isolate, GetWrapper(isolate), "emit", args.size(), + &args[0]); if (use_native_event) { Handle native_event; diff --git a/atom/browser/api/event_emitter.h b/atom/browser/api/event_emitter.h index 03af9319d04..239dc2a5105 100644 --- a/atom/browser/api/event_emitter.h +++ b/atom/browser/api/event_emitter.h @@ -5,6 +5,8 @@ #ifndef ATOM_BROWSER_API_EVENT_EMITTER_H_ #define ATOM_BROWSER_API_EVENT_EMITTER_H_ +#include + #include "native_mate/wrappable.h" namespace base { @@ -23,6 +25,9 @@ namespace mate { // Provide helperers to emit event in JavaScript. class EventEmitter : public Wrappable { + public: + typedef std::vector> Arguments; + protected: EventEmitter(); @@ -36,6 +41,13 @@ class EventEmitter : public Wrappable { bool Emit(const base::StringPiece& name, const base::ListValue& args, 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: DISALLOW_COPY_AND_ASSIGN(EventEmitter); }; diff --git a/atom/browser/api/lib/web-contents.coffee b/atom/browser/api/lib/web-contents.coffee index 92e41cdbde1..fa4bed48692 100644 --- a/atom/browser/api/lib/web-contents.coffee +++ b/atom/browser/api/lib/web-contents.coffee @@ -1,4 +1,5 @@ EventEmitter = require('events').EventEmitter +binding = process.atomBinding 'web_contents' ipc = require 'ipc' module.exports.wrap = (webContents) -> @@ -31,12 +32,15 @@ module.exports.wrap = (webContents) -> process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', "#{processId}-#{routingId}" # 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 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, 'sender', value: webContents ipc.emit channel, event, args... webContents + +module.exports.create = (options={}) -> + @wrap binding.create(options) diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 22ff32ad219..ad5aaf9df7d 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -7,15 +7,17 @@ #include "atom/browser/atom_access_token_store.h" #include "atom/browser/atom_browser_context.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/native_window.h" +#include "atom/browser/web_view/web_view_renderer_state.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/speech/tts_message_filter.h" #include "content/public/browser/render_process_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/web_contents.h" #include "content/public/common/web_preferences.h" @@ -58,12 +60,6 @@ void AtomBrowserClient::RenderProcessWillLaunch( 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* AtomBrowserClient::GetSpeechRecognitionManagerDelegate() { return new AtomSpeechRecognitionManagerDelegate; @@ -80,7 +76,7 @@ void AtomBrowserClient::OverrideWebkitPrefs( prefs->javascript_enabled = true; prefs->web_security_enabled = true; prefs->javascript_can_open_windows_automatically = true; - prefs->plugins_enabled = false; + prefs->plugins_enabled = true; prefs->dom_paste_enabled = true; prefs->java_enabled = false; prefs->allow_scripts_to_close_windows = true; @@ -146,8 +142,20 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( window = *iter; } - if (window != NULL) + if (window != NULL) { 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; } diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 523b95143d0..86193172997 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -11,8 +11,6 @@ namespace atom { -class AtomResourceDispatcherHostDelegate; - class AtomBrowserClient : public brightray::BrowserClient { public: AtomBrowserClient(); @@ -22,7 +20,6 @@ class AtomBrowserClient : public brightray::BrowserClient { // content::ContentBrowserClient: virtual void RenderProcessWillLaunch( content::RenderProcessHost* host) OVERRIDE; - virtual void ResourceDispatcherHostCreated() OVERRIDE; virtual content::SpeechRecognitionManagerDelegate* GetSpeechRecognitionManagerDelegate() override; virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE; @@ -41,8 +38,6 @@ class AtomBrowserClient : public brightray::BrowserClient { virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts( const content::MainFunctionParams&) OVERRIDE; - scoped_ptr resource_dispatcher_delegate_; - // The render process which would be swapped out soon. content::RenderProcessHost* dying_render_process_; diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 4c4ac3d384c..ae3c6f70ae0 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -7,6 +7,7 @@ #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/net/atom_url_request_job_factory.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/worker_pool.h" #include "chrome/browser/browser_process.h" @@ -68,6 +69,12 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory( return top_job_factory.release(); } +content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { + if (!guest_manager_) + guest_manager_.reset(new WebViewManager(this)); + return guest_manager_.get(); +} + // static AtomBrowserContext* AtomBrowserContext::Get() { return static_cast( diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index 95db71ff699..4ed5e1c3bbd 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -12,6 +12,7 @@ class BrowserProcess; namespace atom { class AtomURLRequestJobFactory; +class WebViewManager; class AtomBrowserContext : public brightray::BrowserContext { public: @@ -21,17 +22,20 @@ class AtomBrowserContext : public brightray::BrowserContext { // Returns the browser context singleton. static AtomBrowserContext* Get(); - AtomURLRequestJobFactory* job_factory() const { return job_factory_; } - - protected: // brightray::URLRequestContextGetter::Delegate: virtual net::URLRequestJobFactory* CreateURLRequestJobFactory( 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: // A fake BrowserProcess object that used to feed the source code from chrome. scoped_ptr fake_browser_process_; + scoped_ptr guest_manager_; AtomURLRequestJobFactory* job_factory_; // Weak reference. diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc deleted file mode 100644 index 30ddca2ae9c..00000000000 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ /dev/null @@ -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 - -#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 diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.h b/atom/browser/atom_resource_dispatcher_host_delegate.h deleted file mode 100644 index 10e993bcf02..00000000000 --- a/atom/browser/atom_resource_dispatcher_host_delegate.h +++ /dev/null @@ -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_ diff --git a/atom/browser/lib/guest-view-manager.coffee b/atom/browser/lib/guest-view-manager.coffee new file mode 100644 index 00000000000..b2827ad54d0 --- /dev/null +++ b/atom/browser/lib/guest-view-manager.coffee @@ -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 diff --git a/atom/browser/lib/init.coffee b/atom/browser/lib/init.coffee index 7bf9e4e1390..90d52ab4f56 100644 --- a/atom/browser/lib/init.coffee +++ b/atom/browser/lib/init.coffee @@ -23,9 +23,8 @@ process.argv.splice startMark, endMark - startMark + 1 globalPaths = module.globalPaths globalPaths.push path.join process.resourcesPath, 'atom', 'browser', 'api', 'lib' -# Do loading in next tick since we still need some initialize work before -# native bindings can work. -setImmediate -> +# Following operations need extra bindings by AtomBindings. +process.once 'BIND_DONE', -> # Import common settings. require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') @@ -61,6 +60,9 @@ setImmediate -> # Load the RPC server. require './rpc-server.js' + # Load the guest view manager. + require './guest-view-manager.js' + # Now we try to load app's package.json. packageJson = null diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index 95d30f366a0..1703f1e2475 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -94,11 +94,15 @@ ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) -> catch e event.returnValue = errorToMeta e -ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) -> +ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, guestInstanceId) -> try BrowserWindow = require 'browser-window' - window = BrowserWindow.fromWebContents event.sender - window = BrowserWindow.fromDevToolsWebContents event.sender unless window? + if guestInstanceId? + 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 catch 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) -> 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 diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index dfd58904963..71eb65112d1 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -84,7 +84,7 @@ NativeWindow::NativeWindow(content::WebContents* web_contents, has_frame_(true), enable_larger_than_screen_(false), is_closed_(false), - node_integration_("except-iframe"), + node_integration_(true), has_dialog_attached_(false), zoom_factor_(1.0), weak_factory_(this), @@ -94,12 +94,16 @@ NativeWindow::NativeWindow(content::WebContents* web_contents, options.Get(switches::kFrame, &has_frame_); options.Get(switches::kEnableLargerThanScreen, &enable_larger_than_screen_); + options.Get(switches::kNodeIntegration, &node_integration_); // Read icon before window is created. options.Get(switches::kIcon, &icon_); - // Read iframe security before any navigation. - options.Get(switches::kNodeIntegration, &node_integration_); + // Be compatible with old API of "node-integration" option. + std::string old_string_token; + if (options.Get(switches::kNodeIntegration, &old_string_token) && + old_string_token != "disable") + node_integration_ = true; // Read the web preferences. options.Get(switches::kWebPreferences, &web_preferences_); @@ -341,7 +345,7 @@ void NativeWindow::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int child_process_id) { // Append --node-integration to renderer process. command_line->AppendSwitchASCII(switches::kNodeIntegration, - node_integration_); + node_integration_ ? "true" : "false"); // Append --zoom-factor. if (zoom_factor_ != 1.0) @@ -358,6 +362,10 @@ void NativeWindow::AppendExtraCommandLineSwitches( command_line->AppendSwitch(::switches::kDisableDirectWrite); #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 // them via command line and enable them in renderer procss. for (size_t i = 0; i < arraysize(kWebRuntimeFeatures); ++i) { @@ -388,8 +396,6 @@ void NativeWindow::OverrideWebkitPrefs(const GURL& url, prefs->experimental_webgl_enabled = b; if (web_preferences_.Get("webaudio", &b)) prefs->webaudio_enabled = b; - if (web_preferences_.Get("plugins", &b)) - prefs->plugins_enabled = b; if (web_preferences_.Get("extra-plugin-dirs", &list)) for (size_t i = 0; i < list.size(); ++i) content::PluginService::GetInstance()->AddExtraPluginDir(list[i]); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 2b9fbfa09b4..9accb93f0cd 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -288,8 +288,8 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, // The windows has been closed. bool is_closed_; - // The security token of iframe. - std::string node_integration_; + // Whether node integration is enabled. + bool node_integration_; // There is a dialog that has been attached to window. bool has_dialog_attached_; diff --git a/atom/browser/web_view/web_view_manager.cc b/atom/browser/web_view/web_view_manager.cc new file mode 100644 index 00000000000..edc51d5bdfa --- /dev/null +++ b/atom/browser/web_view/web_view_manager.cc @@ -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 { + static bool FromV8(v8::Isolate* isolate, v8::Handle val, + content::WebContents** out) { + atom::api::WebContents* contents; + if (!Converter::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 exports, v8::Handle unused, + v8::Handle context, void* priv) { + using atom::WebViewManager; + auto manager = static_cast( + 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) diff --git a/atom/browser/web_view/web_view_manager.h b/atom/browser/web_view/web_view_manager.h new file mode 100644 index 00000000000..058db0ca317 --- /dev/null +++ b/atom/browser/web_view/web_view_manager.h @@ -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 + +#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 web_contents_map_; + + DISALLOW_COPY_AND_ASSIGN(WebViewManager); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_ diff --git a/atom/browser/web_view/web_view_renderer_state.cc b/atom/browser/web_view/web_view_renderer_state.cc new file mode 100644 index 00000000000..0c0e455f5cf --- /dev/null +++ b/atom/browser/web_view/web_view_renderer_state.cc @@ -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::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 diff --git a/atom/browser/web_view/web_view_renderer_state.h b/atom/browser/web_view/web_view_renderer_state.h new file mode 100644 index 00000000000..fd402b775f0 --- /dev/null +++ b/atom/browser/web_view/web_view_renderer_state.h @@ -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 +#include +#include + +#include "base/memory/singleton.h" + +namespace atom { + +class WebViewManager; + +// This class keeps track of 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 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; + + typedef std::map WebViewInfoMap; + + WebViewRendererState(); + ~WebViewRendererState(); + + // Adds or removes a 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_ diff --git a/atom/common/api/atom_api_screen.cc b/atom/common/api/atom_api_screen.cc index 2a10390a63b..25ac5395bdd 100644 --- a/atom/common/api/atom_api_screen.cc +++ b/atom/common/api/atom_api_screen.cc @@ -2,61 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "native_mate/dictionary.h" -#include "ui/gfx/screen.h" - +#include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/node_includes.h" -namespace mate { - -template<> -struct Converter { - static v8::Handle 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 { - static v8::Handle 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 { - static v8::Handle 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 { - static v8::Handle 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 { void Initialize(v8::Handle exports, v8::Handle unused, diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index 8fdc6d03ce2..32f87b83c3c 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -85,6 +85,9 @@ void AtomBindings::BindTo(v8::Isolate* isolate, versions->Set(mate::StringToV8(isolate, "chrome"), mate::StringToV8(isolate, CHROME_VERSION_STRING)); } + + v8::Handle event = mate::StringToV8(isolate, "BIND_DONE"); + node::MakeCallback(isolate, process, "emit", 1, &event); } void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) { diff --git a/atom/common/native_mate_converters/gfx_converter.h b/atom/common/native_mate_converters/gfx_converter.h new file mode 100644 index 00000000000..1dc913c58b4 --- /dev/null +++ b/atom/common/native_mate_converters/gfx_converter.h @@ -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 { + static v8::Handle 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 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 { + static v8::Handle 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 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 { + static v8::Handle 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 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 { + static v8::Handle 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_ diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index a6f3659a227..8d290271139 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -68,6 +68,7 @@ REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_protocol); REFERENCE_MODULE(atom_browser_global_shortcut); REFERENCE_MODULE(atom_browser_tray); +REFERENCE_MODULE(atom_browser_web_contents); REFERENCE_MODULE(atom_browser_window); REFERENCE_MODULE(atom_common_asar); REFERENCE_MODULE(atom_common_clipboard); @@ -77,7 +78,7 @@ REFERENCE_MODULE(atom_common_screen); REFERENCE_MODULE(atom_common_shell); REFERENCE_MODULE(atom_common_v8_util); REFERENCE_MODULE(atom_renderer_ipc); -REFERENCE_MODULE(atom_renderer_web_view); +REFERENCE_MODULE(atom_renderer_web_frame); #undef REFERENCE_MODULE namespace atom { diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 3d37a7d87ea..4b552cce648 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -60,6 +60,12 @@ const char kDarkTheme[] = "dark-theme"; // Enable DirectWrite on Windows. 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. const char kExperimentalFeatures[] = "experimental-features"; const char kExperimentalCanvasFeatures[] = "experimental-canvas-features"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index a9befacbae9..b218aaddc2f 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -36,6 +36,8 @@ extern const char kAutoHideMenuBar[]; extern const char kEnableLargerThanScreen[]; extern const char kDarkTheme[]; extern const char kDirectWrite[]; +extern const char kEnablePlugins[]; +extern const char kGuestInstanceID[]; extern const char kExperimentalFeatures[]; extern const char kExperimentalCanvasFeatures[]; diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc new file mode 100644 index 00000000000..00085ee9ab3 --- /dev/null +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -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 WebFrame::RegisterEmbedderCustomElement( + const base::string16& name, v8::Handle 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::Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new WebFrame); +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Handle exports, v8::Handle unused, + v8::Handle 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) diff --git a/atom/renderer/api/atom_api_web_view.h b/atom/renderer/api/atom_api_web_frame.h similarity index 51% rename from atom/renderer/api/atom_api_web_view.h rename to atom/renderer/api/atom_api_web_frame.h index be6012e84dd..b237c5e0f00 100644 --- a/atom/renderer/api/atom_api_web_view.h +++ b/atom/renderer/api/atom_api_web_frame.h @@ -2,44 +2,51 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ -#define ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ +#ifndef ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ +#define ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ + +#include #include "native_mate/handle.h" #include "native_mate/wrappable.h" namespace blink { -class WebView; +class WebLocalFrame; } namespace atom { namespace api { -class WebView : public mate::Wrappable { +class WebFrame : public mate::Wrappable { public: - static mate::Handle Create(v8::Isolate* isolate); + static mate::Handle Create(v8::Isolate* isolate); private: - WebView(); - virtual ~WebView(); + WebFrame(); + virtual ~WebFrame(); + + void SetName(const std::string& name); double SetZoomLevel(double level); double GetZoomLevel() const; double SetZoomFactor(double factor); double GetZoomFactor() const; + v8::Handle RegisterEmbedderCustomElement( + const base::string16& name, v8::Handle options); + // mate::Wrappable: virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate); - blink::WebView* web_view_; + blink::WebLocalFrame* web_frame_; - DISALLOW_COPY_AND_ASSIGN(WebView); + DISALLOW_COPY_AND_ASSIGN(WebFrame); }; } // namespace api } // namespace atom -#endif // ATOM_RENDERER_API_ATOM_API_WEB_VIEW_H_ +#endif // ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ diff --git a/atom/renderer/api/atom_api_web_view.cc b/atom/renderer/api/atom_api_web_view.cc deleted file mode 100644 index defa8237ec1..00000000000 --- a/atom/renderer/api/atom_api_web_view.cc +++ /dev/null @@ -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::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new WebView); -} - -} // namespace api - -} // namespace atom - -namespace { - -void Initialize(v8::Handle exports, v8::Handle unused, - v8::Handle 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) diff --git a/atom/renderer/api/lib/ipc.coffee b/atom/renderer/api/lib/ipc.coffee index 3d4ec30ba35..cf9e7af18ae 100644 --- a/atom/renderer/api/lib/ipc.coffee +++ b/atom/renderer/api/lib/ipc.coffee @@ -1,4 +1,5 @@ EventEmitter = require('events').EventEmitter +process = global.process ipc = process.atomBinding('ipc') class Ipc extends EventEmitter diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index 07b06001038..0f45790915a 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -1,6 +1,7 @@ +process = global.process ipc = require 'ipc' -CallbacksRegistry = require 'callbacks-registry' v8Util = process.atomBinding 'v8_util' +CallbacksRegistry = require 'callbacks-registry' callbacksRegistry = new CallbacksRegistry @@ -110,7 +111,7 @@ exports.require = (module) -> windowCache = null exports.getCurrentWindow = -> return windowCache if windowCache? - meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW' + meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId windowCache = metaToValue meta # Get a global object in browser. @@ -129,3 +130,8 @@ exports.createFunctionWithReturnValue = (returnValue) -> func = -> returnValue v8Util.setHiddenValue func, 'returnValue', true func + +# Get the guest WebContents from guestInstanceId. +exports.getGuestWebContents = (guestInstanceId) -> + meta = ipc.sendChannelSync 'ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId + metaToValue meta diff --git a/atom/renderer/api/lib/web-frame.coffee b/atom/renderer/api/lib/web-frame.coffee new file mode 100644 index 00000000000..6525730e86f --- /dev/null +++ b/atom/renderer/api/lib/web-frame.coffee @@ -0,0 +1 @@ +module.exports = process.atomBinding('web_frame').webFrame diff --git a/atom/renderer/api/lib/web-view.coffee b/atom/renderer/api/lib/web-view.coffee deleted file mode 100644 index a02a16663ab..00000000000 --- a/atom/renderer/api/lib/web-view.coffee +++ /dev/null @@ -1 +0,0 @@ -module.exports = process.atomBinding('web_view').webView diff --git a/atom/renderer/atom_render_view_observer.cc b/atom/renderer/atom_render_view_observer.cc index 1ec21ef1f6a..e4e6c1a1620 100644 --- a/atom/renderer/atom_render_view_observer.cc +++ b/atom/renderer/atom_render_view_observer.cc @@ -76,13 +76,6 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { void AtomRenderViewObserver::OnBrowserMessage(const base::string16& channel, 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( render_view(), channel, args); } diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 751074165dd..59316c22190 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -13,13 +13,15 @@ #include "atom/renderer/atom_render_view_observer.h" #include "chrome/renderer/printing/print_web_view_helper.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_observer.h" #include "content/public/renderer/render_thread.h" #include "base/command_line.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/WebPluginParams.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" @@ -29,13 +31,6 @@ namespace atom { 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, const char* switch_string, bool* enabled) { @@ -73,24 +68,9 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver { } // namespace AtomRendererClient::AtomRendererClient() - : node_integration_(EXCEPT_IFRAME), + : node_bindings_(NodeBindings::Create(false)), + atom_bindings_(new AtomRendererBindings), 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() { @@ -99,8 +79,8 @@ AtomRendererClient::~AtomRendererClient() { void AtomRendererClient::WebKitInitialized() { EnableWebRuntimeFeatures(); - if (!IsNodeBindingEnabled()) - return; + blink::WebCustomElement::addEmbedderCustomElementName("webview"); + blink::WebCustomElement::addEmbedderCustomElementName("browserplugin"); node_bindings_->Initialize(); node_bindings_->PrepareMessageLoop(); @@ -134,6 +114,20 @@ blink::WebSpeechSynthesizer* AtomRendererClient::OverrideSpeechSynthesizer( 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, v8::Handle context, int extension_group, @@ -142,9 +136,6 @@ void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame, if (main_frame_ == NULL) main_frame_ = frame; - if (!IsNodeBindingEnabled(frame)) - return; - v8::Context::Scope scope(context); // Check the existance of process object to prevent duplicate initialization. @@ -173,9 +164,6 @@ void AtomRendererClient::WillReleaseScriptContext( blink::WebFrame* frame, v8::Handle context, int world_id) { - if (!IsNodeBindingEnabled(frame)) - return; - node::Environment* env = node::Environment::GetCurrent(context); if (env == NULL) { 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_server_redirect, 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. // FIXME We only support GET here because http method will be ignored when // 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"; } -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() { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); bool b; diff --git a/atom/renderer/atom_renderer_client.h b/atom/renderer/atom_renderer_client.h index f1c3b79a020..7761dcbcd02 100644 --- a/atom/renderer/atom_renderer_client.h +++ b/atom/renderer/atom_renderer_client.h @@ -26,8 +26,6 @@ class AtomRendererClient : public content::ContentRendererClient, AtomRendererClient(); virtual ~AtomRendererClient(); - bool IsNodeBindingEnabled(blink::WebFrame* frame = NULL); - // Forwarded by RenderFrameObserver. void WillReleaseScriptContext(blink::WebFrame* frame, v8::Handle context, @@ -47,21 +45,25 @@ class AtomRendererClient : public content::ContentRendererClient, virtual void WebKitInitialized() OVERRIDE; // content::ContentRendererClient: - virtual void RenderThreadStarted() OVERRIDE; - virtual void RenderFrameCreated(content::RenderFrame* render_frame) OVERRIDE; - virtual void RenderViewCreated(content::RenderView*) OVERRIDE; - virtual blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer( - blink::WebSpeechSynthesizerClient* client); - virtual void DidCreateScriptContext(blink::WebFrame* frame, - v8::Handle context, - int extension_group, - int world_id) OVERRIDE; - virtual bool ShouldFork(blink::WebFrame* frame, - const GURL& url, - const std::string& http_method, - bool is_initial_navigation, - bool is_server_redirect, - bool* send_referrer) OVERRIDE; + void RenderThreadStarted() override; + void RenderFrameCreated(content::RenderFrame* render_frame) override; + void RenderViewCreated(content::RenderView*) override; + blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer( + blink::WebSpeechSynthesizerClient* client) override; + bool OverrideCreatePlugin(content::RenderFrame* render_frame, + blink::WebLocalFrame* frame, + const blink::WebPluginParams& params, + blink::WebPlugin** plugin) override; + void DidCreateScriptContext(blink::WebFrame* frame, + v8::Handle context, + int extension_group, + int world_id) override; + bool ShouldFork(blink::WebFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect, + bool* send_referrer) override; void EnableWebRuntimeFeatures(); @@ -70,9 +72,6 @@ class AtomRendererClient : public content::ContentRendererClient, scoped_ptr node_bindings_; scoped_ptr atom_bindings_; - // The level of node integration we should support. - NodeIntegration node_integration_; - // The main frame. blink::WebFrame* main_frame_; diff --git a/atom/renderer/lib/guest-view-internal.coffee b/atom/renderer/lib/guest-view-internal.coffee new file mode 100644 index 00000000000..24a57ea1faf --- /dev/null +++ b/atom/renderer/lib/guest-view-internal.coffee @@ -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 diff --git a/atom/renderer/lib/init.coffee b/atom/renderer/lib/init.coffee index ac11ac0d341..69bb0ac7c4d 100644 --- a/atom/renderer/lib/init.coffee +++ b/atom/renderer/lib/init.coffee @@ -20,32 +20,16 @@ globalPaths.push path.join(process.resourcesPath, 'app') # Import common settings. require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') -# Expose global variables. -global.require = require -global.module = module - -# Emit the 'exit' event when page is unloading. -window.addEventListener 'unload', -> - process.emit 'exit' - -# 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 +# Process command line arguments. +nodeIntegration = 'false' +for arg in process.argv + if arg.indexOf('--guest-instance-id=') == 0 + # This is a guest web view. + process.guestInstanceId = parseInt arg.substr(arg.indexOf('=') + 1) + # Set the frame name to make AtomRendererClient recognize this guest. + require('web-frame').setName 'ATOM_SHELL_GUEST_WEB_VIEW' + else if arg.indexOf('--node-integration=') == 0 + nodeIntegration = arg.substr arg.indexOf('=') + 1 if location.protocol is 'chrome-devtools:' # Override some inspector APIs. @@ -56,3 +40,46 @@ else if location.protocol is 'chrome-extension:' else # Override default web functions. 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 diff --git a/atom/renderer/lib/override.coffee b/atom/renderer/lib/override.coffee index eedd468502c..d9ece6696bb 100644 --- a/atom/renderer/lib/override.coffee +++ b/atom/renderer/lib/override.coffee @@ -1,50 +1,43 @@ -# Redirect window.onerror to uncaughtException. -window.onerror = (error) -> - if global.process.listeners('uncaughtException').length > 0 - global.process.emit 'uncaughtException', error - true - else - false +process = global.process +remote = require 'remote' -# Override default window.close, see: -# https://github.com/atom/atom-shell/issues/70 -window.close = -> - require('remote').getCurrentWindow().close() +unless process.guestInstanceId? + # Override default window.close, see: + window.close = -> + remote.getCurrentWindow().close() -# Override default window.open. -window.open = (url, name, features) -> - options = {} - for feature in features.split ',' - [name, value] = feature.split '=' - options[name] = - if value is 'yes' - true - else if value is 'no' - false - else - value + # Override default window.open. + window.open = (url, name, features) -> + options = {} + for feature in features.split ',' + [name, value] = feature.split '=' + options[name] = + if value is 'yes' + true + else if value is 'no' + false + else + value - options.x ?= options.left - options.y ?= options.top - options.title ?= name - options.width ?= 800 - options.height ?= 600 + options.x ?= options.left + options.y ?= options.top + options.title ?= name + options.width ?= 800 + options.height ?= 600 - BrowserWindow = require('remote').require 'browser-window' - browser = new BrowserWindow options - browser.loadUrl url - browser + BrowserWindow = require('remote').require 'browser-window' + browser = new BrowserWindow options + browser.loadUrl url + browser # Use the dialog API to implement alert(). window.alert = (message, title='') -> - remote = require 'remote' dialog = remote.require 'dialog' buttons = ['OK'] dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons} # And the confirm(). window.confirm = (message, title='') -> - remote = require 'remote' dialog = remote.require 'dialog' buttons = ['OK', 'Cancel'] not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons} diff --git a/atom/renderer/lib/web-view.coffee b/atom/renderer/lib/web-view.coffee new file mode 100644 index 00000000000..8061f9b049c --- /dev/null +++ b/atom/renderer/lib/web-view.coffee @@ -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 = ': ' + + 'Script cannot be injected into content until the page has loaded.' +ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE = ': ' + + '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 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 element to the DOM. + reset: -> + # If guestInstanceId is defined then the 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 .request property. + setRequestPropertyOnWebViewNode: (request) -> + Object.defineProperty @webviewNode, 'request', value: request, enumerable: true + + setupFocusPropagation: -> + unless @webviewNode.hasAttribute 'tabIndex' + # needs a tabIndex in order to be focusable. + # TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute + # to allow to be focusable. + # See http://crbug.com/231664. + @webviewNode.setAttribute 'tabIndex', -1 + @webviewNode.addEventListener 'focus', (e) => + # Focus the BrowserPlugin when the takes focus. + @browserPluginNode.focus() + @webviewNode.addEventListener 'blur', (e) => + # Blur the BrowserPlugin when the 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 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 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 + # 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 has actually + # changed. + @dispatchEvent webViewEvent + + # Returns if 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' 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 custom element. +registerBrowserPluginElement = -> + proto = Object.create HTMLObjectElement.prototype + + proto.createdCallback = -> + @setAttribute 'type', 'application/browser-plugin' + @setAttribute 'id', 'browser-plugin-' + getNextId() + # The node fills in the 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 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 diff --git a/docs/README.md b/docs/README.md index d5d3468f760..3746adc069e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,6 +14,11 @@ * [Process object](api/process.md) * [Supported Chrome command line switches](api/chrome-command-line-switches.md) +Custom DOM elements: + +* [`File` object](app/file-object.md) +* [`` tag](app/web-view-tag.md) + Modules for browser side: * [app](api/app.md) @@ -33,7 +38,7 @@ Modules for web page: * [ipc (renderer)](api/ipc-renderer.md) * [remote](api/remote.md) -* [web-view](api/web-view.md) +* [web-frame](api/web-frame.md) Modules for both sides: @@ -47,8 +52,6 @@ Modules for both sides: * [Coding style](development/coding-style.md) * [Source code directory structure](development/source-code-directory-structure.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 (Windows)](development/build-instructions-windows.md) * [Build instructions (Linux)](development/build-instructions-linux.md) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 6d50ef1b469..dd0bd4bd06b 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -18,9 +18,6 @@ win.show(); You can also create a window without chrome by using [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 `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 * `frame` Boolean - Specify `false` to create a [Frameless Window](frameless-window.md) - * `node-integration` String - Default value is `except-iframe`, can also be - `all`, `manual-enable-iframe` or `disable`, see - [Web Security](web-security.md) for more informations. + * `node-integration` Boolean - Whether node integration is enabled, default + is `true` * `accept-first-mouse` Boolean - Whether the web view accepts a single mouse-down event that simultaneously activates the window * `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 [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). -### Event: 'crashed' - -Emitted when the renderer process is crashed. - ### Event: 'did-finish-load' 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' @@ -530,8 +531,29 @@ Emitted when a frame has done navigation. ### Event: 'did-start-loading' +Corresponds to the points in time when the spinner of the tab starts spinning. + ### 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) * `url` URL @@ -602,10 +624,22 @@ Navigates to the specified absolute index. Navigates to the specified offset from the "current entry". -### WebContents.IsCrashed() +### WebContents.isCrashed() 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) * `code` String diff --git a/docs/api/file-object.md b/docs/api/file-object.md new file mode 100644 index 00000000000..87be35ab510 --- /dev/null +++ b/docs/api/file-object.md @@ -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 +
+ Drag your file here +
+ + +``` diff --git a/docs/api/web-view.md b/docs/api/web-frame.md similarity index 65% rename from docs/api/web-view.md rename to docs/api/web-frame.md index 672830b12bb..1bd00e41d20 100644 --- a/docs/api/web-view.md +++ b/docs/api/web-frame.md @@ -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%. ```javascript -var webView = require('web-view'); -webView.setZoomFactor(2); +var webFrame = require('web-frame'); +webFrame.setZoomFactor(2); ``` -## webView.setZoomFactor(factor) +## webFrame.setZoomFactor(factor) * `factor` Number - Zoom factor Changes the zoom factor to the specified factor, zoom factor is zoom percent / 100, so 300% = 3.0. -## webView.getZoomFactor() +## webFrame.getZoomFactor() Returns the current zoom factor. -## webView.setZoomLevel(level) +## webFrame.setZoomLevel(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 limits of 300% and 50% of original size, respectively. -## webView.getZoomLevel() +## webFrame.getZoomLevel() Returns the current zoom level. diff --git a/docs/api/web-security.md b/docs/api/web-security.md deleted file mode 100644 index cdff3bc41eb..00000000000 --- a/docs/api/web-security.md +++ /dev/null @@ -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 - - - - -``` - -## 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 - -``` - -## 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 - - - - - -``` - -[x-frame-options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md new file mode 100644 index 00000000000..a7b908dee97 --- /dev/null +++ b/docs/api/web-view-tag.md @@ -0,0 +1,271 @@ +# `` 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 + +``` + +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 + +``` + +## Tag attributes + +### src + +```html + +``` + +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 + +``` + +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 + +``` + +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 + +### ``.getUrl() + +Returns URL of guest page. + +### ``.getTitle() + +Returns the title of guest page. + +### ``.isLoading() + +Returns whether guest page is still loading resources. + +### ``.isWaitingForResponse() + +Returns whether guest page is waiting for a first-response for the main resource +of the page. + +### ``.stop() + +Stops any pending navigation. + +### ``.reload() + +Reloads guest page. + +### ``.reloadIgnoringCache() + +Reloads guest page and ignores cache. + +### ``.canGoBack() + +Returns whether guest page can go back. + +### ``.canGoForward() + +Returns whether guest page can go forward. + +### ``.canGoToOffset(offset) + +* `offset` Integer + +Returns whether guest page can go to `offset`. + +### ``.goBack() + +Makes guest page go back. + +### ``.goForward() + +Makes guest page go forward. + +### ``.goToIndex(index) + +* `index` Integer + +Navigates to the specified absolute index. + +### ``.goToOffset(offset) + +* `offset` Integer + +Navigates to the specified offset from the "current entry". + +### ``.isCrashed() + +Whether the renderer process has crashed. + +### ``.setUserAgent(userAgent) + +* `userAgent` String + +Overrides the user agent for guest page. + +### ``.insertCSS(css) + +* `css` String + +Injects CSS into guest page. + +### ``.executeJavaScript(code) + +* `code` String + +Evaluate `code` in guest page. + +### ``.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. diff --git a/script/lib/config.py b/script/lib/config.py index 09ea75cbb8f..e1da281c4b1 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -4,7 +4,7 @@ import platform import sys BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '56984fa0e4c3c745652510f342c0fb2724d846c2' +LIBCHROMIUMCONTENT_COMMIT = '2dfdf169b582e3f051e1fec3dd7df2bc179e1aa6' ARCH = { 'cygwin': '32bit', diff --git a/spec/asar-spec.coffee b/spec/asar-spec.coffee index 2cfab885682..12cbff7989a 100644 --- a/spec/asar-spec.coffee +++ b/spec/asar-spec.coffee @@ -387,7 +387,6 @@ describe 'asar package', -> w = new BrowserWindow(show: false, width: 400, height: 400) p = path.resolve fixtures, 'asar', 'web.asar', 'index.html' u = url.format protocol: 'asar', slashed: false, pathname: p - console.log u w.loadUrl u ipc.on 'dirname', (event, dirname) -> assert.equal dirname, path.dirname(p) diff --git a/spec/chromium-spec.coffee b/spec/chromium-spec.coffee index 5bec7bf8a89..b7fd0bc9747 100644 --- a/spec/chromium-spec.coffee +++ b/spec/chromium-spec.coffee @@ -37,34 +37,6 @@ describe 'chromium feature', -> assert.equal b.constructor.name, 'BrowserWindow' 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 = $('