Merge pull request #234 from atom/api-web-contents

Separate out WebContents API to a new class
This commit is contained in:
Cheng Zhao 2014-04-25 09:41:58 +00:00
commit 3584d48dbe
30 changed files with 603 additions and 835 deletions

View file

@ -24,6 +24,7 @@
'atom/browser/api/lib/menu-item.coffee',
'atom/browser/api/lib/power-monitor.coffee',
'atom/browser/api/lib/protocol.coffee',
'atom/browser/api/lib/web-contents.coffee',
'atom/browser/lib/init.coffee',
'atom/browser/lib/objects-registry.coffee',
'atom/browser/lib/rpc-server.coffee',
@ -48,7 +49,6 @@
'atom/browser/api/atom_api_app.h',
'atom/browser/api/atom_api_auto_updater.cc',
'atom/browser/api/atom_api_auto_updater.h',
'atom/browser/api/atom_api_browser_ipc.cc',
'atom/browser/api/atom_api_dialog.cc',
'atom/browser/api/atom_api_menu.cc',
'atom/browser/api/atom_api_menu.h',
@ -62,10 +62,10 @@
'atom/browser/api/atom_api_power_monitor.h',
'atom/browser/api/atom_api_protocol.cc',
'atom/browser/api/atom_api_protocol.h',
'atom/browser/api/atom_api_web_contents.cc',
'atom/browser/api/atom_api_web_contents.h',
'atom/browser/api/atom_api_window.cc',
'atom/browser/api/atom_api_window.h',
'atom/browser/api/atom_browser_bindings.cc',
'atom/browser/api/atom_browser_bindings.h',
'atom/browser/api/event.cc',
'atom/browser/api/event.h',
'atom/browser/api/event_emitter.cc',
@ -93,8 +93,6 @@
'atom/browser/browser_observer.h',
'atom/browser/devtools_delegate.cc',
'atom/browser/devtools_delegate.h',
'atom/browser/devtools_web_contents_observer.cc',
'atom/browser/devtools_web_contents_observer.h',
'atom/browser/mac/atom_application.h',
'atom/browser/mac/atom_application.mm',
'atom/browser/mac/atom_application_delegate.h',

View file

@ -1,37 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/common/api/api_messages.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "content/public/browser/render_view_host.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_includes.h"
using content::RenderViewHost;
namespace {
bool Send(const string16& channel, int process_id, int routing_id,
const base::ListValue& arguments) {
RenderViewHost* render_view_host(RenderViewHost::FromID(
process_id, routing_id));
if (!render_view_host) {
node::ThrowError("Invalid render view host");
return false;
}
return render_view_host->Send(new AtomViewMsg_Message(routing_id, channel,
arguments));
}
void Initialize(v8::Handle<v8::Object> exports) {
mate::Dictionary dict(v8::Isolate::GetCurrent(), exports);
dict.SetMethod("send", &Send);
}
} // namespace
NODE_MODULE(atom_browser_ipc, Initialize)

View file

@ -0,0 +1,206 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/common/api/api_messages.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"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "native_mate/object_template_builder.h"
namespace atom {
namespace api {
WebContents::WebContents(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
web_contents_(web_contents) {
}
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
base::ListValue args;
args.AppendInteger(render_view_host->GetProcess()->GetID());
args.AppendInteger(render_view_host->GetRoutingID());
Emit("render-view-deleted", args);
}
void WebContents::RenderProcessGone(base::TerminationStatus status) {
Emit("crashed");
}
void WebContents::DidFinishLoad(int64 frame_id,
const GURL& validated_url,
bool is_main_frame,
content::RenderViewHost* render_view_host) {
Emit("did-finish-load");
}
void WebContents::DidStartLoading(content::RenderViewHost* render_view_host) {
Emit("did-start-loading");
}
void WebContents::DidStopLoading(content::RenderViewHost* render_view_host) {
Emit("did-stop-loading");
}
bool WebContents::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WebContents, message)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void WebContents::WebContentsDestroyed(content::WebContents*) {
// The RenderViewDeleted was not called when the WebContents is destroyed.
RenderViewDeleted(web_contents_->GetRenderViewHost());
Emit("destroyed");
}
bool WebContents::IsAlive() const {
return web_contents() != NULL;
}
void WebContents::LoadURL(const GURL& url) {
content::NavigationController::LoadURLParams params(url);
params.transition_type = content::PAGE_TRANSITION_TYPED;
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
web_contents()->GetController().LoadURLWithParams(params);
}
GURL WebContents::GetURL() const {
return web_contents()->GetURL();
}
string16 WebContents::GetTitle() const {
return web_contents()->GetTitle();
}
bool WebContents::IsLoading() const {
return web_contents()->IsLoading();
}
bool WebContents::IsWaitingForResponse() const {
return web_contents()->IsWaitingForResponse();
}
void WebContents::Stop() {
web_contents()->Stop();
}
void WebContents::Reload() {
web_contents()->GetController().Reload(false);
}
void WebContents::ReloadIgnoringCache() {
web_contents()->GetController().ReloadIgnoringCache(false);
}
bool WebContents::CanGoBack() const {
return web_contents()->GetController().CanGoBack();
}
bool WebContents::CanGoForward() const {
return web_contents()->GetController().CanGoForward();
}
bool WebContents::CanGoToOffset(int offset) const {
return web_contents()->GetController().CanGoToOffset(offset);
}
void WebContents::GoBack() {
web_contents()->GetController().GoBack();
}
void WebContents::GoForward() {
web_contents()->GetController().GoForward();
}
void WebContents::GoToIndex(int index) {
web_contents()->GetController().GoToIndex(index);
}
void WebContents::GoToOffset(int offset) {
web_contents()->GetController().GoToOffset(offset);
}
int WebContents::GetRoutingID() const {
return web_contents()->GetRoutingID();
}
int WebContents::GetProcessID() const {
return web_contents()->GetRenderProcessHost()->GetID();
}
bool WebContents::IsCrashed() const {
return web_contents()->IsCrashed();
}
void WebContents::ExecuteJavaScript(const string16& code) {
web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
string16(), code);
}
bool WebContents::SendIPCMessage(const string16& channel,
const base::ListValue& args) {
return Send(new AtomViewMsg_Message(routing_id(), channel, args));
}
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);
}
void WebContents::OnRendererMessage(const string16& channel,
const base::ListValue& args) {
// webContents.emit(channel, new Event(), args...);
Emit(UTF16ToUTF8(channel), args, web_contents(), NULL);
}
void WebContents::OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
IPC::Message* message) {
// webContents.emit(channel, new Event(sender, message), args...);
Emit(UTF16ToUTF8(channel), args, web_contents(), message);
}
// static
mate::Handle<WebContents> WebContents::Create(
v8::Isolate* isolate, content::WebContents* web_contents) {
return CreateHandle(isolate, new WebContents(web_contents));
}
} // namespace api
} // namespace atom

View file

@ -0,0 +1,84 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_
#define ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_
#include "atom/browser/api/event_emitter.h"
#include "content/public/browser/web_contents_observer.h"
#include "native_mate/handle.h"
namespace atom {
namespace api {
class WebContents : public mate::EventEmitter,
public content::WebContentsObserver {
public:
static mate::Handle<WebContents> Create(v8::Isolate* isolate,
content::WebContents* web_contents);
bool IsAlive() const;
void LoadURL(const GURL& url);
GURL GetURL() const;
string16 GetTitle() const;
bool IsLoading() const;
bool IsWaitingForResponse() const;
void Stop();
void Reload();
void ReloadIgnoringCache();
bool CanGoBack() const;
bool CanGoForward() const;
bool CanGoToOffset(int offset) const;
void GoBack();
void GoForward();
void GoToIndex(int index);
void GoToOffset(int offset);
int GetRoutingID() const;
int GetProcessID() const;
bool IsCrashed() const;
void ExecuteJavaScript(const string16& code);
bool SendIPCMessage(const string16& channel, const base::ListValue& args);
protected:
explicit WebContents(content::WebContents* web_contents);
// mate::Wrappable implementations:
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) OVERRIDE;
// content::WebContentsObserver implementations:
virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
virtual void DidFinishLoad(
int64 frame_id,
const GURL& validated_url,
bool is_main_frame,
content::RenderViewHost* render_view_host) OVERRIDE;
virtual void DidStartLoading(
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(content::WebContents*) OVERRIDE;
private:
// Called when received a message from renderer.
void OnRendererMessage(const string16& channel, const base::ListValue& args);
// Called when received a synchronous message from renderer.
void OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
IPC::Message* message);
content::WebContents* web_contents_; // Weak.
DISALLOW_COPY_AND_ASSIGN(WebContents);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_WEB_CONTENTS_H_

View file

@ -4,15 +4,12 @@
#include "atom/browser/api/atom_api_window.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/native_window.h"
#include "atom/common/native_mate_converters/function_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"
#include "base/bind.h"
#include "base/callback.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/render_process_host.h"
#include "native_mate/constructor.h"
#include "native_mate/dictionary.h"
@ -22,8 +19,6 @@
#include "atom/common/node_includes.h"
using content::NavigationController;
namespace mate {
template<>
@ -86,12 +81,6 @@ void Window::OnPageTitleUpdated(bool* prevent_default,
*prevent_default = Emit("page-title-updated", args);
}
void Window::OnLoadingStateChanged(bool is_loading) {
base::ListValue args;
args.AppendBoolean(is_loading);
Emit("loading-state-changed", args);
}
void Window::WillCloseWindow(bool* prevent_default) {
*prevent_default = Emit("close");
}
@ -114,17 +103,6 @@ void Window::OnRendererResponsive() {
Emit("responsive");
}
void Window::OnRenderViewDeleted(int process_id, int routing_id) {
base::ListValue args;
args.AppendInteger(process_id);
args.AppendInteger(routing_id);
Emit("render-view-deleted", args);
}
void Window::OnRendererCrashed() {
Emit("crashed");
}
// static
mate::Wrappable* Window::New(mate::Arguments* args,
const base::DictionaryValue& options) {
@ -326,96 +304,13 @@ void Window::CapturePage(mate::Arguments* args) {
window_->CapturePage(rect, base::Bind(&OnCapturePageDone, callback));
}
string16 Window::GetPageTitle() {
return window_->GetWebContents()->GetTitle();
mate::Handle<WebContents> Window::GetWebContents(v8::Isolate* isolate) const {
return WebContents::Create(isolate, window_->GetWebContents());
}
bool Window::IsLoading() {
return window_->GetWebContents()->IsLoading();
}
bool Window::IsWaitingForResponse() {
return window_->GetWebContents()->IsWaitingForResponse();
}
void Window::Stop() {
window_->GetWebContents()->Stop();
}
int Window::GetRoutingID() {
return window_->GetWebContents()->GetRoutingID();
}
int Window::GetProcessID() {
return window_->GetWebContents()->GetRenderProcessHost()->GetID();
}
bool Window::IsCrashed() {
return window_->GetWebContents()->IsCrashed();
}
mate::Dictionary Window::GetDevTools(v8::Isolate* isolate) {
mate::Dictionary dict(mate::Dictionary::CreateEmpty(isolate));
content::WebContents* web_contents = window_->GetDevToolsWebContents();
dict.Set("processId", web_contents->GetRenderProcessHost()->GetID());
dict.Set("routingId", web_contents->GetRoutingID());
return dict;
}
void Window::ExecuteJavaScriptInDevTools(const std::string& code) {
window_->ExecuteJavaScriptInDevTools(code);
}
void Window::LoadURL(const GURL& url) {
NavigationController& controller = window_->GetWebContents()->GetController();
content::NavigationController::LoadURLParams params(url);
params.transition_type = content::PAGE_TRANSITION_TYPED;
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
controller.LoadURLWithParams(params);
}
GURL Window::GetURL() {
NavigationController& controller = window_->GetWebContents()->GetController();
if (!controller.GetActiveEntry())
return GURL();
return controller.GetActiveEntry()->GetVirtualURL();
}
bool Window::CanGoBack() {
return window_->GetWebContents()->GetController().CanGoBack();
}
bool Window::CanGoForward() {
return window_->GetWebContents()->GetController().CanGoForward();
}
bool Window::CanGoToOffset(int offset) {
return window_->GetWebContents()->GetController().CanGoToOffset(offset);
}
void Window::GoBack() {
window_->GetWebContents()->GetController().GoBack();
}
void Window::GoForward() {
window_->GetWebContents()->GetController().GoForward();
}
void Window::GoToIndex(int index) {
window_->GetWebContents()->GetController().GoToIndex(index);
}
void Window::GoToOffset(int offset) {
window_->GetWebContents()->GetController().GoToOffset(offset);
}
void Window::Reload() {
window_->GetWebContents()->GetController().Reload(false);
}
void Window::ReloadIgnoringCache() {
window_->GetWebContents()->GetController().ReloadIgnoringCache(false);
mate::Handle<WebContents> Window::GetDevToolsWebContents(
v8::Isolate* isolate) const {
return WebContents::Create(isolate, window_->GetDevToolsWebContents());
}
// static
@ -462,27 +357,8 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("blurWebView", &Window::BlurWebView)
.SetMethod("isWebViewFocused", &Window::IsWebViewFocused)
.SetMethod("capturePage", &Window::CapturePage)
.SetMethod("getPageTitle", &Window::GetPageTitle)
.SetMethod("isLoading", &Window::IsLoading)
.SetMethod("isWaitingForResponse", &Window::IsWaitingForResponse)
.SetMethod("stop", &Window::Stop)
.SetMethod("getRoutingId", &Window::GetRoutingID)
.SetMethod("getProcessId", &Window::GetProcessID)
.SetMethod("isCrashed", &Window::IsCrashed)
.SetMethod("getDevTools", &Window::GetDevTools)
.SetMethod("executeJavaScriptInDevTools",
&Window::ExecuteJavaScriptInDevTools)
.SetMethod("loadUrl", &Window::LoadURL)
.SetMethod("getUrl", &Window::GetURL)
.SetMethod("canGoBack", &Window::CanGoBack)
.SetMethod("canGoForward", &Window::CanGoForward)
.SetMethod("canGoToOffset", &Window::CanGoToOffset)
.SetMethod("goBack", &Window::GoBack)
.SetMethod("goForward", &Window::GoForward)
.SetMethod("goToIndex", &Window::GoToIndex)
.SetMethod("goToOffset", &Window::GoToOffset)
.SetMethod("reload", &Window::Reload)
.SetMethod("reloadIgnoringCache", &Window::ReloadIgnoringCache);
.SetMethod("_getWebContents", &Window::GetWebContents)
.SetMethod("_getDevToolsWebContents", &Window::GetDevToolsWebContents);
}
} // namespace api

View file

@ -11,6 +11,7 @@
#include "base/memory/scoped_ptr.h"
#include "atom/browser/native_window_observer.h"
#include "atom/browser/api/event_emitter.h"
#include "native_mate/handle.h"
class GURL;
@ -29,6 +30,8 @@ class NativeWindow;
namespace api {
class WebContents;
class Window : public mate::EventEmitter,
public NativeWindowObserver {
public:
@ -47,14 +50,11 @@ class Window : public mate::EventEmitter,
// Implementations of NativeWindowObserver:
virtual void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) OVERRIDE;
virtual void OnLoadingStateChanged(bool is_loading) OVERRIDE;
virtual void WillCloseWindow(bool* prevent_default) OVERRIDE;
virtual void OnWindowClosed() OVERRIDE;
virtual void OnWindowBlur() OVERRIDE;
virtual void OnRendererUnresponsive() OVERRIDE;
virtual void OnRendererResponsive() OVERRIDE;
virtual void OnRenderViewDeleted(int process_id, int routing_id) OVERRIDE;
virtual void OnRendererCrashed() OVERRIDE;
private:
// APIs for NativeWindow.
@ -100,30 +100,8 @@ class Window : public mate::EventEmitter,
void CapturePage(mate::Arguments* args);
// APIs for WebContents.
string16 GetPageTitle();
bool IsLoading();
bool IsWaitingForResponse();
void Stop();
int GetRoutingID();
int GetProcessID();
bool IsCrashed();
// APIs for devtools.
mate::Dictionary GetDevTools(v8::Isolate* isolate);
void ExecuteJavaScriptInDevTools(const std::string& code);
// APIs for NavigationController.
void LoadURL(const GURL& url);
GURL GetURL();
bool CanGoBack();
bool CanGoForward();
bool CanGoToOffset(int offset);
void GoBack();
void GoForward();
void GoToIndex(int index);
void GoToOffset(int offset);
void Reload();
void ReloadIgnoringCache();
mate::Handle<WebContents> GetWebContents(v8::Isolate* isolate) const;
mate::Handle<WebContents> GetDevToolsWebContents(v8::Isolate* isolate) const;
scoped_ptr<NativeWindow> window_;

View file

@ -1,93 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_browser_bindings.h"
#include <vector>
#include "atom/browser/api/event.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/v8_value_converter.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "atom/common/node_includes.h"
namespace atom {
AtomBrowserBindings::AtomBrowserBindings() {
}
void AtomBrowserBindings::OnRendererMessage(int process_id,
int routing_id,
const string16& channel,
const base::ListValue& args) {
v8::Locker locker(node_isolate);
v8::HandleScope handle_scope(node_isolate);
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
// process.emit(channel, 'message', process_id, routing_id);
std::vector<v8::Handle<v8::Value>> arguments;
arguments.reserve(3 + args.GetSize());
arguments.push_back(mate::ConvertToV8(node_isolate, channel));
const base::Value* value;
if (args.Get(0, &value))
arguments.push_back(converter->ToV8Value(value, global_env->context()));
arguments.push_back(v8::Integer::New(process_id));
arguments.push_back(v8::Integer::New(routing_id));
for (size_t i = 1; i < args.GetSize(); i++) {
const base::Value* value;
if (args.Get(i, &value))
arguments.push_back(converter->ToV8Value(value, global_env->context()));
}
node::MakeCallback(global_env->process_object(),
"emit",
arguments.size(),
&arguments[0]);
}
void AtomBrowserBindings::OnRendererMessageSync(
int process_id,
int routing_id,
const string16& channel,
const base::ListValue& args,
content::WebContents* sender,
IPC::Message* message) {
v8::Locker locker(node_isolate);
v8::HandleScope handle_scope(node_isolate);
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
// Create the event object.
mate::Handle<mate::Event> event = mate::Event::Create(node_isolate);
event->SetSenderAndMessage(sender, message);
// process.emit(channel, 'sync-message', event, process_id, routing_id);
std::vector<v8::Handle<v8::Value>> arguments;
arguments.reserve(3 + args.GetSize());
arguments.push_back(mate::ConvertToV8(node_isolate, channel));
const base::Value* value;
if (args.Get(0, &value))
arguments.push_back(converter->ToV8Value(value, global_env->context()));
arguments.push_back(event.ToV8());
arguments.push_back(v8::Integer::New(process_id));
arguments.push_back(v8::Integer::New(routing_id));
for (size_t i = 1; i < args.GetSize(); i++) {
const base::Value* value;
if (args.Get(i, &value))
arguments.push_back(converter->ToV8Value(value, global_env->context()));
}
node::MakeCallback(global_env->process_object(),
"emit",
arguments.size(),
&arguments[0]);
}
} // namespace atom

View file

@ -1,49 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_H_
#define ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_H_
#include "atom/common/api/atom_bindings.h"
#include "base/strings/string16.h"
namespace base {
class ListValue;
}
namespace content {
class WebContents;
}
namespace IPC {
class Message;
}
namespace atom {
class AtomBrowserBindings : public AtomBindings {
public:
AtomBrowserBindings();
// Called when received a message from renderer.
void OnRendererMessage(int process_id,
int routing_id,
const string16& channel,
const base::ListValue& args);
// Called when received a synchronous message from renderer.
void OnRendererMessageSync(int process_id,
int routing_id,
const string16& channel,
const base::ListValue& args,
content::WebContents* sender,
IPC::Message* message);
private:
DISALLOW_COPY_AND_ASSIGN(AtomBrowserBindings);
};
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_H_

View file

@ -24,6 +24,13 @@ bool EventEmitter::Emit(const base::StringPiece& name) {
bool EventEmitter::Emit(const base::StringPiece& name,
const base::ListValue& args) {
return Emit(name, args, NULL, NULL);
}
bool EventEmitter::Emit(const base::StringPiece& name,
const base::ListValue& args,
content::WebContents* sender,
IPC::Message* message) {
v8::Locker locker(node_isolate);
v8::HandleScope handle_scope(node_isolate);
@ -31,6 +38,8 @@ bool EventEmitter::Emit(const base::StringPiece& name,
scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter);
mate::Handle<mate::Event> event = mate::Event::Create(node_isolate);
if (sender && message)
event->SetSenderAndMessage(sender, message);
// v8_args = [name, event, args...];
std::vector<v8::Handle<v8::Value>> v8_args;

View file

@ -11,6 +11,14 @@ namespace base {
class ListValue;
}
namespace content {
class WebContents;
}
namespace IPC {
class Message;
}
namespace mate {
// Provide helperers to emit event in JavaScript.
@ -18,12 +26,16 @@ class EventEmitter : public Wrappable {
protected:
EventEmitter();
// this.emit(name);
// this.emit(name, new Event());
bool Emit(const base::StringPiece& name);
// this.emit(name, args...);
// this.emit(name, new Event(), args...);
bool Emit(const base::StringPiece& name, const base::ListValue& args);
// this.emit(name, new Event(sender, message), args...);
bool Emit(const base::StringPiece& name, const base::ListValue& args,
content::WebContents* sender, IPC::Message* message);
private:
DISALLOW_COPY_AND_ASSIGN(EventEmitter);
};

View file

@ -1,6 +1,7 @@
EventEmitter = require('events').EventEmitter
IDWeakMap = require 'id-weak-map'
app = require 'app'
wrapWebContents = require('web-contents').wrap
BrowserWindow = process.atomBinding('window').BrowserWindow
BrowserWindow::__proto__ = EventEmitter.prototype
@ -14,6 +15,20 @@ BrowserWindow::_init = ->
menu = app.getApplicationMenu()
@setMenu menu if menu?
@webContents = @getWebContents()
@webContents.once 'destroyed', => @webContents = null
# Define getter for devToolsWebContents.
devToolsWebContents = null
@__defineGetter__ 'devToolsWebContents', ->
if @isDevToolsOpened()
# Get a new devToolsWebContents if previous one has been destroyed, it
# could happen when the devtools has been closed and then reopened.
devToolsWebContents = null unless devToolsWebContents?.isAlive()
devToolsWebContents ?= @getDevToolsWebContents()
else
devToolsWebContents = null
# Remember the window.
id = BrowserWindow.windows.add this
@ -24,12 +39,18 @@ BrowserWindow::_init = ->
# Tell the rpc server that a render view has been deleted and we need to
# release all objects owned by it.
@on 'render-view-deleted', (event, processId, routingId) ->
process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', processId, routingId
@webContents.on 'render-view-deleted', (event, processId, routingId) ->
process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', "#{processId}-#{routingId}"
BrowserWindow::toggleDevTools = ->
if @isDevToolsOpened() then @closeDevTools() else @openDevTools()
BrowserWindow::getWebContents = ->
wrapWebContents @_getWebContents()
BrowserWindow::getDevToolsWebContents = ->
wrapWebContents @_getDevToolsWebContents()
BrowserWindow::restart = ->
@loadUrl(@getUrl())
@ -50,16 +71,30 @@ BrowserWindow.getFocusedWindow = ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.isFocused()
BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) ->
BrowserWindow.fromWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.getProcessId() == processId and
window.getRoutingId() == routingId
return window for window in windows when webContents.equal window.webContents
BrowserWindow.fromDevTools = (processId, routingId) ->
BrowserWindow.fromDevToolsWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
for window in windows when window.isDevToolsOpened()
devtools = window.getDevTools()
return window if devtools.processId == processId and
devtools.routingId == routingId
return window for window in windows when webContents.equal window.devToolsWebContents
# Helpers.
BrowserWindow::loadUrl = -> @webContents.loadUrl.apply @webContents, arguments
BrowserWindow::send = -> @webContents.send.apply @webContents, arguments
# Be compatible with old API.
BrowserWindow::getUrl = -> @webContents.getUrl()
BrowserWindow::reload = -> @webContents.reload()
BrowserWindow::reloadIgnoringCache = -> @webContents.reloadIgnoringCache()
BrowserWindow::getPageTitle = -> @webContents.getTitle()
BrowserWindow::isLoading = -> @webContents.isLoading()
BrowserWindow::isWaitingForResponse = -> @webContents.isWaitingForResponse()
BrowserWindow::stop = -> @webContents.stop()
BrowserWindow::getRoutingId = -> @webContents.getRoutingId()
BrowserWindow::getProcessId = -> @webContents.getProcessId()
BrowserWindow::isCrashed = -> @webContents.isCrashed()
BrowserWindow::executeJavaScriptInDevTools = (code) ->
@devToolsWebContents.executeJavaScript code
module.exports = BrowserWindow

View file

@ -1,32 +1,3 @@
EventEmitter = require('events').EventEmitter
send = process.atomBinding('ipc').send
sendWrap = (channel, processId, routingId, args...) ->
BrowserWindow = require 'browser-window'
if processId?.constructor is BrowserWindow
window = processId
args = [routingId, args...]
processId = window.getProcessId()
routingId = window.getRoutingId()
send channel, processId, routingId, [args...]
class Ipc extends EventEmitter
constructor: ->
process.on 'ATOM_INTERNAL_MESSAGE', (args...) =>
@emit(args...)
process.on 'ATOM_INTERNAL_MESSAGE_SYNC', (channel, event, args...) =>
set = (value) -> event.sendReply JSON.stringify(value)
Object.defineProperty event, 'returnValue', {set}
Object.defineProperty event, 'result', {set}
@emit(channel, event, args...)
send: (processId, routingId, args...) ->
@sendChannel(processId, routingId, 'message', args...)
sendChannel: (args...) ->
sendWrap('ATOM_INTERNAL_MESSAGE', args...)
module.exports = new Ipc
module.exports = new EventEmitter

View file

@ -0,0 +1,27 @@
EventEmitter = require('events').EventEmitter
ipc = require 'ipc'
module.exports.wrap = (webContents) ->
return null unless webContents.isAlive()
# webContents is an EventEmitter.
webContents.__proto__ = EventEmitter.prototype
# WebContents::send(channel, args..)
webContents.send = (args...) ->
@_send 'ATOM_INTERNAL_MESSAGE', [args...]
# The processId and routingId and identify a webContents.
webContents.getId = -> "#{@getProcessId()}-#{@getRoutingId()}"
webContents.equal = (other) -> @getId() is other.getId()
# Dispatch IPC messages to the ipc module.
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...) =>
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
Object.defineProperty event, 'sender', value: webContents
ipc.emit channel, event, args...
webContents

View file

@ -4,10 +4,10 @@
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/api/atom_browser_bindings.h"
#include "atom/browser/atom_browser_client.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/browser.h"
#include "atom/common/api/atom_bindings.h"
#include "atom/common/node_bindings.h"
#include "net/proxy/proxy_resolver_v8.h"
@ -23,7 +23,7 @@ namespace atom {
AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
AtomBrowserMainParts::AtomBrowserMainParts()
: atom_bindings_(new AtomBrowserBindings),
: atom_bindings_(new AtomBindings),
browser_(new Browser),
node_bindings_(NodeBindings::Create(true)) {
DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";

View file

@ -9,7 +9,7 @@
namespace atom {
class AtomBrowserBindings;
class AtomBindings;
class Browser;
class NodeBindings;
@ -20,7 +20,6 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
static AtomBrowserMainParts* Get();
AtomBrowserBindings* atom_bindings() { return atom_bindings_.get(); }
Browser* browser() { return browser_.get(); }
protected:
@ -37,7 +36,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
#endif
private:
scoped_ptr<AtomBrowserBindings> atom_bindings_;
scoped_ptr<AtomBindings> atom_bindings_;
scoped_ptr<Browser> browser_;
scoped_ptr<NodeBindings> node_bindings_;

View file

@ -1,64 +0,0 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "atom/browser/devtools_web_contents_observer.h"
#include "atom/browser/api/atom_browser_bindings.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/native_window.h"
#include "atom/common/api/api_messages.h"
#include "base/logging.h"
#include "content/public/browser/render_process_host.h"
#include "ipc/ipc_message_macros.h"
namespace atom {
DevToolsWebContentsObserver::DevToolsWebContentsObserver(
NativeWindow* native_window,
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
inspected_window_(native_window) {
DCHECK(native_window);
}
DevToolsWebContentsObserver::~DevToolsWebContentsObserver() {
}
bool DevToolsWebContentsObserver::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(DevToolsWebContentsObserver, message)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void DevToolsWebContentsObserver::OnRendererMessage(
const string16& channel,
const base::ListValue& args) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage(
web_contents()->GetRenderProcessHost()->GetID(),
web_contents()->GetRoutingID(),
channel,
args);
}
void DevToolsWebContentsObserver::OnRendererMessageSync(
const string16& channel,
const base::ListValue& args,
IPC::Message* reply_msg) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessageSync(
web_contents()->GetRenderProcessHost()->GetID(),
web_contents()->GetRoutingID(),
channel,
args,
web_contents(),
reply_msg);
}
} // namespace atom

View file

@ -1,41 +0,0 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_
#define ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_
#include "content/public/browser/web_contents_observer.h"
namespace base {
class ListValue;
}
namespace atom {
class NativeWindow;
class DevToolsWebContentsObserver : public content::WebContentsObserver {
public:
DevToolsWebContentsObserver(NativeWindow* native_window,
content::WebContents* web_contents);
virtual ~DevToolsWebContentsObserver();
protected:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void OnRendererMessage(const string16& channel,
const base::ListValue& args);
void OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
IPC::Message* reply_msg);
private:
NativeWindow* inspected_window_;
DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver);
};
} // namespace atom
#endif // ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_

View file

@ -29,13 +29,11 @@ class ObjectsStore
throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
@objects[id]
@forRenderView: (processId, routingId) ->
key = "#{processId}_#{routingId}"
@forRenderView: (key) ->
@stores[key] = new ObjectsStore unless @stores[key]?
@stores[key]
@releaseForRenderView: (processId, routingId) ->
key = "#{processId}_#{routingId}"
@releaseForRenderView: (key) ->
delete @stores[key]
class ObjectsRegistry extends EventEmitter
@ -52,7 +50,7 @@ class ObjectsRegistry extends EventEmitter
# Register a new object, the object would be kept referenced until you release
# it explicitly.
add: (processId, routingId, obj) ->
add: (key, obj) ->
# Some native objects may already been added to objectsWeakMap, be care not
# to add it twice.
@objectsWeakMap.add obj unless v8Util.getHiddenValue obj, 'atomId'
@ -63,7 +61,7 @@ class ObjectsRegistry extends EventEmitter
# with the storeId.
# We use a difference key because the same object can be referenced for
# multiple times by the same renderer view.
store = ObjectsStore.forRenderView processId, routingId
store = ObjectsStore.forRenderView key
storeId = store.add obj
[id, storeId]
@ -73,12 +71,12 @@ class ObjectsRegistry extends EventEmitter
@objectsWeakMap.get id
# Remove an object according to its storeId.
remove: (processId, routingId, storeId) ->
ObjectsStore.forRenderView(processId, routingId).remove storeId
remove: (key, storeId) ->
ObjectsStore.forRenderView(key).remove storeId
# Clear all references to objects from renderer view.
clear: (processId, routingId) ->
@emit "release-renderer-view-#{processId}-#{routingId}"
ObjectsStore.releaseForRenderView processId, routingId
clear: (key) ->
@emit "clear-#{key}"
ObjectsStore.releaseForRenderView key
module.exports = new ObjectsRegistry

View file

@ -4,7 +4,7 @@ objectsRegistry = require './objects-registry.js'
v8Util = process.atomBinding 'v8_util'
# Convert a real value into meta data.
valueToMeta = (processId, routingId, value) ->
valueToMeta = (sender, value) ->
meta = type: typeof value
meta.type = 'value' if value is null
@ -15,14 +15,14 @@ valueToMeta = (processId, routingId, value) ->
if meta.type is 'array'
meta.members = []
meta.members.push valueToMeta(processId, routingId, el) for el in value
meta.members.push valueToMeta(sender, el) for el in value
else if meta.type is 'object' or meta.type is 'function'
meta.name = value.constructor.name
# Reference the original value if it's an object, because when it's
# passed to renderer we would assume the renderer keeps a reference of
# it.
[meta.id, meta.storeId] = objectsRegistry.add processId, routingId, value
[meta.id, meta.storeId] = objectsRegistry.add sender.getId(), value
meta.members = []
meta.members.push {name: prop, type: typeof field} for prop, field of value
@ -37,12 +37,12 @@ errorToMeta = (error) ->
type: 'error', message: error.message, stack: (error.stack || error)
# Convert array of meta data from renderer into array of real values.
unwrapArgs = (processId, routingId, args) ->
unwrapArgs = (sender, args) ->
metaToValue = (meta) ->
switch meta.type
when 'value' then meta.value
when 'remote-object' then objectsRegistry.get meta.id
when 'array' then unwrapArgs processId, routingId, meta.value
when 'array' then unwrapArgs sender, meta.value
when 'object'
ret = v8Util.createObjectWithName meta.name
for member in meta.members
@ -53,15 +53,15 @@ unwrapArgs = (processId, routingId, args) ->
-> returnValue
when 'function'
rendererReleased = false
objectsRegistry.once "release-renderer-view-#{processId}-#{routingId}", ->
objectsRegistry.once "clear-#{sender.getId()}", ->
rendererReleased = true
ret = ->
throw new Error('Calling a callback of released renderer view') if rendererReleased
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
sender.send 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments)
v8Util.setDestructor ret, ->
return if rendererReleased
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
sender.send 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
ret
else throw new TypeError("Unknown type: #{meta.type}")
@ -69,78 +69,78 @@ unwrapArgs = (processId, routingId, args) ->
# Call a function and send reply asynchronously if it's a an asynchronous
# style function and the caller didn't pass a callback.
callFunction = (event, processId, routingId, func, caller, args) ->
callFunction = (event, func, caller, args) ->
if v8Util.getHiddenValue(func, 'asynchronous') and typeof args[args.length - 1] isnt 'function'
args.push (ret) ->
event.returnValue = valueToMeta processId, routingId, ret
event.returnValue = valueToMeta event.sender, ret
func.apply caller, args
else
ret = func.apply caller, args
event.returnValue = valueToMeta processId, routingId, ret
event.returnValue = valueToMeta event.sender, ret
# Send by BrowserWindow when its render view is deleted.
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (processId, routingId) ->
objectsRegistry.clear processId, routingId
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
objectsRegistry.clear id
ipc.on 'ATOM_BROWSER_REQUIRE', (event, processId, routingId, module) ->
ipc.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
try
event.returnValue = valueToMeta processId, routingId, require(module)
event.returnValue = valueToMeta event.sender, require(module)
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_GLOBAL', (event, processId, routingId, name) ->
ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
try
event.returnValue = valueToMeta processId, routingId, global[name]
event.returnValue = valueToMeta event.sender, global[name]
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) ->
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
try
BrowserWindow = require 'browser-window'
window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId
window = BrowserWindow.fromDevTools processId, routingId unless window?
event.returnValue = valueToMeta processId, routingId, window
window = BrowserWindow.fromWebContents event.sender
window = BrowserWindow.fromDevToolsWebContents event.sender unless window?
event.returnValue = valueToMeta event.sender, window
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) ->
ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
try
args = unwrapArgs processId, routingId, args
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get id
# Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta processId, routingId, obj
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) ->
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
try
args = unwrapArgs processId, routingId, args
args = unwrapArgs event.sender, args
func = objectsRegistry.get id
callFunction event, processId, routingId, func, global, args
callFunction event, func, global, args
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, method, args) ->
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
try
args = unwrapArgs processId, routingId, args
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get(id)[method]
# Call new with array of arguments.
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta processId, routingId, obj
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) ->
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
try
args = unwrapArgs processId, routingId, args
args = unwrapArgs event.sender, args
obj = objectsRegistry.get id
callFunction event, processId, routingId, obj[method], obj, args
callFunction event, obj[method], obj, args
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, processId, routingId, id, name, value) ->
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
try
obj = objectsRegistry.get id
obj[name] = value
@ -148,12 +148,12 @@ ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, processId, routingId, id, name, value)
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, processId, routingId, id, name) ->
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
try
obj = objectsRegistry.get id
event.returnValue = valueToMeta processId, routingId, obj[name]
event.returnValue = valueToMeta event.sender, obj[name]
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_DEREFERENCE', (processId, routingId, storeId) ->
objectsRegistry.remove processId, routingId, storeId
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, storeId) ->
objectsRegistry.remove event.sender.getId(), storeId

View file

@ -8,13 +8,10 @@
#include <utility>
#include <vector>
#include "atom/browser/api/atom_browser_bindings.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_javascript_dialog_manager.h"
#include "atom/browser/browser.h"
#include "atom/browser/devtools_delegate.h"
#include "atom/browser/devtools_web_contents_observer.h"
#include "atom/browser/ui/file_dialog.h"
#include "atom/browser/window_list.h"
#include "atom/common/api/api_messages.h"
@ -195,12 +192,8 @@ bool NativeWindow::HasModalDialog() {
void NativeWindow::OpenDevTools() {
if (devtools_window_) {
devtools_window_->Focus(true);
devtools_web_contents_observer_.reset(new DevToolsWebContentsObserver(
this, devtools_window_->GetWebContents()));
} else {
inspectable_web_contents()->ShowDevTools();
devtools_web_contents_observer_.reset(new DevToolsWebContentsObserver(
this, GetDevToolsWebContents()));
#if defined(OS_MACOSX)
// Temporary fix for flashing devtools, try removing this after upgraded to
// Chrome 32.
@ -229,11 +222,6 @@ void NativeWindow::InspectElement(int x, int y) {
agent->InspectElement(x, y);
}
void NativeWindow::ExecuteJavaScriptInDevTools(const std::string& script) {
GetDevToolsWebContents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
string16(), base::UTF8ToUTF16(script));
}
void NativeWindow::FocusOnWebView() {
GetWebContents()->GetRenderViewHost()->Focus();
}
@ -305,14 +293,6 @@ void NativeWindow::DestroyWebContents() {
if (!inspectable_web_contents_)
return;
// The OnRenderViewDeleted is not called when the WebContents is destroyed
// directly (e.g. when closing the window), so we make sure it's always
// emitted to users by sending it before window is closed..
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
OnRenderViewDeleted(
GetWebContents()->GetRenderProcessHost()->GetID(),
GetWebContents()->GetRoutingID()));
inspectable_web_contents_.reset();
}
@ -437,13 +417,6 @@ void NativeWindow::DeactivateContents(content::WebContents* contents) {
BlurWebView();
}
void NativeWindow::LoadingStateChanged(content::WebContents* source) {
bool is_loading = source->IsLoading();
FOR_EACH_OBSERVER(NativeWindowObserver,
observers_,
OnLoadingStateChanged(is_loading));
}
void NativeWindow::MoveContents(content::WebContents* source,
const gfx::Rect& pos) {
SetPosition(pos.origin());
@ -482,16 +455,6 @@ void NativeWindow::RendererResponsive(content::WebContents* source) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererResponsive());
}
void NativeWindow::RenderViewDeleted(content::RenderViewHost* rvh) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
OnRenderViewDeleted(rvh->GetProcess()->GetID(),
rvh->GetRoutingID()));
}
void NativeWindow::RenderProcessGone(base::TerminationStatus status) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererCrashed());
}
void NativeWindow::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
// Do nothing, we override this method just to avoid compilation error since
// there are two virtual functions named BeforeUnloadFired.
@ -500,9 +463,6 @@ void NativeWindow::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(NativeWindow, message)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_UpdateDraggableRegions,
UpdateDraggableRegions)
IPC_MESSAGE_UNHANDLED(handled = false)
@ -628,28 +588,8 @@ void NativeWindow::CallDevToolsFunction(const std::string& function_name,
}
}
}
ExecuteJavaScriptInDevTools(function_name + "(" + params + ");");
}
void NativeWindow::OnRendererMessage(const string16& channel,
const base::ListValue& args) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage(
GetWebContents()->GetRenderProcessHost()->GetID(),
GetWebContents()->GetRoutingID(),
channel,
args);
}
void NativeWindow::OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
IPC::Message* reply_msg) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessageSync(
GetWebContents()->GetRenderProcessHost()->GetID(),
GetWebContents()->GetRoutingID(),
channel,
args,
GetWebContents(),
reply_msg);
GetDevToolsWebContents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
string16(), base::UTF8ToUTF16(function_name + "(" + params + ");"));
}
} // namespace atom

View file

@ -46,7 +46,6 @@ namespace atom {
class AtomJavaScriptDialogManager;
class DevToolsDelegate;
class DevToolsWebContentsObserver;
struct DraggableRegion;
class NativeWindow : public brightray::DefaultWebContentsDelegate,
@ -136,7 +135,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
virtual void CloseDevTools();
virtual bool IsDevToolsOpened();
virtual void InspectElement(int x, int y);
virtual void ExecuteJavaScriptInDevTools(const std::string& script);
virtual void FocusOnWebView();
virtual void BlurWebView();
@ -220,7 +218,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
virtual bool CanOverscrollContent() const OVERRIDE;
virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
virtual void DeactivateContents(content::WebContents* contents) OVERRIDE;
virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
virtual void MoveContents(content::WebContents* source,
const gfx::Rect& pos) OVERRIDE;
virtual void CloseContents(content::WebContents* source) OVERRIDE;
@ -230,8 +227,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
virtual void RendererResponsive(content::WebContents* source) OVERRIDE;
// Implementations of content::WebContentsObserver.
virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
@ -274,13 +269,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
bool succeed,
const SkBitmap& bitmap);
void OnRendererMessage(const string16& channel,
const base::ListValue& args);
void OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
IPC::Message* reply_msg);
// Notification manager.
content::NotificationRegistrar registrar_;
@ -305,9 +293,6 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
base::WeakPtr<NativeWindow> devtools_window_;
scoped_ptr<DevToolsDelegate> devtools_delegate_;
// WebContentsObserver for the WebContents of devtools.
scoped_ptr<DevToolsWebContentsObserver> devtools_web_contents_observer_;
scoped_ptr<AtomJavaScriptDialogManager> dialog_manager_;
// Notice that inspectable_web_contents_ must be placed after dialog_manager_,

View file

@ -17,9 +17,6 @@ class NativeWindowObserver {
virtual void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) {}
// Called when the window is starting or is done loading a page.
virtual void OnLoadingStateChanged(bool is_loading) {}
// Called when the window is gonna closed.
virtual void WillCloseWindow(bool* prevent_default) {}
@ -34,12 +31,6 @@ class NativeWindowObserver {
// Called when renderer recovers.
virtual void OnRendererResponsive() {}
// Called when a render view has been deleted.
virtual void OnRenderViewDeleted(int process_id, int routing_id) {}
// Called when renderer has crashed.
virtual void OnRendererCrashed() {}
};
} // namespace atom

View file

@ -12,7 +12,6 @@ NODE_EXT_LIST_START
NODE_EXT_LIST_ITEM(atom_browser_app)
NODE_EXT_LIST_ITEM(atom_browser_auto_updater)
NODE_EXT_LIST_ITEM(atom_browser_dialog)
NODE_EXT_LIST_ITEM(atom_browser_ipc)
NODE_EXT_LIST_ITEM(atom_browser_menu)
NODE_EXT_LIST_ITEM(atom_browser_power_monitor)
NODE_EXT_LIST_ITEM(atom_browser_protocol)

View file

@ -10,15 +10,13 @@ class Ipc extends EventEmitter
process.removeAllListeners 'ATOM_INTERNAL_MESSAGE'
send: (args...) ->
@sendChannel 'message', args...
sendChannel: (args...) ->
ipc.send 'ATOM_INTERNAL_MESSAGE', [args...]
ipc.send 'ipc-message', [args...]
sendSync: (args...) ->
@sendSync 'sync-message', args...
JSON.parse ipc.sendSync('ipc-message-sync', [args...])
sendChannelSync: (args...) ->
JSON.parse ipc.sendSync('ATOM_INTERNAL_MESSAGE_SYNC', [args...])
# Discarded
sendChannel: -> @send.apply this, arguments
sendChannelSync: -> @sendSync.apply this, arguments
module.exports = new Ipc

View file

@ -7,7 +7,7 @@ example is:
var BrowserWindow = require('browser-window');
var win = new BrowserWindow({ width: 800, height: 600, show: false });
win.on('destroyed', function() {
win.on('closed', function() {
win = null;
});
@ -86,13 +86,6 @@ you should explictly set `sandbox` to `none`:
Emitted when the document changed its title, calling `event.preventDefault()`
would prevent the native window's title to change.
### Event: 'loading-state-changed'
* `event` Event
* `isLoading` Boolean
Emitted when the window is starting or is done loading a page.
### Event: 'close'
* `event` Event
@ -137,10 +130,6 @@ Emiited when the web page becomes unresponsive.
Emitted when the unresponsive web page becomes responsive again.
### Event: 'crashed'
Emitted when the renderer process is crashed.
### Event: 'blur'
Emiited when window loses focus.
@ -153,12 +142,20 @@ Returns an array of all opened browser windows.
Returns the window that is focused in this application.
### Class Method: BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
### Class Method: BrowserWindow.fromWebContents(webContents)
* `processId` Integer
* `routingId` Integer
* `webContents` WebContents
Find a window according to its `processId` and `routingId`.
Find a window according to the `webContents` it owns
### BrowserWindow.webContents
The `WebContents` object this window owns, all web page related events and
operations would be done via it.
### BrowserWindow.devToolsWebContents
Get the `WebContents` of devtools of this window.
### BrowserWindow.destroy()
@ -334,13 +331,6 @@ Closes the developer tools.
Starts inspecting element at position (`x`, `y`).
### BrowserWindow.executeJavaScriptInDevTools(code)
* `code` String
Evaluate `code` in devtools to use
[InspectorFrontendAPI](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js&q=InspectorFrontendAPI&sq=package:chromium&type=cs)
### BrowserWindow.focusOnWebView()
### BrowserWindow.blurWebView()
@ -366,82 +356,113 @@ encode it and use data URL to embed the image in HTML.
[remote](../renderer/remote.md) if you are going to use this API in renderer
process.
### BrowserWindow.getPageTitle()
Returns the title of web page.
### BrowserWindow.isLoading()
Returns whether web page is still loading resources.
### BrowserWindow.isWaitingForResponse()
Returns whether web page is waiting for a first-response for the main resource
of the page.
### BrowserWindow.stop()
Stops any pending navigation.
### BrowserWindow.getProcessId()
Returns window's process ID. The process ID and routing ID can be used
together to locate a window.
### BrowserWindow.getRoutingId()
Returns window's routing ID. The process ID and routing ID can be used
together to locate a window.
### BrowserWindow.loadUrl(url)
Same with `webContents.loadUrl(url)`.
## Class: WebContents
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.
### Event: 'did-start-loading'
### Event: 'did-stop-loading'
### WebContents.loadUrl(url)
* `url` URL
Loads the `url` in the window, the `url` must contains the protocol prefix,
e.g. the `http://` or `file://`.
### BrowserWindow.getUrl()
### WebContents.getUrl()
Returns URL of current web page.
### BrowserWindow.canGoBack()
### WebContents.getTitle()
Returns the title of web page.
### WebContents.isLoading()
Returns whether web page is still loading resources.
### WebContents.isWaitingForResponse()
Returns whether web page is waiting for a first-response for the main resource
of the page.
### WebContents.stop()
Stops any pending navigation.
### WebContents.reload()
Reloads current page.
### WebContents.reloadIgnoringCache()
Reloads current page and ignores cache.
### WebContents.canGoBack()
Returns whether the web page can go back.
### BrowserWindow.canGoForward()
### WebContents.canGoForward()
Returns whether the web page can go forward.
### BrowserWindow.canGoToOffset(offset)
### WebContents.canGoToOffset(offset)
* `offset` Integer
Returns whether the web page can go to `offset`.
### BrowserWindow.goBack()
### WebContents.goBack()
Makes the web page go back.
### BrowserWindow.goForward()
### WebContents.goForward()
Makes the web page go forward.
### BrowserWindow.goToIndex(index)
### WebContents.goToIndex(index)
* `index` Integer
Navigates to the specified absolute index.
### BrowserWindow.goToOffset(offset)
### WebContents.goToOffset(offset)
* `offset` Integer
Navigates to the specified offset from the "current entry".
### BrowserWindow.reload()
### WebContents.IsCrashed()
Reloads current window.
Whether the renderer process has crashed.
### BrowserWindow.reloadIgnoringCache()
### WebContents.executeJavaScript(code)
Reloads current window and ignores cache.
* `code` String
Evaluate `code` in page.
### WebContents.send(channel[, args...])
* `channel` String
Send `args..` to the web page via `channel` in asynchronous message, the web
page can handle it by listening to the `channel` event of `ipc` module.

View file

@ -1,52 +1,45 @@
# ipc (browser)
The `ipc` module allows developers to send asynchronous messages to renderers.
To avoid possible dead-locks, it's not allowed to send synchronous messages in
browser.
Handles asynchronous and synchronous message sent from web page.
## Event: 'message'
The messages sent from web page would be emitted to this module, the event name
is the `channel` when sending message. To reply a synchronous message, you need
to set `event.returnValue`, to send an asynchronous back to the sender, you can
use `event.sender.send(...)`.
* `processId` Integer
* `routingId` Integer
Emitted when renderer sent a message to the browser.
## Event: 'sync-message'
* `event` Object
* `processId` Integer
* `routingId` Integer
Emitted when renderer sent a synchronous message to the browser. The receiver
should store the result in `event.returnValue`.
**Note:** Due to the limitation of `EventEmitter`, returning value in the
event handler has no effect, so we have to store the result by using the
`event` parameter.
## ipc.send(processId, routingId, [args...])
* `processId` Integer
* `routingId` Integer
Send `args...` to the renderer specified by `processId` and `routingId` and
return immediately, the renderer should handle the message by listening to the
`message` event.
## ipc.sendChannel(processId, routingId, channel, [args...])
* `processId` Integer
* `routingId` Integer
* `channel` String
This is the same with ipc.send, except that the renderer should listen to the
`channel` event. The ipc.send(processId, routingId, args...) can be seen as
ipc.sendChannel(processId, routingId, 'message', args...).
**Note:** If the the first argument (e.g. `processId`) is a `BrowserWindow`,
`ipc.sendChannel` would automatically get the `processId` and `routingId`
from it, so you can send a message to window like this:
An example of sending and handling messages:
```javascript
ipc.sendChannel(browserWindow, 'message', ...);
// In browser.
var ipc = require('ipc');
ipc.on('asynchronous-message', function(event, arg) {
console.log(arg); // prints "ping"
event.sender.send('asynchronous-reply', 'pong');
});
ipc.on('synchronous-message', function(event, arg) {
console.log(arg); // prints "ping"
event.returnValue = 'pong'.
});
```
```javascript
// In web page.
var ipc = require('ipc');
console.log(ipc.sendSync('synchronous-message', 'ping')); // prints "pong"
ipc.on('asynchronous-reply', function(arg) {
console.log(arg); // prints "pong"
});
ipc.send('asynchronous-message', 'ping');
```
### Class: Event
## Event.returnValue
Assign to this to return an value to synchronous messages.
## Event.sender
The `WebContents` of the web page that has sent the message.

View file

@ -5,75 +5,18 @@ asynchronous messages to the browser, and also receive messages sent from
browser. If you want to make use of modules of browser from renderer, you
might consider using the [remote](remote.md) module.
An example of echoing messages between browser and renderer:
See [ipc (browser)](../browser/ipc-browser.md) for examples.
```javascript
// In browser:
var ipc = require('ipc');
ipc.on('message', function(processId, routingId, m) {
ipc.send(processId, routingId, m);
});
```
## ipc.send(channel[, args...])
```javascript
// In renderer:
var ipc = require('ipc');
ipc.on('message', function(m) {
console.log('Received message', m);
});
ipc.send('Hello world');
```
Send `args..` to the web page via `channel` in asynchronous message, the browser
process can handle it by listening to the `channel` event of `ipc` module.
An example of sending synchronous message from renderer to browser:
## ipc.sendSync(channel[, args...])
```javascript
// In browser:
var ipc = require('ipc');
ipc.on('browser-data-request', function(event, processId, routingId, message) {
event.returnValue = 'THIS SOME DATA FROM THE BROWSER';
});
```
```javascript
// In renderer:
var ipc = require('ipc');
console.log(ipc.sendChannelSync('browser-data-request', 'THIS IS FROM THE RENDERER'));
```
## Event: 'message'
Emitted when browser sent a message to this window.
## ipc.send([args...])
Send all arguments to the browser and return immediately, the browser should
handle the message by listening to the `message` event.
## ipc.sendSync([args...])
Send all arguments to the browser synchronously, and returns the result sent
from browser. The browser should handle the message by listening to the
`sync-message` event.
Send `args..` to the web page via `channel` in synchronous message, and returns
the result sent from browser. The browser process can handle it by listening to
the `channel` event of `ipc` module, and returns by setting `event.returnValue`.
**Note:** Usually developers should never use this API, since sending
synchronous message would block the browser.
## ipc.sendChannel(channel, [args...])
* `channel` String
This is the same with `ipc.send`, except that the browser should listen to the
`channel` event. The `ipc.send(args...)` can be seen as
`ipc.sendChannel('message', args...)`.
## ipc.sendChannelSync(channel, [args...])
* `channel` String
This is the same with `ipc.sendSync`, except that the browser should listen to
the `channel` event. The `ipc.sendSync(args...)` can be seen as
`ipc.sendChannelSync('sync-message', args...)`.
**Note:** Usually developers should never use this API, since sending
synchronous message would block the browser.
synchronous message would block the whole web page.

View file

@ -18,9 +18,8 @@ describe 'browser-window module', ->
describe 'BrowserWindow.close()', ->
it 'should emit unload handler', (done) ->
w.on 'loading-state-changed', (event, isLoading) ->
if (!isLoading)
w.close()
w.webContents.on 'did-finish-load', ->
w.close()
w.on 'closed', ->
test = path.join(fixtures, 'api', 'unload')
content = fs.readFileSync(test)
@ -32,9 +31,8 @@ describe 'browser-window module', ->
it 'should emit beforeunload handler', (done) ->
w.on 'onbeforeunload', ->
done()
w.on 'loading-state-changed', (event, isLoading) ->
if (!isLoading)
w.close()
w.webContents.on 'did-finish-load', ->
w.close()
w.loadUrl 'file://' + path.join(fixtures, 'api', 'beforeunload-false.html')
describe 'window.close()', ->
@ -53,18 +51,9 @@ describe 'browser-window module', ->
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')
describe 'BrowserWindow.loadUrl(url)', ->
it 'should emit loading-state-changed event', (done) ->
count = 0
w.on 'loading-state-changed', (event, isLoading) ->
if count == 0
assert.equal isLoading, true
else if count == 1
assert.equal isLoading, false
done()
else
assert false
++count
it 'should emit did-start-loading event', (done) ->
w.webContents.on 'did-start-loading', ->
done()
w.loadUrl 'about:blank'
describe 'BrowserWindow.focus()', ->

View file

@ -48,13 +48,13 @@ describe 'ipc module', ->
print_name = remote.require path.join(fixtures, 'module', 'print_name.js')
assert.equal print_name.print(buf), 'Buffer'
describe 'ipc.send', ->
describe 'ipc.sender.send', ->
it 'should work when sending an object containing id property', (done) ->
obj = id: 1, name: 'ly'
ipc.once 'message', (message) ->
assert.deepEqual message, obj
done()
ipc.send obj
ipc.send 'message', obj
describe 'ipc.sendSync', ->
it 'can be replied by setting event.returnValue', ->

View file

@ -8,8 +8,8 @@ var window = null;
app.commandLine.appendSwitch('js-flags', '--expose_gc');
ipc.on('message', function() {
ipc.send.apply(this, arguments);
ipc.on('message', function(event, arg) {
event.sender.send('message', arg);
});
ipc.on('console.log', function(pid, rid, args) {
@ -24,11 +24,11 @@ ipc.on('process.exit', function(pid, rid, code) {
process.exit(code);
});
ipc.on('eval', function(ev, pid, rid, script) {
ipc.on('eval', function(ev, script) {
ev.returnValue = eval(script);
});
ipc.on('echo', function(ev, pid, rid, msg) {
ipc.on('echo', function(ev, msg) {
ev.returnValue = msg;
});