Compare commits

..

134 Commits

Author SHA1 Message Date
Cheng Zhao
8cb624d828 Dump v0.4.6. 2013-09-12 16:10:11 +08:00
Cheng Zhao
cec640f572 mac: Always use "Atom" as name when find helper process. Fixes #89. 2013-09-12 15:42:36 +08:00
Cheng Zhao
f38eb1b66f mac: Import chromium's CustomFrameView code. 2013-09-11 13:46:36 +08:00
Cheng Zhao
a567ba08ea Hide the fullscreen button when leaving fullscreen mode. Fixes #88. 2013-09-11 13:05:08 +08:00
Cheng Zhao
91d54a74e1 doc: Metion the frameless-window-demo. 2013-09-09 20:17:47 +08:00
Cheng Zhao
da9cce3f2d Dump v0.4.5. 2013-09-09 16:13:01 +08:00
Cheng Zhao
92241b91ce doc: Separate pages into sub directories. 2013-09-09 15:49:13 +08:00
Cheng Zhao
6b81070f67 doc: Add titles for all pages. 2013-09-09 15:35:57 +08:00
Cheng Zhao
3715dd2a20 💄 Remove a mistyped character. 2013-09-09 15:27:19 +08:00
Cheng Zhao
eb6fa98ed0 Merge pull request #85 from atom/frameless-window
Add frameless window support, fixes #72.
2013-09-09 00:24:20 -07:00
Cheng Zhao
8ddb85774a doc: Add titles for browser-window and frameless-window. 2013-09-09 15:19:09 +08:00
Cheng Zhao
8caf5fac06 doc: Document frameless window. 2013-09-09 14:52:46 +08:00
Cheng Zhao
cc62978ac3 win: Add NativeWindowFramelessView as non-client view. 2013-09-09 14:30:07 +08:00
Cheng Zhao
f833423a2f win: Save draggable region. 2013-09-09 12:12:17 +08:00
Cheng Zhao
3c0671c179 Quit when all windows are closed if running an app by passing it in command line. 2013-09-09 10:54:08 +08:00
Cheng Zhao
a00bf3e1e1 Print stack when got error on startup. 2013-09-09 10:49:28 +08:00
Cheng Zhao
ce487fe1da Make sure child scripts are quit after specs are done. 2013-09-09 09:53:08 +08:00
Cheng Zhao
a73aea3bda Update apm: set both HOME and USERPROFILE in environment under Windows. 2013-09-09 09:24:54 +08:00
Cheng Zhao
b9d994dca2 Make sure the cursor doesn't drift away when dragging window. 2013-09-06 12:12:17 +08:00
Cheng Zhao
b7c2295a1c Don't use setMouseDownCanMoveWindow to implement draggable area.
It would not work when we have the in-window devtools.
2013-09-06 11:54:52 +08:00
Cheng Zhao
da2ded5453 Implement frameless window on OS X.
Most of the code came from Chromium's packaged app window.
2013-09-05 23:52:29 +08:00
Cheng Zhao
a5eb9ea08f Add has_frame_ attribute for NativeWindow. 2013-09-05 21:43:47 +08:00
Cheng Zhao
4223867dbc Send and receive the AtomViewHostMsg_UpdateDraggableRegions message. 2013-09-05 20:06:54 +08:00
Cheng Zhao
40273cf37d Add IPC messages and structs for passing draggable regions. 2013-09-05 19:46:12 +08:00
Cheng Zhao
bc9c95d77d 💄 fix the protocol module spec on OS X. 2013-09-05 18:28:48 +08:00
Cheng Zhao
6a322f8bd6 Update apm for node v0.10.18. 2013-09-05 15:41:44 +08:00
Cheng Zhao
e7bc368785 Dump version to v0.4.4. 2013-09-05 15:17:05 +08:00
Cheng Zhao
aad0c8e996 Update node to v0.10.18. 2013-09-05 15:15:13 +08:00
Cheng Zhao
bf4756fdfb Pass PATH environment in the child_process.fork spec.
The uv_spawn under Windows requires the PATH environment variable to be
there, otherwise it would throw a 203 system error, it should be a bug
of node.
2013-09-05 14:55:22 +08:00
Cheng Zhao
8acd6d6c8a 💄 fix spec failure caused by win32 path delimiter. 2013-09-05 12:24:08 +08:00
Cheng Zhao
256215b749 Use base::Environment to replace getenv. 2013-09-05 12:18:19 +08:00
Cheng Zhao
a3e5b21118 Merge pull request #84 from atom/nested-fork
Use environment variable to detect whether to run as node.

Fixed # 83.
2013-09-04 18:55:20 -07:00
Cheng Zhao
84a3eb5411 Also fix nested child_process.fork on Windows. 2013-09-05 09:49:22 +08:00
Cheng Zhao
e17da272f4 Make child_process.fork work when options.env is set. 2013-09-05 09:47:32 +08:00
Cheng Zhao
88bdff5832 Use environment variable to detect whether to run as node. 2013-09-05 09:22:24 +08:00
Cheng Zhao
ae18a90f7e Add test case for #83. 2013-09-05 09:21:39 +08:00
Cheng Zhao
3b7dd85d3f Merge pull request #65 from atom/custom-protocol
Support custom protocols
2013-09-04 03:33:09 -07:00
Cheng Zhao
128d9c78db Add documentation for protocol module. 2013-09-03 18:22:40 +08:00
Cheng Zhao
c7fed48c4a Emit erros when getting errors in IO thread. 2013-09-03 17:21:10 +08:00
Cheng Zhao
7737708fdd Add protocol interceptor API. 2013-09-03 16:50:10 +08:00
Cheng Zhao
9ba08d5e67 💄 fix reversed flag for updating latest version. 2013-09-02 18:17:06 +08:00
Cheng Zhao
6c3dc9e526 Dump v0.4.3. 2013-09-02 17:59:17 +08:00
Cheng Zhao
75a24a2e67 Update specs to match the documents. 2013-09-02 16:54:54 +08:00
Cheng Zhao
893309aa8a 💄 fix the window specs. 2013-09-02 16:47:53 +08:00
Cheng Zhao
f17864372e Use setTimeout instead of setImmediate in fixtures.
The setImmediate is implemented in node and may have unexpected affects.
2013-09-02 16:46:08 +08:00
Cheng Zhao
610ac5b045 Revert "💄 for the beforeunload handler."
Restore the previous behavior of beforeunload handler.

This reverts commit b1f30c1eb6.
2013-09-02 16:39:00 +08:00
Cheng Zhao
cfb957a603 Add specs for the beforeunload handler. 2013-09-02 16:28:36 +08:00
Cheng Zhao
e423f601c0 Dump v0.4.2. 2013-09-02 13:34:58 +08:00
Cheng Zhao
504f96ae08 Merge pull request #81 from atom/window-native-modules
Fix node native modules support on Windows
2013-09-01 22:31:31 -07:00
Cheng Zhao
3b149945bf Upload a fake empty x64 node.lib.
We only allow building ia32 build of atom-shell, so the x64 node.lib is
not in use, but it's required by node-gyp.
2013-09-02 13:27:26 +08:00
Cheng Zhao
5fe9f281ac Only generate node.lib when we need to upload node headers. 2013-08-31 17:00:13 +08:00
Cheng Zhao
766347ffae Generate node.lib from atom.lib and chromiumcontent.dll.lib. 2013-08-31 16:37:01 +08:00
Cheng Zhao
a5bc2fdb44 Don't use the cmd paramter passed by WinMain.
It doesn't include the argv[0], 💩.
2013-08-31 15:42:41 +08:00
Cheng Zhao
e7d4b44d05 💄 for node version and upload node.lib. 2013-08-31 15:20:59 +08:00
Cheng Zhao
30c9cd4318 Use dummy stdin stream on Windows. 2013-08-31 12:13:08 +08:00
Cheng Zhao
5787b4cd6f Also ship ffmpegsumo.dll in the distribution. 2013-08-31 11:13:11 +08:00
Cheng Zhao
a26308d902 Use node's version for the node headers tarball. 2013-08-31 10:51:53 +08:00
Cheng Zhao
c2093946c8 Add flag for upload script to skip updating version.
When uploading multiple distributions in future, we need to make sure
all distributions have to ben uploaded before triggering the
update-atom-shell script of Atom.
2013-08-31 10:48:47 +08:00
Cheng Zhao
0286379706 Don't require multipart for uploading to S3.
The file we are uploading is not large, and multipart works really bad
on bad networking.
2013-08-31 10:36:13 +08:00
Cheng Zhao
6765ec30f1 The distribution name should contain version and platform. 2013-08-31 10:35:01 +08:00
Cheng Zhao
beba27ed1e Create zip distribution for binaries on Windows. 2013-08-31 09:37:23 +08:00
Cheng Zhao
24f510ca03 Ship correct binaries on Windows. 2013-08-31 09:06:27 +08:00
Cheng Zhao
2cbe823773 Use Chromium V8's headers in the node headers tarball. 2013-08-31 08:22:16 +08:00
Cheng Zhao
790c53825b Merge pull request #79 from atom/message-box-button-order
Fix button order on Win32
2013-08-30 16:41:25 -07:00
Paul Betts
10bd2384d0 Fix button order on Win32
On Windows, the button order is left to right, with the primary action on the
left (i.e. "Ok" "Cancel"). On Mac, the button order is that the primary action
is nearest to the corner.
2013-08-30 12:08:04 -07:00
Cheng Zhao
335db788a5 Add InterceptProtocol AtomURLRequestJobFactory. 2013-08-30 21:15:46 +08:00
Cheng Zhao
d8cd3d78ff Make ReadRawData a public member, so there is no need to detect types. 2013-08-30 20:49:27 +08:00
Cheng Zhao
2a462cc2b7 💄 fix cpplint warning. 2013-08-30 20:24:42 +08:00
Cheng Zhao
df30f130d3 Separate logics between low level URLRequestJob and js calls. 2013-08-30 20:02:17 +08:00
Cheng Zhao
24e613c827 Separate URLRequestStringJob from atom_api_protocol. 2013-08-30 16:16:41 +08:00
Cheng Zhao
7df256f8dc Move atom_url_request_job_factory to browser/net. 2013-08-30 16:10:36 +08:00
Cheng Zhao
2000f88c84 Allow passing a fallback protocol handler for request jobs. 2013-08-30 15:48:57 +08:00
Cheng Zhao
db890feb51 Don't pollute console with error stack. 2013-08-30 13:04:21 +08:00
Cheng Zhao
738cbd4080 💄 Fix protocol specs. 2013-08-30 13:04:02 +08:00
Cheng Zhao
11221979e5 Disable overriding built-in protocols with protocol.registerProtocol. 2013-08-30 12:51:15 +08:00
Cheng Zhao
72c604f741 Custom the implementation of url request job factory.
It's needed for interceptor API.
2013-08-30 12:04:51 +08:00
Cheng Zhao
04910b8391 Skelecton for interceptor APIs. 2013-08-30 10:15:15 +08:00
Paul Betts
132eb09d96 Merge pull request #71 from atom/78-chars-or-death
Indent all the files to 78-characters so that doc diffs are usable
2013-08-29 10:41:39 -07:00
Paul Betts
38b37f2520 Indent all the files to 78-characters so that doc diffs are usable 2013-08-29 16:37:51 +02:00
Cheng Zhao
4bdd1b88ad Wrap passed 'url' and 'referrer' in an 'request' object. 2013-08-29 21:12:48 +08:00
Cheng Zhao
abd3e86fb1 💄 2013-08-29 21:06:22 +08:00
Cheng Zhao
c2fd43c3e8 Add spec for protocol.isHandledProtocol. 2013-08-29 20:57:09 +08:00
Cheng Zhao
261f50701a Add 'registered' and 'unregistered' events for protocol module.
This is only used for writing specs.
2013-08-29 20:56:25 +08:00
Cheng Zhao
bc4201f911 Make protocol module an EventEmitter. 2013-08-29 20:38:04 +08:00
Cheng Zhao
6915f020d9 Add protocol.isHandledProtocol API. 2013-08-29 20:22:52 +08:00
Cheng Zhao
f7de0e8d38 Disable spec for window.close() since it would not be fixed for a while. 2013-08-29 18:03:56 +08:00
Cheng Zhao
efd2bbbede 💄 Avoid duplicate temporary file names. 2013-08-29 18:03:14 +08:00
Cheng Zhao
b1f30c1eb6 💄 for the beforeunload handler.
Returning text in beforeunload handler should prevent the close instead
of allow the close.
2013-08-29 15:36:09 +08:00
Cheng Zhao
34e1800716 Add 'loading-state-changed' event for BrowserWindow.
It's required for testing the BrowserWindow class.
2013-08-29 11:47:07 +08:00
Cheng Zhao
e00d3d4b37 Add spec for #70. 2013-08-29 11:40:07 +08:00
Cheng Zhao
19aa2b7979 Update apm. 2013-08-29 11:30:56 +08:00
Cheng Zhao
dbdf2d8d54 Add OnLoadingStateChanged for NativeWindowObserver. 2013-08-29 11:19:34 +08:00
Cheng Zhao
3be4a01963 Dump v0.4.1.
Should make this automatic in future.
2013-08-29 11:19:34 +08:00
Paul Betts
d0ab7e2c1e Merge pull request #69 from atom/apm-vs2012
Version bump atom/apm to fix script/bootstrap.py on Win32
2013-08-28 04:29:20 -07:00
Paul Betts
566b8136c9 Force pushery? 2013-08-28 13:27:49 +02:00
Paul Betts
c8150e570b Version bump atom/apm to fix script/bootstrap.py on Win32 2013-08-28 13:14:54 +02:00
Cheng Zhao
d2b4b761ba Update node: fix #66. 2013-08-27 18:39:32 +08:00
Cheng Zhao
018a48770a Add spec for heap snapshot crash (#66). 2013-08-27 17:47:44 +08:00
Cheng Zhao
799d9ada7d Make sure referrer is sent to the protocol handler. 2013-08-27 11:37:06 +08:00
Cheng Zhao
15ba32b489 Only redirect output to browser when running in CI. 2013-08-25 20:54:15 +08:00
Cheng Zhao
0f6617ec26 Make sure protocol module's specs do not pollute main.js. 2013-08-25 20:45:34 +08:00
Cheng Zhao
9e16e41bb3 Enable creating object from remote object's member. 2013-08-25 20:44:52 +08:00
Cheng Zhao
c86acc4cd7 Add remote.createFunctionWithReturnValue API. 2013-08-25 17:22:36 +08:00
Cheng Zhao
d88676bf65 💄 2013-08-25 16:49:54 +08:00
Cheng Zhao
d56a7d75de Use jQuery instead of zepto.
zepto always fails when requesting custom protocols, 💩.
2013-08-25 16:23:40 +08:00
Cheng Zhao
244d7eaf17 Allow returning file for custom protocol. 2013-08-25 16:06:29 +08:00
Cheng Zhao
1ed77371c0 Enable customing mime type and charset when returning reqeust string job. 2013-08-25 15:07:07 +08:00
Cheng Zhao
912bac698c Allow returning string in custom protocols. 2013-08-25 12:36:06 +08:00
Cheng Zhao
8464fb4f64 Pass weak pointers when posting tasks to other threads. 2013-08-24 20:32:12 +08:00
Cheng Zhao
0b01e49cd3 💄 2013-08-24 20:21:46 +08:00
Cheng Zhao
8cd7ccdc0d Call the JS handler to get the type of job. 2013-08-24 20:18:12 +08:00
Cheng Zhao
214df5ef69 Use zepto in the specs. 2013-08-24 20:15:31 +08:00
Cheng Zhao
b7c9f8ba1c The request should go through UI thread. 2013-08-24 19:46:38 +08:00
Cheng Zhao
f63661256f Return AdapterRequestJob when creating job. 2013-08-24 19:33:23 +08:00
Cheng Zhao
33279b1a2f Actually set and remove protocol handlers in IO thread.. 2013-08-24 17:59:34 +08:00
Cheng Zhao
96c173217f Remember registered schemes. 2013-08-24 16:38:19 +08:00
Cheng Zhao
b9cbfb8103 Skelecton for the protocol module. 2013-08-24 15:26:10 +08:00
Kevin Sawicki
fd299cb3fe Upgrade apm for more logging on errors 2013-08-21 10:47:48 -07:00
Cheng Zhao
c87956ef4f Make sure cibuild calls all necessary scripts. 2013-08-21 21:41:34 +08:00
Cheng Zhao
833190e8fa 💄 for cpplint. 2013-08-21 21:36:16 +08:00
Cheng Zhao
7cc1589097 Abort when there is a error in cpplint. 2013-08-21 21:35:41 +08:00
Cheng Zhao
e8ecbec3a7 💄 2013-08-21 21:24:18 +08:00
Cheng Zhao
4914dd67b6 Also upload node's headers to S3. 2013-08-21 12:16:40 +08:00
Cheng Zhao
1e895bdf76 Create the tarball containing node's headers. 2013-08-21 12:09:26 +08:00
Cheng Zhao
5b74dff8f1 Generate the distribution file under 'dist' directory. 2013-08-21 11:57:35 +08:00
Cheng Zhao
5b862fdf60 Also copy gyp files when copying headers. 2013-08-21 11:52:13 +08:00
Cheng Zhao
b411657b76 Hide window when running in CI. 2013-08-21 11:41:08 +08:00
Cheng Zhao
d160da7752 Add script to run specs in CI, fixes #61. 2013-08-21 11:41:08 +08:00
Cheng Zhao
0cd3f3cc40 No need to open video when testing webkitGetUserMedia.
The purpose of this test is to test whether the callback can be called.
2013-08-21 11:41:08 +08:00
Cheng Zhao
9105914b9e Merge pull request #58 from atom/windows-bootstrap
Fix invocation of npm on non-Cygwin
2013-08-20 10:14:06 -07:00
Kevin Sawicki
dea52ae767 Upgrade apm for output fixes 2013-08-20 09:41:10 -07:00
Kevin Sawicki
79d6e88d99 Update link to docs 2013-08-20 09:40:40 -07:00
Kevin Sawicki
f63cf3f283 Update apm URL to new location in atom org. 2013-08-20 09:30:05 -07:00
Paul Betts
22c8b55cb9 Fix invocation of npm on non-Cygwin 2013-08-20 16:37:02 +02:00
98 changed files with 2949 additions and 311 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,4 @@
.DS_Store
atom-shell.zip
version
/build/
/dist/
/frameworks/

2
.gitmodules vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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
],
}

View 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)

View 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_

View File

@@ -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");
}

View File

@@ -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;

View 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

View File

@@ -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 = {}

View File

@@ -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

View File

@@ -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,

View File

@@ -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);
};

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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();

View File

@@ -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);
};

View File

@@ -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) {

View File

@@ -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) {}

View File

@@ -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

View File

@@ -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_;

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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 */)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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
View 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_

View File

@@ -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)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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.

View File

@@ -1,4 +1,4 @@
## Synopsis
# crash-reporter
An example of automatically submitting crash reporters to remote server:

View File

@@ -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.

View 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.

View File

@@ -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', ...);
```
```

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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.

View 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.

View File

@@ -1,4 +1,4 @@
## Synopsis
# clipboard
An example of writing a string to clipboard:

View File

@@ -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.

View File

@@ -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.

View 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.

View File

@@ -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
```
```

View File

@@ -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.

View File

@@ -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

View File

@@ -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
```
```

View 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.

View File

@@ -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).

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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,

View File

@@ -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():

View File

@@ -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
View 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
View File

@@ -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__':

View File

@@ -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__':

View File

@@ -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:

View File

@@ -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__':

View File

@@ -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())

View File

@@ -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
View 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
View 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')

View 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>

View 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>

View 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>

View 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>

View 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
View 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
View 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
View 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
View 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);
});

View File

@@ -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
View File

@@ -0,0 +1,3 @@
exports.print = function(obj) {
return obj.constructor.name;
}

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -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();
});

View File

@@ -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
View File

@@ -0,0 +1,4 @@
describe 'debugger', ->
describe 'heap snapshot', ->
it 'should not crash', ->
process.atomBinding('v8_util').takeHeapSnapshot()

View File

@@ -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

Submodule vendor/apm updated: cbf46094b6...2c9da12d10

2
vendor/node vendored