mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
339 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30b35644f6 | ||
|
|
a26cbf640c | ||
|
|
367590a32f | ||
|
|
77cf30f758 | ||
|
|
0fe829cff7 | ||
|
|
8348910483 | ||
|
|
8309e38ce9 | ||
|
|
15ce59be64 | ||
|
|
93f268cbd1 | ||
|
|
3b9fb6f9e6 | ||
|
|
516b01e398 | ||
|
|
7d54bb54cd | ||
|
|
4305e98fcb | ||
|
|
05aeceeb52 | ||
|
|
d195cc3796 | ||
|
|
f820b0db83 | ||
|
|
30ff18ac64 | ||
|
|
223b440bc8 | ||
|
|
3c9e08faf1 | ||
|
|
1da13625d1 | ||
|
|
e6fbb0adfa | ||
|
|
172fc91768 | ||
|
|
e9d00b1fc0 | ||
|
|
c4df96df67 | ||
|
|
a6bcc5d110 | ||
|
|
dc0151099c | ||
|
|
3ca941a949 | ||
|
|
ab96150016 | ||
|
|
f32a486441 | ||
|
|
3a5949d66a | ||
|
|
1ce85a69b9 | ||
|
|
8011578e8c | ||
|
|
4ab6846123 | ||
|
|
b01d677086 | ||
|
|
cbf11f38ca | ||
|
|
864449db89 | ||
|
|
2c7717d98e | ||
|
|
f87edba7fb | ||
|
|
d0587a9c52 | ||
|
|
205b2455f2 | ||
|
|
ab8e6650bd | ||
|
|
4c43be2148 | ||
|
|
2af7c37d13 | ||
|
|
b461969114 | ||
|
|
a34618866f | ||
|
|
d8c8214f33 | ||
|
|
a83aee90e5 | ||
|
|
059d97e1aa | ||
|
|
58127e3d52 | ||
|
|
253a4f226f | ||
|
|
93cdc91f1a | ||
|
|
e1e58b642e | ||
|
|
7761815339 | ||
|
|
42493a7ed3 | ||
|
|
45798d1933 | ||
|
|
cc560a3900 | ||
|
|
8239f69139 | ||
|
|
c33e8de676 | ||
|
|
c41de501cb | ||
|
|
010cc3276a | ||
|
|
239bfe970c | ||
|
|
d704b3f7ba | ||
|
|
ae7e96f181 | ||
|
|
5372b6cd91 | ||
|
|
d6f2b7c982 | ||
|
|
6ab236f9c0 | ||
|
|
7229d2462a | ||
|
|
09966aec38 | ||
|
|
85df3157c7 | ||
|
|
31624995bc | ||
|
|
8cabe0f008 | ||
|
|
d938dd68b0 | ||
|
|
000af78d41 | ||
|
|
e071e4f310 | ||
|
|
ff41b4a267 | ||
|
|
2fcd3ce7cd | ||
|
|
85991d312a | ||
|
|
3182504969 | ||
|
|
ef51e4e108 | ||
|
|
9b28b0e943 | ||
|
|
984462be44 | ||
|
|
d1051b55cc | ||
|
|
2d8e5e3a16 | ||
|
|
ca77c95c6d | ||
|
|
0e3a3d0748 | ||
|
|
5f195a789a | ||
|
|
360266ba5b | ||
|
|
ea9b0cfed0 | ||
|
|
985131d25e | ||
|
|
3a60ab386c | ||
|
|
df5bad3f89 | ||
|
|
072ab0ddea | ||
|
|
d186dceb4b | ||
|
|
e96e674201 | ||
|
|
d492ff45d5 | ||
|
|
63ae73b281 | ||
|
|
a230a89de4 | ||
|
|
b039741c6b | ||
|
|
b614c484dc | ||
|
|
980bc7bf32 | ||
|
|
ed34e33fbf | ||
|
|
b9d62d2cab | ||
|
|
1b8200ea34 | ||
|
|
a3b6f69772 | ||
|
|
21f5e27a5e | ||
|
|
859d9763e9 | ||
|
|
4b02a8ead0 | ||
|
|
bbf622187c | ||
|
|
a98d576b48 | ||
|
|
56e8ddbdaf | ||
|
|
000625e23f | ||
|
|
7192a8517b | ||
|
|
4cf5982e92 | ||
|
|
239baa3e9a | ||
|
|
0e2323c9c8 | ||
|
|
bd2252ea55 | ||
|
|
fda480d1aa | ||
|
|
3a55647ae3 | ||
|
|
b46d8ec91b | ||
|
|
e9b3a51703 | ||
|
|
669b815758 | ||
|
|
b5cf352312 | ||
|
|
b399455808 | ||
|
|
16b4b58de1 | ||
|
|
b1f679ff6d | ||
|
|
70bcb0ac5a | ||
|
|
ccce284a5b | ||
|
|
4f4456bde8 | ||
|
|
45ddbb6d67 | ||
|
|
fbfbf2158a | ||
|
|
fab34b400e | ||
|
|
c14dd71df1 | ||
|
|
8a00406862 | ||
|
|
2e8a2c3a7f | ||
|
|
2fba39d719 | ||
|
|
40557083f0 | ||
|
|
ee2d2cc532 | ||
|
|
cac8466fb1 | ||
|
|
5bd2d4b08d | ||
|
|
8140adb6bc | ||
|
|
5d249e2a94 | ||
|
|
54b5369215 | ||
|
|
f1ef24b359 | ||
|
|
9a82c5a1c0 | ||
|
|
a874abd5a8 | ||
|
|
65325dc63f | ||
|
|
af3def91f2 | ||
|
|
b014eea8d3 | ||
|
|
517dce443b | ||
|
|
c719b252cb | ||
|
|
fb3ab9e1f1 | ||
|
|
01e1d0dfef | ||
|
|
d3186a9665 | ||
|
|
fd5c249cb8 | ||
|
|
0a79204abb | ||
|
|
c3049e1e99 | ||
|
|
2a554cb138 | ||
|
|
140bc4effc | ||
|
|
b9c9da3f4e | ||
|
|
b4aaaed843 | ||
|
|
fc42e97144 | ||
|
|
7842a657d0 | ||
|
|
2907c4f068 | ||
|
|
307add22b2 | ||
|
|
def1477466 | ||
|
|
40a93f1374 | ||
|
|
4e183df997 | ||
|
|
6f1fe2e478 | ||
|
|
6ecacd18be | ||
|
|
3b62ce7fa6 | ||
|
|
775e475d5c | ||
|
|
5a83f94524 | ||
|
|
25afcf2673 | ||
|
|
f42d283fcd | ||
|
|
83b36ce40b | ||
|
|
712f11a9a3 | ||
|
|
b132c0dde8 | ||
|
|
d779f8d898 | ||
|
|
85449e953e | ||
|
|
0fe539f2c3 | ||
|
|
ae20292322 | ||
|
|
f830af49c2 | ||
|
|
503cd04c75 | ||
|
|
da120d8874 | ||
|
|
2869869a99 | ||
|
|
1d3e7dc6e4 | ||
|
|
b87599a6a2 | ||
|
|
5fc671f522 | ||
|
|
aab2821122 | ||
|
|
aab1568682 | ||
|
|
02f055b784 | ||
|
|
756f8039e5 | ||
|
|
0a3331a07c | ||
|
|
4db4d409a7 | ||
|
|
ad3376b9dc | ||
|
|
caf7cf5582 | ||
|
|
b9789ef191 | ||
|
|
79f1029d59 | ||
|
|
34030d7b2b | ||
|
|
2b95aeba3c | ||
|
|
2833db78ad | ||
|
|
c2d473cf65 | ||
|
|
1b0da44f38 | ||
|
|
741d148776 | ||
|
|
b119704ea9 | ||
|
|
64f310168c | ||
|
|
b75dccb0be | ||
|
|
63278aeec2 | ||
|
|
d6e2db22b3 | ||
|
|
7e7175b6a5 | ||
|
|
0937144849 | ||
|
|
4b0119f996 | ||
|
|
7586078d0a | ||
|
|
187cb54c25 | ||
|
|
233e2d7288 | ||
|
|
9ccc7ee30d | ||
|
|
63c646242a | ||
|
|
60d44b3b04 | ||
|
|
c10c419f1d | ||
|
|
3875b30fcf | ||
|
|
a5cc105311 | ||
|
|
39225ac5ec | ||
|
|
f93d890874 | ||
|
|
2598b00b41 | ||
|
|
cda88db77c | ||
|
|
003de0debe | ||
|
|
d4b8c65017 | ||
|
|
572f53dc5e | ||
|
|
5a4a8e1fa7 | ||
|
|
047975609d | ||
|
|
2be7f6c693 | ||
|
|
ae2893ceaa | ||
|
|
5412ecdcc1 | ||
|
|
ab8908a4af | ||
|
|
1779701445 | ||
|
|
3a0fb42737 | ||
|
|
9103253f62 | ||
|
|
f31289c735 | ||
|
|
030d2a843c | ||
|
|
990dc30e8d | ||
|
|
f4af744519 | ||
|
|
ad9c254730 | ||
|
|
26350f4ccb | ||
|
|
f93b4e76cb | ||
|
|
10e4698baa | ||
|
|
88725d4fd7 | ||
|
|
bc699a9dbe | ||
|
|
918825cb58 | ||
|
|
463bc8738a | ||
|
|
8b38bbcf18 | ||
|
|
9bfc8c9bd0 | ||
|
|
edbf1994dd | ||
|
|
b373fbc552 | ||
|
|
a82ecfda06 | ||
|
|
489539d62e | ||
|
|
884a8a753f | ||
|
|
8ae836d82d | ||
|
|
44b8343585 | ||
|
|
af02739c4e | ||
|
|
118924ba7a | ||
|
|
350c572a8c | ||
|
|
55dfddba77 | ||
|
|
5b7d1a9890 | ||
|
|
8433d94cac | ||
|
|
5567baf335 | ||
|
|
d9d821cea5 | ||
|
|
640ad65cc2 | ||
|
|
b547a38b3f | ||
|
|
78aff6a39f | ||
|
|
ab73f4c94a | ||
|
|
ed63441688 | ||
|
|
61af98cf94 | ||
|
|
dd902c61fa | ||
|
|
088a39ea9d | ||
|
|
ddfe4809e7 | ||
|
|
714e544b51 | ||
|
|
1f9691ae13 | ||
|
|
403870a27e | ||
|
|
1fd1ae9694 | ||
|
|
630cd091a0 | ||
|
|
57da8908b2 | ||
|
|
8832d7b77b | ||
|
|
c2f6f64e6a | ||
|
|
d674db6606 | ||
|
|
eb63e6b9a7 | ||
|
|
554575b029 | ||
|
|
6d850ea61f | ||
|
|
1242fe04a8 | ||
|
|
17be978ced | ||
|
|
7333e403b7 | ||
|
|
4702b3e0c7 | ||
|
|
c7a89b6e29 | ||
|
|
9e82334b67 | ||
|
|
192fa4c86b | ||
|
|
34c9279fc5 | ||
|
|
a1da3c278b | ||
|
|
b25435c465 | ||
|
|
26bd97d6cf | ||
|
|
edcd34d4ce | ||
|
|
e3d3cd1198 | ||
|
|
440c588a33 | ||
|
|
91ca84d106 | ||
|
|
fe45a4ac98 | ||
|
|
018ae5189b | ||
|
|
8557cd223c | ||
|
|
33b7b774c1 | ||
|
|
1944472c2f | ||
|
|
54e1d7b3bf | ||
|
|
76a34a27ea | ||
|
|
630819e1d6 | ||
|
|
12f5e5072d | ||
|
|
1de9cfd4eb | ||
|
|
4f1be903c4 | ||
|
|
20a92d7d1e | ||
|
|
7bec597ea0 | ||
|
|
373bed33c5 | ||
|
|
595a3469d9 | ||
|
|
1ee565ab81 | ||
|
|
fee301e768 | ||
|
|
0f2c35e2b8 | ||
|
|
adcdf123af | ||
|
|
da52017d82 | ||
|
|
29939dc0b7 | ||
|
|
803b06b7de | ||
|
|
ae5c6add11 | ||
|
|
f1d388fb36 | ||
|
|
be50ca2653 | ||
|
|
8b07918650 | ||
|
|
56d1235bbd | ||
|
|
c5dee6a3d8 | ||
|
|
a4636ba18c | ||
|
|
dfce803045 | ||
|
|
c4c145ef6a | ||
|
|
22c455175a | ||
|
|
f1edd5f26f | ||
|
|
6728d9291d | ||
|
|
943e46f3bd | ||
|
|
224955d6ac | ||
|
|
3f0c007578 |
@@ -70,7 +70,8 @@ API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝
|
||||
- Atom 포럼의 [`electron`](http://discuss.atom.io/c/electron) 카테고리
|
||||
- Freenode 채팅의 `#atom-shell` 채널
|
||||
- Slack의 [`Atom`](http://atom-slack.herokuapp.com/) 채널
|
||||
- [`electron-br`](https://electron-br.slack.com) *(브라질 포르투갈어)*
|
||||
- [`electron-br`](https://electron-br.slack.com) *(브라질 포르투갈어)* 커뮤니티
|
||||
- [`electron-kr`](http://www.meetup.com/electron-kr/) *(한국어)* 커뮤니티
|
||||
|
||||
[awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트에
|
||||
커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기
|
||||
|
||||
@@ -69,6 +69,7 @@ forums
|
||||
- `#atom-shell` channel on Freenode
|
||||
- [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack
|
||||
- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
|
||||
- [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)*
|
||||
|
||||
Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron)
|
||||
for a community maintained list of useful example apps, tools and resources.
|
||||
|
||||
26
atom.gyp
26
atom.gyp
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '0.36.3',
|
||||
'version%': '0.36.6',
|
||||
},
|
||||
'includes': [
|
||||
'filenames.gypi',
|
||||
@@ -28,7 +28,7 @@
|
||||
'target_name': '<(project_name)',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'compile_coffee',
|
||||
'js2asar',
|
||||
'<(project_name)_lib',
|
||||
],
|
||||
'sources': [
|
||||
@@ -221,7 +221,7 @@
|
||||
'target_name': '<(project_name)_lib',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'atom_coffee2c',
|
||||
'atom_js2c',
|
||||
'vendor/brightray/brightray.gyp:brightray',
|
||||
'vendor/node/node.gyp:node',
|
||||
],
|
||||
@@ -351,11 +351,11 @@
|
||||
],
|
||||
}, # target <(product_name)_lib
|
||||
{
|
||||
'target_name': 'compile_coffee',
|
||||
'target_name': 'js2asar',
|
||||
'type': 'none',
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'compile_coffee',
|
||||
'action_name': 'js2asar',
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
@@ -366,41 +366,41 @@
|
||||
],
|
||||
},
|
||||
'inputs': [
|
||||
'<@(coffee_sources)',
|
||||
'<@(js_sources)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(resources_path)/atom.asar',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/coffee2asar.py',
|
||||
'tools/js2asar.py',
|
||||
'<@(_outputs)',
|
||||
'<@(_inputs)',
|
||||
],
|
||||
}
|
||||
],
|
||||
}, # target compile_coffee
|
||||
}, # target js2asar
|
||||
{
|
||||
'target_name': 'atom_coffee2c',
|
||||
'target_name': 'atom_js2c',
|
||||
'type': 'none',
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'atom_coffee2c',
|
||||
'action_name': 'atom_js2c',
|
||||
'inputs': [
|
||||
'<@(coffee2c_sources)',
|
||||
'<@(js2c_sources)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/coffee2c.py',
|
||||
'tools/js2c.py',
|
||||
'<@(_outputs)',
|
||||
'<@(_inputs)',
|
||||
],
|
||||
}
|
||||
],
|
||||
}, # target atom_coffee2c
|
||||
}, # target atom_js2c
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
|
||||
@@ -37,7 +37,7 @@ bool UvTaskRunner::PostNonNestableDelayedTask(
|
||||
const tracked_objects::Location& from_here,
|
||||
const base::Closure& task,
|
||||
base::TimeDelta delay) {
|
||||
return PostDelayedTask(from_here, task, delay);;
|
||||
return PostDelayedTask(from_here, task, delay);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "atom/common/native_mate_converters/image_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
@@ -34,9 +35,11 @@
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#endif
|
||||
|
||||
using atom::Browser;
|
||||
@@ -318,6 +321,12 @@ std::string App::GetLocale() {
|
||||
return l10n_util::GetApplicationLocale("");
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool App::IsAeroGlassEnabled() {
|
||||
return ui::win::IsAeroGlassEnabled();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool App::MakeSingleInstance(
|
||||
const ProcessSingleton::NotificationCallback& callback) {
|
||||
if (process_singleton_.get())
|
||||
@@ -361,6 +370,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
|
||||
#if defined(OS_WIN)
|
||||
.SetMethod("setUserTasks",
|
||||
base::Bind(&Browser::SetUserTasks, browser))
|
||||
.SetMethod("isAeroGlassEnabled", &App::IsAeroGlassEnabled)
|
||||
#endif
|
||||
.SetMethod("setPath", &App::SetPath)
|
||||
.SetMethod("getPath", &App::GetPath)
|
||||
@@ -440,6 +450,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
dict.SetMethod("dockHide", base::Bind(&Browser::DockHide, browser));
|
||||
dict.SetMethod("dockShow", base::Bind(&Browser::DockShow, browser));
|
||||
dict.SetMethod("dockSetMenu", &DockSetMenu);
|
||||
dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ class App : public AtomBrowserClient::Delegate,
|
||||
const ProcessSingleton::NotificationCallback& callback);
|
||||
std::string GetLocale();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool IsAeroGlassEnabled();
|
||||
#endif
|
||||
|
||||
scoped_ptr<ProcessSingleton> process_singleton_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(App);
|
||||
|
||||
195
atom/browser/api/atom_api_debugger.cc
Normal file
195
atom/browser/api/atom_api_debugger.cc
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/api/atom_api_debugger.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/json/json_writer.h"
|
||||
#include "content/public/browser/devtools_agent_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
using content::DevToolsAgentHost;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
|
||||
// The wrapDebugger funtion which is implemented in JavaScript.
|
||||
using WrapDebuggerCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
WrapDebuggerCallback g_wrap_debugger;
|
||||
|
||||
} // namespace
|
||||
|
||||
Debugger::Debugger(content::WebContents* web_contents)
|
||||
: web_contents_(web_contents),
|
||||
previous_request_id_(0) {
|
||||
}
|
||||
|
||||
Debugger::~Debugger() {
|
||||
}
|
||||
|
||||
void Debugger::AgentHostClosed(DevToolsAgentHost* agent_host,
|
||||
bool replaced_with_another_client) {
|
||||
std::string detach_reason = "target closed";
|
||||
if (replaced_with_another_client)
|
||||
detach_reason = "replaced with devtools";
|
||||
Emit("detach", detach_reason);
|
||||
}
|
||||
|
||||
void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
|
||||
const std::string& message) {
|
||||
DCHECK(agent_host == agent_host_.get());
|
||||
|
||||
scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
|
||||
if (!parsed_message->IsType(base::Value::TYPE_DICTIONARY))
|
||||
return;
|
||||
|
||||
base::DictionaryValue* dict =
|
||||
static_cast<base::DictionaryValue*>(parsed_message.get());
|
||||
int id;
|
||||
if (!dict->GetInteger("id", &id)) {
|
||||
std::string method;
|
||||
if (!dict->GetString("method", &method))
|
||||
return;
|
||||
base::DictionaryValue* params_value = nullptr;
|
||||
base::DictionaryValue params;
|
||||
if (dict->GetDictionary("params", ¶ms_value))
|
||||
params.Swap(params_value);
|
||||
Emit("message", method, params);
|
||||
} else {
|
||||
auto send_command_callback = pending_requests_[id];
|
||||
pending_requests_.erase(id);
|
||||
if (send_command_callback.is_null())
|
||||
return;
|
||||
base::DictionaryValue* error_body = nullptr;
|
||||
base::DictionaryValue error;
|
||||
if (dict->GetDictionary("error", &error_body))
|
||||
error.Swap(error_body);
|
||||
|
||||
base::DictionaryValue* result_body = nullptr;
|
||||
base::DictionaryValue result;
|
||||
if (dict->GetDictionary("result", &result_body))
|
||||
result.Swap(result_body);
|
||||
send_command_callback.Run(error, result);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::Attach(mate::Arguments* args) {
|
||||
std::string protocol_version;
|
||||
args->GetNext(&protocol_version);
|
||||
|
||||
if (!protocol_version.empty() &&
|
||||
!DevToolsAgentHost::IsSupportedProtocolVersion(protocol_version)) {
|
||||
args->ThrowError("Requested protocol version is not supported");
|
||||
return;
|
||||
}
|
||||
agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents_);
|
||||
if (!agent_host_.get()) {
|
||||
args->ThrowError("No target available");
|
||||
return;
|
||||
}
|
||||
if (agent_host_->IsAttached()) {
|
||||
args->ThrowError("Another debugger is already attached to this target");
|
||||
return;
|
||||
}
|
||||
|
||||
agent_host_->AttachClient(this);
|
||||
}
|
||||
|
||||
bool Debugger::IsAttached() {
|
||||
return agent_host_.get() ? agent_host_->IsAttached() : false;
|
||||
}
|
||||
|
||||
void Debugger::Detach() {
|
||||
if (!agent_host_.get())
|
||||
return;
|
||||
agent_host_->DetachClient();
|
||||
AgentHostClosed(agent_host_.get(), false);
|
||||
agent_host_ = nullptr;
|
||||
}
|
||||
|
||||
void Debugger::SendCommand(mate::Arguments* args) {
|
||||
if (!agent_host_.get())
|
||||
return;
|
||||
|
||||
std::string method;
|
||||
if (!args->GetNext(&method)) {
|
||||
args->ThrowError();
|
||||
return;
|
||||
}
|
||||
base::DictionaryValue command_params;
|
||||
args->GetNext(&command_params);
|
||||
SendCommandCallback callback;
|
||||
args->GetNext(&callback);
|
||||
|
||||
base::DictionaryValue request;
|
||||
int request_id = ++previous_request_id_;
|
||||
pending_requests_[request_id] = callback;
|
||||
request.SetInteger("id", request_id);
|
||||
request.SetString("method", method);
|
||||
if (!command_params.empty())
|
||||
request.Set("params", command_params.DeepCopy());
|
||||
|
||||
std::string json_args;
|
||||
base::JSONWriter::Write(request, &json_args);
|
||||
agent_host_->DispatchProtocolMessage(json_args);
|
||||
}
|
||||
|
||||
// static
|
||||
mate::Handle<Debugger> Debugger::Create(
|
||||
v8::Isolate* isolate,
|
||||
content::WebContents* web_contents) {
|
||||
auto handle = mate::CreateHandle(isolate, new Debugger(web_contents));
|
||||
g_wrap_debugger.Run(handle.ToV8());
|
||||
return handle;
|
||||
}
|
||||
|
||||
// static
|
||||
void Debugger::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype) {
|
||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.SetMethod("attach", &Debugger::Attach)
|
||||
.SetMethod("isAttached", &Debugger::IsAttached)
|
||||
.SetMethod("detach", &Debugger::Detach)
|
||||
.SetMethod("sendCommand", &Debugger::SendCommand);
|
||||
}
|
||||
|
||||
void ClearWrapDebugger() {
|
||||
g_wrap_debugger.Reset();
|
||||
}
|
||||
|
||||
void SetWrapDebugger(const WrapDebuggerCallback& callback) {
|
||||
g_wrap_debugger = callback;
|
||||
|
||||
// Cleanup the wrapper on exit.
|
||||
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
|
||||
base::Bind(ClearWrapDebugger));
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("_setWrapDebugger", &atom::api::SetWrapDebugger);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_debugger, Initialize);
|
||||
75
atom/browser/api/atom_api_debugger.h
Normal file
75
atom/browser/api/atom_api_debugger.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/devtools_agent_host_client.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
namespace content {
|
||||
class DevToolsAgentHost;
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace mate {
|
||||
class Arguments;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class Debugger: public mate::TrackableObject<Debugger>,
|
||||
public content::DevToolsAgentHostClient {
|
||||
public:
|
||||
using SendCommandCallback =
|
||||
base::Callback<void(const base::DictionaryValue&,
|
||||
const base::DictionaryValue&)>;
|
||||
|
||||
static mate::Handle<Debugger> Create(
|
||||
v8::Isolate* isolate, content::WebContents* web_contents);
|
||||
|
||||
// mate::TrackableObject:
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
protected:
|
||||
explicit Debugger(content::WebContents* web_contents);
|
||||
~Debugger();
|
||||
|
||||
// content::DevToolsAgentHostClient:
|
||||
void AgentHostClosed(content::DevToolsAgentHost* agent_host,
|
||||
bool replaced_with_another_client) override;
|
||||
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
|
||||
const std::string& message) override;
|
||||
|
||||
private:
|
||||
using PendingRequestMap = std::map<int, SendCommandCallback>;
|
||||
|
||||
void Attach(mate::Arguments* args);
|
||||
bool IsAttached();
|
||||
void Detach();
|
||||
void SendCommand(mate::Arguments* args);
|
||||
|
||||
content::WebContents* web_contents_; // Weak Reference.
|
||||
scoped_refptr<content::DevToolsAgentHost> agent_host_;
|
||||
|
||||
PendingRequestMap pending_requests_;
|
||||
int previous_request_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Debugger);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_
|
||||
@@ -41,6 +41,7 @@ namespace {
|
||||
|
||||
void ShowMessageBox(int type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -54,12 +55,13 @@ void ShowMessageBox(int type,
|
||||
if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(),
|
||||
peek,
|
||||
&callback)) {
|
||||
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, cancel_id,
|
||||
options, title, message, detail, icon, callback);
|
||||
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons,
|
||||
default_id, cancel_id, options, title,
|
||||
message, detail, icon, callback);
|
||||
} else {
|
||||
int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type,
|
||||
buttons, cancel_id, options, title,
|
||||
message, detail, icon);
|
||||
buttons, default_id, cancel_id,
|
||||
options, title, message, detail, icon);
|
||||
args->Return(chosen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt)
|
||||
.SetMethod("isEnabledAt", &Menu::IsEnabledAt)
|
||||
.SetMethod("isVisibleAt", &Menu::IsVisibleAt)
|
||||
.SetMethod("_popup", &Menu::Popup)
|
||||
.SetMethod("_popupAt", &Menu::PopupAt);
|
||||
.SetMethod("popupAt", &Menu::PopupAt);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -51,8 +51,9 @@ class Menu : public mate::TrackableObject<Menu>,
|
||||
void ExecuteCommand(int command_id, int event_flags) override;
|
||||
void MenuWillShow(ui::SimpleMenuModel* source) override;
|
||||
|
||||
virtual void Popup(Window* window) = 0;
|
||||
virtual void PopupAt(Window* window, int x, int y) = 0;
|
||||
virtual void PopupAt(Window* window,
|
||||
int x = -1, int y = -1,
|
||||
int positioning_item = 0) = 0;
|
||||
|
||||
scoped_ptr<AtomMenuModel> model_;
|
||||
Menu* parent_;
|
||||
|
||||
@@ -19,8 +19,7 @@ class MenuMac : public Menu {
|
||||
protected:
|
||||
MenuMac();
|
||||
|
||||
void Popup(Window* window) override;
|
||||
void PopupAt(Window* window, int x, int y) override;
|
||||
void PopupAt(Window* window, int x, int y, int positioning_item = 0) override;
|
||||
|
||||
base::scoped_nsobject<AtomMenuController> menu_controller_;
|
||||
|
||||
|
||||
@@ -18,39 +18,7 @@ namespace api {
|
||||
MenuMac::MenuMac() {
|
||||
}
|
||||
|
||||
void MenuMac::Popup(Window* window) {
|
||||
NativeWindow* native_window = window->window();
|
||||
if (!native_window)
|
||||
return;
|
||||
content::WebContents* web_contents = native_window->web_contents();
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
NSWindow* nswindow = native_window->GetNativeWindow();
|
||||
base::scoped_nsobject<AtomMenuController> menu_controller(
|
||||
[[AtomMenuController alloc] initWithModel:model_.get()]);
|
||||
|
||||
// Fake out a context menu event.
|
||||
NSEvent* currentEvent = [NSApp currentEvent];
|
||||
NSPoint position = [nswindow mouseLocationOutsideOfEventStream];
|
||||
NSTimeInterval eventTime = [currentEvent timestamp];
|
||||
NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
|
||||
location:position
|
||||
modifierFlags:NSRightMouseDownMask
|
||||
timestamp:eventTime
|
||||
windowNumber:[nswindow windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
|
||||
// Show the menu.
|
||||
[NSMenu popUpContextMenu:[menu_controller menu]
|
||||
withEvent:clickEvent
|
||||
forView:web_contents->GetContentNativeView()];
|
||||
}
|
||||
|
||||
void MenuMac::PopupAt(Window* window, int x, int y) {
|
||||
void MenuMac::PopupAt(Window* window, int x, int y, int positioning_item) {
|
||||
NativeWindow* native_window = window->window();
|
||||
if (!native_window)
|
||||
return;
|
||||
@@ -63,10 +31,23 @@ void MenuMac::PopupAt(Window* window, int x, int y) {
|
||||
NSMenu* menu = [menu_controller menu];
|
||||
NSView* view = web_contents->GetContentNativeView();
|
||||
|
||||
// Which menu item to show.
|
||||
NSMenuItem* item = nil;
|
||||
if (positioning_item < [menu numberOfItems] && positioning_item >= 0)
|
||||
item = [menu itemAtIndex:positioning_item];
|
||||
|
||||
// (-1, -1) means showing on mouse location.
|
||||
NSPoint position;
|
||||
if (x == -1 || y == -1) {
|
||||
NSWindow* nswindow = native_window->GetNativeWindow();
|
||||
position = [view convertPoint:[nswindow mouseLocationOutsideOfEventStream]
|
||||
fromView:nil];
|
||||
} else {
|
||||
position = NSMakePoint(x, [view frame].size.height - y);
|
||||
}
|
||||
|
||||
// Show the menu.
|
||||
[menu popUpMenuPositioningItem:[menu itemAtIndex:0]
|
||||
atLocation:NSMakePoint(x, [view frame].size.height - y)
|
||||
inView:view];
|
||||
[menu popUpMenuPositioningItem:item atLocation:position inView:view];
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -16,11 +16,7 @@ namespace api {
|
||||
MenuViews::MenuViews() {
|
||||
}
|
||||
|
||||
void MenuViews::Popup(Window* window) {
|
||||
PopupAtPoint(window, gfx::Screen::GetNativeScreen()->GetCursorScreenPoint());
|
||||
}
|
||||
|
||||
void MenuViews::PopupAt(Window* window, int x, int y) {
|
||||
void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) {
|
||||
NativeWindow* native_window = static_cast<NativeWindow*>(window->window());
|
||||
if (!native_window)
|
||||
return;
|
||||
@@ -31,18 +27,23 @@ void MenuViews::PopupAt(Window* window, int x, int y) {
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
gfx::Point origin = view->GetViewBounds().origin();
|
||||
PopupAtPoint(window, gfx::Point(origin.x() + x, origin.y() + y));
|
||||
}
|
||||
// (-1, -1) means showing on mouse location.
|
||||
gfx::Point location;
|
||||
if (x == -1 || y == -1) {
|
||||
location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
|
||||
} else {
|
||||
gfx::Point origin = view->GetViewBounds().origin();
|
||||
location = gfx::Point(origin.x() + x, origin.y() + y);
|
||||
}
|
||||
|
||||
void MenuViews::PopupAtPoint(Window* window, const gfx::Point& point) {
|
||||
// Show the menu.
|
||||
views::MenuRunner menu_runner(
|
||||
model(),
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
static_cast<NativeWindowViews*>(window->window())->widget(),
|
||||
NULL,
|
||||
gfx::Rect(point, gfx::Size()),
|
||||
gfx::Rect(location, gfx::Size()),
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
}
|
||||
|
||||
@@ -17,12 +17,9 @@ class MenuViews : public Menu {
|
||||
MenuViews();
|
||||
|
||||
protected:
|
||||
void Popup(Window* window) override;
|
||||
void PopupAt(Window* window, int x, int y) override;
|
||||
void PopupAt(Window* window, int x, int y, int positioning_item = 0) override;
|
||||
|
||||
private:
|
||||
void PopupAtPoint(Window* window, const gfx::Point& point);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MenuViews);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/prefs/pref_service.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "brightray/browser/net/devtools_network_conditions.h"
|
||||
@@ -114,14 +115,25 @@ struct Converter<net::ProxyConfig> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
net::ProxyConfig* out) {
|
||||
std::string proxy;
|
||||
if (!ConvertFromV8(isolate, val, &proxy))
|
||||
std::string proxy_rules;
|
||||
GURL pac_url;
|
||||
mate::Dictionary options;
|
||||
// Fallback to previous API when passed String.
|
||||
// https://git.io/vuhjj
|
||||
if (ConvertFromV8(isolate, val, &proxy_rules)) {
|
||||
pac_url = GURL(proxy_rules); // Assume it is PAC script if it is URL.
|
||||
} else if (ConvertFromV8(isolate, val, &options)) {
|
||||
options.Get("pacScript", &pac_url);
|
||||
options.Get("proxyRules", &proxy_rules);
|
||||
} else {
|
||||
return false;
|
||||
auto pac_url = GURL(proxy);
|
||||
if (pac_url.is_valid()) {
|
||||
}
|
||||
|
||||
// pacScript takes precedence over proxyRules.
|
||||
if (!pac_url.is_empty() && pac_url.is_valid()) {
|
||||
out->set_pac_url(pac_url);
|
||||
} else {
|
||||
out->proxy_rules().ParseFromString(proxy);
|
||||
out->proxy_rules().ParseFromString(proxy_rules);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -193,7 +205,7 @@ class ResolveProxyHelper {
|
||||
};
|
||||
|
||||
// Runs the callback in UI thread.
|
||||
template <typename ...T>
|
||||
template<typename ...T>
|
||||
void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE, base::Bind(callback, result...));
|
||||
@@ -201,19 +213,35 @@ void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
|
||||
|
||||
// Callback of HttpCache::GetBackend.
|
||||
void OnGetBackend(disk_cache::Backend** backend_ptr,
|
||||
Session::CacheAction action,
|
||||
const net::CompletionCallback& callback,
|
||||
int result) {
|
||||
if (result != net::OK) {
|
||||
RunCallbackInUI(callback, result);
|
||||
} else if (backend_ptr && *backend_ptr) {
|
||||
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>, callback));
|
||||
if (action == Session::CacheAction::CLEAR) {
|
||||
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>,
|
||||
callback));
|
||||
} else if (action == Session::CacheAction::STATS) {
|
||||
base::StringPairs stats;
|
||||
(*backend_ptr)->GetStats(&stats);
|
||||
for (size_t i = 0; i < stats.size(); ++i) {
|
||||
if (stats[i].first == "Current size") {
|
||||
int current_size;
|
||||
base::StringToInt(stats[i].second, ¤t_size);
|
||||
RunCallbackInUI(callback, current_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RunCallbackInUI<int>(callback, net::ERR_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearHttpCacheInIO(
|
||||
void DoCacheActionInIO(
|
||||
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
|
||||
Session::CacheAction action,
|
||||
const net::CompletionCallback& callback) {
|
||||
auto request_context = context_getter->GetURLRequestContext();
|
||||
auto http_cache = request_context->http_transaction_factory()->GetCache();
|
||||
@@ -224,7 +252,7 @@ void ClearHttpCacheInIO(
|
||||
using BackendPtr = disk_cache::Backend*;
|
||||
BackendPtr* backend_ptr = new BackendPtr(nullptr);
|
||||
net::CompletionCallback on_get_backend =
|
||||
base::Bind(&OnGetBackend, base::Owned(backend_ptr), callback);
|
||||
base::Bind(&OnGetBackend, base::Owned(backend_ptr), action, callback);
|
||||
int rv = http_cache->GetBackend(backend_ptr, on_get_backend);
|
||||
if (rv != net::ERR_IO_PENDING)
|
||||
on_get_backend.Run(net::OK);
|
||||
@@ -276,10 +304,12 @@ void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
||||
new ResolveProxyHelper(browser_context(), url, callback);
|
||||
}
|
||||
|
||||
void Session::ClearCache(const net::CompletionCallback& callback) {
|
||||
template<Session::CacheAction action>
|
||||
void Session::DoCacheAction(const net::CompletionCallback& callback) {
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&ClearHttpCacheInIO,
|
||||
base::Bind(&DoCacheActionInIO,
|
||||
make_scoped_refptr(browser_context_->GetRequestContext()),
|
||||
action,
|
||||
callback));
|
||||
}
|
||||
|
||||
@@ -301,6 +331,12 @@ void Session::ClearStorageData(mate::Arguments* args) {
|
||||
base::Time(), base::Time::Max(), callback);
|
||||
}
|
||||
|
||||
void Session::FlushStorageData() {
|
||||
auto storage_partition =
|
||||
content::BrowserContext::GetStoragePartition(browser_context(), nullptr);
|
||||
storage_partition->Flush();
|
||||
}
|
||||
|
||||
void Session::SetProxy(const net::ProxyConfig& config,
|
||||
const base::Closure& callback) {
|
||||
auto getter = browser_context_->GetRequestContext();
|
||||
@@ -403,8 +439,10 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.MakeDestroyable()
|
||||
.SetMethod("resolveProxy", &Session::ResolveProxy)
|
||||
.SetMethod("clearCache", &Session::ClearCache)
|
||||
.SetMethod("getCacheSize", &Session::DoCacheAction<CacheAction::STATS>)
|
||||
.SetMethod("clearCache", &Session::DoCacheAction<CacheAction::CLEAR>)
|
||||
.SetMethod("clearStorageData", &Session::ClearStorageData)
|
||||
.SetMethod("flushStorageData", &Session::FlushStorageData)
|
||||
.SetMethod("setProxy", &Session::SetProxy)
|
||||
.SetMethod("setDownloadPath", &Session::SetDownloadPath)
|
||||
.SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
|
||||
|
||||
@@ -38,6 +38,11 @@ class Session: public mate::TrackableObject<Session>,
|
||||
public:
|
||||
using ResolveProxyCallback = base::Callback<void(std::string)>;
|
||||
|
||||
enum class CacheAction {
|
||||
CLEAR,
|
||||
STATS,
|
||||
};
|
||||
|
||||
// Gets or creates Session from the |browser_context|.
|
||||
static mate::Handle<Session> CreateFrom(
|
||||
v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
||||
@@ -62,8 +67,10 @@ class Session: public mate::TrackableObject<Session>,
|
||||
|
||||
private:
|
||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
void ClearCache(const net::CompletionCallback& callback);
|
||||
template<CacheAction action>
|
||||
void DoCacheAction(const net::CompletionCallback& callback);
|
||||
void ClearStorageData(mate::Arguments* args);
|
||||
void FlushStorageData();
|
||||
void SetProxy(const net::ProxyConfig& config, const base::Closure& callback);
|
||||
void SetDownloadPath(const base::FilePath& path);
|
||||
void EnableNetworkEmulation(const mate::Dictionary& options);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/api/atom_api_debugger.h"
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
#include "atom/browser/api/atom_api_window.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
@@ -33,7 +34,6 @@
|
||||
#include "chrome/browser/printing/print_view_manager_basic.h"
|
||||
#include "chrome/browser/printing/print_preview_message_handler.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/browser/browser_plugin_guest_manager.h"
|
||||
#include "content/public/browser/favicon_status.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/navigation_details.h"
|
||||
@@ -75,15 +75,6 @@ void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
|
||||
user_agent));
|
||||
}
|
||||
|
||||
bool NotifyZoomLevelChanged(
|
||||
double level, content::WebContents* guest_web_contents) {
|
||||
guest_web_contents->SendToAllFrames(
|
||||
new AtomViewMsg_SetZoomLevel(MSG_ROUTING_NONE, level));
|
||||
|
||||
// Return false to iterate over all guests.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mate {
|
||||
@@ -290,14 +281,17 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
}
|
||||
|
||||
WebContents::~WebContents() {
|
||||
if (type_ == WEB_VIEW && managed_web_contents()) {
|
||||
// When force destroying the "destroyed" event is not emitted.
|
||||
// The destroy() is called.
|
||||
if (managed_web_contents()) {
|
||||
// For webview we need to tell content module to do some cleanup work before
|
||||
// destroying it.
|
||||
if (type_ == WEB_VIEW)
|
||||
guest_delegate_->Destroy();
|
||||
|
||||
// The WebContentsDestroyed will not be called automatically because we
|
||||
// unsubscribe from webContents before destroying it. So we have to manually
|
||||
// call it here to make sure "destroyed" event is emitted.
|
||||
WebContentsDestroyed();
|
||||
|
||||
guest_delegate_->Destroy();
|
||||
|
||||
Observe(nullptr);
|
||||
DestroyWebContents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,18 +619,43 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
|
||||
OnRendererMessageSync)
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_ZoomLevelChanged, OnZoomLevelChanged)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
// There are three ways of destroying a webContents:
|
||||
// 1. call webContents.destory();
|
||||
// 2. garbage collection;
|
||||
// 3. user closes the window of webContents;
|
||||
// For webview only #1 will happen, for BrowserWindow both #1 and #3 may
|
||||
// happen. The #2 should never happen for webContents, because webview is
|
||||
// managed by GuestViewManager, and BrowserWindow's webContents is managed
|
||||
// by api::Window.
|
||||
// For #1, the destructor will do the cleanup work and we only need to make
|
||||
// sure "destroyed" event is emitted. For #3, the content::WebContents will
|
||||
// be destroyed on close, and WebContentsDestroyed would be called for it, so
|
||||
// we need to make sure the api::WebContents is also deleted.
|
||||
void WebContents::WebContentsDestroyed() {
|
||||
// The RenderViewDeleted was not called when the WebContents is destroyed.
|
||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
||||
Emit("destroyed");
|
||||
|
||||
// This event is only for internal use, which is emitted when WebContents is
|
||||
// being destroyed.
|
||||
Emit("will-destroy");
|
||||
|
||||
// Cleanup relationships with other parts.
|
||||
RemoveFromWeakMap();
|
||||
|
||||
// We can not call Destroy here because we need to call Emit first, but we
|
||||
// also do not want any method to be used, so just mark as destroyed here.
|
||||
MarkDestroyed();
|
||||
|
||||
Emit("destroyed");
|
||||
|
||||
// Destroy the native class in next tick.
|
||||
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
||||
}
|
||||
|
||||
void WebContents::NavigationEntryCommitted(
|
||||
@@ -748,11 +767,6 @@ bool WebContents::SavePage(const base::FilePath& full_file_path,
|
||||
return handler->Handle(full_file_path, save_type);
|
||||
}
|
||||
|
||||
void WebContents::ExecuteJavaScript(const base::string16& code,
|
||||
bool has_user_gesture) {
|
||||
Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture));
|
||||
}
|
||||
|
||||
void WebContents::OpenDevTools(mate::Arguments* args) {
|
||||
if (type_ == REMOTE)
|
||||
return;
|
||||
@@ -992,7 +1006,7 @@ void WebContents::SendInputEvent(v8::Isolate* isolate,
|
||||
return;
|
||||
}
|
||||
} else if (blink::WebInputEvent::isKeyboardEventType(type)) {
|
||||
content::NativeWebKeyboardEvent keyboard_event;;
|
||||
content::NativeWebKeyboardEvent keyboard_event;
|
||||
if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
|
||||
host->ForwardKeyboardEvent(keyboard_event);
|
||||
return;
|
||||
@@ -1063,6 +1077,14 @@ v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
|
||||
return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
|
||||
if (debugger_.IsEmpty()) {
|
||||
auto handle = atom::api::Debugger::Create(isolate, web_contents());
|
||||
debugger_.Reset(isolate, handle.ToV8());
|
||||
}
|
||||
return v8::Local<v8::Value>::New(isolate, debugger_);
|
||||
}
|
||||
|
||||
// static
|
||||
void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype) {
|
||||
@@ -1085,7 +1107,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("getUserAgent", &WebContents::GetUserAgent)
|
||||
.SetMethod("insertCSS", &WebContents::InsertCSS)
|
||||
.SetMethod("savePage", &WebContents::SavePage)
|
||||
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript)
|
||||
.SetMethod("openDevTools", &WebContents::OpenDevTools)
|
||||
.SetMethod("closeDevTools", &WebContents::CloseDevTools)
|
||||
.SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
|
||||
@@ -1132,7 +1153,8 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
|
||||
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
||||
.SetProperty("session", &WebContents::Session)
|
||||
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents);
|
||||
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
|
||||
.SetProperty("debugger", &WebContents::Debugger);
|
||||
}
|
||||
|
||||
AtomBrowserContext* WebContents::GetBrowserContext() const {
|
||||
@@ -1152,15 +1174,6 @@ void WebContents::OnRendererMessageSync(const base::string16& channel,
|
||||
EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
|
||||
}
|
||||
|
||||
void WebContents::OnZoomLevelChanged(double level) {
|
||||
auto manager = web_contents()->GetBrowserContext()->GetGuestManager();
|
||||
if (!manager)
|
||||
return;
|
||||
manager->ForEachGuest(web_contents(),
|
||||
base::Bind(&NotifyZoomLevelChanged,
|
||||
level));
|
||||
}
|
||||
|
||||
// static
|
||||
mate::Handle<WebContents> WebContents::CreateFrom(
|
||||
v8::Isolate* isolate, content::WebContents* web_contents) {
|
||||
|
||||
@@ -74,8 +74,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
bool SavePage(const base::FilePath& full_file_path,
|
||||
const content::SavePageType& save_type,
|
||||
const SavePageHandler::SavePageCallback& callback);
|
||||
void ExecuteJavaScript(const base::string16& code,
|
||||
bool has_user_gesture);
|
||||
void OpenDevTools(mate::Arguments* args);
|
||||
void CloseDevTools();
|
||||
bool IsDevToolsOpened();
|
||||
@@ -144,6 +142,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
// Properties.
|
||||
v8::Local<v8::Value> Session(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
||||
|
||||
// mate::TrackableObject:
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
@@ -265,12 +264,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
const base::ListValue& args,
|
||||
IPC::Message* message);
|
||||
|
||||
// Called when guests need to be notified of
|
||||
// embedders' zoom level change.
|
||||
void OnZoomLevelChanged(double level);
|
||||
|
||||
v8::Global<v8::Value> session_;
|
||||
v8::Global<v8::Value> devtools_web_contents_;
|
||||
v8::Global<v8::Value> debugger_;
|
||||
|
||||
scoped_ptr<WebViewGuestDelegate> guest_delegate_;
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ void TranslateOldOptions(v8::Isolate* isolate, v8::Local<v8::Object> options) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Converts binary data to Buffer.
|
||||
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
|
||||
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
|
||||
@@ -116,7 +115,6 @@ v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
|
||||
else
|
||||
return buffer.ToLocalChecked();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -229,6 +227,14 @@ void Window::OnWindowLeaveFullScreen() {
|
||||
Emit("leave-full-screen");
|
||||
}
|
||||
|
||||
void Window::OnWindowScrollTouchBegin() {
|
||||
Emit("scroll-touch-begin");
|
||||
}
|
||||
|
||||
void Window::OnWindowScrollTouchEnd() {
|
||||
Emit("scroll-touch-end");
|
||||
}
|
||||
|
||||
void Window::OnWindowEnterHtmlFullScreen() {
|
||||
Emit("enter-html-full-screen");
|
||||
}
|
||||
@@ -340,16 +346,20 @@ bool Window::IsFullscreen() {
|
||||
return window_->IsFullscreen();
|
||||
}
|
||||
|
||||
void Window::SetBounds(const gfx::Rect& bounds) {
|
||||
window_->SetBounds(bounds);
|
||||
void Window::SetBounds(const gfx::Rect& bounds, mate::Arguments* args) {
|
||||
bool animate = false;
|
||||
args->GetNext(&animate);
|
||||
window_->SetBounds(bounds, animate);
|
||||
}
|
||||
|
||||
gfx::Rect Window::GetBounds() {
|
||||
return window_->GetBounds();
|
||||
}
|
||||
|
||||
void Window::SetSize(int width, int height) {
|
||||
window_->SetSize(gfx::Size(width, height));
|
||||
void Window::SetSize(int width, int height, mate::Arguments* args) {
|
||||
bool animate = false;
|
||||
args->GetNext(&animate);
|
||||
window_->SetSize(gfx::Size(width, height), animate);
|
||||
}
|
||||
|
||||
std::vector<int> Window::GetSize() {
|
||||
@@ -360,8 +370,10 @@ std::vector<int> Window::GetSize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Window::SetContentSize(int width, int height) {
|
||||
window_->SetContentSize(gfx::Size(width, height));
|
||||
void Window::SetContentSize(int width, int height, mate::Arguments* args) {
|
||||
bool animate = false;
|
||||
args->GetNext(&animate);
|
||||
window_->SetContentSize(gfx::Size(width, height), animate);
|
||||
}
|
||||
|
||||
std::vector<int> Window::GetContentSize() {
|
||||
@@ -404,6 +416,46 @@ bool Window::IsResizable() {
|
||||
return window_->IsResizable();
|
||||
}
|
||||
|
||||
void Window::SetMovable(bool movable) {
|
||||
window_->SetMovable(movable);
|
||||
}
|
||||
|
||||
bool Window::IsMovable() {
|
||||
return window_->IsMovable();
|
||||
}
|
||||
|
||||
void Window::SetMinimizable(bool minimizable) {
|
||||
window_->SetMinimizable(minimizable);
|
||||
}
|
||||
|
||||
bool Window::IsMinimizable() {
|
||||
return window_->IsMinimizable();
|
||||
}
|
||||
|
||||
void Window::SetMaximizable(bool maximizable) {
|
||||
window_->SetMaximizable(maximizable);
|
||||
}
|
||||
|
||||
bool Window::IsMaximizable() {
|
||||
return window_->IsMaximizable();
|
||||
}
|
||||
|
||||
void Window::SetFullScreenable(bool fullscreenable) {
|
||||
window_->SetFullScreenable(fullscreenable);
|
||||
}
|
||||
|
||||
bool Window::IsFullScreenable() {
|
||||
return window_->IsFullScreenable();
|
||||
}
|
||||
|
||||
void Window::SetClosable(bool closable) {
|
||||
window_->SetClosable(closable);
|
||||
}
|
||||
|
||||
bool Window::IsClosable() {
|
||||
return window_->IsClosable();
|
||||
}
|
||||
|
||||
void Window::SetAlwaysOnTop(bool top) {
|
||||
window_->SetAlwaysOnTop(top);
|
||||
}
|
||||
@@ -416,8 +468,10 @@ void Window::Center() {
|
||||
window_->Center();
|
||||
}
|
||||
|
||||
void Window::SetPosition(int x, int y) {
|
||||
window_->SetPosition(gfx::Point(x, y));
|
||||
void Window::SetPosition(int x, int y, mate::Arguments* args) {
|
||||
bool animate = false;
|
||||
args->GetNext(&animate);
|
||||
window_->SetPosition(gfx::Point(x, y), animate);
|
||||
}
|
||||
|
||||
std::vector<int> Window::GetPosition() {
|
||||
@@ -456,6 +510,14 @@ void Window::SetBackgroundColor(const std::string& color_name) {
|
||||
window_->SetBackgroundColor(color_name);
|
||||
}
|
||||
|
||||
void Window::SetHasShadow(bool has_shadow) {
|
||||
window_->SetHasShadow(has_shadow);
|
||||
}
|
||||
|
||||
bool Window::HasShadow() {
|
||||
return window_->HasShadow();
|
||||
}
|
||||
|
||||
void Window::FocusOnWebView() {
|
||||
window_->FocusOnWebView();
|
||||
}
|
||||
@@ -594,6 +656,12 @@ void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
|
||||
window_->SetAspectRatio(aspect_ratio, extra_size);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Window::GetNativeWindowHandle() {
|
||||
gfx::AcceleratedWidget handle = window_->GetAcceleratedWidget();
|
||||
return ToBuffer(
|
||||
isolate(), static_cast<void*>(&handle), sizeof(gfx::AcceleratedWidget));
|
||||
}
|
||||
|
||||
void Window::SetVisibleOnAllWorkspaces(bool visible) {
|
||||
return window_->SetVisibleOnAllWorkspaces(visible);
|
||||
}
|
||||
@@ -634,6 +702,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("setFullScreen", &Window::SetFullScreen)
|
||||
.SetMethod("isFullScreen", &Window::IsFullscreen)
|
||||
.SetMethod("setAspectRatio", &Window::SetAspectRatio)
|
||||
.SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
|
||||
.SetMethod("getBounds", &Window::GetBounds)
|
||||
.SetMethod("setBounds", &Window::SetBounds)
|
||||
.SetMethod("getSize", &Window::GetSize)
|
||||
@@ -646,6 +715,16 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("getMaximumSize", &Window::GetMaximumSize)
|
||||
.SetMethod("setResizable", &Window::SetResizable)
|
||||
.SetMethod("isResizable", &Window::IsResizable)
|
||||
.SetMethod("setMovable", &Window::SetMovable)
|
||||
.SetMethod("isMovable", &Window::IsMovable)
|
||||
.SetMethod("setMinimizable", &Window::SetMinimizable)
|
||||
.SetMethod("isMinimizable", &Window::IsMinimizable)
|
||||
.SetMethod("setMaximizable", &Window::SetMaximizable)
|
||||
.SetMethod("isMaximizable", &Window::IsMaximizable)
|
||||
.SetMethod("setFullScreenable", &Window::SetFullScreenable)
|
||||
.SetMethod("isFullScreenable", &Window::IsFullScreenable)
|
||||
.SetMethod("setClosable", &Window::SetClosable)
|
||||
.SetMethod("isClosable", &Window::IsClosable)
|
||||
.SetMethod("setAlwaysOnTop", &Window::SetAlwaysOnTop)
|
||||
.SetMethod("isAlwaysOnTop", &Window::IsAlwaysOnTop)
|
||||
.SetMethod("center", &Window::Center)
|
||||
@@ -658,6 +737,8 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("setKiosk", &Window::SetKiosk)
|
||||
.SetMethod("isKiosk", &Window::IsKiosk)
|
||||
.SetMethod("setBackgroundColor", &Window::SetBackgroundColor)
|
||||
.SetMethod("setHasShadow", &Window::SetHasShadow)
|
||||
.SetMethod("hasShadow", &Window::HasShadow)
|
||||
.SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
|
||||
.SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename)
|
||||
.SetMethod("setDocumentEdited", &Window::SetDocumentEdited)
|
||||
|
||||
@@ -65,6 +65,8 @@ class Window : public mate::TrackableObject<Window>,
|
||||
void OnWindowResize() override;
|
||||
void OnWindowMove() override;
|
||||
void OnWindowMoved() override;
|
||||
void OnWindowScrollTouchBegin() override;
|
||||
void OnWindowScrollTouchEnd() override;
|
||||
void OnWindowEnterFullScreen() override;
|
||||
void OnWindowLeaveFullScreen() override;
|
||||
void OnWindowEnterHtmlFullScreen() override;
|
||||
@@ -94,11 +96,11 @@ class Window : public mate::TrackableObject<Window>,
|
||||
bool IsMinimized();
|
||||
void SetFullScreen(bool fullscreen);
|
||||
bool IsFullscreen();
|
||||
void SetBounds(const gfx::Rect& bounds);
|
||||
void SetBounds(const gfx::Rect& bounds, mate::Arguments* args);
|
||||
gfx::Rect GetBounds();
|
||||
void SetSize(int width, int height);
|
||||
void SetSize(int width, int height, mate::Arguments* args);
|
||||
std::vector<int> GetSize();
|
||||
void SetContentSize(int width, int height);
|
||||
void SetContentSize(int width, int height, mate::Arguments* args);
|
||||
std::vector<int> GetContentSize();
|
||||
void SetMinimumSize(int width, int height);
|
||||
std::vector<int> GetMinimumSize();
|
||||
@@ -106,10 +108,20 @@ class Window : public mate::TrackableObject<Window>,
|
||||
std::vector<int> GetMaximumSize();
|
||||
void SetResizable(bool resizable);
|
||||
bool IsResizable();
|
||||
void SetMovable(bool movable);
|
||||
bool IsMovable();
|
||||
void SetMinimizable(bool minimizable);
|
||||
bool IsMinimizable();
|
||||
void SetMaximizable(bool maximizable);
|
||||
bool IsMaximizable();
|
||||
void SetFullScreenable(bool fullscreenable);
|
||||
bool IsFullScreenable();
|
||||
void SetClosable(bool closable);
|
||||
bool IsClosable();
|
||||
void SetAlwaysOnTop(bool top);
|
||||
bool IsAlwaysOnTop();
|
||||
void Center();
|
||||
void SetPosition(int x, int y);
|
||||
void SetPosition(int x, int y, mate::Arguments* args);
|
||||
std::vector<int> GetPosition();
|
||||
void SetTitle(const std::string& title);
|
||||
std::string GetTitle();
|
||||
@@ -118,6 +130,8 @@ class Window : public mate::TrackableObject<Window>,
|
||||
void SetKiosk(bool kiosk);
|
||||
bool IsKiosk();
|
||||
void SetBackgroundColor(const std::string& color_name);
|
||||
void SetHasShadow(bool has_shadow);
|
||||
bool HasShadow();
|
||||
void FocusOnWebView();
|
||||
void BlurWebView();
|
||||
bool IsWebViewFocused();
|
||||
@@ -137,6 +151,7 @@ class Window : public mate::TrackableObject<Window>,
|
||||
void SetMenuBarVisibility(bool visible);
|
||||
bool IsMenuBarVisible();
|
||||
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
|
||||
v8::Local<v8::Value> GetNativeWindowHandle();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef base::Callback<void(v8::Local<v8::Value>,
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
{deprecate, session, Menu} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
bindings = process.atomBinding 'app'
|
||||
downloadItemBindings = process.atomBinding 'download_item'
|
||||
|
||||
app = bindings.app
|
||||
app.__proto__ = EventEmitter.prototype
|
||||
|
||||
app.setApplicationMenu = (menu) ->
|
||||
Menu.setApplicationMenu menu
|
||||
|
||||
app.getApplicationMenu = ->
|
||||
Menu.getApplicationMenu()
|
||||
|
||||
app.commandLine =
|
||||
appendSwitch: bindings.appendSwitch,
|
||||
appendArgument: bindings.appendArgument
|
||||
|
||||
if process.platform is 'darwin'
|
||||
app.dock =
|
||||
bounce: (type='informational') -> bindings.dockBounce type
|
||||
cancelBounce: bindings.dockCancelBounce
|
||||
setBadge: bindings.dockSetBadgeText
|
||||
getBadge: bindings.dockGetBadgeText
|
||||
hide: bindings.dockHide
|
||||
show: bindings.dockShow
|
||||
setMenu: bindings.dockSetMenu
|
||||
|
||||
appPath = null
|
||||
app.setAppPath = (path) ->
|
||||
appPath = path
|
||||
|
||||
app.getAppPath = ->
|
||||
appPath
|
||||
|
||||
# Routes the events to webContents.
|
||||
for name in ['login', 'certificate-error', 'select-client-certificate']
|
||||
do (name) ->
|
||||
app.on name, (event, webContents, args...) ->
|
||||
webContents.emit name, event, args...
|
||||
|
||||
# Deprecated.
|
||||
app.getHomeDir = deprecate 'app.getHomeDir', 'app.getPath', ->
|
||||
@getPath 'home'
|
||||
app.getDataPath = deprecate 'app.getDataPath', 'app.getPath', ->
|
||||
@getPath 'userData'
|
||||
app.setDataPath = deprecate 'app.setDataPath', 'app.setPath', (path) ->
|
||||
@setPath 'userData', path
|
||||
app.resolveProxy = deprecate 'app.resolveProxy', 'session.defaultSession.resolveProxy', (url, callback) ->
|
||||
session.defaultSession.resolveProxy url, callback
|
||||
deprecate.rename app, 'terminate', 'quit'
|
||||
deprecate.event app, 'finish-launching', 'ready', ->
|
||||
setImmediate => # give default app a chance to setup default menu.
|
||||
@emit 'finish-launching'
|
||||
deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) ->
|
||||
@emit 'activate-with-no-open-windows', event if not hasVisibleWindows
|
||||
deprecate.event app, 'select-certificate', 'select-client-certificate'
|
||||
|
||||
# Wrappers for native classes.
|
||||
wrapDownloadItem = (downloadItem) ->
|
||||
# downloadItem is an EventEmitter.
|
||||
downloadItem.__proto__ = EventEmitter.prototype
|
||||
# Deprecated.
|
||||
deprecate.property downloadItem, 'url', 'getURL'
|
||||
deprecate.property downloadItem, 'filename', 'getFilename'
|
||||
deprecate.property downloadItem, 'mimeType', 'getMimeType'
|
||||
deprecate.rename downloadItem, 'getUrl', 'getURL'
|
||||
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
|
||||
|
||||
# Only one App object pemitted.
|
||||
module.exports = app
|
||||
123
atom/browser/api/lib/app.js
Normal file
123
atom/browser/api/lib/app.js
Normal file
@@ -0,0 +1,123 @@
|
||||
const deprecate = require('electron').deprecate;
|
||||
const session = require('electron').session;
|
||||
const Menu = require('electron').Menu;
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
const bindings = process.atomBinding('app');
|
||||
const downloadItemBindings = process.atomBinding('download_item');
|
||||
const app = bindings.app;
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
app.__proto__ = EventEmitter.prototype;
|
||||
|
||||
app.setApplicationMenu = function(menu) {
|
||||
return Menu.setApplicationMenu(menu);
|
||||
};
|
||||
|
||||
app.getApplicationMenu = function() {
|
||||
return Menu.getApplicationMenu();
|
||||
};
|
||||
|
||||
app.commandLine = {
|
||||
appendSwitch: bindings.appendSwitch,
|
||||
appendArgument: bindings.appendArgument
|
||||
};
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock = {
|
||||
bounce: function(type) {
|
||||
if (type == null) {
|
||||
type = 'informational';
|
||||
}
|
||||
return bindings.dockBounce(type);
|
||||
},
|
||||
cancelBounce: bindings.dockCancelBounce,
|
||||
setBadge: bindings.dockSetBadgeText,
|
||||
getBadge: bindings.dockGetBadgeText,
|
||||
hide: bindings.dockHide,
|
||||
show: bindings.dockShow,
|
||||
setMenu: bindings.dockSetMenu,
|
||||
setIcon: bindings.dockSetIcon
|
||||
};
|
||||
}
|
||||
|
||||
var appPath = null;
|
||||
|
||||
app.setAppPath = function(path) {
|
||||
return appPath = path;
|
||||
};
|
||||
|
||||
app.getAppPath = function() {
|
||||
return appPath;
|
||||
};
|
||||
|
||||
// Routes the events to webContents.
|
||||
var ref1 = ['login', 'certificate-error', 'select-client-certificate'];
|
||||
var fn = function(name) {
|
||||
return app.on(name, function() {
|
||||
var args, event, webContents;
|
||||
event = arguments[0], webContents = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return webContents.emit.apply(webContents, [name, event].concat(slice.call(args)));
|
||||
});
|
||||
};
|
||||
var i, len;
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
fn(ref1[i]);
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
|
||||
app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function() {
|
||||
return this.getPath('home');
|
||||
});
|
||||
|
||||
app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function() {
|
||||
return this.getPath('userData');
|
||||
});
|
||||
|
||||
app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function(path) {
|
||||
return this.setPath('userData', path);
|
||||
});
|
||||
|
||||
app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function(url, callback) {
|
||||
return session.defaultSession.resolveProxy(url, callback);
|
||||
});
|
||||
|
||||
deprecate.rename(app, 'terminate', 'quit');
|
||||
|
||||
deprecate.event(app, 'finish-launching', 'ready', function() {
|
||||
|
||||
// give default app a chance to setup default menu.
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit('finish-launching');
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
deprecate.event(app, 'activate-with-no-open-windows', 'activate', function(event, hasVisibleWindows) {
|
||||
if (!hasVisibleWindows) {
|
||||
return this.emit('activate-with-no-open-windows', event);
|
||||
}
|
||||
});
|
||||
|
||||
deprecate.event(app, 'select-certificate', 'select-client-certificate');
|
||||
|
||||
// Wrappers for native classes.
|
||||
var wrapDownloadItem = function(downloadItem) {
|
||||
|
||||
// downloadItem is an EventEmitter.
|
||||
downloadItem.__proto__ = EventEmitter.prototype;
|
||||
|
||||
// Deprecated.
|
||||
deprecate.property(downloadItem, 'url', 'getURL');
|
||||
deprecate.property(downloadItem, 'filename', 'getFilename');
|
||||
deprecate.property(downloadItem, 'mimeType', 'getMimeType');
|
||||
return deprecate.rename(downloadItem, 'getUrl', 'getURL');
|
||||
};
|
||||
|
||||
downloadItemBindings._setWrapDownloadItem(wrapDownloadItem);
|
||||
|
||||
// Only one App object pemitted.
|
||||
module.exports = app;
|
||||
@@ -1,12 +0,0 @@
|
||||
{deprecate} = require 'electron'
|
||||
|
||||
autoUpdater =
|
||||
if process.platform is 'win32'
|
||||
require './auto-updater/auto-updater-win'
|
||||
else
|
||||
require './auto-updater/auto-updater-native'
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename autoUpdater, 'setFeedUrl', 'setFeedURL'
|
||||
|
||||
module.exports = autoUpdater
|
||||
7
atom/browser/api/lib/auto-updater.js
Normal file
7
atom/browser/api/lib/auto-updater.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const deprecate = require('electron').deprecate;
|
||||
const autoUpdater = process.platform === 'win32' ? require('./auto-updater/auto-updater-win') : require('./auto-updater/auto-updater-native');
|
||||
|
||||
// Deprecated.
|
||||
deprecate.rename(autoUpdater, 'setFeedUrl', 'setFeedURL');
|
||||
|
||||
module.exports = autoUpdater;
|
||||
@@ -1,6 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
{autoUpdater} = process.atomBinding 'auto_updater'
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = autoUpdater
|
||||
6
atom/browser/api/lib/auto-updater/auto-updater-native.js
Normal file
6
atom/browser/api/lib/auto-updater/auto-updater-native.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const autoUpdater = process.atomBinding('auto_updater').autoUpdater;
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = autoUpdater;
|
||||
@@ -1,42 +0,0 @@
|
||||
{app} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
url = require 'url'
|
||||
|
||||
squirrelUpdate = require './squirrel-update-win'
|
||||
|
||||
class AutoUpdater extends EventEmitter
|
||||
quitAndInstall: ->
|
||||
squirrelUpdate.processStart()
|
||||
app.quit()
|
||||
|
||||
setFeedURL: (updateURL) ->
|
||||
@updateURL = updateURL
|
||||
|
||||
checkForUpdates: ->
|
||||
return @emitError 'Update URL is not set' unless @updateURL
|
||||
return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported()
|
||||
|
||||
@emit 'checking-for-update'
|
||||
|
||||
squirrelUpdate.download @updateURL, (error, update) =>
|
||||
return @emitError error if error?
|
||||
return @emit 'update-not-available' unless update?
|
||||
|
||||
@emit 'update-available'
|
||||
|
||||
squirrelUpdate.update @updateURL, (error) =>
|
||||
return @emitError error if error?
|
||||
|
||||
{releaseNotes, version} = update
|
||||
# Following information is not available on Windows, so fake them.
|
||||
date = new Date
|
||||
url = @updateURL
|
||||
|
||||
@emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
|
||||
|
||||
# Private: Emit both error object and message, this is to keep compatibility
|
||||
# with Old APIs.
|
||||
emitError: (message) ->
|
||||
@emit 'error', new Error(message), message
|
||||
|
||||
module.exports = new AutoUpdater
|
||||
62
atom/browser/api/lib/auto-updater/auto-updater-win.js
Normal file
62
atom/browser/api/lib/auto-updater/auto-updater-win.js
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const app = require('electron').app;
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const squirrelUpdate = require('./squirrel-update-win');
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
quitAndInstall() {
|
||||
squirrelUpdate.processStart();
|
||||
return app.quit();
|
||||
}
|
||||
|
||||
setFeedURL(updateURL) {
|
||||
return this.updateURL = updateURL;
|
||||
}
|
||||
|
||||
checkForUpdates() {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set');
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel');
|
||||
}
|
||||
this.emit('checking-for-update');
|
||||
return squirrelUpdate.download(this.updateURL, (function(_this) {
|
||||
return function(error, update) {
|
||||
if (error != null) {
|
||||
return _this.emitError(error);
|
||||
}
|
||||
if (update == null) {
|
||||
return _this.emit('update-not-available');
|
||||
}
|
||||
_this.emit('update-available');
|
||||
return squirrelUpdate.update(_this.updateURL, function(error) {
|
||||
var date, releaseNotes, version;
|
||||
if (error != null) {
|
||||
return _this.emitError(error);
|
||||
}
|
||||
releaseNotes = update.releaseNotes, version = update.version;
|
||||
|
||||
// Following information is not available on Windows, so fake them.
|
||||
date = new Date;
|
||||
return _this.emit('update-downloaded', {}, releaseNotes, version, date, _this.updateURL, function() {
|
||||
return _this.quitAndInstall();
|
||||
});
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError(message) {
|
||||
return this.emit('error', new Error(message), message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AutoUpdater;
|
||||
@@ -1,67 +0,0 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
{spawn} = require 'child_process'
|
||||
|
||||
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
|
||||
updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
|
||||
exeName = path.basename process.execPath
|
||||
|
||||
# Spawn a command and invoke the callback when it completes with an error
|
||||
# and the output from standard out.
|
||||
spawnUpdate = (args, detached, callback) ->
|
||||
try
|
||||
spawnedProcess = spawn updateExe, args, {detached}
|
||||
catch error
|
||||
# Shouldn't happen, but still guard it.
|
||||
process.nextTick -> callback error
|
||||
return
|
||||
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
spawnedProcess.stdout.on 'data', (data) -> stdout += data
|
||||
spawnedProcess.stderr.on 'data', (data) -> stderr += data
|
||||
|
||||
errorEmitted = false
|
||||
spawnedProcess.on 'error', (error) ->
|
||||
errorEmitted = true
|
||||
callback error
|
||||
spawnedProcess.on 'exit', (code, signal) ->
|
||||
# We may have already emitted an error.
|
||||
return if errorEmitted
|
||||
|
||||
# Process terminated with error.
|
||||
if code isnt 0
|
||||
return callback "Command failed: #{signal ? code}\n#{stderr}"
|
||||
|
||||
# Success.
|
||||
callback null, stdout
|
||||
|
||||
# Start an instance of the installed app.
|
||||
exports.processStart = (callback) ->
|
||||
spawnUpdate ['--processStart', exeName], true, ->
|
||||
|
||||
# Download the releases specified by the URL and write new results to stdout.
|
||||
exports.download = (updateURL, callback) ->
|
||||
spawnUpdate ['--download', updateURL], false, (error, stdout) ->
|
||||
return callback(error) if error?
|
||||
|
||||
try
|
||||
# Last line of output is the JSON details about the releases
|
||||
json = stdout.trim().split('\n').pop()
|
||||
update = JSON.parse(json)?.releasesToApply?.pop?()
|
||||
catch
|
||||
return callback "Invalid result:\n#{stdout}"
|
||||
|
||||
callback null, update
|
||||
|
||||
# Update the application to the latest remote version specified by URL.
|
||||
exports.update = (updateURL, callback) ->
|
||||
spawnUpdate ['--update', updateURL], false, callback
|
||||
|
||||
# Is the Update.exe installed with the current application?
|
||||
exports.supported = ->
|
||||
try
|
||||
fs.accessSync updateExe, fs.R_OK
|
||||
return true
|
||||
catch
|
||||
return false
|
||||
98
atom/browser/api/lib/auto-updater/squirrel-update-win.js
Normal file
98
atom/browser/api/lib/auto-updater/squirrel-update-win.js
Normal file
@@ -0,0 +1,98 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
// i.e. my-app/app-0.1.13/
|
||||
const appFolder = path.dirname(process.execPath);
|
||||
|
||||
// i.e. my-app/Update.exe
|
||||
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
|
||||
|
||||
const exeName = path.basename(process.execPath);
|
||||
|
||||
// Spawn a command and invoke the callback when it completes with an error
|
||||
// and the output from standard out.
|
||||
var spawnUpdate = function(args, detached, callback) {
|
||||
var error, errorEmitted, spawnedProcess, stderr, stdout;
|
||||
try {
|
||||
spawnedProcess = spawn(updateExe, args, {
|
||||
detached: detached
|
||||
});
|
||||
} catch (error1) {
|
||||
error = error1;
|
||||
|
||||
// Shouldn't happen, but still guard it.
|
||||
process.nextTick(function() {
|
||||
return callback(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
stdout = '';
|
||||
stderr = '';
|
||||
spawnedProcess.stdout.on('data', function(data) {
|
||||
return stdout += data;
|
||||
});
|
||||
spawnedProcess.stderr.on('data', function(data) {
|
||||
return stderr += data;
|
||||
});
|
||||
errorEmitted = false;
|
||||
spawnedProcess.on('error', function(error) {
|
||||
errorEmitted = true;
|
||||
return callback(error);
|
||||
});
|
||||
return spawnedProcess.on('exit', function(code, signal) {
|
||||
|
||||
// We may have already emitted an error.
|
||||
if (errorEmitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process terminated with error.
|
||||
if (code !== 0) {
|
||||
return callback("Command failed: " + (signal != null ? signal : code) + "\n" + stderr);
|
||||
}
|
||||
|
||||
// Success.
|
||||
return callback(null, stdout);
|
||||
});
|
||||
};
|
||||
|
||||
// Start an instance of the installed app.
|
||||
exports.processStart = function() {
|
||||
return spawnUpdate(['--processStart', exeName], true, function() {});
|
||||
};
|
||||
|
||||
// Download the releases specified by the URL and write new results to stdout.
|
||||
exports.download = function(updateURL, callback) {
|
||||
return spawnUpdate(['--download', updateURL], false, function(error, stdout) {
|
||||
var json, ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
}
|
||||
try {
|
||||
// Last line of output is the JSON details about the releases
|
||||
json = stdout.trim().split('\n').pop();
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === "function" ? ref1.pop() : void 0 : void 0 : void 0;
|
||||
} catch (jsonError) {
|
||||
return callback("Invalid result:\n" + stdout);
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Update the application to the latest remote version specified by URL.
|
||||
exports.update = function(updateURL, callback) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
};
|
||||
|
||||
|
||||
// Is the Update.exe installed with the current application?
|
||||
exports.supported = function() {
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -1,113 +0,0 @@
|
||||
{ipcMain, deprecate} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
{BrowserWindow} = process.atomBinding 'window'
|
||||
BrowserWindow::__proto__ = EventEmitter.prototype
|
||||
|
||||
BrowserWindow::_init = ->
|
||||
{app} = require 'electron' # avoid recursive require.
|
||||
|
||||
# Simulate the application menu on platforms other than OS X.
|
||||
if process.platform isnt 'darwin'
|
||||
menu = app.getApplicationMenu()
|
||||
@setMenu menu if menu?
|
||||
|
||||
# Make new windows requested by links behave like "window.open"
|
||||
@webContents.on '-new-window', (event, url, frameName) ->
|
||||
options = show: true, width: 800, height: 600
|
||||
ipcMain.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
|
||||
|
||||
# window.resizeTo(...)
|
||||
# window.moveTo(...)
|
||||
@webContents.on 'move', (event, size) =>
|
||||
@setBounds size
|
||||
|
||||
# Hide the auto-hide menu when webContents is focused.
|
||||
@webContents.on 'activate', =>
|
||||
if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
|
||||
@setMenuBarVisibility false
|
||||
|
||||
# Forward the crashed event.
|
||||
@webContents.on 'crashed', =>
|
||||
@emit 'crashed'
|
||||
|
||||
# Change window title to page title.
|
||||
@webContents.on 'page-title-updated', (event, title, explicitSet) =>
|
||||
@emit 'page-title-updated', event, title
|
||||
@setTitle title unless event.defaultPrevented
|
||||
|
||||
# Sometimes the webContents doesn't get focus when window is shown, so we have
|
||||
# to force focusing on webContents in this case. The safest way is to focus it
|
||||
# when we first start to load URL, if we do it earlier it won't have effect,
|
||||
# if we do it later we might move focus in the page.
|
||||
# Though this hack is only needed on OS X when the app is launched from
|
||||
# Finder, we still do it on all platforms in case of other bugs we don't know.
|
||||
@webContents.once 'load-url', ->
|
||||
@focus()
|
||||
|
||||
# Redirect focus/blur event to app instance too.
|
||||
@on 'blur', (event) =>
|
||||
app.emit 'browser-window-blur', event, this
|
||||
@on 'focus', (event) =>
|
||||
app.emit 'browser-window-focus', event, this
|
||||
|
||||
# Notify the creation of the window.
|
||||
app.emit 'browser-window-created', {}, this
|
||||
|
||||
# Be compatible with old APIs.
|
||||
@webContents.on 'devtools-focused', => @emit 'devtools-focused'
|
||||
@webContents.on 'devtools-opened', => @emit 'devtools-opened'
|
||||
@webContents.on 'devtools-closed', => @emit 'devtools-closed'
|
||||
Object.defineProperty this, 'devToolsWebContents',
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: -> @webContents.devToolsWebContents
|
||||
|
||||
BrowserWindow.getFocusedWindow = ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.isFocused()
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.webContents?.equal webContents
|
||||
|
||||
BrowserWindow.fromDevToolsWebContents = (webContents) ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.devToolsWebContents?.equal webContents
|
||||
|
||||
# Helpers.
|
||||
BrowserWindow::loadURL = -> @webContents.loadURL.apply @webContents, arguments
|
||||
BrowserWindow::getURL = -> @webContents.getURL()
|
||||
BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments
|
||||
BrowserWindow::send = -> @webContents.send.apply @webContents, arguments
|
||||
BrowserWindow::openDevTools = -> @webContents.openDevTools.apply @webContents, arguments
|
||||
BrowserWindow::closeDevTools = -> @webContents.closeDevTools()
|
||||
BrowserWindow::isDevToolsOpened = -> @webContents.isDevToolsOpened()
|
||||
BrowserWindow::isDevToolsFocused = -> @webContents.isDevToolsFocused()
|
||||
BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools()
|
||||
BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments
|
||||
BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker()
|
||||
|
||||
# Deprecated.
|
||||
deprecate.member BrowserWindow, 'undo', 'webContents'
|
||||
deprecate.member BrowserWindow, 'redo', 'webContents'
|
||||
deprecate.member BrowserWindow, 'cut', 'webContents'
|
||||
deprecate.member BrowserWindow, 'copy', 'webContents'
|
||||
deprecate.member BrowserWindow, 'paste', 'webContents'
|
||||
deprecate.member BrowserWindow, 'selectAll', 'webContents'
|
||||
deprecate.member BrowserWindow, 'reloadIgnoringCache', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isLoading', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isWaitingForResponse', 'webContents'
|
||||
deprecate.member BrowserWindow, 'stop', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isCrashed', 'webContents'
|
||||
deprecate.member BrowserWindow, 'print', 'webContents'
|
||||
deprecate.member BrowserWindow, 'printToPDF', 'webContents'
|
||||
deprecate.rename BrowserWindow, 'restart', 'reload'
|
||||
deprecate.rename BrowserWindow, 'loadUrl', 'loadURL'
|
||||
deprecate.rename BrowserWindow, 'getUrl', 'getURL'
|
||||
BrowserWindow::executeJavaScriptInDevTools = deprecate 'executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', (code) ->
|
||||
@devToolsWebContents?.executeJavaScript code
|
||||
BrowserWindow::getPageTitle = deprecate 'getPageTitle', 'webContents.getTitle', ->
|
||||
@webContents?.getTitle()
|
||||
|
||||
module.exports = BrowserWindow
|
||||
241
atom/browser/api/lib/browser-window.js
Normal file
241
atom/browser/api/lib/browser-window.js
Normal file
@@ -0,0 +1,241 @@
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const deprecate = require('electron').deprecate;
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const BrowserWindow = process.atomBinding('window').BrowserWindow;
|
||||
|
||||
BrowserWindow.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
BrowserWindow.prototype._init = function() {
|
||||
|
||||
// avoid recursive require.
|
||||
var app, menu;
|
||||
app = require('electron').app;
|
||||
|
||||
// Simulate the application menu on platforms other than OS X.
|
||||
if (process.platform !== 'darwin') {
|
||||
menu = app.getApplicationMenu();
|
||||
if (menu != null) {
|
||||
this.setMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
// Make new windows requested by links behave like "window.open"
|
||||
this.webContents.on('-new-window', function(event, url, frameName) {
|
||||
var options;
|
||||
options = {
|
||||
show: true,
|
||||
width: 800,
|
||||
height: 600
|
||||
};
|
||||
return ipcMain.emit('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options);
|
||||
});
|
||||
|
||||
// window.resizeTo(...)
|
||||
// window.moveTo(...)
|
||||
this.webContents.on('move', (function(_this) {
|
||||
return function(event, size) {
|
||||
return _this.setBounds(size);
|
||||
};
|
||||
})(this));
|
||||
|
||||
// Hide the auto-hide menu when webContents is focused.
|
||||
this.webContents.on('activate', (function(_this) {
|
||||
return function() {
|
||||
if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) {
|
||||
return _this.setMenuBarVisibility(false);
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
|
||||
// Forward the crashed event.
|
||||
this.webContents.on('crashed', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('crashed');
|
||||
};
|
||||
})(this));
|
||||
|
||||
// Change window title to page title.
|
||||
this.webContents.on('page-title-updated', (function(_this) {
|
||||
return function(event, title) {
|
||||
_this.emit('page-title-updated', event, title);
|
||||
if (!event.defaultPrevented) {
|
||||
return _this.setTitle(title);
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
|
||||
// Sometimes the webContents doesn't get focus when window is shown, so we have
|
||||
// to force focusing on webContents in this case. The safest way is to focus it
|
||||
// when we first start to load URL, if we do it earlier it won't have effect,
|
||||
// if we do it later we might move focus in the page.
|
||||
// Though this hack is only needed on OS X when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't know.
|
||||
this.webContents.once('load-url', function() {
|
||||
return this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (function(_this) {
|
||||
return function(event) {
|
||||
return app.emit('browser-window-blur', event, _this);
|
||||
};
|
||||
})(this));
|
||||
this.on('focus', (function(_this) {
|
||||
return function(event) {
|
||||
return app.emit('browser-window-focus', event, _this);
|
||||
};
|
||||
})(this));
|
||||
|
||||
// Notify the creation of the window.
|
||||
app.emit('browser-window-created', {}, this);
|
||||
|
||||
// Be compatible with old APIs.
|
||||
this.webContents.on('devtools-focused', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-focused');
|
||||
};
|
||||
})(this));
|
||||
this.webContents.on('devtools-opened', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-opened');
|
||||
};
|
||||
})(this));
|
||||
this.webContents.on('devtools-closed', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-closed');
|
||||
};
|
||||
})(this));
|
||||
return Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: function() {
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = function() {
|
||||
var i, len, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if (window.isFocused()) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = function(webContents) {
|
||||
var i, len, ref1, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if ((ref1 = window.webContents) != null ? ref1.equal(webContents) : void 0) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BrowserWindow.fromDevToolsWebContents = function(webContents) {
|
||||
var i, len, ref1, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if ((ref1 = window.devToolsWebContents) != null ? ref1.equal(webContents) : void 0) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers.
|
||||
|
||||
BrowserWindow.prototype.loadURL = function() {
|
||||
return this.webContents.loadURL.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getURL = function() {
|
||||
return this.webContents.getURL();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.reload = function() {
|
||||
return this.webContents.reload.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.send = function() {
|
||||
return this.webContents.send.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.openDevTools = function() {
|
||||
return this.webContents.openDevTools.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.closeDevTools = function() {
|
||||
return this.webContents.closeDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsOpened = function() {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsFocused = function() {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.toggleDevTools = function() {
|
||||
return this.webContents.toggleDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectElement = function() {
|
||||
return this.webContents.inspectElement.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectServiceWorker = function() {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
};
|
||||
|
||||
// Deprecated.
|
||||
|
||||
deprecate.member(BrowserWindow, 'undo', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'redo', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'cut', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'copy', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'paste', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'selectAll', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'reloadIgnoringCache', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isLoading', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isWaitingForResponse', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'stop', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isCrashed', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'print', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'printToPDF', 'webContents');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'restart', 'reload');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'loadUrl', 'loadURL');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'getUrl', 'getURL');
|
||||
|
||||
BrowserWindow.prototype.executeJavaScriptInDevTools = deprecate('executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', function(code) {
|
||||
var ref1;
|
||||
return (ref1 = this.devToolsWebContents) != null ? ref1.executeJavaScript(code) : void 0;
|
||||
});
|
||||
|
||||
BrowserWindow.prototype.getPageTitle = deprecate('getPageTitle', 'webContents.getTitle', function() {
|
||||
var ref1;
|
||||
return (ref1 = this.webContents) != null ? ref1.getTitle() : void 0;
|
||||
});
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = process.atomBinding 'content_tracing'
|
||||
1
atom/browser/api/lib/content-tracing.js
Normal file
1
atom/browser/api/lib/content-tracing.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = process.atomBinding('content_tracing');
|
||||
@@ -1,125 +0,0 @@
|
||||
{app, BrowserWindow} = require 'electron'
|
||||
|
||||
binding = process.atomBinding 'dialog'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
fileDialogProperties =
|
||||
openFile: 1 << 0
|
||||
openDirectory: 1 << 1
|
||||
multiSelections: 1 << 2
|
||||
createDirectory: 1 << 3
|
||||
|
||||
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
|
||||
|
||||
messageBoxOptions =
|
||||
noLink: 1 << 0
|
||||
|
||||
parseArgs = (window, options, callback) ->
|
||||
unless window is null or window?.constructor is BrowserWindow
|
||||
# Shift.
|
||||
callback = options
|
||||
options = window
|
||||
window = null
|
||||
if not callback? and typeof options is 'function'
|
||||
# Shift.
|
||||
callback = options
|
||||
options = null
|
||||
[window, options, callback]
|
||||
|
||||
checkAppInitialized = ->
|
||||
throw new Error('dialog module can only be used after app is ready') unless app.isReady()
|
||||
|
||||
module.exports =
|
||||
showOpenDialog: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= title: 'Open', properties: ['openFile']
|
||||
options.properties ?= ['openFile']
|
||||
throw new TypeError('Properties need to be array') unless Array.isArray options.properties
|
||||
|
||||
properties = 0
|
||||
for prop, value of fileDialogProperties
|
||||
properties |= value if prop in options.properties
|
||||
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
(success, result) -> callback(if success then result)
|
||||
else
|
||||
null
|
||||
|
||||
binding.showOpenDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
properties,
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showSaveDialog: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= title: 'Save'
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
(success, result) -> callback(if success then result)
|
||||
else
|
||||
null
|
||||
|
||||
binding.showSaveDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showMessageBox: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= type: 'none'
|
||||
options.type ?= 'none'
|
||||
messageBoxType = messageBoxTypes.indexOf options.type
|
||||
throw new TypeError('Invalid message box type') unless messageBoxType > -1
|
||||
|
||||
throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons
|
||||
|
||||
options.title ?= ''
|
||||
options.message ?= ''
|
||||
options.detail ?= ''
|
||||
options.icon ?= null
|
||||
|
||||
# Choose a default button to get selected when dialog is cancelled.
|
||||
unless options.cancelId?
|
||||
options.cancelId = 0
|
||||
for text, i in options.buttons
|
||||
if text.toLowerCase() in ['cancel', 'no']
|
||||
options.cancelId = i
|
||||
break
|
||||
|
||||
flags = if options.noLink then messageBoxOptions.noLink else 0
|
||||
|
||||
binding.showMessageBox messageBoxType,
|
||||
options.buttons,
|
||||
options.cancelId,
|
||||
flags,
|
||||
options.title,
|
||||
options.message,
|
||||
options.detail,
|
||||
options.icon,
|
||||
window,
|
||||
callback
|
||||
|
||||
showErrorBox: (args...) ->
|
||||
binding.showErrorBox args...
|
||||
|
||||
# Mark standard asynchronous functions.
|
||||
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
|
||||
v8Util.setHiddenValue module.exports[api], 'asynchronous', true
|
||||
170
atom/browser/api/lib/dialog.js
Normal file
170
atom/browser/api/lib/dialog.js
Normal file
@@ -0,0 +1,170 @@
|
||||
const app = require('electron').app;
|
||||
const BrowserWindow = require('electron').BrowserWindow;
|
||||
const binding = process.atomBinding('dialog');
|
||||
const v8Util = process.atomBinding('v8_util');
|
||||
|
||||
var slice = [].slice;
|
||||
var includes = [].includes;
|
||||
|
||||
var fileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
openDirectory: 1 << 1,
|
||||
multiSelections: 1 << 2,
|
||||
createDirectory: 1 << 3
|
||||
};
|
||||
|
||||
var messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
|
||||
var messageBoxOptions = {
|
||||
noLink: 1 << 0
|
||||
};
|
||||
|
||||
var parseArgs = function(window, options, callback) {
|
||||
if (!(window === null || (window != null ? window.constructor : void 0) === BrowserWindow)) {
|
||||
// Shift.
|
||||
callback = options;
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
if ((callback == null) && typeof options === 'function') {
|
||||
// Shift.
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
return [window, options, callback];
|
||||
};
|
||||
|
||||
var checkAppInitialized = function() {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function() {
|
||||
var args, callback, options, prop, properties, ref1, value, window, wrappedCallback;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
if (options.properties == null) {
|
||||
options.properties = ['openFile'];
|
||||
}
|
||||
if (!Array.isArray(options.properties)) {
|
||||
throw new TypeError('Properties need to be array');
|
||||
}
|
||||
properties = 0;
|
||||
for (prop in fileDialogProperties) {
|
||||
value = fileDialogProperties[prop];
|
||||
if (includes.call(options.properties, prop)) {
|
||||
properties |= value;
|
||||
}
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.defaultPath == null) {
|
||||
options.defaultPath = '';
|
||||
}
|
||||
if (options.filters == null) {
|
||||
options.filters = [];
|
||||
}
|
||||
wrappedCallback = typeof callback === 'function' ? function(success, result) {
|
||||
return callback(success ? result : void 0);
|
||||
} : null;
|
||||
return binding.showOpenDialog(String(options.title), String(options.defaultPath), options.filters, properties, window, wrappedCallback);
|
||||
},
|
||||
showSaveDialog: function() {
|
||||
var args, callback, options, ref1, window, wrappedCallback;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Save'
|
||||
};
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.defaultPath == null) {
|
||||
options.defaultPath = '';
|
||||
}
|
||||
if (options.filters == null) {
|
||||
options.filters = [];
|
||||
}
|
||||
wrappedCallback = typeof callback === 'function' ? function(success, result) {
|
||||
return callback(success ? result : void 0);
|
||||
} : null;
|
||||
return binding.showSaveDialog(String(options.title), String(options.defaultPath), options.filters, window, wrappedCallback);
|
||||
},
|
||||
showMessageBox: function() {
|
||||
var args, callback, flags, i, j, len, messageBoxType, options, ref1, ref2, ref3, text, window;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
type: 'none'
|
||||
};
|
||||
}
|
||||
if (options.type == null) {
|
||||
options.type = 'none';
|
||||
}
|
||||
messageBoxType = messageBoxTypes.indexOf(options.type);
|
||||
if (!(messageBoxType > -1)) {
|
||||
throw new TypeError('Invalid message box type');
|
||||
}
|
||||
if (!Array.isArray(options.buttons)) {
|
||||
throw new TypeError('Buttons need to be array');
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.message == null) {
|
||||
options.message = '';
|
||||
}
|
||||
if (options.detail == null) {
|
||||
options.detail = '';
|
||||
}
|
||||
if (options.icon == null) {
|
||||
options.icon = null;
|
||||
}
|
||||
if (options.defaultId == null) {
|
||||
options.defaultId = -1;
|
||||
}
|
||||
|
||||
// Choose a default button to get selected when dialog is cancelled.
|
||||
if (options.cancelId == null) {
|
||||
options.cancelId = 0;
|
||||
ref2 = options.buttons;
|
||||
for (i = j = 0, len = ref2.length; j < len; i = ++j) {
|
||||
text = ref2[i];
|
||||
if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') {
|
||||
options.cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
flags = options.noLink ? messageBoxOptions.noLink : 0;
|
||||
return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback);
|
||||
},
|
||||
showErrorBox: function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return binding.showErrorBox.apply(binding, args);
|
||||
}
|
||||
};
|
||||
|
||||
// Mark standard asynchronous functions.
|
||||
var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog'];
|
||||
var j, len, api;
|
||||
for (j = 0, len = ref1.length; j < len; j++) {
|
||||
api = ref1[j];
|
||||
v8Util.setHiddenValue(module.exports[api], 'asynchronous', true);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
common = require '../../../../common/api/lib/exports/electron'
|
||||
|
||||
# Import common modules.
|
||||
common.defineProperties exports
|
||||
|
||||
Object.defineProperties exports,
|
||||
# Browser side modules, please sort with alphabet order.
|
||||
app:
|
||||
enumerable: true
|
||||
get: -> require '../app'
|
||||
autoUpdater:
|
||||
enumerable: true
|
||||
get: -> require '../auto-updater'
|
||||
BrowserWindow:
|
||||
enumerable: true
|
||||
get: -> require '../browser-window'
|
||||
contentTracing:
|
||||
enumerable: true
|
||||
get: -> require '../content-tracing'
|
||||
dialog:
|
||||
enumerable: true
|
||||
get: -> require '../dialog'
|
||||
ipcMain:
|
||||
enumerable: true
|
||||
get: -> require '../ipc-main'
|
||||
globalShortcut:
|
||||
enumerable: true
|
||||
get: -> require '../global-shortcut'
|
||||
Menu:
|
||||
enumerable: true
|
||||
get: -> require '../menu'
|
||||
MenuItem:
|
||||
enumerable: true
|
||||
get: -> require '../menu-item'
|
||||
powerMonitor:
|
||||
enumerable: true
|
||||
get: -> require '../power-monitor'
|
||||
powerSaveBlocker:
|
||||
enumerable: true
|
||||
get: -> require '../power-save-blocker'
|
||||
protocol:
|
||||
enumerable: true
|
||||
get: -> require '../protocol'
|
||||
screen:
|
||||
enumerable: true
|
||||
get: -> require '../screen'
|
||||
session:
|
||||
enumerable: true
|
||||
get: -> require '../session'
|
||||
Tray:
|
||||
enumerable: true
|
||||
get: -> require '../tray'
|
||||
# The internal modules, invisible unless you know their names.
|
||||
NavigationController:
|
||||
get: -> require '../navigation-controller'
|
||||
webContents:
|
||||
get: -> require '../web-contents'
|
||||
112
atom/browser/api/lib/exports/electron.js
Normal file
112
atom/browser/api/lib/exports/electron.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const common = require('../../../../common/api/lib/exports/electron');
|
||||
|
||||
|
||||
// Import common modules.
|
||||
common.defineProperties(exports);
|
||||
|
||||
Object.defineProperties(exports, {
|
||||
|
||||
// Browser side modules, please sort with alphabet order.
|
||||
app: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../app');
|
||||
}
|
||||
},
|
||||
autoUpdater: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../auto-updater');
|
||||
}
|
||||
},
|
||||
BrowserWindow: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../browser-window');
|
||||
}
|
||||
},
|
||||
contentTracing: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../content-tracing');
|
||||
}
|
||||
},
|
||||
dialog: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../dialog');
|
||||
}
|
||||
},
|
||||
ipcMain: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../ipc-main');
|
||||
}
|
||||
},
|
||||
globalShortcut: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../global-shortcut');
|
||||
}
|
||||
},
|
||||
Menu: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../menu');
|
||||
}
|
||||
},
|
||||
MenuItem: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../menu-item');
|
||||
}
|
||||
},
|
||||
powerMonitor: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../power-monitor');
|
||||
}
|
||||
},
|
||||
powerSaveBlocker: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../power-save-blocker');
|
||||
}
|
||||
},
|
||||
protocol: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../protocol');
|
||||
}
|
||||
},
|
||||
screen: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../screen');
|
||||
}
|
||||
},
|
||||
session: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../session');
|
||||
}
|
||||
},
|
||||
Tray: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../tray');
|
||||
}
|
||||
},
|
||||
|
||||
// The internal modules, invisible unless you know their names.
|
||||
NavigationController: {
|
||||
get: function() {
|
||||
return require('../navigation-controller');
|
||||
}
|
||||
},
|
||||
webContents: {
|
||||
get: function() {
|
||||
return require('../web-contents');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
{globalShortcut} = process.atomBinding 'global_shortcut'
|
||||
|
||||
module.exports = globalShortcut
|
||||
5
atom/browser/api/lib/global-shortcut.js
Normal file
5
atom/browser/api/lib/global-shortcut.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var globalShortcut;
|
||||
|
||||
globalShortcut = process.atomBinding('global_shortcut').globalShortcut;
|
||||
|
||||
module.exports = globalShortcut;
|
||||
@@ -1,3 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
module.exports = new EventEmitter
|
||||
3
atom/browser/api/lib/ipc-main.js
Normal file
3
atom/browser/api/lib/ipc-main.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
module.exports = new EventEmitter;
|
||||
@@ -1,6 +0,0 @@
|
||||
{deprecate, ipcMain} = require 'electron'
|
||||
|
||||
# This module is deprecated, we mirror everything from ipcMain.
|
||||
deprecate.warn 'ipc module', 'require("electron").ipcMain'
|
||||
|
||||
module.exports = ipcMain
|
||||
7
atom/browser/api/lib/ipc.js
Normal file
7
atom/browser/api/lib/ipc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const deprecate = require('electron').deprecate;
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
|
||||
// This module is deprecated, we mirror everything from ipcMain.
|
||||
deprecate.warn('ipc module', 'require("electron").ipcMain');
|
||||
|
||||
module.exports = ipcMain;
|
||||
@@ -1,73 +0,0 @@
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
nextCommandId = 0
|
||||
|
||||
# Maps role to methods of webContents
|
||||
rolesMap =
|
||||
undo: 'undo'
|
||||
redo: 'redo'
|
||||
cut: 'cut'
|
||||
copy: 'copy'
|
||||
paste: 'paste'
|
||||
selectall: 'selectAll'
|
||||
minimize: 'minimize'
|
||||
close: 'close'
|
||||
|
||||
# Maps methods that should be called directly on the BrowserWindow instance
|
||||
methodInBrowserWindow =
|
||||
minimize: true
|
||||
close: true
|
||||
|
||||
class MenuItem
|
||||
@types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
|
||||
|
||||
constructor: (options) ->
|
||||
{Menu} = require 'electron'
|
||||
|
||||
{click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
|
||||
|
||||
if @submenu? and @submenu.constructor isnt Menu
|
||||
@submenu = Menu.buildFromTemplate @submenu
|
||||
@type = 'submenu' if not @type? and @submenu?
|
||||
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
|
||||
|
||||
@overrideReadOnlyProperty 'type', 'normal'
|
||||
@overrideReadOnlyProperty 'role'
|
||||
@overrideReadOnlyProperty 'accelerator'
|
||||
@overrideReadOnlyProperty 'icon'
|
||||
@overrideReadOnlyProperty 'submenu'
|
||||
@overrideProperty 'label', ''
|
||||
@overrideProperty 'sublabel', ''
|
||||
@overrideProperty 'enabled', true
|
||||
@overrideProperty 'visible', true
|
||||
@overrideProperty 'checked', false
|
||||
|
||||
throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1
|
||||
|
||||
@commandId = ++nextCommandId
|
||||
@click = (focusedWindow) =>
|
||||
# Manually flip the checked flags when clicked.
|
||||
@checked = !@checked if @type in ['checkbox', 'radio']
|
||||
|
||||
if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow?
|
||||
methodName = rolesMap[@role]
|
||||
if methodInBrowserWindow[methodName]
|
||||
focusedWindow[methodName]()
|
||||
else
|
||||
focusedWindow.webContents?[methodName]()
|
||||
else if typeof click is 'function'
|
||||
click this, focusedWindow
|
||||
else if typeof @selector is 'string'
|
||||
Menu.sendActionToFirstResponder @selector
|
||||
|
||||
overrideProperty: (name, defaultValue=null) ->
|
||||
this[name] ?= defaultValue
|
||||
|
||||
overrideReadOnlyProperty: (name, defaultValue=null) ->
|
||||
this[name] ?= defaultValue
|
||||
Object.defineProperty this, name,
|
||||
enumerable: true
|
||||
writable: false
|
||||
value: this[name]
|
||||
|
||||
module.exports = MenuItem
|
||||
102
atom/browser/api/lib/menu-item.js
Normal file
102
atom/browser/api/lib/menu-item.js
Normal file
@@ -0,0 +1,102 @@
|
||||
var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap;
|
||||
|
||||
nextCommandId = 0;
|
||||
|
||||
// Maps role to methods of webContents
|
||||
rolesMap = {
|
||||
undo: 'undo',
|
||||
redo: 'redo',
|
||||
cut: 'cut',
|
||||
copy: 'copy',
|
||||
paste: 'paste',
|
||||
selectall: 'selectAll',
|
||||
minimize: 'minimize',
|
||||
close: 'close'
|
||||
};
|
||||
|
||||
// Maps methods that should be called directly on the BrowserWindow instance
|
||||
methodInBrowserWindow = {
|
||||
minimize: true,
|
||||
close: true
|
||||
};
|
||||
|
||||
MenuItem = (function() {
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
|
||||
|
||||
function MenuItem(options) {
|
||||
var click, ref;
|
||||
const Menu = require('electron').Menu;
|
||||
click = options.click, this.selector = options.selector, this.type = options.type, this.role = options.role, this.label = options.label, this.sublabel = options.sublabel, this.accelerator = options.accelerator, this.icon = options.icon, this.enabled = options.enabled, this.visible = options.visible, this.checked = options.checked, this.submenu = options.submenu;
|
||||
if ((this.submenu != null) && this.submenu.constructor !== Menu) {
|
||||
this.submenu = Menu.buildFromTemplate(this.submenu);
|
||||
}
|
||||
if ((this.type == null) && (this.submenu != null)) {
|
||||
this.type = 'submenu';
|
||||
}
|
||||
if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== Menu) {
|
||||
throw new Error('Invalid submenu');
|
||||
}
|
||||
this.overrideReadOnlyProperty('type', 'normal');
|
||||
this.overrideReadOnlyProperty('role');
|
||||
this.overrideReadOnlyProperty('accelerator');
|
||||
this.overrideReadOnlyProperty('icon');
|
||||
this.overrideReadOnlyProperty('submenu');
|
||||
this.overrideProperty('label', '');
|
||||
this.overrideProperty('sublabel', '');
|
||||
this.overrideProperty('enabled', true);
|
||||
this.overrideProperty('visible', true);
|
||||
this.overrideProperty('checked', false);
|
||||
if (MenuItem.types.indexOf(this.type) === -1) {
|
||||
throw new Error("Unknown menu type " + this.type);
|
||||
}
|
||||
this.commandId = ++nextCommandId;
|
||||
this.click = (function(_this) {
|
||||
return function(focusedWindow) {
|
||||
|
||||
// Manually flip the checked flags when clicked.
|
||||
var methodName, ref1, ref2;
|
||||
if ((ref1 = _this.type) === 'checkbox' || ref1 === 'radio') {
|
||||
_this.checked = !_this.checked;
|
||||
}
|
||||
if (_this.role && rolesMap[_this.role] && process.platform !== 'darwin' && (focusedWindow != null)) {
|
||||
methodName = rolesMap[_this.role];
|
||||
if (methodInBrowserWindow[methodName]) {
|
||||
return focusedWindow[methodName]();
|
||||
} else {
|
||||
return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0;
|
||||
}
|
||||
} else if (typeof click === 'function') {
|
||||
return click(_this, focusedWindow);
|
||||
} else if (typeof _this.selector === 'string') {
|
||||
return Menu.sendActionToFirstResponder(_this.selector);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
|
||||
MenuItem.prototype.overrideProperty = function(name, defaultValue) {
|
||||
if (defaultValue == null) {
|
||||
defaultValue = null;
|
||||
}
|
||||
return this[name] != null ? this[name] : this[name] = defaultValue;
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function(name, defaultValue) {
|
||||
if (defaultValue == null) {
|
||||
defaultValue = null;
|
||||
}
|
||||
if (this[name] == null) {
|
||||
this[name] = defaultValue;
|
||||
}
|
||||
return Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
value: this[name]
|
||||
});
|
||||
};
|
||||
|
||||
return MenuItem;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = MenuItem;
|
||||
@@ -1,178 +0,0 @@
|
||||
{BrowserWindow, MenuItem} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
bindings = process.atomBinding 'menu'
|
||||
|
||||
# Automatically generated radio menu item's group id.
|
||||
nextGroupId = 0
|
||||
|
||||
# Search between seperators to find a radio menu item and return its group id,
|
||||
# otherwise generate a group id.
|
||||
generateGroupId = (items, pos) ->
|
||||
if pos > 0
|
||||
for i in [pos - 1..0]
|
||||
item = items[i]
|
||||
return item.groupId if item.type is 'radio'
|
||||
break if item.type is 'separator'
|
||||
else if pos < items.length
|
||||
for i in [pos..items.length - 1]
|
||||
item = items[i]
|
||||
return item.groupId if item.type is 'radio'
|
||||
break if item.type is 'separator'
|
||||
++nextGroupId
|
||||
|
||||
# Returns the index of item according to |id|.
|
||||
indexOfItemById = (items, id) ->
|
||||
return i for item, i in items when item.id is id
|
||||
-1
|
||||
|
||||
# Returns the index of where to insert the item according to |position|.
|
||||
indexToInsertByPosition = (items, position) ->
|
||||
return items.length unless position
|
||||
|
||||
[query, id] = position.split '='
|
||||
insertIndex = indexOfItemById items, id
|
||||
if insertIndex is -1 and query isnt 'endof'
|
||||
console.warn "Item with id '#{id}' is not found"
|
||||
return items.length
|
||||
|
||||
switch query
|
||||
when 'after'
|
||||
insertIndex++
|
||||
when 'endof'
|
||||
# If the |id| doesn't exist, then create a new group with the |id|.
|
||||
if insertIndex is -1
|
||||
items.push id: id, type: 'separator'
|
||||
insertIndex = items.length - 1
|
||||
|
||||
# Find the end of the group.
|
||||
insertIndex++
|
||||
while insertIndex < items.length and items[insertIndex].type isnt 'separator'
|
||||
insertIndex++
|
||||
|
||||
insertIndex
|
||||
|
||||
Menu = bindings.Menu
|
||||
Menu::__proto__ = EventEmitter.prototype
|
||||
|
||||
Menu::_init = ->
|
||||
@commandsMap = {}
|
||||
@groupsMap = {}
|
||||
@items = []
|
||||
@delegate =
|
||||
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
|
||||
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
|
||||
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
|
||||
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
|
||||
getIconForCommandId: (commandId) => @commandsMap[commandId]?.icon
|
||||
executeCommand: (commandId) =>
|
||||
@commandsMap[commandId]?.click BrowserWindow.getFocusedWindow()
|
||||
menuWillShow: =>
|
||||
# Make sure radio groups have at least one menu item seleted.
|
||||
for id, group of @groupsMap
|
||||
checked = false
|
||||
for radioItem in group when radioItem.checked
|
||||
checked = true
|
||||
break
|
||||
v8Util.setHiddenValue group[0], 'checked', true unless checked
|
||||
|
||||
Menu::popup = (window, x, y) ->
|
||||
unless window?.constructor is BrowserWindow
|
||||
# Shift.
|
||||
y = x
|
||||
x = window
|
||||
window = BrowserWindow.getFocusedWindow()
|
||||
if x? and y?
|
||||
@_popupAt(window, x, y)
|
||||
else
|
||||
@_popup window
|
||||
|
||||
Menu::append = (item) ->
|
||||
@insert @getItemCount(), item
|
||||
|
||||
Menu::insert = (pos, item) ->
|
||||
throw new TypeError('Invalid item') unless item?.constructor is MenuItem
|
||||
|
||||
switch item.type
|
||||
when 'normal' then @insertItem pos, item.commandId, item.label
|
||||
when 'checkbox' then @insertCheckItem pos, item.commandId, item.label
|
||||
when 'separator' then @insertSeparator pos
|
||||
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
|
||||
when 'radio'
|
||||
# Grouping radio menu items.
|
||||
item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
|
||||
@groupsMap[item.groupId] ?= []
|
||||
@groupsMap[item.groupId].push item
|
||||
|
||||
# Setting a radio menu item should flip other items in the group.
|
||||
v8Util.setHiddenValue item, 'checked', item.checked
|
||||
Object.defineProperty item, 'checked',
|
||||
enumerable: true
|
||||
get: -> v8Util.getHiddenValue item, 'checked'
|
||||
set: (val) =>
|
||||
for otherItem in @groupsMap[item.groupId] when otherItem isnt item
|
||||
v8Util.setHiddenValue otherItem, 'checked', false
|
||||
v8Util.setHiddenValue item, 'checked', true
|
||||
|
||||
@insertRadioItem pos, item.commandId, item.label, item.groupId
|
||||
|
||||
@setSublabel pos, item.sublabel if item.sublabel?
|
||||
@setIcon pos, item.icon if item.icon?
|
||||
@setRole pos, item.role if item.role?
|
||||
|
||||
# Make menu accessable to items.
|
||||
item.overrideReadOnlyProperty 'menu', this
|
||||
|
||||
# Remember the items.
|
||||
@items.splice pos, 0, item
|
||||
@commandsMap[item.commandId] = item
|
||||
|
||||
# Force menuWillShow to be called
|
||||
Menu::_callMenuWillShow = ->
|
||||
@delegate?.menuWillShow()
|
||||
item.submenu._callMenuWillShow() for item in @items when item.submenu?
|
||||
|
||||
applicationMenu = null
|
||||
Menu.setApplicationMenu = (menu) ->
|
||||
throw new TypeError('Invalid menu') unless menu is null or menu.constructor is Menu
|
||||
applicationMenu = menu # Keep a reference.
|
||||
|
||||
if process.platform is 'darwin'
|
||||
return if menu is null
|
||||
menu._callMenuWillShow()
|
||||
bindings.setApplicationMenu menu
|
||||
else
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
w.setMenu menu for w in windows
|
||||
|
||||
Menu.getApplicationMenu = -> applicationMenu
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||
|
||||
Menu.buildFromTemplate = (template) ->
|
||||
throw new TypeError('Invalid template for Menu') unless Array.isArray template
|
||||
|
||||
positionedTemplate = []
|
||||
insertIndex = 0
|
||||
|
||||
for item in template
|
||||
if item.position
|
||||
insertIndex = indexToInsertByPosition positionedTemplate, item.position
|
||||
else
|
||||
# If no |position| is specified, insert after last item.
|
||||
insertIndex++
|
||||
positionedTemplate.splice insertIndex, 0, item
|
||||
|
||||
menu = new Menu
|
||||
|
||||
for item in positionedTemplate
|
||||
throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object'
|
||||
|
||||
menuItem = new MenuItem(item)
|
||||
menuItem[key] ?= value for key, value of item
|
||||
menu.append menuItem
|
||||
|
||||
menu
|
||||
|
||||
module.exports = Menu
|
||||
335
atom/browser/api/lib/menu.js
Normal file
335
atom/browser/api/lib/menu.js
Normal file
@@ -0,0 +1,335 @@
|
||||
const BrowserWindow = require('electron').BrowserWindow;
|
||||
const MenuItem = require('electron').MenuItem;
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const v8Util = process.atomBinding('v8_util');
|
||||
const bindings = process.atomBinding('menu');
|
||||
|
||||
// Automatically generated radio menu item's group id.
|
||||
var nextGroupId = 0;
|
||||
|
||||
// Search between seperators to find a radio menu item and return its group id,
|
||||
// otherwise generate a group id.
|
||||
var generateGroupId = function(items, pos) {
|
||||
var i, item, j, k, ref1, ref2, ref3;
|
||||
if (pos > 0) {
|
||||
for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
|
||||
item = items[i];
|
||||
if (item.type === 'radio') {
|
||||
return item.groupId;
|
||||
}
|
||||
if (item.type === 'separator') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (pos < items.length) {
|
||||
for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
|
||||
item = items[i];
|
||||
if (item.type === 'radio') {
|
||||
return item.groupId;
|
||||
}
|
||||
if (item.type === 'separator') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ++nextGroupId;
|
||||
};
|
||||
|
||||
// Returns the index of item according to |id|.
|
||||
var indexOfItemById = function(items, id) {
|
||||
var i, item, j, len;
|
||||
for (i = j = 0, len = items.length; j < len; i = ++j) {
|
||||
item = items[i];
|
||||
if (item.id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Returns the index of where to insert the item according to |position|.
|
||||
var indexToInsertByPosition = function(items, position) {
|
||||
var id, insertIndex, query, ref1;
|
||||
if (!position) {
|
||||
return items.length;
|
||||
}
|
||||
ref1 = position.split('='), query = ref1[0], id = ref1[1];
|
||||
insertIndex = indexOfItemById(items, id);
|
||||
if (insertIndex === -1 && query !== 'endof') {
|
||||
console.warn("Item with id '" + id + "' is not found");
|
||||
return items.length;
|
||||
}
|
||||
switch (query) {
|
||||
case 'after':
|
||||
insertIndex++;
|
||||
break;
|
||||
case 'endof':
|
||||
|
||||
// If the |id| doesn't exist, then create a new group with the |id|.
|
||||
if (insertIndex === -1) {
|
||||
items.push({
|
||||
id: id,
|
||||
type: 'separator'
|
||||
});
|
||||
insertIndex = items.length - 1;
|
||||
}
|
||||
|
||||
// Find the end of the group.
|
||||
insertIndex++;
|
||||
while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
return insertIndex;
|
||||
};
|
||||
|
||||
const Menu = bindings.Menu;
|
||||
|
||||
Menu.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Menu.prototype._init = function() {
|
||||
this.commandsMap = {};
|
||||
this.groupsMap = {};
|
||||
this.items = [];
|
||||
return this.delegate = {
|
||||
isCommandIdChecked: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.checked : void 0;
|
||||
};
|
||||
})(this),
|
||||
isCommandIdEnabled: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.enabled : void 0;
|
||||
};
|
||||
})(this),
|
||||
isCommandIdVisible: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.visible : void 0;
|
||||
};
|
||||
})(this),
|
||||
getAcceleratorForCommandId: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.accelerator : void 0;
|
||||
};
|
||||
})(this),
|
||||
getIconForCommandId: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.icon : void 0;
|
||||
};
|
||||
})(this),
|
||||
executeCommand: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.click(BrowserWindow.getFocusedWindow()) : void 0;
|
||||
};
|
||||
})(this),
|
||||
menuWillShow: (function(_this) {
|
||||
return function() {
|
||||
|
||||
// Make sure radio groups have at least one menu item seleted.
|
||||
var checked, group, id, j, len, radioItem, ref1, results;
|
||||
ref1 = _this.groupsMap;
|
||||
results = [];
|
||||
for (id in ref1) {
|
||||
group = ref1[id];
|
||||
checked = false;
|
||||
for (j = 0, len = group.length; j < len; j++) {
|
||||
radioItem = group[j];
|
||||
if (!radioItem.checked) {
|
||||
continue;
|
||||
}
|
||||
checked = true;
|
||||
break;
|
||||
}
|
||||
if (!checked) {
|
||||
results.push(v8Util.setHiddenValue(group[0], 'checked', true));
|
||||
} else {
|
||||
results.push(void 0);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
})(this)
|
||||
};
|
||||
};
|
||||
|
||||
Menu.prototype.popup = function(window, x, y, positioningItem) {
|
||||
if (typeof window != 'object' || window.constructor !== BrowserWindow) {
|
||||
// Shift.
|
||||
positioningItem = y;
|
||||
y = x;
|
||||
x = window;
|
||||
window = BrowserWindow.getFocusedWindow();
|
||||
}
|
||||
|
||||
// Default parameters.
|
||||
if (typeof x !== 'number') x = -1;
|
||||
if (typeof y !== 'number') y = -1;
|
||||
if (typeof positioningItem !== 'number') positioningItem = 0;
|
||||
|
||||
this.popupAt(window, x, y, positioningItem);
|
||||
};
|
||||
|
||||
Menu.prototype.append = function(item) {
|
||||
return this.insert(this.getItemCount(), item);
|
||||
};
|
||||
|
||||
Menu.prototype.insert = function(pos, item) {
|
||||
var base, name;
|
||||
if ((item != null ? item.constructor : void 0) !== MenuItem) {
|
||||
throw new TypeError('Invalid item');
|
||||
}
|
||||
switch (item.type) {
|
||||
case 'normal':
|
||||
this.insertItem(pos, item.commandId, item.label);
|
||||
break;
|
||||
case 'checkbox':
|
||||
this.insertCheckItem(pos, item.commandId, item.label);
|
||||
break;
|
||||
case 'separator':
|
||||
this.insertSeparator(pos);
|
||||
break;
|
||||
case 'submenu':
|
||||
this.insertSubMenu(pos, item.commandId, item.label, item.submenu);
|
||||
break;
|
||||
case 'radio':
|
||||
// Grouping radio menu items.
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
|
||||
if ((base = this.groupsMap)[name = item.groupId] == null) {
|
||||
base[name] = [];
|
||||
}
|
||||
this.groupsMap[item.groupId].push(item);
|
||||
|
||||
// Setting a radio menu item should flip other items in the group.
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked);
|
||||
Object.defineProperty(item, 'checked', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return v8Util.getHiddenValue(item, 'checked');
|
||||
},
|
||||
set: (function(_this) {
|
||||
return function() {
|
||||
var j, len, otherItem, ref1;
|
||||
ref1 = _this.groupsMap[item.groupId];
|
||||
for (j = 0, len = ref1.length; j < len; j++) {
|
||||
otherItem = ref1[j];
|
||||
if (otherItem !== item) {
|
||||
v8Util.setHiddenValue(otherItem, 'checked', false);
|
||||
}
|
||||
}
|
||||
return v8Util.setHiddenValue(item, 'checked', true);
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
|
||||
}
|
||||
if (item.sublabel != null) {
|
||||
this.setSublabel(pos, item.sublabel);
|
||||
}
|
||||
if (item.icon != null) {
|
||||
this.setIcon(pos, item.icon);
|
||||
}
|
||||
if (item.role != null) {
|
||||
this.setRole(pos, item.role);
|
||||
}
|
||||
|
||||
// Make menu accessable to items.
|
||||
item.overrideReadOnlyProperty('menu', this);
|
||||
|
||||
// Remember the items.
|
||||
this.items.splice(pos, 0, item);
|
||||
return this.commandsMap[item.commandId] = item;
|
||||
};
|
||||
|
||||
|
||||
// Force menuWillShow to be called
|
||||
Menu.prototype._callMenuWillShow = function() {
|
||||
var item, j, len, ref1, ref2, results;
|
||||
if ((ref1 = this.delegate) != null) {
|
||||
ref1.menuWillShow();
|
||||
}
|
||||
ref2 = this.items;
|
||||
results = [];
|
||||
for (j = 0, len = ref2.length; j < len; j++) {
|
||||
item = ref2[j];
|
||||
if (item.submenu != null) {
|
||||
results.push(item.submenu._callMenuWillShow());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
var applicationMenu = null;
|
||||
|
||||
Menu.setApplicationMenu = function(menu) {
|
||||
var j, len, results, w, windows;
|
||||
if (!(menu === null || menu.constructor === Menu)) {
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
|
||||
// Keep a reference.
|
||||
applicationMenu = menu;
|
||||
if (process.platform === 'darwin') {
|
||||
if (menu === null) {
|
||||
return;
|
||||
}
|
||||
menu._callMenuWillShow();
|
||||
return bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
results = [];
|
||||
for (j = 0, len = windows.length; j < len; j++) {
|
||||
w = windows[j];
|
||||
results.push(w.setMenu(menu));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
Menu.getApplicationMenu = function() {
|
||||
return applicationMenu;
|
||||
};
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
Menu.buildFromTemplate = function(template) {
|
||||
var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate, value;
|
||||
if (!Array.isArray(template)) {
|
||||
throw new TypeError('Invalid template for Menu');
|
||||
}
|
||||
positionedTemplate = [];
|
||||
insertIndex = 0;
|
||||
for (j = 0, len = template.length; j < len; j++) {
|
||||
item = template[j];
|
||||
if (item.position) {
|
||||
insertIndex = indexToInsertByPosition(positionedTemplate, item.position);
|
||||
} else {
|
||||
// If no |position| is specified, insert after last item.
|
||||
insertIndex++;
|
||||
}
|
||||
positionedTemplate.splice(insertIndex, 0, item);
|
||||
}
|
||||
menu = new Menu;
|
||||
for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
|
||||
item = positionedTemplate[k];
|
||||
if (typeof item !== 'object') {
|
||||
throw new TypeError('Invalid template for MenuItem');
|
||||
}
|
||||
menuItem = new MenuItem(item);
|
||||
for (key in item) {
|
||||
value = item[key];
|
||||
if (menuItem[key] == null) {
|
||||
menuItem[key] = value;
|
||||
}
|
||||
}
|
||||
menu.append(menuItem);
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
module.exports = Menu;
|
||||
@@ -1,122 +0,0 @@
|
||||
{ipcMain} = require 'electron'
|
||||
|
||||
# The history operation in renderer is redirected to browser.
|
||||
ipcMain.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
|
||||
event.sender[method] args...
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (event, method, args...) ->
|
||||
event.returnValue = event.sender[method] args...
|
||||
|
||||
# JavaScript implementation of Chromium's NavigationController.
|
||||
# Instead of relying on Chromium for history control, we compeletely do history
|
||||
# control on user land, and only rely on WebContents.loadURL for navigation.
|
||||
# This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||
# process is restarted everytime.
|
||||
class NavigationController
|
||||
constructor: (@webContents) ->
|
||||
@clearHistory()
|
||||
|
||||
# webContents may have already navigated to a page.
|
||||
if @webContents._getURL()
|
||||
@currentIndex++
|
||||
@history.push @webContents._getURL()
|
||||
|
||||
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
|
||||
if @inPageIndex > -1 and not inPage
|
||||
# Navigated to a new page, clear in-page mark.
|
||||
@inPageIndex = -1
|
||||
else if @inPageIndex is -1 and inPage
|
||||
# Started in-page navigations.
|
||||
@inPageIndex = @currentIndex
|
||||
|
||||
if @pendingIndex >= 0 # Go to index.
|
||||
@currentIndex = @pendingIndex
|
||||
@pendingIndex = -1
|
||||
@history[@currentIndex] = url
|
||||
else if replaceEntry # Non-user initialized navigation.
|
||||
@history[@currentIndex] = url
|
||||
else # Normal navigation.
|
||||
@history = @history.slice 0, @currentIndex + 1 # Clear history.
|
||||
currentEntry = @history[@currentIndex]
|
||||
if currentEntry?.url isnt url
|
||||
@currentIndex++
|
||||
@history.push url
|
||||
|
||||
loadURL: (url, options={}) ->
|
||||
@pendingIndex = -1
|
||||
@webContents._loadURL url, options
|
||||
@webContents.emit 'load-url', url, options
|
||||
|
||||
getURL: ->
|
||||
if @currentIndex is -1
|
||||
''
|
||||
else
|
||||
@history[@currentIndex]
|
||||
|
||||
stop: ->
|
||||
@pendingIndex = -1
|
||||
@webContents._stop()
|
||||
|
||||
reload: ->
|
||||
@pendingIndex = @currentIndex
|
||||
@webContents._loadURL @getURL(), {}
|
||||
|
||||
reloadIgnoringCache: ->
|
||||
@pendingIndex = @currentIndex
|
||||
@webContents._loadURL @getURL(), {extraHeaders: "pragma: no-cache\n"}
|
||||
|
||||
canGoBack: ->
|
||||
@getActiveIndex() > 0
|
||||
|
||||
canGoForward: ->
|
||||
@getActiveIndex() < @history.length - 1
|
||||
|
||||
canGoToIndex: (index) ->
|
||||
index >=0 and index < @history.length
|
||||
|
||||
canGoToOffset: (offset) ->
|
||||
@canGoToIndex @currentIndex + offset
|
||||
|
||||
clearHistory: ->
|
||||
@history = []
|
||||
@currentIndex = -1
|
||||
@pendingIndex = -1
|
||||
@inPageIndex = -1
|
||||
|
||||
goBack: ->
|
||||
return unless @canGoBack()
|
||||
@pendingIndex = @getActiveIndex() - 1
|
||||
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||
@webContents._goBack()
|
||||
else
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goForward: ->
|
||||
return unless @canGoForward()
|
||||
@pendingIndex = @getActiveIndex() + 1
|
||||
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||
@webContents._goForward()
|
||||
else
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goToIndex: (index) ->
|
||||
return unless @canGoToIndex index
|
||||
@pendingIndex = index
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goToOffset: (offset) ->
|
||||
return unless @canGoToOffset offset
|
||||
pendingIndex = @currentIndex + offset
|
||||
if @inPageIndex > -1 and pendingIndex >= @inPageIndex
|
||||
@pendingIndex = pendingIndex
|
||||
@webContents._goToOffset offset
|
||||
else
|
||||
@goToIndex pendingIndex
|
||||
|
||||
getActiveIndex: ->
|
||||
if @pendingIndex is -1 then @currentIndex else @pendingIndex
|
||||
|
||||
length: ->
|
||||
@history.length
|
||||
|
||||
module.exports = NavigationController
|
||||
188
atom/browser/api/lib/navigation-controller.js
Normal file
188
atom/browser/api/lib/navigation-controller.js
Normal file
@@ -0,0 +1,188 @@
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
// The history operation in renderer is redirected to browser.
|
||||
ipcMain.on('ATOM_SHELL_NAVIGATION_CONTROLLER', function() {
|
||||
var args, event, method, ref;
|
||||
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return (ref = event.sender)[method].apply(ref, args);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', function() {
|
||||
var args, event, method, ref;
|
||||
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return event.returnValue = (ref = event.sender)[method].apply(ref, args);
|
||||
});
|
||||
|
||||
// JavaScript implementation of Chromium's NavigationController.
|
||||
// Instead of relying on Chromium for history control, we compeletely do history
|
||||
// control on user land, and only rely on WebContents.loadURL for navigation.
|
||||
// This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||
// process is restarted everytime.
|
||||
var NavigationController = (function() {
|
||||
function NavigationController(webContents) {
|
||||
this.webContents = webContents;
|
||||
this.clearHistory();
|
||||
|
||||
// webContents may have already navigated to a page.
|
||||
if (this.webContents._getURL()) {
|
||||
this.currentIndex++;
|
||||
this.history.push(this.webContents._getURL());
|
||||
}
|
||||
this.webContents.on('navigation-entry-commited', (function(_this) {
|
||||
return function(event, url, inPage, replaceEntry) {
|
||||
var currentEntry;
|
||||
if (_this.inPageIndex > -1 && !inPage) {
|
||||
|
||||
// Navigated to a new page, clear in-page mark.
|
||||
_this.inPageIndex = -1;
|
||||
} else if (_this.inPageIndex === -1 && inPage) {
|
||||
|
||||
// Started in-page navigations.
|
||||
_this.inPageIndex = _this.currentIndex;
|
||||
}
|
||||
if (_this.pendingIndex >= 0) {
|
||||
|
||||
// Go to index.
|
||||
_this.currentIndex = _this.pendingIndex;
|
||||
_this.pendingIndex = -1;
|
||||
return _this.history[_this.currentIndex] = url;
|
||||
} else if (replaceEntry) {
|
||||
|
||||
// Non-user initialized navigation.
|
||||
return _this.history[_this.currentIndex] = url;
|
||||
} else {
|
||||
|
||||
// Normal navigation. Clear history.
|
||||
_this.history = _this.history.slice(0, _this.currentIndex + 1);
|
||||
currentEntry = _this.history[_this.currentIndex];
|
||||
if ((currentEntry != null ? currentEntry.url : void 0) !== url) {
|
||||
_this.currentIndex++;
|
||||
return _this.history.push(url);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
NavigationController.prototype.loadURL = function(url, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.pendingIndex = -1;
|
||||
this.webContents._loadURL(url, options);
|
||||
return this.webContents.emit('load-url', url, options);
|
||||
};
|
||||
|
||||
NavigationController.prototype.getURL = function() {
|
||||
if (this.currentIndex === -1) {
|
||||
return '';
|
||||
} else {
|
||||
return this.history[this.currentIndex];
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.stop = function() {
|
||||
this.pendingIndex = -1;
|
||||
return this.webContents._stop();
|
||||
};
|
||||
|
||||
NavigationController.prototype.reload = function() {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {});
|
||||
};
|
||||
|
||||
NavigationController.prototype.reloadIgnoringCache = function() {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {
|
||||
extraHeaders: "pragma: no-cache\n"
|
||||
});
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoBack = function() {
|
||||
return this.getActiveIndex() > 0;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoForward = function() {
|
||||
return this.getActiveIndex() < this.history.length - 1;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoToIndex = function(index) {
|
||||
return index >= 0 && index < this.history.length;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoToOffset = function(offset) {
|
||||
return this.canGoToIndex(this.currentIndex + offset);
|
||||
};
|
||||
|
||||
NavigationController.prototype.clearHistory = function() {
|
||||
this.history = [];
|
||||
this.currentIndex = -1;
|
||||
this.pendingIndex = -1;
|
||||
return this.inPageIndex = -1;
|
||||
};
|
||||
|
||||
NavigationController.prototype.goBack = function() {
|
||||
if (!this.canGoBack()) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = this.getActiveIndex() - 1;
|
||||
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
|
||||
return this.webContents._goBack();
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.goForward = function() {
|
||||
if (!this.canGoForward()) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = this.getActiveIndex() + 1;
|
||||
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
|
||||
return this.webContents._goForward();
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.goToIndex = function(index) {
|
||||
if (!this.canGoToIndex(index)) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = index;
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
};
|
||||
|
||||
NavigationController.prototype.goToOffset = function(offset) {
|
||||
var pendingIndex;
|
||||
if (!this.canGoToOffset(offset)) {
|
||||
return;
|
||||
}
|
||||
pendingIndex = this.currentIndex + offset;
|
||||
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
|
||||
this.pendingIndex = pendingIndex;
|
||||
return this.webContents._goToOffset(offset);
|
||||
} else {
|
||||
return this.goToIndex(pendingIndex);
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.getActiveIndex = function() {
|
||||
if (this.pendingIndex === -1) {
|
||||
return this.currentIndex;
|
||||
} else {
|
||||
return this.pendingIndex;
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.length = function() {
|
||||
return this.history.length;
|
||||
};
|
||||
|
||||
return NavigationController;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = NavigationController;
|
||||
@@ -1,7 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
{powerMonitor} = process.atomBinding 'power_monitor'
|
||||
|
||||
powerMonitor.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = powerMonitor
|
||||
6
atom/browser/api/lib/power-monitor.js
Normal file
6
atom/browser/api/lib/power-monitor.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const powerMonitor = process.atomBinding('power_monitor').powerMonitor;
|
||||
|
||||
powerMonitor.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = powerMonitor;
|
||||
@@ -1,3 +0,0 @@
|
||||
{powerSaveBlocker} = process.atomBinding 'power_save_blocker'
|
||||
|
||||
module.exports = powerSaveBlocker
|
||||
5
atom/browser/api/lib/power-save-blocker.js
Normal file
5
atom/browser/api/lib/power-save-blocker.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var powerSaveBlocker;
|
||||
|
||||
powerSaveBlocker = process.atomBinding('power_save_blocker').powerSaveBlocker;
|
||||
|
||||
module.exports = powerSaveBlocker;
|
||||
@@ -1,25 +0,0 @@
|
||||
{app} = require 'electron'
|
||||
|
||||
throw new Error('Can not initialize protocol module before app is ready') unless app.isReady()
|
||||
|
||||
{protocol} = process.atomBinding 'protocol'
|
||||
|
||||
# Warn about removed APIs.
|
||||
logAndThrow = (callback, message) ->
|
||||
console.error message
|
||||
if callback then callback(new Error(message)) else throw new Error(message)
|
||||
protocol.registerProtocol = (scheme, handler, callback) ->
|
||||
logAndThrow callback,
|
||||
'registerProtocol API has been replaced by the
|
||||
register[File/Http/Buffer/String]Protocol API family, please
|
||||
switch to the new APIs.'
|
||||
protocol.isHandledProtocol = (scheme, callback) ->
|
||||
logAndThrow callback,
|
||||
'isHandledProtocol API has been replaced by isProtocolHandled.'
|
||||
protocol.interceptProtocol = (scheme, handler, callback) ->
|
||||
logAndThrow callback,
|
||||
'interceptProtocol API has been replaced by the
|
||||
intercept[File/Http/Buffer/String]Protocol API family, please
|
||||
switch to the new APIs.'
|
||||
|
||||
module.exports = protocol
|
||||
31
atom/browser/api/lib/protocol.js
Normal file
31
atom/browser/api/lib/protocol.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const app = require('electron').app;
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('Can not initialize protocol module before app is ready');
|
||||
}
|
||||
|
||||
const protocol = process.atomBinding('protocol').protocol;
|
||||
|
||||
// Warn about removed APIs.
|
||||
var logAndThrow = function(callback, message) {
|
||||
console.error(message);
|
||||
if (callback) {
|
||||
return callback(new Error(message));
|
||||
} else {
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
protocol.registerProtocol = function(scheme, handler, callback) {
|
||||
return logAndThrow(callback, 'registerProtocol API has been replaced by the register[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
|
||||
};
|
||||
|
||||
protocol.isHandledProtocol = function(scheme, callback) {
|
||||
return logAndThrow(callback, 'isHandledProtocol API has been replaced by isProtocolHandled.');
|
||||
};
|
||||
|
||||
protocol.interceptProtocol = function(scheme, handler, callback) {
|
||||
return logAndThrow(callback, 'interceptProtocol API has been replaced by the intercept[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
|
||||
};
|
||||
|
||||
module.exports = protocol;
|
||||
@@ -1,6 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
{screen} = process.atomBinding 'screen'
|
||||
|
||||
screen.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = screen
|
||||
6
atom/browser/api/lib/screen.js
Normal file
6
atom/browser/api/lib/screen.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const screen = process.atomBinding('screen').screen;
|
||||
|
||||
screen.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = screen;
|
||||
@@ -1,24 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
bindings = process.atomBinding 'session'
|
||||
|
||||
PERSIST_PERFIX = 'persist:'
|
||||
|
||||
# Returns the Session from |partition| string.
|
||||
exports.fromPartition = (partition='') ->
|
||||
return exports.defaultSession if partition is ''
|
||||
if partition.startsWith PERSIST_PERFIX
|
||||
bindings.fromPartition partition.substr(PERSIST_PERFIX.length), false
|
||||
else
|
||||
bindings.fromPartition partition, true
|
||||
|
||||
# Returns the default session.
|
||||
Object.defineProperty exports, 'defaultSession',
|
||||
enumerable: true
|
||||
get: -> bindings.fromPartition '', false
|
||||
|
||||
wrapSession = (session) ->
|
||||
# session is an EventEmitter.
|
||||
session.__proto__ = EventEmitter.prototype
|
||||
|
||||
bindings._setWrapSession wrapSession
|
||||
33
atom/browser/api/lib/session.js
Normal file
33
atom/browser/api/lib/session.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const bindings = process.atomBinding('session');
|
||||
const PERSIST_PERFIX = 'persist:';
|
||||
|
||||
// Returns the Session from |partition| string.
|
||||
exports.fromPartition = function(partition) {
|
||||
if (partition == null) {
|
||||
partition = '';
|
||||
}
|
||||
if (partition === '') {
|
||||
return exports.defaultSession;
|
||||
}
|
||||
if (partition.startsWith(PERSIST_PERFIX)) {
|
||||
return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false);
|
||||
} else {
|
||||
return bindings.fromPartition(partition, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the default session.
|
||||
Object.defineProperty(exports, 'defaultSession', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return bindings.fromPartition('', false);
|
||||
}
|
||||
});
|
||||
|
||||
var wrapSession = function(session) {
|
||||
// session is an EventEmitter.
|
||||
return session.__proto__ = EventEmitter.prototype;
|
||||
};
|
||||
|
||||
bindings._setWrapSession(wrapSession);
|
||||
@@ -1,19 +0,0 @@
|
||||
{deprecate} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
{Tray} = process.atomBinding 'tray'
|
||||
Tray::__proto__ = EventEmitter.prototype
|
||||
|
||||
Tray::_init = ->
|
||||
# Deprecated.
|
||||
deprecate.rename this, 'popContextMenu', 'popUpContextMenu'
|
||||
deprecate.event this, 'clicked', 'click'
|
||||
deprecate.event this, 'double-clicked', 'double-click'
|
||||
deprecate.event this, 'right-clicked', 'right-click'
|
||||
deprecate.event this, 'balloon-clicked', 'balloon-click'
|
||||
|
||||
Tray::setContextMenu = (menu) ->
|
||||
@_setContextMenu menu
|
||||
@menu = menu # Keep a strong reference of menu.
|
||||
|
||||
module.exports = Tray
|
||||
23
atom/browser/api/lib/tray.js
Normal file
23
atom/browser/api/lib/tray.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const deprecate = require('electron').deprecate;
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Tray = process.atomBinding('tray').Tray;
|
||||
|
||||
Tray.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Tray.prototype._init = function() {
|
||||
// Deprecated.
|
||||
deprecate.rename(this, 'popContextMenu', 'popUpContextMenu');
|
||||
deprecate.event(this, 'clicked', 'click');
|
||||
deprecate.event(this, 'double-clicked', 'double-click');
|
||||
deprecate.event(this, 'right-clicked', 'right-click');
|
||||
return deprecate.event(this, 'balloon-clicked', 'balloon-click');
|
||||
};
|
||||
|
||||
Tray.prototype.setContextMenu = function(menu) {
|
||||
this._setContextMenu(menu);
|
||||
|
||||
// Keep a strong reference of menu.
|
||||
return this.menu = menu;
|
||||
};
|
||||
|
||||
module.exports = Tray;
|
||||
@@ -1,137 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
{deprecate, ipcMain, session, NavigationController, Menu} = require 'electron'
|
||||
|
||||
binding = process.atomBinding 'web_contents'
|
||||
|
||||
nextId = 0
|
||||
getNextId = -> ++nextId
|
||||
|
||||
PDFPageSize =
|
||||
A5:
|
||||
custom_display_name: "A5"
|
||||
height_microns: 210000
|
||||
name: "ISO_A5"
|
||||
width_microns: 148000
|
||||
A4:
|
||||
custom_display_name: "A4"
|
||||
height_microns: 297000
|
||||
name: "ISO_A4"
|
||||
is_default: "true"
|
||||
width_microns: 210000
|
||||
A3:
|
||||
custom_display_name: "A3"
|
||||
height_microns: 420000
|
||||
name: "ISO_A3"
|
||||
width_microns: 297000
|
||||
Legal:
|
||||
custom_display_name: "Legal"
|
||||
height_microns: 355600
|
||||
name: "NA_LEGAL"
|
||||
width_microns: 215900
|
||||
Letter:
|
||||
custom_display_name: "Letter"
|
||||
height_microns: 279400
|
||||
name: "NA_LETTER"
|
||||
width_microns: 215900
|
||||
Tabloid:
|
||||
height_microns: 431800
|
||||
name: "NA_LEDGER"
|
||||
width_microns: 279400
|
||||
custom_display_name: "Tabloid"
|
||||
|
||||
wrapWebContents = (webContents) ->
|
||||
# webContents is an EventEmitter.
|
||||
webContents.__proto__ = EventEmitter.prototype
|
||||
|
||||
# WebContents::send(channel, args..)
|
||||
webContents.send = (channel, args...) ->
|
||||
@_send channel, [args...]
|
||||
|
||||
# Make sure webContents.executeJavaScript would run the code only when the
|
||||
# web contents has been loaded.
|
||||
webContents.executeJavaScript = (code, hasUserGesture=false) ->
|
||||
if @getURL() and not @isLoading()
|
||||
@_executeJavaScript code, hasUserGesture
|
||||
else
|
||||
webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
|
||||
|
||||
# The navigation controller.
|
||||
controller = new NavigationController(webContents)
|
||||
for name, method of NavigationController.prototype when method instanceof Function
|
||||
do (name, method) ->
|
||||
webContents[name] = -> method.apply controller, arguments
|
||||
|
||||
# Dispatch IPC messages to the ipc module.
|
||||
webContents.on 'ipc-message', (event, packed) ->
|
||||
[channel, args...] = packed
|
||||
ipcMain.emit channel, event, args...
|
||||
webContents.on 'ipc-message-sync', (event, packed) ->
|
||||
[channel, args...] = packed
|
||||
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
|
||||
ipcMain.emit channel, event, args...
|
||||
|
||||
# Handle context menu action request from pepper plugin.
|
||||
webContents.on 'pepper-context-menu', (event, params) ->
|
||||
menu = Menu.buildFromTemplate params.menu
|
||||
menu.popup params.x, params.y
|
||||
|
||||
# This error occurs when host could not be found.
|
||||
webContents.on 'did-fail-provisional-load', (args...) ->
|
||||
# Calling loadURL during this event might cause crash, so delay the event
|
||||
# until next tick.
|
||||
setImmediate => @emit 'did-fail-load', args...
|
||||
|
||||
# Delays the page-title-updated event to next tick.
|
||||
webContents.on '-page-title-updated', (args...) ->
|
||||
setImmediate => @emit 'page-title-updated', args...
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename webContents, 'loadUrl', 'loadURL'
|
||||
deprecate.rename webContents, 'getUrl', 'getURL'
|
||||
deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) ->
|
||||
@emit 'page-title-set', args...
|
||||
|
||||
webContents.printToPDF = (options, callback) ->
|
||||
printingSetting =
|
||||
pageRage: []
|
||||
mediaSize: {}
|
||||
landscape: false
|
||||
color: 2
|
||||
headerFooterEnabled: false
|
||||
marginsType: 0
|
||||
isFirstRequest: false
|
||||
requestID: getNextId()
|
||||
previewModifiable: true
|
||||
printToPDF: true
|
||||
printWithCloudPrint: false
|
||||
printWithPrivet: false
|
||||
printWithExtension: false
|
||||
deviceName: "Save as PDF"
|
||||
generateDraftData: true
|
||||
fitToPageEnabled: false
|
||||
duplex: 0
|
||||
copies: 1
|
||||
collate: true
|
||||
shouldPrintBackgrounds: false
|
||||
shouldPrintSelectionOnly: false
|
||||
|
||||
if options.landscape
|
||||
printingSetting.landscape = options.landscape
|
||||
if options.marginsType
|
||||
printingSetting.marginsType = options.marginsType
|
||||
if options.printSelectionOnly
|
||||
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
|
||||
if options.printBackground
|
||||
printingSetting.shouldPrintBackgrounds = options.printBackground
|
||||
|
||||
if options.pageSize and PDFPageSize[options.pageSize]
|
||||
printingSetting.mediaSize = PDFPageSize[options.pageSize]
|
||||
else
|
||||
printingSetting.mediaSize = PDFPageSize['A4']
|
||||
|
||||
@_printToPDF printingSetting, callback
|
||||
|
||||
binding._setWrapWebContents wrapWebContents
|
||||
|
||||
module.exports.create = (options={}) ->
|
||||
binding.create(options)
|
||||
233
atom/browser/api/lib/web-contents.js
Normal file
233
atom/browser/api/lib/web-contents.js
Normal file
@@ -0,0 +1,233 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const deprecate = require('electron').deprecate;
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const NavigationController = require('electron').NavigationController;
|
||||
const Menu = require('electron').Menu;
|
||||
|
||||
const binding = process.atomBinding('web_contents');
|
||||
const debuggerBinding = process.atomBinding('debugger');
|
||||
|
||||
let slice = [].slice;
|
||||
let nextId = 0;
|
||||
|
||||
let getNextId = function() {
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
let PDFPageSize = {
|
||||
A5: {
|
||||
custom_display_name: "A5",
|
||||
height_microns: 210000,
|
||||
name: "ISO_A5",
|
||||
width_microns: 148000
|
||||
},
|
||||
A4: {
|
||||
custom_display_name: "A4",
|
||||
height_microns: 297000,
|
||||
name: "ISO_A4",
|
||||
is_default: "true",
|
||||
width_microns: 210000
|
||||
},
|
||||
A3: {
|
||||
custom_display_name: "A3",
|
||||
height_microns: 420000,
|
||||
name: "ISO_A3",
|
||||
width_microns: 297000
|
||||
},
|
||||
Legal: {
|
||||
custom_display_name: "Legal",
|
||||
height_microns: 355600,
|
||||
name: "NA_LEGAL",
|
||||
width_microns: 215900
|
||||
},
|
||||
Letter: {
|
||||
custom_display_name: "Letter",
|
||||
height_microns: 279400,
|
||||
name: "NA_LETTER",
|
||||
width_microns: 215900
|
||||
},
|
||||
Tabloid: {
|
||||
height_microns: 431800,
|
||||
name: "NA_LEDGER",
|
||||
width_microns: 279400,
|
||||
custom_display_name: "Tabloid"
|
||||
}
|
||||
};
|
||||
|
||||
// Following methods are mapped to webFrame.
|
||||
const webFrameMethods = [
|
||||
'executeJavaScript',
|
||||
'insertText',
|
||||
'setZoomFactor',
|
||||
'setZoomLevel',
|
||||
'setZoomLevelLimits'
|
||||
];
|
||||
|
||||
let wrapWebContents = function(webContents) {
|
||||
// webContents is an EventEmitter.
|
||||
var controller, method, name, ref1;
|
||||
webContents.__proto__ = EventEmitter.prototype;
|
||||
|
||||
// Every remote callback from renderer process would add a listenter to the
|
||||
// render-view-deleted event, so ignore the listenters warning.
|
||||
webContents.setMaxListeners(0);
|
||||
|
||||
// WebContents::send(channel, args..)
|
||||
webContents.send = function() {
|
||||
var args, channel;
|
||||
channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return this._send(channel, slice.call(args));
|
||||
};
|
||||
|
||||
// The navigation controller.
|
||||
controller = new NavigationController(webContents);
|
||||
ref1 = NavigationController.prototype;
|
||||
for (name in ref1) {
|
||||
method = ref1[name];
|
||||
if (method instanceof Function) {
|
||||
(function(name, method) {
|
||||
return webContents[name] = function() {
|
||||
return method.apply(controller, arguments);
|
||||
};
|
||||
})(name, method);
|
||||
}
|
||||
}
|
||||
|
||||
// Mapping webFrame methods.
|
||||
for (let method of webFrameMethods) {
|
||||
webContents[method] = function() {
|
||||
let args = Array.prototype.slice.call(arguments);
|
||||
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args);
|
||||
};
|
||||
}
|
||||
|
||||
// Make sure webContents.executeJavaScript would run the code only when the
|
||||
// webContents has been loaded.
|
||||
const executeJavaScript = webContents.executeJavaScript;
|
||||
webContents.executeJavaScript = function(code, hasUserGesture) {
|
||||
if (this.getURL() && !this.isLoading())
|
||||
return executeJavaScript.call(this, code, hasUserGesture);
|
||||
else
|
||||
return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture));
|
||||
};
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
webContents.on('ipc-message', function(event, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
|
||||
});
|
||||
webContents.on('ipc-message-sync', function(event, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: function(value) {
|
||||
return event.sendReply(JSON.stringify(value));
|
||||
}
|
||||
});
|
||||
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
|
||||
});
|
||||
|
||||
// Handle context menu action request from pepper plugin.
|
||||
webContents.on('pepper-context-menu', function(event, params) {
|
||||
var menu;
|
||||
menu = Menu.buildFromTemplate(params.menu);
|
||||
return menu.popup(params.x, params.y);
|
||||
});
|
||||
|
||||
// This error occurs when host could not be found.
|
||||
webContents.on('did-fail-provisional-load', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
|
||||
// Calling loadURL during this event might cause crash, so delay the event
|
||||
// until next tick.
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args)));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
// Delays the page-title-updated event to next tick.
|
||||
webContents.on('-page-title-updated', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args)));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
// Deprecated.
|
||||
deprecate.rename(webContents, 'loadUrl', 'loadURL');
|
||||
deprecate.rename(webContents, 'getUrl', 'getURL');
|
||||
deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return this.emit.apply(this, ['page-title-set'].concat(slice.call(args)));
|
||||
});
|
||||
return webContents.printToPDF = function(options, callback) {
|
||||
var printingSetting;
|
||||
printingSetting = {
|
||||
pageRage: [],
|
||||
mediaSize: {},
|
||||
landscape: false,
|
||||
color: 2,
|
||||
headerFooterEnabled: false,
|
||||
marginsType: 0,
|
||||
isFirstRequest: false,
|
||||
requestID: getNextId(),
|
||||
previewModifiable: true,
|
||||
printToPDF: true,
|
||||
printWithCloudPrint: false,
|
||||
printWithPrivet: false,
|
||||
printWithExtension: false,
|
||||
deviceName: "Save as PDF",
|
||||
generateDraftData: true,
|
||||
fitToPageEnabled: false,
|
||||
duplex: 0,
|
||||
copies: 1,
|
||||
collate: true,
|
||||
shouldPrintBackgrounds: false,
|
||||
shouldPrintSelectionOnly: false
|
||||
};
|
||||
if (options.landscape) {
|
||||
printingSetting.landscape = options.landscape;
|
||||
}
|
||||
if (options.marginsType) {
|
||||
printingSetting.marginsType = options.marginsType;
|
||||
}
|
||||
if (options.printSelectionOnly) {
|
||||
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
|
||||
}
|
||||
if (options.printBackground) {
|
||||
printingSetting.shouldPrintBackgrounds = options.printBackground;
|
||||
}
|
||||
if (options.pageSize && PDFPageSize[options.pageSize]) {
|
||||
printingSetting.mediaSize = PDFPageSize[options.pageSize];
|
||||
} else {
|
||||
printingSetting.mediaSize = PDFPageSize['A4'];
|
||||
}
|
||||
return this._printToPDF(printingSetting, callback);
|
||||
};
|
||||
};
|
||||
|
||||
// Wrapper for native class.
|
||||
let wrapDebugger = function(webContentsDebugger) {
|
||||
// debugger is an EventEmitter.
|
||||
webContentsDebugger.__proto__ = EventEmitter.prototype;
|
||||
};
|
||||
|
||||
binding._setWrapWebContents(wrapWebContents);
|
||||
debuggerBinding._setWrapDebugger(wrapDebugger);
|
||||
|
||||
module.exports.create = function(options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return binding.create(options);
|
||||
};
|
||||
@@ -19,11 +19,13 @@
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/prefs/pref_registry_simple.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/threading/sequenced_worker_pool.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/common/url_constants.h"
|
||||
@@ -180,8 +182,10 @@ net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() {
|
||||
void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
|
||||
pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory,
|
||||
base::FilePath());
|
||||
base::FilePath download_dir;
|
||||
PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &download_dir);
|
||||
pref_registry->RegisterFilePathPref(prefs::kDownloadDefaultDirectory,
|
||||
base::FilePath());
|
||||
download_dir);
|
||||
}
|
||||
|
||||
bool AtomBrowserContext::AllowNTLMCredentialsForDomain(const GURL& origin) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
|
||||
#include "ui/events/devices/x11/touch_factory_x11.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
@@ -116,6 +117,10 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
node_bindings_->PrepareMessageLoop();
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
#if defined(USE_X11)
|
||||
ui::TouchFactory::SetTouchDeviceListFromCommandLine();
|
||||
#endif
|
||||
|
||||
// Start idle gc.
|
||||
gc_timer_.Start(
|
||||
FROM_HERE, base::TimeDelta::FromMinutes(1),
|
||||
|
||||
@@ -125,11 +125,6 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget(
|
||||
download_manager_->GetBrowserContext());
|
||||
base::FilePath default_download_path = browser_context->prefs()->GetFilePath(
|
||||
prefs::kDownloadDefaultDirectory);
|
||||
// If users didn't set download path, use 'Downloads' directory by default.
|
||||
if (default_download_path.empty()) {
|
||||
auto path = download_manager_->GetBrowserContext()->GetPath();
|
||||
default_download_path = path.Append(FILE_PATH_LITERAL("Downloads"));
|
||||
}
|
||||
|
||||
CreateDownloadPathCallback download_path_callback =
|
||||
base::Bind(&AtomDownloadManagerDelegate::OnDownloadPathGenerated,
|
||||
|
||||
@@ -27,6 +27,10 @@ namespace ui {
|
||||
class MenuModel;
|
||||
}
|
||||
|
||||
namespace gfx {
|
||||
class Image;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class LoginHandler;
|
||||
@@ -91,6 +95,9 @@ class Browser : public WindowListObserver {
|
||||
|
||||
// Set docks' menu.
|
||||
void DockSetMenu(ui::MenuModel* model);
|
||||
|
||||
// Set docks' icon.
|
||||
void DockSetIcon(const gfx::Image& image);
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -48,8 +48,8 @@ int Browser::DockBounce(BounceType type) {
|
||||
requestUserAttention:(NSRequestUserAttentionType)type];
|
||||
}
|
||||
|
||||
void Browser::DockCancelBounce(int rid) {
|
||||
[[AtomApplication sharedApplication] cancelUserAttentionRequest:rid];
|
||||
void Browser::DockCancelBounce(int request_id) {
|
||||
[[AtomApplication sharedApplication] cancelUserAttentionRequest:request_id];
|
||||
}
|
||||
|
||||
void Browser::DockSetBadgeText(const std::string& label) {
|
||||
@@ -102,4 +102,9 @@ void Browser::DockSetMenu(ui::MenuModel* model) {
|
||||
[delegate setApplicationDockMenu:model];
|
||||
}
|
||||
|
||||
void Browser::DockSetIcon(const gfx::Image& image) {
|
||||
[[NSApplication sharedApplication]
|
||||
setApplicationIconImage:image.AsNSImage()];
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -144,23 +144,29 @@ app.once('ready', function() {
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: function() { shell.openExternal('http://electron.atom.io') }
|
||||
click: function() {
|
||||
shell.openExternal('http://electron.atom.io');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: function() {
|
||||
shell.openExternal(
|
||||
`https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme`
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: function() { shell.openExternal('https://discuss.atom.io/c/electron') }
|
||||
click: function() {
|
||||
shell.openExternal('https://discuss.atom.io/c/electron');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click: function() { shell.openExternal('https://github.com/atom/electron/issues') }
|
||||
click: function() {
|
||||
shell.openExternal('https://github.com/atom/electron/issues');
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -192,7 +198,7 @@ app.once('ready', function() {
|
||||
},
|
||||
{
|
||||
label: 'Hide Others',
|
||||
accelerator: 'Command+Shift+H',
|
||||
accelerator: 'Command+Alt+H',
|
||||
role: 'hideothers'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
electron = require 'electron'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
url = require 'url'
|
||||
|
||||
# Mapping between hostname and file path.
|
||||
hostPathMap = {}
|
||||
hostPathMapNextKey = 0
|
||||
|
||||
getHostForPath = (path) ->
|
||||
key = "extension-#{++hostPathMapNextKey}"
|
||||
hostPathMap[key] = path
|
||||
key
|
||||
|
||||
getPathForHost = (host) ->
|
||||
hostPathMap[host]
|
||||
|
||||
# Cache extensionInfo.
|
||||
extensionInfoMap = {}
|
||||
|
||||
getExtensionInfoFromPath = (srcDirectory) ->
|
||||
manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
|
||||
unless extensionInfoMap[manifest.name]?
|
||||
# We can not use 'file://' directly because all resources in the extension
|
||||
# will be treated as relative to the root in Chrome.
|
||||
page = url.format
|
||||
protocol: 'chrome-extension'
|
||||
slashes: true
|
||||
hostname: getHostForPath srcDirectory
|
||||
pathname: manifest.devtools_page
|
||||
extensionInfoMap[manifest.name] =
|
||||
startPage: page
|
||||
name: manifest.name
|
||||
srcDirectory: srcDirectory
|
||||
exposeExperimentalAPIs: true
|
||||
extensionInfoMap[manifest.name]
|
||||
|
||||
# The loaded extensions cache and its persistent path.
|
||||
loadedExtensions = null
|
||||
loadedExtensionsPath = null
|
||||
|
||||
# Persistent loaded extensions.
|
||||
{app} = electron
|
||||
app.on 'will-quit', ->
|
||||
try
|
||||
loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
|
||||
try
|
||||
fs.mkdirSync path.dirname(loadedExtensionsPath)
|
||||
catch e
|
||||
fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
|
||||
catch e
|
||||
|
||||
# We can not use protocol or BrowserWindow until app is ready.
|
||||
app.once 'ready', ->
|
||||
{protocol, BrowserWindow} = electron
|
||||
|
||||
# Load persistented extensions.
|
||||
loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'
|
||||
|
||||
try
|
||||
loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
|
||||
loadedExtensions = [] unless Array.isArray loadedExtensions
|
||||
# Preheat the extensionInfo cache.
|
||||
getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
|
||||
catch e
|
||||
|
||||
# The chrome-extension: can map a extension URL request to real file path.
|
||||
chromeExtensionHandler = (request, callback) ->
|
||||
parsed = url.parse request.url
|
||||
return callback() unless parsed.hostname and parsed.path?
|
||||
return callback() unless /extension-\d+/.test parsed.hostname
|
||||
|
||||
directory = getPathForHost parsed.hostname
|
||||
return callback() unless directory?
|
||||
callback path.join(directory, parsed.path)
|
||||
protocol.registerFileProtocol 'chrome-extension', chromeExtensionHandler, (error) ->
|
||||
console.error 'Unable to register chrome-extension protocol' if error
|
||||
|
||||
BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) ->
|
||||
@devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});"
|
||||
|
||||
BrowserWindow.addDevToolsExtension = (srcDirectory) ->
|
||||
extensionInfo = getExtensionInfoFromPath srcDirectory
|
||||
if extensionInfo
|
||||
window._loadDevToolsExtensions [extensionInfo] for window in BrowserWindow.getAllWindows()
|
||||
extensionInfo.name
|
||||
|
||||
BrowserWindow.removeDevToolsExtension = (name) ->
|
||||
delete extensionInfoMap[name]
|
||||
|
||||
# Load persistented extensions when devtools is opened.
|
||||
init = BrowserWindow::_init
|
||||
BrowserWindow::_init = ->
|
||||
init.call this
|
||||
@on 'devtools-opened', ->
|
||||
@_loadDevToolsExtensions Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key]
|
||||
141
atom/browser/lib/chrome-extension.js
Normal file
141
atom/browser/lib/chrome-extension.js
Normal file
@@ -0,0 +1,141 @@
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
|
||||
// Mapping between hostname and file path.
|
||||
var hostPathMap = {};
|
||||
var hostPathMapNextKey = 0;
|
||||
|
||||
var getHostForPath = function(path) {
|
||||
var key;
|
||||
key = "extension-" + (++hostPathMapNextKey);
|
||||
hostPathMap[key] = path;
|
||||
return key;
|
||||
};
|
||||
|
||||
var getPathForHost = function(host) {
|
||||
return hostPathMap[host];
|
||||
};
|
||||
|
||||
// Cache extensionInfo.
|
||||
var extensionInfoMap = {};
|
||||
|
||||
var getExtensionInfoFromPath = function(srcDirectory) {
|
||||
var manifest, page;
|
||||
manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json')));
|
||||
if (extensionInfoMap[manifest.name] == null) {
|
||||
|
||||
// We can not use 'file://' directly because all resources in the extension
|
||||
// will be treated as relative to the root in Chrome.
|
||||
page = url.format({
|
||||
protocol: 'chrome-extension',
|
||||
slashes: true,
|
||||
hostname: getHostForPath(srcDirectory),
|
||||
pathname: manifest.devtools_page
|
||||
});
|
||||
extensionInfoMap[manifest.name] = {
|
||||
startPage: page,
|
||||
name: manifest.name,
|
||||
srcDirectory: srcDirectory,
|
||||
exposeExperimentalAPIs: true
|
||||
};
|
||||
return extensionInfoMap[manifest.name];
|
||||
}
|
||||
};
|
||||
|
||||
// The loaded extensions cache and its persistent path.
|
||||
var loadedExtensions = null;
|
||||
var loadedExtensionsPath = null;
|
||||
|
||||
app.on('will-quit', function() {
|
||||
try {
|
||||
loadedExtensions = Object.keys(extensionInfoMap).map(function(key) {
|
||||
return extensionInfoMap[key].srcDirectory;
|
||||
});
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(loadedExtensionsPath));
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
return fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions));
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
});
|
||||
|
||||
// We can not use protocol or BrowserWindow until app is ready.
|
||||
app.once('ready', function() {
|
||||
var BrowserWindow, chromeExtensionHandler, i, init, len, protocol, srcDirectory;
|
||||
protocol = electron.protocol, BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
// Load persistented extensions.
|
||||
loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
|
||||
try {
|
||||
loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath));
|
||||
if (!Array.isArray(loadedExtensions)) {
|
||||
loadedExtensions = [];
|
||||
}
|
||||
|
||||
// Preheat the extensionInfo cache.
|
||||
for (i = 0, len = loadedExtensions.length; i < len; i++) {
|
||||
srcDirectory = loadedExtensions[i];
|
||||
getExtensionInfoFromPath(srcDirectory);
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
// The chrome-extension: can map a extension URL request to real file path.
|
||||
chromeExtensionHandler = function(request, callback) {
|
||||
var directory, parsed;
|
||||
parsed = url.parse(request.url);
|
||||
if (!(parsed.hostname && (parsed.path != null))) {
|
||||
return callback();
|
||||
}
|
||||
if (!/extension-\d+/.test(parsed.hostname)) {
|
||||
return callback();
|
||||
}
|
||||
directory = getPathForHost(parsed.hostname);
|
||||
if (directory == null) {
|
||||
return callback();
|
||||
}
|
||||
return callback(path.join(directory, parsed.path));
|
||||
};
|
||||
protocol.registerFileProtocol('chrome-extension', chromeExtensionHandler, function(error) {
|
||||
if (error) {
|
||||
return console.error('Unable to register chrome-extension protocol');
|
||||
}
|
||||
});
|
||||
BrowserWindow.prototype._loadDevToolsExtensions = function(extensionInfoArray) {
|
||||
var ref;
|
||||
return (ref = this.devToolsWebContents) != null ? ref.executeJavaScript("DevToolsAPI.addExtensions(" + (JSON.stringify(extensionInfoArray)) + ");") : void 0;
|
||||
};
|
||||
BrowserWindow.addDevToolsExtension = function(srcDirectory) {
|
||||
var extensionInfo, j, len1, ref, window;
|
||||
extensionInfo = getExtensionInfoFromPath(srcDirectory);
|
||||
if (extensionInfo) {
|
||||
ref = BrowserWindow.getAllWindows();
|
||||
for (j = 0, len1 = ref.length; j < len1; j++) {
|
||||
window = ref[j];
|
||||
window._loadDevToolsExtensions([extensionInfo]);
|
||||
}
|
||||
return extensionInfo.name;
|
||||
}
|
||||
};
|
||||
BrowserWindow.removeDevToolsExtension = function(name) {
|
||||
return delete extensionInfoMap[name];
|
||||
};
|
||||
|
||||
// Load persistented extensions when devtools is opened.
|
||||
init = BrowserWindow.prototype._init;
|
||||
return BrowserWindow.prototype._init = function() {
|
||||
init.call(this);
|
||||
return this.on('devtools-opened', function() {
|
||||
return this._loadDevToolsExtensions(Object.keys(extensionInfoMap).map(function(key) {
|
||||
return extensionInfoMap[key];
|
||||
}));
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
{ipcMain} = require 'electron'
|
||||
{desktopCapturer} = process.atomBinding 'desktop_capturer'
|
||||
|
||||
deepEqual = (opt1, opt2) ->
|
||||
return JSON.stringify(opt1) is JSON.stringify(opt2)
|
||||
|
||||
# A queue for holding all requests from renderer process.
|
||||
requestsQueue = []
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) ->
|
||||
request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender
|
||||
requestsQueue.push request
|
||||
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1
|
||||
# If the WebContents is destroyed before receiving result, just remove the
|
||||
# reference from requestsQueue to make the module not send the result to it.
|
||||
event.sender.once 'destroyed', ->
|
||||
request.webContents = null
|
||||
|
||||
desktopCapturer.emit = (event, name, sources) ->
|
||||
# Receiving sources result from main process, now send them back to renderer.
|
||||
handledRequest = requestsQueue.shift 0
|
||||
result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources)
|
||||
handledRequest.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{handledRequest.id}", result
|
||||
|
||||
# Check the queue to see whether there is other same request. If has, handle
|
||||
# it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
||||
unhandledRequestsQueue = []
|
||||
for request in requestsQueue
|
||||
if deepEqual handledRequest.options, request.options
|
||||
request.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{request.id}", errorMessage, result
|
||||
else
|
||||
unhandledRequestsQueue.push request
|
||||
requestsQueue = unhandledRequestsQueue
|
||||
# If the requestsQueue is not empty, start a new request handling.
|
||||
if requestsQueue.length > 0
|
||||
{captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
|
||||
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize
|
||||
75
atom/browser/lib/desktop-capturer.js
Normal file
75
atom/browser/lib/desktop-capturer.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer;
|
||||
|
||||
var deepEqual = function(opt1, opt2) {
|
||||
return JSON.stringify(opt1) === JSON.stringify(opt2);
|
||||
};
|
||||
|
||||
// A queue for holding all requests from renderer process.
|
||||
var requestsQueue = [];
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function(event, captureWindow, captureScreen, thumbnailSize, id) {
|
||||
var request;
|
||||
request = {
|
||||
id: id,
|
||||
options: {
|
||||
captureWindow: captureWindow,
|
||||
captureScreen: captureScreen,
|
||||
thumbnailSize: thumbnailSize
|
||||
},
|
||||
webContents: event.sender
|
||||
};
|
||||
requestsQueue.push(request);
|
||||
if (requestsQueue.length === 1) {
|
||||
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
|
||||
}
|
||||
|
||||
// If the WebContents is destroyed before receiving result, just remove the
|
||||
// reference from requestsQueue to make the module not send the result to it.
|
||||
return event.sender.once('destroyed', function() {
|
||||
return request.webContents = null;
|
||||
});
|
||||
});
|
||||
|
||||
desktopCapturer.emit = function(event, name, sources) {
|
||||
// Receiving sources result from main process, now send them back to renderer.
|
||||
var captureScreen, captureWindow, handledRequest, i, len, ref, ref1, ref2, request, result, source, thumbnailSize, unhandledRequestsQueue;
|
||||
handledRequest = requestsQueue.shift(0);
|
||||
result = (function() {
|
||||
var i, len, results;
|
||||
results = [];
|
||||
for (i = 0, len = sources.length; i < len; i++) {
|
||||
source = sources[i];
|
||||
results.push({
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
thumbnail: source.thumbnail.toDataUrl()
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})();
|
||||
if ((ref = handledRequest.webContents) != null) {
|
||||
ref.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + handledRequest.id, result);
|
||||
}
|
||||
|
||||
// Check the queue to see whether there is other same request. If has, handle
|
||||
// it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
||||
unhandledRequestsQueue = [];
|
||||
for (i = 0, len = requestsQueue.length; i < len; i++) {
|
||||
request = requestsQueue[i];
|
||||
if (deepEqual(handledRequest.options, request.options)) {
|
||||
if ((ref1 = request.webContents) != null) {
|
||||
ref1.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + request.id, result);
|
||||
}
|
||||
} else {
|
||||
unhandledRequestsQueue.push(request);
|
||||
}
|
||||
}
|
||||
requestsQueue = unhandledRequestsQueue;
|
||||
|
||||
// If the requestsQueue is not empty, start a new request handling.
|
||||
if (requestsQueue.length > 0) {
|
||||
ref2 = requestsQueue[0].options, captureWindow = ref2.captureWindow, captureScreen = ref2.captureScreen, thumbnailSize = ref2.thumbnailSize;
|
||||
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
|
||||
}
|
||||
};
|
||||
@@ -1,174 +0,0 @@
|
||||
{ipcMain, webContents} = require 'electron'
|
||||
|
||||
webViewManager = null # Doesn't exist in early initialization.
|
||||
|
||||
supportedWebViewEvents = [
|
||||
'load-commit'
|
||||
'did-finish-load'
|
||||
'did-fail-load'
|
||||
'did-frame-finish-load'
|
||||
'did-start-loading'
|
||||
'did-stop-loading'
|
||||
'did-get-response-details'
|
||||
'did-get-redirect-request'
|
||||
'dom-ready'
|
||||
'console-message'
|
||||
'devtools-opened'
|
||||
'devtools-closed'
|
||||
'devtools-focused'
|
||||
'new-window'
|
||||
'will-navigate'
|
||||
'did-navigate'
|
||||
'did-navigate-in-page'
|
||||
'close'
|
||||
'crashed'
|
||||
'gpu-crashed'
|
||||
'plugin-crashed'
|
||||
'destroyed'
|
||||
'page-title-updated'
|
||||
'page-favicon-updated'
|
||||
'enter-html-full-screen'
|
||||
'leave-html-full-screen'
|
||||
'media-started-playing'
|
||||
'media-paused'
|
||||
'found-in-page'
|
||||
'did-change-theme-color'
|
||||
]
|
||||
|
||||
nextInstanceId = 0
|
||||
guestInstances = {}
|
||||
embedderElementsMap = {}
|
||||
reverseEmbedderElementsMap = {}
|
||||
|
||||
# Moves the last element of array to the first one.
|
||||
moveLastToFirst = (list) ->
|
||||
list.unshift list.pop()
|
||||
|
||||
# Generate guestInstanceId.
|
||||
getNextInstanceId = (webContents) ->
|
||||
++nextInstanceId
|
||||
|
||||
# Create a new guest instance.
|
||||
createGuest = (embedder, params) ->
|
||||
webViewManager ?= process.atomBinding 'web_view_manager'
|
||||
|
||||
id = getNextInstanceId embedder
|
||||
guest = webContents.create {isGuest: true, partition: params.partition, embedder}
|
||||
guestInstances[id] = {guest, embedder}
|
||||
|
||||
# Destroy guest when the embedder is gone or navigated.
|
||||
destroyEvents = ['destroyed', 'crashed', 'did-navigate']
|
||||
destroy = ->
|
||||
destroyGuest embedder, id if guestInstances[id]?
|
||||
for event in destroyEvents
|
||||
embedder.once event, destroy
|
||||
# Users might also listen to the crashed event, so We must ensure the guest
|
||||
# is destroyed before users' listener gets called. It is done by moving our
|
||||
# listener to the first one in queue.
|
||||
listeners = embedder._events[event]
|
||||
moveLastToFirst listeners if Array.isArray listeners
|
||||
guest.once 'destroyed', ->
|
||||
embedder.removeListener event, destroy for event in destroyEvents
|
||||
|
||||
# Init guest web view after attached.
|
||||
guest.once 'did-attach', ->
|
||||
params = @attachParams
|
||||
delete @attachParams
|
||||
|
||||
@viewInstanceId = params.instanceId
|
||||
@setSize
|
||||
normal:
|
||||
width: params.elementWidth, height: params.elementHeight
|
||||
enableAutoSize: params.autosize
|
||||
min:
|
||||
width: params.minwidth, height: params.minheight
|
||||
max:
|
||||
width: params.maxwidth, height: params.maxheight
|
||||
|
||||
if params.src
|
||||
opts = {}
|
||||
opts.httpReferrer = params.httpreferrer if params.httpreferrer
|
||||
opts.userAgent = params.useragent if params.useragent
|
||||
@loadURL params.src, opts
|
||||
|
||||
if params.allowtransparency?
|
||||
@setAllowTransparency params.allowtransparency
|
||||
|
||||
guest.allowPopups = params.allowpopups
|
||||
|
||||
# Dispatch events to embedder.
|
||||
for event in supportedWebViewEvents
|
||||
do (event) ->
|
||||
guest.on event, (_, args...) ->
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args...
|
||||
|
||||
# Dispatch guest's IPC messages to embedder.
|
||||
guest.on 'ipc-message-host', (_, packed) ->
|
||||
[channel, args...] = packed
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{guest.viewInstanceId}", channel, args...
|
||||
|
||||
# Autosize.
|
||||
guest.on 'size-changed', (_, args...) ->
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{guest.viewInstanceId}", args...
|
||||
|
||||
id
|
||||
|
||||
# Attach the guest to an element of embedder.
|
||||
attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
|
||||
guest = guestInstances[guestInstanceId].guest
|
||||
|
||||
# Destroy the old guest when attaching.
|
||||
key = "#{embedder.getId()}-#{elementInstanceId}"
|
||||
oldGuestInstanceId = embedderElementsMap[key]
|
||||
if oldGuestInstanceId?
|
||||
# Reattachment to the same guest is not currently supported.
|
||||
return unless oldGuestInstanceId != guestInstanceId
|
||||
|
||||
return unless guestInstances[oldGuestInstanceId]?
|
||||
destroyGuest embedder, oldGuestInstanceId
|
||||
|
||||
webPreferences =
|
||||
guestInstanceId: guestInstanceId
|
||||
nodeIntegration: params.nodeintegration ? false
|
||||
plugins: params.plugins
|
||||
webSecurity: !params.disablewebsecurity
|
||||
webPreferences.preloadURL = params.preload if params.preload
|
||||
webViewManager.addGuest guestInstanceId, elementInstanceId, embedder, guest, webPreferences
|
||||
|
||||
guest.attachParams = params
|
||||
embedderElementsMap[key] = guestInstanceId
|
||||
reverseEmbedderElementsMap[guestInstanceId] = key
|
||||
|
||||
# Destroy an existing guest instance.
|
||||
destroyGuest = (embedder, id) ->
|
||||
webViewManager.removeGuest embedder, id
|
||||
guestInstances[id].guest.destroy()
|
||||
delete guestInstances[id]
|
||||
|
||||
key = reverseEmbedderElementsMap[id]
|
||||
if key?
|
||||
delete reverseEmbedderElementsMap[id]
|
||||
delete embedderElementsMap[key]
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
|
||||
event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
|
||||
attachGuest event.sender, elementInstanceId, guestInstanceId, params
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
|
||||
destroyGuest event.sender, id
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
|
||||
guestInstances[id]?.guest.setSize params
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
|
||||
guestInstances[id]?.guest.setAllowTransparency allowtransparency
|
||||
|
||||
# Returns WebContents from its guest id.
|
||||
exports.getGuest = (id) ->
|
||||
guestInstances[id]?.guest
|
||||
|
||||
# Returns the embedder of the guest.
|
||||
exports.getEmbedder = (id) ->
|
||||
guestInstances[id]?.embedder
|
||||
216
atom/browser/lib/guest-view-manager.js
Normal file
216
atom/browser/lib/guest-view-manager.js
Normal file
@@ -0,0 +1,216 @@
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const webContents = require('electron').webContents;
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
// Doesn't exist in early initialization.
|
||||
var webViewManager = null;
|
||||
|
||||
var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color'];
|
||||
|
||||
var nextInstanceId = 0;
|
||||
var guestInstances = {};
|
||||
var embedderElementsMap = {};
|
||||
var reverseEmbedderElementsMap = {};
|
||||
|
||||
// Moves the last element of array to the first one.
|
||||
var moveLastToFirst = function(list) {
|
||||
return list.unshift(list.pop());
|
||||
};
|
||||
|
||||
// Generate guestInstanceId.
|
||||
var getNextInstanceId = function() {
|
||||
return ++nextInstanceId;
|
||||
};
|
||||
|
||||
// Create a new guest instance.
|
||||
var createGuest = function(embedder, params) {
|
||||
var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners;
|
||||
if (webViewManager == null) {
|
||||
webViewManager = process.atomBinding('web_view_manager');
|
||||
}
|
||||
id = getNextInstanceId(embedder);
|
||||
guest = webContents.create({
|
||||
isGuest: true,
|
||||
partition: params.partition,
|
||||
embedder: embedder
|
||||
});
|
||||
guestInstances[id] = {
|
||||
guest: guest,
|
||||
embedder: embedder
|
||||
};
|
||||
|
||||
// Destroy guest when the embedder is gone or navigated.
|
||||
destroyEvents = ['will-destroy', 'crashed', 'did-navigate'];
|
||||
destroy = function() {
|
||||
if (guestInstances[id] != null) {
|
||||
return destroyGuest(embedder, id);
|
||||
}
|
||||
};
|
||||
for (i = 0, len = destroyEvents.length; i < len; i++) {
|
||||
event = destroyEvents[i];
|
||||
embedder.once(event, destroy);
|
||||
|
||||
// Users might also listen to the crashed event, so We must ensure the guest
|
||||
// is destroyed before users' listener gets called. It is done by moving our
|
||||
// listener to the first one in queue.
|
||||
listeners = embedder._events[event];
|
||||
if (Array.isArray(listeners)) {
|
||||
moveLastToFirst(listeners);
|
||||
}
|
||||
}
|
||||
guest.once('destroyed', function() {
|
||||
var j, len1, results;
|
||||
results = [];
|
||||
for (j = 0, len1 = destroyEvents.length; j < len1; j++) {
|
||||
event = destroyEvents[j];
|
||||
results.push(embedder.removeListener(event, destroy));
|
||||
}
|
||||
return results;
|
||||
});
|
||||
|
||||
// Init guest web view after attached.
|
||||
guest.once('did-attach', function() {
|
||||
var opts;
|
||||
params = this.attachParams;
|
||||
delete this.attachParams;
|
||||
this.viewInstanceId = params.instanceId;
|
||||
this.setSize({
|
||||
normal: {
|
||||
width: params.elementWidth,
|
||||
height: params.elementHeight
|
||||
},
|
||||
enableAutoSize: params.autosize,
|
||||
min: {
|
||||
width: params.minwidth,
|
||||
height: params.minheight
|
||||
},
|
||||
max: {
|
||||
width: params.maxwidth,
|
||||
height: params.maxheight
|
||||
}
|
||||
});
|
||||
if (params.src) {
|
||||
opts = {};
|
||||
if (params.httpreferrer) {
|
||||
opts.httpReferrer = params.httpreferrer;
|
||||
}
|
||||
if (params.useragent) {
|
||||
opts.userAgent = params.useragent;
|
||||
}
|
||||
this.loadURL(params.src, opts);
|
||||
}
|
||||
if (params.allowtransparency != null) {
|
||||
this.setAllowTransparency(params.allowtransparency);
|
||||
}
|
||||
return guest.allowPopups = params.allowpopups;
|
||||
});
|
||||
|
||||
// Dispatch events to embedder.
|
||||
fn = function(event) {
|
||||
return guest.on(event, function() {
|
||||
var args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args)));
|
||||
});
|
||||
};
|
||||
for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) {
|
||||
event = supportedWebViewEvents[j];
|
||||
fn(event);
|
||||
}
|
||||
|
||||
// Dispatch guest's IPC messages to embedder.
|
||||
guest.on('ipc-message-host', function(_, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + guest.viewInstanceId, channel].concat(slice.call(args)));
|
||||
});
|
||||
|
||||
// Autosize.
|
||||
guest.on('size-changed', function() {
|
||||
var args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args)));
|
||||
});
|
||||
return id;
|
||||
};
|
||||
|
||||
// Attach the guest to an element of embedder.
|
||||
var attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) {
|
||||
var guest, key, oldGuestInstanceId, ref1, webPreferences;
|
||||
guest = guestInstances[guestInstanceId].guest;
|
||||
|
||||
// Destroy the old guest when attaching.
|
||||
key = (embedder.getId()) + "-" + elementInstanceId;
|
||||
oldGuestInstanceId = embedderElementsMap[key];
|
||||
if (oldGuestInstanceId != null) {
|
||||
|
||||
// Reattachment to the same guest is not currently supported.
|
||||
if (oldGuestInstanceId === guestInstanceId) {
|
||||
return;
|
||||
}
|
||||
if (guestInstances[oldGuestInstanceId] == null) {
|
||||
return;
|
||||
}
|
||||
destroyGuest(embedder, oldGuestInstanceId);
|
||||
}
|
||||
webPreferences = {
|
||||
guestInstanceId: guestInstanceId,
|
||||
nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
|
||||
plugins: params.plugins,
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
blinkFeatures: params.blinkfeatures
|
||||
};
|
||||
if (params.preload) {
|
||||
webPreferences.preloadURL = params.preload;
|
||||
}
|
||||
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
|
||||
guest.attachParams = params;
|
||||
embedderElementsMap[key] = guestInstanceId;
|
||||
return reverseEmbedderElementsMap[guestInstanceId] = key;
|
||||
};
|
||||
|
||||
// Destroy an existing guest instance.
|
||||
var destroyGuest = function(embedder, id) {
|
||||
var key;
|
||||
webViewManager.removeGuest(embedder, id);
|
||||
guestInstances[id].guest.destroy();
|
||||
delete guestInstances[id];
|
||||
key = reverseEmbedderElementsMap[id];
|
||||
if (key != null) {
|
||||
delete reverseEmbedderElementsMap[id];
|
||||
return delete embedderElementsMap[key];
|
||||
}
|
||||
};
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', function(event, params, requestId) {
|
||||
return event.sender.send("ATOM_SHELL_RESPONSE_" + requestId, createGuest(event.sender, params));
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', function(event, elementInstanceId, guestInstanceId, params) {
|
||||
return attachGuest(event.sender, elementInstanceId, guestInstanceId, params);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) {
|
||||
return destroyGuest(event.sender, id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(event, id, allowtransparency) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0;
|
||||
});
|
||||
|
||||
// Returns WebContents from its guest id.
|
||||
exports.getGuest = function(id) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0;
|
||||
};
|
||||
|
||||
// Returns the embedder of the guest.
|
||||
exports.getEmbedder = function(id) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0;
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
{ipcMain, BrowserWindow} = require 'electron'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
frameToGuest = {}
|
||||
|
||||
# Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||
mergeOptions = (child, parent) ->
|
||||
for own key, value of parent when key not of child
|
||||
if typeof value is 'object'
|
||||
child[key] = mergeOptions {}, value
|
||||
else
|
||||
child[key] = value
|
||||
child
|
||||
|
||||
# Merge |options| with the |embedder|'s window's options.
|
||||
mergeBrowserWindowOptions = (embedder, options) ->
|
||||
if embedder.browserWindowOptions?
|
||||
# Inherit the original options if it is a BrowserWindow.
|
||||
mergeOptions options, embedder.browserWindowOptions
|
||||
else
|
||||
# Or only inherit web-preferences if it is a webview.
|
||||
options.webPreferences ?= {}
|
||||
mergeOptions options.webPreferences, embedder.getWebPreferences()
|
||||
options
|
||||
|
||||
# Create a new guest created by |embedder| with |options|.
|
||||
createGuest = (embedder, url, frameName, options) ->
|
||||
guest = frameToGuest[frameName]
|
||||
if frameName and guest?
|
||||
guest.loadURL url
|
||||
return guest.id
|
||||
|
||||
# Remember the embedder window's id.
|
||||
options.webPreferences ?= {}
|
||||
options.webPreferences.openerId = BrowserWindow.fromWebContents(embedder)?.id
|
||||
|
||||
guest = new BrowserWindow(options)
|
||||
guest.loadURL url
|
||||
|
||||
# When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
# guest is closed by user then we should prevent |embedder| from double
|
||||
# closing guest.
|
||||
guestId = guest.id
|
||||
closedByEmbedder = ->
|
||||
guest.removeListener 'closed', closedByUser
|
||||
guest.destroy()
|
||||
closedByUser = ->
|
||||
embedder.send "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{guestId}"
|
||||
embedder.removeListener 'render-view-deleted', closedByEmbedder
|
||||
embedder.once 'render-view-deleted', closedByEmbedder
|
||||
guest.once 'closed', closedByUser
|
||||
|
||||
if frameName
|
||||
frameToGuest[frameName] = guest
|
||||
guest.frameName = frameName
|
||||
guest.once 'closed', ->
|
||||
delete frameToGuest[frameName]
|
||||
|
||||
guest.id
|
||||
|
||||
# Routed window.open messages.
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
|
||||
[url, frameName, options] = args
|
||||
options = mergeBrowserWindowOptions event.sender, options
|
||||
event.sender.emit 'new-window', event, url, frameName, 'new-window', options
|
||||
if (event.sender.isGuest() and not event.sender.allowPopups) or event.defaultPrevented
|
||||
event.returnValue = null
|
||||
else
|
||||
event.returnValue = createGuest event.sender, url, frameName, options
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) ->
|
||||
BrowserWindow.fromId(guestId)?.destroy()
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestId, method, args...) ->
|
||||
BrowserWindow.fromId(guestId)?[method] args...
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestId, message, targetOrigin, sourceOrigin) ->
|
||||
sourceId = BrowserWindow.fromWebContents(event.sender)?.id
|
||||
return unless sourceId?
|
||||
|
||||
guestContents = BrowserWindow.fromId(guestId)?.webContents
|
||||
if guestContents?.getURL().indexOf(targetOrigin) is 0 or targetOrigin is '*'
|
||||
guestContents?.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestId, method, args...) ->
|
||||
BrowserWindow.fromId(guestId)?.webContents?[method] args...
|
||||
124
atom/browser/lib/guest-window-manager.js
Normal file
124
atom/browser/lib/guest-window-manager.js
Normal file
@@ -0,0 +1,124 @@
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const BrowserWindow = require('electron').BrowserWindow;
|
||||
|
||||
var hasProp = {}.hasOwnProperty;
|
||||
var slice = [].slice;
|
||||
var frameToGuest = {};
|
||||
|
||||
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||
var mergeOptions = function(child, parent) {
|
||||
var key, value;
|
||||
for (key in parent) {
|
||||
if (!hasProp.call(parent, key)) continue;
|
||||
value = parent[key];
|
||||
if (!(key in child)) {
|
||||
if (typeof value === 'object') {
|
||||
child[key] = mergeOptions({}, value);
|
||||
} else {
|
||||
child[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return child;
|
||||
};
|
||||
|
||||
// Merge |options| with the |embedder|'s window's options.
|
||||
var mergeBrowserWindowOptions = function(embedder, options) {
|
||||
if (embedder.browserWindowOptions != null) {
|
||||
|
||||
// Inherit the original options if it is a BrowserWindow.
|
||||
mergeOptions(options, embedder.browserWindowOptions);
|
||||
} else {
|
||||
|
||||
// Or only inherit web-preferences if it is a webview.
|
||||
if (options.webPreferences == null) {
|
||||
options.webPreferences = {};
|
||||
}
|
||||
mergeOptions(options.webPreferences, embedder.getWebPreferences());
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
// Create a new guest created by |embedder| with |options|.
|
||||
var createGuest = function(embedder, url, frameName, options) {
|
||||
var closedByEmbedder, closedByUser, guest, guestId, ref1;
|
||||
guest = frameToGuest[frameName];
|
||||
if (frameName && (guest != null)) {
|
||||
guest.loadURL(url);
|
||||
return guest.id;
|
||||
}
|
||||
|
||||
// Remember the embedder window's id.
|
||||
if (options.webPreferences == null) {
|
||||
options.webPreferences = {};
|
||||
}
|
||||
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0;
|
||||
guest = new BrowserWindow(options);
|
||||
guest.loadURL(url);
|
||||
|
||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
// guest is closed by user then we should prevent |embedder| from double
|
||||
// closing guest.
|
||||
guestId = guest.id;
|
||||
closedByEmbedder = function() {
|
||||
guest.removeListener('closed', closedByUser);
|
||||
return guest.destroy();
|
||||
};
|
||||
closedByUser = function() {
|
||||
embedder.send("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + guestId);
|
||||
return embedder.removeListener('render-view-deleted', closedByEmbedder);
|
||||
};
|
||||
embedder.once('render-view-deleted', closedByEmbedder);
|
||||
guest.once('closed', closedByUser);
|
||||
if (frameName) {
|
||||
frameToGuest[frameName] = guest;
|
||||
guest.frameName = frameName;
|
||||
guest.once('closed', function() {
|
||||
return delete frameToGuest[frameName];
|
||||
});
|
||||
}
|
||||
return guest.id;
|
||||
};
|
||||
|
||||
// Routed window.open messages.
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function() {
|
||||
var args, event, frameName, options, url;
|
||||
event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
url = args[0], frameName = args[1], options = args[2];
|
||||
options = mergeBrowserWindowOptions(event.sender, options);
|
||||
event.sender.emit('new-window', event, url, frameName, 'new-window', options);
|
||||
if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
|
||||
return event.returnValue = null;
|
||||
} else {
|
||||
return event.returnValue = createGuest(event.sender, url, frameName, options);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function(event, guestId) {
|
||||
var ref1;
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function() {
|
||||
var args, guestId, method, ref1;
|
||||
guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function(event, guestId, message, targetOrigin, sourceOrigin) {
|
||||
var guestContents, ref1, ref2, sourceId;
|
||||
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0;
|
||||
if (sourceId == null) {
|
||||
return;
|
||||
}
|
||||
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0;
|
||||
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
|
||||
return guestContents != null ? guestContents.send('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function() {
|
||||
var args, guestId, method, ref1, ref2;
|
||||
guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0;
|
||||
});
|
||||
@@ -1,118 +0,0 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
util = require 'util'
|
||||
Module = require 'module'
|
||||
|
||||
# We modified the original process.argv to let node.js load the atom.js,
|
||||
# we need to restore it here.
|
||||
process.argv.splice 1, 1
|
||||
|
||||
# Clear search paths.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')
|
||||
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
|
||||
|
||||
globalPaths = Module.globalPaths
|
||||
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
|
||||
|
||||
# Expose public APIs.
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib', 'exports')
|
||||
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle console output when running as GUI program.
|
||||
consoleLog = (args...) ->
|
||||
process.log util.format(args...) + "\n"
|
||||
streamWrite = (chunk, encoding, callback) ->
|
||||
chunk = chunk.toString(encoding) if Buffer.isBuffer chunk
|
||||
process.log chunk
|
||||
callback() if callback
|
||||
true
|
||||
console.log = console.error = console.warn = consoleLog
|
||||
process.stdout.write = process.stderr.write = streamWrite
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
|
||||
# Don't quit on fatal error.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
# Do nothing if the user has a custom uncaught exception handler.
|
||||
if process.listeners('uncaughtException').length > 1
|
||||
return
|
||||
|
||||
# Show error in GUI.
|
||||
{dialog} = require 'electron'
|
||||
stack = error.stack ? "#{error.name}: #{error.message}"
|
||||
message = "Uncaught Exception:\n#{stack}"
|
||||
dialog.showErrorBox 'A JavaScript error occurred in the main process', message
|
||||
|
||||
# Emit 'exit' event on quit.
|
||||
{app} = require 'electron'
|
||||
app.on 'quit', (event, exitCode) ->
|
||||
process.emit 'exit', exitCode
|
||||
|
||||
# Map process.exit to app.exit, which quits gracefully.
|
||||
process.exit = app.exit
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc-server'
|
||||
|
||||
# Load the guest view manager.
|
||||
require './guest-view-manager'
|
||||
require './guest-window-manager'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
break
|
||||
catch e
|
||||
continue
|
||||
|
||||
unless packageJson?
|
||||
process.nextTick -> process.exit 1
|
||||
throw new Error("Unable to find a valid app")
|
||||
|
||||
# Set application's version.
|
||||
app.setVersion packageJson.version if packageJson.version?
|
||||
|
||||
# Set application's name.
|
||||
if packageJson.productName?
|
||||
app.setName packageJson.productName
|
||||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
|
||||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
app.setDesktopName "#{app.getName()}.desktop"
|
||||
|
||||
# Chrome 42 disables NPAPI plugins by default, reenable them here
|
||||
app.commandLine.appendSwitch 'enable-npapi'
|
||||
|
||||
# Set the user path according to application's name.
|
||||
app.setPath 'userData', path.join(app.getPath('appData'), app.getName())
|
||||
app.setPath 'userCache', path.join(app.getPath('cache'), app.getName())
|
||||
app.setAppPath packagePath
|
||||
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension'
|
||||
|
||||
# Load internal desktop-capturer module.
|
||||
require './desktop-capturer'
|
||||
|
||||
# Set main startup script of the app.
|
||||
mainStartupScript = packageJson.main or 'index.js'
|
||||
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
Module._load path.join(packagePath, mainStartupScript), Module, true
|
||||
152
atom/browser/lib/init.js
Normal file
152
atom/browser/lib/init.js
Normal file
@@ -0,0 +1,152 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const Module = require('module');
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
// We modified the original process.argv to let node.js load the atom.js,
|
||||
// we need to restore it here.
|
||||
process.argv.splice(1, 1);
|
||||
|
||||
// Clear search paths.
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths'));
|
||||
|
||||
// Import common settings.
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init'));
|
||||
|
||||
var globalPaths = Module.globalPaths;
|
||||
|
||||
if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) {
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib'));
|
||||
}
|
||||
|
||||
// Expose public APIs.
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', 'exports'));
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// Redirect node's console to use our own implementations, since node can not
|
||||
// handle console output when running as GUI program.
|
||||
var consoleLog = function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return process.log(util.format.apply(util, args) + "\n");
|
||||
};
|
||||
var streamWrite = function(chunk, encoding, callback) {
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
chunk = chunk.toString(encoding);
|
||||
}
|
||||
process.log(chunk);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
console.log = console.error = console.warn = consoleLog;
|
||||
process.stdout.write = process.stderr.write = streamWrite;
|
||||
|
||||
// Always returns EOF for stdin stream.
|
||||
var Readable = require('stream').Readable;
|
||||
var stdin = new Readable;
|
||||
stdin.push(null);
|
||||
process.__defineGetter__('stdin', function() {
|
||||
return stdin;
|
||||
});
|
||||
}
|
||||
|
||||
// Don't quit on fatal error.
|
||||
process.on('uncaughtException', function(error) {
|
||||
|
||||
// Do nothing if the user has a custom uncaught exception handler.
|
||||
var dialog, message, ref, stack;
|
||||
if (process.listeners('uncaughtException').length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show error in GUI.
|
||||
dialog = require('electron').dialog;
|
||||
stack = (ref = error.stack) != null ? ref : error.name + ": " + error.message;
|
||||
message = "Uncaught Exception:\n" + stack;
|
||||
return dialog.showErrorBox('A JavaScript error occurred in the main process', message);
|
||||
});
|
||||
|
||||
// Emit 'exit' event on quit.
|
||||
var app = require('electron').app;
|
||||
|
||||
app.on('quit', function(event, exitCode) {
|
||||
return process.emit('exit', exitCode);
|
||||
});
|
||||
|
||||
// Map process.exit to app.exit, which quits gracefully.
|
||||
process.exit = app.exit;
|
||||
|
||||
// Load the RPC server.
|
||||
require('./rpc-server');
|
||||
|
||||
// Load the guest view manager.
|
||||
require('./guest-view-manager');
|
||||
|
||||
require('./guest-window-manager');
|
||||
|
||||
// Now we try to load app's package.json.
|
||||
var packageJson = null;
|
||||
var searchPaths = ['app', 'app.asar', 'default_app'];
|
||||
var i, len, packagePath;
|
||||
for (i = 0, len = searchPaths.length; i < len; i++) {
|
||||
packagePath = searchPaths[i];
|
||||
try {
|
||||
packagePath = path.join(process.resourcesPath, packagePath);
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')));
|
||||
break;
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (packageJson == null) {
|
||||
process.nextTick(function() {
|
||||
return process.exit(1);
|
||||
});
|
||||
throw new Error("Unable to find a valid app");
|
||||
}
|
||||
|
||||
// Set application's version.
|
||||
if (packageJson.version != null) {
|
||||
app.setVersion(packageJson.version);
|
||||
}
|
||||
|
||||
// Set application's name.
|
||||
if (packageJson.productName != null) {
|
||||
app.setName(packageJson.productName);
|
||||
} else if (packageJson.name != null) {
|
||||
app.setName(packageJson.name);
|
||||
}
|
||||
|
||||
// Set application's desktop name.
|
||||
if (packageJson.desktopName != null) {
|
||||
app.setDesktopName(packageJson.desktopName);
|
||||
} else {
|
||||
app.setDesktopName((app.getName()) + ".desktop");
|
||||
}
|
||||
|
||||
// Chrome 42 disables NPAPI plugins by default, reenable them here
|
||||
app.commandLine.appendSwitch('enable-npapi');
|
||||
|
||||
// Set the user path according to application's name.
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
|
||||
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()));
|
||||
|
||||
app.setAppPath(packagePath);
|
||||
|
||||
// Load the chrome extension support.
|
||||
require('./chrome-extension');
|
||||
|
||||
// Load internal desktop-capturer module.
|
||||
require('./desktop-capturer');
|
||||
|
||||
// Set main startup script of the app.
|
||||
var mainStartupScript = packageJson.main || 'index.js';
|
||||
|
||||
// Finally load app's main.js and transfer control to C++.
|
||||
Module._load(path.join(packagePath, mainStartupScript), Module, true);
|
||||
@@ -1,67 +0,0 @@
|
||||
{EventEmitter} = require 'events'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
class ObjectsRegistry extends EventEmitter
|
||||
constructor: ->
|
||||
@setMaxListeners Number.MAX_VALUE
|
||||
@nextId = 0
|
||||
|
||||
# Stores all objects by ref-counting.
|
||||
# (id) => {object, count}
|
||||
@storage = {}
|
||||
|
||||
# Stores the IDs of objects referenced by WebContents.
|
||||
# (webContentsId) => {(id) => (count)}
|
||||
@owners = {}
|
||||
|
||||
# Register a new object, the object would be kept referenced until you release
|
||||
# it explicitly.
|
||||
add: (webContentsId, obj) ->
|
||||
id = @saveToStorage obj
|
||||
# Remember the owner.
|
||||
@owners[webContentsId] ?= {}
|
||||
@owners[webContentsId][id] ?= 0
|
||||
@owners[webContentsId][id]++
|
||||
# Returns object's id
|
||||
id
|
||||
|
||||
# Get an object according to its ID.
|
||||
get: (id) ->
|
||||
@storage[id]?.object
|
||||
|
||||
# Dereference an object according to its ID.
|
||||
remove: (webContentsId, id) ->
|
||||
@dereference id, 1
|
||||
# Also reduce the count in owner.
|
||||
pointer = @owners[webContentsId]
|
||||
return unless pointer?
|
||||
--pointer[id]
|
||||
delete pointer[id] if pointer[id] is 0
|
||||
|
||||
# Clear all references to objects refrenced by the WebContents.
|
||||
clear: (webContentsId) ->
|
||||
@emit "clear-#{webContentsId}"
|
||||
return unless @owners[webContentsId]?
|
||||
@dereference id, count for id, count of @owners[webContentsId]
|
||||
delete @owners[webContentsId]
|
||||
|
||||
# Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage: (object) ->
|
||||
id = v8Util.getHiddenValue object, 'atomId'
|
||||
unless id
|
||||
id = ++@nextId
|
||||
@storage[id] = {count: 0, object}
|
||||
v8Util.setHiddenValue object, 'atomId', id
|
||||
++@storage[id].count
|
||||
id
|
||||
|
||||
# Private: Dereference the object from store.
|
||||
dereference: (id, count) ->
|
||||
pointer = @storage[id]
|
||||
return unless pointer?
|
||||
pointer.count -= count
|
||||
if pointer.count is 0
|
||||
v8Util.deleteHiddenValue pointer.object, 'atomId'
|
||||
delete @storage[id]
|
||||
|
||||
module.exports = new ObjectsRegistry
|
||||
109
atom/browser/lib/objects-registry.js
Normal file
109
atom/browser/lib/objects-registry.js
Normal file
@@ -0,0 +1,109 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const v8Util = process.atomBinding('v8_util');
|
||||
|
||||
class ObjectsRegistry extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.setMaxListeners(Number.MAX_VALUE);
|
||||
this.nextId = 0;
|
||||
|
||||
// Stores all objects by ref-counting.
|
||||
// (id) => {object, count}
|
||||
this.storage = {};
|
||||
|
||||
// Stores the IDs of objects referenced by WebContents.
|
||||
// (webContentsId) => {(id) => (count)}
|
||||
this.owners = {};
|
||||
}
|
||||
|
||||
// Register a new object, the object would be kept referenced until you release
|
||||
// it explicitly.
|
||||
add(webContentsId, obj) {
|
||||
var base, base1, id;
|
||||
id = this.saveToStorage(obj);
|
||||
|
||||
// Remember the owner.
|
||||
if ((base = this.owners)[webContentsId] == null) {
|
||||
base[webContentsId] = {};
|
||||
}
|
||||
if ((base1 = this.owners[webContentsId])[id] == null) {
|
||||
base1[id] = 0;
|
||||
}
|
||||
this.owners[webContentsId][id]++;
|
||||
|
||||
// Returns object's id
|
||||
return id;
|
||||
}
|
||||
|
||||
// Get an object according to its ID.
|
||||
get(id) {
|
||||
var ref;
|
||||
return (ref = this.storage[id]) != null ? ref.object : void 0;
|
||||
}
|
||||
|
||||
// Dereference an object according to its ID.
|
||||
remove(webContentsId, id) {
|
||||
var pointer;
|
||||
this.dereference(id, 1);
|
||||
|
||||
// Also reduce the count in owner.
|
||||
pointer = this.owners[webContentsId];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
--pointer[id];
|
||||
if (pointer[id] === 0) {
|
||||
return delete pointer[id];
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all references to objects refrenced by the WebContents.
|
||||
clear(webContentsId) {
|
||||
var count, id, ref;
|
||||
this.emit("clear-" + webContentsId);
|
||||
if (this.owners[webContentsId] == null) {
|
||||
return;
|
||||
}
|
||||
ref = this.owners[webContentsId];
|
||||
for (id in ref) {
|
||||
count = ref[id];
|
||||
this.dereference(id, count);
|
||||
}
|
||||
return delete this.owners[webContentsId];
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage(object) {
|
||||
var id;
|
||||
id = v8Util.getHiddenValue(object, 'atomId');
|
||||
if (!id) {
|
||||
id = ++this.nextId;
|
||||
this.storage[id] = {
|
||||
count: 0,
|
||||
object: object
|
||||
};
|
||||
v8Util.setHiddenValue(object, 'atomId', id);
|
||||
}
|
||||
++this.storage[id].count;
|
||||
return id;
|
||||
}
|
||||
|
||||
// Private: Dereference the object from store.
|
||||
dereference(id, count) {
|
||||
var pointer;
|
||||
pointer = this.storage[id];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
pointer.count -= count;
|
||||
if (pointer.count === 0) {
|
||||
v8Util.deleteHiddenValue(pointer.object, 'atomId');
|
||||
return delete this.storage[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ObjectsRegistry;
|
||||
@@ -1,231 +0,0 @@
|
||||
path = require 'path'
|
||||
|
||||
electron = require 'electron'
|
||||
{ipcMain} = electron
|
||||
objectsRegistry = require './objects-registry'
|
||||
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
{IDWeakMap} = process.atomBinding 'id_weak_map'
|
||||
|
||||
# Convert a real value into meta data.
|
||||
valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
||||
meta = type: typeof value
|
||||
|
||||
meta.type = 'buffer' if Buffer.isBuffer value
|
||||
meta.type = 'value' if value is null
|
||||
meta.type = 'array' if Array.isArray value
|
||||
meta.type = 'error' if value instanceof Error
|
||||
meta.type = 'date' if value instanceof Date
|
||||
meta.type = 'promise' if value?.constructor.name is 'Promise'
|
||||
|
||||
# Treat simple objects as value.
|
||||
if optimizeSimpleObject and meta.type is 'object' and v8Util.getHiddenValue value, 'simple'
|
||||
meta.type = 'value'
|
||||
|
||||
# Treat the arguments object as array.
|
||||
meta.type = 'array' if meta.type is 'object' and value.callee? and value.length?
|
||||
|
||||
if meta.type is 'array'
|
||||
meta.members = []
|
||||
meta.members.push valueToMeta(sender, el) for el in value
|
||||
else if meta.type is 'object' or meta.type is 'function'
|
||||
meta.name = value.constructor.name
|
||||
|
||||
# Reference the original value if it's an object, because when it's
|
||||
# passed to renderer we would assume the renderer keeps a reference of
|
||||
# it.
|
||||
meta.id = objectsRegistry.add sender.getId(), value
|
||||
|
||||
meta.members = ({name, type: typeof field} for name, field of value)
|
||||
else if meta.type is 'buffer'
|
||||
meta.value = Array::slice.call value, 0
|
||||
else if meta.type is 'promise'
|
||||
meta.then = valueToMeta sender, value.then.bind(value)
|
||||
else if meta.type is 'error'
|
||||
meta.members = plainObjectToMeta value
|
||||
# Error.name is not part of own properties.
|
||||
meta.members.push {name: 'name', value: value.name}
|
||||
else if meta.type is 'date'
|
||||
meta.value = value.getTime()
|
||||
else
|
||||
meta.type = 'value'
|
||||
meta.value = value
|
||||
|
||||
meta
|
||||
|
||||
# Convert object to meta by value.
|
||||
plainObjectToMeta = (obj) ->
|
||||
Object.getOwnPropertyNames(obj).map (name) -> {name, value: obj[name]}
|
||||
|
||||
# Convert Error into meta data.
|
||||
exceptionToMeta = (error) ->
|
||||
type: 'exception', message: error.message, stack: (error.stack || error)
|
||||
|
||||
# Convert array of meta data from renderer into array of real values.
|
||||
unwrapArgs = (sender, args) ->
|
||||
metaToValue = (meta) ->
|
||||
switch meta.type
|
||||
when 'value' then meta.value
|
||||
when 'remote-object' then objectsRegistry.get meta.id
|
||||
when 'array' then unwrapArgs sender, meta.value
|
||||
when 'buffer' then new Buffer(meta.value)
|
||||
when 'date' then new Date(meta.value)
|
||||
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
|
||||
when 'object'
|
||||
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'
|
||||
# Cache the callbacks in renderer.
|
||||
unless sender.callbacks
|
||||
sender.callbacks = new IDWeakMap
|
||||
sender.on 'render-view-deleted', ->
|
||||
sender.callbacks.clear()
|
||||
return sender.callbacks.get meta.id if sender.callbacks.has meta.id
|
||||
|
||||
rendererReleased = false
|
||||
objectsRegistry.once "clear-#{sender.getId()}", ->
|
||||
rendererReleased = true
|
||||
|
||||
ret = ->
|
||||
if rendererReleased
|
||||
throw new Error("Attempting to call a function in a renderer window
|
||||
that has been closed or released. Function provided here: #{meta.location}.")
|
||||
sender.send 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments)
|
||||
v8Util.setDestructor ret, ->
|
||||
return if rendererReleased
|
||||
sender.callbacks.remove meta.id
|
||||
sender.send 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
|
||||
sender.callbacks.set meta.id, ret
|
||||
ret
|
||||
else throw new TypeError("Unknown type: #{meta.type}")
|
||||
|
||||
args.map metaToValue
|
||||
|
||||
# Call a function and send reply asynchronously if it's a an asynchronous
|
||||
# style function and the caller didn't pass a callback.
|
||||
callFunction = (event, func, caller, args) ->
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||
funcPassedCallback = typeof args[args.length - 1] is 'function'
|
||||
|
||||
try
|
||||
if funcMarkedAsync and not funcPassedCallback
|
||||
args.push (ret) ->
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
func.apply caller, args
|
||||
else
|
||||
ret = func.apply caller, args
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
catch e
|
||||
# Catch functions thrown further down in function invocation and wrap
|
||||
# them with the function name so it's easier to trace things like
|
||||
# `Error processing argument -1.`
|
||||
funcName = func.name ? "anonymous"
|
||||
throw new Error("Could not call remote function `#{funcName}`.
|
||||
Check that the function signature is correct.
|
||||
Underlying error: #{e.message}")
|
||||
|
||||
# Send by BrowserWindow when its render view is deleted.
|
||||
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
|
||||
objectsRegistry.clear id
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GET_BUILTIN', (event, module) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, electron[module]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, global[name]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
|
||||
event.returnValue = valueToMeta event.sender, event.sender
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
constructor = objectsRegistry.get id
|
||||
# Call new with array of arguments.
|
||||
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
func = objectsRegistry.get id
|
||||
callFunction event, func, global, args
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
constructor = objectsRegistry.get(id)[method]
|
||||
# Call new with array of arguments.
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
obj = objectsRegistry.get id
|
||||
callFunction event, obj[method], obj, args
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
|
||||
try
|
||||
obj = objectsRegistry.get id
|
||||
obj[name] = value
|
||||
event.returnValue = null
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
|
||||
try
|
||||
obj = objectsRegistry.get id
|
||||
event.returnValue = valueToMeta event.sender, obj[name]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
|
||||
objectsRegistry.remove event.sender.getId(), id
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
|
||||
try
|
||||
guestViewManager = require './guest-view-manager'
|
||||
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', (event, guestInstanceId, method, args...) ->
|
||||
try
|
||||
guestViewManager = require './guest-view-manager'
|
||||
guest = guestViewManager.getGuest(guestInstanceId)
|
||||
guest[method].apply(guest, args)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
344
atom/browser/lib/rpc-server.js
Normal file
344
atom/browser/lib/rpc-server.js
Normal file
@@ -0,0 +1,344 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const ipcMain = electron.ipcMain;
|
||||
const objectsRegistry = require('./objects-registry');
|
||||
const v8Util = process.atomBinding('v8_util');
|
||||
const IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap;
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
// Convert a real value into meta data.
|
||||
var valueToMeta = function(sender, value, optimizeSimpleObject) {
|
||||
var el, field, i, len, meta, name;
|
||||
if (optimizeSimpleObject == null) {
|
||||
optimizeSimpleObject = false;
|
||||
}
|
||||
meta = {
|
||||
type: typeof value
|
||||
};
|
||||
if (Buffer.isBuffer(value)) {
|
||||
meta.type = 'buffer';
|
||||
}
|
||||
if (value === null) {
|
||||
meta.type = 'value';
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
meta.type = 'array';
|
||||
}
|
||||
if (value instanceof Error) {
|
||||
meta.type = 'error';
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
meta.type = 'date';
|
||||
}
|
||||
if ((value != null ? value.constructor.name : void 0) === 'Promise') {
|
||||
meta.type = 'promise';
|
||||
}
|
||||
|
||||
// Treat simple objects as value.
|
||||
if (optimizeSimpleObject && meta.type === 'object' && v8Util.getHiddenValue(value, 'simple')) {
|
||||
meta.type = 'value';
|
||||
}
|
||||
|
||||
// Treat the arguments object as array.
|
||||
if (meta.type === 'object' && (value.hasOwnProperty('callee')) && (value.length != null)) {
|
||||
meta.type = 'array';
|
||||
}
|
||||
if (meta.type === 'array') {
|
||||
meta.members = [];
|
||||
for (i = 0, len = value.length; i < len; i++) {
|
||||
el = value[i];
|
||||
meta.members.push(valueToMeta(sender, el));
|
||||
}
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor.name;
|
||||
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender.getId(), value);
|
||||
meta.members = (function() {
|
||||
var results;
|
||||
results = [];
|
||||
for (name in value) {
|
||||
field = value[name];
|
||||
results.push({
|
||||
name: name,
|
||||
type: typeof field
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})();
|
||||
} else if (meta.type === 'buffer') {
|
||||
meta.value = Array.prototype.slice.call(value, 0);
|
||||
} else if (meta.type === 'promise') {
|
||||
meta.then = valueToMeta(sender, value.then.bind(value));
|
||||
} else if (meta.type === 'error') {
|
||||
meta.members = plainObjectToMeta(value);
|
||||
|
||||
// Error.name is not part of own properties.
|
||||
meta.members.push({
|
||||
name: 'name',
|
||||
value: value.name
|
||||
});
|
||||
} else if (meta.type === 'date') {
|
||||
meta.value = value.getTime();
|
||||
} else {
|
||||
meta.type = 'value';
|
||||
meta.value = value;
|
||||
}
|
||||
return meta;
|
||||
};
|
||||
|
||||
// Convert object to meta by value.
|
||||
var plainObjectToMeta = function(obj) {
|
||||
return Object.getOwnPropertyNames(obj).map(function(name) {
|
||||
return {
|
||||
name: name,
|
||||
value: obj[name]
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Convert Error into meta data.
|
||||
var exceptionToMeta = function(error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
message: error.message,
|
||||
stack: error.stack || error
|
||||
};
|
||||
};
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
var unwrapArgs = function(sender, args) {
|
||||
var metaToValue;
|
||||
metaToValue = function(meta) {
|
||||
var i, len, member, ref, rendererReleased, returnValue;
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value;
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id);
|
||||
case 'array':
|
||||
return unwrapArgs(sender, meta.value);
|
||||
case 'buffer':
|
||||
return new Buffer(meta.value);
|
||||
case 'date':
|
||||
return new Date(meta.value);
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
});
|
||||
case 'object':
|
||||
let ret = v8Util.createObjectWithName(meta.name);
|
||||
ref = meta.members;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
member = ref[i];
|
||||
ret[member.name] = metaToValue(member.value);
|
||||
}
|
||||
return ret;
|
||||
case 'function-with-return-value':
|
||||
returnValue = metaToValue(meta.value);
|
||||
return function() {
|
||||
return returnValue;
|
||||
};
|
||||
case 'function':
|
||||
// Cache the callbacks in renderer.
|
||||
if (!sender.callbacks) {
|
||||
sender.callbacks = new IDWeakMap;
|
||||
sender.on('render-view-deleted', function() {
|
||||
return this.callbacks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
if (sender.callbacks.has(meta.id))
|
||||
return sender.callbacks.get(meta.id);
|
||||
|
||||
// Prevent the callback from being called when its page is gone.
|
||||
rendererReleased = false;
|
||||
sender.once('render-view-deleted', function() {
|
||||
rendererReleased = true;
|
||||
});
|
||||
|
||||
let callIntoRenderer = function(...args) {
|
||||
if (rendererReleased)
|
||||
throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`);
|
||||
sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args));
|
||||
};
|
||||
v8Util.setDestructor(callIntoRenderer, function() {
|
||||
if (!rendererReleased)
|
||||
sender.send('ATOM_RENDERER_RELEASE_CALLBACK', meta.id);
|
||||
});
|
||||
sender.callbacks.set(meta.id, callIntoRenderer);
|
||||
return callIntoRenderer;
|
||||
default:
|
||||
throw new TypeError("Unknown type: " + meta.type);
|
||||
}
|
||||
};
|
||||
return args.map(metaToValue);
|
||||
};
|
||||
|
||||
// Call a function and send reply asynchronously if it's a an asynchronous
|
||||
// style function and the caller didn't pass a callback.
|
||||
var callFunction = function(event, func, caller, args) {
|
||||
var funcMarkedAsync, funcName, funcPassedCallback, ref, ret;
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous');
|
||||
funcPassedCallback = typeof args[args.length - 1] === 'function';
|
||||
try {
|
||||
if (funcMarkedAsync && !funcPassedCallback) {
|
||||
args.push(function(ret) {
|
||||
return event.returnValue = valueToMeta(event.sender, ret, true);
|
||||
});
|
||||
return func.apply(caller, args);
|
||||
} else {
|
||||
ret = func.apply(caller, args);
|
||||
return event.returnValue = valueToMeta(event.sender, ret, true);
|
||||
}
|
||||
} catch (error) {
|
||||
// Catch functions thrown further down in function invocation and wrap
|
||||
// them with the function name so it's easier to trace things like
|
||||
// `Error processing argument -1.`
|
||||
funcName = (ref = func.name) != null ? ref : "anonymous";
|
||||
throw new Error("Could not call remote function `" + funcName + "`. Check that the function signature is correct. Underlying error: " + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Send by BrowserWindow when its render view is deleted.
|
||||
process.on('ATOM_BROWSER_RELEASE_RENDER_VIEW', function(id) {
|
||||
return objectsRegistry.clear(id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_REQUIRE', function(event, module) {
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, process.mainModule.require(module));
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GET_BUILTIN', function(event, module) {
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, electron[module]);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GLOBAL', function(event, name) {
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, global[name]);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CURRENT_WINDOW', function(event) {
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow());
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CURRENT_WEB_CONTENTS', function(event) {
|
||||
return event.returnValue = valueToMeta(event.sender, event.sender);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) {
|
||||
var constructor, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id);
|
||||
|
||||
// Call new with array of arguments.
|
||||
// http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) {
|
||||
var func;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
func = objectsRegistry.get(id);
|
||||
return callFunction(event, func, global, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args) {
|
||||
var constructor, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id)[method];
|
||||
|
||||
// Call new with array of arguments.
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) {
|
||||
var obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
obj = objectsRegistry.get(id);
|
||||
return callFunction(event, obj[method], obj, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) {
|
||||
var obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
obj[name] = value;
|
||||
return event.returnValue = null;
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_GET', function(event, id, name) {
|
||||
var obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
return event.returnValue = valueToMeta(event.sender, obj[name]);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_DEREFERENCE', function(event, id) {
|
||||
return objectsRegistry.remove(event.sender.getId(), id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) {
|
||||
var guestViewManager;
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
return event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId));
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function() {
|
||||
var args, event, guest, guestInstanceId, guestViewManager, method;
|
||||
event = arguments[0], guestInstanceId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
guest = guestViewManager.getGuest(guestInstanceId);
|
||||
return guest[method].apply(guest, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
@@ -116,11 +116,19 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
||||
SetSizeConstraints(size_constraints);
|
||||
}
|
||||
#if defined(OS_WIN) || defined(USE_X11)
|
||||
bool resizable;
|
||||
if (options.Get(options::kResizable, &resizable)) {
|
||||
SetResizable(resizable);
|
||||
bool closable;
|
||||
if (options.Get(options::kClosable, &closable)) {
|
||||
SetClosable(closable);
|
||||
}
|
||||
#endif
|
||||
bool movable;
|
||||
if (options.Get(options::kMovable, &movable)) {
|
||||
SetMovable(movable);
|
||||
}
|
||||
bool has_shadow;
|
||||
if (options.Get(options::kHasShadow, &has_shadow)) {
|
||||
SetHasShadow(has_shadow);
|
||||
}
|
||||
bool top;
|
||||
if (options.Get(options::kAlwaysOnTop, &top) && top) {
|
||||
SetAlwaysOnTop(true);
|
||||
@@ -154,24 +162,24 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
||||
Show();
|
||||
}
|
||||
|
||||
void NativeWindow::SetSize(const gfx::Size& size) {
|
||||
SetBounds(gfx::Rect(GetPosition(), size));
|
||||
void NativeWindow::SetSize(const gfx::Size& size, bool animate) {
|
||||
SetBounds(gfx::Rect(GetPosition(), size), animate);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindow::GetSize() {
|
||||
return GetBounds().size();
|
||||
}
|
||||
|
||||
void NativeWindow::SetPosition(const gfx::Point& position) {
|
||||
SetBounds(gfx::Rect(position, GetSize()));
|
||||
void NativeWindow::SetPosition(const gfx::Point& position, bool animate) {
|
||||
SetBounds(gfx::Rect(position, GetSize()), animate);
|
||||
}
|
||||
|
||||
gfx::Point NativeWindow::GetPosition() {
|
||||
return GetBounds().origin();
|
||||
}
|
||||
|
||||
void NativeWindow::SetContentSize(const gfx::Size& size) {
|
||||
SetSize(ContentSizeToWindowSize(size));
|
||||
void NativeWindow::SetContentSize(const gfx::Size& size, bool animate) {
|
||||
SetSize(ContentSizeToWindowSize(size), animate);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindow::GetContentSize() {
|
||||
@@ -442,6 +450,16 @@ void NativeWindow::NotifyWindowEnterFullScreen() {
|
||||
OnWindowEnterFullScreen());
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowScrollTouchBegin() {
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
|
||||
OnWindowScrollTouchBegin());
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowScrollTouchEnd() {
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
|
||||
OnWindowScrollTouchEnd());
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowLeaveFullScreen() {
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
|
||||
OnWindowLeaveFullScreen());
|
||||
@@ -551,4 +569,27 @@ void NativeWindow::OnCapturePageDone(const CapturePageCallback& callback,
|
||||
callback.Run(bitmap);
|
||||
}
|
||||
|
||||
SkColor NativeWindow::ParseHexColor(const std::string& name) {
|
||||
auto color = name.substr(1);
|
||||
unsigned length = color.size();
|
||||
SkColor result = (length != 8 ? 0xFF000000 : 0x00000000);
|
||||
unsigned value = 0;
|
||||
if (length != 3 && length != 6 && length != 8)
|
||||
return result;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (!base::IsHexDigit(color[i]))
|
||||
return result;
|
||||
value <<= 4;
|
||||
value |= (color[i] < 'A' ? color[i] - '0' : (color[i] - 'A' + 10) & 0xF);
|
||||
}
|
||||
if (length == 6 || length == 8) {
|
||||
result |= value;
|
||||
return result;
|
||||
}
|
||||
result |= (value & 0xF00) << 12 | (value & 0xF00) << 8
|
||||
| (value & 0xF0) << 8 | (value & 0xF0) << 4
|
||||
| (value & 0xF) << 4 | (value & 0xF);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -105,13 +105,13 @@ class NativeWindow : public base::SupportsUserData,
|
||||
virtual bool IsMinimized() = 0;
|
||||
virtual void SetFullScreen(bool fullscreen) = 0;
|
||||
virtual bool IsFullscreen() const = 0;
|
||||
virtual void SetBounds(const gfx::Rect& bounds) = 0;
|
||||
virtual void SetBounds(const gfx::Rect& bounds, bool animate = false) = 0;
|
||||
virtual gfx::Rect GetBounds() = 0;
|
||||
virtual void SetSize(const gfx::Size& size);
|
||||
virtual void SetSize(const gfx::Size& size, bool animate = false);
|
||||
virtual gfx::Size GetSize();
|
||||
virtual void SetPosition(const gfx::Point& position);
|
||||
virtual void SetPosition(const gfx::Point& position, bool animate = false);
|
||||
virtual gfx::Point GetPosition();
|
||||
virtual void SetContentSize(const gfx::Size& size);
|
||||
virtual void SetContentSize(const gfx::Size& size, bool animate = false);
|
||||
virtual gfx::Size GetContentSize();
|
||||
virtual void SetSizeConstraints(
|
||||
const extensions::SizeConstraints& size_constraints);
|
||||
@@ -125,6 +125,16 @@ class NativeWindow : public base::SupportsUserData,
|
||||
virtual gfx::Size GetMaximumSize();
|
||||
virtual void SetResizable(bool resizable) = 0;
|
||||
virtual bool IsResizable() = 0;
|
||||
virtual void SetMovable(bool movable) = 0;
|
||||
virtual bool IsMovable() = 0;
|
||||
virtual void SetMinimizable(bool minimizable) = 0;
|
||||
virtual bool IsMinimizable() = 0;
|
||||
virtual void SetMaximizable(bool maximizable) = 0;
|
||||
virtual bool IsMaximizable() = 0;
|
||||
virtual void SetFullScreenable(bool fullscreenable) = 0;
|
||||
virtual bool IsFullScreenable() = 0;
|
||||
virtual void SetClosable(bool closable) = 0;
|
||||
virtual bool IsClosable() = 0;
|
||||
virtual void SetAlwaysOnTop(bool top) = 0;
|
||||
virtual bool IsAlwaysOnTop() = 0;
|
||||
virtual void Center() = 0;
|
||||
@@ -135,6 +145,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||
virtual void SetKiosk(bool kiosk) = 0;
|
||||
virtual bool IsKiosk() = 0;
|
||||
virtual void SetBackgroundColor(const std::string& color_name) = 0;
|
||||
virtual void SetHasShadow(bool has_shadow) = 0;
|
||||
virtual bool HasShadow() = 0;
|
||||
virtual void SetRepresentedFilename(const std::string& filename);
|
||||
virtual std::string GetRepresentedFilename();
|
||||
virtual void SetDocumentEdited(bool edited);
|
||||
@@ -143,6 +155,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||
virtual void SetMenu(ui::MenuModel* menu);
|
||||
virtual bool HasModalDialog();
|
||||
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
||||
virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
|
||||
|
||||
// Taskbar/Dock APIs.
|
||||
virtual void SetProgressBar(double progress) = 0;
|
||||
@@ -204,6 +217,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||
void NotifyWindowMove();
|
||||
void NotifyWindowResize();
|
||||
void NotifyWindowMoved();
|
||||
void NotifyWindowScrollTouchBegin();
|
||||
void NotifyWindowScrollTouchEnd();
|
||||
void NotifyWindowEnterFullScreen();
|
||||
void NotifyWindowLeaveFullScreen();
|
||||
void NotifyWindowEnterHtmlFullScreen();
|
||||
@@ -264,6 +279,9 @@ class NativeWindow : public base::SupportsUserData,
|
||||
void BeforeUnloadDialogCancelled() override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
// Parse hex color like "#FFF" or "#EFEFEF"
|
||||
SkColor ParseHexColor(const std::string& name);
|
||||
|
||||
private:
|
||||
// Schedule a notification unresponsive event.
|
||||
void ScheduleUnresponsiveEvent(int ms);
|
||||
|
||||
@@ -42,12 +42,22 @@ class NativeWindowMac : public NativeWindow {
|
||||
bool IsMinimized() override;
|
||||
void SetFullScreen(bool fullscreen) override;
|
||||
bool IsFullscreen() const override;
|
||||
void SetBounds(const gfx::Rect& bounds) override;
|
||||
void SetBounds(const gfx::Rect& bounds, bool animate = false) override;
|
||||
gfx::Rect GetBounds() override;
|
||||
void SetContentSizeConstraints(
|
||||
const extensions::SizeConstraints& size_constraints) override;
|
||||
void SetResizable(bool resizable) override;
|
||||
bool IsResizable() override;
|
||||
void SetMovable(bool movable) override;
|
||||
bool IsMovable() override;
|
||||
void SetMinimizable(bool minimizable) override;
|
||||
bool IsMinimizable() override;
|
||||
void SetMaximizable(bool maximizable) override;
|
||||
bool IsMaximizable() override;
|
||||
void SetFullScreenable(bool fullscreenable) override;
|
||||
bool IsFullScreenable() override;
|
||||
void SetClosable(bool closable) override;
|
||||
bool IsClosable() override;
|
||||
void SetAlwaysOnTop(bool top) override;
|
||||
bool IsAlwaysOnTop() override;
|
||||
void Center() override;
|
||||
@@ -58,6 +68,8 @@ class NativeWindowMac : public NativeWindow {
|
||||
void SetKiosk(bool kiosk) override;
|
||||
bool IsKiosk() override;
|
||||
void SetBackgroundColor(const std::string& color_name) override;
|
||||
void SetHasShadow(bool has_shadow) override;
|
||||
bool HasShadow() override;
|
||||
void SetRepresentedFilename(const std::string& filename) override;
|
||||
std::string GetRepresentedFilename() override;
|
||||
void SetDocumentEdited(bool edited) override;
|
||||
@@ -65,6 +77,7 @@ class NativeWindowMac : public NativeWindow {
|
||||
void SetIgnoreMouseEvents(bool ignore) override;
|
||||
bool HasModalDialog() override;
|
||||
gfx::NativeWindow GetNativeWindow() override;
|
||||
gfx::AcceleratedWidget GetAcceleratedWidget() override;
|
||||
void SetProgressBar(double progress) override;
|
||||
void SetOverlayIcon(const gfx::Image& overlay,
|
||||
const std::string& description) override;
|
||||
@@ -107,9 +120,16 @@ class NativeWindowMac : public NativeWindow {
|
||||
// whehter we can drag.
|
||||
void UpdateDraggableRegionViews(const std::vector<DraggableRegion>& regions);
|
||||
|
||||
// Set the attribute of NSWindow while work around a bug of zo0m button.
|
||||
void SetStyleMask(bool on, NSUInteger flag);
|
||||
void SetCollectionBehavior(bool on, NSUInteger flag);
|
||||
|
||||
base::scoped_nsobject<AtomNSWindow> window_;
|
||||
base::scoped_nsobject<AtomNSWindowDelegate> window_delegate_;
|
||||
|
||||
// Event monitor for scroll wheel event.
|
||||
id wheel_event_monitor_;
|
||||
|
||||
// The view that will fill the whole frameless window.
|
||||
base::scoped_nsobject<FullSizeContentView> content_view_;
|
||||
|
||||
|
||||
@@ -369,6 +369,15 @@ NativeWindowMac::NativeWindowMac(
|
||||
bool resizable = true;
|
||||
options.Get(options::kResizable, &resizable);
|
||||
|
||||
bool minimizable = true;
|
||||
options.Get(options::kMinimizable, &minimizable);
|
||||
|
||||
bool maximizable = true;
|
||||
options.Get(options::kMaximizable, &maximizable);
|
||||
|
||||
bool closable = true;
|
||||
options.Get(options::kClosable, &closable);
|
||||
|
||||
// New title bar styles are available in Yosemite or newer
|
||||
std::string titleBarStyle;
|
||||
if (base::mac::IsOSYosemiteOrLater())
|
||||
@@ -385,8 +394,13 @@ NativeWindowMac::NativeWindowMac(
|
||||
useStandardWindow = false;
|
||||
}
|
||||
|
||||
NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask |
|
||||
NSMiniaturizableWindowMask;
|
||||
NSUInteger styleMask = NSTitledWindowMask;
|
||||
if (minimizable) {
|
||||
styleMask |= NSMiniaturizableWindowMask;
|
||||
}
|
||||
if (closable) {
|
||||
styleMask |= NSClosableWindowMask;
|
||||
}
|
||||
if (!useStandardWindow || transparent() || !has_frame()) {
|
||||
styleMask |= NSTexturedBackgroundWindowMask;
|
||||
}
|
||||
@@ -418,7 +432,7 @@ NativeWindowMac::NativeWindowMac(
|
||||
if (transparent()) {
|
||||
// Make window has transparent background.
|
||||
[window_ setOpaque:NO];
|
||||
[window_ setHasShadow:NO];
|
||||
// Setting the background color to clear will also hide the shadow.
|
||||
[window_ setBackgroundColor:[NSColor clearColor]];
|
||||
}
|
||||
|
||||
@@ -468,27 +482,51 @@ NativeWindowMac::NativeWindowMac(
|
||||
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
||||
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
||||
|
||||
// Disable fullscreen button when 'fullscreen' is specified to false.
|
||||
// Disable fullscreen button when 'fullscreenable' is false or 'fullscreen'
|
||||
// is specified to false.
|
||||
bool fullscreenable = true;
|
||||
options.Get(options::kFullScreenable, &fullscreenable);
|
||||
bool fullscreen = false;
|
||||
if (!(options.Get(options::kFullscreen, &fullscreen) &&
|
||||
!fullscreen)) {
|
||||
NSUInteger collectionBehavior = [window_ collectionBehavior];
|
||||
collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
|
||||
[window_ setCollectionBehavior:collectionBehavior];
|
||||
} else if (base::mac::IsOSElCapitanOrLater()) {
|
||||
// On EL Capitan this flag is required to hide fullscreen button.
|
||||
NSUInteger collectionBehavior = [window_ collectionBehavior];
|
||||
collectionBehavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
|
||||
[window_ setCollectionBehavior:collectionBehavior];
|
||||
if (options.Get(options::kFullscreen, &fullscreen) && !fullscreen)
|
||||
fullscreenable = false;
|
||||
SetFullScreenable(fullscreenable);
|
||||
|
||||
// Disable zoom button if window is not resizable
|
||||
if (!maximizable) {
|
||||
SetMaximizable(false);
|
||||
}
|
||||
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
// Use an NSEvent monitor to listen for the wheel event.
|
||||
BOOL __block began = NO;
|
||||
wheel_event_monitor_ = [NSEvent
|
||||
addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
|
||||
handler:^(NSEvent* event) {
|
||||
if ([[event window] windowNumber] != [window_ windowNumber])
|
||||
return event;
|
||||
|
||||
if (!web_contents)
|
||||
return event;
|
||||
|
||||
if (!began && (([event phase] == NSEventPhaseMayBegin) ||
|
||||
([event phase] == NSEventPhaseBegan))) {
|
||||
this->NotifyWindowScrollTouchBegin();
|
||||
began = YES;
|
||||
} else if (began && (([event phase] == NSEventPhaseEnded) ||
|
||||
([event phase] == NSEventPhaseCancelled))) {
|
||||
this->NotifyWindowScrollTouchEnd();
|
||||
began = NO;
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
InstallView();
|
||||
}
|
||||
|
||||
NativeWindowMac::~NativeWindowMac() {
|
||||
[NSEvent removeMonitor:wheel_event_monitor_];
|
||||
Observe(nullptr);
|
||||
}
|
||||
|
||||
@@ -576,7 +614,7 @@ bool NativeWindowMac::IsFullscreen() const {
|
||||
return [window_ styleMask] & NSFullScreenWindowMask;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetBounds(const gfx::Rect& bounds) {
|
||||
void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
|
||||
NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0,
|
||||
bounds.width(),
|
||||
bounds.height());
|
||||
@@ -585,7 +623,7 @@ void NativeWindowMac::SetBounds(const gfx::Rect& bounds) {
|
||||
cocoa_bounds.origin.y =
|
||||
NSHeight([screen frame]) - bounds.height() - bounds.y();
|
||||
|
||||
[window_ setFrame:cocoa_bounds display:YES];
|
||||
[window_ setFrame:cocoa_bounds display:YES animate:animate];
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowMac::GetBounds() {
|
||||
@@ -626,19 +664,58 @@ void NativeWindowMac::SetResizable(bool resizable) {
|
||||
// Change styleMask for frameless causes the window to change size, so we have
|
||||
// to explicitly disables that.
|
||||
ScopedDisableResize disable_resize;
|
||||
if (resizable) {
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setEnabled:YES];
|
||||
[window_ setStyleMask:[window_ styleMask] | NSResizableWindowMask];
|
||||
} else {
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO];
|
||||
[window_ setStyleMask:[window_ styleMask] & (~NSResizableWindowMask)];
|
||||
}
|
||||
SetStyleMask(resizable, NSResizableWindowMask);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsResizable() {
|
||||
return [window_ styleMask] & NSResizableWindowMask;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetMovable(bool movable) {
|
||||
[window_ setMovable:movable];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMovable() {
|
||||
return [window_ isMovable];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetMinimizable(bool minimizable) {
|
||||
SetStyleMask(minimizable, NSMiniaturizableWindowMask);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMinimizable() {
|
||||
return [window_ styleMask] & NSMiniaturizableWindowMask;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetMaximizable(bool maximizable) {
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setEnabled:maximizable];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMaximizable() {
|
||||
return [[window_ standardWindowButton:NSWindowZoomButton] isEnabled];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
|
||||
SetCollectionBehavior(
|
||||
fullscreenable, NSWindowCollectionBehaviorFullScreenPrimary);
|
||||
// On EL Capitan this flag is required to hide fullscreen button.
|
||||
SetCollectionBehavior(
|
||||
!fullscreenable, NSWindowCollectionBehaviorFullScreenAuxiliary);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsFullScreenable() {
|
||||
NSUInteger collectionBehavior = [window_ collectionBehavior];
|
||||
return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetClosable(bool closable) {
|
||||
SetStyleMask(closable, NSClosableWindowMask);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsClosable() {
|
||||
return [window_ styleMask] & NSClosableWindowMask;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetAlwaysOnTop(bool top) {
|
||||
[window_ setLevel:(top ? NSFloatingWindowLevel : NSNormalWindowLevel)];
|
||||
}
|
||||
@@ -701,6 +778,20 @@ bool NativeWindowMac::IsKiosk() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetBackgroundColor(const std::string& color_name) {
|
||||
SkColor background_color = NativeWindow::ParseHexColor(color_name);
|
||||
NSColor *color = [NSColor colorWithCalibratedRed:SkColorGetR(background_color)
|
||||
green:SkColorGetG(background_color)
|
||||
blue:SkColorGetB(background_color)
|
||||
alpha:SkColorGetA(background_color)/255.0f];
|
||||
[window_ setBackgroundColor:color];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetHasShadow(bool has_shadow) {
|
||||
[window_ setHasShadow:has_shadow];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::HasShadow() {
|
||||
return [window_ hasShadow];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
|
||||
@@ -731,6 +822,10 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
|
||||
return window_;
|
||||
}
|
||||
|
||||
gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() {
|
||||
return inspectable_web_contents()->GetView()->GetNativeView();
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetProgressBar(double progress) {
|
||||
NSDockTile* dock_tile = [NSApp dockTile];
|
||||
|
||||
@@ -781,13 +876,7 @@ void NativeWindowMac::ShowDefinitionForSelection() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) {
|
||||
NSUInteger collectionBehavior = [window_ collectionBehavior];
|
||||
if (visible) {
|
||||
collectionBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
|
||||
} else {
|
||||
collectionBehavior &= ~NSWindowCollectionBehaviorCanJoinAllSpaces;
|
||||
}
|
||||
[window_ setCollectionBehavior:collectionBehavior];
|
||||
SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsVisibleOnAllWorkspaces() {
|
||||
@@ -944,6 +1033,30 @@ void NativeWindowMac::UpdateDraggableRegionViews(
|
||||
[window_ setMovableByWindowBackground:YES];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
|
||||
bool zoom_button_enabled = IsMaximizable();
|
||||
if (on)
|
||||
[window_ setStyleMask:[window_ styleMask] | flag];
|
||||
else
|
||||
[window_ setStyleMask:[window_ styleMask] & (~flag)];
|
||||
// Change style mask will make the zoom button revert to default, probably
|
||||
// a bug of Cocoa or OS X.
|
||||
if (!zoom_button_enabled)
|
||||
SetMaximizable(false);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
|
||||
bool zoom_button_enabled = IsMaximizable();
|
||||
if (on)
|
||||
[window_ setCollectionBehavior:[window_ collectionBehavior] | flag];
|
||||
else
|
||||
[window_ setCollectionBehavior:[window_ collectionBehavior] & (~flag)];
|
||||
// Change collectionBehavior will make the zoom button revert to default,
|
||||
// probably a bug of Cocoa or OS X.
|
||||
if (!zoom_button_enabled)
|
||||
SetMaximizable(false);
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Create(
|
||||
brightray::InspectableWebContents* inspectable_web_contents,
|
||||
|
||||
@@ -50,6 +50,8 @@ class NativeWindowObserver {
|
||||
virtual void OnWindowResize() {}
|
||||
virtual void OnWindowMove() {}
|
||||
virtual void OnWindowMoved() {}
|
||||
virtual void OnWindowScrollTouchBegin() {}
|
||||
virtual void OnWindowScrollTouchEnd() {}
|
||||
virtual void OnWindowEnterFullScreen() {}
|
||||
virtual void OnWindowLeaveFullScreen() {}
|
||||
virtual void OnWindowEnterHtmlFullScreen() {}
|
||||
|
||||
@@ -59,6 +59,17 @@ const int kMenuBarHeight = 20;
|
||||
const int kMenuBarHeight = 25;
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void FlipWindowStyle(HWND handle, bool on, DWORD flag) {
|
||||
DWORD style = ::GetWindowLong(handle, GWL_STYLE);
|
||||
if (on)
|
||||
style |= flag;
|
||||
else
|
||||
style &= ~flag;
|
||||
::SetWindowLong(handle, GWL_STYLE, style);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
|
||||
return event.windowsKeyCode == ui::VKEY_MENU;
|
||||
}
|
||||
@@ -73,29 +84,6 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
||||
(modifiers == (Modifiers::AltKey | Modifiers::IsRight));
|
||||
}
|
||||
|
||||
SkColor ParseHexColor(const std::string& name) {
|
||||
SkColor result = 0xFF000000;
|
||||
unsigned value = 0;
|
||||
auto color = name.substr(1);
|
||||
unsigned length = color.size();
|
||||
if (length != 3 && length != 6)
|
||||
return result;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (!base::IsHexDigit(color[i]))
|
||||
return result;
|
||||
value <<= 4;
|
||||
value |= (color[i] < 'A' ? color[i] - '0' : (color[i] - 'A' + 10) & 0xF);
|
||||
}
|
||||
if (length == 6) {
|
||||
result |= value;
|
||||
return result;
|
||||
}
|
||||
result |= (value & 0xF00) << 12 | (value & 0xF00) << 8
|
||||
| (value & 0xF0) << 8 | (value & 0xF0) << 4
|
||||
| (value & 0xF) << 4 | (value & 0xF);
|
||||
return result;
|
||||
}
|
||||
|
||||
class NativeWindowClientView : public views::ClientView {
|
||||
public:
|
||||
NativeWindowClientView(views::Widget* widget,
|
||||
@@ -126,7 +114,10 @@ NativeWindowViews::NativeWindowViews(
|
||||
menu_bar_alt_pressed_(false),
|
||||
keyboard_event_handler_(new views::UnhandledKeyboardEventHandler),
|
||||
use_content_size_(false),
|
||||
resizable_(true) {
|
||||
movable_(true),
|
||||
resizable_(true),
|
||||
maximizable_(true),
|
||||
minimizable_(true) {
|
||||
options.Get(options::kTitle, &title_);
|
||||
options.Get(options::kAutoHideMenuBar, &menu_bar_autohide_);
|
||||
|
||||
@@ -134,6 +125,8 @@ NativeWindowViews::NativeWindowViews(
|
||||
// On Windows we rely on the CanResize() to indicate whether window can be
|
||||
// resized, and it should be set before window is created.
|
||||
options.Get(options::kResizable, &resizable_);
|
||||
options.Get(options::kMinimizable, &minimizable_);
|
||||
options.Get(options::kMaximizable, &maximizable_);
|
||||
#endif
|
||||
|
||||
if (enable_larger_than_screen())
|
||||
@@ -162,6 +155,11 @@ NativeWindowViews::NativeWindowViews(
|
||||
if (transparent())
|
||||
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
|
||||
|
||||
// The given window is most likely not rectangular since it uses
|
||||
// transparency and has no standard frame, don't show a shadow for it.
|
||||
if (transparent() && !has_frame())
|
||||
params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
params.native_widget =
|
||||
new views::DesktopNativeWidgetAura(window_.get());
|
||||
@@ -233,14 +231,18 @@ NativeWindowViews::NativeWindowViews(
|
||||
last_window_state_ = ui::SHOW_STATE_FULLSCREEN;
|
||||
else
|
||||
last_window_state_ = ui::SHOW_STATE_NORMAL;
|
||||
|
||||
last_normal_size_ = gfx::Size(widget_size_);
|
||||
|
||||
if (!has_frame()) {
|
||||
// Set Window style so that we get a minimize and maximize animation when
|
||||
// frameless.
|
||||
DWORD frame_style = WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
|
||||
WS_CAPTION;
|
||||
DWORD frame_style = WS_CAPTION;
|
||||
if (resizable_)
|
||||
frame_style |= WS_THICKFRAME;
|
||||
if (minimizable_)
|
||||
frame_style |= WS_MINIMIZEBOX;
|
||||
if (maximizable_)
|
||||
frame_style |= WS_MAXIMIZEBOX;
|
||||
// We should not show a frame for transparent window.
|
||||
if (transparent())
|
||||
frame_style &= ~(WS_THICKFRAME | WS_CAPTION);
|
||||
@@ -262,11 +264,6 @@ NativeWindowViews::NativeWindowViews(
|
||||
window_->FrameTypeChanged();
|
||||
}
|
||||
|
||||
// The given window is most likely not rectangular since it uses
|
||||
// transparency and has no standard frame, don't show a shadow for it.
|
||||
if (transparent() && !has_frame())
|
||||
wm::SetShadowType(GetNativeWindow(), wm::SHADOW_TYPE_NONE);
|
||||
|
||||
gfx::Size size = bounds.size();
|
||||
if (has_frame() &&
|
||||
options.Get(options::kUseContentSize, &use_content_size_) &&
|
||||
@@ -375,7 +372,8 @@ bool NativeWindowViews::IsFullscreen() const {
|
||||
return window_->IsFullscreen();
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetBounds(const gfx::Rect& bounds) {
|
||||
void NativeWindowViews::SetBounds(const gfx::Rect& bounds,
|
||||
bool animate = false) {
|
||||
#if defined(USE_X11)
|
||||
// On Linux the minimum and maximum size should be updated with window size
|
||||
// when window is not resizable.
|
||||
@@ -421,17 +419,8 @@ void NativeWindowViews::SetContentSizeConstraints(
|
||||
|
||||
void NativeWindowViews::SetResizable(bool resizable) {
|
||||
#if defined(OS_WIN)
|
||||
// WS_MAXIMIZEBOX => Maximize button
|
||||
// WS_MINIMIZEBOX => Minimize button
|
||||
// WS_THICKFRAME => Resize handle
|
||||
if (!transparent()) {
|
||||
DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE);
|
||||
if (resizable)
|
||||
style |= WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME;
|
||||
else
|
||||
style = (style & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)) | WS_MINIMIZEBOX;
|
||||
::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style);
|
||||
}
|
||||
if (!transparent())
|
||||
FlipWindowStyle(GetAcceleratedWidget(), resizable, WS_THICKFRAME);
|
||||
#elif defined(USE_X11)
|
||||
if (resizable != resizable_) {
|
||||
// On Linux there is no "resizable" property of a window, we have to set
|
||||
@@ -452,7 +441,87 @@ void NativeWindowViews::SetResizable(bool resizable) {
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsResizable() {
|
||||
return resizable_;
|
||||
#if defined(OS_WIN)
|
||||
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME;
|
||||
#else
|
||||
return CanResize();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMovable(bool movable) {
|
||||
movable_ = movable;
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsMovable() {
|
||||
#if defined(OS_WIN)
|
||||
return movable_;
|
||||
#else
|
||||
return true; // Not implemented on Linux.
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMinimizable(bool minimizable) {
|
||||
#if defined(OS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), minimizable, WS_MINIMIZEBOX);
|
||||
#endif
|
||||
minimizable_ = minimizable;
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsMinimizable() {
|
||||
#if defined(OS_WIN)
|
||||
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_MINIMIZEBOX;
|
||||
#else
|
||||
return true; // Not implemented on Linux.
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMaximizable(bool maximizable) {
|
||||
#if defined(OS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), maximizable, WS_MAXIMIZEBOX);
|
||||
#endif
|
||||
maximizable_ = maximizable;
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsMaximizable() {
|
||||
#if defined(OS_WIN)
|
||||
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_MAXIMIZEBOX;
|
||||
#else
|
||||
return true; // Not implemented on Linux.
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetFullScreenable(bool maximizable) {
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsFullScreenable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetClosable(bool closable) {
|
||||
#if defined(OS_WIN)
|
||||
HMENU menu = GetSystemMenu(GetAcceleratedWidget(), false);
|
||||
if (closable) {
|
||||
EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
|
||||
} else {
|
||||
EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsClosable() {
|
||||
#if defined(OS_WIN)
|
||||
HMENU menu = GetSystemMenu(GetAcceleratedWidget(), false);
|
||||
MENUITEMINFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.cbSize = sizeof(info);
|
||||
info.fMask = MIIM_STATE;
|
||||
if (!GetMenuItemInfo(menu, SC_CLOSE, false, &info)) {
|
||||
return false;
|
||||
}
|
||||
return !(info.fState & MFS_DISABLED);
|
||||
#elif defined(USE_X11)
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetAlwaysOnTop(bool top) {
|
||||
@@ -519,7 +588,7 @@ bool NativeWindowViews::IsKiosk() {
|
||||
|
||||
void NativeWindowViews::SetBackgroundColor(const std::string& color_name) {
|
||||
// web views' background color.
|
||||
SkColor background_color = ParseHexColor(color_name);
|
||||
SkColor background_color = NativeWindow::ParseHexColor(color_name);
|
||||
set_background(views::Background::CreateSolidBackground(background_color));
|
||||
|
||||
#if defined(OS_WIN)
|
||||
@@ -532,6 +601,16 @@ void NativeWindowViews::SetBackgroundColor(const std::string& color_name) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetHasShadow(bool has_shadow) {
|
||||
wm::SetShadowType(
|
||||
GetNativeWindow(),
|
||||
has_shadow ? wm::SHADOW_TYPE_RECTANGULAR : wm::SHADOW_TYPE_NONE);
|
||||
}
|
||||
|
||||
bool NativeWindowViews::HasShadow() {
|
||||
return wm::GetShadowType(GetNativeWindow()) != wm::SHADOW_TYPE_NONE;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||
if (menu_model == nullptr) {
|
||||
// Remove accelerators
|
||||
@@ -712,11 +791,15 @@ bool NativeWindowViews::CanResize() const {
|
||||
}
|
||||
|
||||
bool NativeWindowViews::CanMaximize() const {
|
||||
return resizable_;
|
||||
return resizable_ && maximizable_;
|
||||
}
|
||||
|
||||
bool NativeWindowViews::CanMinimize() const {
|
||||
#if defined(OS_WIN)
|
||||
return minimizable_;
|
||||
#elif defined(USE_X11)
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
base::string16 NativeWindowViews::GetWindowTitle() const {
|
||||
|
||||
@@ -61,13 +61,23 @@ class NativeWindowViews : public NativeWindow,
|
||||
bool IsMinimized() override;
|
||||
void SetFullScreen(bool fullscreen) override;
|
||||
bool IsFullscreen() const override;
|
||||
void SetBounds(const gfx::Rect& bounds) override;
|
||||
void SetBounds(const gfx::Rect& bounds, bool animate) override;
|
||||
gfx::Rect GetBounds() override;
|
||||
gfx::Size GetContentSize() override;
|
||||
void SetContentSizeConstraints(
|
||||
const extensions::SizeConstraints& size_constraints) override;
|
||||
void SetResizable(bool resizable) override;
|
||||
bool IsResizable() override;
|
||||
void SetMovable(bool movable) override;
|
||||
bool IsMovable() override;
|
||||
void SetMinimizable(bool minimizable) override;
|
||||
bool IsMinimizable() override;
|
||||
void SetMaximizable(bool maximizable) override;
|
||||
bool IsMaximizable() override;
|
||||
void SetFullScreenable(bool fullscreenable) override;
|
||||
bool IsFullScreenable() override;
|
||||
void SetClosable(bool closable) override;
|
||||
bool IsClosable() override;
|
||||
void SetAlwaysOnTop(bool top) override;
|
||||
bool IsAlwaysOnTop() override;
|
||||
void Center() override;
|
||||
@@ -78,6 +88,8 @@ class NativeWindowViews : public NativeWindow,
|
||||
void SetKiosk(bool kiosk) override;
|
||||
bool IsKiosk() override;
|
||||
void SetBackgroundColor(const std::string& color_name) override;
|
||||
void SetHasShadow(bool has_shadow) override;
|
||||
bool HasShadow() override;
|
||||
void SetMenu(ui::MenuModel* menu_model) override;
|
||||
gfx::NativeWindow GetNativeWindow() override;
|
||||
void SetOverlayIcon(const gfx::Image& overlay,
|
||||
@@ -90,7 +102,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
void SetVisibleOnAllWorkspaces(bool visible) override;
|
||||
bool IsVisibleOnAllWorkspaces() override;
|
||||
|
||||
gfx::AcceleratedWidget GetAcceleratedWidget();
|
||||
gfx::AcceleratedWidget GetAcceleratedWidget() override;
|
||||
|
||||
views::Widget* widget() const { return window_.get(); }
|
||||
|
||||
@@ -196,7 +208,10 @@ class NativeWindowViews : public NativeWindow,
|
||||
accelerator_util::AcceleratorTable accelerator_table_;
|
||||
|
||||
bool use_content_size_;
|
||||
bool movable_;
|
||||
bool resizable_;
|
||||
bool maximizable_;
|
||||
bool minimizable_;
|
||||
std::string title_;
|
||||
gfx::Size widget_size_;
|
||||
|
||||
|
||||
@@ -89,10 +89,18 @@ bool NativeWindowViews::PreHandleMSG(
|
||||
if (HIWORD(w_param) == THBN_CLICKED)
|
||||
return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param));
|
||||
return false;
|
||||
|
||||
case WM_SIZE:
|
||||
// Handle window state change.
|
||||
HandleSizeEvent(w_param, l_param);
|
||||
return false;
|
||||
|
||||
case WM_MOVING: {
|
||||
if (!movable_)
|
||||
::GetWindowRect(GetAcceleratedWidget(), (LPRECT)l_param);
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
||||
details->SetString("resourceType",
|
||||
info ? ResourceTypeToString(info->GetResourceType())
|
||||
: "other");
|
||||
scoped_ptr<base::ListValue> list(new base::ListValue);
|
||||
GetUploadData(list.get(), request);
|
||||
if (!list->empty())
|
||||
details->Set("uploadData", list.Pass());
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.36.3</string>
|
||||
<string>0.36.6</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.36.3</string>
|
||||
<string>0.36.6</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -56,8 +56,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,36,3,0
|
||||
PRODUCTVERSION 0,36,3,0
|
||||
FILEVERSION 0,36,6,0
|
||||
PRODUCTVERSION 0,36,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -74,12 +74,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "0.36.3"
|
||||
VALUE "FileVersion", "0.36.6"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "0.36.3"
|
||||
VALUE "ProductVersion", "0.36.6"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -38,6 +38,7 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int default_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
@@ -47,6 +48,7 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
|
||||
@@ -29,6 +29,7 @@ class GtkMessageBox {
|
||||
GtkMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
@@ -60,9 +61,10 @@ class GtkMessageBox {
|
||||
|
||||
// Add buttons.
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
gtk_dialog_add_button(GTK_DIALOG(dialog_),
|
||||
TranslateToStock(i, buttons[i]),
|
||||
i);
|
||||
GtkWidget* button = gtk_dialog_add_button(
|
||||
GTK_DIALOG(dialog_), TranslateToStock(i, buttons[i]), i);
|
||||
if (static_cast<int>(i) == default_id)
|
||||
gtk_widget_grab_focus(button);
|
||||
}
|
||||
|
||||
// Parent window.
|
||||
@@ -161,19 +163,21 @@ void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
|
||||
int ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
return GtkMessageBox(parent, type, buttons, cancel_id, title, message, detail,
|
||||
icon).RunSynchronous();
|
||||
return GtkMessageBox(parent, type, buttons, default_id, cancel_id,
|
||||
title, message, detail, icon).RunSynchronous();
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -181,13 +185,13 @@ void ShowMessageBox(NativeWindow* parent,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
(new GtkMessageBox(parent, type, buttons, cancel_id, title, message, detail,
|
||||
icon))->RunAsynchronous(callback);
|
||||
(new GtkMessageBox(parent, type, buttons, default_id, cancel_id,
|
||||
title, message, detail, icon))->RunAsynchronous(callback);
|
||||
}
|
||||
|
||||
void ShowErrorBox(const base::string16& title, const base::string16& content) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, { "OK" }, 0, "Error",
|
||||
GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, { "OK" }, -1, 0, "Error",
|
||||
base::UTF16ToUTF8(title).c_str(),
|
||||
base::UTF16ToUTF8(content).c_str(),
|
||||
gfx::ImageSkia()).RunSynchronous();
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace {
|
||||
NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail) {
|
||||
@@ -82,6 +83,15 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
[button setTag:i];
|
||||
}
|
||||
|
||||
NSArray* ns_buttons = [alert buttons];
|
||||
if (default_id >= 0 && default_id < static_cast<int>([ns_buttons count])) {
|
||||
// Focus the button at default_id if the user opted to do so.
|
||||
// The first button added gets set as the default selected.
|
||||
// So remove that default, and make the requested button the default.
|
||||
[[ns_buttons objectAtIndex:0] setKeyEquivalent:@""];
|
||||
[[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"];
|
||||
}
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
@@ -94,6 +104,7 @@ void SetReturnCode(int* ret_code, int result) {
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -101,7 +112,8 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, title, message, detail);
|
||||
parent_window, type, buttons, default_id, title, message,
|
||||
detail);
|
||||
|
||||
// Use runModal for synchronous alert without parent, since we don't have a
|
||||
// window to wait for.
|
||||
@@ -127,6 +139,7 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -135,7 +148,8 @@ void ShowMessageBox(NativeWindow* parent_window,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, title, message, detail);
|
||||
parent_window, type, buttons, default_id, title, message,
|
||||
detail);
|
||||
ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback
|
||||
andAlert:alert
|
||||
callEndModal:false];
|
||||
|
||||
@@ -72,6 +72,7 @@ void MapToCommonID(const std::vector<base::string16>& buttons,
|
||||
int ShowMessageBoxUTF16(HWND parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<base::string16>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const base::string16& title,
|
||||
@@ -88,6 +89,9 @@ int ShowMessageBoxUTF16(HWND parent,
|
||||
config.hInstance = GetModuleHandle(NULL);
|
||||
config.dwFlags = flags;
|
||||
|
||||
if (default_id > 0)
|
||||
config.nDefaultButton = kIDStart + default_id;
|
||||
|
||||
// TaskDialogIndirect doesn't allow empty name, if we set empty title it
|
||||
// will show "electron.exe" in title.
|
||||
base::string16 app_name = base::UTF8ToUTF16(Browser::Get()->GetName());
|
||||
@@ -156,6 +160,7 @@ void RunMessageBoxInNewThread(base::Thread* thread,
|
||||
NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -163,8 +168,8 @@ void RunMessageBoxInNewThread(base::Thread* thread,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
int result = ShowMessageBox(parent, type, buttons, cancel_id, options, title,
|
||||
message, detail, icon);
|
||||
int result = ShowMessageBox(parent, type, buttons, default_id,
|
||||
cancel_id, options, title, message, detail, icon);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
|
||||
content::BrowserThread::DeleteSoon(
|
||||
@@ -176,6 +181,7 @@ void RunMessageBoxInNewThread(base::Thread* thread,
|
||||
int ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -194,6 +200,7 @@ int ShowMessageBox(NativeWindow* parent,
|
||||
return ShowMessageBoxUTF16(hwnd_parent,
|
||||
type,
|
||||
utf16_buttons,
|
||||
default_id,
|
||||
cancel_id,
|
||||
options,
|
||||
base::UTF8ToUTF16(title),
|
||||
@@ -205,6 +212,7 @@ int ShowMessageBox(NativeWindow* parent,
|
||||
void ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int default_id,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
@@ -224,13 +232,13 @@ void ShowMessageBox(NativeWindow* parent,
|
||||
unretained->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
|
||||
parent, type, buttons, cancel_id, options, title, message,
|
||||
detail, icon, callback));
|
||||
parent, type, buttons, default_id, cancel_id, options, title,
|
||||
message, detail, icon, callback));
|
||||
}
|
||||
|
||||
void ShowErrorBox(const base::string16& title, const base::string16& content) {
|
||||
ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, 0, 0, L"Error", title,
|
||||
content, gfx::ImageSkia());
|
||||
ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, -1, 0, 0, L"Error",
|
||||
title, content, gfx::ImageSkia());
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user