mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cb624d828 | ||
|
|
cec640f572 | ||
|
|
f38eb1b66f | ||
|
|
a567ba08ea | ||
|
|
91d54a74e1 | ||
|
|
da9cce3f2d | ||
|
|
92241b91ce | ||
|
|
6b81070f67 | ||
|
|
3715dd2a20 | ||
|
|
eb6fa98ed0 | ||
|
|
8ddb85774a | ||
|
|
8caf5fac06 | ||
|
|
cc62978ac3 | ||
|
|
f833423a2f | ||
|
|
3c0671c179 | ||
|
|
a00bf3e1e1 | ||
|
|
ce487fe1da | ||
|
|
a73aea3bda | ||
|
|
b9d994dca2 | ||
|
|
b7c2295a1c | ||
|
|
da2ded5453 | ||
|
|
a5eb9ea08f | ||
|
|
4223867dbc | ||
|
|
40273cf37d | ||
|
|
bc9c95d77d | ||
|
|
6a322f8bd6 | ||
|
|
e7bc368785 | ||
|
|
aad0c8e996 | ||
|
|
bf4756fdfb | ||
|
|
8acd6d6c8a | ||
|
|
256215b749 | ||
|
|
a3e5b21118 | ||
|
|
84a3eb5411 | ||
|
|
e17da272f4 | ||
|
|
88bdff5832 | ||
|
|
ae18a90f7e | ||
|
|
3b7dd85d3f | ||
|
|
128d9c78db | ||
|
|
c7fed48c4a | ||
|
|
7737708fdd | ||
|
|
9ba08d5e67 | ||
|
|
6c3dc9e526 | ||
|
|
75a24a2e67 | ||
|
|
893309aa8a | ||
|
|
f17864372e | ||
|
|
610ac5b045 | ||
|
|
cfb957a603 | ||
|
|
e423f601c0 | ||
|
|
504f96ae08 | ||
|
|
3b149945bf | ||
|
|
5fe9f281ac | ||
|
|
766347ffae | ||
|
|
a5bc2fdb44 | ||
|
|
e7d4b44d05 | ||
|
|
30c9cd4318 | ||
|
|
5787b4cd6f | ||
|
|
a26308d902 | ||
|
|
c2093946c8 | ||
|
|
0286379706 | ||
|
|
6765ec30f1 | ||
|
|
beba27ed1e | ||
|
|
24f510ca03 | ||
|
|
2cbe823773 | ||
|
|
790c53825b | ||
|
|
10bd2384d0 | ||
|
|
335db788a5 | ||
|
|
d8cd3d78ff | ||
|
|
2a462cc2b7 | ||
|
|
df30f130d3 | ||
|
|
24e613c827 | ||
|
|
7df256f8dc | ||
|
|
2000f88c84 | ||
|
|
db890feb51 | ||
|
|
738cbd4080 | ||
|
|
11221979e5 | ||
|
|
72c604f741 | ||
|
|
04910b8391 | ||
|
|
132eb09d96 | ||
|
|
38b37f2520 | ||
|
|
4bdd1b88ad | ||
|
|
abd3e86fb1 | ||
|
|
c2fd43c3e8 | ||
|
|
261f50701a | ||
|
|
bc4201f911 | ||
|
|
6915f020d9 | ||
|
|
f7de0e8d38 | ||
|
|
efd2bbbede | ||
|
|
b1f30c1eb6 | ||
|
|
34e1800716 | ||
|
|
e00d3d4b37 | ||
|
|
19aa2b7979 | ||
|
|
dbdf2d8d54 | ||
|
|
3be4a01963 | ||
|
|
d0ab7e2c1e | ||
|
|
566b8136c9 | ||
|
|
c8150e570b | ||
|
|
d2b4b761ba | ||
|
|
018a48770a | ||
|
|
799d9ada7d | ||
|
|
15ba32b489 | ||
|
|
0f6617ec26 | ||
|
|
9e16e41bb3 | ||
|
|
c86acc4cd7 | ||
|
|
d88676bf65 | ||
|
|
d56a7d75de | ||
|
|
244d7eaf17 | ||
|
|
1ed77371c0 | ||
|
|
912bac698c | ||
|
|
8464fb4f64 | ||
|
|
0b01e49cd3 | ||
|
|
8cd7ccdc0d | ||
|
|
214df5ef69 | ||
|
|
b7c9f8ba1c | ||
|
|
f63661256f | ||
|
|
33279b1a2f | ||
|
|
96c173217f | ||
|
|
b9cbfb8103 | ||
|
|
fd299cb3fe | ||
|
|
c87956ef4f | ||
|
|
833190e8fa | ||
|
|
7cc1589097 | ||
|
|
e8ecbec3a7 | ||
|
|
4914dd67b6 | ||
|
|
1e895bdf76 | ||
|
|
5b74dff8f1 | ||
|
|
5b862fdf60 | ||
|
|
b411657b76 | ||
|
|
d160da7752 | ||
|
|
0cd3f3cc40 | ||
|
|
9105914b9e | ||
|
|
dea52ae767 | ||
|
|
79d6e88d99 | ||
|
|
f63cf3f283 | ||
|
|
22c8b55cb9 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,4 @@
|
||||
.DS_Store
|
||||
atom-shell.zip
|
||||
version
|
||||
/build/
|
||||
/dist/
|
||||
/frameworks/
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -12,4 +12,4 @@
|
||||
url = https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/github/apm.git
|
||||
url = https://github.com/atom/apm.git
|
||||
|
||||
@@ -5,7 +5,7 @@ Native layer for the [Atom](https://github.com/github/atom).
|
||||
## Features
|
||||
|
||||
* Write desktop applications with web techniques
|
||||
* Support built-in and third-party modules of node.js
|
||||
* Support built-in and third-party modules of node.js
|
||||
* Support native node.js modules
|
||||
* Extended built-in modules for desktop programming
|
||||
* JavaScript on browser side
|
||||
@@ -13,7 +13,7 @@ Native layer for the [Atom](https://github.com/github/atom).
|
||||
|
||||
## Usage & Development
|
||||
|
||||
See the [Wiki](https://github.com/atom/atom-shell/wiki).
|
||||
See the docs [here](https://github.com/atom/atom-shell/tree/master/docs).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "content/public/app/content_main.h"
|
||||
@@ -16,13 +17,18 @@ int Start(int argc, char *argv[]);
|
||||
#include <shellapi.h> // NOLINT
|
||||
|
||||
#include "app/atom_main_delegate.h"
|
||||
#include "base/environment.h"
|
||||
#include "content/public/app/startup_helper_win.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
|
||||
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
||||
int argc = 0;
|
||||
wchar_t** wargv = ::CommandLineToArgvW(cmd, &argc);
|
||||
if (argc > 1 && wcscmp(wargv[1], L"--atom-child_process-fork") == 0) {
|
||||
wchar_t** wargv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
std::string node_indicator;
|
||||
if (env->GetVar("ATOM_SHELL_INTERNAL_RUN_AS_NODE", &node_indicator) &&
|
||||
node_indicator == "1") {
|
||||
// Convert argv to to UTF8
|
||||
char** argv = new char*[argc];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
@@ -57,8 +63,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
||||
}
|
||||
}
|
||||
// Now that conversion is done, we can finally start.
|
||||
argv[1] = argv[0];
|
||||
return node::Start(argc - 1, argv + 1);
|
||||
return node::Start(argc, argv);
|
||||
}
|
||||
|
||||
sandbox::SandboxInterfaceInfo sandbox_info = {0};
|
||||
@@ -72,10 +77,9 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
||||
#include "app/atom_library_main.h"
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc > 1 && strcmp(argv[1], "--atom-child_process-fork") == 0) {
|
||||
argv[1] = argv[0];
|
||||
return node::Start(argc - 1, const_cast<char**>(argv + 1));
|
||||
}
|
||||
char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
|
||||
if (node_indicator != NULL && strcmp(node_indicator, "1") == 0)
|
||||
return node::Start(argc, const_cast<char**>(argv));
|
||||
|
||||
return AtomMain(argc, argv);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,27 @@
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "renderer/atom_renderer_client.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
|
||||
#include "base/mac/bundle_locations.h"
|
||||
#include "base/path_service.h"
|
||||
#include "content/public/common/content_paths.h"
|
||||
|
||||
namespace brightray {
|
||||
base::FilePath MainApplicationBundlePath();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
base::FilePath GetFrameworksPath() {
|
||||
return brightray::MainApplicationBundlePath().Append("Contents")
|
||||
.Append("Frameworks");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomMainDelegate::AtomMainDelegate() {
|
||||
@@ -40,6 +61,16 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||
void AtomMainDelegate::PreSandboxStartup() {
|
||||
brightray::MainDelegate::PreSandboxStartup();
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Override the path to helper process, since third party users may want to
|
||||
// change the application name.
|
||||
base::FilePath helper_path = GetFrameworksPath().Append("Atom Helper.app")
|
||||
.Append("Contents")
|
||||
.Append("MacOS")
|
||||
.Append("Atom Helper");
|
||||
PathService::Override(content::CHILD_PROCESS_EXE, helper_path);
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
// Disable renderer sandbox for most of node's functions.
|
||||
CommandLine* command_line = CommandLine::ForCurrentProcess();
|
||||
command_line->AppendSwitch(switches::kNoSandbox);
|
||||
|
||||
46
atom.gyp
46
atom.gyp
@@ -19,6 +19,7 @@
|
||||
'browser/api/lib/menu.coffee',
|
||||
'browser/api/lib/menu-item.coffee',
|
||||
'browser/api/lib/power-monitor.coffee',
|
||||
'browser/api/lib/protocol.coffee',
|
||||
'browser/atom/atom.coffee',
|
||||
'browser/atom/objects-registry.coffee',
|
||||
'browser/atom/rpc-server.coffee',
|
||||
@@ -54,6 +55,8 @@
|
||||
'browser/api/atom_api_menu_win.h',
|
||||
'browser/api/atom_api_power_monitor.cc',
|
||||
'browser/api/atom_api_power_monitor.h',
|
||||
'browser/api/atom_api_protocol.cc',
|
||||
'browser/api/atom_api_protocol.h',
|
||||
'browser/api/atom_api_window.cc',
|
||||
'browser/api/atom_api_window.h',
|
||||
'browser/api/atom_browser_bindings.cc',
|
||||
@@ -94,12 +97,20 @@
|
||||
'browser/native_window_win.cc',
|
||||
'browser/native_window_win.h',
|
||||
'browser/native_window_observer.h',
|
||||
'browser/net/adapter_request_job.cc',
|
||||
'browser/net/adapter_request_job.h',
|
||||
'browser/net/atom_url_request_job_factory.cc',
|
||||
'browser/net/atom_url_request_job_factory.h',
|
||||
'browser/net/url_request_string_job.cc',
|
||||
'browser/net/url_request_string_job.h',
|
||||
'browser/ui/accelerator_util.cc',
|
||||
'browser/ui/accelerator_util.h',
|
||||
'browser/ui/accelerator_util_mac.mm',
|
||||
'browser/ui/accelerator_util_win.cc',
|
||||
'browser/ui/atom_menu_controller_mac.h',
|
||||
'browser/ui/atom_menu_controller_mac.mm',
|
||||
'browser/ui/cocoa/custom_frame_view.h',
|
||||
'browser/ui/cocoa/custom_frame_view.mm',
|
||||
'browser/ui/file_dialog.h',
|
||||
'browser/ui/file_dialog_mac.mm',
|
||||
'browser/ui/file_dialog_win.cc',
|
||||
@@ -130,6 +141,8 @@
|
||||
'common/api/atom_extensions.h',
|
||||
'common/api/object_life_monitor.cc',
|
||||
'common/api/object_life_monitor.h',
|
||||
'common/draggable_region.cc',
|
||||
'common/draggable_region.h',
|
||||
'common/node_bindings.cc',
|
||||
'common/node_bindings.h',
|
||||
'common/node_bindings_mac.cc',
|
||||
@@ -275,6 +288,7 @@
|
||||
'destination': '<(PRODUCT_DIR)',
|
||||
'files': [
|
||||
'<(libchromiumcontent_library_dir)/chromiumcontent.dll',
|
||||
'<(libchromiumcontent_library_dir)/ffmpegsumo.dll',
|
||||
'<(libchromiumcontent_library_dir)/icudt.dll',
|
||||
'<(libchromiumcontent_library_dir)/libGLESv2.dll',
|
||||
'<(libchromiumcontent_resources_dir)/content_shell.pak',
|
||||
@@ -469,5 +483,37 @@
|
||||
}, # target helper
|
||||
],
|
||||
}], # OS==Mac
|
||||
['OS=="win"', {
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'generate_node_lib',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'<(project_name)',
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Create node.lib',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/atom.lib',
|
||||
'<(libchromiumcontent_library_dir)/chromiumcontent.dll.lib',
|
||||
],
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/node.lib',
|
||||
],
|
||||
'action': [
|
||||
'lib.exe',
|
||||
'/nologo',
|
||||
# We can't use <(_outputs) here because that escapes the
|
||||
# backslash in the path, which confuses lib.exe.
|
||||
'/OUT:<(PRODUCT_DIR)\\node.lib',
|
||||
'<@(_inputs)',
|
||||
],
|
||||
'msvs_cygwin_shell': 0,
|
||||
},
|
||||
],
|
||||
}, # target generate_node_lib
|
||||
],
|
||||
}], # OS==win
|
||||
],
|
||||
}
|
||||
|
||||
363
browser/api/atom_api_protocol.cc
Normal file
363
browser/api/atom_api_protocol.cc
Normal file
@@ -0,0 +1,363 @@
|
||||
// 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 "browser/api/atom_api_protocol.h"
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "browser/atom_browser_context.h"
|
||||
#include "browser/net/adapter_request_job.h"
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "vendor/node/src/node.h"
|
||||
#include "vendor/node/src/node_internals.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
namespace {
|
||||
|
||||
// The protocol module object.
|
||||
v8::Persistent<v8::Object> g_protocol_object;
|
||||
|
||||
// Registered protocol handlers.
|
||||
typedef std::map<std::string, v8::Persistent<v8::Function>> HandlersMap;
|
||||
static HandlersMap g_handlers;
|
||||
|
||||
// Emit an event for the protocol module.
|
||||
void EmitEventInUI(const std::string& event, const std::string& parameter) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
v8::Local<v8::Value> argv[] = {
|
||||
v8::String::New(event.data(), event.size()),
|
||||
v8::String::New(parameter.data(), parameter.size()),
|
||||
};
|
||||
node::MakeCallback(g_protocol_object, "emit", arraysize(argv), argv);
|
||||
}
|
||||
|
||||
// Convert the URLRequest object to V8 object.
|
||||
v8::Handle<v8::Object> ConvertURLRequestToV8Object(
|
||||
const net::URLRequest* request) {
|
||||
v8::Local<v8::Object> obj = v8::Object::New();
|
||||
obj->Set(v8::String::New("method"),
|
||||
v8::String::New(request->method().c_str()));
|
||||
obj->Set(v8::String::New("url"),
|
||||
v8::String::New(request->url().spec().c_str()));
|
||||
obj->Set(v8::String::New("referrer"),
|
||||
v8::String::New(request->referrer().c_str()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Get the job factory.
|
||||
AtomURLRequestJobFactory* GetRequestJobFactory() {
|
||||
return static_cast<AtomURLRequestJobFactory*>(
|
||||
const_cast<net::URLRequestJobFactory*>(
|
||||
static_cast<content::BrowserContext*>(AtomBrowserContext::Get())->
|
||||
GetRequestContext()->GetURLRequestContext()->job_factory()));
|
||||
}
|
||||
|
||||
class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
public:
|
||||
CustomProtocolRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: AdapterRequestJob(protocol_handler, request, network_delegate) {
|
||||
}
|
||||
|
||||
// AdapterRequestJob:
|
||||
virtual void GetJobTypeInUI() OVERRIDE {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
|
||||
// Call the JS handler.
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::Value> argv[] = {
|
||||
ConvertURLRequestToV8Object(request()),
|
||||
};
|
||||
v8::Handle<v8::Value> result = g_handlers[request()->url().scheme()]->Call(
|
||||
v8::Context::GetCurrent()->Global(), arraysize(argv), argv);
|
||||
|
||||
// Determine the type of the job we are going to create.
|
||||
if (result->IsString()) {
|
||||
std::string data = *v8::String::Utf8Value(result);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
|
||||
GetWeakPtr(),
|
||||
"text/plain",
|
||||
"UTF-8",
|
||||
data));
|
||||
return;
|
||||
} else if (result->IsObject()) {
|
||||
v8::Handle<v8::Object> obj = result->ToObject();
|
||||
std::string name = *v8::String::Utf8Value(obj->GetConstructorName());
|
||||
if (name == "RequestStringJob") {
|
||||
std::string mime_type = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("mimeType")));
|
||||
std::string charset = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("charset")));
|
||||
std::string data = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("data")));
|
||||
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
|
||||
GetWeakPtr(),
|
||||
mime_type,
|
||||
charset,
|
||||
data));
|
||||
return;
|
||||
} else if (name == "RequestFileJob") {
|
||||
base::FilePath path = base::FilePath::FromUTF8Unsafe(
|
||||
*v8::String::Utf8Value(obj->Get(v8::String::New("path"))));
|
||||
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
|
||||
GetWeakPtr(),
|
||||
path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try the default protocol handler if we have.
|
||||
if (default_protocol_handler()) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
|
||||
GetWeakPtr()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to the not implemented error.
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
|
||||
GetWeakPtr(),
|
||||
net::ERR_NOT_IMPLEMENTED));
|
||||
}
|
||||
};
|
||||
|
||||
// Always return the same CustomProtocolRequestJob for all requests, because
|
||||
// the content API needs the ProtocolHandler to return a job immediately, and
|
||||
// getting the real job from the JS requires asynchronous calls, so we have
|
||||
// to create an adapter job first.
|
||||
// Users can also pass an extra ProtocolHandler as the fallback one when
|
||||
// registered handler doesn't want to deal with the request.
|
||||
class CustomProtocolHandler : public ProtocolHandler {
|
||||
public:
|
||||
explicit CustomProtocolHandler(ProtocolHandler* protocol_handler = NULL)
|
||||
: protocol_handler_(protocol_handler) {
|
||||
}
|
||||
|
||||
virtual net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE {
|
||||
return new CustomProtocolRequestJob(protocol_handler_.get(),
|
||||
request,
|
||||
network_delegate);
|
||||
}
|
||||
|
||||
ProtocolHandler* ReleaseDefaultProtocolHandler() {
|
||||
return protocol_handler_.release();
|
||||
}
|
||||
|
||||
ProtocolHandler* original_handler() { return protocol_handler_.get(); }
|
||||
|
||||
private:
|
||||
scoped_ptr<ProtocolHandler> protocol_handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::RegisterProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
if (g_handlers.find(scheme) != g_handlers.end() ||
|
||||
net::URLRequest::IsHandledProtocol(scheme))
|
||||
return node::ThrowError("The scheme is already registered");
|
||||
|
||||
// Store the handler in a map.
|
||||
if (!args[1]->IsFunction())
|
||||
return node::ThrowError("Handler must be a function");
|
||||
g_handlers[scheme] = v8::Persistent<v8::Function>::New(
|
||||
node::node_isolate, v8::Handle<v8::Function>::Cast(args[1]));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&RegisterProtocolInIO, scheme));
|
||||
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::UnregisterProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
|
||||
// Erase the handler from map.
|
||||
HandlersMap::iterator it(g_handlers.find(scheme));
|
||||
if (it == g_handlers.end())
|
||||
return node::ThrowError("The scheme has not been registered");
|
||||
g_handlers.erase(it);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&UnregisterProtocolInIO, scheme));
|
||||
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::IsHandledProtocol(const v8::Arguments& args) {
|
||||
return v8::Boolean::New(net::URLRequest::IsHandledProtocol(
|
||||
*v8::String::Utf8Value(args[0])));
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::InterceptProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
if (!GetRequestJobFactory()->HasProtocolHandler(scheme))
|
||||
return node::ThrowError("Cannot intercept procotol");
|
||||
|
||||
if (ContainsKey(g_handlers, scheme))
|
||||
return node::ThrowError("Cannot intercept custom procotols");
|
||||
|
||||
// Store the handler in a map.
|
||||
if (!args[1]->IsFunction())
|
||||
return node::ThrowError("Handler must be a function");
|
||||
g_handlers[scheme] = v8::Persistent<v8::Function>::New(
|
||||
node::node_isolate, v8::Handle<v8::Function>::Cast(args[1]));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&InterceptProtocolInIO, scheme));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::UninterceptProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
|
||||
// Erase the handler from map.
|
||||
HandlersMap::iterator it(g_handlers.find(scheme));
|
||||
if (it == g_handlers.end())
|
||||
return node::ThrowError("The scheme has not been registered");
|
||||
g_handlers.erase(it);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&UninterceptProtocolInIO,
|
||||
scheme));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
job_factory->SetProtocolHandler(scheme, new CustomProtocolHandler);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"registered",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
job_factory->SetProtocolHandler(scheme, NULL);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"unregistered",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
ProtocolHandler* original_handler = job_factory->GetProtocolHandler(scheme);
|
||||
if (original_handler == NULL) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"error",
|
||||
"There is no protocol handler to intercpet"));
|
||||
return;
|
||||
}
|
||||
|
||||
job_factory->ReplaceProtocol(scheme,
|
||||
new CustomProtocolHandler(original_handler));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"intercepted",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
|
||||
// Check if the protocol handler is intercepted.
|
||||
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
|
||||
job_factory->GetProtocolHandler(scheme));
|
||||
if (handler->original_handler() == NULL) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"error",
|
||||
"The protocol is not intercpeted"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the protocol handler to the orignal one and delete current
|
||||
// protocol handler.
|
||||
ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
|
||||
delete job_factory->ReplaceProtocol(scheme, original_handler);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"unintercepted",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::Initialize(v8::Handle<v8::Object> target) {
|
||||
// Remember the protocol object, used for emitting event later.
|
||||
g_protocol_object = v8::Persistent<v8::Object>::New(
|
||||
node::node_isolate, target);
|
||||
|
||||
node::SetMethod(target, "registerProtocol", RegisterProtocol);
|
||||
node::SetMethod(target, "unregisterProtocol", UnregisterProtocol);
|
||||
node::SetMethod(target, "isHandledProtocol", IsHandledProtocol);
|
||||
node::SetMethod(target, "interceptProtocol", InterceptProtocol);
|
||||
node::SetMethod(target, "uninterceptProtocol", UninterceptProtocol);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)
|
||||
43
browser/api/atom_api_protocol.h
Normal file
43
browser/api/atom_api_protocol.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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_API_PROTOCOL_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class Protocol {
|
||||
public:
|
||||
static void Initialize(v8::Handle<v8::Object> target);
|
||||
|
||||
private:
|
||||
static v8::Handle<v8::Value> RegisterProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> UnregisterProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> IsHandledProtocol(const v8::Arguments& args);
|
||||
|
||||
static v8::Handle<v8::Value> InterceptProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> UninterceptProtocol(const v8::Arguments& args);
|
||||
|
||||
static void RegisterProtocolInIO(const std::string& scheme);
|
||||
static void UnregisterProtocolInIO(const std::string& scheme);
|
||||
|
||||
static void InterceptProtocolInIO(const std::string& scheme);
|
||||
static void UninterceptProtocolInIO(const std::string& scheme);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Protocol);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
|
||||
@@ -56,6 +56,12 @@ 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");
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class Window : public 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;
|
||||
|
||||
20
browser/api/lib/protocol.coffee
Normal file
20
browser/api/lib/protocol.coffee
Normal file
@@ -0,0 +1,20 @@
|
||||
protocol = process.atomBinding 'protocol'
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
protocol[key] = value for key, value of EventEmitter.prototype
|
||||
|
||||
protocol.RequestStringJob =
|
||||
class RequestStringJob
|
||||
constructor: ({mimeType, charset, data}) ->
|
||||
if typeof data isnt 'string' and not data instanceof Buffer
|
||||
throw new TypeError('Data should be string or Buffer')
|
||||
|
||||
@mimeType = mimeType ? 'text/plain'
|
||||
@charset = charset ? 'UTF-8'
|
||||
@data = String data
|
||||
|
||||
protocol.RequestFileJob =
|
||||
class RequestFileJob
|
||||
constructor: (@path) ->
|
||||
|
||||
module.exports = protocol
|
||||
@@ -1,12 +1,18 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle output when running as GUI program.
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle output when running as GUI program.
|
||||
console.log = console.error = console.warn = process.log
|
||||
process.stdout.write = process.stderr.write = process.log
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
|
||||
# Provide default Content API implementations.
|
||||
atom = {}
|
||||
|
||||
|
||||
@@ -44,10 +44,13 @@ unwrapArgs = (processId, routingId, args) ->
|
||||
when 'remote-object' then objectsRegistry.get meta.id
|
||||
when 'array' then unwrapArgs processId, routingId, meta.value
|
||||
when 'object'
|
||||
ret = {}
|
||||
ret = v8Util.createObjectWithName meta.name
|
||||
for member in meta.members
|
||||
ret[member.name] = metaToValue(member.value)
|
||||
ret
|
||||
when 'function-with-return-value'
|
||||
returnValue = metaToValue meta.value
|
||||
-> returnValue
|
||||
when 'function'
|
||||
ret = ->
|
||||
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
|
||||
@@ -101,6 +104,16 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) ->
|
||||
catch e
|
||||
event.result = errorToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs processId, routingId, args
|
||||
constructor = objectsRegistry.get(id)[method]
|
||||
# Call new with array of arguments.
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.result = valueToMeta processId, routingId, obj
|
||||
catch e
|
||||
event.result = errorToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs processId, routingId, args
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
#include "browser/atom_browser_client.h"
|
||||
|
||||
#include "browser/atom_browser_main_parts.h"
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
#include "content/public/common/url_constants.h"
|
||||
#include "net/url_request/data_protocol_handler.h"
|
||||
#include "net/url_request/file_protocol_handler.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_storage.h"
|
||||
#include "vendor/brightray/browser/url_request_context_getter.h"
|
||||
#include "webkit/glue/webpreferences.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -15,6 +22,36 @@ AtomBrowserClient::AtomBrowserClient() {
|
||||
AtomBrowserClient::~AtomBrowserClient() {
|
||||
}
|
||||
|
||||
net::URLRequestContextGetter* AtomBrowserClient::CreateRequestContext(
|
||||
content::BrowserContext* browser_context,
|
||||
content::ProtocolHandlerMap* protocol_handlers) {
|
||||
content::ProtocolHandlerMap preset_handlers;
|
||||
std::swap(preset_handlers, *protocol_handlers);
|
||||
|
||||
// Create our implementaton of job factory.
|
||||
AtomURLRequestJobFactory* job_factory = new AtomURLRequestJobFactory;
|
||||
content::ProtocolHandlerMap::iterator it;
|
||||
for (it = preset_handlers.begin(); it != preset_handlers.end(); ++it)
|
||||
job_factory->SetProtocolHandler(it->first, it->second.release());
|
||||
job_factory->SetProtocolHandler(chrome::kDataScheme,
|
||||
new net::DataProtocolHandler);
|
||||
job_factory->SetProtocolHandler(chrome::kFileScheme,
|
||||
new net::FileProtocolHandler);
|
||||
|
||||
// Go through default procedure.
|
||||
net::URLRequestContextGetter* request_context_getter =
|
||||
brightray::BrowserClient::CreateRequestContext(browser_context,
|
||||
protocol_handlers);
|
||||
net::URLRequestContext* request_context =
|
||||
request_context_getter->GetURLRequestContext();
|
||||
|
||||
// Replace default job factory.
|
||||
storage_.reset(new net::URLRequestContextStorage(request_context));
|
||||
storage_->set_job_factory(job_factory);
|
||||
|
||||
return request_context_getter;
|
||||
}
|
||||
|
||||
void AtomBrowserClient::OverrideWebkitPrefs(
|
||||
content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
|
||||
#include "brightray/browser/browser_client.h"
|
||||
|
||||
namespace net {
|
||||
class URLRequestContextStorage;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserClient : public brightray::BrowserClient {
|
||||
@@ -15,6 +19,9 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||
virtual ~AtomBrowserClient();
|
||||
|
||||
protected:
|
||||
net::URLRequestContextGetter* CreateRequestContext(
|
||||
content::BrowserContext* browser_context,
|
||||
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
|
||||
virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
WebPreferences* prefs) OVERRIDE;
|
||||
@@ -27,6 +34,8 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||
virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
|
||||
const content::MainFunctionParams&) OVERRIDE;
|
||||
|
||||
scoped_ptr<net::URLRequestContextStorage> storage_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
var app = require('app');
|
||||
var argv = require('optimist').argv;
|
||||
var dialog = require('dialog');
|
||||
var path = require('path');
|
||||
|
||||
// Quit when all windows are closed and no other one is listening to this.
|
||||
app.on('window-all-closed', function() {
|
||||
if (app.listeners('window-all-closed').length == 1)
|
||||
app.quit();
|
||||
});
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (argv._.length > 0) {
|
||||
@@ -9,6 +16,7 @@ if (argv._.length > 0) {
|
||||
require(path.resolve(argv._[0]));
|
||||
} catch(e) {
|
||||
if (e.code == 'MODULE_NOT_FOUND') {
|
||||
console.error(e.stack);
|
||||
console.error('Specified app is invalid');
|
||||
process.exit(1);
|
||||
} else {
|
||||
|
||||
@@ -39,10 +39,13 @@ namespace atom {
|
||||
NativeWindow::NativeWindow(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
has_frame_(true),
|
||||
is_closed_(false),
|
||||
not_responding_(false),
|
||||
inspectable_web_contents_(
|
||||
brightray::InspectableWebContents::Create(web_contents)) {
|
||||
options->GetBoolean(switches::kFrame, &has_frame_);
|
||||
|
||||
web_contents->SetDelegate(this);
|
||||
|
||||
WindowList::AddWindow(this);
|
||||
@@ -250,6 +253,13 @@ 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());
|
||||
@@ -291,6 +301,8 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
|
||||
IPC_BEGIN_MESSAGE_MAP(NativeWindow, message)
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message_Sync, OnRendererMessageSync)
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_UpdateDraggableRegions,
|
||||
UpdateDraggableRegions)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class Size;
|
||||
namespace atom {
|
||||
|
||||
class AtomJavaScriptDialogManager;
|
||||
struct DraggableRegion;
|
||||
|
||||
class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
public content::WebContentsObserver,
|
||||
@@ -114,6 +115,8 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
observers_.RemoveObserver(obs);
|
||||
}
|
||||
|
||||
bool has_frame() const { return has_frame_; }
|
||||
|
||||
protected:
|
||||
explicit NativeWindow(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options);
|
||||
@@ -125,6 +128,10 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
void NotifyWindowClosed();
|
||||
void NotifyWindowBlur();
|
||||
|
||||
// Called when the window needs to update its draggable region.
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) = 0;
|
||||
|
||||
// Implementations of content::WebContentsDelegate.
|
||||
virtual void WebContentsCreated(content::WebContents* source_contents,
|
||||
int64 source_frame_id,
|
||||
@@ -142,6 +149,7 @@ 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;
|
||||
@@ -159,6 +167,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) OVERRIDE;
|
||||
|
||||
// Whether window has standard frame.
|
||||
bool has_frame_;
|
||||
|
||||
private:
|
||||
void RendererUnresponsiveDelayed();
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "browser/native_window.h"
|
||||
|
||||
class SkRegion;
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindowMac : public NativeWindow {
|
||||
@@ -52,11 +54,22 @@ class NativeWindowMac : public NativeWindow {
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
|
||||
NSWindow*& window() { return window_; }
|
||||
|
||||
void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); }
|
||||
|
||||
// Returns true if |point| in local Cocoa coordinate system falls within
|
||||
// the draggable region.
|
||||
bool IsWithinDraggableRegion(NSPoint point) const;
|
||||
|
||||
// Called to handle a mouse event.
|
||||
void HandleMouseEvent(NSEvent* event);
|
||||
|
||||
NSWindow*& window() { return window_; }
|
||||
SkRegion* draggable_region() const { return draggable_region_.get(); }
|
||||
|
||||
protected:
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) OVERRIDE;
|
||||
|
||||
// Implementations of content::WebContentsDelegate.
|
||||
virtual void HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
@@ -65,6 +78,9 @@ class NativeWindowMac : public NativeWindow {
|
||||
private:
|
||||
void InstallView();
|
||||
void UninstallView();
|
||||
void InstallDraggableRegionViews();
|
||||
void UpdateDraggableRegionsForCustomDrag(
|
||||
const std::vector<DraggableRegion>& regions);
|
||||
|
||||
NSWindow* window_;
|
||||
|
||||
@@ -72,6 +88,18 @@ class NativeWindowMac : public NativeWindow {
|
||||
|
||||
NSInteger attention_request_id_; // identifier from requestUserAttention
|
||||
|
||||
// For system drag, the whole window is draggable and the non-draggable areas
|
||||
// have to been explicitly excluded.
|
||||
std::vector<gfx::Rect> system_drag_exclude_areas_;
|
||||
|
||||
// For custom drag, the whole window is non-draggable and the draggable region
|
||||
// has to been explicitly provided.
|
||||
scoped_ptr<SkRegion> draggable_region_; // used in custom drag.
|
||||
|
||||
// Mouse location since the last mouse event, in screen coordinates. This is
|
||||
// used in custom drag to compute the window movement.
|
||||
NSPoint last_mouse_offset_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowMac);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,14 +14,28 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#import "browser/atom_event_processing_window.h"
|
||||
#import "browser/ui/cocoa/custom_frame_view.h"
|
||||
#include "brightray/browser/inspectable_web_contents.h"
|
||||
#include "brightray/browser/inspectable_web_contents_view.h"
|
||||
#include "common/draggable_region.h"
|
||||
#include "common/options_switches.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
|
||||
@interface NSWindow (NSPrivateApis)
|
||||
- (void)setBottomCornerRounded:(BOOL)rounded;
|
||||
@end
|
||||
|
||||
@interface NSView (WebContentsView)
|
||||
- (void)setMouseDownCanMoveWindow:(BOOL)can_move;
|
||||
@end
|
||||
|
||||
@interface NSView (PrivateMethods)
|
||||
- (CGFloat)roundedCornerRadius;
|
||||
@end
|
||||
|
||||
@interface AtomNSWindowDelegate : NSObject<NSWindowDelegate> {
|
||||
@private
|
||||
atom::NativeWindowMac* shell_;
|
||||
@@ -54,6 +68,13 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
||||
if (!shell_->has_frame()) {
|
||||
NSWindow* window = shell_->GetNativeWindow();
|
||||
[[window standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface AtomNSWindow : AtomEventProcessingWindow {
|
||||
@@ -80,6 +101,83 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface AtomFramelessNSWindow : AtomNSWindow
|
||||
- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
|
||||
@end
|
||||
|
||||
@implementation AtomFramelessNSWindow
|
||||
|
||||
- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
|
||||
[[NSBezierPath bezierPathWithRect:rect] addClip];
|
||||
[[NSColor clearColor] set];
|
||||
NSRectFill(rect);
|
||||
|
||||
// Set up our clip.
|
||||
CGFloat cornerRadius = 4.0;
|
||||
if ([view respondsToSelector:@selector(roundedCornerRadius)])
|
||||
cornerRadius = [view roundedCornerRadius];
|
||||
[[NSBezierPath bezierPathWithRoundedRect:[view bounds]
|
||||
xRadius:cornerRadius
|
||||
yRadius:cornerRadius] addClip];
|
||||
[[NSColor whiteColor] set];
|
||||
NSRectFill(rect);
|
||||
}
|
||||
|
||||
+ (NSRect)frameRectForContentRect:(NSRect)contentRect
|
||||
styleMask:(NSUInteger)mask {
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
+ (NSRect)contentRectForFrameRect:(NSRect)frameRect
|
||||
styleMask:(NSUInteger)mask {
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
- (NSRect)frameRectForContentRect:(NSRect)contentRect {
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ControlRegionView : NSView {
|
||||
@private
|
||||
atom::NativeWindowMac* shellWindow_; // Weak; owns self.
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ControlRegionView
|
||||
|
||||
- (id)initWithShellWindow:(atom::NativeWindowMac*)shellWindow {
|
||||
if ((self = [super init]))
|
||||
shellWindow_ = shellWindow;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSView*)hitTest:(NSPoint)aPoint {
|
||||
if (!shellWindow_->IsWithinDraggableRegion(aPoint)) {
|
||||
return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event {
|
||||
shellWindow_->HandleMouseEvent(event);
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(NSEvent*)event {
|
||||
shellWindow_->HandleMouseEvent(event);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace atom {
|
||||
|
||||
NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
|
||||
@@ -100,7 +198,13 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
|
||||
NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
|
||||
NSMiniaturizableWindowMask | NSResizableWindowMask |
|
||||
NSTexturedBackgroundWindowMask;
|
||||
AtomNSWindow* atom_window = [[AtomNSWindow alloc]
|
||||
AtomNSWindow* atom_window = has_frame_ ?
|
||||
[[AtomNSWindow alloc]
|
||||
initWithContentRect:cocoa_bounds
|
||||
styleMask:style_mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:YES] :
|
||||
[[AtomFramelessNSWindow alloc]
|
||||
initWithContentRect:cocoa_bounds
|
||||
styleMask:style_mask
|
||||
backing:NSBackingStoreBuffered
|
||||
@@ -119,6 +223,9 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
|
||||
[window() setCollectionBehavior:collectionBehavior];
|
||||
}
|
||||
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
InstallView();
|
||||
}
|
||||
|
||||
@@ -324,6 +431,43 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
|
||||
return window();
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const {
|
||||
if (!draggable_region_)
|
||||
return false;
|
||||
NSView* webView = web_contents()->GetView()->GetNativeView();
|
||||
NSInteger webViewHeight = NSHeight([webView bounds]);
|
||||
// |draggable_region_| is stored in local platform-indepdent coordiate system
|
||||
// while |point| is in local Cocoa coordinate system. Do the conversion
|
||||
// to match these two.
|
||||
return draggable_region_->contains(point.x, webViewHeight - point.y);
|
||||
}
|
||||
|
||||
void NativeWindowMac::HandleMouseEvent(NSEvent* event) {
|
||||
NSPoint current_mouse_location =
|
||||
[window() convertBaseToScreen:[event locationInWindow]];
|
||||
|
||||
if ([event type] == NSLeftMouseDown) {
|
||||
NSPoint frame_origin = [window() frame].origin;
|
||||
last_mouse_offset_ = NSMakePoint(
|
||||
frame_origin.x - current_mouse_location.x,
|
||||
frame_origin.y - current_mouse_location.y);
|
||||
} else if ([event type] == NSLeftMouseDragged) {
|
||||
[window() setFrameOrigin:NSMakePoint(
|
||||
current_mouse_location.x + last_mouse_offset_.x,
|
||||
current_mouse_location.y + last_mouse_offset_.y)];
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
// Draggable region is not supported for non-frameless window.
|
||||
if (has_frame_)
|
||||
return;
|
||||
|
||||
UpdateDraggableRegionsForCustomDrag(regions);
|
||||
InstallDraggableRegionViews();
|
||||
}
|
||||
|
||||
void NativeWindowMac::HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
@@ -339,16 +483,87 @@ void NativeWindowMac::HandleKeyboardEvent(
|
||||
|
||||
void NativeWindowMac::InstallView() {
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[view setFrame:[[window() contentView] bounds]];
|
||||
[[window() contentView] addSubview:view];
|
||||
if (has_frame_) {
|
||||
[view setFrame:[[window() contentView] bounds]];
|
||||
[[window() contentView] addSubview:view];
|
||||
} else {
|
||||
NSView* frameView = [[window() contentView] superview];
|
||||
[view setFrame:[frameView bounds]];
|
||||
[frameView addSubview:view];
|
||||
|
||||
[[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
|
||||
[[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
|
||||
[[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
|
||||
[[window() standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UninstallView() {
|
||||
NSView* view = GetWebContents()->GetView()->GetNativeView();
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
void NativeWindowMac::InstallDraggableRegionViews() {
|
||||
DCHECK(!has_frame_);
|
||||
|
||||
// All ControlRegionViews should be added as children of the WebContentsView,
|
||||
// because WebContentsView will be removed and re-added when entering and
|
||||
// leaving fullscreen mode.
|
||||
NSView* webView = GetWebContents()->GetView()->GetNativeView();
|
||||
NSInteger webViewHeight = NSHeight([webView bounds]);
|
||||
|
||||
// Remove all ControlRegionViews that are added last time.
|
||||
// Note that [webView subviews] returns the view's mutable internal array and
|
||||
// it should be copied to avoid mutating the original array while enumerating
|
||||
// it.
|
||||
scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
|
||||
for (NSView* subview in subviews.get())
|
||||
if ([subview isKindOfClass:[ControlRegionView class]])
|
||||
[subview removeFromSuperview];
|
||||
|
||||
// Create and add ControlRegionView for each region that needs to be excluded
|
||||
// from the dragging.
|
||||
for (std::vector<gfx::Rect>::const_iterator iter =
|
||||
system_drag_exclude_areas_.begin();
|
||||
iter != system_drag_exclude_areas_.end();
|
||||
++iter) {
|
||||
scoped_nsobject<NSView> controlRegion(
|
||||
[[ControlRegionView alloc] initWithShellWindow:this]);
|
||||
[controlRegion setFrame:NSMakeRect(iter->x(),
|
||||
webViewHeight - iter->bottom(),
|
||||
iter->width(),
|
||||
iter->height())];
|
||||
[webView addSubview:controlRegion];
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UpdateDraggableRegionsForCustomDrag(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
// We still need one ControlRegionView to cover the whole window such that
|
||||
// mouse events could be captured.
|
||||
NSView* web_view = GetWebContents()->GetView()->GetNativeView();
|
||||
gfx::Rect window_bounds(
|
||||
0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds]));
|
||||
system_drag_exclude_areas_.clear();
|
||||
system_drag_exclude_areas_.push_back(window_bounds);
|
||||
|
||||
// Aggregate the draggable areas and non-draggable areas such that hit test
|
||||
// could be performed easily.
|
||||
SkRegion* draggable_region = new SkRegion;
|
||||
for (std::vector<DraggableRegion>::const_iterator iter = regions.begin();
|
||||
iter != regions.end();
|
||||
++iter) {
|
||||
const DraggableRegion& region = *iter;
|
||||
draggable_region->op(
|
||||
region.bounds.x(),
|
||||
region.bounds.y(),
|
||||
region.bounds.right(),
|
||||
region.bounds.bottom(),
|
||||
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
|
||||
}
|
||||
draggable_region_.reset(draggable_region);
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options) {
|
||||
|
||||
@@ -17,6 +17,9 @@ 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) {}
|
||||
|
||||
|
||||
@@ -6,8 +6,14 @@
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "common/draggable_region.h"
|
||||
#include "common/options_switches.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "ui/gfx/path.h"
|
||||
#include "ui/views/controls/webview/webview.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/window/client_view.h"
|
||||
@@ -17,6 +23,9 @@ namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kResizeInsideBoundsSize = 5;
|
||||
const int kResizeAreaCornerSize = 16;
|
||||
|
||||
class NativeWindowClientView : public views::ClientView {
|
||||
public:
|
||||
NativeWindowClientView(views::Widget* widget,
|
||||
@@ -60,6 +69,99 @@ class NativeWindowFrameView : public views::NativeFrameView {
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowFrameView);
|
||||
};
|
||||
|
||||
class NativeWindowFramelessView : public views::NonClientFrameView {
|
||||
public:
|
||||
explicit NativeWindowFramelessView(views::Widget* frame,
|
||||
NativeWindowWin* shell)
|
||||
: frame_(frame),
|
||||
shell_(shell) {
|
||||
}
|
||||
virtual ~NativeWindowFramelessView() {}
|
||||
|
||||
// views::NonClientFrameView implementations:
|
||||
virtual gfx::Rect NativeWindowFramelessView::GetBoundsForClientView() const
|
||||
OVERRIDE {
|
||||
return bounds();
|
||||
}
|
||||
|
||||
virtual gfx::Rect NativeWindowFramelessView::GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const OVERRIDE {
|
||||
gfx::Rect window_bounds = client_bounds;
|
||||
// Enforce minimum size (1, 1) in case that client_bounds is passed with
|
||||
// empty size. This could occur when the frameless window is being
|
||||
// initialized.
|
||||
if (window_bounds.IsEmpty()) {
|
||||
window_bounds.set_width(1);
|
||||
window_bounds.set_height(1);
|
||||
}
|
||||
return window_bounds;
|
||||
}
|
||||
|
||||
virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
|
||||
if (frame_->IsFullscreen())
|
||||
return HTCLIENT;
|
||||
|
||||
// Check the frame first, as we allow a small area overlapping the contents
|
||||
// to be used for resize handles.
|
||||
bool can_ever_resize = frame_->widget_delegate() ?
|
||||
frame_->widget_delegate()->CanResize() :
|
||||
false;
|
||||
// Don't allow overlapping resize handles when the window is maximized or
|
||||
// fullscreen, as it can't be resized in those states.
|
||||
int resize_border =
|
||||
frame_->IsMaximized() || frame_->IsFullscreen() ? 0 :
|
||||
kResizeInsideBoundsSize;
|
||||
int frame_component = GetHTComponentForFrame(point,
|
||||
resize_border,
|
||||
resize_border,
|
||||
kResizeAreaCornerSize,
|
||||
kResizeAreaCornerSize,
|
||||
can_ever_resize);
|
||||
if (frame_component != HTNOWHERE)
|
||||
return frame_component;
|
||||
|
||||
// Check for possible draggable region in the client area for the frameless
|
||||
// window.
|
||||
if (shell_->draggable_region() &&
|
||||
shell_->draggable_region()->contains(point.x(), point.y()))
|
||||
return HTCAPTION;
|
||||
|
||||
int client_component = frame_->client_view()->NonClientHitTest(point);
|
||||
if (client_component != HTNOWHERE)
|
||||
return client_component;
|
||||
|
||||
// Caption is a safe default.
|
||||
return HTCAPTION;
|
||||
}
|
||||
|
||||
virtual void GetWindowMask(const gfx::Size& size,
|
||||
gfx::Path* window_mask) OVERRIDE {}
|
||||
virtual void ResetWindowControls() OVERRIDE {}
|
||||
virtual void UpdateWindowIcon() OVERRIDE {}
|
||||
virtual void UpdateWindowTitle() OVERRIDE {}
|
||||
|
||||
// views::View implementations:
|
||||
virtual gfx::Size NativeWindowFramelessView::GetPreferredSize() OVERRIDE {
|
||||
gfx::Size pref = frame_->client_view()->GetPreferredSize();
|
||||
gfx::Rect bounds(0, 0, pref.width(), pref.height());
|
||||
return frame_->non_client_view()->GetWindowBoundsForClientBounds(
|
||||
bounds).size();
|
||||
}
|
||||
|
||||
virtual gfx::Size GetMinimumSize() OVERRIDE {
|
||||
return shell_->GetMinimumSize();
|
||||
}
|
||||
|
||||
virtual gfx::Size GetMaximumSize() OVERRIDE {
|
||||
return shell_->GetMaximumSize();
|
||||
}
|
||||
|
||||
private:
|
||||
views::Widget* frame_;
|
||||
NativeWindowWin* shell_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowFramelessView);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -71,6 +173,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
|
||||
resizable_(true) {
|
||||
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
|
||||
params.delegate = this;
|
||||
params.remove_standard_frame = !has_frame_;
|
||||
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||
window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
|
||||
window_->Init(params);
|
||||
@@ -83,6 +186,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
|
||||
window_->CenterWindow(size);
|
||||
|
||||
web_view_->SetWebContents(web_contents);
|
||||
OnViewWasResized();
|
||||
}
|
||||
|
||||
NativeWindowWin::~NativeWindowWin() {
|
||||
@@ -221,6 +325,30 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() {
|
||||
return window_->GetNativeView();
|
||||
}
|
||||
|
||||
void NativeWindowWin::UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
if (has_frame_)
|
||||
return;
|
||||
|
||||
SkRegion* draggable_region = new SkRegion;
|
||||
|
||||
// By default, the whole window is non-draggable. We need to explicitly
|
||||
// include those draggable regions.
|
||||
for (std::vector<DraggableRegion>::const_iterator iter = regions.begin();
|
||||
iter != regions.end(); ++iter) {
|
||||
const DraggableRegion& region = *iter;
|
||||
draggable_region->op(
|
||||
region.bounds.x(),
|
||||
region.bounds.y(),
|
||||
region.bounds.right(),
|
||||
region.bounds.bottom(),
|
||||
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
|
||||
}
|
||||
|
||||
draggable_region_.reset(draggable_region);
|
||||
OnViewWasResized();
|
||||
}
|
||||
|
||||
void NativeWindowWin::HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
@@ -264,7 +392,40 @@ views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) {
|
||||
|
||||
views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView(
|
||||
views::Widget* widget) {
|
||||
return new NativeWindowFrameView(widget, this);
|
||||
if (has_frame_)
|
||||
return new NativeWindowFrameView(widget, this);
|
||||
|
||||
return new NativeWindowFramelessView(widget, this);
|
||||
}
|
||||
|
||||
void NativeWindowWin::OnViewWasResized() {
|
||||
// Set the window shape of the RWHV.
|
||||
gfx::Size sz = web_view_->size();
|
||||
int height = sz.height(), width = sz.width();
|
||||
gfx::Path path;
|
||||
path.addRect(0, 0, width, height);
|
||||
SetWindowRgn(web_contents()->GetView()->GetNativeView(),
|
||||
path.CreateNativeRegion(),
|
||||
1);
|
||||
|
||||
SkRegion* rgn = new SkRegion;
|
||||
if (!window_->IsFullscreen() && !window_->IsMaximized()) {
|
||||
if (draggable_region())
|
||||
rgn->op(*draggable_region(), SkRegion::kUnion_Op);
|
||||
|
||||
if (!has_frame_ && CanResize()) {
|
||||
rgn->op(0, 0, width, kResizeInsideBoundsSize, SkRegion::kUnion_Op);
|
||||
rgn->op(0, 0, kResizeInsideBoundsSize, height, SkRegion::kUnion_Op);
|
||||
rgn->op(width - kResizeInsideBoundsSize, 0, width, height,
|
||||
SkRegion::kUnion_Op);
|
||||
rgn->op(0, height - kResizeInsideBoundsSize, width, height,
|
||||
SkRegion::kUnion_Op);
|
||||
}
|
||||
}
|
||||
|
||||
content::WebContents* web_contents = GetWebContents();
|
||||
if (web_contents->GetRenderViewHost()->GetView())
|
||||
web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -60,7 +60,12 @@ class NativeWindowWin : public NativeWindow,
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
|
||||
SkRegion* draggable_region() { return draggable_region_.get(); }
|
||||
|
||||
protected:
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) OVERRIDE;
|
||||
|
||||
// Overridden from content::WebContentsDelegate:
|
||||
virtual void HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
@@ -79,9 +84,13 @@ class NativeWindowWin : public NativeWindow,
|
||||
views::Widget* widget) OVERRIDE;
|
||||
|
||||
private:
|
||||
void OnViewWasResized();
|
||||
|
||||
scoped_ptr<views::Widget> window_;
|
||||
views::WebView* web_view_; // managed by window_.
|
||||
|
||||
scoped_ptr<SkRegion> draggable_region_;
|
||||
|
||||
bool resizable_;
|
||||
string16 title_;
|
||||
gfx::Size minimum_size_;
|
||||
|
||||
105
browser/net/adapter_request_job.cc
Normal file
105
browser/net/adapter_request_job.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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 "browser/net/adapter_request_job.h"
|
||||
|
||||
#include "browser/net/url_request_string_job.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_error_job.h"
|
||||
#include "net/url_request/url_request_file_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AdapterRequestJob::AdapterRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: URLRequestJob(request, network_delegate),
|
||||
protocol_handler_(protocol_handler),
|
||||
weak_factory_(this) {
|
||||
}
|
||||
|
||||
void AdapterRequestJob::Start() {
|
||||
DCHECK(!real_job_);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::GetJobTypeInUI,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void AdapterRequestJob::Kill() {
|
||||
DCHECK(real_job_);
|
||||
real_job_->Kill();
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int *bytes_read) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->ReadRawData(buf, buf_size, bytes_read);
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::IsRedirectResponse(GURL* location,
|
||||
int* http_status_code) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->IsRedirectResponse(location, http_status_code);
|
||||
}
|
||||
|
||||
net::Filter* AdapterRequestJob::SetupFilter() const {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->SetupFilter();
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::GetMimeType(std::string* mime_type) const {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->GetMimeType(mime_type);
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::GetCharset(std::string* charset) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->GetCharset(charset);
|
||||
}
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> AdapterRequestJob::GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateErrorJobAndStart(int error_code) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new net::URLRequestErrorJob(
|
||||
request(), network_delegate(), error_code);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateStringJobAndStart(const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new URLRequestStringJob(
|
||||
request(), network_delegate(), mime_type, charset, data);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new net::URLRequestFileJob(request(), network_delegate(), path);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
DCHECK(protocol_handler_);
|
||||
real_job_ = protocol_handler_->MaybeCreateJob(request(),
|
||||
network_delegate());
|
||||
if (!real_job_.get())
|
||||
CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED);
|
||||
else
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
68
browser/net/adapter_request_job.h
Normal file
68
browser/net/adapter_request_job.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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_NET_ADAPTER_REQUEST_JOB_H_
|
||||
#define ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Ask JS which type of job it wants, and then delegate corresponding methods.
|
||||
class AdapterRequestJob : public net::URLRequestJob {
|
||||
public:
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
AdapterRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate);
|
||||
|
||||
public:
|
||||
// net::URLRequestJob:
|
||||
virtual void Start() OVERRIDE;
|
||||
virtual void Kill() OVERRIDE;
|
||||
virtual bool ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int *bytes_read) OVERRIDE;
|
||||
virtual bool IsRedirectResponse(GURL* location,
|
||||
int* http_status_code) OVERRIDE;
|
||||
virtual net::Filter* SetupFilter() const OVERRIDE;
|
||||
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
|
||||
virtual bool GetCharset(std::string* charset) OVERRIDE;
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> GetWeakPtr();
|
||||
|
||||
ProtocolHandler* default_protocol_handler() { return protocol_handler_; }
|
||||
|
||||
// Override this function to determine which job should be started.
|
||||
virtual void GetJobTypeInUI() = 0;
|
||||
|
||||
void CreateErrorJobAndStart(int error_code);
|
||||
void CreateStringJobAndStart(const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data);
|
||||
void CreateFileJobAndStart(const base::FilePath& path);
|
||||
void CreateJobFromProtocolHandlerAndStart();
|
||||
|
||||
private:
|
||||
// The delegated request job.
|
||||
scoped_refptr<net::URLRequestJob> real_job_;
|
||||
|
||||
// Default protocol handler.
|
||||
ProtocolHandler* protocol_handler_;
|
||||
|
||||
base::WeakPtrFactory<AdapterRequestJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
|
||||
105
browser/net/atom_url_request_job_factory.cc
Normal file
105
browser/net/atom_url_request_job_factory.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "googleurl/src/gurl.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
AtomURLRequestJobFactory::AtomURLRequestJobFactory() {}
|
||||
|
||||
AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
|
||||
STLDeleteValues(&protocol_handler_map_);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::SetProtocolHandler(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
|
||||
if (!protocol_handler) {
|
||||
ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return false;
|
||||
|
||||
delete it->second;
|
||||
protocol_handler_map_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ContainsKey(protocol_handler_map_, scheme))
|
||||
return false;
|
||||
protocol_handler_map_[scheme] = protocol_handler;
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK(protocol_handler);
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
if (!ContainsKey(protocol_handler_map_, scheme))
|
||||
return NULL;
|
||||
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
|
||||
protocol_handler_map_[scheme] = protocol_handler;
|
||||
return original_protocol_handler;
|
||||
}
|
||||
|
||||
ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::HasProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
base::AutoLock locked(lock_);
|
||||
return ContainsKey(protocol_handler_map_, scheme);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return NULL;
|
||||
return it->second->MaybeCreateJob(request, network_delegate);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledProtocol(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
return HasProtocolHandler(scheme) ||
|
||||
net::URLRequest::IsHandledProtocol(scheme);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledURL(const GURL& url) const {
|
||||
if (!url.is_valid()) {
|
||||
// We handle error cases.
|
||||
return true;
|
||||
}
|
||||
return IsHandledProtocol(url.scheme());
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
61
browser/net/atom_url_request_job_factory.h
Normal file
61
browser/net/atom_url_request_job_factory.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Copyright (c) 2011 The Chromium Authors. 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_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
|
||||
public:
|
||||
AtomURLRequestJobFactory();
|
||||
virtual ~AtomURLRequestJobFactory();
|
||||
|
||||
// Sets the ProtocolHandler for a scheme. Returns true on success, false on
|
||||
// failure (a ProtocolHandler already exists for |scheme|). On success,
|
||||
// URLRequestJobFactory takes ownership of |protocol_handler|.
|
||||
bool SetProtocolHandler(const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler);
|
||||
|
||||
// Intercepts the ProtocolHandler for a scheme. Returns the original protocol
|
||||
// handler on success, otherwise returns NULL.
|
||||
ProtocolHandler* ReplaceProtocol(const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler);
|
||||
|
||||
// Returns the protocol handler registered with scheme.
|
||||
ProtocolHandler* GetProtocolHandler(const std::string& scheme) const;
|
||||
|
||||
// Whether the protocol handler is registered by the job factory.
|
||||
bool HasProtocolHandler(const std::string& scheme) const;
|
||||
|
||||
// URLRequestJobFactory implementation
|
||||
virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE;
|
||||
virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE;
|
||||
virtual bool IsHandledURL(const GURL& url) const OVERRIDE;
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
|
||||
|
||||
ProtocolHandlerMap protocol_handler_map_;
|
||||
|
||||
mutable base::Lock lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
||||
33
browser/net/url_request_string_job.cc
Normal file
33
browser/net/url_request_string_job.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 "browser/net/url_request_string_job.h"
|
||||
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
URLRequestStringJob::URLRequestStringJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data)
|
||||
: net::URLRequestSimpleJob(request, network_delegate),
|
||||
mime_type_(mime_type),
|
||||
charset_(charset),
|
||||
data_(data) {
|
||||
}
|
||||
|
||||
int URLRequestStringJob::GetData(
|
||||
std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
const net::CompletionCallback& callback) const {
|
||||
*mime_type = mime_type_;
|
||||
*charset = charset_;
|
||||
*data = data_;
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
36
browser/net/url_request_string_job.h
Normal file
36
browser/net/url_request_string_job.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// 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_NET_URL_REQUEST_STRING_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
|
||||
#include "net/url_request/url_request_simple_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestStringJob : public net::URLRequestSimpleJob {
|
||||
public:
|
||||
URLRequestStringJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data);
|
||||
|
||||
// URLRequestSimpleJob:
|
||||
virtual int GetData(std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
const net::CompletionCallback& callback) const OVERRIDE;
|
||||
|
||||
private:
|
||||
std::string mime_type_;
|
||||
std::string charset_;
|
||||
std::string data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestStringJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
56
browser/ui/cocoa/custom_frame_view.h
Normal file
56
browser/ui/cocoa/custom_frame_view.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// CustomFrameView is a class whose methods we swizzle into NSGrayFrame
|
||||
// on 10.7 and below, or NSThemeFrame on 10.8 and above, so that we can
|
||||
// support custom frame drawing. This is used with a textured window so that
|
||||
// AppKit does not draw a title bar.
|
||||
// This class is never to be instantiated on its own.
|
||||
// We explored a variety of ways to support custom frame drawing and custom
|
||||
// window widgets.
|
||||
// Our requirements were:
|
||||
// a) that we could fall back on standard system drawing at any time for the
|
||||
// "default theme"
|
||||
// b) We needed to be able to draw both a background pattern, and an overlay
|
||||
// graphic, and we need to be able to set the pattern phase of our background
|
||||
// window.
|
||||
// c) We had to be able to support "transparent" themes, so that you could see
|
||||
// through to the underlying windows in places without the system theme
|
||||
// getting in the way.
|
||||
//
|
||||
// Since we want "A" we couldn't just do a transparent borderless window. At
|
||||
// least I couldn't find the right combination of HITheme calls to make it draw
|
||||
// nicely, and I don't trust that the HITheme calls are going to exist in future
|
||||
// system versions.
|
||||
// "C" precluded us from inserting a view between the system frame and the
|
||||
// the content frame in Z order. To get the transparency we actually need to
|
||||
// replace the drawing of the system frame.
|
||||
// "B" precluded us from just setting a background color on the window.
|
||||
//
|
||||
// Originally we tried overriding the private API +frameViewForStyleMask: to
|
||||
// add our own subclass of NSGrayView to our window. Turns out that if you
|
||||
// subclass NSGrayView it does not draw correctly when you call NSGrayView's
|
||||
// drawRect. It appears that NSGrayView's drawRect: method (and that of its
|
||||
// superclasses) do lots of "isMemberOfClass/isKindOfClass" calls, and if your
|
||||
// class is NOT an instance of NSGrayView (as opposed to a subclass of
|
||||
// NSGrayView) then the system drawing will not work correctly.
|
||||
//
|
||||
// Given all of the above, we found swizzling drawRect in _load to be the
|
||||
// easiest and safest method of achieving our goals. We do the best we can to
|
||||
// check that everything is safe, and attempt to fallback gracefully if it is
|
||||
// not.
|
||||
|
||||
@interface NSWindow (CustomFrameView)
|
||||
|
||||
// To define custom window drawing behaviour, override this method on an
|
||||
// NSWindow subclass. Call the default method (on super) to draw the
|
||||
// default frame.
|
||||
// NOTE: Always call the default implementation (even if you just immediately
|
||||
// draw over it), otherwise the top-left and top-right corners of the window
|
||||
// won't be drawn correctly.
|
||||
- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
|
||||
|
||||
@end
|
||||
138
browser/ui/cocoa/custom_frame_view.mm
Normal file
138
browser/ui/cocoa/custom_frame_view.mm
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "browser/ui/cocoa/custom_frame_view.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
|
||||
namespace {
|
||||
BOOL gCanDrawTitle = NO;
|
||||
BOOL gCanGetCornerRadius = NO;
|
||||
} // namespace
|
||||
|
||||
@interface NSView (Swizzles)
|
||||
- (void)drawRectOriginal:(NSRect)rect;
|
||||
- (NSPoint)_fullScreenButtonOriginOriginal;
|
||||
@end
|
||||
|
||||
@interface NSWindow (FramedBrowserWindow)
|
||||
- (NSPoint)fullScreenButtonOriginAdjustment;
|
||||
@end
|
||||
|
||||
@implementation NSWindow (CustomFrameView)
|
||||
- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
|
||||
[view drawRectOriginal:rect];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface CustomFrameView : NSView
|
||||
|
||||
@end
|
||||
|
||||
@implementation CustomFrameView
|
||||
|
||||
// This is where we swizzle drawRect, and add in two methods that we
|
||||
// need. If any of these fail it shouldn't affect the functionality of the
|
||||
// others. If they all fail, we will lose window frame theming and
|
||||
// roll overs for our close widgets, but things should still function
|
||||
// correctly.
|
||||
+ (void)load {
|
||||
base::mac::ScopedNSAutoreleasePool pool;
|
||||
|
||||
// On 10.8+ the background for textured windows are no longer drawn by
|
||||
// NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
|
||||
Class borderViewClass = NSClassFromString(
|
||||
base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame");
|
||||
DCHECK(borderViewClass);
|
||||
if (!borderViewClass) return;
|
||||
|
||||
// Exchange draw rect.
|
||||
Method m0 = class_getInstanceMethod([self class], @selector(drawRect:));
|
||||
DCHECK(m0);
|
||||
if (m0) {
|
||||
BOOL didAdd = class_addMethod(borderViewClass,
|
||||
@selector(drawRectOriginal:),
|
||||
method_getImplementation(m0),
|
||||
method_getTypeEncoding(m0));
|
||||
DCHECK(didAdd);
|
||||
if (didAdd) {
|
||||
Method m1 = class_getInstanceMethod(borderViewClass,
|
||||
@selector(drawRect:));
|
||||
Method m2 = class_getInstanceMethod(borderViewClass,
|
||||
@selector(drawRectOriginal:));
|
||||
DCHECK(m1 && m2);
|
||||
if (m1 && m2) {
|
||||
method_exchangeImplementations(m1, m2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swizzle the method that sets the origin for the Lion fullscreen button. Do
|
||||
// nothing if it cannot be found.
|
||||
m0 = class_getInstanceMethod([self class],
|
||||
@selector(_fullScreenButtonOrigin));
|
||||
if (m0) {
|
||||
BOOL didAdd = class_addMethod(borderViewClass,
|
||||
@selector(_fullScreenButtonOriginOriginal),
|
||||
method_getImplementation(m0),
|
||||
method_getTypeEncoding(m0));
|
||||
if (didAdd) {
|
||||
Method m1 = class_getInstanceMethod(borderViewClass,
|
||||
@selector(_fullScreenButtonOrigin));
|
||||
Method m2 = class_getInstanceMethod(borderViewClass,
|
||||
@selector(_fullScreenButtonOriginOriginal));
|
||||
if (m1 && m2) {
|
||||
method_exchangeImplementations(m1, m2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)canDrawTitle {
|
||||
return gCanDrawTitle;
|
||||
}
|
||||
|
||||
+ (BOOL)canGetCornerRadius {
|
||||
return gCanGetCornerRadius;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame {
|
||||
// This class is not for instantiating.
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder*)coder {
|
||||
// This class is not for instantiating.
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Here is our custom drawing for our frame.
|
||||
- (void)drawRect:(NSRect)rect {
|
||||
// Delegate drawing to the window, whose default implementation (above) is to
|
||||
// call into the original implementation.
|
||||
[[self window] drawCustomFrameRect:rect forView:self];
|
||||
}
|
||||
|
||||
// Override to move the fullscreen button to the left of the profile avatar.
|
||||
- (NSPoint)_fullScreenButtonOrigin {
|
||||
NSWindow* window = [self window];
|
||||
NSPoint offset = NSZeroPoint;
|
||||
|
||||
if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)])
|
||||
offset = [window fullScreenButtonOriginAdjustment];
|
||||
|
||||
NSPoint origin = [self _fullScreenButtonOriginOriginal];
|
||||
origin.x += offset.x;
|
||||
origin.y += offset.y;
|
||||
return origin;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -195,7 +195,10 @@ void MessageDialog::Layout() {
|
||||
int x = bounds.width();
|
||||
int height = buttons_[0]->GetPreferredSize().height() +
|
||||
views::kRelatedControlVerticalSpacing;
|
||||
for (size_t i = 0; i < buttons_.size(); ++i) {
|
||||
|
||||
// NB: We iterate through the buttons backwards here because
|
||||
// Mac and Windows buttons are laid out in opposite order.
|
||||
for (int i = buttons_.size() - 1; i >= 0; --i) {
|
||||
gfx::Size size = buttons_[i]->GetPreferredSize();
|
||||
x -= size.width() + views::kRelatedButtonHSpacing;
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ class NativeMenuWin {
|
||||
virtual ~NativeMenuWin();
|
||||
|
||||
void RunMenuAt(const gfx::Point& point, int alignment);
|
||||
void CancelMenu() ;
|
||||
void Rebuild(views::MenuInsertionDelegateWin* delegate) ;
|
||||
void UpdateStates() ;
|
||||
HMENU GetNativeMenu() const ;
|
||||
MenuAction GetMenuAction() const ;
|
||||
void AddMenuListener(views::MenuListener* listener) ;
|
||||
void RemoveMenuListener(views::MenuListener* listener) ;
|
||||
void SetMinimumWidth(int width) ;
|
||||
void CancelMenu();
|
||||
void Rebuild(views::MenuInsertionDelegateWin* delegate);
|
||||
void UpdateStates();
|
||||
HMENU GetNativeMenu() const;
|
||||
MenuAction GetMenuAction() const;
|
||||
void AddMenuListener(views::MenuListener* listener);
|
||||
void RemoveMenuListener(views::MenuListener* listener);
|
||||
void SetMinimumWidth(int width);
|
||||
|
||||
// Flag to create a window menu instead of popup menu.
|
||||
void set_create_as_window_menu(bool flag) { create_as_window_menu_ = flag; }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/values.h"
|
||||
#include "common/draggable_region.h"
|
||||
#include "content/public/common/common_param_traits.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
|
||||
@@ -15,6 +16,11 @@
|
||||
|
||||
#define IPC_MESSAGE_START ShellMsgStart
|
||||
|
||||
IPC_STRUCT_TRAITS_BEGIN(atom::DraggableRegion)
|
||||
IPC_STRUCT_TRAITS_MEMBER(draggable)
|
||||
IPC_STRUCT_TRAITS_MEMBER(bounds)
|
||||
IPC_STRUCT_TRAITS_END()
|
||||
|
||||
IPC_MESSAGE_ROUTED2(AtomViewHostMsg_Message,
|
||||
std::string /* channel */,
|
||||
ListValue /* arguments */)
|
||||
@@ -27,3 +33,7 @@ IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync,
|
||||
IPC_MESSAGE_ROUTED2(AtomViewMsg_Message,
|
||||
std::string /* channel */,
|
||||
ListValue /* arguments */)
|
||||
|
||||
// Sent by the renderer when the draggable regions are updated.
|
||||
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
|
||||
std::vector<atom::DraggableRegion> /* regions */)
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/api/object_life_monitor.h"
|
||||
#include "v8/include/v8-profiler.h"
|
||||
#include "vendor/node/src/node.h"
|
||||
#include "vendor/node/src/node_internals.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -44,6 +46,12 @@ v8::Handle<v8::Value> SetDestructor(const v8::Arguments& args) {
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> TakeHeapSnapshot(const v8::Arguments& args) {
|
||||
node::node_isolate->GetHeapProfiler()->TakeHeapSnapshot(
|
||||
v8::String::New("test"));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeV8Util(v8::Handle<v8::Object> target) {
|
||||
@@ -52,6 +60,7 @@ void InitializeV8Util(v8::Handle<v8::Object> target) {
|
||||
NODE_SET_METHOD(target, "setHiddenValue", SetHiddenValue);
|
||||
NODE_SET_METHOD(target, "getObjectHash", GetObjectHash);
|
||||
NODE_SET_METHOD(target, "setDestructor", SetDestructor);
|
||||
NODE_SET_METHOD(target, "takeHeapSnapshot", TakeHeapSnapshot);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -16,6 +16,7 @@ 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)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_window)
|
||||
|
||||
// Module names start with `atom_renderer_` can only be used by renderer
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 3
|
||||
#define ATOM_PATCH_VERSION 4
|
||||
#define ATOM_MINOR_VERSION 4
|
||||
#define ATOM_PATCH_VERSION 6
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
13
common/draggable_region.cc
Normal file
13
common/draggable_region.cc
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/draggable_region.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
DraggableRegion::DraggableRegion()
|
||||
: draggable(false) {
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
21
common/draggable_region.h
Normal file
21
common/draggable_region.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. 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_COMMON_DRAGGABLE_REGION_H_
|
||||
#define ATOM_COMMON_DRAGGABLE_REGION_H_
|
||||
|
||||
#include "ui/gfx/rect.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
struct DraggableRegion {
|
||||
bool draggable;
|
||||
gfx::Rect bounds;
|
||||
|
||||
DraggableRegion();
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_DRAGGABLE_REGION_H_
|
||||
@@ -7,32 +7,33 @@
|
||||
|
||||
## Development
|
||||
|
||||
* [Coding style](coding-style.md)
|
||||
* [Source code directory structure](source-code-directory-structure.md)
|
||||
* [Build instructions (Mac)](build-instructions-mac.md)
|
||||
* [Build instructions (Windows)](build-instructions-windows.md)
|
||||
* [Coding style](development/coding-style.md)
|
||||
* [Source code directory structure](development/source-code-directory-structure.md)
|
||||
* [Build instructions (Mac)](development/build-instructions-mac.md)
|
||||
* [Build instructions (Windows)](development/build-instructions-windows.md)
|
||||
|
||||
## API References
|
||||
|
||||
Renderer side modules:
|
||||
|
||||
* [ipc (renderer)](ipc-renderer.md)
|
||||
* [remote](remote.md)
|
||||
* [ipc (renderer)](api/renderer/ipc-renderer.md)
|
||||
* [remote](api/renderer/remote.md)
|
||||
|
||||
Browser side modules:
|
||||
|
||||
* [app](app.md)
|
||||
* [atom-delegate](atom-delegate.md)
|
||||
* [auto-updater](auto-updater.md)
|
||||
* [browser-window](browser-window.md)
|
||||
* [crash-reporter](crash-reporter.md)
|
||||
* [dialog](dialog.md)
|
||||
* [ipc (browser)](ipc-browser.md)
|
||||
* [menu](menu.md)
|
||||
* [menu-item](menu-item.md)
|
||||
* [power-monitor](power-monitor.md)
|
||||
* [app](api/browser/app.md)
|
||||
* [atom-delegate](api/browser/atom-delegate.md)
|
||||
* [auto-updater](api/browser/auto-updater.md)
|
||||
* [browser-window](api/browser/browser-window.md)
|
||||
* [crash-reporter](api/browser/crash-reporter.md)
|
||||
* [dialog](api/browser/dialog.md)
|
||||
* [ipc (browser)](api/browser/ipc-browser.md)
|
||||
* [menu](api/browser/menu.md)
|
||||
* [menu-item](api/browser/menu-item.md)
|
||||
* [power-monitor](api/browser/power-monitor.md)
|
||||
* [protocol](api/browser/protocol.md)
|
||||
|
||||
Common modules:
|
||||
|
||||
* [clipboard](clipboard.md)
|
||||
* [shell](shell.md)
|
||||
* [clipboard](api/common/clipboard.md)
|
||||
* [shell](api/common/shell.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Synopsis
|
||||
# app
|
||||
|
||||
The `app` module is responsible for controlling the application's life time.
|
||||
|
||||
@@ -23,22 +23,30 @@ Do final startup like creating browser window here.
|
||||
|
||||
Emitted when all windows have been closed.
|
||||
|
||||
This event is only emitted when the application is not going to quit. If a user pressed `Cmd + Q`, or the developer called `app.quit()`, atom-shell would first try to close all windows and then emit the `will-quit` event, and in this case the `window-all-closed` would not be emitted.
|
||||
This event is only emitted when the application is not going to quit. If a
|
||||
user pressed `Cmd + Q`, or the developer called `app.quit()`, atom-shell would
|
||||
first try to close all windows and then emit the `will-quit` event, and in
|
||||
this case the `window-all-closed` would not be emitted.
|
||||
|
||||
## Event: will-quit
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when all windows have been closed and the application will quit. Calling `event.preventDefault()` will prevent the default behaviour, which is terminating the application.
|
||||
Emitted when all windows have been closed and the application will quit.
|
||||
Calling `event.preventDefault()` will prevent the default behaviour, which is
|
||||
terminating the application.
|
||||
|
||||
See description of `window-all-closed` for the differences between `will-quit` and it.
|
||||
See description of `window-all-closed` for the differences between `will-quit`
|
||||
and it.
|
||||
|
||||
## Event: open-file
|
||||
|
||||
* `event` Event
|
||||
* `path` String
|
||||
|
||||
Emitted when user wants to open a file with the application, it usually happens when the application is already opened and then OS wants to reuse the application to open file.
|
||||
Emitted when user wants to open a file with the application, it usually
|
||||
happens when the application is already opened and then OS wants to reuse the
|
||||
application to open file.
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
|
||||
@@ -47,19 +55,25 @@ You should call `event.preventDefault()` if you want to handle this event.
|
||||
* `event` Event
|
||||
* `url` String
|
||||
|
||||
Emitted when user wants to open a URL with the application, this URL scheme must be registered to be opened by your application.
|
||||
Emitted when user wants to open a URL with the application, this URL scheme
|
||||
must be registered to be opened by your application.
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
|
||||
## app.quit()
|
||||
|
||||
Try to close all windows. If all windows are successfully closed, the `will-quit` event will be emitted and by default the application would be terminated.
|
||||
Try to close all windows. If all windows are successfully closed, the
|
||||
`will-quit` event will be emitted and by default the application would be
|
||||
terminated.
|
||||
|
||||
This method guarantees all `beforeunload` and `unload` handlers are correctly executed. It is possible that a window cancels the quitting by returning `false` in `beforeunload` handler.
|
||||
This method guarantees all `beforeunload` and `unload` handlers are correctly
|
||||
executed. It is possible that a window cancels the quitting by returning
|
||||
`false` in `beforeunload` handler.
|
||||
|
||||
## app.terminate()
|
||||
|
||||
Quit the application directly, it will not try to close all windows so cleanup code will not run.
|
||||
Quit the application directly, it will not try to close all windows so cleanup
|
||||
code will not run.
|
||||
|
||||
## app.getVersion()
|
||||
|
||||
@@ -69,7 +83,8 @@ Returns the version of current bundle or executable.
|
||||
|
||||
Append a switch [with optional value] to Chromium's command line.
|
||||
|
||||
**Note:** This will not affect `process.argv`, and is mainly used by developers to control some low-level Chromium behaviors.
|
||||
**Note:** This will not affect `process.argv`, and is mainly used by
|
||||
**developers to control some low-level Chromium behaviors.
|
||||
|
||||
## app.commandLine.appendArgument(value)
|
||||
|
||||
@@ -79,11 +94,15 @@ Append an argument to Chromium's command line. The argument will quoted properly
|
||||
|
||||
## app.dock.bounce([type])
|
||||
|
||||
* `type` String - Can be `critical` or `informational`, the default is `informational`
|
||||
* `type` String - Can be `critical` or `informational`, the default is
|
||||
* `informational`
|
||||
|
||||
When `critical` is passed, the dock icon will bounce until either the application becomes active or the request is canceled.
|
||||
When `critical` is passed, the dock icon will bounce until either the
|
||||
application becomes active or the request is canceled.
|
||||
|
||||
When `informational` is passed, the dock icon will bounce for one second. The request, though, remains active until either the application becomes active or the request is canceled.
|
||||
When `informational` is passed, the dock icon will bounce for one second. The
|
||||
request, though, remains active until either the application becomes active or
|
||||
the request is canceled.
|
||||
|
||||
An ID representing the request would be returned.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
## Synopsis
|
||||
# atom-delegate
|
||||
|
||||
The `atom-delegate` returns the delegate object for Chrome Content API. The atom-shell would call methods of the delegate object when the corresponding C++ code is called. Developers can override methods of it to control the underlying behaviour of the browser.
|
||||
The `atom-delegate` returns the delegate object for Chrome Content API. The
|
||||
atom-shell would call methods of the delegate object when the corresponding
|
||||
C++ code is called. Developers can override methods of it to control the
|
||||
underlying behaviour of the browser.
|
||||
|
||||
An example of creating a new window when the browser is initialized:
|
||||
|
||||
@@ -24,4 +27,5 @@ delegate.browserMainParts.preMainMessageLoopRun = function() {
|
||||
|
||||
## atom-delegate.browserMainParts.preMainMessageLoopRun()
|
||||
|
||||
Called when atom-shell has done everything initialization and ready for creating browser windows.
|
||||
Called when atom-shell has done everything initialization and ready for
|
||||
creating browser windows.
|
||||
@@ -1,8 +1,10 @@
|
||||
## Synopsis
|
||||
# auto-updater
|
||||
|
||||
`auto-upater` module is a simple wrap around the Sparkle framework, it provides auto update service for the application.
|
||||
`auto-updater` module is a simple wrap around the Sparkle framework, it
|
||||
provides auto update service for the application.
|
||||
|
||||
Before using this module, you should edit the `Info.plist` following https://github.com/andymatuschak/Sparkle/wiki.
|
||||
Before using this module, you should edit the `Info.plist` following
|
||||
https://github.com/andymatuschak/Sparkle/wiki.
|
||||
|
||||
## Event: will-install-update
|
||||
|
||||
@@ -10,7 +12,9 @@ Before using this module, you should edit the `Info.plist` following https://git
|
||||
* `version` String
|
||||
* `continueUpdate` Function
|
||||
|
||||
This event is emitted when the update is found and going to be installed. Calling `event.preventDefault()` would pause it, and you can call `continueUpdate` to continue the update.
|
||||
This event is emitted when the update is found and going to be installed.
|
||||
Calling `event.preventDefault()` would pause it, and you can call
|
||||
`continueUpdate` to continue the update.
|
||||
|
||||
## Event: ready-for-update-on-quit
|
||||
|
||||
@@ -18,7 +22,8 @@ This event is emitted when the update is found and going to be installed. Callin
|
||||
* `version` String
|
||||
* `quitAndUpdate` Function
|
||||
|
||||
This event is emitted when user chose to delay the update until the quit. Calling `quitAndUpdate()` would quit the application and install the update.
|
||||
This event is emitted when user chose to delay the update until the quit.
|
||||
Calling `quitAndUpdate()` would quit the application and install the update.
|
||||
|
||||
## autoUpdater.setFeedUrl(url)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## Synopsis
|
||||
# browser-window
|
||||
|
||||
The `BrowserWindow` class gives you ability to create a browser window, an example is:
|
||||
The `BrowserWindow` class gives you ability to create a browser window, an
|
||||
example is:
|
||||
|
||||
```javascript
|
||||
var BrowserWindow = require('browser-window');
|
||||
@@ -14,11 +15,16 @@ win.loadUrl('https://github.com');
|
||||
win.show();
|
||||
```
|
||||
|
||||
You can also create a window without chrome by using
|
||||
[Frameless Window](frameless-window.md) API.
|
||||
|
||||
|
||||
**Note:** Be careful not to use `window` as the variable name.
|
||||
|
||||
## Class: BrowserWindow
|
||||
|
||||
`BrowserWindow` is an [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter).
|
||||
`BrowserWindow` is an
|
||||
[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter).
|
||||
|
||||
### new BrowserWindow(options)
|
||||
|
||||
@@ -36,22 +42,39 @@ win.show();
|
||||
* `kiosk` Boolean - The kiosk mode
|
||||
* `title` String - Default window title
|
||||
* `show` Boolean - Whether window should be shown when created
|
||||
* `frame` Boolean - Specify `false` to create a
|
||||
[Frameless Window](frameless-window.md)
|
||||
|
||||
Creates a new `BrowserWindow` with native properties set by the `options`. Usually you only need to set the `width` and `height`, other properties will have decent default values.
|
||||
Creates a new `BrowserWindow` with native properties set by the `options`.
|
||||
Usually you only need to set the `width` and `height`, other properties will
|
||||
have decent default values.
|
||||
|
||||
### Event: 'page-title-updated'
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when the document changed its title, calling `event.preventDefault()` would prevent the native window's title to change.
|
||||
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
|
||||
|
||||
Emitted when the window is going to be closed. It's emitted before the `beforeunload` and `unload` event of DOM, calling `event.preventDefault()` would cancel the close.
|
||||
Emitted when the window is going to be closed. It's emitted before the
|
||||
`beforeunload` and `unload` event of DOM, calling `event.preventDefault()`
|
||||
would cancel the close.
|
||||
|
||||
Usually you would want to use the `beforeunload` handler to decide whether the window should be closed, which will also be called when the window is reloaded. In atom-shell, returning an empty string or `false` would cancel the close. An example is:
|
||||
Usually you would want to use the `beforeunload` handler to decide whether the
|
||||
window should be closed, which will also be called when the window is
|
||||
reloaded. In atom-shell, returning an empty string or `false` would cancel the
|
||||
close. An example is:
|
||||
|
||||
```javascript
|
||||
window.onbeforeunload = function(e) {
|
||||
@@ -67,11 +90,14 @@ window.onbeforeunload = function(e) {
|
||||
|
||||
### Event: 'closed'
|
||||
|
||||
Emitted when the window is closed. At the time of this event, window is not destroyed yet so you can still do some operations to the window (but you shouldn't!).
|
||||
Emitted when the window is closed. At the time of this event, window is not
|
||||
destroyed yet so you can still do some operations to the window (but you
|
||||
shouldn't!).
|
||||
|
||||
### Event: 'destroyed'
|
||||
|
||||
Emitted when the memory taken by the native window is released. Usually you should dereference the javascript object when received this event.
|
||||
Emitted when the memory taken by the native window is released. Usually you
|
||||
should dereference the javascript object when received this event.
|
||||
|
||||
### Class Method: BrowserWindow.getFocusedWindow()
|
||||
|
||||
@@ -88,11 +114,16 @@ Find a window according to its `processId` and `routingId`.
|
||||
|
||||
Destroy the window and free the memory without closing it.
|
||||
|
||||
**Note:** Usually you should always call `Window.close()` to close the window, which will emit `beforeunload` and `unload` events for DOM. Only use `Window.destroy()` when the window gets into a very bad state and you want to force closing it.
|
||||
**Note:** Usually you should always call `Window.close()` to close the window,
|
||||
**which will emit `beforeunload` and `unload` events for DOM. Only use
|
||||
**`Window.destroy()` when the window gets into a very bad state and you want
|
||||
**to force closing it.
|
||||
|
||||
### BrowserWindow.close()
|
||||
|
||||
Try to close the window, this has the same effect with user manually clicking the close button of the window. The web page may cancel the close though, see the [close event](window#event-close).
|
||||
Try to close the window, this has the same effect with user manually clicking
|
||||
the close button of the window. The web page may cancel the close though, see
|
||||
the [close event](window#event-close).
|
||||
|
||||
### BrowserWindow.focus()
|
||||
|
||||
@@ -120,7 +151,8 @@ Unmaximizes the window.
|
||||
|
||||
### BrowserWindow.minimize()
|
||||
|
||||
Minimizes the window. On some platforms the minimized window will be shown in the Dock.
|
||||
Minimizes the window. On some platforms the minimized window will be shown in
|
||||
the Dock.
|
||||
|
||||
### BrowserWindow.restore()
|
||||
|
||||
@@ -183,7 +215,9 @@ Returns whether the window can be manually resized by user.
|
||||
|
||||
* `flag` Boolean
|
||||
|
||||
Sets whether the window should show always on top of other windows. After setting this, the window is still a normal window, not a toolbox window which can not be focused on.
|
||||
Sets whether the window should show always on top of other windows. After
|
||||
setting this, the window is still a normal window, not a toolbox window which
|
||||
can not be focused on.
|
||||
|
||||
### BrowserWindow.isAlwaysOnTop()
|
||||
|
||||
@@ -214,7 +248,8 @@ Changes the title of native window to `title`.
|
||||
|
||||
Returns the title of the native window.
|
||||
|
||||
**Note:** The title of web page can be different from the title of the native window.
|
||||
**Note:** The title of web page can be different from the title of the native
|
||||
**window.
|
||||
|
||||
### BrowserWindow.flashFlame()
|
||||
|
||||
@@ -261,7 +296,8 @@ 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.
|
||||
Returns whether web page is waiting for a first-response for the main resource
|
||||
of the page.
|
||||
|
||||
### BrowserWindow.stop()
|
||||
|
||||
@@ -269,17 +305,20 @@ 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.
|
||||
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.
|
||||
Returns window's routing ID. The process ID and routing ID can be used
|
||||
together to locate a window.
|
||||
|
||||
### BrowserWindow.loadUrl(url)
|
||||
|
||||
* `url` URL
|
||||
|
||||
Loads the `url` in the window, the `url` must contains the protocol prefix, e.g. the `http://` or `file://`.
|
||||
Loads the `url` in the window, the `url` must contains the protocol prefix,
|
||||
e.g. the `http://` or `file://`.
|
||||
|
||||
### BrowserWindow.getUrl()
|
||||
|
||||
@@ -325,4 +364,4 @@ Reloads current window.
|
||||
|
||||
### BrowserWindow.reloadIgnoringCache()
|
||||
|
||||
Reloads current window and ignores cache.
|
||||
Reloads current window and ignores cache.
|
||||
@@ -1,4 +1,4 @@
|
||||
## Synopsis
|
||||
# crash-reporter
|
||||
|
||||
An example of automatically submitting crash reporters to remote server:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## Synopsis
|
||||
# dialog
|
||||
|
||||
The `dialog` module provides functions to show system dialogs, so web applications can get the same user experience with native applications.
|
||||
The `dialog` module provides functions to show system dialogs, so web
|
||||
applications can get the same user experience with native applications.
|
||||
|
||||
An example of showing a dialog to select multiple files and directories:
|
||||
|
||||
@@ -14,10 +15,13 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', '
|
||||
|
||||
* `options` Object
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
* `properties` Array - Contains which features the dialog should use, can contain `openFile`, `openDirectory`, `multiSelections` and `createDirectory`
|
||||
* `defaultPath` String
|
||||
* `properties` Array - Contains which features the dialog should use, can
|
||||
contain `openFile`, `openDirectory`, `multiSelections` and
|
||||
`createDirectory`
|
||||
|
||||
On success, returns an array of file paths chosen by the user, otherwise returns `undefined`.
|
||||
On success, returns an array of file paths chosen by the user, otherwise
|
||||
returns `undefined`.
|
||||
|
||||
**Note:** The `dialog.showOpenDialog` API is synchronous and blocks all windows.
|
||||
|
||||
@@ -28,7 +32,8 @@ On success, returns an array of file paths chosen by the user, otherwise returns
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
|
||||
On success, returns the path of file chosen by the user, otherwise returns `undefined`.
|
||||
On success, returns the path of file chosen by the user, otherwise returns
|
||||
`undefined`.
|
||||
|
||||
**Note:** The `dialog.showSaveDialog` API is synchronous and blocks all windows.
|
||||
|
||||
@@ -42,6 +47,7 @@ On success, returns the path of file chosen by the user, otherwise returns `unde
|
||||
* `message` String - Content of the message box
|
||||
* `detail` String - Extra information of the message
|
||||
|
||||
Shows a message box, it will block until the message box is closed. It returns the index of the clicked button.
|
||||
Shows a message box, it will block until the message box is closed. It returns
|
||||
the index of the clicked button.
|
||||
|
||||
**Note:** The `dialog.showMessageBox` API is synchronous and blocks all windows.
|
||||
**Note:** The `dialog.showMessageBox` API is synchronous and blocks all windows.
|
||||
65
docs/api/browser/frameless-window.md
Normal file
65
docs/api/browser/frameless-window.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Frameless window
|
||||
|
||||
A frameless window is a window that has no chrome, you can find a demo at
|
||||
[atom/frameless-window-demo](https://github.com/atom/frameless-window-demo).
|
||||
|
||||
## Create a frameless window
|
||||
|
||||
To create a frameless window, you only need to specify `frame` to `false` in
|
||||
[BrowserWindow](browser-window.md)'s `options`:
|
||||
|
||||
|
||||
```javascript
|
||||
var BrowserWindow = require('browser-window');
|
||||
var win = new BrowserWindow({ width: 800, height: 600, frame: false });
|
||||
```
|
||||
|
||||
## Draggable region
|
||||
|
||||
By default, the frameless window is non-draggable. Apps need to specify
|
||||
`-webkit-app-region: drag` in CSS to tell atom-shell which regions are draggable
|
||||
(like the OS's standard titlebar), and apps can also use
|
||||
`-webkit-app-region: no-drag` to exclude the non-draggable area from the
|
||||
draggable region. Note that only rectangular shape is currently supported.
|
||||
|
||||
To make the whole window draggable, you can add `-webkit-app-region: drag` as
|
||||
`body`'s style:
|
||||
|
||||
```html
|
||||
<body style="-webkit-app-region: drag">
|
||||
</body>
|
||||
```
|
||||
|
||||
And note that if you have made the whole window draggable, you must also mark
|
||||
buttons as non-draggable, otherwise it would be impossible for users to click on
|
||||
them:
|
||||
|
||||
```css
|
||||
button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
```
|
||||
|
||||
If you're only using a custom titlebar, you also need to make buttons in
|
||||
titlebar non-draggable.
|
||||
|
||||
## Text selection
|
||||
|
||||
One thing on frameless window is that the dragging behaviour may conflict with
|
||||
selecting text, for example, when you drag the titlebar, you may accidentally
|
||||
select the text on titlebar. To prevent this, you need to disable text
|
||||
selection on dragging area like this:
|
||||
|
||||
```css
|
||||
.titlebar {
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
```
|
||||
|
||||
## Context menu
|
||||
|
||||
On some platforms, the draggable area would be treated as non-client frame, so
|
||||
when you right click on it a system menu would be popuped. To make context menu
|
||||
behave correctly on all platforms, you should never custom context menu on
|
||||
draggable areas.
|
||||
@@ -1,6 +1,8 @@
|
||||
## Synopsis
|
||||
# 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.
|
||||
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.
|
||||
|
||||
## Event: 'message'
|
||||
|
||||
@@ -15,16 +17,21 @@ Emitted when renderer sent a message to the browser.
|
||||
* `processId` Integer
|
||||
* `routingId` Integer
|
||||
|
||||
Emitted when renderer sent a synchronous message to the browser. The receiver should store the result in `event.result`.
|
||||
Emitted when renderer sent a synchronous message to the browser. The receiver
|
||||
should store the result in `event.result`.
|
||||
|
||||
**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.
|
||||
**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.
|
||||
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...])
|
||||
|
||||
@@ -32,10 +39,14 @@ Send `args...` to the renderer specified by `processId` and `routingId` and retu
|
||||
* `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...).
|
||||
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:
|
||||
**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:
|
||||
|
||||
```javascript
|
||||
ipc.sendChannel(browserWindow, 'message', ...);
|
||||
```
|
||||
```
|
||||
@@ -1,20 +1,27 @@
|
||||
# menu-item
|
||||
|
||||
## Class: MenuItem
|
||||
|
||||
### new MenuItem(options)
|
||||
|
||||
* `options` Object
|
||||
* `click` Function - Callback when the menu item is clicked
|
||||
* `selector` String - Call the selector of first responder when clicked (OS X only)
|
||||
* `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or `radio`
|
||||
* `selector` String - Call the selector of first responder when clicked (OS
|
||||
X only)
|
||||
* `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or
|
||||
`radio`
|
||||
* `label` String
|
||||
* `sublabel` String
|
||||
* `accelerator` String - In the form of `Command+R`, `Ctrl+C`, `Shift+Command+D`, `D`, etc.
|
||||
* `accelerator` String - In the form of `Command+R`, `Ctrl+C`,
|
||||
`Shift+Command+D`, `D`, etc.
|
||||
* `enabled` Boolean
|
||||
* `visible` Boolean
|
||||
* `checked` Boolean
|
||||
* `groupId` Boolean - Should be specified for `radio` type menu item
|
||||
* `submenu` Menu - Should be specified for `submenu` type menu item, when it's specified the `type: 'submenu'` can be omitted for the menu item
|
||||
* `submenu` Menu - Should be specified for `submenu` type menu item, when
|
||||
it's specified the `type: 'submenu'` can be omitted for the menu item
|
||||
|
||||
## Notes on accelerator
|
||||
|
||||
On OS X, the `Ctrl` would automatically translated to `Command`, if you really want `Ctrl` on OS X, you should use `MacCtrl`.
|
||||
On OS X, the `Ctrl` would automatically translated to `Command`, if you really
|
||||
want `Ctrl` on OS X, you should use `MacCtrl`.
|
||||
@@ -1,8 +1,11 @@
|
||||
## Synopsis
|
||||
# menu
|
||||
|
||||
The `Menu` class is used to create native menus that can be used as application menu and context menu. Each menu is consisted of multiple menu items, and each menu item can have a submenu.
|
||||
The `Menu` class is used to create native menus that can be used as
|
||||
application menu and context menu. Each menu is consisted of multiple menu
|
||||
items, and each menu item can have a submenu.
|
||||
|
||||
An example of creating a menu dynamically and show it when user right clicks the page:
|
||||
An example of creating a menu dynamically and show it when user right clicks
|
||||
the page:
|
||||
|
||||
```javascript
|
||||
var Menu = require('menu');
|
||||
@@ -156,7 +159,9 @@ Sets `menu` as the application menu.
|
||||
|
||||
* `action` String
|
||||
|
||||
Sends the `action` to the first responder of application, this is used for emulating default Cocoa menu behaviors, usually you would just use the `selector` property of `MenuItem`.
|
||||
Sends the `action` to the first responder of application, this is used for
|
||||
emulating default Cocoa menu behaviors, usually you would just use the
|
||||
`selector` property of `MenuItem`.
|
||||
|
||||
**Note:** This method is OS X only.
|
||||
|
||||
@@ -164,9 +169,11 @@ Sends the `action` to the first responder of application, this is used for emula
|
||||
|
||||
* `template` Array
|
||||
|
||||
Generally, the `template` is just an array of `options` for constructing `MenuItem`, the usage can be referenced above.
|
||||
Generally, the `template` is just an array of `options` for constructing
|
||||
`MenuItem`, the usage can be referenced above.
|
||||
|
||||
You can also attach other fields to element of the `template`, and they will become properties of the constructed menu items.
|
||||
You can also attach other fields to element of the `template`, and they will
|
||||
become properties of the constructed menu items.
|
||||
|
||||
### Menu.popup(browserWindow)
|
||||
|
||||
@@ -189,4 +196,4 @@ Inserts the `menuItem` to the `pos` position of the menu.
|
||||
|
||||
### Menu.items
|
||||
|
||||
Get the array containing the menu's items.
|
||||
Get the array containing the menu's items.
|
||||
@@ -1,6 +1,7 @@
|
||||
## Synopsis
|
||||
# power-monitor
|
||||
|
||||
The `power-monitor` module is used to monitor the power state change, you can only use it on the browser side.
|
||||
The `power-monitor` module is used to monitor the power state change, you can
|
||||
only use it on the browser side.
|
||||
|
||||
An example is:
|
||||
|
||||
@@ -16,4 +17,4 @@ Emitted when the system is suspending.
|
||||
|
||||
## Event: resume
|
||||
|
||||
Emitted when system is resuming.
|
||||
Emitted when system is resuming.
|
||||
68
docs/api/browser/protocol.md
Normal file
68
docs/api/browser/protocol.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# protocol
|
||||
|
||||
The `protocol` module can register a new protocol or intercept an existing
|
||||
protocol, so you can custom the response to the requests for vairous protocols.
|
||||
|
||||
An example of implementing a protocol that has the same effect with the
|
||||
`file://` protocol:
|
||||
|
||||
```javascript
|
||||
var protocol = require('protocol');
|
||||
protocol.registerProtocol('atom', function(request) {
|
||||
var path = request.url.substr(7)
|
||||
return new protocol.RequestFileJob(path);
|
||||
});
|
||||
```
|
||||
|
||||
## protocol.registerProtocol(scheme, handler)
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
|
||||
Registers a custom protocol of `scheme`, the `handler` would be called with
|
||||
`handler(request)` when the a request with registered `scheme` is made.
|
||||
|
||||
You need to return a request job in the `handler` to specify which type of
|
||||
response you would like to send.
|
||||
|
||||
## protocol.unregisterProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Unregisters the custom protocol of `scheme`.
|
||||
|
||||
## protocol.isHandledProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns whether the `scheme` can be handled already.
|
||||
|
||||
## protocol.interceptProtocol(scheme, handler)
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
|
||||
Intercepts an existing protocol with `scheme`, returning `null` or `undefined`
|
||||
in `handler` would use the original protocol handler to handle the request.
|
||||
|
||||
## protocol.uninterceptProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Unintercepts a protocol.
|
||||
|
||||
## Class: protocol.RequestFileJob(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
Create a request job which would query a file of `path` and set corresponding
|
||||
mime types.
|
||||
|
||||
## Class: protocol.RequestStringJob(options)
|
||||
|
||||
* `options` Object
|
||||
* `mimeType` String - Default is `text/plain`
|
||||
* `charset` String - Default is `UTF-8`
|
||||
* `data` String
|
||||
|
||||
Create a request job which sends a string as response.
|
||||
@@ -1,4 +1,4 @@
|
||||
## Synopsis
|
||||
# clipboard
|
||||
|
||||
An example of writing a string to clipboard:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Synopsis
|
||||
# shell
|
||||
|
||||
The `shell` module provides functions related to desktop integration.
|
||||
|
||||
@@ -25,7 +25,8 @@ Open the given file in the desktop's default manner.
|
||||
|
||||
* `url` String
|
||||
|
||||
Open the given external protocol URL in the desktop's default manner. (For example, mailto: URLs in the default mail user agent.)
|
||||
Open the given external protocol URL in the desktop's default manner. (For
|
||||
example, mailto: URLs in the default mail user agent.)
|
||||
|
||||
## shell.moveItemToTrash(fullPath)
|
||||
|
||||
@@ -35,4 +36,4 @@ Move the given file to trash.
|
||||
|
||||
## shell.beep()
|
||||
|
||||
Play the beep sound.
|
||||
Play the beep sound.
|
||||
@@ -1,6 +1,9 @@
|
||||
## Synopsis
|
||||
# ipc (renderer)
|
||||
|
||||
The `ipc` module provides a few methods so you can send synchronous and 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.
|
||||
The `ipc` module provides a few methods so you can send synchronous and
|
||||
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:
|
||||
|
||||
@@ -43,25 +46,34 @@ 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.
|
||||
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 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.
|
||||
|
||||
**Note:** Usually developers should never use this API, since sending synchronous message would block the browser.
|
||||
**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...)`.
|
||||
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...)`.
|
||||
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.
|
||||
**Note:** Usually developers should never use this API, since sending
|
||||
synchronous message would block the browser.
|
||||
82
docs/api/renderer/remote.md
Normal file
82
docs/api/renderer/remote.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# remote
|
||||
|
||||
It's common that the developers want to use modules in browsers from the
|
||||
renderer, like closing current window, opening file dialogs, etc. Instead of
|
||||
writing IPC code for every operation you want to do, atom-shell provides the
|
||||
`remote` module to let you do RPC call just like using normal javascript
|
||||
objects.
|
||||
|
||||
An example of creating a window in renderer:
|
||||
|
||||
```javascript
|
||||
var remote = require('remote');
|
||||
var BrowserWindow = remote.require('browser-window');
|
||||
var win = new BrowserWindow({ width: 800, height: 600 });
|
||||
win.loadUrl('https://github.com');
|
||||
```
|
||||
|
||||
## Lifetime of remote objects
|
||||
|
||||
Every object returned by `remote` module represents an object in browser (e.g.
|
||||
a remote object), so when you call methods of an object, or call a returned
|
||||
function, or even create a object with the returned constructor, you are
|
||||
indeed making a synchronous RPC call. And when the renderer releases the last
|
||||
reference to the remote object, the browser would release the corresponding
|
||||
reference too.
|
||||
|
||||
This also means that, if the renderer keeps a reference to an object in
|
||||
browser, the object would never be released. So be careful to never leak the
|
||||
remote objects.
|
||||
|
||||
## Passing callbacks
|
||||
|
||||
Many APIs in browser accepts callbacks, so the `remote` module also supports
|
||||
passing callbacks when calling remote functions, and the callbacks passed
|
||||
would become remote functions in the browser.
|
||||
|
||||
But in order to avoid possible dead locks, the callbacks passed to browser
|
||||
would be called asynchronously in browser, so you should never expect the
|
||||
browser to get the return value of the passed callback.
|
||||
|
||||
Another thing is the lifetime of the remote callbacks in browser, it might be
|
||||
very tempting to do things like following:
|
||||
|
||||
```javascript
|
||||
var remote = require('remote');
|
||||
remote.getCurrentWindow().on('close', function() {
|
||||
// blabla...
|
||||
});
|
||||
```
|
||||
|
||||
Yes it will work correctly, but when you reload the window, the callback you
|
||||
setup on the object in browser will not be erased, resources are leaked and
|
||||
there is no magic in javascript to release a referenced object.
|
||||
|
||||
So if you really need to keep a reference of callbacks in browser, you should
|
||||
write the callback in browser and send messages to renderer. And also make use
|
||||
of DOM's events like `unload` and `beforeunload`, they will work perfectly.
|
||||
|
||||
## remote.require(module)
|
||||
|
||||
* `module` String
|
||||
|
||||
Return a module in browser.
|
||||
|
||||
## remote.getCurrentWindow()
|
||||
|
||||
Return the `BrowserWindow` object that represents current window.
|
||||
|
||||
`Note:` it doesn't return the `window` object which represents the global
|
||||
scope, instead it returns an instance of the `BrowserWindow` class which is
|
||||
created with `browser-window` module in browser.
|
||||
|
||||
## remote.getGlobal(name)
|
||||
|
||||
* `name` String
|
||||
|
||||
Return the `global[name]` value in browser.
|
||||
|
||||
## remote.process
|
||||
|
||||
Getter to return the `process` object in browser, this is the same with
|
||||
`remote.getGlobal('process')` but gets cached.
|
||||
@@ -1,27 +1,39 @@
|
||||
Since atom-shell is using a different V8 version from the official node, you need to build native module against atom-shell's headers to use them.
|
||||
# Build native modules
|
||||
|
||||
You need to use node-gyp to compile native modules, you can install node-gyp via npm if you hadn't:
|
||||
Since atom-shell is using a different V8 version from the official node, you
|
||||
need to build native module against atom-shell's headers to use them.
|
||||
|
||||
You need to use node-gyp to compile native modules, you can install node-gyp
|
||||
via npm if you hadn't:
|
||||
|
||||
```bash
|
||||
$ npm install -g node-gyp
|
||||
```
|
||||
|
||||
First you need to check which node release atom-shell is carrying via `process.version` (at the time of writing it is v0.10.5), then you can configure and build native modules via following commands:
|
||||
First you need to check which node release atom-shell is carrying via
|
||||
`process.version` (at the time of writing it is v0.10.5), then you can
|
||||
configure and build native modules via following commands:
|
||||
|
||||
```bash
|
||||
$ cd /path-to-module/
|
||||
$ HOME=~/.atom-shell-gyp node-gyp rebuild --target=0.10.5 --arch=ia32 --dist-url=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
|
||||
```
|
||||
|
||||
The `HOME=~/.atom-shell-gyp` changes where to find development headers. The `--target=0.10.5` is specifying node's version. The `--dist-url=...` specifies where to download the headers.
|
||||
The `HOME=~/.atom-shell-gyp` changes where to find development headers. The
|
||||
`--target=0.10.5` is specifying node's version. The `--dist-url=...` specifies
|
||||
where to download the headers.
|
||||
|
||||
## Use npm to build native modules
|
||||
|
||||
Under most circumstances you would want to use npm to install modules, if you're using npm >= v1.2.19 (because [a patch](https://github.com/TooTallNate/node-gyp/commit/afbcdea1ffd25c02bc88d119b10337852c44d400) is needed to make `npm_config_disturl` work) you can use following code to download and build native modules against atom-shell's headers:
|
||||
Under most circumstances you would want to use npm to install modules, if
|
||||
you're using npm >= v1.2.19 (because [a
|
||||
patch](https://github.com/TooTallNate/node-gyp/commit/afbcdea1ffd25c02bc88d119b10337852c44d400)
|
||||
is needed to make `npm_config_disturl` work) you can use following code to
|
||||
download and build native modules against atom-shell's headers:
|
||||
|
||||
```bash
|
||||
export npm_config_disturl=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
|
||||
export npm_config_target=0.10.5
|
||||
export npm_config_arch=ia32
|
||||
HOME=~/.atom-shell-gyp npm install module-name
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
## C++ and Python
|
||||
|
||||
For C++ and Python, we just follow Chromium's [Coding Style](http://www.chromium.org/developers/coding-style), there is also a script `script/cpplint.py` to check whether all files confirm.
|
||||
|
||||
The python's version we are using now is Python 2.7.
|
||||
|
||||
## CoffeeScript
|
||||
|
||||
For CoffeeScript, we follow GitHub's [Style Guide](https://github.com/styleguide/javascript), and also following rules:
|
||||
|
||||
* Files should **NOT** end with new line, because we want to match Google's styles.
|
||||
* File names should be concatenated with `-` instead of `_`, e.g. `file-name.coffee` rather than `file_name.coffee`, because in [github/atom](https://github.com/github/atom) module names are usually in the `module-name` form, this rule only apply to `.coffee` files.
|
||||
|
||||
## API Names
|
||||
|
||||
When creating a new API, we should prefer getters and setters instead of jQuery's one-function style, for example, `.getText()` and `.setText(text)` are preferred to `.text([text])`. There is a [discussion](https://github.com/atom/atom-shell/issues/46) of this.
|
||||
@@ -1,3 +1,5 @@
|
||||
# Build instructions (Mac)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Mac OS X >= 10.7
|
||||
@@ -12,7 +14,9 @@ $ git clone https://github.com/atom/atom-shell.git
|
||||
|
||||
## Bootstrapping
|
||||
|
||||
The bootstrap script will download all necessary build dependencies and create build project files. Notice that we're using `ninja` to build `atom-shell` so there is no Xcode project generated.
|
||||
The bootstrap script will download all necessary build dependencies and create
|
||||
build project files. Notice that we're using `ninja` to build `atom-shell` so
|
||||
there is no Xcode project generated.
|
||||
|
||||
```bash
|
||||
$ cd atom-shell
|
||||
@@ -1,3 +1,5 @@
|
||||
# Build instructions (Windows)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Windows 7 or later
|
||||
@@ -6,11 +8,17 @@
|
||||
* [node.js](http://nodejs.org/)
|
||||
* [git](http://git-scm.com)
|
||||
|
||||
The instructions bellow are executed under [cygwin](http://www.cygwin.com), but it's not a requirement, you can also build atom-shell under Windows's console or other terminals.
|
||||
The instructions bellow are executed under [cygwin](http://www.cygwin.com),
|
||||
but it's not a requirement, you can also build atom-shell under Windows's
|
||||
console or other terminals.
|
||||
|
||||
The building of atom-shell is done entirely with command line scripts, so you can use any editor you like to develop atom-shell, but it also means you can not use Visual Studio for the development. Support of building with Visual Studio will come in future.
|
||||
The building of atom-shell is done entirely with command line scripts, so you
|
||||
can use any editor you like to develop atom-shell, but it also means you can
|
||||
not use Visual Studio for the development. Support of building with Visual
|
||||
Studio will come in future.
|
||||
|
||||
**Note:** Even though Visual Studio is not used for building, it's still required because we need the build toolchains it provided.
|
||||
**Note:** Even though Visual Studio is not used for building, it's still
|
||||
**required because we need the build toolchains it provided.
|
||||
|
||||
## Getting the code
|
||||
|
||||
@@ -20,7 +28,9 @@ $ git clone https://github.com/atom/atom-shell.git
|
||||
|
||||
## Bootstrapping
|
||||
|
||||
The bootstrap script will download all necessary build dependencies and create build project files. Notice that we're using `ninja` to build atom-shell so there is no Visual Studio project generated.
|
||||
The bootstrap script will download all necessary build dependencies and create
|
||||
build project files. Notice that we're using `ninja` to build atom-shell so
|
||||
there is no Visual Studio project generated.
|
||||
|
||||
```bash
|
||||
$ cd atom-shell
|
||||
@@ -47,4 +57,4 @@ After building is done, you can find `atom.exe` under `out\Debug`.
|
||||
|
||||
```bash
|
||||
$ python script/test.py
|
||||
```
|
||||
```
|
||||
28
docs/development/coding-style.md
Normal file
28
docs/development/coding-style.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Coding style
|
||||
|
||||
## C++ and Python
|
||||
|
||||
For C++ and Python, we just follow Chromium's [Coding
|
||||
Style](http://www.chromium.org/developers/coding-style), there is also a
|
||||
script `script/cpplint.py` to check whether all files confirm.
|
||||
|
||||
The python's version we are using now is Python 2.7.
|
||||
|
||||
## CoffeeScript
|
||||
|
||||
For CoffeeScript, we follow GitHub's [Style
|
||||
Guide](https://github.com/styleguide/javascript), and also following rules:
|
||||
|
||||
* Files should **NOT** end with new line, because we want to match Google's
|
||||
styles.
|
||||
* File names should be concatenated with `-` instead of `_`, e.g.
|
||||
`file-name.coffee` rather than `file_name.coffee`, because in
|
||||
[github/atom](https://github.com/github/atom) module names are usually in
|
||||
the `module-name` form, this rule only apply to `.coffee` files.
|
||||
|
||||
## API Names
|
||||
|
||||
When creating a new API, we should prefer getters and setters instead of
|
||||
jQuery's one-function style, for example, `.getText()` and `.setText(text)`
|
||||
are preferred to `.text([text])`. There is a
|
||||
[discussion](https://github.com/atom/atom-shell/issues/46) of this.
|
||||
@@ -1,22 +1,33 @@
|
||||
# Source code directory structure
|
||||
|
||||
## Overview
|
||||
|
||||
The source code of atom-shell is separated into a few parts, and we are mostly following Chromium on the separation conventions.
|
||||
The source code of atom-shell is separated into a few parts, and we are mostly
|
||||
following Chromium on the separation conventions.
|
||||
|
||||
You may need to become familiar with [Chromium's multi-process architecture](http://dev.chromium.org/developers/design-documents/multi-process-architecture) to understand the source code better.
|
||||
You may need to become familiar with [Chromium's multi-process
|
||||
architecture](http://dev.chromium.org/developers/design-documents/multi-process-architecture)
|
||||
to understand the source code better.
|
||||
|
||||
## Structure of source code
|
||||
|
||||
* **app** - Contains system entry code, this is the most basic level of the program.
|
||||
* **browser** - The frontend including the main window, UI, and all browser side things. This talks to the renderer to manage web pages.
|
||||
* **app** - Contains system entry code, this is the most basic level of the
|
||||
program.
|
||||
* **browser** - The frontend including the main window, UI, and all browser
|
||||
side things. This talks to the renderer to manage web pages.
|
||||
* **ui** - Implementation of UI stuff for different platforms.
|
||||
* **atom** - Initializes the javascript environment of browser.
|
||||
* **default_app** - The default page to show when atom-shell is started without providing an app.
|
||||
* **default_app** - The default page to show when atom-shell is started
|
||||
without providing an app.
|
||||
* **api** - The implementation of browser side APIs.
|
||||
* **lib** - Javascript part of the API implementation.
|
||||
* **renderer** - Code that runs in renderer.
|
||||
* **api** - The implementation of renderer side APIs.
|
||||
* **lib** - Javascript part of the API implementation.
|
||||
* **common** - Code that used by both browser and renderer, including some utility functions and code to integrate node's message loop into Chromium's message loop.
|
||||
* **api** - The implementation of common APIs, and foundations of atom-shell's built-in modules.
|
||||
* **common** - Code that used by both browser and renderer, including some
|
||||
utility functions and code to integrate node's message loop into Chromium's message loop.
|
||||
* **api** - The implementation of common APIs, and foundations of
|
||||
atom-shell's built-in modules.
|
||||
* **lib** - Javascript part of the API implementation.
|
||||
* **spec** - Automatic tests.
|
||||
* **script** - Scripts for building atom-shell.
|
||||
@@ -25,8 +36,12 @@ You may need to become familiar with [Chromium's multi-process architecture](htt
|
||||
|
||||
* **vendor** - Build dependencies.
|
||||
* **tools** - Helper scripts to build atom-shell.
|
||||
* **node_modules** - Third party node modules used for building or running specs.
|
||||
* **node_modules** - Third party node modules used for building or running
|
||||
specs.
|
||||
* **out** - Output directory for `ninja`.
|
||||
* **dist** - Temporary directory created by `script/create-dist.py` script when creating an distribution.
|
||||
* **node** - Downloaded node binary, it's built from https://github.com/atom/node/tree/chromium-v8.
|
||||
* **frameworks** - Downloaded third-party binaries of frameworks (only on Mac).
|
||||
* **dist** - Temporary directory created by `script/create-dist.py` script
|
||||
when creating an distribution.
|
||||
* **node** - Downloaded node binary, it's built from
|
||||
https://github.com/atom/node/tree/chromium-v8.
|
||||
* **frameworks** - Downloaded third-party binaries of frameworks (only on
|
||||
Mac).
|
||||
@@ -1,12 +1,26 @@
|
||||
# Quick start
|
||||
|
||||
## Introduction
|
||||
|
||||
Generally, atom-shell lets you create a web-based desktop application in pure javascript. Unlike CEF, which requires you to use C++ to write underlying code, or node-webkit, which only allows you to write everything in the web page, atom-shell gives you the power to use javascript to control the browser side.
|
||||
Generally, atom-shell lets you create a web-based desktop application in pure
|
||||
javascript. Unlike CEF, which requires you to use C++ to write underlying
|
||||
code, or node-webkit, which only allows you to write everything in the web
|
||||
page, atom-shell gives you the power to use javascript to control the browser
|
||||
side.
|
||||
|
||||
## Browser and renderer
|
||||
|
||||
Atom-shell is built upon Chromium's Content API, so it has the same multi-processes architecture with the Chrome browser. In summary, things about UI are done in the browser process, and each web page instance would start a new renderer process.
|
||||
Atom-shell is built upon Chromium's Content API, so it has the same
|
||||
multi-processes architecture with the Chrome browser. In summary, things about
|
||||
UI are done in the browser process, and each web page instance would start a
|
||||
new renderer process.
|
||||
|
||||
In atom-shell, you can just put everything in a simpler way: when you are executing javascript in browser side, you can control the application's life, create UI widget, deal with system events, and create windows which contain web pages; while on the renderer side, you can only control the web page you are showing, if you want something more like creating a new window, you should use IPC API to tell the browser to do that.
|
||||
In atom-shell, you can just put everything in a simpler way: when you are
|
||||
executing javascript in browser side, you can control the application's life,
|
||||
create UI widget, deal with system events, and create windows which contain
|
||||
web pages; while on the renderer side, you can only control the web page you
|
||||
are showing, if you want something more like creating a new window, you should
|
||||
use IPC API to tell the browser to do that.
|
||||
|
||||
## The architecture of an app
|
||||
|
||||
@@ -19,7 +33,10 @@ app/
|
||||
└── index.html
|
||||
```
|
||||
|
||||
The format of `package.json` is exactly the same with node's modules, and the script specified by the `main` field is the startup script of your app, which will run under the browser side. An example of your `package.json` is like this:
|
||||
The format of `package.json` is exactly the same with node's modules, and the
|
||||
script specified by the `main` field is the startup script of your app, which
|
||||
will run under the browser side. An example of your `package.json` is like
|
||||
this:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -29,7 +46,10 @@ The format of `package.json` is exactly the same with node's modules, and the sc
|
||||
}
|
||||
```
|
||||
|
||||
The `main.js` will be executed, and in which you should do the initialization work. To give the developers more power, atom-shell works by exposing necessary Content APIs in javascript, so developers can precisely control every piece of the app. An example of `main.js` is:
|
||||
The `main.js` will be executed, and in which you should do the initialization
|
||||
work. To give the developers more power, atom-shell works by exposing
|
||||
necessary Content APIs in javascript, so developers can precisely control
|
||||
every piece of the app. An example of `main.js` is:
|
||||
|
||||
```javascript
|
||||
var app = require('app'); // Module to control application life.
|
||||
@@ -72,11 +92,14 @@ delegate.browserMainParts.preMainMessageLoopRun = function() {
|
||||
}
|
||||
```
|
||||
|
||||
Finally the `index.html` is the web page you want to show, in fact you actually don't need to provide it, you can just make the window load url of a remote page.
|
||||
Finally the `index.html` is the web page you want to show, in fact you
|
||||
actually don't need to provide it, you can just make the window load url of a
|
||||
remote page.
|
||||
|
||||
## Package your app in atom-shell
|
||||
|
||||
To make atom-shell run your app, you should name the folder of your app as `app`, and put it under `Atom.app/Contents/Resources/`, like this:
|
||||
To make atom-shell run your app, you should name the folder of your app as
|
||||
`app`, and put it under `Atom.app/Contents/Resources/`, like this:
|
||||
|
||||
```text
|
||||
Atom.app/Contents/Resources/app/
|
||||
@@ -85,8 +108,14 @@ Atom.app/Contents/Resources/app/
|
||||
└── index.html
|
||||
```
|
||||
|
||||
Then atom-shell will automatically read your `package.json`. If there is no `Atom.app/Contents/Resources/app/`, atom-shell will load the default empty app, which is `Atom.app/Contents/Resources/browser/default_app/`.
|
||||
Then atom-shell will automatically read your `package.json`. If there is no
|
||||
`Atom.app/Contents/Resources/app/`, atom-shell will load the default empty
|
||||
app, which is `Atom.app/Contents/Resources/browser/default_app/`.
|
||||
|
||||
## IPC between browser and renderer
|
||||
|
||||
Atom-shell provides a set of javascript APIs for developers to communicate between browser and renderers. There are two types of message: asynchronous messages and synchronous messages, the former one is quite similar with node's IPC APIs, while the latter one is mainly used for implement the RPC API. Details can be found in the `ipc` module reference.
|
||||
Atom-shell provides a set of javascript APIs for developers to communicate
|
||||
between browser and renderers. There are two types of message: asynchronous
|
||||
messages and synchronous messages, the former one is quite similar with node's
|
||||
IPC APIs, while the latter one is mainly used for implement the RPC API.
|
||||
Details can be found in the `ipc` module reference.
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
## Synopsis
|
||||
|
||||
It's common that the developers want to use modules in browsers from the renderer, like closing current window, opening file dialogs, etc. Instead of writing IPC code for every operation you want to do, atom-shell provides the `remote` module to let you do RPC call just like using normal javascript objects.
|
||||
|
||||
An example of creating a window in renderer:
|
||||
|
||||
```javascript
|
||||
var remote = require('remote');
|
||||
var BrowserWindow = remote.require('browser-window');
|
||||
var win = new BrowserWindow({ width: 800, height: 600 });
|
||||
win.loadUrl('https://github.com');
|
||||
```
|
||||
|
||||
## Lifetime of remote objects
|
||||
|
||||
Every object returned by `remote` module represents an object in browser (e.g. a remote object), so when you call methods of an object, or call a returned function, or even create a object with the returned constructor, you are indeed making a synchronous RPC call. And when the renderer releases the last reference to the remote object, the browser would release the corresponding reference too.
|
||||
|
||||
This also means that, if the renderer keeps a reference to an object in browser, the object would never be released. So be careful to never leak the remote objects.
|
||||
|
||||
## Passing callbacks
|
||||
|
||||
Many APIs in browser accepts callbacks, so the `remote` module also supports passing callbacks when calling remote functions, and the callbacks passed would become remote functions in the browser.
|
||||
|
||||
But in order to avoid possible dead locks, the callbacks passed to browser would be called asynchronously in browser, so you should never expect the browser to get the return value of the passed callback.
|
||||
|
||||
Another thing is the lifetime of the remote callbacks in browser, it might be very tempting to do things like following:
|
||||
|
||||
```javascript
|
||||
var remote = require('remote');
|
||||
remote.getCurrentWindow().on('close', function() {
|
||||
// blabla...
|
||||
});
|
||||
```
|
||||
|
||||
Yes it will work correctly, but when you reload the window, the callback you setup on the object in browser will not be erased, resources are leaked and there is no magic in javascript to release a referenced object.
|
||||
|
||||
So if you really need to keep a reference of callbacks in browser, you should write the callback in browser and send messages to renderer. And also make use of DOM's events like `unload` and `beforeunload`, they will work perfectly.
|
||||
|
||||
## remote.require(module)
|
||||
|
||||
* `module` String
|
||||
|
||||
Return a module in browser.
|
||||
|
||||
## remote.getCurrentWindow()
|
||||
|
||||
Return the `BrowserWindow` object that represents current window.
|
||||
|
||||
`Note:` it doesn't return the `window` object which represents the global scope, instead it returns an instance of the `BrowserWindow` class which is created with `browser-window` module in browser.
|
||||
|
||||
## remote.getGlobal(name)
|
||||
|
||||
* `name` String
|
||||
|
||||
Return the `global[name]` value in browser.
|
||||
|
||||
## remote.process
|
||||
|
||||
Getter to return the `process` object in browser, this is the same with `remote.getGlobal('process')` but gets cached.
|
||||
@@ -13,9 +13,11 @@ wrapArgs = (args) ->
|
||||
else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId'
|
||||
type: 'remote-object', id: v8Util.getHiddenValue value, 'atomId'
|
||||
else if value? and typeof value is 'object'
|
||||
ret = type: 'object', members: []
|
||||
ret = type: 'object', name: value.constructor.name, members: []
|
||||
ret.members.push(name: prop, value: valueToMeta(field)) for prop, field of value
|
||||
ret
|
||||
else if typeof value is 'function' and v8Util.getHiddenValue value, 'returnValue'
|
||||
type: 'function-with-return-value', value: valueToMeta(value())
|
||||
else if typeof value is 'function'
|
||||
type: 'function', id: callbacksRegistry.add(value)
|
||||
else
|
||||
@@ -29,8 +31,7 @@ metaToValue = (meta) ->
|
||||
when 'value' then meta.value
|
||||
when 'array' then (metaToValue(el) for el in meta.members)
|
||||
when 'error'
|
||||
console.log meta.stack
|
||||
throw new Error(meta.message)
|
||||
throw new Error("#{meta.message}\n#{meta.stack}")
|
||||
else
|
||||
if meta.type is 'function'
|
||||
# A shadow class to represent the remote function object.
|
||||
@@ -56,10 +57,17 @@ metaToValue = (meta) ->
|
||||
for member in meta.members
|
||||
do (member) ->
|
||||
if member.type is 'function'
|
||||
ret[member.name] = ->
|
||||
# Call member function.
|
||||
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments)
|
||||
metaToValue ret
|
||||
ret[member.name] =
|
||||
class RemoteMemberFunction
|
||||
constructor: ->
|
||||
if @constructor is RemoteMemberFunction
|
||||
# Constructor call.
|
||||
obj = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', meta.id, member.name, wrapArgs(arguments)
|
||||
return metaToValue obj
|
||||
else
|
||||
# Call member function.
|
||||
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments)
|
||||
return metaToValue ret
|
||||
else
|
||||
ret.__defineSetter__ member.name, (value) ->
|
||||
# Set member data.
|
||||
@@ -121,3 +129,9 @@ processCache = null
|
||||
exports.__defineGetter__ 'process', ->
|
||||
processCache = exports.getGlobal('process') unless processCache?
|
||||
processCache
|
||||
|
||||
# Create a funtion that will return the specifed value when called in browser.
|
||||
exports.createFunctionWithReturnValue = (returnValue) ->
|
||||
func = -> returnValue
|
||||
v8Util.setHiddenValue func, 'returnValue', true
|
||||
func
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "renderer/api/atom_renderer_bindings.h"
|
||||
#include "renderer/atom_renderer_client.h"
|
||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
|
||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDraggableRegion.h"
|
||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
@@ -76,6 +78,19 @@ void AtomRenderViewObserver::FrameWillClose(WebFrame* frame) {
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), frame), vec.end());
|
||||
}
|
||||
|
||||
void AtomRenderViewObserver::DraggableRegionsChanged(WebKit::WebFrame* frame) {
|
||||
WebKit::WebVector<WebKit::WebDraggableRegion> webregions =
|
||||
frame->document().draggableRegions();
|
||||
std::vector<DraggableRegion> regions;
|
||||
for (size_t i = 0; i < webregions.size(); ++i) {
|
||||
DraggableRegion region;
|
||||
region.bounds = webregions[i].bounds;
|
||||
region.draggable = webregions[i].draggable;
|
||||
regions.push_back(region);
|
||||
}
|
||||
Send(new AtomViewHostMsg_UpdateDraggableRegions(routing_id(), regions));
|
||||
}
|
||||
|
||||
bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message)
|
||||
|
||||
@@ -32,6 +32,7 @@ class AtomRenderViewObserver : content::RenderViewObserver {
|
||||
|
||||
private:
|
||||
// content::RenderViewObserver implementation.
|
||||
virtual void DraggableRegionsChanged(WebKit::WebFrame* frame) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
|
||||
void OnBrowserMessage(const std::string& channel,
|
||||
|
||||
@@ -57,8 +57,13 @@ def bootstrap_brightray(url):
|
||||
|
||||
|
||||
def update_apm():
|
||||
## NB: Without this, subprocess incorrectly searches for npm.exe
|
||||
npm_cmd = 'npm'
|
||||
if sys.platform == 'win32':
|
||||
npm_cmd += '.cmd'
|
||||
|
||||
with scoped_cwd(os.path.join('vendor', 'apm')):
|
||||
subprocess.check_call(['npm', 'install', '.'])
|
||||
subprocess.check_call([npm_cmd, 'install', '.'])
|
||||
|
||||
|
||||
def update_node_modules():
|
||||
|
||||
@@ -20,7 +20,7 @@ def main():
|
||||
args = parse_args()
|
||||
for config in args.configuration:
|
||||
build_path = os.path.join('out', config)
|
||||
subprocess.call([ninja, '-C', build_path])
|
||||
subprocess.call([ninja, '-C', build_path, args.target])
|
||||
|
||||
|
||||
def parse_args():
|
||||
@@ -30,6 +30,10 @@ def parse_args():
|
||||
nargs='+',
|
||||
default=CONFIGURATIONS,
|
||||
required=False)
|
||||
parser.add_argument('-t', '--target',
|
||||
help='Build specified target',
|
||||
default='atom',
|
||||
required=False)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
|
||||
25
script/cibuild
Executable file
25
script/cibuild
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def main():
|
||||
run_script('bootstrap.py')
|
||||
run_script('cpplint.py')
|
||||
run_script('build.py')
|
||||
run_script('test.py', ['--ci'])
|
||||
run_script('create-dist.py')
|
||||
|
||||
|
||||
def run_script(script, args=[]):
|
||||
script = os.path.join(SOURCE_ROOT, 'script', script)
|
||||
subprocess.check_call([sys.executable, script] + args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
2
script/cpplint.py
vendored
2
script/cpplint.py
vendored
@@ -41,7 +41,7 @@ def list_files(directories, filters):
|
||||
def call_cpplint(files):
|
||||
cpplint = os.path.join(SOURCE_ROOT, 'vendor', 'depot_tools', 'cpplint.py')
|
||||
rules = '--filter=-build/header_guard,-build/include_what_you_use'
|
||||
subprocess.call([sys.executable, cpplint, rules] + files)
|
||||
subprocess.check_call([sys.executable, cpplint, rules] + files)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -6,17 +6,47 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
|
||||
from lib.util import *
|
||||
|
||||
|
||||
ATOM_SHELL_VRESION = get_atom_shell_version()
|
||||
NODE_VERSION = 'v0.10.18'
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
|
||||
NODE_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'node')
|
||||
DIST_HEADERS_DIR = os.path.join(DIST_DIR, 'node-{0}'.format(get_atom_shell_version()))
|
||||
DIST_HEADERS_NAME = 'node-{0}'.format(NODE_VERSION)
|
||||
DIST_HEADERS_DIR = os.path.join(DIST_DIR, DIST_HEADERS_NAME)
|
||||
|
||||
BUNDLE_NAME = 'Atom.app'
|
||||
BUNDLE_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release', BUNDLE_NAME)
|
||||
TARGET_PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
'darwin': 'darwin',
|
||||
'linux2': 'linux',
|
||||
'win32': 'win32',
|
||||
}[sys.platform]
|
||||
|
||||
TARGET_BINARIES = {
|
||||
'darwin': [
|
||||
],
|
||||
'win32': [
|
||||
'atom.exe',
|
||||
'chromiumcontent.dll',
|
||||
'content_shell.pak',
|
||||
'ffmpegsumo.dll',
|
||||
'icudt.dll',
|
||||
'libGLESv2.dll',
|
||||
],
|
||||
}
|
||||
TARGET_DIRECTORIES = {
|
||||
'darwin': [
|
||||
'Atom.app',
|
||||
],
|
||||
'win32': [
|
||||
'resources',
|
||||
],
|
||||
}
|
||||
|
||||
HEADERS_SUFFIX = [
|
||||
'.h',
|
||||
@@ -25,12 +55,12 @@ HEADERS_SUFFIX = [
|
||||
HEADERS_DIRS = [
|
||||
'src',
|
||||
'deps/http_parser',
|
||||
'deps/v8',
|
||||
'deps/zlib',
|
||||
'deps/uv',
|
||||
'tools',
|
||||
]
|
||||
HEADERS_FILES = [
|
||||
'common.gypi',
|
||||
'config.gypi',
|
||||
]
|
||||
|
||||
|
||||
@@ -44,6 +74,7 @@ def main():
|
||||
copy_license()
|
||||
create_version()
|
||||
create_zip()
|
||||
create_header_tarball()
|
||||
|
||||
|
||||
def force_build():
|
||||
@@ -52,12 +83,20 @@ def force_build():
|
||||
|
||||
|
||||
def copy_binaries():
|
||||
shutil.copytree(BUNDLE_DIR, os.path.join(DIST_DIR, BUNDLE_NAME),
|
||||
symlinks=True)
|
||||
out_dir = os.path.join(SOURCE_ROOT, 'out', 'Release')
|
||||
|
||||
for binary in TARGET_BINARIES[TARGET_PLATFORM]:
|
||||
shutil.copy2(os.path.join(out_dir, binary), DIST_DIR)
|
||||
|
||||
for directory in TARGET_DIRECTORIES[TARGET_PLATFORM]:
|
||||
shutil.copytree(os.path.join(out_dir, directory),
|
||||
os.path.join(DIST_DIR, directory),
|
||||
symlinks=True)
|
||||
|
||||
|
||||
def copy_headers():
|
||||
os.mkdir(DIST_HEADERS_DIR)
|
||||
# Copy standard node headers from node. repository.
|
||||
for include_path in HEADERS_DIRS:
|
||||
abs_path = os.path.join(NODE_DIR, include_path)
|
||||
for dirpath, dirnames, filenames in os.walk(abs_path):
|
||||
@@ -66,6 +105,20 @@ def copy_headers():
|
||||
if extension not in HEADERS_SUFFIX:
|
||||
continue
|
||||
copy_source_file(os.path.join(dirpath, filename))
|
||||
for other_file in HEADERS_FILES:
|
||||
copy_source_file(source = os.path.join(NODE_DIR, other_file))
|
||||
|
||||
# Copy V8 headers from chromium's repository.
|
||||
src = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', 'download',
|
||||
'libchromiumcontent', 'src')
|
||||
for dirpath, dirnames, filenames in os.walk(os.path.join(src, 'v8')):
|
||||
for filename in filenames:
|
||||
extension = os.path.splitext(filename)[1]
|
||||
if extension not in HEADERS_SUFFIX:
|
||||
continue
|
||||
copy_source_file(source=os.path.join(dirpath, filename),
|
||||
start=src,
|
||||
destination=os.path.join(DIST_HEADERS_DIR, 'deps'))
|
||||
|
||||
|
||||
def copy_license():
|
||||
@@ -76,24 +129,33 @@ def copy_license():
|
||||
def create_version():
|
||||
version_path = os.path.join(SOURCE_ROOT, 'dist', 'version')
|
||||
with open(version_path, 'w') as version_file:
|
||||
version_file.write(get_atom_shell_version())
|
||||
version_file.write(ATOM_SHELL_VRESION)
|
||||
|
||||
|
||||
def create_zip():
|
||||
print "Zipping distribution..."
|
||||
zip_file = os.path.join(SOURCE_ROOT, 'atom-shell.zip')
|
||||
safe_unlink(zip_file)
|
||||
dist_name = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION,
|
||||
TARGET_PLATFORM)
|
||||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = ['Atom.app', 'LICENSE', 'version']
|
||||
subprocess.check_call(['zip', '-r', '-y', zip_file] + files)
|
||||
files = TARGET_BINARIES[TARGET_PLATFORM] + \
|
||||
TARGET_DIRECTORIES[TARGET_PLATFORM] + \
|
||||
['LICENSE', 'version']
|
||||
make_zip(zip_file, files)
|
||||
|
||||
|
||||
def copy_source_file(source):
|
||||
relative = os.path.relpath(source, start=NODE_DIR)
|
||||
destination = os.path.join(DIST_HEADERS_DIR, relative)
|
||||
safe_mkdir(os.path.dirname(destination))
|
||||
shutil.copy2(source, destination)
|
||||
def create_header_tarball():
|
||||
with scoped_cwd(DIST_DIR):
|
||||
tarball = tarfile.open(name=DIST_HEADERS_DIR + '.tar.gz', mode='w:gz')
|
||||
tarball.add(DIST_HEADERS_NAME)
|
||||
tarball.close()
|
||||
|
||||
|
||||
def copy_source_file(source, start=NODE_DIR, destination=DIST_HEADERS_DIR):
|
||||
relative = os.path.relpath(source, start=start)
|
||||
final_destination = os.path.join(destination, relative)
|
||||
safe_mkdir(os.path.dirname(final_destination))
|
||||
shutil.copy2(source, final_destination)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -64,6 +64,16 @@ def extract_zip(zip_path, destination):
|
||||
with zipfile.ZipFile(zip_path) as z:
|
||||
z.extractall(destination)
|
||||
|
||||
def make_zip(zip_file_path, files):
|
||||
safe_unlink(zip_file_path)
|
||||
if sys.platform == 'darwin':
|
||||
subprocess.check_call(['zip', '-r', '-y', zip_file_path] + files)
|
||||
else:
|
||||
zip_file = zipfile.ZipFile(zip_file_path, "w")
|
||||
for filename in files:
|
||||
zip_file.write(filename, filename)
|
||||
zip_file.close()
|
||||
|
||||
|
||||
def rm_rf(path):
|
||||
try:
|
||||
|
||||
@@ -17,7 +17,7 @@ def main():
|
||||
else:
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'atom.exe')
|
||||
|
||||
subprocess.check_call([atom_shell, 'spec'])
|
||||
subprocess.check_call([atom_shell, 'spec'] + sys.argv[1:])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import glob
|
||||
import os
|
||||
@@ -10,28 +11,41 @@ import tempfile
|
||||
from lib.util import *
|
||||
|
||||
|
||||
TARGET_PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
'darwin': 'darwin',
|
||||
'linux2': 'linux',
|
||||
'win32': 'win32',
|
||||
}[sys.platform]
|
||||
|
||||
ATOM_SHELL_VRESION = get_atom_shell_version()
|
||||
NODE_VERSION = 'v0.10.18'
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release')
|
||||
DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
|
||||
DIST_NAME = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION, TARGET_PLATFORM)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
ensure_s3put()
|
||||
if not dist_newer_than_head():
|
||||
create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
|
||||
subprocess.check_call([sys.executable, create_dist])
|
||||
upload()
|
||||
except AssertionError as e:
|
||||
return e.message
|
||||
args = parse_args()
|
||||
|
||||
if not dist_newer_than_head():
|
||||
create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
|
||||
subprocess.check_call([sys.executable, create_dist])
|
||||
|
||||
bucket, access_key, secret_key = s3_config()
|
||||
upload(bucket, access_key, secret_key)
|
||||
if not args.no_update_version:
|
||||
update_version(bucket, access_key, secret_key)
|
||||
|
||||
|
||||
def ensure_s3put():
|
||||
output = ''
|
||||
try:
|
||||
output = subprocess.check_output(['s3put', '--help'])
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
assert 'multipart' in output, 'Error: Please install boto and filechunkio'
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='upload distribution file')
|
||||
parser.add_argument('-n', '--no-update-version',
|
||||
help='Do not update the latest version file',
|
||||
action='store_true')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def dist_newer_than_head():
|
||||
@@ -39,7 +53,7 @@ def dist_newer_than_head():
|
||||
try:
|
||||
head_time = subprocess.check_output(['git', 'log', '--pretty=format:%at',
|
||||
'-n', '1']).strip()
|
||||
dist_time = os.path.getmtime(os.path.join(SOURCE_ROOT, 'atom-shell.zip'))
|
||||
dist_time = os.path.getmtime(os.path.join(DIST_DIR, DIST_NAME))
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
@@ -48,15 +62,36 @@ def dist_newer_than_head():
|
||||
return dist_time > int(head_time)
|
||||
|
||||
|
||||
def upload():
|
||||
os.chdir(SOURCE_ROOT)
|
||||
bucket, access_key, secret_key = s3_config()
|
||||
def upload(bucket, access_key, secret_key, version=ATOM_SHELL_VRESION):
|
||||
os.chdir(DIST_DIR)
|
||||
|
||||
version = get_atom_shell_version()
|
||||
s3put(bucket, access_key, secret_key, SOURCE_ROOT,
|
||||
'atom-shell/{0}'.format(version), glob.glob('atom-shell*.zip'))
|
||||
s3put(bucket, access_key, secret_key, DIST_DIR,
|
||||
'atom-shell/{0}'.format(version), [DIST_NAME])
|
||||
s3put(bucket, access_key, secret_key, DIST_DIR,
|
||||
'atom-shell/dist/{0}'.format(NODE_VERSION), glob.glob('node-*.tar.gz'))
|
||||
|
||||
update_version(bucket, access_key, secret_key)
|
||||
if TARGET_PLATFORM == 'win32':
|
||||
# Generate the node.lib.
|
||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||
subprocess.check_call([sys.executable, build, '-c', 'Release',
|
||||
'-t', 'generate_node_lib'])
|
||||
|
||||
# Upload the 32bit node.lib.
|
||||
node_lib = os.path.join(OUT_DIR, 'node.lib')
|
||||
s3put(bucket, access_key, secret_key, OUT_DIR,
|
||||
'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
|
||||
|
||||
# Upload the fake 64bit node.lib.
|
||||
touch_x64_node_lib()
|
||||
node_lib = os.path.join(OUT_DIR, 'x64', 'node.lib')
|
||||
s3put(bucket, access_key, secret_key, OUT_DIR,
|
||||
'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
|
||||
|
||||
|
||||
def update_version(bucket, access_key, secret_key):
|
||||
prefix = os.path.join(SOURCE_ROOT, 'dist')
|
||||
version = os.path.join(prefix, 'version')
|
||||
s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [version])
|
||||
|
||||
|
||||
def s3_config():
|
||||
@@ -70,12 +105,6 @@ def s3_config():
|
||||
return config
|
||||
|
||||
|
||||
def update_version(bucket, access_key, secret_key):
|
||||
prefix = os.path.join(SOURCE_ROOT, 'dist')
|
||||
version = os.path.join(prefix, 'version')
|
||||
s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [ version ])
|
||||
|
||||
|
||||
def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
|
||||
args = [
|
||||
's3put',
|
||||
@@ -90,6 +119,13 @@ def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
|
||||
subprocess.check_call(args)
|
||||
|
||||
|
||||
def touch_x64_node_lib():
|
||||
x64_dir = os.path.join(OUT_DIR, 'x64')
|
||||
safe_mkdir(x64_dir)
|
||||
with open(os.path.join(x64_dir, 'node.lib'), 'w+') as node_lib:
|
||||
node_lib.write('Invalid library')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main())
|
||||
|
||||
@@ -16,7 +16,14 @@ describe 'ipc', ->
|
||||
a = remote.require path.join(fixtures, 'module', 'id.js')
|
||||
assert.equal a.id, 1127
|
||||
|
||||
describe 'remote object', ->
|
||||
describe 'remote.createFunctionWithReturnValue', ->
|
||||
it 'should be called in browser synchronously', ->
|
||||
buf = new Buffer('test')
|
||||
call = remote.require path.join(fixtures, 'module', 'call.js')
|
||||
result = call.call remote.createFunctionWithReturnValue(buf)
|
||||
assert.equal result.constructor.name, 'Buffer'
|
||||
|
||||
describe 'remote object in renderer', ->
|
||||
it 'can change its properties', ->
|
||||
property = remote.require path.join(fixtures, 'module', 'property.js')
|
||||
assert.equal property.property, 1127
|
||||
@@ -28,6 +35,17 @@ describe 'ipc', ->
|
||||
# Restore.
|
||||
property.property = 1127
|
||||
|
||||
it 'can construct an object from its member', ->
|
||||
call = remote.require path.join(fixtures, 'module', 'call.js')
|
||||
obj = new call.constructor
|
||||
assert.equal obj.test, 'test'
|
||||
|
||||
describe 'remote value in browser', ->
|
||||
it 'keeps its constructor name for objects', ->
|
||||
buf = new Buffer('test')
|
||||
print_name = remote.require path.join(fixtures, 'module', 'print_name.js')
|
||||
assert.equal print_name.print(buf), 'Buffer'
|
||||
|
||||
describe 'ipc.send', ->
|
||||
it 'should work when sending an object containing id property', (done) ->
|
||||
obj = id: 1, name: 'ly'
|
||||
|
||||
137
spec/api/protocol.coffee
Normal file
137
spec/api/protocol.coffee
Normal file
@@ -0,0 +1,137 @@
|
||||
assert = require 'assert'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
protocol = remote.require 'protocol'
|
||||
|
||||
describe 'protocol API', ->
|
||||
describe 'protocol.registerProtocol', ->
|
||||
it 'throws error when scheme is already registered', (done) ->
|
||||
register = -> protocol.registerProtocol('test1', ->)
|
||||
protocol.once 'registered', (scheme) ->
|
||||
assert.equal scheme, 'test1'
|
||||
assert.throws register, /The scheme is already registered/
|
||||
protocol.unregisterProtocol 'test1'
|
||||
done()
|
||||
register()
|
||||
|
||||
it 'calls the callback when scheme is visited', (done) ->
|
||||
protocol.registerProtocol 'test2', (request) ->
|
||||
assert.equal request.url, 'test2://test2'
|
||||
assert.equal request.referrer, window.location.toString()
|
||||
protocol.unregisterProtocol 'test2'
|
||||
done()
|
||||
$.get 'test2://test2', ->
|
||||
|
||||
describe 'protocol.unregisterProtocol', ->
|
||||
it 'throws error when scheme does not exist', ->
|
||||
unregister = -> protocol.unregisterProtocol 'test3'
|
||||
assert.throws unregister, /The scheme has not been registered/
|
||||
|
||||
describe 'registered protocol callback', ->
|
||||
it 'returns string should send the string as request content', (done) ->
|
||||
handler = remote.createFunctionWithReturnValue 'valar morghulis'
|
||||
protocol.registerProtocol 'atom-string', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-string://fake-host'
|
||||
success: (data) ->
|
||||
assert.equal data, handler()
|
||||
protocol.unregisterProtocol 'atom-string'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-string'
|
||||
|
||||
it 'returns RequestStringJob should send string', (done) ->
|
||||
data = 'valar morghulis'
|
||||
job = new protocol.RequestStringJob(mimeType: 'text/html', data: data)
|
||||
handler = remote.createFunctionWithReturnValue job
|
||||
protocol.registerProtocol 'atom-string-job', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-string-job://fake-host'
|
||||
success: (response) ->
|
||||
assert.equal response, data
|
||||
protocol.unregisterProtocol 'atom-string-job'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-string-job'
|
||||
|
||||
it 'returns RequestFileJob should send file', (done) ->
|
||||
job = new protocol.RequestFileJob(__filename)
|
||||
handler = remote.createFunctionWithReturnValue job
|
||||
protocol.registerProtocol 'atom-file-job', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-file-job://' + __filename
|
||||
success: (data) ->
|
||||
content = require('fs').readFileSync __filename
|
||||
assert.equal data, String(content)
|
||||
protocol.unregisterProtocol 'atom-file-job'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-file-job'
|
||||
|
||||
describe 'protocol.isHandledProtocol', ->
|
||||
it 'returns true if the scheme can be handled', ->
|
||||
assert.equal protocol.isHandledProtocol('file'), true
|
||||
assert.equal protocol.isHandledProtocol('http'), true
|
||||
assert.equal protocol.isHandledProtocol('https'), true
|
||||
assert.equal protocol.isHandledProtocol('atom'), false
|
||||
|
||||
describe 'protocol.interceptProtocol', ->
|
||||
it 'throws error when scheme is not a registered one', ->
|
||||
register = -> protocol.interceptProtocol('test-intercept', ->)
|
||||
assert.throws register, /Cannot intercept procotol/
|
||||
|
||||
it 'throws error when scheme is a custom protocol', (done) ->
|
||||
protocol.once 'unregistered', (scheme) ->
|
||||
assert.equal scheme, 'atom'
|
||||
done()
|
||||
protocol.once 'registered', (scheme) ->
|
||||
assert.equal scheme, 'atom'
|
||||
register = -> protocol.interceptProtocol('test-intercept', ->)
|
||||
assert.throws register, /Cannot intercept procotol/
|
||||
protocol.unregisterProtocol scheme
|
||||
protocol.registerProtocol('atom', ->)
|
||||
|
||||
it 'returns original job when callback returns nothing', (done) ->
|
||||
targetScheme = 'file'
|
||||
protocol.once 'intercepted', (scheme) ->
|
||||
assert.equal scheme, targetScheme
|
||||
free = -> protocol.uninterceptProtocol targetScheme
|
||||
$.ajax
|
||||
url: "#{targetScheme}://#{__filename}",
|
||||
success: ->
|
||||
protocol.once 'unintercepted', (scheme) ->
|
||||
assert.equal scheme, targetScheme
|
||||
done()
|
||||
free()
|
||||
error: (xhr, errorType, error) ->
|
||||
free()
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.interceptProtocol targetScheme, (request) ->
|
||||
if process.platform is 'win32'
|
||||
pathInUrl = path.normalize request.url.substr(8)
|
||||
assert.equal pathInUrl, __filename
|
||||
else
|
||||
assert.equal request.url, "#{targetScheme}://#{__filename}"
|
||||
|
||||
it 'can override original protocol handler', (done) ->
|
||||
handler = remote.createFunctionWithReturnValue 'valar morghulis'
|
||||
protocol.once 'intercepted', ->
|
||||
free = -> protocol.uninterceptProtocol 'file'
|
||||
$.ajax
|
||||
url: 'file://fake-host'
|
||||
success: (data) ->
|
||||
protocol.once 'unintercepted', ->
|
||||
assert.equal data, handler()
|
||||
done()
|
||||
free()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
free()
|
||||
protocol.interceptProtocol 'file', handler
|
||||
95
spec/api/window.coffee
Normal file
95
spec/api/window.coffee
Normal file
@@ -0,0 +1,95 @@
|
||||
assert = require 'assert'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
BrowserWindow = remote.require 'browser-window'
|
||||
|
||||
fixtures = path.resolve __dirname, '..', 'fixtures'
|
||||
|
||||
describe 'window module', ->
|
||||
describe 'BrowserWindow.close()', ->
|
||||
it 'should emit unload handler', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'loading-state-changed', (event, isLoading) ->
|
||||
if (!isLoading)
|
||||
w.close()
|
||||
w.on 'destroyed', ->
|
||||
test = path.join(fixtures, 'api', 'unload')
|
||||
content = fs.readFileSync(test)
|
||||
fs.unlinkSync(test)
|
||||
assert.equal String(content), 'unload'
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'unload.html')
|
||||
|
||||
it 'should emit beforeunload handler', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'onbeforeunload', ->
|
||||
w.destroy()
|
||||
done()
|
||||
w.on 'loading-state-changed', (event, isLoading) ->
|
||||
if (!isLoading)
|
||||
w.close()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'beforeunload-false.html')
|
||||
|
||||
describe 'window.close()', ->
|
||||
xit 'should emit unload handler', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'closed', ->
|
||||
test = path.join(fixtures, 'api', 'close')
|
||||
content = fs.readFileSync(test)
|
||||
fs.unlinkSync(test)
|
||||
assert.equal String(content), 'close'
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close.html')
|
||||
|
||||
it 'should emit beforeunload handler', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'onbeforeunload', ->
|
||||
w.destroy()
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')
|
||||
|
||||
describe 'BrowserWindow.loadUrl(url)', ->
|
||||
it 'should emit loading-state-changed event', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
count = 0
|
||||
w.on 'loading-state-changed', (event, isLoading) ->
|
||||
if count == 0
|
||||
assert.equal isLoading, true
|
||||
else if count == 1
|
||||
assert.equal isLoading, false
|
||||
w.close()
|
||||
done()
|
||||
else
|
||||
w.close()
|
||||
assert false
|
||||
|
||||
++count
|
||||
w.loadUrl 'about:blank'
|
||||
|
||||
describe 'beforeunload handler', ->
|
||||
it 'returning true would not prevent close', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'closed', ->
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-true.html')
|
||||
|
||||
it 'returning non-empty string would not prevent close', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'closed', ->
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-string.html')
|
||||
|
||||
it 'returning false would prevent close', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'onbeforeunload', ->
|
||||
w.destroy()
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')
|
||||
|
||||
it 'returning empty string would prevent close', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'onbeforeunload', ->
|
||||
w.destroy()
|
||||
done()
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'close-beforeunload-empty-string.html')
|
||||
12
spec/fixtures/api/beforeunload-false.html
vendored
Normal file
12
spec/fixtures/api/beforeunload-false.html
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('remote').getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
13
spec/fixtures/api/close-beforeunload-empty-string.html
vendored
Normal file
13
spec/fixtures/api/close-beforeunload-empty-string.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('remote').getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
return '';
|
||||
}
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
13
spec/fixtures/api/close-beforeunload-false.html
vendored
Normal file
13
spec/fixtures/api/close-beforeunload-false.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('remote').getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
return false;
|
||||
}
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
13
spec/fixtures/api/close-beforeunload-string.html
vendored
Normal file
13
spec/fixtures/api/close-beforeunload-string.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('remote').getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
return 'string';
|
||||
}
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
14
spec/fixtures/api/close-beforeunload-true.html
vendored
Normal file
14
spec/fixtures/api/close-beforeunload-true.html
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('remote').getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
return true;
|
||||
}
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
spec/fixtures/api/close.html
vendored
Normal file
10
spec/fixtures/api/close.html
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.addEventListener('unload', function (e) {
|
||||
require('fs').writeFileSync(__dirname + '/close', 'close');
|
||||
}, false);
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
spec/fixtures/api/unload.html
vendored
Normal file
9
spec/fixtures/api/unload.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.addEventListener('unload', function (e) {
|
||||
require('fs').writeFileSync(__dirname + '/unload', 'unload');
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
7
spec/fixtures/module/call.js
vendored
Normal file
7
spec/fixtures/module/call.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
exports.call = function(func) {
|
||||
return func();
|
||||
}
|
||||
|
||||
exports.constructor = function() {
|
||||
this.test = 'test';
|
||||
}
|
||||
14
spec/fixtures/module/fork_ping.js
vendored
Normal file
14
spec/fixtures/module/fork_ping.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
process.on('uncaughtException', function(error) {
|
||||
process.send(error.stack);
|
||||
});
|
||||
|
||||
var child = require('child_process').fork(__dirname + '/ping.js');
|
||||
process.on('message', function(msg) {
|
||||
child.send(msg);
|
||||
});
|
||||
child.on('message', function (msg) {
|
||||
process.send(msg);
|
||||
});
|
||||
child.on('exit', function(code) {
|
||||
process.exit(code);
|
||||
});
|
||||
1
spec/fixtures/module/ping.js
vendored
1
spec/fixtures/module/ping.js
vendored
@@ -1,3 +1,4 @@
|
||||
process.on('message', function(msg) {
|
||||
process.send(msg);
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
3
spec/fixtures/module/print_name.js
vendored
Normal file
3
spec/fixtures/module/print_name.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
exports.print = function(obj) {
|
||||
return obj.constructor.name;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="referrer" content="always">
|
||||
<link href="../node_modules/mocha/mocha.css" rel="stylesheet">
|
||||
<script src="jquery-2.0.3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -8,17 +10,44 @@
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
(function() {
|
||||
// Check if we are running in CI.
|
||||
var argv = require('remote').process.argv;
|
||||
var isCi = argv[1] == '--ci';
|
||||
|
||||
if (!isCi) {
|
||||
var win = require('remote').getCurrentWindow();
|
||||
win.show();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
// Show DevTools.
|
||||
document.oncontextmenu = function(e) {
|
||||
require('remote').getCurrentWindow().inspectElement(e.clientX, e.clientY);
|
||||
}
|
||||
|
||||
require('coffee-script'); // Supports .coffee tests.
|
||||
var ipc = require('ipc');
|
||||
|
||||
// Rediret all output to browser.
|
||||
if (isCi) {
|
||||
global.__defineGetter__('console', function() {
|
||||
return {
|
||||
log: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.log', args);
|
||||
},
|
||||
error: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.error', args);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var Mocha = require('mocha');
|
||||
|
||||
var mocha = new Mocha();
|
||||
mocha.ui('bdd').reporter('html');
|
||||
mocha.ui('bdd').reporter(isCi ? 'tap' : 'html');
|
||||
|
||||
var query = Mocha.utils.parseQuery(window.location.search || '');
|
||||
if (query.grep) mocha.grep(query.grep);
|
||||
@@ -33,8 +62,10 @@
|
||||
});
|
||||
|
||||
walker.on('end', function() {
|
||||
mocha.run(function() {
|
||||
var runner = mocha.run(function() {
|
||||
Mocha.utils.highlightTags('code');
|
||||
if (isCi)
|
||||
ipc.sendChannel('process.exit', runner.failures);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
6
spec/jquery-2.0.3.min.js
vendored
Normal file
6
spec/jquery-2.0.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
spec/main.js
18
spec/main.js
@@ -10,6 +10,22 @@ ipc.on('message', function() {
|
||||
ipc.send.apply(this, arguments);
|
||||
});
|
||||
|
||||
ipc.on('console.log', function(pid, rid, args) {
|
||||
console.log.apply(console, args);
|
||||
});
|
||||
|
||||
ipc.on('console.error', function(pid, rid, args) {
|
||||
console.log.apply(console, args);
|
||||
});
|
||||
|
||||
ipc.on('process.exit', function(pid, rid, code) {
|
||||
process.exit(code);
|
||||
});
|
||||
|
||||
ipc.on('eval', function(ev, pid, rid, script) {
|
||||
ev.result = eval(script);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function() {
|
||||
window.openDevTools();
|
||||
});
|
||||
@@ -21,9 +37,9 @@ app.on('window-all-closed', function() {
|
||||
app.on('finish-launching', function() {
|
||||
window = new BrowserWindow({
|
||||
title: 'atom-shell tests',
|
||||
show: false,
|
||||
width: 800,
|
||||
height: 600
|
||||
});
|
||||
window.loadUrl('file://' + __dirname + '/index.html');
|
||||
window.focus();
|
||||
});
|
||||
|
||||
@@ -12,3 +12,19 @@ describe 'child_process', ->
|
||||
assert.equal msg, 'message'
|
||||
done()
|
||||
child.send 'message'
|
||||
|
||||
it 'should work in forked process', (done) ->
|
||||
child = child_process.fork path.join(fixtures, 'module', 'fork_ping.js')
|
||||
child.on 'message', (msg) ->
|
||||
assert.equal msg, 'message'
|
||||
done()
|
||||
child.send 'message'
|
||||
|
||||
it 'should work in forked process when options.env is specifed', (done) ->
|
||||
child = child_process.fork path.join(fixtures, 'module', 'fork_ping.js'),
|
||||
[],
|
||||
path: process.env['PATH']
|
||||
child.on 'message', (msg) ->
|
||||
assert.equal msg, 'message'
|
||||
done()
|
||||
child.send 'message'
|
||||
|
||||
4
spec/web/debugger.coffee
Normal file
4
spec/web/debugger.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
describe 'debugger', ->
|
||||
describe 'heap snapshot', ->
|
||||
it 'should not crash', ->
|
||||
process.atomBinding('v8_util').takeHeapSnapshot()
|
||||
@@ -2,6 +2,6 @@ describe 'webrtc', ->
|
||||
describe 'navigator.webkitGetUserMedia', ->
|
||||
it 'should call its callbacks', (done) ->
|
||||
@timeout 5000
|
||||
navigator.webkitGetUserMedia audio: true, video: true,
|
||||
navigator.webkitGetUserMedia audio: true, video: false,
|
||||
-> done()
|
||||
-> done()
|
||||
|
||||
2
vendor/apm
vendored
2
vendor/apm
vendored
Submodule vendor/apm updated: cbf46094b6...2c9da12d10
2
vendor/brightray
vendored
2
vendor/brightray
vendored
Submodule vendor/brightray updated: c62f91a82e...3cb2782cff
2
vendor/node
vendored
2
vendor/node
vendored
Submodule vendor/node updated: 4927dc9d04...7f44fac0fd
Reference in New Issue
Block a user