mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ceca9f426 | ||
|
|
720dc92efe | ||
|
|
73ded9d378 | ||
|
|
48975d04e0 | ||
|
|
417e97eef0 | ||
|
|
b52c07f650 | ||
|
|
1a93b1db52 | ||
|
|
b547772c68 | ||
|
|
d822e0d720 | ||
|
|
29c574cf0f | ||
|
|
3840a10da6 | ||
|
|
9bb87af66b | ||
|
|
9ec60cd585 | ||
|
|
8960aa956c | ||
|
|
de17894fce | ||
|
|
6c44553456 | ||
|
|
cc2a9f617d | ||
|
|
1578d2fda9 | ||
|
|
da724d65d7 | ||
|
|
eb92e9cdd8 | ||
|
|
5a980497e8 | ||
|
|
72eb87a631 | ||
|
|
df35700b94 | ||
|
|
ea1b89c699 | ||
|
|
a8658b7dca | ||
|
|
e64fbe3529 | ||
|
|
97c90d31d3 | ||
|
|
a1ec07e07e | ||
|
|
d701e1aa40 | ||
|
|
6d25c81bd1 | ||
|
|
58f1907579 | ||
|
|
3250764e72 | ||
|
|
462e6e0a82 | ||
|
|
59269a70a0 | ||
|
|
8df8b5731e | ||
|
|
dbab889fcc | ||
|
|
891d107a51 | ||
|
|
3ea878941b | ||
|
|
f25cf7481f | ||
|
|
d342c9a6df | ||
|
|
a1f0c24bf4 | ||
|
|
a11d8ea558 | ||
|
|
f6263f8c6b | ||
|
|
d3055a5ca0 | ||
|
|
5010c15ffc | ||
|
|
6656afd57f | ||
|
|
8cf9df2d8d | ||
|
|
3145c78b61 | ||
|
|
de3ccc4b98 | ||
|
|
d0c6176640 | ||
|
|
7cdfa44438 | ||
|
|
10faf314d4 | ||
|
|
378e81ffaa | ||
|
|
423ea00263 | ||
|
|
666aca7803 | ||
|
|
135aca02af | ||
|
|
7ab8134613 | ||
|
|
57244e4718 | ||
|
|
8eb87c5d2b | ||
|
|
02cadde8de | ||
|
|
88ab23def9 | ||
|
|
db6d8de5dc | ||
|
|
652843f447 | ||
|
|
21e5054fac | ||
|
|
a2c26b8c74 | ||
|
|
2cd6ad1a97 | ||
|
|
78e55414d4 | ||
|
|
17628b3e40 | ||
|
|
736fe0c1db | ||
|
|
ed4c69343f | ||
|
|
4421fbf9f3 | ||
|
|
002eb1a326 | ||
|
|
4baaf03ac7 | ||
|
|
c2bfc60a59 | ||
|
|
f78dcfb8e0 | ||
|
|
91bfd1f77c | ||
|
|
9ee0d46734 | ||
|
|
ca2cb9c9ba | ||
|
|
00c484c68b | ||
|
|
38c33d69ae | ||
|
|
5ad3fff6a0 | ||
|
|
cca4f4abd5 | ||
|
|
e54fda6b34 | ||
|
|
1e9eccf959 | ||
|
|
4b06c0645c | ||
|
|
c6f870d4e4 | ||
|
|
053594eae8 | ||
|
|
1615c97ce8 | ||
|
|
940db1d1dd | ||
|
|
56dfef8d0d | ||
|
|
3ffd774405 | ||
|
|
076942ca7a | ||
|
|
b6e8420bf2 | ||
|
|
096439dce7 | ||
|
|
346fb745f2 | ||
|
|
020ccd8018 | ||
|
|
ce24226128 | ||
|
|
b3c51e46e7 | ||
|
|
886db7a3f2 | ||
|
|
6c5dde60a3 | ||
|
|
7f06072420 | ||
|
|
5708b7fbec | ||
|
|
5863ed4c33 | ||
|
|
14388feb23 | ||
|
|
e5f852d7d5 | ||
|
|
83e8ceceda | ||
|
|
3ef54147c9 | ||
|
|
f8df377631 | ||
|
|
868dee55de | ||
|
|
7dba4d1d8d | ||
|
|
6840d424cd | ||
|
|
34819140c3 | ||
|
|
9e8a118d10 | ||
|
|
0c7c6ddcc9 | ||
|
|
eadd2f8de6 | ||
|
|
04606a9f97 | ||
|
|
cb4309bbd9 | ||
|
|
1249196118 | ||
|
|
d6ae874038 | ||
|
|
e0e4c1b54c | ||
|
|
628fb5f5e9 | ||
|
|
967c273ddb | ||
|
|
a24d2921af | ||
|
|
5ae57baf11 | ||
|
|
7a390bdd4c | ||
|
|
72f4884127 | ||
|
|
6383eb876e | ||
|
|
c56b3425a9 | ||
|
|
2d3e938a7f | ||
|
|
260ec96edd | ||
|
|
4379d24e9d | ||
|
|
fedf764b7b | ||
|
|
39c6e2d2e5 | ||
|
|
35aaad68d7 | ||
|
|
779583adf5 | ||
|
|
a0784bd038 | ||
|
|
fc9612a5ed | ||
|
|
2cd5fb5694 | ||
|
|
da00329d78 | ||
|
|
fb99bfac52 | ||
|
|
7b19b6b4f2 | ||
|
|
d661099322 | ||
|
|
ea63a04388 | ||
|
|
6f92141587 | ||
|
|
078bd7fb5b | ||
|
|
1703a6f20e | ||
|
|
001b4a3179 | ||
|
|
aab78db8b8 | ||
|
|
9793473b10 | ||
|
|
a89e5592f2 | ||
|
|
989351a41d | ||
|
|
6fb8b2ce4f | ||
|
|
f0be4025a5 | ||
|
|
49da74f976 | ||
|
|
1146441c2a | ||
|
|
d01c200345 | ||
|
|
b7cdb00d09 | ||
|
|
cb92df687d | ||
|
|
2e51afcd9e | ||
|
|
fdc01b8ba8 | ||
|
|
e0528655a8 | ||
|
|
08983e1ba9 | ||
|
|
7d456d3556 | ||
|
|
df4b5f4ede | ||
|
|
fb537d91fc | ||
|
|
b158427271 | ||
|
|
083d0b8b60 | ||
|
|
a2857d2dca | ||
|
|
a90a994a89 | ||
|
|
a2d1ec2c1f | ||
|
|
9b25c16980 | ||
|
|
810f14aecb | ||
|
|
e627592eed | ||
|
|
3e6394a004 | ||
|
|
02e28ea758 | ||
|
|
b98cdf71c4 | ||
|
|
8fca1f52d3 | ||
|
|
1ff00281f3 | ||
|
|
7bcbad925e | ||
|
|
3fa1f3ca6f | ||
|
|
5e7f1ce383 | ||
|
|
79b0fe967b | ||
|
|
d3204e9a9d | ||
|
|
808ceb8811 | ||
|
|
74603624df | ||
|
|
0c0446e254 | ||
|
|
41c1a34b4f | ||
|
|
519b51f055 | ||
|
|
5e2481e631 | ||
|
|
f8786e9d17 | ||
|
|
85a4ff83da | ||
|
|
7231de7c2b | ||
|
|
ae52af3870 | ||
|
|
1569dfa2e8 | ||
|
|
54dac0f37a | ||
|
|
9cf09b8850 | ||
|
|
ae3b47aa75 | ||
|
|
99e8238f90 | ||
|
|
f5a1ffcbd4 | ||
|
|
7042054913 | ||
|
|
16fd56c083 | ||
|
|
9afc016ff9 | ||
|
|
3064afbac4 | ||
|
|
1979b42ee7 | ||
|
|
68155e5fb7 | ||
|
|
9f0571772d | ||
|
|
07cf2eac4e | ||
|
|
822efa2f97 | ||
|
|
0e888ccf22 | ||
|
|
f5b502186b | ||
|
|
4e94e0c82f | ||
|
|
3315e6bda5 | ||
|
|
506d6688e0 | ||
|
|
c7518f84f8 | ||
|
|
cb3e758bc6 | ||
|
|
2bcf8c7bc2 | ||
|
|
bea45867d2 | ||
|
|
70987a9029 | ||
|
|
5c85c2e5d5 | ||
|
|
6f137d62c7 | ||
|
|
3533be2f9f | ||
|
|
a1b28c362d | ||
|
|
26525d6b0f | ||
|
|
16acd669a7 | ||
|
|
a3463119e8 | ||
|
|
9159b06835 | ||
|
|
4c76112698 | ||
|
|
d158dbdb68 | ||
|
|
7576de639b | ||
|
|
960b279419 | ||
|
|
6902b876a8 | ||
|
|
dcd10d1e4b | ||
|
|
783e4bc591 | ||
|
|
b5c5cce725 | ||
|
|
fcf4da1097 | ||
|
|
1c907ffa36 | ||
|
|
ebb031dafe | ||
|
|
bf5b084945 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -19,3 +19,6 @@
|
||||
[submodule "vendor/requests"]
|
||||
path = vendor/requests
|
||||
url = https://github.com/kennethreitz/requests
|
||||
[submodule "vendor/boto"]
|
||||
path = vendor/boto
|
||||
url = https://github.com/boto/boto.git
|
||||
|
||||
@@ -17,5 +17,8 @@ matrix:
|
||||
env: TARGET_ARCH=arm
|
||||
- os: linux
|
||||
env: TARGET_ARCH=ia32
|
||||
allow_failures:
|
||||
- env: TARGET_ARCH=arm
|
||||
- env: TARGET_ARCH=ia32
|
||||
|
||||
script: './script/cibuild'
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to uphold this code.
|
||||
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#Electron/opensource@github.com
|
||||
|
||||
The following is a set of guidelines for contributing to Electron.
|
||||
These are just guidelines, not rules, use your best judgment and feel free to
|
||||
propose changes to this document in a pull request.
|
||||
|
||||
@@ -33,8 +33,8 @@ npm install electron-prebuilt --save-dev
|
||||
|
||||
## 참조문서
|
||||
|
||||
[docs](https://github.com/preco21/electron/tree/master/docs) 에 프레임워크 사용 가이드와 API 레퍼런스가 있습니다.
|
||||
또한, Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법 문서에 포함되어 있으니 참고바랍니다.
|
||||
[docs](https://github.com/atom/electron/tree/master/docs/README-ko.md) 에 프레임워크 사용 가이드와 API 레퍼런스가 있습니다.
|
||||
추가적으로 Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문서에 포함되어 있으니 참고하기 바랍니다.
|
||||
|
||||
## 커뮤니티
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ editor](https://github.com/atom/atom).
|
||||
Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important
|
||||
announcements.
|
||||
|
||||
This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to uphold this code.
|
||||
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#Electron/opensource@github.com
|
||||
|
||||
## Downloads
|
||||
|
||||
Prebuilt binaries and debug symbols of Electron for Linux, Windows and Mac can
|
||||
|
||||
16
atom.gyp
16
atom.gyp
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '0.29.0',
|
||||
'version%': '0.30.1',
|
||||
},
|
||||
'includes': [
|
||||
'filenames.gypi',
|
||||
@@ -116,6 +116,15 @@
|
||||
],
|
||||
}], # OS!="mac"
|
||||
['OS=="win"', {
|
||||
'include_dirs': [
|
||||
'<(libchromiumcontent_dir)/gen/ui/resources',
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCManifestTool': {
|
||||
'EmbedManifest': 'true',
|
||||
'AdditionalManifestFiles': 'atom/browser/resources/win/atom.manifest',
|
||||
}
|
||||
},
|
||||
'copies': [
|
||||
{
|
||||
'variables': {
|
||||
@@ -263,8 +272,9 @@
|
||||
'libraries': [
|
||||
'-limm32.lib',
|
||||
'-loleacc.lib',
|
||||
'-lComdlg32.lib',
|
||||
'-lWininet.lib',
|
||||
'-lcomctl32.lib',
|
||||
'-lcomdlg32.lib',
|
||||
'-lwininet.lib',
|
||||
],
|
||||
},
|
||||
'dependencies': [
|
||||
|
||||
@@ -41,27 +41,25 @@ namespace {
|
||||
|
||||
void ShowMessageBox(int type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::vector<std::string>& texts,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
atom::NativeWindow* window,
|
||||
mate::Arguments* args) {
|
||||
// FIXME We are exceeding the parameters limit of base::Bind here, so we have
|
||||
// to pass some parameters in an array. We should remove this once we have
|
||||
// variadic template support in base::Bind.
|
||||
const std::string& title = texts[0];
|
||||
const std::string& message = texts[1];
|
||||
const std::string& detail = texts[2];
|
||||
|
||||
v8::Local<v8::Value> peek = args->PeekNext();
|
||||
atom::MessageBoxCallback callback;
|
||||
if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(),
|
||||
peek,
|
||||
&callback)) {
|
||||
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, title,
|
||||
message, detail, icon, callback);
|
||||
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, cancel_id,
|
||||
options, title, message, detail, icon, callback);
|
||||
} else {
|
||||
int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type,
|
||||
buttons, title, message, detail, icon);
|
||||
buttons, cancel_id, options, title,
|
||||
message, detail, icon);
|
||||
args->Return(chosen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
#include "atom/browser/net/adapter_request_job.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
@@ -25,7 +26,7 @@ namespace mate {
|
||||
template<>
|
||||
struct Converter<const net::URLRequest*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const net::URLRequest* val) {
|
||||
const net::URLRequest* val) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetValue("method", val->method())
|
||||
.SetValue("url", val->url().spec())
|
||||
@@ -34,8 +35,24 @@ struct Converter<const net::URLRequest*> {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
template<>
|
||||
struct Converter<net::URLRequestContextGetter*> {
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
|
||||
net::URLRequestContextGetter** out) {
|
||||
if (val->IsNull()) {
|
||||
*out = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
atom::api::Session* session;
|
||||
if (!Converter<atom::api::Session*>::FromV8(isolate, val, &session))
|
||||
return false;
|
||||
*out = session->browser_context()->GetRequestContext();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -63,16 +80,13 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
registry_(registry) {
|
||||
}
|
||||
|
||||
// AdapterRequestJob:
|
||||
void GetJobTypeInUI() override {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
void GetJobTypeInUI(const Protocol::JsProtocolHandler& callback) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
v8::Locker locker(registry_->isolate());
|
||||
v8::HandleScope handle_scope(registry_->isolate());
|
||||
|
||||
// Call the JS handler.
|
||||
Protocol::JsProtocolHandler callback =
|
||||
registry_->GetProtocolHandler(request()->url().scheme());
|
||||
v8::Local<v8::Value> result = callback.Run(request());
|
||||
|
||||
// Determine the type of the job we are going to create.
|
||||
@@ -127,13 +141,16 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
} else if (name == "RequestHttpJob") {
|
||||
GURL url;
|
||||
std::string method, referrer;
|
||||
net::URLRequestContextGetter* getter =
|
||||
registry_->browser_context()->GetRequestContext();
|
||||
dict.Get("url", &url);
|
||||
dict.Get("method", &method);
|
||||
dict.Get("referrer", &referrer);
|
||||
dict.Get("session", &getter);
|
||||
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateHttpJobAndStart, GetWeakPtr(),
|
||||
registry_->browser_context(), url, method, referrer));
|
||||
base::Unretained(getter), url, method, referrer));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -152,6 +169,14 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
GetWeakPtr(), net::ERR_NOT_IMPLEMENTED));
|
||||
}
|
||||
|
||||
// AdapterRequestJob:
|
||||
void GetJobType() override {
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&CustomProtocolRequestJob::GetJobTypeInUI,
|
||||
base::Unretained(this),
|
||||
registry_->GetProtocolHandler(request()->url().scheme())));
|
||||
}
|
||||
|
||||
private:
|
||||
Protocol* registry_; // Weak, the Protocol class is expected to live forever.
|
||||
};
|
||||
@@ -189,6 +214,26 @@ class CustomProtocolHandler : public ProtocolHandler {
|
||||
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
|
||||
};
|
||||
|
||||
std::string ConvertErrorCode(int error_code) {
|
||||
switch (error_code) {
|
||||
case Protocol::ERR_SCHEME_REGISTERED:
|
||||
return "The Scheme is already registered";
|
||||
case Protocol::ERR_SCHEME_UNREGISTERED:
|
||||
return "The Scheme has not been registered";
|
||||
case Protocol::ERR_SCHEME_INTERCEPTED:
|
||||
return "There is no protocol handler to intercept";
|
||||
case Protocol::ERR_SCHEME_UNINTERCEPTED:
|
||||
return "The protocol is not intercepted";
|
||||
case Protocol::ERR_NO_SCHEME:
|
||||
return "The Scheme does not exist.";
|
||||
case Protocol::ERR_SCHEME:
|
||||
return "Cannot intercept custom protocols";
|
||||
default:
|
||||
NOTREACHED();
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Protocol::Protocol(AtomBrowserContext* browser_context)
|
||||
@@ -202,42 +247,30 @@ Protocol::JsProtocolHandler Protocol::GetProtocolHandler(
|
||||
return protocol_handlers_[scheme];
|
||||
}
|
||||
|
||||
void Protocol::OnIOActionCompleted(const JsCompletionCallback& callback,
|
||||
int error) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
|
||||
if (error) {
|
||||
callback.Run(v8::Exception::Error(
|
||||
mate::StringToV8(isolate(), ConvertErrorCode(error))));
|
||||
return;
|
||||
}
|
||||
|
||||
callback.Run(v8::Null(isolate()));
|
||||
}
|
||||
|
||||
mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("registerProtocol", &Protocol::RegisterProtocol)
|
||||
.SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol)
|
||||
.SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes)
|
||||
.SetMethod("isHandledProtocol", &Protocol::IsHandledProtocol)
|
||||
.SetMethod("interceptProtocol", &Protocol::InterceptProtocol)
|
||||
.SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol);
|
||||
}
|
||||
|
||||
void Protocol::RegisterProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsProtocolHandler& callback) {
|
||||
if (ContainsKey(protocol_handlers_, scheme) ||
|
||||
job_factory_->IsHandledProtocol(scheme))
|
||||
return node::ThrowError(isolate, "The scheme is already registered");
|
||||
|
||||
protocol_handlers_[scheme] = callback;
|
||||
BrowserThread::PostTask(BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::RegisterProtocolInIO,
|
||||
base::Unretained(this), scheme));
|
||||
}
|
||||
|
||||
void Protocol::UnregisterProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme) {
|
||||
ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme));
|
||||
if (it == protocol_handlers_.end())
|
||||
return node::ThrowError(isolate, "The scheme has not been registered");
|
||||
|
||||
protocol_handlers_.erase(it);
|
||||
BrowserThread::PostTask(BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::UnregisterProtocolInIO,
|
||||
base::Unretained(this), scheme));
|
||||
.SetMethod("_registerProtocol", &Protocol::RegisterProtocol)
|
||||
.SetMethod("_unregisterProtocol", &Protocol::UnregisterProtocol)
|
||||
.SetMethod("_interceptProtocol", &Protocol::InterceptProtocol)
|
||||
.SetMethod("_uninterceptProtocol", &Protocol::UninterceptProtocol);
|
||||
}
|
||||
|
||||
void Protocol::RegisterStandardSchemes(
|
||||
@@ -245,109 +278,127 @@ void Protocol::RegisterStandardSchemes(
|
||||
atom::AtomBrowserClient::SetCustomSchemes(schemes);
|
||||
}
|
||||
|
||||
bool Protocol::IsHandledProtocol(const std::string& scheme) {
|
||||
return job_factory_->IsHandledProtocol(scheme);
|
||||
void Protocol::IsHandledProtocol(const std::string& scheme,
|
||||
const net::CompletionCallback& callback) {
|
||||
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&AtomURLRequestJobFactory::IsHandledProtocol,
|
||||
base::Unretained(job_factory_), scheme),
|
||||
callback);
|
||||
}
|
||||
|
||||
void Protocol::RegisterProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsProtocolHandler& handler,
|
||||
const JsCompletionCallback& callback) {
|
||||
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&Protocol::RegisterProtocolInIO,
|
||||
base::Unretained(this), scheme, handler),
|
||||
base::Bind(&Protocol::OnIOActionCompleted,
|
||||
base::Unretained(this), callback));
|
||||
}
|
||||
|
||||
void Protocol::UnregisterProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsCompletionCallback& callback) {
|
||||
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&Protocol::UnregisterProtocolInIO,
|
||||
base::Unretained(this), scheme),
|
||||
base::Bind(&Protocol::OnIOActionCompleted,
|
||||
base::Unretained(this), callback));
|
||||
}
|
||||
|
||||
void Protocol::InterceptProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsProtocolHandler& callback) {
|
||||
if (!job_factory_->HasProtocolHandler(scheme))
|
||||
return node::ThrowError(isolate, "Scheme does not exist.");
|
||||
|
||||
if (ContainsKey(protocol_handlers_, scheme))
|
||||
return node::ThrowError(isolate, "Cannot intercept custom procotols");
|
||||
|
||||
protocol_handlers_[scheme] = callback;
|
||||
BrowserThread::PostTask(BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::InterceptProtocolInIO,
|
||||
base::Unretained(this), scheme));
|
||||
const JsProtocolHandler& handler,
|
||||
const JsCompletionCallback& callback) {
|
||||
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&Protocol::InterceptProtocolInIO,
|
||||
base::Unretained(this), scheme, handler),
|
||||
base::Bind(&Protocol::OnIOActionCompleted,
|
||||
base::Unretained(this), callback));
|
||||
}
|
||||
|
||||
void Protocol::UninterceptProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme) {
|
||||
const std::string& scheme,
|
||||
const JsCompletionCallback& callback) {
|
||||
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&Protocol::UninterceptProtocolInIO,
|
||||
base::Unretained(this), scheme),
|
||||
base::Bind(&Protocol::OnIOActionCompleted,
|
||||
base::Unretained(this), callback));
|
||||
}
|
||||
|
||||
int Protocol::RegisterProtocolInIO(const std::string& scheme,
|
||||
const JsProtocolHandler& handler) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (ContainsKey(protocol_handlers_, scheme) ||
|
||||
job_factory_->IsHandledProtocol(scheme)) {
|
||||
return ERR_SCHEME_REGISTERED;
|
||||
}
|
||||
|
||||
protocol_handlers_[scheme] = handler;
|
||||
job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int Protocol::UnregisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme));
|
||||
if (it == protocol_handlers_.end())
|
||||
return node::ThrowError(isolate, "The scheme has not been registered");
|
||||
if (it == protocol_handlers_.end()) {
|
||||
return ERR_SCHEME_UNREGISTERED;
|
||||
}
|
||||
|
||||
protocol_handlers_.erase(it);
|
||||
BrowserThread::PostTask(BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::UninterceptProtocolInIO,
|
||||
base::Unretained(this), scheme));
|
||||
}
|
||||
|
||||
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
|
||||
job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this));
|
||||
BrowserThread::PostTask(BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"registered", scheme));
|
||||
}
|
||||
|
||||
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
|
||||
job_factory_->SetProtocolHandler(scheme, NULL);
|
||||
BrowserThread::PostTask(BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"unregistered", scheme));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
int Protocol::InterceptProtocolInIO(const std::string& scheme,
|
||||
const JsProtocolHandler& handler) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (!job_factory_->HasProtocolHandler(scheme))
|
||||
return ERR_NO_SCHEME;
|
||||
|
||||
if (ContainsKey(protocol_handlers_, scheme))
|
||||
return ERR_SCHEME;
|
||||
|
||||
protocol_handlers_[scheme] = handler;
|
||||
ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme);
|
||||
if (original_handler == NULL) {
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
|
||||
&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"error", "There is no protocol handler to intercpet"));
|
||||
return;
|
||||
if (original_handler == nullptr) {
|
||||
return ERR_SCHEME_INTERCEPTED;
|
||||
}
|
||||
|
||||
job_factory_->ReplaceProtocol(
|
||||
scheme, new CustomProtocolHandler(this, original_handler));
|
||||
BrowserThread::PostTask(BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"intercepted", scheme));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
int Protocol::UninterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme));
|
||||
if (it == protocol_handlers_.end())
|
||||
return ERR_SCHEME_UNREGISTERED;
|
||||
|
||||
protocol_handlers_.erase(it);
|
||||
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
|
||||
job_factory_->GetProtocolHandler(scheme));
|
||||
if (handler->original_handler() == NULL) {
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
|
||||
&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"error", "The protocol is not intercpeted"));
|
||||
return;
|
||||
if (handler->original_handler() == nullptr) {
|
||||
return ERR_SCHEME_UNINTERCEPTED;
|
||||
}
|
||||
|
||||
// Reset the protocol handler to the orignal one and delete current protocol
|
||||
// handler.
|
||||
ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
|
||||
delete job_factory_->ReplaceProtocol(scheme, original_handler);
|
||||
BrowserThread::PostTask(BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&Protocol::EmitEventInUI,
|
||||
base::Unretained(this),
|
||||
"unintercepted", scheme));
|
||||
}
|
||||
|
||||
void Protocol::EmitEventInUI(const std::string& event,
|
||||
const std::string& parameter) {
|
||||
Emit(event, parameter);
|
||||
return OK;
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "base/callback.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "net/base/completion_callback.h"
|
||||
|
||||
namespace net {
|
||||
class URLRequest;
|
||||
@@ -26,8 +27,19 @@ namespace api {
|
||||
|
||||
class Protocol : public mate::EventEmitter {
|
||||
public:
|
||||
typedef base::Callback<v8::Local<v8::Value>(const net::URLRequest*)>
|
||||
JsProtocolHandler;
|
||||
using JsProtocolHandler =
|
||||
base::Callback<v8::Local<v8::Value>(const net::URLRequest*)>;
|
||||
using JsCompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
|
||||
enum {
|
||||
OK = 0,
|
||||
ERR_SCHEME_REGISTERED,
|
||||
ERR_SCHEME_UNREGISTERED,
|
||||
ERR_SCHEME_INTERCEPTED,
|
||||
ERR_SCHEME_UNINTERCEPTED,
|
||||
ERR_NO_SCHEME,
|
||||
ERR_SCHEME
|
||||
};
|
||||
|
||||
static mate::Handle<Protocol> Create(
|
||||
v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
||||
@@ -46,35 +58,41 @@ class Protocol : public mate::EventEmitter {
|
||||
private:
|
||||
typedef std::map<std::string, JsProtocolHandler> ProtocolHandlersMap;
|
||||
|
||||
// Callback called after performing action on IO thread.
|
||||
void OnIOActionCompleted(const JsCompletionCallback& callback,
|
||||
int error);
|
||||
|
||||
// Register schemes to standard scheme list.
|
||||
void RegisterStandardSchemes(const std::vector<std::string>& schemes);
|
||||
|
||||
// Returns whether a scheme has been registered.
|
||||
void IsHandledProtocol(const std::string& scheme,
|
||||
const net::CompletionCallback& callback);
|
||||
|
||||
// Register/unregister an networking |scheme| which would be handled by
|
||||
// |callback|.
|
||||
void RegisterProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsProtocolHandler& callback);
|
||||
void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme);
|
||||
|
||||
// Returns whether a scheme has been registered.
|
||||
// FIXME Should accept a callback and be asynchronous so we do not have to use
|
||||
// locks.
|
||||
bool IsHandledProtocol(const std::string& scheme);
|
||||
const JsProtocolHandler& handler,
|
||||
const JsCompletionCallback& callback);
|
||||
void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme,
|
||||
const JsCompletionCallback& callback);
|
||||
|
||||
// Intercept/unintercept an existing protocol handler.
|
||||
void InterceptProtocol(v8::Isolate* isolate,
|
||||
const std::string& scheme,
|
||||
const JsProtocolHandler& callback);
|
||||
void UninterceptProtocol(v8::Isolate* isolate, const std::string& scheme);
|
||||
const JsProtocolHandler& handler,
|
||||
const JsCompletionCallback& callback);
|
||||
void UninterceptProtocol(v8::Isolate* isolate, const std::string& scheme,
|
||||
const JsCompletionCallback& callback);
|
||||
|
||||
// The networking related operations have to be done in IO thread.
|
||||
void RegisterProtocolInIO(const std::string& scheme);
|
||||
void UnregisterProtocolInIO(const std::string& scheme);
|
||||
void InterceptProtocolInIO(const std::string& scheme);
|
||||
void UninterceptProtocolInIO(const std::string& scheme);
|
||||
|
||||
// Do protocol.emit(event, parameter) under UI thread.
|
||||
void EmitEventInUI(const std::string& event, const std::string& parameter);
|
||||
int RegisterProtocolInIO(const std::string& scheme,
|
||||
const JsProtocolHandler& handler);
|
||||
int UnregisterProtocolInIO(const std::string& scheme);
|
||||
int InterceptProtocolInIO(const std::string& scheme,
|
||||
const JsProtocolHandler& handler);
|
||||
int UninterceptProtocolInIO(const std::string& scheme);
|
||||
|
||||
AtomBrowserContext* browser_context_;
|
||||
AtomURLRequestJobFactory* job_factory_;
|
||||
|
||||
@@ -5,22 +5,99 @@
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/atom_api_cookies.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "native_mate/callback.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/disk_cache/disk_cache.h"
|
||||
#include "net/proxy/proxy_service.h"
|
||||
#include "net/proxy/proxy_config_service_fixed.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
using content::StoragePartition;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ClearStorageDataOptions {
|
||||
GURL origin;
|
||||
uint32 storage_types = StoragePartition::REMOVE_DATA_MASK_ALL;
|
||||
uint32 quota_types = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL;
|
||||
};
|
||||
|
||||
uint32 GetStorageMask(const std::vector<std::string>& storage_types) {
|
||||
uint32 storage_mask = 0;
|
||||
for (const auto& it : storage_types) {
|
||||
auto type = base::StringToLowerASCII(it);
|
||||
if (type == "appcache")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
|
||||
else if (type == "cookies")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
|
||||
else if (type == "filesystem")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
|
||||
else if (type == "indexdb")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
|
||||
else if (type == "localstorage")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
|
||||
else if (type == "shadercache")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
|
||||
else if (type == "websql")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
|
||||
else if (type == "serviceworkers")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
|
||||
}
|
||||
return storage_mask;
|
||||
}
|
||||
|
||||
uint32 GetQuotaMask(const std::vector<std::string>& quota_types) {
|
||||
uint32 quota_mask = 0;
|
||||
for (const auto& it : quota_types) {
|
||||
auto type = base::StringToLowerASCII(it);
|
||||
if (type == "temporary")
|
||||
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY;
|
||||
else if (type == "persistent")
|
||||
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_PERSISTENT;
|
||||
else if (type == "syncable")
|
||||
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_SYNCABLE;
|
||||
}
|
||||
return quota_mask;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<ClearStorageDataOptions> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
ClearStorageDataOptions* out) {
|
||||
mate::Dictionary options;
|
||||
if (!ConvertFromV8(isolate, val, &options))
|
||||
return false;
|
||||
options.Get("origin", &out->origin);
|
||||
std::vector<std::string> types;
|
||||
if (options.Get("storages", &types))
|
||||
out->storage_types = GetStorageMask(types);
|
||||
if (options.Get("quotas", &types))
|
||||
out->quota_types = GetQuotaMask(types);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
@@ -80,6 +157,54 @@ class ResolveProxyHelper {
|
||||
DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper);
|
||||
};
|
||||
|
||||
// Runs the callback in UI thread.
|
||||
template <typename ...T>
|
||||
void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE, base::Bind(callback, result...));
|
||||
}
|
||||
|
||||
// Callback of HttpCache::GetBackend.
|
||||
void OnGetBackend(disk_cache::Backend** backend_ptr,
|
||||
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));
|
||||
} else {
|
||||
RunCallbackInUI<int>(callback, net::ERR_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearHttpCacheInIO(content::BrowserContext* browser_context,
|
||||
const net::CompletionCallback& callback) {
|
||||
auto request_context =
|
||||
browser_context->GetRequestContext()->GetURLRequestContext();
|
||||
auto http_cache = request_context->http_transaction_factory()->GetCache();
|
||||
if (!http_cache)
|
||||
RunCallbackInUI<int>(callback, net::ERR_FAILED);
|
||||
|
||||
// Call GetBackend and make the backend's ptr accessable in OnGetBackend.
|
||||
using BackendPtr = disk_cache::Backend*;
|
||||
BackendPtr* backend_ptr = new BackendPtr(nullptr);
|
||||
net::CompletionCallback on_get_backend =
|
||||
base::Bind(&OnGetBackend, base::Owned(backend_ptr), callback);
|
||||
int rv = http_cache->GetBackend(backend_ptr, on_get_backend);
|
||||
if (rv != net::ERR_IO_PENDING)
|
||||
on_get_backend.Run(net::OK);
|
||||
}
|
||||
|
||||
void SetProxyInIO(net::URLRequestContextGetter* getter,
|
||||
const std::string& proxy,
|
||||
const base::Closure& callback) {
|
||||
net::ProxyConfig config;
|
||||
config.proxy_rules().ParseFromString(proxy);
|
||||
auto proxy_service = getter->GetURLRequestContext()->proxy_service();
|
||||
proxy_service->ResetConfigService(new net::ProxyConfigServiceFixed(config));
|
||||
RunCallbackInUI(callback);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Session::Session(AtomBrowserContext* browser_context)
|
||||
@@ -94,6 +219,38 @@ void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
||||
new ResolveProxyHelper(browser_context_, url, callback);
|
||||
}
|
||||
|
||||
void Session::ClearCache(const net::CompletionCallback& callback) {
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&ClearHttpCacheInIO,
|
||||
base::Unretained(browser_context_),
|
||||
callback));
|
||||
}
|
||||
|
||||
void Session::ClearStorageData(mate::Arguments* args) {
|
||||
// clearStorageData([options, ]callback)
|
||||
ClearStorageDataOptions options;
|
||||
args->GetNext(&options);
|
||||
base::Closure callback;
|
||||
if (!args->GetNext(&callback)) {
|
||||
args->ThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
auto storage_partition =
|
||||
content::BrowserContext::GetStoragePartition(browser_context_, nullptr);
|
||||
storage_partition->ClearData(
|
||||
options.storage_types, options.quota_types, options.origin,
|
||||
content::StoragePartition::OriginMatcherFunction(),
|
||||
base::Time(), base::Time::Max(), callback);
|
||||
}
|
||||
|
||||
void Session::SetProxy(const std::string& proxy,
|
||||
const base::Closure& callback) {
|
||||
auto getter = browser_context_->GetRequestContext();
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&SetProxyInIO, base::Unretained(getter), proxy, callback));
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
||||
if (cookies_.IsEmpty()) {
|
||||
auto handle = atom::api::Cookies::Create(isolate, browser_context_);
|
||||
@@ -106,6 +263,9 @@ mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("resolveProxy", &Session::ResolveProxy)
|
||||
.SetMethod("clearCache", &Session::ClearCache)
|
||||
.SetMethod("clearStorageData", &Session::ClearStorageData)
|
||||
.SetMethod("setProxy", &Session::SetProxy)
|
||||
.SetProperty("cookies", &Session::Cookies);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "base/callback.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "net/base/completion_callback.h"
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace mate {
|
||||
class Arguments;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserContext;
|
||||
@@ -27,6 +31,8 @@ class Session: public mate::TrackableObject<Session> {
|
||||
static mate::Handle<Session> CreateFrom(
|
||||
v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
||||
|
||||
AtomBrowserContext* browser_context() const { return browser_context_; }
|
||||
|
||||
protected:
|
||||
explicit Session(AtomBrowserContext* browser_context);
|
||||
~Session();
|
||||
@@ -37,6 +43,9 @@ class Session: public mate::TrackableObject<Session> {
|
||||
|
||||
private:
|
||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
void ClearCache(const net::CompletionCallback& callback);
|
||||
void ClearStorageData(mate::Arguments* args);
|
||||
void SetProxy(const std::string& proxy, const base::Closure& callback);
|
||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||
|
||||
v8::Global<v8::Value> cookies_;
|
||||
|
||||
@@ -60,45 +60,44 @@ void Tray::OnBalloonClosed() {
|
||||
Emit("balloon-closed");
|
||||
}
|
||||
|
||||
void Tray::OnRightClicked(const gfx::Rect& bounds) {
|
||||
Emit("right-clicked", bounds);
|
||||
}
|
||||
|
||||
void Tray::OnDropFiles(const std::vector<std::string>& files) {
|
||||
Emit("drop-files", files);
|
||||
}
|
||||
|
||||
bool Tray::IsDestroyed() const {
|
||||
return !tray_icon_;
|
||||
}
|
||||
|
||||
void Tray::Destroy() {
|
||||
tray_icon_.reset();
|
||||
}
|
||||
|
||||
void Tray::SetImage(mate::Arguments* args, const gfx::Image& image) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetImage(image);
|
||||
}
|
||||
|
||||
void Tray::SetPressedImage(mate::Arguments* args, const gfx::Image& image) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetPressedImage(image);
|
||||
}
|
||||
|
||||
void Tray::SetToolTip(mate::Arguments* args, const std::string& tool_tip) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetToolTip(tool_tip);
|
||||
}
|
||||
|
||||
void Tray::SetTitle(mate::Arguments* args, const std::string& title) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetTitle(title);
|
||||
}
|
||||
|
||||
void Tray::SetHighlightMode(mate::Arguments* args, bool highlight) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetHighlightMode(highlight);
|
||||
}
|
||||
|
||||
void Tray::DisplayBalloon(mate::Arguments* args,
|
||||
const mate::Dictionary& options) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
|
||||
gfx::Image icon;
|
||||
options.Get("icon", &icon);
|
||||
base::string16 title, content;
|
||||
@@ -111,32 +110,28 @@ void Tray::DisplayBalloon(mate::Arguments* args,
|
||||
tray_icon_->DisplayBalloon(icon, title, content);
|
||||
}
|
||||
|
||||
void Tray::SetContextMenu(mate::Arguments* args, Menu* menu) {
|
||||
if (!CheckTrayLife(args))
|
||||
return;
|
||||
tray_icon_->SetContextMenu(menu->model());
|
||||
void Tray::PopContextMenu(mate::Arguments* args) {
|
||||
gfx::Point pos;
|
||||
args->GetNext(&pos);
|
||||
tray_icon_->PopContextMenu(pos);
|
||||
}
|
||||
|
||||
bool Tray::CheckTrayLife(mate::Arguments* args) {
|
||||
if (!tray_icon_) {
|
||||
args->ThrowError("Tray is already destroyed");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
void Tray::SetContextMenu(mate::Arguments* args, Menu* menu) {
|
||||
tray_icon_->SetContextMenu(menu->model());
|
||||
}
|
||||
|
||||
// static
|
||||
void Tray::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype) {
|
||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.SetMethod("destroy", &Tray::Destroy)
|
||||
.SetMethod("destroy", &Tray::Destroy, true)
|
||||
.SetMethod("setImage", &Tray::SetImage)
|
||||
.SetMethod("setPressedImage", &Tray::SetPressedImage)
|
||||
.SetMethod("setToolTip", &Tray::SetToolTip)
|
||||
.SetMethod("setTitle", &Tray::SetTitle)
|
||||
.SetMethod("setHighlightMode", &Tray::SetHighlightMode)
|
||||
.SetMethod("displayBalloon", &Tray::DisplayBalloon)
|
||||
.SetMethod("popContextMenu", &Tray::PopContextMenu)
|
||||
.SetMethod("_setContextMenu", &Tray::SetContextMenu);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define ATOM_BROWSER_API_ATOM_API_TRAY_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/browser/ui/tray_icon_observer.h"
|
||||
@@ -41,11 +42,16 @@ class Tray : public mate::EventEmitter,
|
||||
virtual ~Tray();
|
||||
|
||||
// TrayIconObserver:
|
||||
void OnClicked(const gfx::Rect&) override;
|
||||
void OnClicked(const gfx::Rect& bounds) override;
|
||||
void OnDoubleClicked() override;
|
||||
void OnBalloonShow() override;
|
||||
void OnBalloonClicked() override;
|
||||
void OnBalloonClosed() override;
|
||||
void OnRightClicked(const gfx::Rect& bounds) override;
|
||||
void OnDropFiles(const std::vector<std::string>& files) override;
|
||||
|
||||
// mate::Wrappable:
|
||||
bool IsDestroyed() const override;
|
||||
|
||||
void Destroy();
|
||||
void SetImage(mate::Arguments* args, const gfx::Image& image);
|
||||
@@ -54,11 +60,10 @@ class Tray : public mate::EventEmitter,
|
||||
void SetTitle(mate::Arguments* args, const std::string& title);
|
||||
void SetHighlightMode(mate::Arguments* args, bool highlight);
|
||||
void DisplayBalloon(mate::Arguments* args, const mate::Dictionary& options);
|
||||
void PopContextMenu(mate::Arguments* args);
|
||||
void SetContextMenu(mate::Arguments* args, Menu* menu);
|
||||
|
||||
private:
|
||||
bool CheckTrayLife(mate::Arguments* args);
|
||||
|
||||
scoped_ptr<TrayIcon> tray_icon_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Tray);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/url_request/static_http_user_agent_settings.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
@@ -51,6 +52,12 @@ struct PrintSettings {
|
||||
bool print_background;
|
||||
};
|
||||
|
||||
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
|
||||
std::string user_agent) {
|
||||
getter->GetURLRequestContext()->set_http_user_agent_settings(
|
||||
new net::StaticHttpUserAgentSettings("en-us,en", user_agent));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mate {
|
||||
@@ -142,6 +149,7 @@ WebContents::WebContents(content::WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
type_(REMOTE) {
|
||||
AttachAsUserData(web_contents);
|
||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||
}
|
||||
|
||||
WebContents::WebContents(const mate::Dictionary& options) {
|
||||
@@ -168,6 +176,8 @@ WebContents::WebContents(const mate::Dictionary& options) {
|
||||
AttachAsUserData(web_contents);
|
||||
InitWithWebContents(web_contents);
|
||||
|
||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||
|
||||
if (is_guest) {
|
||||
guest_delegate_->Initialize(this);
|
||||
|
||||
@@ -250,7 +260,7 @@ void WebContents::MoveContents(content::WebContents* source,
|
||||
}
|
||||
|
||||
void WebContents::CloseContents(content::WebContents* source) {
|
||||
Emit("closed");
|
||||
Emit("close");
|
||||
if (type_ == BROWSER_WINDOW)
|
||||
owner_window()->CloseContents(source);
|
||||
}
|
||||
@@ -547,6 +557,14 @@ bool WebContents::IsCrashed() const {
|
||||
|
||||
void WebContents::SetUserAgent(const std::string& user_agent) {
|
||||
web_contents()->SetUserAgentOverride(user_agent);
|
||||
scoped_refptr<net::URLRequestContextGetter> getter =
|
||||
web_contents()->GetBrowserContext()->GetRequestContext();
|
||||
getter->GetNetworkTaskRunner()->PostTask(FROM_HERE,
|
||||
base::Bind(&SetUserAgentInIO, getter, user_agent));
|
||||
}
|
||||
|
||||
std::string WebContents::GetUserAgent() {
|
||||
return web_contents()->GetUserAgentOverride();
|
||||
}
|
||||
|
||||
void WebContents::InsertCSS(const std::string& css) {
|
||||
@@ -619,9 +637,7 @@ void WebContents::InspectServiceWorker() {
|
||||
|
||||
v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
|
||||
if (session_.IsEmpty()) {
|
||||
mate::Handle<api::Session> handle = Session::CreateFrom(
|
||||
isolate,
|
||||
static_cast<AtomBrowserContext*>(web_contents()->GetBrowserContext()));
|
||||
auto handle = Session::CreateFrom(isolate, GetBrowserContext());
|
||||
session_.Reset(isolate, handle.ToV8());
|
||||
}
|
||||
return v8::Local<v8::Value>::New(isolate, session_);
|
||||
@@ -717,6 +733,14 @@ void WebContents::ReplaceMisspelling(const base::string16& word) {
|
||||
web_contents()->ReplaceMisspelling(word);
|
||||
}
|
||||
|
||||
void WebContents::Focus() {
|
||||
web_contents()->Focus();
|
||||
}
|
||||
|
||||
void WebContents::TabTraverse(bool reverse) {
|
||||
web_contents()->FocusThroughTabTraversal(reverse);
|
||||
}
|
||||
|
||||
bool WebContents::SendIPCMessage(const base::string16& channel,
|
||||
const base::ListValue& args) {
|
||||
return Send(new AtomViewMsg_Message(routing_id(), channel, args));
|
||||
@@ -740,8 +764,8 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
if (template_.IsEmpty())
|
||||
template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("destroy", &WebContents::Destroy)
|
||||
.SetMethod("isAlive", &WebContents::IsAlive)
|
||||
.SetMethod("destroy", &WebContents::Destroy, true)
|
||||
.SetMethod("isAlive", &WebContents::IsAlive, true)
|
||||
.SetMethod("getId", &WebContents::GetID)
|
||||
.SetMethod("equal", &WebContents::Equal)
|
||||
.SetMethod("_loadUrl", &WebContents::LoadURL)
|
||||
@@ -755,6 +779,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
|
||||
.SetMethod("_goToOffset", &WebContents::GoToOffset)
|
||||
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
||||
.SetMethod("setUserAgent", &WebContents::SetUserAgent)
|
||||
.SetMethod("getUserAgent", &WebContents::GetUserAgent)
|
||||
.SetMethod("insertCSS", &WebContents::InsertCSS)
|
||||
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript)
|
||||
.SetMethod("openDevTools", &WebContents::OpenDevTools)
|
||||
@@ -775,7 +800,9 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
|
||||
.SetMethod("unselect", &WebContents::Unselect)
|
||||
.SetMethod("replace", &WebContents::Replace)
|
||||
.SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
|
||||
.SetMethod("_send", &WebContents::SendIPCMessage)
|
||||
.SetMethod("focus", &WebContents::Focus)
|
||||
.SetMethod("tabTraverse", &WebContents::TabTraverse)
|
||||
.SetMethod("_send", &WebContents::SendIPCMessage, true)
|
||||
.SetMethod("setSize", &WebContents::SetSize)
|
||||
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
|
||||
.SetMethod("isGuest", &WebContents::IsGuest)
|
||||
@@ -792,6 +819,14 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
|
||||
isolate, v8::Local<v8::ObjectTemplate>::New(isolate, template_));
|
||||
}
|
||||
|
||||
bool WebContents::IsDestroyed() const {
|
||||
return !IsAlive();
|
||||
}
|
||||
|
||||
AtomBrowserContext* WebContents::GetBrowserContext() const {
|
||||
return static_cast<AtomBrowserContext*>(web_contents()->GetBrowserContext());
|
||||
}
|
||||
|
||||
void WebContents::OnRendererMessage(const base::string16& channel,
|
||||
const base::ListValue& args) {
|
||||
// webContents.emit(channel, new Event(), args...);
|
||||
|
||||
@@ -27,6 +27,7 @@ class Dictionary;
|
||||
namespace atom {
|
||||
|
||||
struct SetSizeParams;
|
||||
class AtomBrowserContext;
|
||||
class WebViewGuestDelegate;
|
||||
|
||||
namespace api {
|
||||
@@ -62,6 +63,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
void GoToOffset(int offset);
|
||||
bool IsCrashed() const;
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
std::string GetUserAgent();
|
||||
void InsertCSS(const std::string& css);
|
||||
void ExecuteJavaScript(const base::string16& code);
|
||||
void OpenDevTools(mate::Arguments* args);
|
||||
@@ -94,6 +96,10 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
void Replace(const base::string16& word);
|
||||
void ReplaceMisspelling(const base::string16& word);
|
||||
|
||||
// Focus.
|
||||
void Focus();
|
||||
void TabTraverse(bool reverse);
|
||||
|
||||
// Sending messages to browser.
|
||||
bool SendIPCMessage(const base::string16& channel,
|
||||
const base::ListValue& args);
|
||||
@@ -111,6 +117,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
// mate::Wrappable:
|
||||
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override;
|
||||
bool IsDestroyed() const override;
|
||||
|
||||
// content::WebContentsDelegate:
|
||||
bool AddMessageToConsole(content::WebContents* source,
|
||||
@@ -190,6 +197,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
REMOTE, // Thin wrap around an existing WebContents.
|
||||
};
|
||||
|
||||
AtomBrowserContext* GetBrowserContext() const;
|
||||
|
||||
// Called when received a message from renderer.
|
||||
void OnRendererMessage(const base::string16& channel,
|
||||
const base::ListValue& args);
|
||||
|
||||
@@ -177,6 +177,10 @@ mate::Wrappable* Window::New(v8::Isolate* isolate,
|
||||
return new Window(isolate, options);
|
||||
}
|
||||
|
||||
bool Window::IsDestroyed() const {
|
||||
return !window_ || window_->IsClosed();
|
||||
}
|
||||
|
||||
void Window::Destroy() {
|
||||
window_->CloseContents(nullptr);
|
||||
}
|
||||
@@ -447,6 +451,12 @@ void Window::ShowDefinitionForSelection() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
|
||||
gfx::Size extra_size;
|
||||
args->GetNext(&extra_size);
|
||||
window_->SetAspectRatio(aspect_ratio, extra_size);
|
||||
}
|
||||
|
||||
void Window::SetVisibleOnAllWorkspaces(bool visible) {
|
||||
return window_->SetVisibleOnAllWorkspaces(visible);
|
||||
}
|
||||
@@ -477,7 +487,7 @@ v8::Local<v8::Value> Window::DevToolsWebContents(v8::Isolate* isolate) {
|
||||
void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype) {
|
||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.SetMethod("destroy", &Window::Destroy)
|
||||
.SetMethod("destroy", &Window::Destroy, true)
|
||||
.SetMethod("close", &Window::Close)
|
||||
.SetMethod("isClosed", &Window::IsClosed)
|
||||
.SetMethod("focus", &Window::Focus)
|
||||
@@ -494,6 +504,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("isMinimized", &Window::IsMinimized)
|
||||
.SetMethod("setFullScreen", &Window::SetFullScreen)
|
||||
.SetMethod("isFullScreen", &Window::IsFullscreen)
|
||||
.SetMethod("setAspectRatio", &Window::SetAspectRatio)
|
||||
.SetMethod("getBounds", &Window::GetBounds)
|
||||
.SetMethod("setBounds", &Window::SetBounds)
|
||||
.SetMethod("getSize", &Window::GetSize)
|
||||
@@ -540,9 +551,9 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("showDefinitionForSelection",
|
||||
&Window::ShowDefinitionForSelection)
|
||||
#endif
|
||||
.SetProperty("id", &Window::ID)
|
||||
.SetProperty("webContents", &Window::WebContents)
|
||||
.SetProperty("devToolsWebContents", &Window::DevToolsWebContents);
|
||||
.SetProperty("id", &Window::ID, true)
|
||||
.SetProperty("webContents", &Window::WebContents, true)
|
||||
.SetProperty("devToolsWebContents", &Window::DevToolsWebContents, true);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -73,6 +73,9 @@ class Window : public mate::TrackableObject<Window>,
|
||||
void OnDevToolsClosed() override;
|
||||
void OnExecuteWindowsCommand(const std::string& command_name) override;
|
||||
|
||||
// mate::Wrappable:
|
||||
bool IsDestroyed() const override;
|
||||
|
||||
private:
|
||||
// APIs for NativeWindow.
|
||||
void Destroy();
|
||||
@@ -131,6 +134,7 @@ class Window : public mate::TrackableObject<Window>,
|
||||
bool IsMenuBarAutoHide();
|
||||
void SetMenuBarVisibility(bool visible);
|
||||
bool IsMenuBarVisible();
|
||||
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void ShowDefinitionForSelection();
|
||||
|
||||
@@ -25,6 +25,13 @@ if process.platform is 'darwin'
|
||||
show: bindings.dockShow
|
||||
setMenu: bindings.dockSetMenu
|
||||
|
||||
appPath = null
|
||||
app.setAppPath = (path) ->
|
||||
appPath = path
|
||||
|
||||
app.getAppPath = ->
|
||||
appPath
|
||||
|
||||
# Be compatible with old API.
|
||||
app.once 'ready', -> @emit 'finish-launching'
|
||||
app.terminate = app.quit
|
||||
|
||||
@@ -16,15 +16,29 @@ BrowserWindow::_init = ->
|
||||
options = show: true, width: 800, height: 600
|
||||
ipc.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
|
||||
|
||||
# window.move(...)
|
||||
# window.resizeTo(...)
|
||||
# window.moveTo(...)
|
||||
@webContents.on 'move', (event, size) =>
|
||||
@setSize 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'
|
||||
|
||||
# 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
|
||||
|
||||
@@ -9,7 +9,10 @@ fileDialogProperties =
|
||||
multiSelections: 1 << 2
|
||||
createDirectory: 1 << 3
|
||||
|
||||
messageBoxTypes = ['none', 'info', 'warning']
|
||||
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
|
||||
|
||||
messageBoxOptions =
|
||||
noLink: 1 << 0
|
||||
|
||||
parseArgs = (window, options, callback) ->
|
||||
unless window is null or window?.constructor is BrowserWindow
|
||||
@@ -93,9 +96,23 @@ module.exports =
|
||||
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.title, options.message, options.detail],
|
||||
options.cancelId,
|
||||
flags,
|
||||
options.title,
|
||||
options.message,
|
||||
options.detail,
|
||||
options.icon,
|
||||
window,
|
||||
callback
|
||||
@@ -104,4 +121,5 @@ module.exports =
|
||||
binding.showErrorBox args...
|
||||
|
||||
# Mark standard asynchronous functions.
|
||||
v8Util.setHiddenValue f, 'asynchronous', true for k, f of module.exports
|
||||
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
|
||||
v8Util.setHiddenValue module.exports[api], 'asynchronous', true
|
||||
|
||||
@@ -40,6 +40,7 @@ class NavigationController
|
||||
loadUrl: (url, options={}) ->
|
||||
@pendingIndex = -1
|
||||
@webContents._loadUrl url, options
|
||||
@webContents.emit 'load-url', url, options
|
||||
|
||||
getUrl: ->
|
||||
if @currentIndex is -1
|
||||
|
||||
@@ -6,6 +6,29 @@ EventEmitter = require('events').EventEmitter
|
||||
|
||||
protocol.__proto__ = EventEmitter.prototype
|
||||
|
||||
GetWrappedCallback = (scheme, callback, notification) ->
|
||||
wrappedCallback = (error) ->
|
||||
if not callback?
|
||||
if error
|
||||
throw error
|
||||
else
|
||||
protocol.emit notification, scheme
|
||||
else
|
||||
callback error, scheme
|
||||
|
||||
# Compatibility with old api.
|
||||
protocol.registerProtocol = (scheme, handler, callback) ->
|
||||
protocol._registerProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'registered')
|
||||
|
||||
protocol.unregisterProtocol = (scheme, callback) ->
|
||||
protocol._unregisterProtocol scheme, GetWrappedCallback(scheme, callback, 'unregistered')
|
||||
|
||||
protocol.interceptProtocol = (scheme, handler, callback) ->
|
||||
protocol._interceptProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'intercepted')
|
||||
|
||||
protocol.uninterceptProtocol = (scheme, callback) ->
|
||||
protocol._uninterceptProtocol scheme, GetWrappedCallback(scheme, callback, 'unintercepted')
|
||||
|
||||
protocol.RequestStringJob =
|
||||
class RequestStringJob
|
||||
constructor: ({mimeType, charset, data}) ->
|
||||
@@ -36,6 +59,6 @@ class RequestErrorJob
|
||||
|
||||
protocol.RequestHttpJob =
|
||||
class RequestHttpJob
|
||||
constructor: ({@url, @method, @referrer}) ->
|
||||
constructor: ({@session, @url, @method, @referrer}) ->
|
||||
|
||||
module.exports = protocol
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/common/id_weak_map.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
|
||||
namespace base {
|
||||
class SupportsUserData;
|
||||
@@ -54,7 +55,10 @@ class TrackableObject : public TrackableObjectBase {
|
||||
public:
|
||||
// Finds out the TrackableObject from its ID in weak map.
|
||||
static T* FromWeakMapID(v8::Isolate* isolate, int32_t id) {
|
||||
v8::MaybeLocal<v8::Object> object = weak_map_.Get(isolate, id);
|
||||
if (!weak_map_)
|
||||
return nullptr;
|
||||
|
||||
v8::MaybeLocal<v8::Object> object = weak_map_->Get(isolate, id);
|
||||
if (object.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
@@ -74,7 +78,10 @@ class TrackableObject : public TrackableObjectBase {
|
||||
|
||||
// Returns all objects in this class's weak map.
|
||||
static std::vector<v8::Local<v8::Object>> GetAll(v8::Isolate* isolate) {
|
||||
return weak_map_.Values(isolate);
|
||||
if (weak_map_)
|
||||
return weak_map_->Values(isolate);
|
||||
else
|
||||
return std::vector<v8::Local<v8::Object>>();
|
||||
}
|
||||
|
||||
TrackableObject() {
|
||||
@@ -83,8 +90,8 @@ class TrackableObject : public TrackableObjectBase {
|
||||
|
||||
// Removes this instance from the weak map.
|
||||
void RemoveFromWeakMap() {
|
||||
if (weak_map_.Has(weak_map_id()))
|
||||
weak_map_.Remove(weak_map_id());
|
||||
if (weak_map_ && weak_map_->Has(weak_map_id()))
|
||||
weak_map_->Remove(weak_map_id());
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -93,23 +100,25 @@ class TrackableObject : public TrackableObjectBase {
|
||||
}
|
||||
|
||||
void AfterInit(v8::Isolate* isolate) override {
|
||||
weak_map_id_ = weak_map_.Add(isolate, GetWrapper(isolate));
|
||||
if (!weak_map_)
|
||||
weak_map_.reset(new atom::IDWeakMap);
|
||||
weak_map_id_ = weak_map_->Add(isolate, GetWrapper(isolate));
|
||||
TrackableObjectBase::AfterInit(isolate);
|
||||
}
|
||||
|
||||
private:
|
||||
// Releases all weak references in weak map, called when app is terminating.
|
||||
static void ReleaseAllWeakReferences() {
|
||||
weak_map_.Clear();
|
||||
weak_map_.reset();
|
||||
}
|
||||
|
||||
static atom::IDWeakMap weak_map_;
|
||||
static scoped_ptr<atom::IDWeakMap> weak_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TrackableObject);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
atom::IDWeakMap TrackableObject<T>::weak_map_;
|
||||
scoped_ptr<atom::IDWeakMap> TrackableObject<T>::weak_map_;
|
||||
|
||||
} // namespace mate
|
||||
|
||||
|
||||
@@ -215,11 +215,8 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
|
||||
|
||||
void AtomBrowserClient::DidCreatePpapiPlugin(
|
||||
content::BrowserPpapiHost* host) {
|
||||
auto command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kEnablePlugins)) {
|
||||
host->GetPpapiHost()->AddHostFactoryFilter(
|
||||
make_scoped_ptr(new chrome::ChromeBrowserPepperHostFactory(host)));
|
||||
}
|
||||
host->GetPpapiHost()->AddHostFactoryFilter(
|
||||
make_scoped_ptr(new chrome::ChromeBrowserPepperHostFactory(host)));
|
||||
}
|
||||
|
||||
content::QuotaPermissionContext*
|
||||
|
||||
@@ -6,16 +6,22 @@
|
||||
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/atom_download_manager_delegate.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
#include "atom/browser/net/http_protocol_handler.h"
|
||||
#include "atom/browser/web_view_manager.h"
|
||||
#include "atom/common/atom_version.h"
|
||||
#include "atom/common/chrome_version.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.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 "content/public/browser/browser_thread.h"
|
||||
#include "content/public/common/url_constants.h"
|
||||
#include "content/public/common/user_agent.h"
|
||||
#include "net/ftp/ftp_network_layer.h"
|
||||
#include "net/url_request/data_protocol_handler.h"
|
||||
#include "net/url_request/ftp_protocol_handler.h"
|
||||
@@ -37,6 +43,14 @@ class NoCacheBackend : public net::HttpCache::BackendFactory {
|
||||
}
|
||||
};
|
||||
|
||||
std::string RemoveWhitespace(const std::string& str) {
|
||||
std::string trimmed;
|
||||
if (base::RemoveChars(str, " ", &trimmed))
|
||||
return trimmed;
|
||||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomBrowserContext::AtomBrowserContext()
|
||||
@@ -46,6 +60,23 @@ AtomBrowserContext::AtomBrowserContext()
|
||||
AtomBrowserContext::~AtomBrowserContext() {
|
||||
}
|
||||
|
||||
std::string AtomBrowserContext::GetUserAgent() {
|
||||
Browser* browser = Browser::Get();
|
||||
std::string name = RemoveWhitespace(browser->GetName());
|
||||
std::string user_agent;
|
||||
if (name == ATOM_PRODUCT_NAME) {
|
||||
user_agent = "Chrome/" CHROME_VERSION_STRING " "
|
||||
ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING;
|
||||
} else {
|
||||
user_agent = base::StringPrintf(
|
||||
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
|
||||
name.c_str(),
|
||||
browser->GetVersion().c_str(),
|
||||
CHROME_VERSION_STRING);
|
||||
}
|
||||
return content::BuildUserAgentFromProduct(user_agent);
|
||||
}
|
||||
|
||||
net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
|
||||
content::ProtocolHandlerMap* handlers,
|
||||
content::URLRequestInterceptorScopedVector* interceptors) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef ATOM_BROWSER_ATOM_BROWSER_CONTEXT_H_
|
||||
#define ATOM_BROWSER_ATOM_BROWSER_CONTEXT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "brightray/browser/browser_context.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -19,6 +21,7 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||
virtual ~AtomBrowserContext();
|
||||
|
||||
// brightray::URLRequestContextGetter::Delegate:
|
||||
std::string GetUserAgent() override;
|
||||
net::URLRequestJobFactory* CreateURLRequestJobFactory(
|
||||
content::ProtocolHandlerMap* handlers,
|
||||
content::URLRequestInterceptorScopedVector* interceptors) override;
|
||||
|
||||
@@ -237,7 +237,7 @@ app.once('ready', function() {
|
||||
},
|
||||
{
|
||||
label: 'Toggle &Developer Tools',
|
||||
accelerator: 'Alt+Ctrl+I',
|
||||
accelerator: 'Shift+Ctrl+I',
|
||||
click: function() {
|
||||
var focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow)
|
||||
@@ -291,6 +291,7 @@ if (option.file && !option.webdriver) {
|
||||
app.setName(packageJson.name);
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()));
|
||||
app.setAppPath(packagePath);
|
||||
}
|
||||
|
||||
// Run the app.
|
||||
|
||||
@@ -21,9 +21,8 @@ createGuest = (embedder, url, frameName, options) ->
|
||||
# guest is closed by user then we should prevent |embedder| from double
|
||||
# closing guest.
|
||||
closedByEmbedder = ->
|
||||
embedder.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', guest.id
|
||||
guest.removeListener 'closed', closedByUser
|
||||
guest.destroy() unless guest.isClosed()
|
||||
guest.destroy()
|
||||
closedByUser = ->
|
||||
embedder.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', guest.id
|
||||
embedder.removeListener 'render-view-deleted', closedByEmbedder
|
||||
|
||||
@@ -87,6 +87,7 @@ 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'
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/window_list.h"
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "atom/common/atom_version.h"
|
||||
#include "atom/common/chrome_version.h"
|
||||
#include "atom/common/native_mate_converters/image_converter.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
@@ -24,7 +25,6 @@
|
||||
#include "base/prefs/pref_service.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "brightray/browser/inspectable_web_contents.h"
|
||||
#include "brightray/browser/inspectable_web_contents_view.h"
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "content/public/common/user_agent.h"
|
||||
#include "content/public/common/web_preferences.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
@@ -73,14 +72,6 @@ const char* kWebRuntimeFeatures[] = {
|
||||
switches::kPageVisibility,
|
||||
};
|
||||
|
||||
std::string RemoveWhitespace(const std::string& str) {
|
||||
std::string trimmed;
|
||||
if (base::RemoveChars(str, " ", &trimmed))
|
||||
return trimmed;
|
||||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindow::NativeWindow(
|
||||
@@ -94,6 +85,7 @@ NativeWindow::NativeWindow(
|
||||
node_integration_(true),
|
||||
has_dialog_attached_(false),
|
||||
zoom_factor_(1.0),
|
||||
aspect_ratio_(0.0),
|
||||
inspectable_web_contents_(inspectable_web_contents),
|
||||
weak_factory_(this) {
|
||||
inspectable_web_contents->GetView()->SetDelegate(this);
|
||||
@@ -130,16 +122,6 @@ NativeWindow::NativeWindow(
|
||||
options.Get(switches::kZoomFactor, &zoom_factor_);
|
||||
|
||||
WindowList::AddWindow(this);
|
||||
|
||||
// Override the user agent to contain application and atom-shell's version.
|
||||
Browser* browser = Browser::Get();
|
||||
std::string product_name = base::StringPrintf(
|
||||
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
|
||||
RemoveWhitespace(browser->GetName()).c_str(),
|
||||
browser->GetVersion().c_str(),
|
||||
CHROME_VERSION_STRING);
|
||||
web_contents()->GetMutableRendererPrefs()->user_agent_override =
|
||||
content::BuildUserAgentFromProduct(product_name);
|
||||
}
|
||||
|
||||
NativeWindow::~NativeWindow() {
|
||||
@@ -189,10 +171,12 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
||||
if (options.Get(switches::kAlwaysOnTop, &top) && top) {
|
||||
SetAlwaysOnTop(true);
|
||||
}
|
||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||
bool fullscreen;
|
||||
if (options.Get(switches::kFullscreen, &fullscreen) && fullscreen) {
|
||||
SetFullScreen(true);
|
||||
}
|
||||
#endif
|
||||
bool skip;
|
||||
if (options.Get(switches::kSkipTaskbar, &skip) && skip) {
|
||||
SetSkipTaskbar(skip);
|
||||
@@ -263,6 +247,20 @@ bool NativeWindow::IsMenuBarVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
double NativeWindow::GetAspectRatio() {
|
||||
return aspect_ratio_;
|
||||
}
|
||||
|
||||
gfx::Size NativeWindow::GetAspectRatioExtraSize() {
|
||||
return aspect_ratio_extraSize_;
|
||||
}
|
||||
|
||||
void NativeWindow::SetAspectRatio(double aspect_ratio,
|
||||
const gfx::Size& extra_size) {
|
||||
aspect_ratio_ = aspect_ratio;
|
||||
aspect_ratio_extraSize_ = extra_size;
|
||||
}
|
||||
|
||||
bool NativeWindow::HasModalDialog() {
|
||||
return has_dialog_attached_;
|
||||
}
|
||||
@@ -386,6 +384,15 @@ void NativeWindow::AppendExtraCommandLineSwitches(
|
||||
command_line->AppendSwitchASCII(switches::kZoomFactor,
|
||||
base::DoubleToString(zoom_factor_));
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Append --app-user-model-id.
|
||||
PWSTR current_app_id;
|
||||
if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(¤t_app_id))) {
|
||||
command_line->AppendSwitchNative(switches::kAppUserModelId, current_app_id);
|
||||
CoTaskMemFree(current_app_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (web_preferences_.IsEmpty())
|
||||
return;
|
||||
|
||||
@@ -417,8 +424,6 @@ void NativeWindow::OverrideWebkitPrefs(content::WebPreferences* prefs) {
|
||||
std::vector<base::FilePath> list;
|
||||
if (web_preferences_.Get("javascript", &b))
|
||||
prefs->javascript_enabled = b;
|
||||
if (web_preferences_.Get("web-security", &b))
|
||||
prefs->web_security_enabled = b;
|
||||
if (web_preferences_.Get("images", &b))
|
||||
prefs->images_enabled = b;
|
||||
if (web_preferences_.Get("java", &b))
|
||||
@@ -429,6 +434,11 @@ void NativeWindow::OverrideWebkitPrefs(content::WebPreferences* prefs) {
|
||||
prefs->experimental_webgl_enabled = b;
|
||||
if (web_preferences_.Get("webaudio", &b))
|
||||
prefs->webaudio_enabled = b;
|
||||
if (web_preferences_.Get("web-security", &b)) {
|
||||
prefs->web_security_enabled = b;
|
||||
prefs->allow_displaying_insecure_content = !b;
|
||||
prefs->allow_running_insecure_content = !b;
|
||||
}
|
||||
if (web_preferences_.Get("extra-plugin-dirs", &list)) {
|
||||
if (content::PluginService::GetInstance()->NPAPIPluginsSupported()) {
|
||||
for (size_t i = 0; i < list.size(); ++i)
|
||||
|
||||
@@ -163,6 +163,11 @@ class NativeWindow : public content::WebContentsObserver,
|
||||
virtual void SetMenuBarVisibility(bool visible);
|
||||
virtual bool IsMenuBarVisible();
|
||||
|
||||
// Set the aspect ratio when resizing window.
|
||||
double GetAspectRatio();
|
||||
gfx::Size GetAspectRatioExtraSize();
|
||||
void SetAspectRatio(double aspect_ratio, const gfx::Size& extra_size);
|
||||
|
||||
base::WeakPtr<NativeWindow> GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
@@ -285,6 +290,11 @@ class NativeWindow : public content::WebContentsObserver,
|
||||
// Page's default zoom factor.
|
||||
double zoom_factor_;
|
||||
|
||||
// Used to maintain the aspect ratio of a view which is inside of the
|
||||
// content view.
|
||||
double aspect_ratio_;
|
||||
gfx::Size aspect_ratio_extraSize_;
|
||||
|
||||
// The page this window is viewing.
|
||||
brightray::InspectableWebContents* inspectable_web_contents_;
|
||||
|
||||
|
||||
@@ -95,6 +95,44 @@ static const CGFloat kAtomWindowCornerRadius = 4.0;
|
||||
shell_->NotifyWindowBlur();
|
||||
}
|
||||
|
||||
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
|
||||
NSSize newSize = frameSize;
|
||||
double aspectRatio = shell_->GetAspectRatio();
|
||||
|
||||
if (aspectRatio > 0.0) {
|
||||
gfx::Size windowSize = shell_->GetSize();
|
||||
gfx::Size contentSize = shell_->GetContentSize();
|
||||
gfx::Size extraSize = shell_->GetAspectRatioExtraSize();
|
||||
|
||||
double extraWidthPlusFrame =
|
||||
windowSize.width() - contentSize.width() + extraSize.width();
|
||||
double extraHeightPlusFrame =
|
||||
windowSize.height() - contentSize.height() + extraSize.height();
|
||||
|
||||
newSize.width =
|
||||
roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
|
||||
extraWidthPlusFrame);
|
||||
|
||||
// If the new width is less than the frame size use it as the primary
|
||||
// constraint. This ensures that the value returned by this method will
|
||||
// never be larger than the users requested window size.
|
||||
if (newSize.width <= frameSize.width) {
|
||||
newSize.height =
|
||||
roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
|
||||
extraHeightPlusFrame);
|
||||
} else {
|
||||
newSize.height =
|
||||
roundf((frameSize.width - extraWidthPlusFrame) / aspectRatio +
|
||||
extraHeightPlusFrame);
|
||||
newSize.width =
|
||||
roundf((newSize.height - extraHeightPlusFrame) * aspectRatio +
|
||||
extraWidthPlusFrame);
|
||||
}
|
||||
}
|
||||
|
||||
return newSize;
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification*)notification {
|
||||
if (!shell_->has_frame())
|
||||
shell_->ClipWebView();
|
||||
@@ -357,7 +395,7 @@ NativeWindowMac::NativeWindowMac(
|
||||
// On OS X the initial window size doesn't include window frame.
|
||||
bool use_content_size = false;
|
||||
options.Get(switches::kUseContentSize, &use_content_size);
|
||||
if (has_frame_ && !use_content_size)
|
||||
if (!has_frame_ || !use_content_size)
|
||||
SetSize(gfx::Size(width, height));
|
||||
|
||||
// Enable the NSView to accept first mouse event.
|
||||
@@ -494,6 +532,11 @@ gfx::Rect NativeWindowMac::GetBounds() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetContentSize(const gfx::Size& size) {
|
||||
if (!has_frame_) {
|
||||
SetSize(size);
|
||||
return;
|
||||
}
|
||||
|
||||
NSRect frame_nsrect = [window_ frame];
|
||||
NSSize frame = frame_nsrect.size;
|
||||
NSSize content = [window_ contentRectForFrameRect:frame_nsrect].size;
|
||||
@@ -507,6 +550,9 @@ void NativeWindowMac::SetContentSize(const gfx::Size& size) {
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowMac::GetContentSize() {
|
||||
if (!has_frame_)
|
||||
return GetSize();
|
||||
|
||||
NSRect bounds = [[window_ contentView] bounds];
|
||||
return gfx::Size(bounds.size.width, bounds.size.height);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/ui/views/global_menu_bar_x11.h"
|
||||
#include "atom/browser/ui/views/frameless_view.h"
|
||||
#include "atom/browser/ui/views/native_frame_view.h"
|
||||
#include "atom/browser/ui/x/window_state_watcher.h"
|
||||
#include "atom/browser/ui/x/x_window_utils.h"
|
||||
#include "base/environment.h"
|
||||
@@ -285,14 +286,21 @@ NativeWindowViews::NativeWindowViews(
|
||||
|
||||
// Before the window is mapped the SetWMSpecState can not work, so we have
|
||||
// to manually set the _NET_WM_STATE.
|
||||
std::vector<::Atom> state_atom_list;
|
||||
bool skip_taskbar = false;
|
||||
if (options.Get(switches::kSkipTaskbar, &skip_taskbar) && skip_taskbar) {
|
||||
std::vector<::Atom> state_atom_list;
|
||||
state_atom_list.push_back(GetAtom("_NET_WM_STATE_SKIP_TASKBAR"));
|
||||
ui::SetAtomArrayProperty(GetAcceleratedWidget(), "_NET_WM_STATE", "ATOM",
|
||||
state_atom_list);
|
||||
}
|
||||
|
||||
// Before the window is mapped, there is no SHOW_FULLSCREEN_STATE.
|
||||
bool fullscreen = false;
|
||||
if (options.Get(switches::kFullscreen, & fullscreen) && fullscreen) {
|
||||
state_atom_list.push_back(GetAtom("_NET_WM_STATE_FULLSCREEN"));
|
||||
}
|
||||
|
||||
ui::SetAtomArrayProperty(GetAcceleratedWidget(), "_NET_WM_STATE", "ATOM",
|
||||
state_atom_list);
|
||||
|
||||
// Set the _NET_WM_WINDOW_TYPE.
|
||||
std::string window_type;
|
||||
if (options.Get(switches::kType, &window_type))
|
||||
@@ -418,17 +426,19 @@ bool NativeWindowViews::IsMinimized() {
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetFullScreen(bool fullscreen) {
|
||||
#if defined(OS_WIN)
|
||||
// There is no native fullscreen state on Windows.
|
||||
window_->SetFullscreen(fullscreen);
|
||||
if (fullscreen)
|
||||
NotifyWindowEnterFullScreen();
|
||||
else
|
||||
NotifyWindowLeaveFullScreen();
|
||||
#else
|
||||
if (IsVisible())
|
||||
window_->SetFullscreen(fullscreen);
|
||||
else
|
||||
window_->native_widget_private()->ShowWithWindowState(
|
||||
ui::SHOW_STATE_FULLSCREEN);
|
||||
#if defined(OS_WIN)
|
||||
// There is no native fullscreen state on Windows.
|
||||
if (fullscreen)
|
||||
NotifyWindowEnterFullScreen();
|
||||
else
|
||||
NotifyWindowLeaveFullScreen();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -482,14 +492,6 @@ gfx::Size NativeWindowViews::GetContentSize() {
|
||||
|
||||
void NativeWindowViews::SetMinimumSize(const gfx::Size& size) {
|
||||
minimum_size_ = size;
|
||||
|
||||
#if defined(USE_X11)
|
||||
XSizeHints size_hints;
|
||||
size_hints.flags = PMinSize;
|
||||
size_hints.min_width = size.width();
|
||||
size_hints.min_height = size.height();
|
||||
XSetWMNormalHints(gfx::GetXDisplay(), GetAcceleratedWidget(), &size_hints);
|
||||
#endif
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowViews::GetMinimumSize() {
|
||||
@@ -498,14 +500,6 @@ gfx::Size NativeWindowViews::GetMinimumSize() {
|
||||
|
||||
void NativeWindowViews::SetMaximumSize(const gfx::Size& size) {
|
||||
maximum_size_ = size;
|
||||
|
||||
#if defined(USE_X11)
|
||||
XSizeHints size_hints;
|
||||
size_hints.flags = PMaxSize;
|
||||
size_hints.max_width = size.width();
|
||||
size_hints.max_height = size.height();
|
||||
XSetWMNormalHints(gfx::GetXDisplay(), GetAcceleratedWidget(), &size_hints);
|
||||
#endif
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowViews::GetMaximumSize() {
|
||||
@@ -890,7 +884,7 @@ views::NonClientFrameView* NativeWindowViews::CreateNonClientFrameView(
|
||||
return frame_view;
|
||||
#else
|
||||
if (has_frame_) {
|
||||
return new views::NativeFrameView(widget);
|
||||
return new NativeFrameView(this, widget);
|
||||
} else {
|
||||
FramelessView* frame_view = new FramelessView;
|
||||
frame_view->Init(this, widget);
|
||||
@@ -920,7 +914,7 @@ bool NativeWindowViews::ExecuteWindowsCommand(int command_id) {
|
||||
} else if ((command_id & sc_mask) == SC_MAXIMIZE) {
|
||||
NotifyWindowMaximize();
|
||||
} else {
|
||||
std::string command = AppCommandToString(command_id & sc_mask);
|
||||
std::string command = AppCommandToString(command_id);
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver,
|
||||
observers_,
|
||||
OnExecuteWindowsCommand(command));
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "atom/browser/net/adapter_request_job.h"
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "base/threading/sequenced_worker_pool.h"
|
||||
#include "atom/browser/net/url_request_buffer_job.h"
|
||||
#include "atom/browser/net/url_request_fetch_job.h"
|
||||
@@ -28,11 +27,7 @@ AdapterRequestJob::AdapterRequestJob(ProtocolHandler* protocol_handler,
|
||||
|
||||
void AdapterRequestJob::Start() {
|
||||
DCHECK(!real_job_.get());
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::GetJobTypeInUI,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
GetJobType();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::Kill() {
|
||||
@@ -44,7 +39,11 @@ bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int *bytes_read) {
|
||||
DCHECK(!real_job_.get());
|
||||
return real_job_->ReadRawData(buf, buf_size, bytes_read);
|
||||
// Read post-filtered data if available.
|
||||
if (real_job_->HasFilter())
|
||||
return real_job_->Read(buf, buf_size, bytes_read);
|
||||
else
|
||||
return real_job_->ReadRawData(buf, buf_size, bytes_read);
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::IsRedirectResponse(GURL* location,
|
||||
@@ -76,6 +75,11 @@ int AdapterRequestJob::GetResponseCode() const {
|
||||
return real_job_->GetResponseCode();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::GetLoadTimingInfo(
|
||||
net::LoadTimingInfo* load_timing_info) const {
|
||||
real_job_->GetLoadTimingInfo(load_timing_info);
|
||||
}
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> AdapterRequestJob::GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
@@ -115,7 +119,7 @@ void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) {
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateHttpJobAndStart(
|
||||
AtomBrowserContext* browser_context,
|
||||
net::URLRequestContextGetter* request_context_getter,
|
||||
const GURL& url,
|
||||
const std::string& method,
|
||||
const std::string& referrer) {
|
||||
@@ -124,7 +128,7 @@ void AdapterRequestJob::CreateHttpJobAndStart(
|
||||
return;
|
||||
}
|
||||
|
||||
real_job_ = new URLRequestFetchJob(browser_context, request(),
|
||||
real_job_ = new URLRequestFetchJob(request_context_getter, request(),
|
||||
network_delegate(), url, method, referrer);
|
||||
real_job_->Start();
|
||||
}
|
||||
@@ -132,10 +136,13 @@ void AdapterRequestJob::CreateHttpJobAndStart(
|
||||
void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() {
|
||||
real_job_ = protocol_handler_->MaybeCreateJob(request(),
|
||||
network_delegate());
|
||||
if (!real_job_.get())
|
||||
if (!real_job_.get()) {
|
||||
CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED);
|
||||
else
|
||||
} else {
|
||||
// Copy headers from original request.
|
||||
real_job_->SetExtraRequestHeaders(request()->extra_request_headers());
|
||||
real_job_->Start();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
#include "v8/include/v8.h"
|
||||
@@ -45,13 +46,15 @@ class AdapterRequestJob : public net::URLRequestJob {
|
||||
bool GetCharset(std::string* charset) override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
int GetResponseCode() const override;
|
||||
void GetLoadTimingInfo(
|
||||
net::LoadTimingInfo* load_timing_info) const override;
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> GetWeakPtr();
|
||||
|
||||
ProtocolHandler* default_protocol_handler() { return protocol_handler_; }
|
||||
|
||||
// Override this function to determine which job should be started.
|
||||
virtual void GetJobTypeInUI() = 0;
|
||||
virtual void GetJobType() = 0;
|
||||
|
||||
void CreateErrorJobAndStart(int error_code);
|
||||
void CreateStringJobAndStart(const std::string& mime_type,
|
||||
@@ -61,10 +64,11 @@ class AdapterRequestJob : public net::URLRequestJob {
|
||||
const std::string& charset,
|
||||
scoped_refptr<base::RefCountedBytes> data);
|
||||
void CreateFileJobAndStart(const base::FilePath& path);
|
||||
void CreateHttpJobAndStart(AtomBrowserContext* browser_context,
|
||||
const GURL& url,
|
||||
const std::string& method,
|
||||
const std::string& referrer);
|
||||
void CreateHttpJobAndStart(
|
||||
net::URLRequestContextGetter* request_context_getter,
|
||||
const GURL& url,
|
||||
const std::string& method,
|
||||
const std::string& referrer);
|
||||
void CreateJobFromProtocolHandlerAndStart();
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
@@ -22,9 +25,7 @@ AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
|
||||
bool AtomURLRequestJobFactory::SetProtocolHandler(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (!protocol_handler) {
|
||||
ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
|
||||
@@ -45,10 +46,9 @@ bool AtomURLRequestJobFactory::SetProtocolHandler(
|
||||
ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
DCHECK(protocol_handler);
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
if (!ContainsKey(protocol_handler_map_, scheme))
|
||||
return nullptr;
|
||||
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
|
||||
@@ -58,9 +58,8 @@ ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
|
||||
|
||||
ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return nullptr;
|
||||
@@ -69,7 +68,6 @@ ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
|
||||
|
||||
bool AtomURLRequestJobFactory::HasProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
base::AutoLock locked(lock_);
|
||||
return ContainsKey(protocol_handler_map_, scheme);
|
||||
}
|
||||
|
||||
@@ -77,9 +75,8 @@ net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return nullptr;
|
||||
@@ -101,7 +98,8 @@ net::URLRequestJob* AtomURLRequestJobFactory::MaybeInterceptResponse(
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledProtocol(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
return HasProtocolHandler(scheme) ||
|
||||
net::URLRequest::IsHandledProtocol(scheme);
|
||||
}
|
||||
|
||||
@@ -56,12 +56,10 @@ class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
|
||||
bool IsSafeRedirectTarget(const GURL& location) const override;
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
|
||||
using ProtocolHandlerMap = std::map<std::string, ProtocolHandler*>;
|
||||
|
||||
ProtocolHandlerMap protocol_handler_map_;
|
||||
|
||||
mutable base::Lock lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/url_request/url_fetcher.h"
|
||||
#include "net/url_request/url_fetcher_response_writer.h"
|
||||
#include "net/url_request/url_request_context_builder.h"
|
||||
#include "net/url_request/url_request_status.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -74,7 +75,7 @@ class ResponsePiper : public net::URLFetcherResponseWriter {
|
||||
} // namespace
|
||||
|
||||
URLRequestFetchJob::URLRequestFetchJob(
|
||||
AtomBrowserContext* browser_context,
|
||||
net::URLRequestContextGetter* request_context_getter,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const GURL& url,
|
||||
@@ -90,7 +91,12 @@ URLRequestFetchJob::URLRequestFetchJob(
|
||||
request_type = GetRequestType(method);
|
||||
|
||||
fetcher_.reset(net::URLFetcher::Create(url, request_type, this));
|
||||
fetcher_->SetRequestContext(browser_context->url_request_context_getter());
|
||||
// Use request context if provided else create one.
|
||||
if (request_context_getter)
|
||||
fetcher_->SetRequestContext(request_context_getter);
|
||||
else
|
||||
fetcher_->SetRequestContext(GetRequestContext());
|
||||
|
||||
fetcher_->SaveResponseWithWriter(make_scoped_ptr(new ResponsePiper(this)));
|
||||
|
||||
// Use |request|'s referrer if |referrer| is not specified.
|
||||
@@ -107,6 +113,17 @@ URLRequestFetchJob::URLRequestFetchJob(
|
||||
}
|
||||
}
|
||||
|
||||
net::URLRequestContextGetter* URLRequestFetchJob::GetRequestContext() {
|
||||
if (!url_request_context_getter_.get()) {
|
||||
auto task_runner = base::ThreadTaskRunnerHandle::Get();
|
||||
net::URLRequestContextBuilder builder;
|
||||
builder.set_proxy_service(net::ProxyService::CreateDirect());
|
||||
url_request_context_getter_ =
|
||||
new net::TrivialURLRequestContextGetter(builder.Build(), task_runner);
|
||||
}
|
||||
return url_request_context_getter_.get();
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::HeadersCompleted() {
|
||||
response_info_.reset(new net::HttpResponseInfo);
|
||||
response_info_->headers = fetcher_->GetResponseHeaders();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "net/url_request/url_fetcher_delegate.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
@@ -17,13 +18,14 @@ class AtomBrowserContext;
|
||||
class URLRequestFetchJob : public net::URLRequestJob,
|
||||
public net::URLFetcherDelegate {
|
||||
public:
|
||||
URLRequestFetchJob(AtomBrowserContext* browser_context,
|
||||
URLRequestFetchJob(net::URLRequestContextGetter* request_context_getter,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const GURL& url,
|
||||
const std::string& method,
|
||||
const std::string& referrer);
|
||||
|
||||
net::URLRequestContextGetter* GetRequestContext();
|
||||
void HeadersCompleted();
|
||||
int DataAvailable(net::IOBuffer* buffer, int num_bytes);
|
||||
|
||||
@@ -41,6 +43,7 @@ class URLRequestFetchJob : public net::URLRequestJob,
|
||||
void OnURLFetchComplete(const net::URLFetcher* source) override;
|
||||
|
||||
private:
|
||||
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
|
||||
scoped_ptr<net::URLFetcher> fetcher_;
|
||||
scoped_refptr<net::IOBuffer> pending_buffer_;
|
||||
int pending_buffer_size_;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.29.0</string>
|
||||
<string>0.30.1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.8.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
|
||||
18
atom/browser/resources/win/atom.manifest
Normal file
18
atom/browser/resources/win/atom.manifest
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
</assembly>
|
||||
@@ -1,6 +1,12 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "grit\\ui_unscaled_resources.h"
|
||||
#include "resource.h"
|
||||
#include <winresrc.h>
|
||||
#ifdef IDC_STATIC
|
||||
#undef IDC_STATIC
|
||||
#endif
|
||||
#define IDC_STATIC (-1)
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -50,8 +56,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,29,0,0
|
||||
PRODUCTVERSION 0,29,0,0
|
||||
FILEVERSION 0,30,1,0
|
||||
PRODUCTVERSION 0,30,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +74,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "0.29.0"
|
||||
VALUE "FileVersion", "0.30.1"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "0.29.0"
|
||||
VALUE "ProductVersion", "0.30.1"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
@@ -106,3 +112,28 @@ END
|
||||
IDR_MAINFRAME ICON "atom.ico"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Cursors
|
||||
//
|
||||
IDC_ALIAS CURSOR "ui\\resources\\cursors\\aliasb.cur"
|
||||
IDC_CELL CURSOR "ui\\resources\\cursors\\cell.cur"
|
||||
IDC_COLRESIZE CURSOR "ui\\resources\\cursors\\col_resize.cur"
|
||||
IDC_COPYCUR CURSOR "ui\\resources\\cursors\\copy.cur"
|
||||
IDC_CURSOR_NONE CURSOR "ui\\resources\\cursors\\none.cur"
|
||||
IDC_HAND_GRAB CURSOR "ui\\resources\\cursors\\hand_grab.cur"
|
||||
IDC_HAND_GRABBING CURSOR "ui\\resources\\cursors\\hand_grabbing.cur"
|
||||
IDC_PAN_EAST CURSOR "ui\\resources\\cursors\\pan_east.cur"
|
||||
IDC_PAN_MIDDLE CURSOR "ui\\resources\\cursors\\pan_middle.cur"
|
||||
IDC_PAN_NORTH CURSOR "ui\\resources\\cursors\\pan_north.cur"
|
||||
IDC_PAN_NORTH_EAST CURSOR "ui\\resources\\cursors\\pan_north_east.cur"
|
||||
IDC_PAN_NORTH_WEST CURSOR "ui\\resources\\cursors\\pan_north_west.cur"
|
||||
IDC_PAN_SOUTH CURSOR "ui\\resources\\cursors\\pan_south.cur"
|
||||
IDC_PAN_SOUTH_EAST CURSOR "ui\\resources\\cursors\\pan_south_east.cur"
|
||||
IDC_PAN_SOUTH_WEST CURSOR "ui\\resources\\cursors\\pan_south_west.cur"
|
||||
IDC_PAN_WEST CURSOR "ui\\resources\\cursors\\pan_west.cur"
|
||||
IDC_ROWRESIZE CURSOR "ui\\resources\\cursors\\row_resize.cur"
|
||||
IDC_VERTICALTEXT CURSOR "ui\\resources\\cursors\\vertical_text.cur"
|
||||
IDC_ZOOMIN CURSOR "ui\\resources\\cursors\\zoom_in.cur"
|
||||
IDC_ZOOMOUT CURSOR "ui\\resources\\cursors\\zoom_out.cur"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -4,49 +4,18 @@
|
||||
|
||||
#include "atom/browser/ui/file_dialog.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
// This conflicts with mate::Converter,
|
||||
#undef True
|
||||
#undef False
|
||||
// and V8.
|
||||
#undef None
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
|
||||
#include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kAuraTransientParent[] = "aura-transient-parent";
|
||||
|
||||
void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent) {
|
||||
if (!parent || !parent->GetHost())
|
||||
return;
|
||||
|
||||
gtk_widget_realize(dialog);
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(dialog);
|
||||
|
||||
// TODO(erg): Check to make sure we're using X11 if wayland or some other
|
||||
// display server ever happens. Otherwise, this will crash.
|
||||
XSetTransientForHint(GDK_WINDOW_XDISPLAY(gdk_window),
|
||||
GDK_WINDOW_XID(gdk_window),
|
||||
parent->GetHost()->GetAcceleratedWidget());
|
||||
|
||||
// We also set the |parent| as a property of |dialog|, so that we can unlink
|
||||
// the two later.
|
||||
g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, parent);
|
||||
}
|
||||
|
||||
// Makes sure that .jpg also shows .JPG.
|
||||
gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info,
|
||||
std::string* file_extension) {
|
||||
@@ -65,7 +34,7 @@ class FileChooserDialog {
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters)
|
||||
: dialog_scope_(new atom::NativeWindow::DialogScope(parent_window)) {
|
||||
: dialog_scope_(parent_window) {
|
||||
const char* confirm_text = GTK_STOCK_OK;
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
confirm_text = GTK_STOCK_SAVE;
|
||||
@@ -81,7 +50,7 @@ class FileChooserDialog {
|
||||
NULL);
|
||||
if (parent_window) {
|
||||
gfx::NativeWindow window = parent_window->GetNativeWindow();
|
||||
SetGtkTransientForAura(dialog_, window);
|
||||
libgtk2ui::SetGtkTransientForAura(dialog_, window);
|
||||
}
|
||||
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
@@ -162,13 +131,13 @@ class FileChooserDialog {
|
||||
private:
|
||||
void AddFilters(const Filters& filters);
|
||||
|
||||
atom::NativeWindow::DialogScope dialog_scope_;
|
||||
|
||||
GtkWidget* dialog_;
|
||||
|
||||
SaveDialogCallback save_callback_;
|
||||
OpenDialogCallback open_callback_;
|
||||
|
||||
scoped_ptr<atom::NativeWindow::DialogScope> dialog_scope_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileChooserDialog);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,25 +18,11 @@ namespace file_dialog {
|
||||
|
||||
namespace {
|
||||
|
||||
CFStringRef CreateUTIFromExtension(const std::string& ext) {
|
||||
base::ScopedCFTypeRef<CFStringRef> ext_cf(base::SysUTF8ToCFStringRef(ext));
|
||||
return UTTypeCreatePreferredIdentifierForTag(
|
||||
kUTTagClassFilenameExtension, ext_cf.get(), NULL);
|
||||
}
|
||||
|
||||
void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
NSMutableSet* file_type_set = [NSMutableSet set];
|
||||
for (size_t i = 0; i < filters.size(); ++i) {
|
||||
const Filter& filter = filters[i];
|
||||
for (size_t j = 0; j < filter.second.size(); ++j) {
|
||||
base::ScopedCFTypeRef<CFStringRef> uti(
|
||||
CreateUTIFromExtension(filter.second[j]));
|
||||
[file_type_set addObject:base::mac::CFToNSCast(uti.get())];
|
||||
|
||||
// Always allow the extension itself, in case the UTI doesn't map
|
||||
// back to the original extension correctly. This occurs with dynamic
|
||||
// UTIs on 10.7 and 10.8.
|
||||
// See http://crbug.com/148840, http://openradar.me/12316273
|
||||
base::ScopedCFTypeRef<CFStringRef> ext_cf(
|
||||
base::SysUTF8ToCFStringRef(filter.second[j]));
|
||||
[file_type_set addObject:base::mac::CFToNSCast(ext_cf.get())];
|
||||
|
||||
@@ -120,12 +120,13 @@ struct RunState {
|
||||
};
|
||||
|
||||
bool CreateDialogThread(RunState* run_state) {
|
||||
base::Thread* thread = new base::Thread(ATOM_PRODUCT_NAME "FileDialogThread");
|
||||
scoped_ptr<base::Thread> thread(
|
||||
new base::Thread(ATOM_PRODUCT_NAME "FileDialogThread"));
|
||||
thread->init_com_with_mta(false);
|
||||
if (!thread->Start())
|
||||
return false;
|
||||
|
||||
run_state->dialog_thread = thread;
|
||||
run_state->dialog_thread = thread.release();
|
||||
run_state->ui_message_loop = base::MessageLoop::current();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,14 @@ class NativeWindow;
|
||||
enum MessageBoxType {
|
||||
MESSAGE_BOX_TYPE_NONE = 0,
|
||||
MESSAGE_BOX_TYPE_INFORMATION,
|
||||
MESSAGE_BOX_TYPE_WARNING
|
||||
MESSAGE_BOX_TYPE_WARNING,
|
||||
MESSAGE_BOX_TYPE_ERROR,
|
||||
MESSAGE_BOX_TYPE_QUESTION,
|
||||
};
|
||||
|
||||
enum MessageBoxOptions {
|
||||
MESSAGE_BOX_NONE = 0,
|
||||
MESSAGE_BOX_NO_LINK = 1 << 0,
|
||||
};
|
||||
|
||||
typedef base::Callback<void(int code)> MessageBoxCallback;
|
||||
@@ -30,6 +37,8 @@ typedef base::Callback<void(int code)> MessageBoxCallback;
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
@@ -38,6 +47,8 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
|
||||
205
atom/browser/ui/message_box_gtk.cc
Normal file
205
atom/browser/ui/message_box_gtk.cc
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/message_box.h"
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
|
||||
#include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
|
||||
|
||||
#define ANSI_FOREGROUND_RED "\x1b[31m"
|
||||
#define ANSI_FOREGROUND_BLACK "\x1b[30m"
|
||||
#define ANSI_TEXT_BOLD "\x1b[1m"
|
||||
#define ANSI_BACKGROUND_GRAY "\x1b[47m"
|
||||
#define ANSI_RESET "\x1b[0m"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
class GtkMessageBox {
|
||||
public:
|
||||
GtkMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon)
|
||||
: dialog_scope_(parent_window),
|
||||
cancel_id_(cancel_id) {
|
||||
// Create dialog.
|
||||
dialog_ = gtk_message_dialog_new(
|
||||
nullptr, // parent
|
||||
static_cast<GtkDialogFlags>(0), // no flags
|
||||
GetMessageType(type), // type
|
||||
GTK_BUTTONS_NONE, // no buttons
|
||||
"%s", message.c_str());
|
||||
if (!detail.empty())
|
||||
gtk_message_dialog_format_secondary_text(
|
||||
GTK_MESSAGE_DIALOG(dialog_), "%s", detail.c_str());
|
||||
if (!title.empty())
|
||||
gtk_window_set_title(GTK_WINDOW(dialog_), title.c_str());
|
||||
|
||||
// Set dialog's icon.
|
||||
if (!icon.isNull()) {
|
||||
GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(*icon.bitmap());
|
||||
GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
|
||||
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog_), image);
|
||||
gtk_widget_show(image);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
|
||||
// Add buttons.
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
gtk_dialog_add_button(GTK_DIALOG(dialog_),
|
||||
TranslateToStock(i, buttons[i]),
|
||||
i);
|
||||
}
|
||||
|
||||
// Parent window.
|
||||
if (parent_window) {
|
||||
gfx::NativeWindow window = parent_window->GetNativeWindow();
|
||||
libgtk2ui::SetGtkTransientForAura(dialog_, window);
|
||||
}
|
||||
}
|
||||
|
||||
~GtkMessageBox() {
|
||||
gtk_widget_destroy(dialog_);
|
||||
}
|
||||
|
||||
GtkMessageType GetMessageType(MessageBoxType type) {
|
||||
switch (type) {
|
||||
case MESSAGE_BOX_TYPE_INFORMATION:
|
||||
return GTK_MESSAGE_INFO;
|
||||
case MESSAGE_BOX_TYPE_WARNING:
|
||||
return GTK_MESSAGE_WARNING;
|
||||
case MESSAGE_BOX_TYPE_QUESTION:
|
||||
return GTK_MESSAGE_QUESTION;
|
||||
case MESSAGE_BOX_TYPE_ERROR:
|
||||
return GTK_MESSAGE_ERROR;
|
||||
default:
|
||||
return GTK_MESSAGE_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
const char* TranslateToStock(int id, const std::string& text) {
|
||||
std::string lower = base::StringToLowerASCII(text);
|
||||
if (lower == "cancel")
|
||||
return GTK_STOCK_CANCEL;
|
||||
else if (lower == "no")
|
||||
return GTK_STOCK_NO;
|
||||
else if (lower == "ok")
|
||||
return GTK_STOCK_OK;
|
||||
else if (lower == "yes")
|
||||
return GTK_STOCK_YES;
|
||||
else
|
||||
return text.c_str();
|
||||
}
|
||||
|
||||
void Show() {
|
||||
gtk_widget_show_all(dialog_);
|
||||
// We need to call gtk_window_present after making the widgets visible to
|
||||
// make sure window gets correctly raised and gets focus.
|
||||
int time = views::X11DesktopHandler::get()->wm_user_time_ms();
|
||||
gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
|
||||
}
|
||||
|
||||
int RunSynchronous() {
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
|
||||
Show();
|
||||
int response = gtk_dialog_run(GTK_DIALOG(dialog_));
|
||||
if (response < 0)
|
||||
return cancel_id_;
|
||||
else
|
||||
return response;
|
||||
}
|
||||
|
||||
void RunAsynchronous(const MessageBoxCallback& callback) {
|
||||
callback_ = callback;
|
||||
g_signal_connect(dialog_, "delete-event",
|
||||
G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
g_signal_connect(dialog_, "response",
|
||||
G_CALLBACK(OnResponseDialogThunk), this);
|
||||
Show();
|
||||
}
|
||||
|
||||
CHROMEGTK_CALLBACK_1(GtkMessageBox, void, OnResponseDialog, int);
|
||||
|
||||
private:
|
||||
atom::NativeWindow::DialogScope dialog_scope_;
|
||||
|
||||
// The id to return when the dialog is closed without pressing buttons.
|
||||
int cancel_id_;
|
||||
|
||||
GtkWidget* dialog_;
|
||||
MessageBoxCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GtkMessageBox);
|
||||
};
|
||||
|
||||
void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
|
||||
gtk_widget_hide_all(dialog_);
|
||||
|
||||
if (response < 0)
|
||||
callback_.Run(cancel_id_);
|
||||
else
|
||||
callback_.Run(response);
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
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();
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
(new GtkMessageBox(parent, type, buttons, 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",
|
||||
base::UTF16ToUTF8(title).c_str(),
|
||||
base::UTF16ToUTF8(content).c_str(),
|
||||
gfx::ImageSkia()).RunSynchronous();
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY
|
||||
ANSI_FOREGROUND_RED "%s\n"
|
||||
ANSI_FOREGROUND_BLACK "%s"
|
||||
ANSI_RESET "\n",
|
||||
base::UTF16ToUTF8(title).c_str(),
|
||||
base::UTF16ToUTF8(content).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
@@ -94,6 +94,8 @@ void SetReturnCode(int* ret_code, int result) {
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
@@ -125,6 +127,8 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/message_box.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/controls/button/label_button.h"
|
||||
#include "ui/views/controls/message_box_view.h"
|
||||
#include "ui/views/layout/grid_layout.h"
|
||||
#include "ui/views/layout/layout_constants.h"
|
||||
#include "ui/views/bubble/bubble_border.h"
|
||||
#include "ui/views/bubble/bubble_frame_view.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
#include "ui/wm/core/shadow_types.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "atom/browser/browser.h"
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/base/win/message_box_win.h"
|
||||
#endif
|
||||
|
||||
#define ANSI_FOREGROUND_RED "\x1b[31m"
|
||||
#define ANSI_FOREGROUND_BLACK "\x1b[30m"
|
||||
#define ANSI_TEXT_BOLD "\x1b[1m"
|
||||
#define ANSI_BACKGROUND_GRAY "\x1b[47m"
|
||||
#define ANSI_RESET "\x1b[0m"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// The group used by the buttons. This name is chosen voluntarily big not to
|
||||
// conflict with other groups that could be in the dialog content.
|
||||
const int kButtonGroup = 1127;
|
||||
|
||||
class MessageDialogClientView;
|
||||
|
||||
class MessageDialog : public views::WidgetDelegate,
|
||||
public views::View,
|
||||
public views::ButtonListener {
|
||||
public:
|
||||
MessageDialog(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon);
|
||||
virtual ~MessageDialog();
|
||||
|
||||
void Show(base::RunLoop* run_loop = NULL);
|
||||
void Close();
|
||||
|
||||
int GetResult() const;
|
||||
|
||||
void set_callback(const MessageBoxCallback& callback) {
|
||||
delete_on_close_ = true;
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
private:
|
||||
// Overridden from views::WidgetDelegate:
|
||||
base::string16 GetWindowTitle() const override;
|
||||
gfx::ImageSkia GetWindowAppIcon() override;
|
||||
gfx::ImageSkia GetWindowIcon() override;
|
||||
bool ShouldShowWindowIcon() const override;
|
||||
views::Widget* GetWidget() override;
|
||||
const views::Widget* GetWidget() const override;
|
||||
views::View* GetContentsView() override;
|
||||
views::View* GetInitiallyFocusedView() override;
|
||||
ui::ModalType GetModalType() const override;
|
||||
views::NonClientFrameView* CreateNonClientFrameView(
|
||||
views::Widget* widget) override;
|
||||
views::ClientView* CreateClientView(views::Widget* widget) override;
|
||||
|
||||
// Overridden from views::View:
|
||||
gfx::Size GetPreferredSize() const override;
|
||||
void Layout() override;
|
||||
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||
|
||||
// Overridden from views::ButtonListener:
|
||||
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
|
||||
|
||||
gfx::ImageSkia icon_;
|
||||
|
||||
bool delete_on_close_;
|
||||
int result_;
|
||||
base::string16 title_;
|
||||
|
||||
NativeWindow* parent_;
|
||||
scoped_ptr<views::Widget> widget_;
|
||||
views::MessageBoxView* message_box_view_;
|
||||
std::vector<views::LabelButton*> buttons_;
|
||||
|
||||
base::RunLoop* run_loop_;
|
||||
scoped_ptr<NativeWindow::DialogScope> dialog_scope_;
|
||||
MessageBoxCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageDialog);
|
||||
};
|
||||
|
||||
class MessageDialogClientView : public views::ClientView {
|
||||
public:
|
||||
MessageDialogClientView(MessageDialog* dialog, views::Widget* widget)
|
||||
: views::ClientView(widget, dialog),
|
||||
dialog_(dialog) {
|
||||
}
|
||||
|
||||
// views::ClientView:
|
||||
bool CanClose() override {
|
||||
dialog_->Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
MessageDialog* dialog_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageDialogClientView);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MessageDialog, public:
|
||||
|
||||
MessageDialog::MessageDialog(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon)
|
||||
: icon_(icon),
|
||||
delete_on_close_(false),
|
||||
result_(-1),
|
||||
title_(base::UTF8ToUTF16(title)),
|
||||
parent_(parent_window),
|
||||
message_box_view_(NULL),
|
||||
run_loop_(NULL),
|
||||
dialog_scope_(new NativeWindow::DialogScope(parent_window)) {
|
||||
DCHECK_GT(buttons.size(), 0u);
|
||||
set_owned_by_client();
|
||||
|
||||
if (!parent_)
|
||||
set_background(views::Background::CreateStandardPanelBackground());
|
||||
|
||||
std::string content = message + "\n" + detail;
|
||||
views::MessageBoxView::InitParams box_params(base::UTF8ToUTF16(content));
|
||||
message_box_view_ = new views::MessageBoxView(box_params);
|
||||
AddChildView(message_box_view_);
|
||||
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
views::LabelButton* button = new views::LabelButton(
|
||||
this, base::UTF8ToUTF16(buttons[i]));
|
||||
button->set_tag(i);
|
||||
button->SetMinSize(gfx::Size(60, 30));
|
||||
button->SetStyle(views::Button::STYLE_BUTTON);
|
||||
button->SetGroup(kButtonGroup);
|
||||
|
||||
buttons_.push_back(button);
|
||||
AddChildView(button);
|
||||
}
|
||||
|
||||
// First button is always default button.
|
||||
buttons_[0]->SetIsDefault(true);
|
||||
buttons_[0]->AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
|
||||
|
||||
views::Widget::InitParams params;
|
||||
params.delegate = this;
|
||||
params.type = views::Widget::InitParams::TYPE_WINDOW;
|
||||
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||
if (parent_) {
|
||||
params.parent = parent_->GetNativeWindow();
|
||||
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
|
||||
// Use bubble style for dialog has a parent.
|
||||
params.remove_standard_frame = true;
|
||||
}
|
||||
|
||||
widget_.reset(new views::Widget);
|
||||
widget_->Init(params);
|
||||
widget_->UpdateWindowIcon();
|
||||
|
||||
// Bind to ESC.
|
||||
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
|
||||
}
|
||||
|
||||
MessageDialog::~MessageDialog() {
|
||||
}
|
||||
|
||||
void MessageDialog::Show(base::RunLoop* run_loop) {
|
||||
run_loop_ = run_loop;
|
||||
widget_->Show();
|
||||
}
|
||||
|
||||
void MessageDialog::Close() {
|
||||
dialog_scope_.reset();
|
||||
|
||||
if (delete_on_close_) {
|
||||
callback_.Run(GetResult());
|
||||
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
|
||||
} else if (run_loop_) {
|
||||
run_loop_->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
int MessageDialog::GetResult() const {
|
||||
// When the dialog is closed without choosing anything, we think the user
|
||||
// chose 'Cancel', otherwise we think the default behavior is chosen.
|
||||
if (result_ == -1) {
|
||||
for (size_t i = 0; i < buttons_.size(); ++i)
|
||||
if (LowerCaseEqualsASCII(buttons_[i]->GetText(), "cancel")) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return result_;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MessageDialog, private:
|
||||
|
||||
base::string16 MessageDialog::GetWindowTitle() const {
|
||||
return title_;
|
||||
}
|
||||
|
||||
gfx::ImageSkia MessageDialog::GetWindowAppIcon() {
|
||||
return icon_;
|
||||
}
|
||||
|
||||
gfx::ImageSkia MessageDialog::GetWindowIcon() {
|
||||
return icon_;
|
||||
}
|
||||
|
||||
bool MessageDialog::ShouldShowWindowIcon() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
views::Widget* MessageDialog::GetWidget() {
|
||||
return widget_.get();
|
||||
}
|
||||
|
||||
const views::Widget* MessageDialog::GetWidget() const {
|
||||
return widget_.get();
|
||||
}
|
||||
|
||||
views::View* MessageDialog::GetContentsView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
views::View* MessageDialog::GetInitiallyFocusedView() {
|
||||
if (buttons_.size() > 0)
|
||||
return buttons_[0];
|
||||
else
|
||||
return this;
|
||||
}
|
||||
|
||||
ui::ModalType MessageDialog::GetModalType() const {
|
||||
return ui::MODAL_TYPE_SYSTEM;
|
||||
}
|
||||
|
||||
views::NonClientFrameView* MessageDialog::CreateNonClientFrameView(
|
||||
views::Widget* widget) {
|
||||
if (!parent_) {
|
||||
#if defined(USE_X11)
|
||||
return new views::NativeFrameView(widget);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Create a bubble style frame like Chrome.
|
||||
views::BubbleFrameView* frame = new views::BubbleFrameView(gfx::Insets());
|
||||
const SkColor color = widget->GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_DialogBackground);
|
||||
scoped_ptr<views::BubbleBorder> border(new views::BubbleBorder(
|
||||
views::BubbleBorder::FLOAT, views::BubbleBorder::SMALL_SHADOW, color));
|
||||
frame->SetBubbleBorder(border.Pass());
|
||||
wm::SetShadowType(widget->GetNativeWindow(), wm::SHADOW_TYPE_NONE);
|
||||
return frame;
|
||||
}
|
||||
|
||||
views::ClientView* MessageDialog::CreateClientView(views::Widget* widget) {
|
||||
return new MessageDialogClientView(this, widget);
|
||||
}
|
||||
|
||||
gfx::Size MessageDialog::GetPreferredSize() const {
|
||||
gfx::Size size(0, buttons_[0]->GetPreferredSize().height());
|
||||
for (size_t i = 0; i < buttons_.size(); ++i)
|
||||
size.Enlarge(buttons_[i]->GetPreferredSize().width(), 0);
|
||||
|
||||
// Button spaces.
|
||||
size.Enlarge(views::kRelatedButtonHSpacing * (buttons_.size() - 1),
|
||||
views::kRelatedControlVerticalSpacing);
|
||||
|
||||
// The message box view.
|
||||
gfx::Size contents_size = message_box_view_->GetPreferredSize();
|
||||
size.Enlarge(0, contents_size.height());
|
||||
if (contents_size.width() > size.width())
|
||||
size.set_width(contents_size.width());
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void MessageDialog::Layout() {
|
||||
gfx::Rect bounds = GetContentsBounds();
|
||||
|
||||
// Layout the row containing the buttons.
|
||||
int x = bounds.width();
|
||||
int height = buttons_[0]->GetPreferredSize().height() +
|
||||
views::kRelatedControlVerticalSpacing;
|
||||
|
||||
// NB: We iterate through the buttons backwards here because
|
||||
// Mac and Windows buttons are laid out in opposite order.
|
||||
for (int i = buttons_.size() - 1; i >= 0; --i) {
|
||||
gfx::Size size = buttons_[i]->GetPreferredSize();
|
||||
x -= size.width() + views::kRelatedButtonHSpacing;
|
||||
|
||||
buttons_[i]->SetBounds(x, bounds.height() - height,
|
||||
size.width(), size.height());
|
||||
}
|
||||
|
||||
// Layout the message box view.
|
||||
message_box_view_->SetBounds(bounds.x(), bounds.y(), bounds.width(),
|
||||
bounds.height() - height);
|
||||
}
|
||||
|
||||
bool MessageDialog::AcceleratorPressed(const ui::Accelerator& accelerator) {
|
||||
DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
|
||||
widget_->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageDialog::ButtonPressed(views::Button* sender,
|
||||
const ui::Event& event) {
|
||||
result_ = sender->tag();
|
||||
widget_->Close();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
MessageDialog dialog(
|
||||
parent_window, type, buttons, title, message, detail, icon);
|
||||
{
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow(
|
||||
base::MessageLoopForUI::current());
|
||||
base::RunLoop run_loop;
|
||||
dialog.Show(&run_loop);
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
return dialog.GetResult();
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
// The dialog would be deleted when the dialog is closed.
|
||||
MessageDialog* dialog = new MessageDialog(
|
||||
parent_window, type, buttons, title, message, detail, icon);
|
||||
dialog->set_callback(callback);
|
||||
dialog->Show();
|
||||
}
|
||||
|
||||
void ShowErrorBox(const base::string16& title, const base::string16& content) {
|
||||
#if defined(OS_WIN)
|
||||
ui::MessageBox(NULL, content, title, MB_OK | MB_ICONERROR | MB_TASKMODAL);
|
||||
#elif defined(USE_X11)
|
||||
if (Browser::Get()->is_ready()) {
|
||||
GtkWidget* dialog = gtk_message_dialog_new(
|
||||
NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
|
||||
"%s", base::UTF16ToUTF8(title).c_str());
|
||||
gtk_message_dialog_format_secondary_text(
|
||||
GTK_MESSAGE_DIALOG(dialog),
|
||||
"%s", base::UTF16ToUTF8(content).c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY
|
||||
ANSI_FOREGROUND_RED "%s\n"
|
||||
ANSI_FOREGROUND_BLACK "%s"
|
||||
ANSI_RESET "\n",
|
||||
base::UTF16ToUTF8(title).c_str(),
|
||||
base::UTF16ToUTF8(content).c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
230
atom/browser/ui/message_box_win.cc
Normal file
230
atom/browser/ui/message_box_win.cc
Normal file
@@ -0,0 +1,230 @@
|
||||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/message_box.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/native_window_views.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "ui/gfx/icon_util.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Small command ID values are already taken by Windows, we have to start from
|
||||
// a large number to avoid conflicts with Windows.
|
||||
const int kIDStart = 100;
|
||||
|
||||
// Get the common ID from button's name.
|
||||
struct CommonButtonID {
|
||||
int button;
|
||||
int id;
|
||||
};
|
||||
CommonButtonID GetCommonID(const base::string16& button) {
|
||||
base::string16 lower = base::StringToLowerASCII(button);
|
||||
if (lower == L"ok")
|
||||
return { TDCBF_OK_BUTTON, IDOK };
|
||||
else if (lower == L"yes")
|
||||
return { TDCBF_YES_BUTTON, IDYES };
|
||||
else if (lower == L"no")
|
||||
return { TDCBF_NO_BUTTON, IDNO };
|
||||
else if (lower == L"cancel")
|
||||
return { TDCBF_CANCEL_BUTTON, IDCANCEL };
|
||||
else if (lower == L"retry")
|
||||
return { TDCBF_RETRY_BUTTON, IDRETRY };
|
||||
else if (lower == L"close")
|
||||
return { TDCBF_CLOSE_BUTTON, IDCLOSE };
|
||||
return { -1, -1 };
|
||||
}
|
||||
|
||||
// Determine whether the buttons are common buttons, if so map common ID
|
||||
// to button ID.
|
||||
void MapToCommonID(const std::vector<base::string16>& buttons,
|
||||
std::map<int, int>* id_map,
|
||||
TASKDIALOG_COMMON_BUTTON_FLAGS* button_flags,
|
||||
std::vector<TASKDIALOG_BUTTON>* dialog_buttons) {
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
auto common = GetCommonID(buttons[i]);
|
||||
if (common.button != -1) {
|
||||
// It is a common button.
|
||||
(*id_map)[common.id] = i;
|
||||
(*button_flags) |= common.button;
|
||||
} else {
|
||||
// It is a custom button.
|
||||
dialog_buttons->push_back({i + kIDStart, buttons[i].c_str()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ShowMessageBoxUTF16(HWND parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<base::string16>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const base::string16& title,
|
||||
const base::string16& message,
|
||||
const base::string16& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
TASKDIALOG_FLAGS flags = TDF_SIZE_TO_CONTENT; // show all content.
|
||||
if (cancel_id != 0)
|
||||
flags |= TDF_ALLOW_DIALOG_CANCELLATION; // allow dialog to be cancelled.
|
||||
|
||||
TASKDIALOGCONFIG config = { 0 };
|
||||
config.cbSize = sizeof(config);
|
||||
config.hwndParent = parent;
|
||||
config.hInstance = GetModuleHandle(NULL);
|
||||
config.dwFlags = flags;
|
||||
|
||||
if (!title.empty())
|
||||
config.pszWindowTitle = title.c_str();
|
||||
|
||||
base::win::ScopedHICON hicon;
|
||||
if (!icon.isNull()) {
|
||||
hicon.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
|
||||
config.dwFlags |= TDF_USE_HICON_MAIN;
|
||||
config.hMainIcon = hicon.Get();
|
||||
} else {
|
||||
// Show icon according to dialog's type.
|
||||
switch (type) {
|
||||
case MESSAGE_BOX_TYPE_INFORMATION:
|
||||
case MESSAGE_BOX_TYPE_QUESTION:
|
||||
config.pszMainIcon = TD_INFORMATION_ICON;
|
||||
break;
|
||||
case MESSAGE_BOX_TYPE_WARNING:
|
||||
config.pszMainIcon = TD_WARNING_ICON;
|
||||
break;
|
||||
case MESSAGE_BOX_TYPE_ERROR:
|
||||
config.pszMainIcon = TD_ERROR_ICON;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If "detail" is empty then don't make message hilighted.
|
||||
if (detail.empty()) {
|
||||
config.pszContent = message.c_str();
|
||||
} else {
|
||||
config.pszMainInstruction = message.c_str();
|
||||
config.pszContent = detail.c_str();
|
||||
}
|
||||
|
||||
// Iterate through the buttons, put common buttons in dwCommonButtons
|
||||
// and custom buttons in pButtons.
|
||||
std::map<int, int> id_map;
|
||||
std::vector<TASKDIALOG_BUTTON> dialog_buttons;
|
||||
if (options & MESSAGE_BOX_NO_LINK) {
|
||||
for (size_t i = 0; i < buttons.size(); ++i)
|
||||
dialog_buttons.push_back({i + kIDStart, buttons[i].c_str()});
|
||||
} else {
|
||||
MapToCommonID(buttons, &id_map, &config.dwCommonButtons, &dialog_buttons);
|
||||
}
|
||||
if (dialog_buttons.size() > 0) {
|
||||
config.pButtons = &dialog_buttons.front();
|
||||
config.cButtons = dialog_buttons.size();
|
||||
if (!(options & MESSAGE_BOX_NO_LINK))
|
||||
config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links.
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
TaskDialogIndirect(&config, &id, NULL, NULL);
|
||||
if (id_map.find(id) != id_map.end()) // common button.
|
||||
return id_map[id];
|
||||
else if (id >= kIDStart) // custom button.
|
||||
return id - kIDStart;
|
||||
else
|
||||
return cancel_id;
|
||||
}
|
||||
|
||||
void RunMessageBoxInNewThread(base::Thread* thread,
|
||||
NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
int result = ShowMessageBox(parent, type, buttons, cancel_id, options, title,
|
||||
message, detail, icon);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
|
||||
content::BrowserThread::DeleteSoon(
|
||||
content::BrowserThread::UI, FROM_HERE, thread);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
std::vector<base::string16> utf16_buttons;
|
||||
for (const auto& button : buttons)
|
||||
utf16_buttons.push_back(base::UTF8ToUTF16(button));
|
||||
|
||||
HWND hwnd_parent = parent ?
|
||||
static_cast<atom::NativeWindowViews*>(parent)->GetAcceleratedWidget() :
|
||||
NULL;
|
||||
|
||||
NativeWindow::DialogScope dialog_scope(parent);
|
||||
return ShowMessageBoxUTF16(hwnd_parent,
|
||||
type,
|
||||
utf16_buttons,
|
||||
cancel_id,
|
||||
options,
|
||||
base::UTF8ToUTF16(title),
|
||||
base::UTF8ToUTF16(message),
|
||||
base::UTF8ToUTF16(detail),
|
||||
icon);
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
int cancel_id,
|
||||
int options,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
scoped_ptr<base::Thread> thread(
|
||||
new base::Thread(ATOM_PRODUCT_NAME "MessageBoxThread"));
|
||||
thread->init_com_with_mta(false);
|
||||
if (!thread->Start()) {
|
||||
callback.Run(cancel_id);
|
||||
return;
|
||||
}
|
||||
|
||||
base::Thread* unretained = thread.release();
|
||||
unretained->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
|
||||
parent, type, buttons, 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());
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
@@ -26,6 +26,9 @@ void TrayIcon::DisplayBalloon(const gfx::Image& icon,
|
||||
const base::string16& contents) {
|
||||
}
|
||||
|
||||
void TrayIcon::PopContextMenu(const gfx::Point& pos) {
|
||||
}
|
||||
|
||||
void TrayIcon::NotifyClicked(const gfx::Rect& bounds) {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnClicked(bounds));
|
||||
}
|
||||
@@ -46,4 +49,12 @@ void TrayIcon::NotifyBalloonClosed() {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnBalloonClosed());
|
||||
}
|
||||
|
||||
void TrayIcon::NotifyRightClicked(const gfx::Rect& bounds) {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnRightClicked(bounds));
|
||||
}
|
||||
|
||||
void TrayIcon::NotfiyDropFiles(const std::vector<std::string>& files) {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnDropFiles(files));
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define ATOM_BROWSER_UI_TRAY_ICON_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/ui/tray_icon_observer.h"
|
||||
#include "base/observer_list.h"
|
||||
@@ -46,6 +47,8 @@ class TrayIcon {
|
||||
const base::string16& title,
|
||||
const base::string16& contents);
|
||||
|
||||
virtual void PopContextMenu(const gfx::Point& pos);
|
||||
|
||||
// Set the context menu for this icon.
|
||||
virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) = 0;
|
||||
|
||||
@@ -56,6 +59,8 @@ class TrayIcon {
|
||||
void NotifyBalloonShow();
|
||||
void NotifyBalloonClicked();
|
||||
void NotifyBalloonClosed();
|
||||
void NotifyRightClicked(const gfx::Rect& bounds = gfx::Rect());
|
||||
void NotfiyDropFiles(const std::vector<std::string>& files);
|
||||
|
||||
protected:
|
||||
TrayIcon();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
|
||||
@class AtomMenuController;
|
||||
@class StatusItemController;
|
||||
@class StatusItemView;
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -27,12 +27,12 @@ class TrayIconCocoa : public TrayIcon {
|
||||
void SetToolTip(const std::string& tool_tip) override;
|
||||
void SetTitle(const std::string& title) override;
|
||||
void SetHighlightMode(bool highlight) override;
|
||||
void PopContextMenu(const gfx::Point& pos) override;
|
||||
void SetContextMenu(ui::SimpleMenuModel* menu_model) override;
|
||||
|
||||
private:
|
||||
base::scoped_nsobject<NSStatusItem> item_;
|
||||
|
||||
base::scoped_nsobject<StatusItemController> controller_;
|
||||
// Atom custom view for NSStatusItem.
|
||||
base::scoped_nsobject<StatusItemView> status_item_view_;
|
||||
|
||||
// Status menu shown when right-clicking the system icon.
|
||||
base::scoped_nsobject<AtomMenuController> menu_;
|
||||
|
||||
@@ -9,81 +9,242 @@
|
||||
#include "ui/gfx/image/image.h"
|
||||
#include "ui/gfx/screen.h"
|
||||
|
||||
@interface StatusItemController : NSObject {
|
||||
namespace {
|
||||
|
||||
const CGFloat kStatusItemLength = 26;
|
||||
const CGFloat kMargin = 3;
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface StatusItemView : NSView {
|
||||
atom::TrayIconCocoa* trayIcon_; // weak
|
||||
AtomMenuController* menuController_; // weak
|
||||
BOOL isHighlightEnable_;
|
||||
BOOL inMouseEventSequence_;
|
||||
base::scoped_nsobject<NSImage> image_;
|
||||
base::scoped_nsobject<NSImage> alternateImage_;
|
||||
base::scoped_nsobject<NSString> title_;
|
||||
base::scoped_nsobject<NSStatusItem> statusItem_;
|
||||
}
|
||||
- (id)initWithIcon:(atom::TrayIconCocoa*)icon;
|
||||
- (void)handleClick:(id)sender;
|
||||
- (void)handleDoubleClick:(id)sender;
|
||||
|
||||
@end // @interface StatusItemController
|
||||
@end // @interface StatusItemView
|
||||
|
||||
@implementation StatusItemController
|
||||
@implementation StatusItemView
|
||||
|
||||
- (id)initWithIcon:(atom::TrayIconCocoa*)icon {
|
||||
trayIcon_ = icon;
|
||||
isHighlightEnable_ = YES;
|
||||
statusItem_.reset([[[NSStatusBar systemStatusBar] statusItemWithLength:
|
||||
NSVariableStatusItemLength] retain]);
|
||||
NSRect frame = NSMakeRect(0,
|
||||
0,
|
||||
kStatusItemLength,
|
||||
[[statusItem_ statusBar] thickness]);
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
[self registerForDraggedTypes:
|
||||
[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
|
||||
[statusItem_ setView:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)handleClick:(id)sender {
|
||||
// Get the frame of the NSStatusItem.
|
||||
NSRect frame = [NSApp currentEvent].window.frame;
|
||||
- (void)removeItem {
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
|
||||
statusItem_.reset();
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
// Draw the tray icon and title that align with NSSStatusItem, layout:
|
||||
// ----------------
|
||||
// | icon | title |
|
||||
/// ----------------
|
||||
BOOL highlight = [self shouldHighlight];
|
||||
CGFloat titleWidth = [self titleWidth];
|
||||
// Calculate the total icon bounds.
|
||||
NSRect statusItemBounds = NSMakeRect(0,
|
||||
0,
|
||||
kStatusItemLength + titleWidth,
|
||||
[[statusItem_ statusBar] thickness]);
|
||||
[statusItem_ drawStatusBarBackgroundInRect:statusItemBounds
|
||||
withHighlight:highlight];
|
||||
[statusItem_ setLength:titleWidth + kStatusItemLength];
|
||||
if (title_) {
|
||||
NSRect titleDrawRect = NSMakeRect(kStatusItemLength,
|
||||
0,
|
||||
titleWidth + kStatusItemLength,
|
||||
[[statusItem_ statusBar] thickness]);
|
||||
[title_ drawInRect:titleDrawRect
|
||||
withAttributes:[self titleAttributes]];
|
||||
}
|
||||
|
||||
NSRect iconRect = NSMakeRect(0,
|
||||
0,
|
||||
kStatusItemLength,
|
||||
[[statusItem_ statusBar] thickness]);
|
||||
if (highlight && alternateImage_) {
|
||||
[alternateImage_ drawInRect:NSInsetRect(iconRect, kMargin, kMargin)
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositeSourceOver
|
||||
fraction:1];
|
||||
} else {
|
||||
[image_ drawInRect:NSInsetRect(iconRect, kMargin, kMargin)
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositeSourceOver
|
||||
fraction:1];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)titleWidth {
|
||||
if (!title_) return 0;
|
||||
NSAttributedString* attributes =
|
||||
[[NSAttributedString alloc] initWithString:title_
|
||||
attributes:[self titleAttributes]];
|
||||
return [attributes size].width;
|
||||
}
|
||||
|
||||
- (NSDictionary*)titleAttributes {
|
||||
NSFont* font = [NSFont menuBarFontOfSize:0];
|
||||
NSColor* foregroundColor = [NSColor blackColor];
|
||||
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
foregroundColor, NSForegroundColorAttributeName,
|
||||
nil];
|
||||
}
|
||||
|
||||
- (void)setImage:(NSImage*)image {
|
||||
image_.reset([image copy]);
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setAlternateImage:(NSImage*)image {
|
||||
alternateImage_.reset([image copy]);
|
||||
}
|
||||
|
||||
- (void)setHighlight:(BOOL)highlight {
|
||||
isHighlightEnable_ = highlight;
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString*)title {
|
||||
title_.reset([title copy]);
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setMenuController:(AtomMenuController*)menu {
|
||||
menuController_ = menu;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event {
|
||||
inMouseEventSequence_ = YES;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(NSEvent*)event {
|
||||
if (!inMouseEventSequence_) {
|
||||
// If the menu is showing, when user clicked the tray icon, the `mouseDown`
|
||||
// event will be dissmissed, we need to close the menu at this time.
|
||||
[self setNeedsDisplay:YES];
|
||||
return;
|
||||
}
|
||||
inMouseEventSequence_ = NO;
|
||||
if (event.clickCount == 1) {
|
||||
if (menuController_) {
|
||||
[statusItem_ popUpStatusItemMenu:[menuController_ menu]];
|
||||
}
|
||||
|
||||
trayIcon_->NotifyClicked([self getBoundsFromEvent:event]);
|
||||
}
|
||||
|
||||
if (event.clickCount == 2 && !menuController_) {
|
||||
trayIcon_->NotifyDoubleClicked();
|
||||
}
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)popContextMenu {
|
||||
if (menuController_ && ![menuController_ isMenuOpen]) {
|
||||
// redraw the dray icon to show highlight if it is enabled.
|
||||
[self setNeedsDisplay:YES];
|
||||
[statusItem_ popUpStatusItemMenu:[menuController_ menu]];
|
||||
// The popUpStatusItemMenu returns only after the showing menu is closed.
|
||||
// When it returns, we need to redraw the tray icon to not show highlight.
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rightMouseUp:(NSEvent*)event {
|
||||
trayIcon_->NotifyRightClicked([self getBoundsFromEvent:event]);
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
|
||||
return NSDragOperationCopy;
|
||||
}
|
||||
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
|
||||
NSPasteboard* pboard = [sender draggingPasteboard];
|
||||
|
||||
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
|
||||
std::vector<std::string> dropFiles;
|
||||
NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
|
||||
for (NSString* file in files)
|
||||
dropFiles.push_back(base::SysNSStringToUTF8(file));
|
||||
trayIcon_->NotfiyDropFiles(dropFiles);
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)shouldHighlight {
|
||||
BOOL is_menu_open = [menuController_ isMenuOpen];
|
||||
return isHighlightEnable_ && (inMouseEventSequence_ || is_menu_open);
|
||||
}
|
||||
|
||||
- (gfx::Rect)getBoundsFromEvent:(NSEvent*)event {
|
||||
NSRect frame = event.window.frame;
|
||||
gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
|
||||
// Flip coordinates to gfx (0,0 in top-left corner) using current screen.
|
||||
NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
|
||||
bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
|
||||
|
||||
trayIcon_->NotifyClicked(bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (void)handleDoubleClick:(id)sender {
|
||||
trayIcon_->NotifyDoubleClicked();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace atom {
|
||||
|
||||
TrayIconCocoa::TrayIconCocoa() {
|
||||
controller_.reset([[StatusItemController alloc] initWithIcon:this]);
|
||||
|
||||
item_.reset([[[NSStatusBar systemStatusBar]
|
||||
statusItemWithLength:NSVariableStatusItemLength] retain]);
|
||||
[item_ setEnabled:YES];
|
||||
[item_ setTarget:controller_];
|
||||
[item_ setAction:@selector(handleClick:)];
|
||||
[item_ setDoubleAction:@selector(handleDoubleClick:)];
|
||||
[item_ setHighlightMode:YES];
|
||||
status_item_view_.reset([[StatusItemView alloc] initWithIcon:this]);
|
||||
}
|
||||
|
||||
TrayIconCocoa::~TrayIconCocoa() {
|
||||
// Remove the status item from the status bar.
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem:item_];
|
||||
[status_item_view_ removeItem];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetImage(const gfx::Image& image) {
|
||||
[item_ setImage:image.AsNSImage()];
|
||||
[status_item_view_ setImage:image.AsNSImage()];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetPressedImage(const gfx::Image& image) {
|
||||
[item_ setAlternateImage:image.AsNSImage()];
|
||||
[status_item_view_ setAlternateImage:image.AsNSImage()];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetToolTip(const std::string& tool_tip) {
|
||||
[item_ setToolTip:base::SysUTF8ToNSString(tool_tip)];
|
||||
[status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetTitle(const std::string& title) {
|
||||
[item_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
[status_item_view_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetHighlightMode(bool highlight) {
|
||||
[item_ setHighlightMode:highlight];
|
||||
[status_item_view_ setHighlight:highlight];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::PopContextMenu(const gfx::Point& pos) {
|
||||
[status_item_view_ popContextMenu];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) {
|
||||
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model]);
|
||||
[item_ setMenu:[menu_ menu]];
|
||||
[status_item_view_ setMenuController:menu_.get()];
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#ifndef ATOM_BROWSER_UI_TRAY_ICON_OBSERVER_H_
|
||||
#define ATOM_BROWSER_UI_TRAY_ICON_OBSERVER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace gfx {
|
||||
class Rect;
|
||||
}
|
||||
@@ -13,11 +16,13 @@ namespace atom {
|
||||
|
||||
class TrayIconObserver {
|
||||
public:
|
||||
virtual void OnClicked(const gfx::Rect&) {}
|
||||
virtual void OnClicked(const gfx::Rect& bounds) {}
|
||||
virtual void OnDoubleClicked() {}
|
||||
virtual void OnBalloonShow() {}
|
||||
virtual void OnBalloonClicked() {}
|
||||
virtual void OnBalloonClosed() {}
|
||||
virtual void OnRightClicked(const gfx::Rect& bounds) {}
|
||||
virtual void OnDropFiles(const std::vector<std::string>& files) {}
|
||||
|
||||
protected:
|
||||
virtual ~TrayIconObserver() {}
|
||||
|
||||
35
atom/browser/ui/views/native_frame_view.cc
Normal file
35
atom/browser/ui/views/native_frame_view.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/ui/views/native_frame_view.h"
|
||||
|
||||
#include "atom/browser/native_window_views.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kViewClassName[] = "AtomNativeFrameView";
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeFrameView::NativeFrameView(NativeWindowViews* window,
|
||||
views::Widget* widget)
|
||||
: views::NativeFrameView(widget),
|
||||
window_(window) {
|
||||
}
|
||||
|
||||
gfx::Size NativeFrameView::GetMinimumSize() const {
|
||||
return window_->GetMinimumSize();
|
||||
}
|
||||
|
||||
gfx::Size NativeFrameView::GetMaximumSize() const {
|
||||
return window_->GetMaximumSize();
|
||||
}
|
||||
|
||||
const char* NativeFrameView::GetClassName() const {
|
||||
return kViewClassName;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
34
atom/browser/ui/views/native_frame_view.h
Normal file
34
atom/browser/ui/views/native_frame_view.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_H_
|
||||
#define ATOM_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_H_
|
||||
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindowViews;
|
||||
|
||||
// Like the views::NativeFrameView, but returns the min/max size from the
|
||||
// NativeWindowViews.
|
||||
class NativeFrameView : public views::NativeFrameView {
|
||||
public:
|
||||
NativeFrameView(NativeWindowViews* window, views::Widget* widget);
|
||||
|
||||
protected:
|
||||
// views::View:
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
gfx::Size GetMaximumSize() const override;
|
||||
const char* GetClassName() const override;
|
||||
|
||||
private:
|
||||
NativeWindowViews* window_; // weak ref.
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeFrameView);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_VIEWS_NATIVE_FRAME_VIEW_H_
|
||||
@@ -47,38 +47,22 @@ NotifyIcon::~NotifyIcon() {
|
||||
|
||||
void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos,
|
||||
bool left_mouse_click) {
|
||||
NOTIFYICONIDENTIFIER icon_id;
|
||||
memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER));
|
||||
icon_id.uID = icon_id_;
|
||||
icon_id.hWnd = window_;
|
||||
icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
|
||||
RECT rect = { 0 };
|
||||
Shell_NotifyIconGetRect(&icon_id, &rect);
|
||||
|
||||
// Pass to the observer if appropriate.
|
||||
if (left_mouse_click) {
|
||||
NOTIFYICONIDENTIFIER icon_id;
|
||||
memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER));
|
||||
icon_id.uID = icon_id_;
|
||||
icon_id.hWnd = window_;
|
||||
icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
|
||||
|
||||
RECT rect = { 0 };
|
||||
Shell_NotifyIconGetRect(&icon_id, &rect);
|
||||
|
||||
NotifyClicked(gfx::Rect(rect));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!menu_model_)
|
||||
return;
|
||||
|
||||
// Set our window as the foreground window, so the context menu closes when
|
||||
// we click away from it.
|
||||
if (!SetForegroundWindow(window_))
|
||||
return;
|
||||
|
||||
views::MenuRunner menu_runner(
|
||||
menu_model_,
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
NULL,
|
||||
NULL,
|
||||
gfx::Rect(cursor_pos, gfx::Size()),
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
NotifyRightClicked(gfx::Rect(rect));
|
||||
PopContextMenu(cursor_pos);
|
||||
}
|
||||
|
||||
void NotifyIcon::ResetIcon() {
|
||||
@@ -151,6 +135,23 @@ void NotifyIcon::DisplayBalloon(const gfx::Image& icon,
|
||||
LOG(WARNING) << "Unable to create status tray balloon.";
|
||||
}
|
||||
|
||||
void NotifyIcon::PopContextMenu(const gfx::Point& pos) {
|
||||
// Set our window as the foreground window, so the context menu closes when
|
||||
// we click away from it.
|
||||
if (!SetForegroundWindow(window_))
|
||||
return;
|
||||
|
||||
views::MenuRunner menu_runner(
|
||||
menu_model_,
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
NULL,
|
||||
NULL,
|
||||
gfx::Rect(pos, gfx::Size()),
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
}
|
||||
|
||||
void NotifyIcon::SetContextMenu(ui::SimpleMenuModel* menu_model) {
|
||||
menu_model_ = menu_model;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class NotifyIcon : public TrayIcon {
|
||||
void DisplayBalloon(const gfx::Image& icon,
|
||||
const base::string16& title,
|
||||
const base::string16& contents) override;
|
||||
void PopContextMenu(const gfx::Point& pos) override;
|
||||
void SetContextMenu(ui::SimpleMenuModel* menu_model) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,17 +4,66 @@
|
||||
|
||||
#include "atom/browser/web_dialog_helper.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/ui/file_dialog.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/prefs/pref_service.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/file_chooser_file_info.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "ui/shell_dialogs/selected_file_info.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char kSelectFileLastDirectory[] = "selectfile.last_directory";
|
||||
|
||||
file_dialog::Filters GetFileTypesFromAcceptType(
|
||||
const std::vector<base::string16>& accept_types) {
|
||||
file_dialog::Filters filters;
|
||||
if (accept_types.empty())
|
||||
return filters;
|
||||
|
||||
std::vector<base::FilePath::StringType> extensions;
|
||||
|
||||
for (const auto& accept_type : accept_types) {
|
||||
std::string ascii_type = base::UTF16ToASCII(accept_type);
|
||||
if (ascii_type[0] == '.') {
|
||||
// If the type starts with a period it is assumed to be a file extension,
|
||||
// like `.txt`, // so we just have to add it to the list.
|
||||
base::FilePath::StringType extension(
|
||||
ascii_type.begin(), ascii_type.end());
|
||||
// Skip the first character.
|
||||
extensions.push_back(extension.substr(1));
|
||||
} else {
|
||||
if (ascii_type == "image/*" || ascii_type == "audio/*" ||
|
||||
ascii_type == "video/*") {
|
||||
// For MIME Type
|
||||
net::GetExtensionsForMimeType(ascii_type, &extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filters.push_back(file_dialog::Filter());
|
||||
for (const auto& extension : extensions) {
|
||||
#if defined(OS_WIN)
|
||||
filters[0].second.push_back(base::UTF16ToASCII(extension));
|
||||
#else
|
||||
filters[0].second.push_back(extension);
|
||||
#endif
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
WebDialogHelper::WebDialogHelper(NativeWindow* window)
|
||||
@@ -25,15 +74,18 @@ WebDialogHelper::WebDialogHelper(NativeWindow* window)
|
||||
WebDialogHelper::~WebDialogHelper() {
|
||||
}
|
||||
|
||||
|
||||
void WebDialogHelper::RunFileChooser(content::WebContents* web_contents,
|
||||
const content::FileChooserParams& params) {
|
||||
std::vector<content::FileChooserFileInfo> result;
|
||||
file_dialog::Filters filters = GetFileTypesFromAcceptType(
|
||||
params.accept_types);
|
||||
if (params.mode == content::FileChooserParams::Save) {
|
||||
base::FilePath path;
|
||||
if (file_dialog::ShowSaveDialog(window_,
|
||||
base::UTF16ToUTF8(params.title),
|
||||
params.default_file_name,
|
||||
file_dialog::Filters(),
|
||||
filters,
|
||||
&path)) {
|
||||
content::FileChooserFileInfo info;
|
||||
info.file_path = path;
|
||||
@@ -56,10 +108,14 @@ void WebDialogHelper::RunFileChooser(content::WebContents* web_contents,
|
||||
}
|
||||
|
||||
std::vector<base::FilePath> paths;
|
||||
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
|
||||
window_->web_contents()->GetBrowserContext());
|
||||
base::FilePath default_file_path = browser_context->prefs()->GetFilePath(
|
||||
kSelectFileLastDirectory).Append(params.default_file_name);
|
||||
if (file_dialog::ShowOpenDialog(window_,
|
||||
base::UTF16ToUTF8(params.title),
|
||||
params.default_file_name,
|
||||
file_dialog::Filters(),
|
||||
default_file_path,
|
||||
filters,
|
||||
flags,
|
||||
&paths)) {
|
||||
for (auto& path : paths) {
|
||||
@@ -68,6 +124,10 @@ void WebDialogHelper::RunFileChooser(content::WebContents* web_contents,
|
||||
info.display_name = path.BaseName().value();
|
||||
result.push_back(info);
|
||||
}
|
||||
if (!paths.empty()) {
|
||||
browser_context->prefs()->SetFilePath(kSelectFileLastDirectory,
|
||||
paths[0].DirName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "atom/common/native_mate_converters/image_converter.h"
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
@@ -50,9 +51,35 @@ std::string Read(const std::string& format_string,
|
||||
return data;
|
||||
}
|
||||
|
||||
void Write(const mate::Dictionary& data,
|
||||
mate::Arguments* args) {
|
||||
ui::ScopedClipboardWriter writer(GetClipboardType(args));
|
||||
base::string16 text, html;
|
||||
gfx::Image image;
|
||||
|
||||
if (data.Get("text", &text))
|
||||
writer.WriteText(text);
|
||||
|
||||
if (data.Get("html", &html))
|
||||
writer.WriteHTML(html, std::string());
|
||||
|
||||
if (data.Get("image", &image))
|
||||
writer.WriteImage(image.AsBitmap());
|
||||
}
|
||||
|
||||
base::string16 ReadText(mate::Arguments* args) {
|
||||
base::string16 data;
|
||||
ui::Clipboard::GetForCurrentThread()->ReadText(GetClipboardType(args), &data);
|
||||
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
|
||||
auto type = GetClipboardType(args);
|
||||
if (clipboard->IsFormatAvailable(
|
||||
ui::Clipboard::GetPlainTextWFormatType(), type)) {
|
||||
clipboard->ReadText(type, &data);
|
||||
} else if (clipboard->IsFormatAvailable(
|
||||
ui::Clipboard::GetPlainTextFormatType(), type)) {
|
||||
std::string result;
|
||||
clipboard->ReadAsciiText(type, &result);
|
||||
data = base::ASCIIToUTF16(result);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -99,6 +126,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
dict.SetMethod("availableFormats", &AvailableFormats);
|
||||
dict.SetMethod("has", &Has);
|
||||
dict.SetMethod("read", &Read);
|
||||
dict.SetMethod("write", &Write);
|
||||
dict.SetMethod("readText", &ReadText);
|
||||
dict.SetMethod("writeText", &WriteText);
|
||||
dict.SetMethod("readHtml", &ReadHtml);
|
||||
|
||||
@@ -54,6 +54,11 @@ bool GetChildNode(const base::DictionaryValue* root,
|
||||
const std::string& name,
|
||||
const base::DictionaryValue* dir,
|
||||
const base::DictionaryValue** out) {
|
||||
if (name == "") {
|
||||
*out = root;
|
||||
return true;
|
||||
}
|
||||
|
||||
const base::DictionaryValue* files = NULL;
|
||||
return GetFilesNode(root, dir, &files) &&
|
||||
files->GetDictionaryWithoutPathExpansion(name, out);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 29
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 30
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "atom/common/event_emitter_caller.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
@@ -11,21 +13,8 @@ namespace internal {
|
||||
v8::Local<v8::Value> CallEmitWithArgs(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> obj,
|
||||
ValueVector* args) {
|
||||
v8::Local<v8::String> emit_name = StringToSymbol(isolate, "emit");
|
||||
v8::Local<v8::Value> emit = obj->Get(emit_name);
|
||||
if (emit.IsEmpty() || !emit->IsFunction()) {
|
||||
isolate->ThrowException(v8::Exception::TypeError(
|
||||
StringToV8(isolate, "\"emit\" is not a function")));
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::Value> result = emit.As<v8::Function>()->Call(
|
||||
isolate->GetCurrentContext(), obj, args->size(), &args->front());
|
||||
if (result.IsEmpty()) {
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
return result.ToLocalChecked();
|
||||
return node::MakeCallback(
|
||||
isolate, obj, "emit", args->size(), &args->front());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
@@ -107,6 +107,9 @@ const char kDisableHttpCache[] = "disable-http-cache";
|
||||
// Register schemes to standard.
|
||||
const char kRegisterStandardSchemes[] = "register-standard-schemes";
|
||||
|
||||
// The browser process app model ID
|
||||
const char kAppUserModelId[] = "app-user-model-id";
|
||||
|
||||
} // namespace switches
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -58,6 +58,8 @@ extern const char kPageVisibility[];
|
||||
extern const char kDisableHttpCache[];
|
||||
extern const char kRegisterStandardSchemes[];
|
||||
|
||||
extern const char kAppUserModelId[];
|
||||
|
||||
} // namespace switches
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
@@ -102,6 +106,15 @@ void AtomRendererClient::WebKitInitialized() {
|
||||
|
||||
void AtomRendererClient::RenderThreadStarted() {
|
||||
content::RenderThread::Get()->AddObserver(this);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
base::string16 app_id =
|
||||
command_line->GetSwitchValueNative(switches::kAppUserModelId);
|
||||
if (!app_id.empty()) {
|
||||
SetCurrentProcessExplicitAppUserModelID(app_id.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AtomRendererClient::RenderFrameCreated(
|
||||
|
||||
@@ -77,7 +77,7 @@ if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe']
|
||||
global.__dirname = __dirname
|
||||
|
||||
# Redirect window.onerror to uncaughtException.
|
||||
window.onerror = (error) ->
|
||||
window.onerror = (message, filename, lineno, colno, error) ->
|
||||
if global.process.listeners('uncaughtException').length > 0
|
||||
global.process.emit 'uncaughtException', error
|
||||
true
|
||||
@@ -93,6 +93,7 @@ else
|
||||
delete global.process
|
||||
delete global.setImmediate
|
||||
delete global.clearImmediate
|
||||
delete global.global
|
||||
|
||||
# Load the script specfied by the "preload" attribute.
|
||||
if preloadScript
|
||||
|
||||
@@ -2,6 +2,12 @@ process = global.process
|
||||
ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
|
||||
# Helper function to resolve relative url.
|
||||
a = window.top.document.createElement 'a'
|
||||
resolveUrl = (url) ->
|
||||
a.href = url
|
||||
a.href
|
||||
|
||||
# Window object returned by "window.open".
|
||||
class BrowserWindowProxy
|
||||
constructor: (@guestId) ->
|
||||
@@ -49,8 +55,17 @@ window.open = (url, frameName='', features='') ->
|
||||
options.width ?= 800
|
||||
options.height ?= 600
|
||||
|
||||
# Resolve relative urls.
|
||||
url = resolveUrl url
|
||||
|
||||
(options[name] = parseInt(options[name], 10) if options[name]?) for name in ints
|
||||
|
||||
# Inherit the node-integration option of current window.
|
||||
unless options['node-integration']
|
||||
for arg in process.argv when arg.indexOf('--node-integration=') is 0
|
||||
options['node-integration'] = arg.substr(-4) is 'true'
|
||||
break
|
||||
|
||||
guestId = ipc.sendSync 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, options
|
||||
if guestId
|
||||
new BrowserWindowProxy(guestId)
|
||||
|
||||
@@ -269,6 +269,7 @@ registerWebViewElement = ->
|
||||
"goToOffset"
|
||||
"isCrashed"
|
||||
"setUserAgent"
|
||||
"getUserAgent"
|
||||
"executeJavaScript"
|
||||
"insertCSS"
|
||||
"openDevTools"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
* [개요](api/synopsis-ko.md)
|
||||
* [프로세스 객체](api/process-ko.md)
|
||||
* [크롬 Command-Line 스위치에 대한 지원](api/chrome-command-line-switches-ko.md)
|
||||
* [크롬 Command-Line 스위치 지원](api/chrome-command-line-switches-ko.md)
|
||||
|
||||
커스텀 DOM Element:
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
* [menu](api/menu-ko.md)
|
||||
* [menu-item](api/menu-item-ko.md)
|
||||
* [power-monitor](api/power-monitor-ko.md)
|
||||
* [power-save-blocker](api/power-save-blocker-ko.md)
|
||||
* [protocol](api/protocol-ko.md)
|
||||
* [tray](api/tray-ko.md)
|
||||
|
||||
@@ -58,12 +59,10 @@
|
||||
## 개발자용
|
||||
|
||||
* [코딩 스타일](development/coding-style-ko.md)
|
||||
* [소스코드 구조](development/source-code-directory-structure-ko.md)
|
||||
* [소스 코드 디렉터리 구조](development/source-code-directory-structure-ko.md)
|
||||
* [NW.js와 기술적으로 다른점 (이전 node-webkit)](development/atom-shell-vs-node-webkit-ko.md)
|
||||
* [빌드 시스템 개요](development/build-system-overview-ko.md)
|
||||
* [빌드 설명서 (Mac)](development/build-instructions-mac-ko.md)
|
||||
* [빌드 설명서 (Windows)](development/build-instructions-windows-ko.md)
|
||||
* [빌드 설명서 (Linux)](development/build-instructions-linux-ko.md)
|
||||
* [디버거에서 디버그 심볼 서버 설정](development/setting-up-symbol-server-ko.md)
|
||||
|
||||
이 문서는 (@preco21)[https://github.com/preco21]이 번역하였습니다.
|
||||
|
||||
@@ -38,6 +38,7 @@ Modules for the main process:
|
||||
* [menu](api/menu.md)
|
||||
* [menu-item](api/menu-item.md)
|
||||
* [power-monitor](api/power-monitor.md)
|
||||
* [power-save-blocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
* [tray](api/tray.md)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Accelerator
|
||||
# Accelerator
|
||||
|
||||
Accelerator는 키보드 단축키를 표현하는 문자열입니다, 여러 혼합키와 키코드를 `+` 문자를
|
||||
이용하여 결합할 수 있습니다.
|
||||
|
||||
317
docs/api/app-ko.md
Normal file
317
docs/api/app-ko.md
Normal file
@@ -0,0 +1,317 @@
|
||||
# app
|
||||
|
||||
The `app` module is responsible for controlling the application's life time.
|
||||
|
||||
The example of quitting the whole application when the last window is closed:
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
app.on('window-all-closed', function() {
|
||||
app.quit();
|
||||
});
|
||||
```
|
||||
|
||||
## Event: will-finish-launching
|
||||
|
||||
Emitted when application has done basic startup. On Windows and Linux it is the
|
||||
same with `ready` event, on OS X this event represents the
|
||||
`applicationWillFinishLaunching` message of `NSApplication`, usually you would
|
||||
setup listeners to `open-file` and `open-url` events here, and start the crash
|
||||
reporter and auto updater.
|
||||
|
||||
Under most cases you should just do everything in `ready` event.
|
||||
|
||||
## Event: ready
|
||||
|
||||
Emitted when Electron has done everything initialization.
|
||||
|
||||
## Event: window-all-closed
|
||||
|
||||
Emitted when all windows have been closed.
|
||||
|
||||
This event is only emitted when the application is not going to quit. If a
|
||||
user pressed `Cmd + Q`, or the developer called `app.quit()`, Electron would
|
||||
first try to close all windows and then emit the `will-quit` event, and in
|
||||
this case the `window-all-closed` would not be emitted.
|
||||
|
||||
## Event: before-quit
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted before the application starts closing its windows.
|
||||
Calling `event.preventDefault()` will prevent the default behaviour, which is
|
||||
terminating the application.
|
||||
|
||||
## Event: will-quit
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when all windows have been closed and the application will quit.
|
||||
Calling `event.preventDefault()` will prevent the default behaviour, which is
|
||||
terminating the application.
|
||||
|
||||
See description of `window-all-closed` for the differences between `will-quit`
|
||||
and it.
|
||||
|
||||
## Event: quit
|
||||
|
||||
Emitted when application is quitting.
|
||||
|
||||
## Event: open-file
|
||||
|
||||
* `event` Event
|
||||
* `path` String
|
||||
|
||||
Emitted when user wants to open a file with the application, it usually happens
|
||||
when the application is already opened and then OS wants to reuse the
|
||||
application to open file. But it is also emitted when a file is dropped onto the
|
||||
dock and the application is not yet running. Make sure to listen to open-file
|
||||
very early in your application startup to handle this case (even before the
|
||||
`ready` event is emitted).
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
|
||||
## Event: open-url
|
||||
|
||||
* `event` Event
|
||||
* `url` String
|
||||
|
||||
Emitted when user wants to open a URL with the application, this URL scheme
|
||||
must be registered to be opened by your application.
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
|
||||
## Event: activate-with-no-open-windows
|
||||
|
||||
Emitted when the application is activated while there is no opened windows. It
|
||||
usually happens when user has closed all of application's windows and then
|
||||
click on the application's dock icon.
|
||||
|
||||
## Event: browser-window-blur
|
||||
|
||||
* `event` Event
|
||||
* `window` BrowserWindow
|
||||
|
||||
Emitted when a [browserWindow](browser-window-ko.md) gets blurred.
|
||||
|
||||
## Event: browser-window-focus
|
||||
|
||||
* `event` Event
|
||||
* `window` BrowserWindow
|
||||
|
||||
Emitted when a [browserWindow](browser-window-ko.md) gets focused.
|
||||
|
||||
### Event: 'select-certificate'
|
||||
|
||||
Emitted when client certificate is requested.
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](browser-window-ko.md#class-webcontents)
|
||||
* `url` String
|
||||
* `certificateList` [Objects]
|
||||
* `data` PEM encoded data
|
||||
* `issuerName` Issuer's Common Name
|
||||
* `callback` Function
|
||||
|
||||
```
|
||||
app.on('select-certificate', function(event, host, url, list, callback) {
|
||||
event.preventDefault();
|
||||
callback(list[0]);
|
||||
})
|
||||
```
|
||||
|
||||
`url` corresponds to the navigation entry requesting the client certificate,
|
||||
`callback` needs to be called with an entry filtered from the list.
|
||||
`event.preventDefault()` prevents from using the first certificate from
|
||||
the store.
|
||||
|
||||
### Event: 'gpu-process-crashed'
|
||||
|
||||
Emitted when the gpu process is crashed.
|
||||
|
||||
## app.quit()
|
||||
|
||||
Try to close all windows. The `before-quit` event will first be emitted. If all
|
||||
windows are successfully closed, the `will-quit` event will be emitted and by
|
||||
default the application would be terminated.
|
||||
|
||||
This method guarantees all `beforeunload` and `unload` handlers are correctly
|
||||
executed. It is possible that a window cancels the quitting by returning
|
||||
`false` in `beforeunload` handler.
|
||||
|
||||
## app.getPath(name)
|
||||
|
||||
* `name` String
|
||||
|
||||
Retrieves a path to a special directory or file associated with `name`. On
|
||||
failure an `Error` would throw.
|
||||
|
||||
You can request following paths by the names:
|
||||
|
||||
* `home`: User's home directory
|
||||
* `appData`: Per-user application data directory, by default it is pointed to:
|
||||
* `%APPDATA%` on Windows
|
||||
* `$XDG_CONFIG_HOME` or `~/.config` on Linux
|
||||
* `~/Library/Application Support` on OS X
|
||||
* `userData`: The directory for storing your app's configuration files, by
|
||||
default it is the `appData` directory appended with your app's name
|
||||
* `cache`: Per-user application cache directory, by default it is pointed to:
|
||||
* `%APPDATA%` on Window, which doesn't has a universal place for cache
|
||||
* `$XDG_CACHE_HOME` or `~/.cache` on Linux
|
||||
* `~/Library/Caches` on OS X
|
||||
* `userCache`: The directory for placing your app's caches, by default it is the
|
||||
`cache` directory appended with your app's name
|
||||
* `temp`: Temporary directory
|
||||
* `userDesktop`: The current user's Desktop directory
|
||||
* `exe`: The current executable file
|
||||
* `module`: The `libchromiumcontent` library
|
||||
|
||||
## app.setPath(name, path)
|
||||
|
||||
* `name` String
|
||||
* `path` String
|
||||
|
||||
Overrides the `path` to a special directory or file associated with `name`. if
|
||||
the path specifies a directory that does not exist, the directory will be
|
||||
created by this method. On failure an `Error` would throw.
|
||||
|
||||
You can only override paths of `name`s defined in `app.getPath`.
|
||||
|
||||
By default web pages' cookies and caches will be stored under `userData`
|
||||
directory, if you want to change this location, you have to override the
|
||||
`userData` path before the `ready` event of `app` module gets emitted.
|
||||
|
||||
## app.getVersion()
|
||||
|
||||
Returns the version of loaded application, if no version is found in
|
||||
application's `package.json`, the version of current bundle or executable would
|
||||
be returned.
|
||||
|
||||
## app.getName()
|
||||
|
||||
Returns current application's name, the name in `package.json` would be
|
||||
used.
|
||||
|
||||
Usually the `name` field of `package.json` is a short lowercased name, according
|
||||
to the spec of npm modules. So usually you should also specify a `productName`
|
||||
field, which is your application's full capitalized name, and it will be
|
||||
preferred over `name` by Electron.
|
||||
|
||||
## app.resolveProxy(url, callback)
|
||||
|
||||
* `url` URL
|
||||
* `callback` Function
|
||||
|
||||
Resolves the proxy information for `url`, the `callback` would be called with
|
||||
`callback(proxy)` when the request is done.
|
||||
|
||||
## app.addRecentDocument(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
Adds `path` to recent documents list.
|
||||
|
||||
This list is managed by the system, on Windows you can visit the list from task
|
||||
bar, and on Mac you can visit it from dock menu.
|
||||
|
||||
## app.clearRecentDocuments()
|
||||
|
||||
Clears the recent documents list.
|
||||
|
||||
## app.setUserTasks(tasks)
|
||||
|
||||
* `tasks` Array - Array of `Task` objects
|
||||
|
||||
Adds `tasks` to the [Tasks][tasks] category of JumpList on Windows.
|
||||
|
||||
The `tasks` is an array of `Task` objects in following format:
|
||||
|
||||
* `Task` Object
|
||||
* `program` String - Path of the program to execute, usually you should
|
||||
specify `process.execPath` which opens current program
|
||||
* `arguments` String - The arguments of command line when `program` is
|
||||
executed
|
||||
* `title` String - The string to be displayed in a JumpList
|
||||
* `description` String - Description of this task
|
||||
* `iconPath` String - The absolute path to an icon to be displayed in a
|
||||
JumpList, it can be arbitrary resource file that contains an icon, usually
|
||||
you can specify `process.execPath` to show the icon of the program
|
||||
* `iconIndex` Integer - The icon index in the icon file. If an icon file
|
||||
consists of two or more icons, set this value to identify the icon. If an
|
||||
icon file consists of one icon, this value is 0
|
||||
|
||||
**Note:** This API is only available on Windows.
|
||||
|
||||
## app.commandLine.appendSwitch(switch, [value])
|
||||
|
||||
Append a switch [with optional value] to Chromium's command line.
|
||||
|
||||
**Note:** This will not affect `process.argv`, and is mainly used by developers
|
||||
to control some low-level Chromium behaviors.
|
||||
|
||||
## app.commandLine.appendArgument(value)
|
||||
|
||||
Append an argument to Chromium's command line. The argument will quoted properly.
|
||||
|
||||
**Note:** This will not affect `process.argv`.
|
||||
|
||||
## app.dock.bounce([type])
|
||||
|
||||
* `type` String - Can be `critical` or `informational`, the default is
|
||||
`informational`
|
||||
|
||||
When `critical` is passed, the dock icon will bounce until either the
|
||||
application becomes active or the request is canceled.
|
||||
|
||||
When `informational` is passed, the dock icon will bounce for one second. The
|
||||
request, though, remains active until either the application becomes active or
|
||||
the request is canceled.
|
||||
|
||||
An ID representing the request would be returned.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.cancelBounce(id)
|
||||
|
||||
* `id` Integer
|
||||
|
||||
Cancel the bounce of `id`.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.setBadge(text)
|
||||
|
||||
* `text` String
|
||||
|
||||
Sets the string to be displayed in the dock’s badging area.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.getBadge()
|
||||
|
||||
Returns the badge string of the dock.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.hide()
|
||||
|
||||
Hides the dock icon.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.show()
|
||||
|
||||
Shows the dock icon.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
## app.dock.setMenu(menu)
|
||||
|
||||
* `menu` Menu
|
||||
|
||||
Sets the application [dock menu][dock-menu].
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
|
||||
[dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103
|
||||
[tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
|
||||
152
docs/api/app.md
152
docs/api/app.md
@@ -1,8 +1,8 @@
|
||||
# app
|
||||
|
||||
The `app` module is responsible for controlling the application's life time.
|
||||
The `app` module is responsible for controlling the application's lifecycle.
|
||||
|
||||
The example of quitting the whole application when the last window is closed:
|
||||
The following example shows how to quit the application when the last window is closed:
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
@@ -13,26 +13,26 @@ app.on('window-all-closed', function() {
|
||||
|
||||
## Event: will-finish-launching
|
||||
|
||||
Emitted when application has done basic startup. On Windows and Linux it is the
|
||||
same with `ready` event, on OS X this event represents the
|
||||
`applicationWillFinishLaunching` message of `NSApplication`, usually you would
|
||||
setup listeners to `open-file` and `open-url` events here, and start the crash
|
||||
reporter and auto updater.
|
||||
Emitted when the application has finished basic startup. On Windows and Linux,
|
||||
the `will-finish-launching` event is the same as the `ready` event; on OS X,
|
||||
this event represents the `applicationWillFinishLaunching` notification of `NSApplication`.
|
||||
You would usually set up listeners for the `open-file` and `open-url` events here,
|
||||
and start the crash reporter and auto updater.
|
||||
|
||||
Under most cases you should just do everything in `ready` event.
|
||||
In most cases, you should just do everything in the `ready` event handler.
|
||||
|
||||
## Event: ready
|
||||
|
||||
Emitted when Electron has done everything initialization.
|
||||
Emitted when Electron has finsished initialization.
|
||||
|
||||
## Event: window-all-closed
|
||||
|
||||
Emitted when all windows have been closed.
|
||||
|
||||
This event is only emitted when the application is not going to quit. If a
|
||||
This event is only emitted when the application is not going to quit. If the
|
||||
user pressed `Cmd + Q`, or the developer called `app.quit()`, Electron would
|
||||
first try to close all windows and then emit the `will-quit` event, and in
|
||||
this case the `window-all-closed` would not be emitted.
|
||||
first try to close all the windows and then emit the `will-quit` event, and in
|
||||
this case the `window-all-closed` event would not be emitted.
|
||||
|
||||
## Event: before-quit
|
||||
|
||||
@@ -50,23 +50,23 @@ Emitted when all windows have been closed and the application will quit.
|
||||
Calling `event.preventDefault()` will prevent the default behaviour, which is
|
||||
terminating the application.
|
||||
|
||||
See description of `window-all-closed` for the differences between `will-quit`
|
||||
and it.
|
||||
See the description of the `window-all-closed` event for the differences between the `will-quit`
|
||||
and `window-all-closed` events.
|
||||
|
||||
## Event: quit
|
||||
|
||||
Emitted when application is quitting.
|
||||
Emitted when the application is quitting.
|
||||
|
||||
## Event: open-file
|
||||
|
||||
* `event` Event
|
||||
* `path` String
|
||||
|
||||
Emitted when user wants to open a file with the application, it usually happens
|
||||
when the application is already opened and then OS wants to reuse the
|
||||
application to open file. But it is also emitted when a file is dropped onto the
|
||||
dock and the application is not yet running. Make sure to listen to open-file
|
||||
very early in your application startup to handle this case (even before the
|
||||
Emitted when the user wants to open a file with the application. The `open-file` event
|
||||
is usually emitted when the application is already open and the OS wants to reuse the
|
||||
application to open the file. `open-file` is also emitted when a file is dropped onto the
|
||||
dock and the application is not yet running. Make sure to listen for the `open-file`
|
||||
event very early in your application startup to handle this case (even before the
|
||||
`ready` event is emitted).
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
@@ -76,16 +76,16 @@ You should call `event.preventDefault()` if you want to handle this event.
|
||||
* `event` Event
|
||||
* `url` String
|
||||
|
||||
Emitted when user wants to open a URL with the application, this URL scheme
|
||||
Emitted when the user wants to open a URL with the application. The URL scheme
|
||||
must be registered to be opened by your application.
|
||||
|
||||
You should call `event.preventDefault()` if you want to handle this event.
|
||||
|
||||
## Event: activate-with-no-open-windows
|
||||
|
||||
Emitted when the application is activated while there is no opened windows. It
|
||||
usually happens when user has closed all of application's windows and then
|
||||
click on the application's dock icon.
|
||||
Emitted when the application is activated while there are no open windows, which
|
||||
usually happens when the user has closed all of the application's windows and then
|
||||
clicks on the application's dock icon.
|
||||
|
||||
## Event: browser-window-blur
|
||||
|
||||
@@ -103,7 +103,7 @@ Emitted when a [browserWindow](browser-window.md) gets focused.
|
||||
|
||||
### Event: 'select-certificate'
|
||||
|
||||
Emitted when client certificate is requested.
|
||||
Emitted when a client certificate is requested.
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](browser-window.md#class-webcontents)
|
||||
@@ -120,43 +120,47 @@ app.on('select-certificate', function(event, host, url, list, callback) {
|
||||
})
|
||||
```
|
||||
|
||||
`url` corresponds to the navigation entry requesting the client certificate,
|
||||
`url` corresponds to the navigation entry requesting the client certificate.
|
||||
`callback` needs to be called with an entry filtered from the list.
|
||||
`event.preventDefault()` prevents from using the first certificate from
|
||||
the store.
|
||||
`event.preventDefault()` prevents the application from using the first certificate
|
||||
from the store.
|
||||
|
||||
### Event: 'gpu-process-crashed'
|
||||
|
||||
Emitted when the gpu process is crashed.
|
||||
Emitted when the gpu process crashes.
|
||||
|
||||
## app.quit()
|
||||
|
||||
Try to close all windows. The `before-quit` event will first be emitted. If all
|
||||
windows are successfully closed, the `will-quit` event will be emitted and by
|
||||
default the application would be terminated.
|
||||
default the application will terminate.
|
||||
|
||||
This method guarantees all `beforeunload` and `unload` handlers are correctly
|
||||
This method guarantees that all `beforeunload` and `unload` event handlers are correctly
|
||||
executed. It is possible that a window cancels the quitting by returning
|
||||
`false` in `beforeunload` handler.
|
||||
`false` in the `beforeunload` event handler.
|
||||
|
||||
## app.getAppPath()
|
||||
|
||||
Returns the current application directory.
|
||||
|
||||
## app.getPath(name)
|
||||
|
||||
* `name` String
|
||||
|
||||
Retrieves a path to a special directory or file associated with `name`. On
|
||||
failure an `Error` would throw.
|
||||
failure an `Error` is thrown.
|
||||
|
||||
You can request following paths by the names:
|
||||
You can request the following paths by the name:
|
||||
|
||||
* `home`: User's home directory
|
||||
* `appData`: Per-user application data directory, by default it is pointed to:
|
||||
* `appData`: Per-user application data directory, which by default points to:
|
||||
* `%APPDATA%` on Windows
|
||||
* `$XDG_CONFIG_HOME` or `~/.config` on Linux
|
||||
* `~/Library/Application Support` on OS X
|
||||
* `userData`: The directory for storing your app's configuration files, by
|
||||
* `userData`: The directory for storing your app's configuration files, which by
|
||||
default it is the `appData` directory appended with your app's name
|
||||
* `cache`: Per-user application cache directory, by default it is pointed to:
|
||||
* `%APPDATA%` on Window, which doesn't has a universal place for cache
|
||||
* `cache`: Per-user application cache directory, which by default points to:
|
||||
* `%APPDATA%` on Windows (which doesn't have a universal cache location)
|
||||
* `$XDG_CACHE_HOME` or `~/.cache` on Linux
|
||||
* `~/Library/Caches` on OS X
|
||||
* `userCache`: The directory for placing your app's caches, by default it is the
|
||||
@@ -171,30 +175,30 @@ You can request following paths by the names:
|
||||
* `name` String
|
||||
* `path` String
|
||||
|
||||
Overrides the `path` to a special directory or file associated with `name`. if
|
||||
Overrides the `path` to a special directory or file associated with `name`. If
|
||||
the path specifies a directory that does not exist, the directory will be
|
||||
created by this method. On failure an `Error` would throw.
|
||||
created by this method. On failure an `Error` is thrown.
|
||||
|
||||
You can only override paths of `name`s defined in `app.getPath`.
|
||||
|
||||
By default web pages' cookies and caches will be stored under `userData`
|
||||
directory, if you want to change this location, you have to override the
|
||||
`userData` path before the `ready` event of `app` module gets emitted.
|
||||
By default, web pages' cookies and caches will be stored under the `userData`
|
||||
directory. If you want to change this location, you have to override the
|
||||
`userData` path before the `ready` event of the `app` module is emitted.
|
||||
|
||||
## app.getVersion()
|
||||
|
||||
Returns the version of loaded application, if no version is found in
|
||||
application's `package.json`, the version of current bundle or executable would
|
||||
be returned.
|
||||
Returns the version of the loaded application. If no version is found in the
|
||||
application's `package.json` file, the version of the current bundle or executable is
|
||||
returned.
|
||||
|
||||
## app.getName()
|
||||
|
||||
Returns current application's name, the name in `package.json` would be
|
||||
used.
|
||||
Returns the current application's name, which is the name in the application's
|
||||
`package.json` file.
|
||||
|
||||
Usually the `name` field of `package.json` is a short lowercased name, according
|
||||
to the spec of npm modules. So usually you should also specify a `productName`
|
||||
field, which is your application's full capitalized name, and it will be
|
||||
to the npm modules spec. You should usually also specify a `productName`
|
||||
field, which is your application's full capitalized name, and which will be
|
||||
preferred over `name` by Electron.
|
||||
|
||||
## app.resolveProxy(url, callback)
|
||||
@@ -202,17 +206,17 @@ preferred over `name` by Electron.
|
||||
* `url` URL
|
||||
* `callback` Function
|
||||
|
||||
Resolves the proxy information for `url`, the `callback` would be called with
|
||||
`callback(proxy)` when the request is done.
|
||||
Resolves the proxy information for `url`. The `callback` will be called with
|
||||
`callback(proxy)` when the request is performed.
|
||||
|
||||
## app.addRecentDocument(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
Adds `path` to recent documents list.
|
||||
Adds `path` to the recent documents list.
|
||||
|
||||
This list is managed by the system, on Windows you can visit the list from task
|
||||
bar, and on Mac you can visit it from dock menu.
|
||||
This list is managed by the OS. On Windows you can visit the list from the task
|
||||
bar, and on OS X you can visit it from dock menu.
|
||||
|
||||
## app.clearRecentDocuments()
|
||||
|
||||
@@ -222,20 +226,20 @@ Clears the recent documents list.
|
||||
|
||||
* `tasks` Array - Array of `Task` objects
|
||||
|
||||
Adds `tasks` to the [Tasks][tasks] category of JumpList on Windows.
|
||||
Adds `tasks` to the [Tasks][tasks] category of the JumpList on Windows.
|
||||
|
||||
The `tasks` is an array of `Task` objects in following format:
|
||||
`tasks` is an array of `Task` objects in following format:
|
||||
|
||||
* `Task` Object
|
||||
* `program` String - Path of the program to execute, usually you should
|
||||
specify `process.execPath` which opens current program
|
||||
* `arguments` String - The arguments of command line when `program` is
|
||||
specify `process.execPath` which opens the current program
|
||||
* `arguments` String - The command line arguments when `program` is
|
||||
executed
|
||||
* `title` String - The string to be displayed in a JumpList
|
||||
* `description` String - Description of this task
|
||||
* `iconPath` String - The absolute path to an icon to be displayed in a
|
||||
JumpList, it can be arbitrary resource file that contains an icon, usually
|
||||
you can specify `process.execPath` to show the icon of the program
|
||||
JumpList, which can be an arbitrary resource file that contains an icon. You can
|
||||
usually specify `process.execPath` to show the icon of the program
|
||||
* `iconIndex` Integer - The icon index in the icon file. If an icon file
|
||||
consists of two or more icons, set this value to identify the icon. If an
|
||||
icon file consists of one icon, this value is 0
|
||||
@@ -251,25 +255,25 @@ to control some low-level Chromium behaviors.
|
||||
|
||||
## app.commandLine.appendArgument(value)
|
||||
|
||||
Append an argument to Chromium's command line. The argument will quoted properly.
|
||||
Append an argument to Chromium's command line. The argument will be quoted correctly.
|
||||
|
||||
**Note:** This will not affect `process.argv`.
|
||||
|
||||
## app.dock.bounce([type])
|
||||
|
||||
* `type` String - Can be `critical` or `informational`, the default is
|
||||
* `type` String - Can be `critical` or `informational`. The default is
|
||||
`informational`
|
||||
|
||||
When `critical` is passed, the dock icon will bounce until either the
|
||||
application becomes active or the request is canceled.
|
||||
|
||||
When `informational` is passed, the dock icon will bounce for one second. The
|
||||
request, though, remains active until either the application becomes active or
|
||||
When `informational` is passed, the dock icon will bounce for one second. However,
|
||||
the request remains active until either the application becomes active or
|
||||
the request is canceled.
|
||||
|
||||
An ID representing the request would be returned.
|
||||
An ID representing the request is returned.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.cancelBounce(id)
|
||||
|
||||
@@ -277,7 +281,7 @@ An ID representing the request would be returned.
|
||||
|
||||
Cancel the bounce of `id`.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.setBadge(text)
|
||||
|
||||
@@ -285,33 +289,33 @@ Cancel the bounce of `id`.
|
||||
|
||||
Sets the string to be displayed in the dock’s badging area.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.getBadge()
|
||||
|
||||
Returns the badge string of the dock.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.hide()
|
||||
|
||||
Hides the dock icon.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.show()
|
||||
|
||||
Shows the dock icon.
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
## app.dock.setMenu(menu)
|
||||
|
||||
* `menu` Menu
|
||||
|
||||
Sets the application [dock menu][dock-menu].
|
||||
Sets the application's [dock menu][dock-menu].
|
||||
|
||||
**Note:** This API is only available on Mac.
|
||||
**Note:** This API is only available on OS X.
|
||||
|
||||
[dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103
|
||||
[tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
|
||||
|
||||
143
docs/api/auto-updater-ko.md
Normal file
143
docs/api/auto-updater-ko.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# auto-updater
|
||||
|
||||
**This module has only been implemented for OS X.**
|
||||
|
||||
Check out [atom/grunt-electron-installer](https://github.com/atom/grunt-electron-installer)
|
||||
for building a Windows installer for your app.
|
||||
|
||||
The `auto-updater` module is a simple wrap around the
|
||||
[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework.
|
||||
|
||||
Squirrel.Mac requires that your `.app` folder is signed using the
|
||||
[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html)
|
||||
utility for updates to be installed.
|
||||
|
||||
## Squirrel
|
||||
|
||||
Squirrel is an OS X framework focused on making application updates **as safe
|
||||
and transparent as updates to a website**.
|
||||
|
||||
Instead of publishing a feed of versions from which your app must select,
|
||||
Squirrel updates to the version your server tells it to. This allows you to
|
||||
intelligently update your clients based on the request you give to Squirrel.
|
||||
|
||||
Your request can include authentication details, custom headers or a request
|
||||
body so that your server has the context it needs in order to supply the most
|
||||
suitable update.
|
||||
|
||||
The update JSON Squirrel requests should be dynamically generated based on
|
||||
criteria in the request, and whether an update is required. Squirrel relies
|
||||
on server side support for determining whether an update is required, see
|
||||
[Server Support](#server-support).
|
||||
|
||||
Squirrel's installer is also designed to be fault tolerant, and ensure that any
|
||||
updates installed are valid.
|
||||
|
||||
## Update Requests
|
||||
|
||||
Squirrel is indifferent to the request the client application provides for
|
||||
update checking. `Accept: application/json` is added to the request headers
|
||||
because Squirrel is responsible for parsing the response.
|
||||
|
||||
For the requirements imposed on the responses and the body format of an update
|
||||
response see [Server Support](#server-support).
|
||||
|
||||
Your update request must *at least* include a version identifier so that the
|
||||
server can determine whether an update for this specific version is required. It
|
||||
may also include other identifying criteria such as operating system version or
|
||||
username, to allow the server to deliver as fine grained an update as you
|
||||
would like.
|
||||
|
||||
How you include the version identifier or other criteria is specific to the
|
||||
server that you are requesting updates from. A common approach is to use query
|
||||
parameters, like this:
|
||||
|
||||
```javascript
|
||||
// On the main process
|
||||
var app = require('app');
|
||||
var autoUpdater = require('auto-updater');
|
||||
autoUpdater.setFeedUrl('http://mycompany.com/myapp/latest?version=' + app.getVersion());
|
||||
```
|
||||
|
||||
## Server Support
|
||||
|
||||
Your server should determine whether an update is required based on the
|
||||
[Update Request](#update-requests) your client issues.
|
||||
|
||||
If an update is required your server should respond with a status code of
|
||||
[200 OK](http://tools.ietf.org/html/rfc2616#section-10.2.1) and include the
|
||||
[update JSON](#update-json-format) in the body. Squirrel **will** download and
|
||||
install this update, even if the version of the update is the same as the
|
||||
currently running version. To save redundantly downloading the same version
|
||||
multiple times your server must not inform the client to update.
|
||||
|
||||
If no update is required your server must respond with a status code of
|
||||
[204 No Content](http://tools.ietf.org/html/rfc2616#section-10.2.5). Squirrel
|
||||
will check for an update again at the interval you specify.
|
||||
|
||||
## Update JSON Format
|
||||
|
||||
When an update is available, Squirrel expects the following schema in response
|
||||
to the update request provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "http://mycompany.com/myapp/releases/myrelease",
|
||||
"name": "My Release Name",
|
||||
"notes": "Theses are some release notes innit",
|
||||
"pub_date": "2013-09-18T12:29:53+01:00",
|
||||
}
|
||||
```
|
||||
|
||||
The only required key is "url", the others are optional.
|
||||
|
||||
Squirrel will request "url" with `Accept: application/zip` and only supports
|
||||
installing ZIP updates. If future update formats are supported their MIME type
|
||||
will be added to the `Accept` header so that your server can return the
|
||||
appropriate format.
|
||||
|
||||
`pub_date` if present must be formatted according to ISO 8601.
|
||||
|
||||
## Event: error
|
||||
|
||||
* `event` Event
|
||||
* `message` String
|
||||
|
||||
Emitted when there is an error updating.
|
||||
|
||||
## Event: checking-for-update
|
||||
|
||||
Emitted when checking for update has started.
|
||||
|
||||
## Event: update-available
|
||||
|
||||
Emitted when there is an available update, the update would be downloaded
|
||||
automatically.
|
||||
|
||||
## Event: update-not-available
|
||||
|
||||
Emitted when there is no available update.
|
||||
|
||||
## Event: update-downloaded
|
||||
|
||||
* `event` Event
|
||||
* `releaseNotes` String
|
||||
* `releaseName` String
|
||||
* `releaseDate` Date
|
||||
* `updateUrl` String
|
||||
* `quitAndUpdate` Function
|
||||
|
||||
Emitted when update has been downloaded, calling `quitAndUpdate()` would restart
|
||||
the application and install the update.
|
||||
|
||||
## autoUpdater.setFeedUrl(url)
|
||||
|
||||
* `url` String
|
||||
|
||||
Set the `url` and initialize the auto updater. The `url` could not be changed
|
||||
once it is set.
|
||||
|
||||
## autoUpdater.checkForUpdates()
|
||||
|
||||
Ask the server whether there is an update, you have to call `setFeedUrl` before
|
||||
using this API.
|
||||
@@ -85,7 +85,7 @@ to the update request provided:
|
||||
"url": "http://mycompany.com/myapp/releases/myrelease",
|
||||
"name": "My Release Name",
|
||||
"notes": "Theses are some release notes innit",
|
||||
"pub_date": "2013-09-18T12:29:53+01:00",
|
||||
"pub_date": "2013-09-18T12:29:53+01:00"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
1143
docs/api/browser-window-ko.md
Normal file
1143
docs/api/browser-window-ko.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -361,6 +361,19 @@ Sets whether the window should be in fullscreen mode.
|
||||
|
||||
Returns whether the window is in fullscreen mode.
|
||||
|
||||
### BrowserWindow.setAspectRatio(aspectRatio[, extraSize])
|
||||
|
||||
* `aspectRatio` The aspect ratio we want to maintain for some portion of the content view.
|
||||
* `rect` Object - The extra size to not be included in the aspect ratio to be maintained.
|
||||
* `width` Integer
|
||||
* `height` Integer
|
||||
|
||||
This will have a window maintain an aspect ratio. The extra size allows a developer to be able to have space, specifified in pixels, not included within the aspect ratio calculations. This API already takes into account the difference between a window's size and it's content size.
|
||||
|
||||
Consider a normal window with an HD video player and associated controls. Perhaps there are 15 pixels of controls on the left edge, 25 pixels of controls on the right edge and 50 pixels of controls below the player. In order to maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within the player itself we would call this function with arguments of 16/9 and [ 40, 50 ]. The second argument doesn't care where the extra width and height are within the content view — only that they exist. Just sum any extra width and height areas you have within the overall content view.
|
||||
|
||||
__Note__: This API is only implemented on OS X.
|
||||
|
||||
### BrowserWindow.setBounds(options)
|
||||
|
||||
* `options` Object
|
||||
@@ -760,7 +773,7 @@ Calling `event.preventDefault()` can prevent creating new windows.
|
||||
* `event` Event
|
||||
* `url` String
|
||||
|
||||
Emitted when user or the page wants to start an navigation, it can happen when
|
||||
Emitted when user or the page wants to start a navigation, it can happen when
|
||||
`window.location` object is changed or user clicks a link in the page.
|
||||
|
||||
This event will not emit when the navigation is started programmatically with APIs
|
||||
@@ -784,6 +797,10 @@ Emitted when a plugin process is crashed.
|
||||
|
||||
Emitted when the WebContents is destroyed.
|
||||
|
||||
### WebContents.session
|
||||
|
||||
Returns the `Session` object used by this WebContents.
|
||||
|
||||
### WebContents.loadUrl(url, [options])
|
||||
|
||||
* `url` URL
|
||||
@@ -871,6 +888,10 @@ Whether the renderer process has crashed.
|
||||
|
||||
Overrides the user agent for this page.
|
||||
|
||||
### WebContents.getUserAgent()
|
||||
|
||||
Returns a `String` represents the user agent for this page.
|
||||
|
||||
### WebContents.insertCSS(css)
|
||||
|
||||
* `css` String
|
||||
@@ -1055,11 +1076,13 @@ app.on('ready', function() {
|
||||
2. There is no way to send synchronous messages from the main process to a
|
||||
renderer process, because it would be very easy to cause dead locks.
|
||||
|
||||
## Class: WebContents.session.cookies
|
||||
## Class: Session
|
||||
|
||||
### Session.cookies
|
||||
|
||||
The `cookies` gives you ability to query and modify cookies, an example is:
|
||||
|
||||
```javascipt
|
||||
```javascript
|
||||
var BrowserWindow = require('browser-window');
|
||||
|
||||
var win = new BrowserWindow({ width: 800, height: 600 });
|
||||
@@ -1091,7 +1114,7 @@ win.webContents.on('did-finish-load', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### WebContents.session.cookies.get(details, callback)
|
||||
### Session.cookies.get(details, callback)
|
||||
|
||||
* `details` Object
|
||||
* `url` String - Retrieves cookies which are associated with `url`.
|
||||
@@ -1118,7 +1141,7 @@ win.webContents.on('did-finish-load', function() {
|
||||
the number of seconds since the UNIX epoch. Not provided for session cookies.
|
||||
|
||||
|
||||
### WebContents.session.cookies.set(details, callback)
|
||||
### Session.cookies.set(details, callback)
|
||||
|
||||
* `details` Object
|
||||
* `url` String - Retrieves cookies which are associated with `url`
|
||||
@@ -1134,10 +1157,66 @@ win.webContents.on('did-finish-load', function() {
|
||||
* `callback` Function - function(error)
|
||||
* `error` Error
|
||||
|
||||
### WebContents.session.cookies.remove(details, callback)
|
||||
### Session.cookies.remove(details, callback)
|
||||
|
||||
* `details` Object
|
||||
* `url` String - The URL associated with the cookie
|
||||
* `name` String - The name of cookie to remove
|
||||
* `callback` Function - function(error)
|
||||
* `error` Error
|
||||
|
||||
### Session.clearCache(callback)
|
||||
|
||||
* `callback` Function - Called when operation is done
|
||||
|
||||
Clears the session's HTTP cache.
|
||||
|
||||
### Session.clearStorageData([options, ]callback)
|
||||
|
||||
* `options` Object
|
||||
* `origin` String - Should follow `window.location.origin`'s representation
|
||||
`scheme://host:port`
|
||||
* `storages` Array - The types of storages to clear, can contain:
|
||||
`appcache`, `cookies`, `filesystem`, `indexdb`, `localstorage`,
|
||||
`shadercache`, `websql`, `serviceworkers`
|
||||
* `quotas` Array - The types of quotas to clear, can contain:
|
||||
`temporary`, `persistent`, `syncable`
|
||||
* `callback` Function - Called when operation is done
|
||||
|
||||
Clears the data of web storages.
|
||||
|
||||
### Session.setProxy(config, callback)
|
||||
|
||||
* `config` String
|
||||
* `callback` Function - Called when operation is done
|
||||
|
||||
Parses the `config` indicating which proxies to use for the session.
|
||||
|
||||
```
|
||||
config = scheme-proxies[";"<scheme-proxies>]
|
||||
scheme-proxies = [<url-scheme>"="]<proxy-uri-list>
|
||||
url-scheme = "http" | "https" | "ftp" | "socks"
|
||||
proxy-uri-list = <proxy-uri>[","<proxy-uri-list>]
|
||||
proxy-uri = [<proxy-scheme>"://"]<proxy-host>[":"<proxy-port>]
|
||||
|
||||
For example:
|
||||
"http=foopy:80;ftp=foopy2" -- use HTTP proxy "foopy:80" for http://
|
||||
URLs, and HTTP proxy "foopy2:80" for
|
||||
ftp:// URLs.
|
||||
"foopy:80" -- use HTTP proxy "foopy:80" for all URLs.
|
||||
"foopy:80,bar,direct://" -- use HTTP proxy "foopy:80" for all URLs,
|
||||
failing over to "bar" if "foopy:80" is
|
||||
unavailable, and after that using no
|
||||
proxy.
|
||||
"socks4://foopy" -- use SOCKS v4 proxy "foopy:1080" for all
|
||||
URLs.
|
||||
"http=foopy,socks5://bar.com -- use HTTP proxy "foopy" for http URLs,
|
||||
and fail over to the SOCKS5 proxy
|
||||
"bar.com" if "foopy" is unavailable.
|
||||
"http=foopy,direct:// -- use HTTP proxy "foopy" for http URLs,
|
||||
and use no proxy if "foopy" is
|
||||
unavailable.
|
||||
"http=foopy;socks=foopy2 -- use HTTP proxy "foopy" for http URLs,
|
||||
and use socks4://foopy2 for all other
|
||||
URLs.
|
||||
```
|
||||
|
||||
109
docs/api/chrome-command-line-switches-ko.md
Normal file
109
docs/api/chrome-command-line-switches-ko.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 크롬 Command-Line 스위치 지원
|
||||
|
||||
The following command lines switches in Chrome browser are also supported in
|
||||
Electron, you can use [app.commandLine.appendSwitch][append-switch] to append
|
||||
them in your app's main script before the [ready][ready] event of [app][app]
|
||||
module is emitted:
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315');
|
||||
app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1');
|
||||
|
||||
app.on('ready', function() {
|
||||
// Your code here
|
||||
});
|
||||
```
|
||||
|
||||
## --client-certificate=`path`
|
||||
|
||||
Sets `path` of client certificate file.
|
||||
|
||||
## --ignore-connections-limit=`domains`
|
||||
|
||||
Ignore the connections limit for `domains` list seperated by `,`.
|
||||
|
||||
## --disable-http-cache
|
||||
|
||||
Disables the disk cache for HTTP requests.
|
||||
|
||||
## --remote-debugging-port=`port`
|
||||
|
||||
Enables remote debug over HTTP on the specified `port`.
|
||||
|
||||
## --proxy-server=`address:port`
|
||||
|
||||
Uses a specified proxy server, overrides system settings. This switch only
|
||||
affects HTTP and HTTPS requests.
|
||||
|
||||
## --no-proxy-server
|
||||
|
||||
Don't use a proxy server, always make direct connections. Overrides any other
|
||||
proxy server flags that are passed.
|
||||
|
||||
## --host-rules=`rules`
|
||||
|
||||
Comma-separated list of `rules` that control how hostnames are mapped.
|
||||
|
||||
For example:
|
||||
|
||||
* `MAP * 127.0.0.1` Forces all hostnames to be mapped to 127.0.0.1
|
||||
* `MAP *.google.com proxy` Forces all google.com subdomains to be resolved to
|
||||
"proxy".
|
||||
* `MAP test.com [::1]:77` Forces "test.com" to resolve to IPv6 loopback. Will
|
||||
also force the port of the resulting socket address to be 77.
|
||||
* `MAP * baz, EXCLUDE www.google.com` Remaps everything to "baz", except for
|
||||
"www.google.com".
|
||||
|
||||
These mappings apply to the endpoint host in a net request (the TCP connect
|
||||
and host resolver in a direct connection, and the `CONNECT` in an http proxy
|
||||
connection, and the endpoint host in a `SOCKS` proxy connection).
|
||||
|
||||
## --host-resolver-rules=`rules`
|
||||
|
||||
Like `--host-rules` but these `rules` only apply to the host resolver.
|
||||
|
||||
[app]: app-ko.md
|
||||
[append-switch]: app-ko.md#appcommandlineappendswitchswitch-value
|
||||
[ready]: app-ko.md#event-ready
|
||||
|
||||
## --ignore-certificate-errors
|
||||
|
||||
Ignores certificate related errors.
|
||||
|
||||
## --ppapi-flash-path=`path`
|
||||
|
||||
Sets `path` of pepper flash plugin.
|
||||
|
||||
## --ppapi-flash-version=`version`
|
||||
|
||||
Sets `version` of pepper flash plugin.
|
||||
|
||||
## --log-net-log=`path`
|
||||
|
||||
Enables saving net log events and writes them to `path`.
|
||||
|
||||
## --v=`log_level`
|
||||
|
||||
Gives the default maximal active V-logging level; 0 is the default. Normally
|
||||
positive values are used for V-logging levels.
|
||||
|
||||
Passing `--v=-1` will disable logging.
|
||||
|
||||
## --vmodule=`pattern`
|
||||
|
||||
Gives the per-module maximal V-logging levels to override the value given by
|
||||
`--v`. E.g. `my_module=2,foo*=3` would change the logging level for all code in
|
||||
source files `my_module.*` and `foo*.*`.
|
||||
|
||||
Any pattern containing a forward or backward slash will be tested against the
|
||||
whole pathname and not just the module. E.g. `*/foo/bar/*=2` would change the
|
||||
logging level for all code in source files under a `foo/bar` directory.
|
||||
|
||||
To disable all chromium related logs and only enable your application logs you
|
||||
can do:
|
||||
|
||||
```javascript
|
||||
app.commandLine.appendSwitch('v', -1);
|
||||
app.commandLine.appendSwitch('vmodule', 'console=0');
|
||||
```
|
||||
@@ -36,6 +36,10 @@ Enables remote debug over HTTP on the specified `port`.
|
||||
Uses a specified proxy server, overrides system settings. This switch only
|
||||
affects HTTP and HTTPS requests.
|
||||
|
||||
## --proxy-pac-url=`url`
|
||||
|
||||
Uses the PAC script at the specified `url`.
|
||||
|
||||
## --no-proxy-server
|
||||
|
||||
Don't use a proxy server, always make direct connections. Overrides any other
|
||||
|
||||
88
docs/api/clipboard-ko.md
Normal file
88
docs/api/clipboard-ko.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# clipboard
|
||||
|
||||
`clipboard`는 복사/붙여넣기 작업을 수행하는 방법을 제공합니다. 다음 예제는 클립보드에 문자열을 씁니다:
|
||||
|
||||
```javascript
|
||||
var clipboard = require('clipboard');
|
||||
clipboard.writeText('Example String');
|
||||
```
|
||||
|
||||
X Window 시스템에선 selection 클립보드도 존재합니다. 이를 사용하려면 인자 뒤에 `selection` 문자열을 같이 지정해주어야 합니다:
|
||||
|
||||
```javascript
|
||||
var clipboard = require('clipboard');
|
||||
clipboard.writeText('Example String', 'selection');
|
||||
console.log(clipboard.readText('selection'));
|
||||
```
|
||||
|
||||
## clipboard.readText([type])
|
||||
|
||||
* `type` String
|
||||
|
||||
클립보드 컨텐츠를 `plain text`로 반환합니다.
|
||||
|
||||
## clipboard.writeText(text[, type])
|
||||
|
||||
* `text` String
|
||||
* `type` String
|
||||
|
||||
클립보드에 `plain text`로 문자열을 씁니다.
|
||||
|
||||
## clipboard.readHtml([type])
|
||||
|
||||
* `type` String
|
||||
|
||||
클립보드 컨텐츠를 `markup`으로 반환합니다.
|
||||
|
||||
## clipboard.writeHtml(markup[, type])
|
||||
|
||||
* `markup` String
|
||||
* `type` String
|
||||
|
||||
클립보드에 `markup`으로 씁니다.
|
||||
|
||||
## clipboard.readImage([type])
|
||||
|
||||
* `type` String
|
||||
|
||||
클립보드로부터 [NativeImage](native-image-ko.md)로 이미지를 읽어들입니다.
|
||||
|
||||
## clipboard.writeImage(image[, type])
|
||||
|
||||
* `image` [NativeImage](native-image-ko.md)
|
||||
* `type` String
|
||||
|
||||
클립보드에 `image`를 씁니다.
|
||||
|
||||
## clipboard.clear([type])
|
||||
|
||||
* `type` String
|
||||
|
||||
클립보드에 저장된 모든 컨텐츠를 삭제합니다.
|
||||
|
||||
## clipboard.availableFormats([type])
|
||||
|
||||
클립보드의 `type`에 해당하는 지원하는 `format`을 문자열로 반환합니다.
|
||||
|
||||
## clipboard.has(data[, type])
|
||||
|
||||
* `data` String
|
||||
* `type` String
|
||||
|
||||
클립보드가 지정한 `data`의 형식을 지원하는지 확인합니다.
|
||||
|
||||
```javascript
|
||||
var clipboard = require('clipboard');
|
||||
console.log(clipboard.has('<p>selection</p>'));
|
||||
```
|
||||
|
||||
**알림:** 이 API는 실험적인 기능이며 차후 최신버전에서 제외될 수 있습니다.
|
||||
|
||||
## clipboard.read(data[, type])
|
||||
|
||||
* `data` String
|
||||
* `type` String
|
||||
|
||||
클립보드로부터 `data`를 읽어들입니다.
|
||||
|
||||
**알림:** 이 API는 실험적인 기능이며 차후 최신버전에서 제외될 수 있습니다.
|
||||
@@ -88,3 +88,17 @@ console.log(clipboard.has('<p>selection</p>'));
|
||||
Reads the `data` in clipboard.
|
||||
|
||||
**Note:** This API is experimental and could be removed in future.
|
||||
|
||||
## clipboard.write(data[, type])
|
||||
|
||||
* `data` Object
|
||||
* `text` String
|
||||
* `html` String
|
||||
* `image` [NativeImage](native-image.md)
|
||||
* `type` String
|
||||
|
||||
```javascript
|
||||
var clipboard = require('clipboard');
|
||||
clipboard.write({text: 'test', html: "<b>test</b>"});
|
||||
```
|
||||
Writes the `data` iinto clipboard.
|
||||
|
||||
137
docs/api/content-tracing-ko.md
Normal file
137
docs/api/content-tracing-ko.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# content-tracing
|
||||
|
||||
The `content-trace` module is used to collect tracing data generated by the
|
||||
underlying Chromium content module. This module does not include a web interface
|
||||
so you need to open `chrome://tracing/` in a Chrome browser and load the generated
|
||||
file to view the result.
|
||||
|
||||
```javascript
|
||||
var tracing = require('content-tracing');
|
||||
tracing.startRecording('*', tracing.DEFAULT_OPTIONS, function() {
|
||||
console.log('Tracing started');
|
||||
|
||||
setTimeout(function() {
|
||||
tracing.stopRecording('', function(path) {
|
||||
console.log('Tracing data recorded to ' + path);
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
```
|
||||
|
||||
## tracing.getCategories(callback)
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Get a set of category groups. The category groups can change as new code paths
|
||||
are reached.
|
||||
|
||||
Once all child processes have acked to the `getCategories` request, `callback`
|
||||
is invoked with an array of category groups.
|
||||
|
||||
## tracing.startRecording(categoryFilter, options, callback)
|
||||
|
||||
* `categoryFilter` String
|
||||
* `options` Integer
|
||||
* `callback` Function
|
||||
|
||||
Start recording on all processes.
|
||||
|
||||
Recording begins immediately locally, and asynchronously on child processes
|
||||
as soon as they receive the EnableRecording request. Once all child processes
|
||||
have acked to the `startRecording` request, `callback` will be called back.
|
||||
|
||||
`categoryFilter` is a filter to control what category groups should be
|
||||
traced. A filter can have an optional `-` prefix to exclude category groups
|
||||
that contain a matching category. Having both included and excluded
|
||||
category patterns in the same list is not supported.
|
||||
|
||||
Examples:
|
||||
|
||||
* `test_MyTest*`,
|
||||
* `test_MyTest*,test_OtherStuff`,
|
||||
* `"-excluded_category1,-excluded_category2`
|
||||
|
||||
`options` controls what kind of tracing is enabled, it could be a OR-ed
|
||||
combination of `tracing.DEFAULT_OPTIONS`, `tracing.ENABLE_SYSTRACE`,
|
||||
`tracing.ENABLE_SAMPLING` and `tracing.RECORD_CONTINUOUSLY`.
|
||||
|
||||
## tracing.stopRecording(resultFilePath, callback)
|
||||
|
||||
* `resultFilePath` String
|
||||
* `callback` Function
|
||||
|
||||
Stop recording on all processes.
|
||||
|
||||
Child processes typically are caching trace data and only rarely flush and send
|
||||
trace data back to the main process. That is because it may be an expensive
|
||||
operation to send the trace data over IPC, and we would like to avoid much
|
||||
runtime overhead of tracing. So, to end tracing, we must asynchronously ask all
|
||||
child processes to flush any pending trace data.
|
||||
|
||||
Once all child processes have acked to the `stopRecording` request, `callback`
|
||||
will be called back with a file that contains the traced data.
|
||||
|
||||
Trace data will be written into `resultFilePath` if it is not empty, or into a
|
||||
temporary file. The actual file path will be passed to `callback` if it's not
|
||||
null.
|
||||
|
||||
## tracing.startMonitoring(categoryFilter, options, callback)
|
||||
|
||||
* `categoryFilter` String
|
||||
* `options` Integer
|
||||
* `callback` Function
|
||||
|
||||
Start monitoring on all processes.
|
||||
|
||||
Monitoring begins immediately locally, and asynchronously on child processes as
|
||||
soon as they receive the `startMonitoring` request.
|
||||
|
||||
Once all child processes have acked to the `startMonitoring` request,
|
||||
`callback` will be called back.
|
||||
|
||||
## tracing.stopMonitoring(callback);
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Stop monitoring on all processes.
|
||||
|
||||
Once all child processes have acked to the `stopMonitoring` request, `callback`
|
||||
is called back.
|
||||
|
||||
## tracing.captureMonitoringSnapshot(resultFilePath, callback)
|
||||
|
||||
* `resultFilePath` String
|
||||
* `callback` Function
|
||||
|
||||
Get the current monitoring traced data.
|
||||
|
||||
Child processes typically are caching trace data and only rarely flush and send
|
||||
trace data back to the main process. That is because it may be an expensive
|
||||
operation to send the trace data over IPC, and we would like to avoid unneeded
|
||||
runtime overhead of tracing. So, to end tracing, we must asynchronously ask all
|
||||
child processes to flush any pending trace data.
|
||||
|
||||
Once all child processes have acked to the `captureMonitoringSnapshot` request,
|
||||
the `callback` will be invoked with a file that contains the traced data.
|
||||
|
||||
|
||||
## tracing.getTraceBufferUsage(callback)
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Get the maximum across processes of trace buffer percent full state. When the
|
||||
TraceBufferUsage value is determined, the `callback` is called.
|
||||
|
||||
## tracing.setWatchEvent(categoryName, eventName, callback)
|
||||
|
||||
* `categoryName` String
|
||||
* `eventName` String
|
||||
* `callback` Function
|
||||
|
||||
`callback` will will be called every time the given event occurs on any
|
||||
process.
|
||||
|
||||
## tracing.cancelWatchEvent()
|
||||
|
||||
Cancel the watch event. If tracing is enabled, this may race with the watch
|
||||
event callback.
|
||||
61
docs/api/crash-reporter-ko.md
Normal file
61
docs/api/crash-reporter-ko.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# crash-reporter
|
||||
|
||||
An example of automatically submitting crash reporters to remote server:
|
||||
|
||||
```javascript
|
||||
crashReporter = require('crash-reporter');
|
||||
crashReporter.start({
|
||||
productName: 'YourName',
|
||||
companyName: 'YourCompany',
|
||||
submitUrl: 'https://your-domain.com/url-to-submit',
|
||||
autoSubmit: true
|
||||
});
|
||||
```
|
||||
|
||||
## crashReporter.start(options)
|
||||
|
||||
* `options` Object
|
||||
* `productName` String, default: Electron
|
||||
* `companyName` String, default: GitHub, Inc
|
||||
* `submitUrl` String, default: http://54.249.141.255:1127/post
|
||||
* URL that crash reports would be sent to as POST
|
||||
* `autoSubmit` Boolean, default: true
|
||||
* Send the crash report without user interaction
|
||||
* `ignoreSystemCrashHandler` Boolean, default: false
|
||||
* `extra` Object
|
||||
* An object you can define which content will be send along with the report.
|
||||
* Only string properties are send correctly.
|
||||
* Nested objects are not supported.
|
||||
|
||||
Developers are required to call the API before using other crashReporter APIs.
|
||||
|
||||
|
||||
**Note:** On OS X, electron uses a new `crashpad` client, which is different
|
||||
with the `breakpad` on Windows and Linux. To enable crash collection feature,
|
||||
you are required to call `crashReporter.start` API to initiliaze `crashpad` in
|
||||
main process, even you only collect crash report in renderer process.
|
||||
|
||||
## crashReporter.getLastCrashReport()
|
||||
|
||||
Returns the date and ID of last crash report, when there was no crash report
|
||||
sent or the crash reporter is not started, `null` will be returned.
|
||||
|
||||
## crashReporter.getUploadedReports()
|
||||
|
||||
Returns all uploaded crash reports, each report contains date and uploaded ID.
|
||||
|
||||
# crash-reporter payload
|
||||
|
||||
The crash reporter will send the following data to the `submitUrl` as `POST`:
|
||||
|
||||
* `rept` String - e.g. 'electron-crash-service'
|
||||
* `ver` String - The version of Electron
|
||||
* `platform` String - e.g. 'win32'
|
||||
* `process_type` String - e.g. 'renderer'
|
||||
* `ptime` Number
|
||||
* `_version` String - The version in `package.json`
|
||||
* `_productName` String - The product name in the crashReporter `options` object
|
||||
* `prod` String - Name of the underlying product. In this case Electron
|
||||
* `_companyName` String - The company name in the crashReporter `options` object
|
||||
* `upload_file_minidump` File - The crashreport as file
|
||||
* All level one properties of the `extra` object in the crashReporter `options` object
|
||||
@@ -1,6 +1,6 @@
|
||||
# crash-reporter
|
||||
|
||||
An example of automatically submitting crash reporters to remote server:
|
||||
An example of automatically submitting crash reporters to a remote server:
|
||||
|
||||
```javascript
|
||||
crashReporter = require('crash-reporter');
|
||||
@@ -24,20 +24,20 @@ crashReporter.start({
|
||||
* `ignoreSystemCrashHandler` Boolean, default: false
|
||||
* `extra` Object
|
||||
* An object you can define which content will be send along with the report.
|
||||
* Only string properties are send correctly.
|
||||
* Only string properties are sent correctly.
|
||||
* Nested objects are not supported.
|
||||
|
||||
Developers are required to call the API before using other crashReporter APIs.
|
||||
Developers are required to call this method before using other crashReporter APIs.
|
||||
|
||||
|
||||
**Note:** On OS X, electron uses a new `crashpad` client, which is different
|
||||
with the `breakpad` on Windows and Linux. To enable crash collection feature,
|
||||
you are required to call `crashReporter.start` API to initiliaze `crashpad` in
|
||||
main process, even you only collect crash report in renderer process.
|
||||
you are required to call `crashReporter.start` API to initialize `crashpad` in
|
||||
main process and in each renderer process that you wish to collect crash reports.
|
||||
|
||||
## crashReporter.getLastCrashReport()
|
||||
|
||||
Returns the date and ID of last crash report, when there was no crash report
|
||||
Returns the date and ID of the last crash report, when there was no crash report
|
||||
sent or the crash reporter is not started, `null` will be returned.
|
||||
|
||||
## crashReporter.getUploadedReports()
|
||||
|
||||
92
docs/api/dialog-ko.md
Normal file
92
docs/api/dialog-ko.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# dialog
|
||||
|
||||
The `dialog` module provides APIs to show native system dialogs, so web
|
||||
applications can deliver the same user experience as native applications.
|
||||
|
||||
An example of showing a dialog to select multiple files and directories:
|
||||
|
||||
```javascript
|
||||
var win = ...; // window in which to show the dialog
|
||||
var dialog = require('dialog');
|
||||
console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', 'multiSelections' ]}));
|
||||
```
|
||||
|
||||
**Note for OS X**: If you want to present dialogs as sheets, the only thing you have to do is provide a `BrowserWindow` reference in the `browserWindow` parameter.
|
||||
|
||||
## dialog.showOpenDialog([browserWindow], [options], [callback])
|
||||
|
||||
* `browserWindow` BrowserWindow
|
||||
* `options` Object
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
* `filters` Array
|
||||
* `properties` Array - Contains which features the dialog should use, can
|
||||
contain `openFile`, `openDirectory`, `multiSelections` and
|
||||
`createDirectory`
|
||||
* `callback` Function
|
||||
|
||||
On success, returns an array of file paths chosen by the user, otherwise
|
||||
returns `undefined`.
|
||||
|
||||
The `filters` specifies an array of file types that can be displayed or
|
||||
selected, an example is:
|
||||
|
||||
```javascript
|
||||
{
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
|
||||
{ name: 'Custom File Type', extensions: ['as'] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If a `callback` is passed, the API call would be asynchronous and the result
|
||||
would be passed via `callback(filenames)`
|
||||
|
||||
**Note:** On Windows and Linux, an open dialog can not be both a file selector
|
||||
and a directory selector, so if you set `properties` to
|
||||
`['openFile', 'openDirectory']` on these platforms, a directory selector will be shown.
|
||||
|
||||
## dialog.showSaveDialog([browserWindow], [options], [callback])
|
||||
|
||||
* `browserWindow` BrowserWindow
|
||||
* `options` Object
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
* `filters` Array
|
||||
* `callback` Function
|
||||
|
||||
On success, returns the path of the file chosen by the user, otherwise returns
|
||||
`undefined`.
|
||||
|
||||
The `filters` specifies an array of file types that can be displayed, see
|
||||
`dialog.showOpenDialog` for an example.
|
||||
|
||||
If a `callback` is passed, the API call will be asynchronous and the result
|
||||
will be passed via `callback(filename)`
|
||||
|
||||
## dialog.showMessageBox([browserWindow], options, [callback])
|
||||
|
||||
* `browserWindow` BrowserWindow
|
||||
* `options` Object
|
||||
* `type` String - Can be `"none"`, `"info"` or `"warning"`
|
||||
* `buttons` Array - Array of texts for buttons
|
||||
* `title` String - Title of the message box, some platforms will not show it
|
||||
* `message` String - Content of the message box
|
||||
* `detail` String - Extra information of the message
|
||||
* `icon` [NativeImage](native-image-ko.md)
|
||||
* `callback` Function
|
||||
|
||||
Shows a message box, it will block until the message box is closed. It returns
|
||||
the index of the clicked button.
|
||||
|
||||
If a `callback` is passed, the API call will be asynchronous and the result
|
||||
will be passed via `callback(response)`
|
||||
|
||||
## dialog.showErrorBox(title, content)
|
||||
|
||||
Runs a modal dialog that shows an error message.
|
||||
|
||||
This API can be called safely before the `ready` event of `app` module emits, it
|
||||
is usually used to report errors in early stage of startup.
|
||||
@@ -70,12 +70,22 @@ will be passed via `callback(filename)`
|
||||
|
||||
* `browserWindow` BrowserWindow
|
||||
* `options` Object
|
||||
* `type` String - Can be `"none"`, `"info"` or `"warning"`
|
||||
* `type` String - Can be `"none"`, `"info"`, `"error"`, `"question"` or `"warning"`. On Windows, "question" displays the same icon as "info", unless if you set an icon using the "icon" option
|
||||
* `buttons` Array - Array of texts for buttons
|
||||
* `title` String - Title of the message box, some platforms will not show it
|
||||
* `message` String - Content of the message box
|
||||
* `detail` String - Extra information of the message
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `cancelId` Integer - The value will be returned when user cancels the dialog
|
||||
instead of clicking the buttons of the dialog. By default it is the index
|
||||
of the buttons that have "cancel" or "no" as label, or 0 if there is no such
|
||||
buttons. On OS X and Windows the index of "Cancel" button will always be
|
||||
used as `cancelId`, not matter whether it is already specified
|
||||
* `noLink` Boolean - On Windows Electron would try to figure out which ones of
|
||||
the `buttons` are common buttons (like "Cancel" or "Yes"), and show the
|
||||
others as command links in the dialog, this can make the dialog appear in
|
||||
the style of modern Windows apps. If you don't like this behavior, you can
|
||||
specify `noLink` to `true`
|
||||
* `callback` Function
|
||||
|
||||
Shows a message box, it will block until the message box is closed. It returns
|
||||
|
||||
28
docs/api/file-object-ko.md
Normal file
28
docs/api/file-object-ko.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# `File` 객체
|
||||
|
||||
DOM의 File 인터페이스는 네이티브 파일을 추상화 합니다. 유저가 직접적으로 HTML5 File API를 사용하여 작업할 때 파일의 경로를
|
||||
알 수 있도록 Electron은 파일시스템의 실제 파일 경로를 담은 `path` 속성을 File 인터페이스에 추가하였습니다.
|
||||
|
||||
다음 예제는 drag n drop한 파일의 실제 경로를 가져옵니다:
|
||||
|
||||
```html
|
||||
<div id="holder">
|
||||
Drag your file here
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var holder = document.getElementById('holder');
|
||||
holder.ondragover = function () {
|
||||
return false;
|
||||
};
|
||||
holder.ondragleave = holder.ondragend = function () {
|
||||
return false;
|
||||
};
|
||||
holder.ondrop = function (e) {
|
||||
e.preventDefault();
|
||||
var file = e.dataTransfer.files[0];
|
||||
console.log('File you dragged here is', file.path);
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
```
|
||||
89
docs/api/frameless-window-ko.md
Normal file
89
docs/api/frameless-window-ko.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Frameless window
|
||||
|
||||
A frameless window is a window that has no chrome.
|
||||
|
||||
## Create a frameless window
|
||||
|
||||
To create a frameless window, you only need to specify `frame` to `false` in
|
||||
[BrowserWindow](browser-window-ko.md)'s `options`:
|
||||
|
||||
|
||||
```javascript
|
||||
var BrowserWindow = require('browser-window');
|
||||
var win = new BrowserWindow({ width: 800, height: 600, frame: false });
|
||||
```
|
||||
|
||||
## Transparent window
|
||||
|
||||
By setting the `transparent` option to `true`, you can also make the frameless
|
||||
window transparent:
|
||||
|
||||
```javascript
|
||||
var win = new BrowserWindow({ transparent: true, frame: false });
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
* You can not click through the transparent area, we are going to introduce an
|
||||
API to set window shape to solve this, but currently blocked at an
|
||||
[upstream bug](https://code.google.com/p/chromium/issues/detail?id=387234).
|
||||
* Transparent window is not resizable, setting `resizable` to `true` may make
|
||||
transparent window stop working on some platforms.
|
||||
* The `blur` filter only applies to the web page, so there is no way to apply
|
||||
blur effect to the content below the window.
|
||||
* On Windows transparent window will not work when DWM is disabled.
|
||||
* On Linux users have to put `--enable-transparent-visuals --disable-gpu` in
|
||||
command line to disable GPU and allow ARGB to make transparent window, this is
|
||||
caused by an upstream bug that [alpha channel doesn't work on some NVidia
|
||||
drivers](https://code.google.com/p/chromium/issues/detail?id=369209) on Linux.
|
||||
* On Mac the native window shadow will not show for transparent window.
|
||||
|
||||
## Draggable region
|
||||
|
||||
By default, the frameless window is non-draggable. Apps need to specify
|
||||
`-webkit-app-region: drag` in CSS to tell Electron which regions are draggable
|
||||
(like the OS's standard titlebar), and apps can also use
|
||||
`-webkit-app-region: no-drag` to exclude the non-draggable area from the
|
||||
draggable region. Note that only rectangular shape is currently supported.
|
||||
|
||||
To make the whole window draggable, you can add `-webkit-app-region: drag` as
|
||||
`body`'s style:
|
||||
|
||||
```html
|
||||
<body style="-webkit-app-region: drag">
|
||||
</body>
|
||||
```
|
||||
|
||||
And note that if you have made the whole window draggable, you must also mark
|
||||
buttons as non-draggable, otherwise it would be impossible for users to click on
|
||||
them:
|
||||
|
||||
```css
|
||||
button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
```
|
||||
|
||||
If you're only using a custom titlebar, you also need to make buttons in
|
||||
titlebar non-draggable.
|
||||
|
||||
## Text selection
|
||||
|
||||
One thing on frameless window is that the dragging behaviour may conflict with
|
||||
selecting text, for example, when you drag the titlebar, you may accidentally
|
||||
select the text on titlebar. To prevent this, you need to disable text
|
||||
selection on dragging area like this:
|
||||
|
||||
```css
|
||||
.titlebar {
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
```
|
||||
|
||||
## Context menu
|
||||
|
||||
On some platforms, the draggable area would be treated as non-client frame, so
|
||||
when you right click on it a system menu would be popuped. To make context menu
|
||||
behave correctly on all platforms, you should never custom context menu on
|
||||
draggable areas.
|
||||
47
docs/api/global-shortcut-ko.md
Normal file
47
docs/api/global-shortcut-ko.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# global-shortcut
|
||||
|
||||
`global-shortcut` 모듈은 사용자가 다양한 단축키 작업을 정의 할 수 있도록 운영체제의 전역 키보드 단축키를 설정 등록/해제 하는 방법을 제공합니다.
|
||||
참고로 설정된 단축키는 어플리케이션이 백그라운드로 작동(창이 포커스 되지 않음) 할 때도 여전히 계속 작동합니다.
|
||||
|
||||
```javascript
|
||||
var globalShortcut = require('global-shortcut');
|
||||
|
||||
// 'ctrl+x' 단축키를 리스너에 등록합니다.
|
||||
var ret = globalShortcut.register('ctrl+x', function() { console.log('ctrl+x is pressed'); })
|
||||
|
||||
if (!ret) {
|
||||
console.log('registration failed');
|
||||
}
|
||||
|
||||
// 단축키가 등록되었는지 확인합니다.
|
||||
console.log(globalShortcut.isRegistered('ctrl+x'));
|
||||
|
||||
// 단축키의 등록을 해제합니다.
|
||||
globalShortcut.unregister('ctrl+x');
|
||||
|
||||
// 모든 단축키의 등록을 해제합니다.
|
||||
globalShortcut.unregisterAll();
|
||||
```
|
||||
|
||||
## globalShortcut.register(accelerator, callback)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `callback` Function
|
||||
|
||||
`accelerator`로 표현된 전역 단축키를 등록합니다. 유저로부터 등록된 단축키가 눌렸을 경우 `callback` 함수가 호출됩니다.
|
||||
|
||||
## globalShortcut.isRegistered(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
|
||||
지정된 `accelerator` 단축키가 등록되었는지 여부를 확인합니다. 반환값은 boolean(true, false) 입니다.
|
||||
|
||||
## globalShortcut.unregister(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
|
||||
`키코드`에 해당하는 전역 단축키를 등록 해제합니다.
|
||||
|
||||
## globalShortcut.unregisterAll()
|
||||
|
||||
모든 전역 단축키 등록을 해제합니다.
|
||||
@@ -1,27 +1,35 @@
|
||||
# global-shortcut
|
||||
|
||||
The `global-shortcut` module can register/unregister a global keyboard shortcut
|
||||
in operating system, so that you can customize the operations for various shortcuts.
|
||||
Note that the shortcut is global, even if the app does not get focused, it will still work.
|
||||
with the operating system, so that you can customize the operations for various shortcuts.
|
||||
Note that the shortcut is global; it will work even if the app does not have the keyboard focus.
|
||||
You should not use this module until the `ready` event of the app module is emitted.
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
var globalShortcut = require('global-shortcut');
|
||||
|
||||
// Register a 'ctrl+x' shortcut listener.
|
||||
var ret = globalShortcut.register('ctrl+x', function() { console.log('ctrl+x is pressed'); })
|
||||
app.on('ready', function() {
|
||||
// Register a 'ctrl+x' shortcut listener.
|
||||
var ret = globalShortcut.register('ctrl+x', function() {
|
||||
console.log('ctrl+x is pressed');
|
||||
})
|
||||
|
||||
if (!ret) {
|
||||
console.log('registration failed');
|
||||
}
|
||||
if (!ret) {
|
||||
console.log('registration failed');
|
||||
}
|
||||
|
||||
// Check whether a shortcut is registered.
|
||||
console.log(globalShortcut.isRegistered('ctrl+x'));
|
||||
// Check whether a shortcut is registered.
|
||||
console.log(globalShortcut.isRegistered('ctrl+x'));
|
||||
});
|
||||
|
||||
// Unregister a shortcut.
|
||||
globalShortcut.unregister('ctrl+x');
|
||||
app.on('will-quit', function() {
|
||||
// Unregister a shortcut.
|
||||
globalShortcut.unregister('ctrl+x');
|
||||
|
||||
// Unregister all shortcuts.
|
||||
globalShortcut.unregisterAll();
|
||||
// Unregister all shortcuts.
|
||||
globalShortcut.unregisterAll();
|
||||
});
|
||||
```
|
||||
|
||||
## globalShortcut.register(accelerator, callback)
|
||||
@@ -29,14 +37,14 @@ globalShortcut.unregisterAll();
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `callback` Function
|
||||
|
||||
Registers a global shortcut of `accelerator`, the `callback` would be called when
|
||||
the registered shortcut is pressed by user.
|
||||
Registers a global shortcut of `accelerator`. The `callback` is called when
|
||||
the registered shortcut is pressed by the user.
|
||||
|
||||
## globalShortcut.isRegistered(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
|
||||
Returns `true` or `false` depending on if the shortcut `accelerator` is registered.
|
||||
Returns `true` or `false` depending on whether the shortcut `accelerator` is registered.
|
||||
|
||||
## globalShortcut.unregister(accelerator)
|
||||
|
||||
|
||||
46
docs/api/ipc-main-process-ko.md
Normal file
46
docs/api/ipc-main-process-ko.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# ipc (main process)
|
||||
|
||||
랜더러 프로세스(웹 페이지)로 부터 동기 또는 비동기로 메시지를 받아 처리합니다.
|
||||
|
||||
랜더러로부터 발신된 메시지들은 모두 이 모듈에서 `channel` 이라는 특정 이벤트 이름을 통해 수신할 수 있습니다.
|
||||
동기 메시지는 `event.returnValue`를 이용하여 반환값(답장)을 설정할 수 있습니다. 비동기 메시지라면 `event.sender.send(...)`를 사용하면 됩니다.
|
||||
|
||||
또한 메인 프로세스에서 랜더러 프로세스로 메시지를 보내는 것도 가능합니다.
|
||||
자세한 내용은 [WebContents.send](browser-window-ko.md#webcontentssendchannel-args)를 참고 하세요.
|
||||
|
||||
보내진 메시지들을 처리하는 예제입니다:
|
||||
|
||||
```javascript
|
||||
// 메인 프로세스에서 처리.
|
||||
var ipc = require('ipc');
|
||||
ipc.on('asynchronous-message', function(event, arg) {
|
||||
console.log(arg); // prints "ping"
|
||||
event.sender.send('asynchronous-reply', 'pong');
|
||||
});
|
||||
|
||||
ipc.on('synchronous-message', function(event, arg) {
|
||||
console.log(arg); // prints "ping"
|
||||
event.returnValue = 'pong';
|
||||
});
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 랜더러 프로세스에서의 처리 (web page).
|
||||
var ipc = require('ipc');
|
||||
console.log(ipc.sendSync('synchronous-message', 'ping')); // prints "pong"
|
||||
|
||||
ipc.on('asynchronous-reply', function(arg) {
|
||||
console.log(arg); // prints "pong"
|
||||
});
|
||||
ipc.send('asynchronous-message', 'ping');
|
||||
```
|
||||
|
||||
## Class: Event
|
||||
|
||||
### Event.returnValue
|
||||
|
||||
동기 메시지를 설정합니다.
|
||||
|
||||
### Event.sender
|
||||
|
||||
메시지를 보내온 sender `WebContents` 객체입니다.
|
||||
25
docs/api/ipc-renderer-ko.md
Normal file
25
docs/api/ipc-renderer-ko.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ipc (renderer)
|
||||
|
||||
`ipc` 모듈은 메인 프로세스로 메시지를 동기 또는 비동기로 보내고 받을 수 있는 몇 가지 방법을 제공합니다.
|
||||
만약 랜더러 프로세스에서 메인 프로세스의 모듈을 직접적으로 사용하고 싶다면 [remote](remote-ko.md) 모듈을 사용하는 것을 고려해보는 것이 좋습니다.
|
||||
|
||||
[ipc (main process)](ipc-main-process-ko.md)에서 예제를 볼 수 있습니다.
|
||||
|
||||
## ipc.send(channel[, args...])
|
||||
|
||||
지정한 `channel`을 통해 `args..`를 비동기로 메시지를 보냅니다. 메인 프로세스는 `ipc` 모듈의 `channel` 이벤트를 통해 메시지를 받을 수 있습니다.
|
||||
|
||||
## ipc.sendSync(channel[, args...])
|
||||
|
||||
지정한 `channel`을 통해 `args..`를 동기로 메시지를 보냅니다. 그리고 메인 프로세스에서 보낸 결과를 반환합니다.
|
||||
메인 프로세스는 `ipc` 모듈의 `channel` 이벤트를 통해 메시지를 받을 수 있습니다. 그리고 `event.returnValue`를 통해 반환값을 설정할 수 있습니다.
|
||||
|
||||
역자 주: `channel`은 이벤트 이름입니다.
|
||||
|
||||
**알림:** 보통 개발자들은 해당 API를 사용하려 하지 않습니다. 동기 ipc 작업은 랜더러 프로세스의 모든 작업을 중단시킵니다.
|
||||
|
||||
## ipc.sendToHost(channel[, args...])
|
||||
|
||||
`ipc.send`와 비슷하지만 메시지를 메인 프로세스 대신 호스트 페이지로 보냅니다.
|
||||
|
||||
이 메소드는 보통 `<webview>`와 호스트 페이지 간의 통신에 사용됩니다.
|
||||
@@ -1,20 +1,20 @@
|
||||
# menu-item
|
||||
# menu-item
|
||||
|
||||
## Class: MenuItem
|
||||
|
||||
### new MenuItem(options)
|
||||
|
||||
* `options` Object
|
||||
* `click` Function - 메뉴 아이템이 클릭될 때 호출되는 콜백함수
|
||||
* `selector` String - First Responder가 클릭될 때 호출 되는 선택자 (OS X 전용)
|
||||
* `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, `checkbox` 또는 `radio` 사용가능
|
||||
* `click` Function - 메뉴 아이템이 클릭될 때 호출되는 콜백함수
|
||||
* `selector` String - First Responder가 클릭될 때 호출 되는 선택자 (OS X 전용)
|
||||
* `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, `checkbox` 또는 `radio` 사용가능
|
||||
* `label` String
|
||||
* `sublabel` String
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `icon` [NativeImage](native-image-ko.md)
|
||||
* `enabled` Boolean
|
||||
* `visible` Boolean
|
||||
* `checked` Boolean
|
||||
* `submenu` Menu - 보조메뉴를 설정합니다. `type`이 `submenu`일 경우 반드시 설정해야합니다. 일반 메뉴 아이템일 경우 생략할 수 있습니다.
|
||||
* `id` String - 현재 메뉴 아이템에 대해 유일키를 지정합니다. 이 키는 이후 `position` 옵션에서 사용할 수 있습니다.
|
||||
* `position` String - 미리 지정한 `id`를 이용하여 메뉴 아이템의 위치를 세밀하게 조정합니다.
|
||||
* `submenu` Menu - 보조메뉴를 설정합니다. `type`이 `submenu`일 경우 반드시 설정해야합니다. 일반 메뉴 아이템일 경우 생략할 수 있습니다.
|
||||
* `id` String - 현재 메뉴 아이템에 대해 유일키를 지정합니다. 이 키는 이후 `position` 옵션에서 사용할 수 있습니다.
|
||||
* `position` String - 미리 지정한 `id`를 이용하여 메뉴 아이템의 위치를 세밀하게 조정합니다.
|
||||
|
||||
330
docs/api/menu-ko.md
Normal file
330
docs/api/menu-ko.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# menu
|
||||
|
||||
The `Menu` class is used to create native menus that can be used as
|
||||
application menus and context menus. Each menu consists of multiple menu
|
||||
items, and each menu item can have a submenu.
|
||||
|
||||
Below is an example of creating a menu dynamically in a web page by using
|
||||
the [remote](remote-ko.md) module, and showing it when the user right clicks
|
||||
the page:
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<script>
|
||||
var remote = require('remote');
|
||||
var Menu = remote.require('menu');
|
||||
var MenuItem = remote.require('menu-item');
|
||||
|
||||
var menu = new Menu();
|
||||
menu.append(new MenuItem({ label: 'MenuItem1', click: function() { console.log('item 1 clicked'); } }));
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true }));
|
||||
|
||||
window.addEventListener('contextmenu', function (e) {
|
||||
e.preventDefault();
|
||||
menu.popup(remote.getCurrentWindow());
|
||||
}, false);
|
||||
</script>
|
||||
```
|
||||
|
||||
Another example of creating the application menu with the simple template API:
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<script>
|
||||
var remote = require('remote');
|
||||
var Menu = remote.require('menu');
|
||||
var template = [
|
||||
{
|
||||
label: 'Electron',
|
||||
submenu: [
|
||||
{
|
||||
label: 'About Electron',
|
||||
selector: 'orderFrontStandardAboutPanel:'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Services',
|
||||
submenu: []
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Hide Electron',
|
||||
accelerator: 'Command+H',
|
||||
selector: 'hide:'
|
||||
},
|
||||
{
|
||||
label: 'Hide Others',
|
||||
accelerator: 'Command+Shift+H',
|
||||
selector: 'hideOtherApplications:'
|
||||
},
|
||||
{
|
||||
label: 'Show All',
|
||||
selector: 'unhideAllApplications:'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
selector: 'terminate:'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Undo',
|
||||
accelerator: 'Command+Z',
|
||||
selector: 'undo:'
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
accelerator: 'Shift+Command+Z',
|
||||
selector: 'redo:'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Cut',
|
||||
accelerator: 'Command+X',
|
||||
selector: 'cut:'
|
||||
},
|
||||
{
|
||||
label: 'Copy',
|
||||
accelerator: 'Command+C',
|
||||
selector: 'copy:'
|
||||
},
|
||||
{
|
||||
label: 'Paste',
|
||||
accelerator: 'Command+V',
|
||||
selector: 'paste:'
|
||||
},
|
||||
{
|
||||
label: 'Select All',
|
||||
accelerator: 'Command+A',
|
||||
selector: 'selectAll:'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'Command+R',
|
||||
click: function() { remote.getCurrentWindow().reload(); }
|
||||
},
|
||||
{
|
||||
label: 'Toggle DevTools',
|
||||
accelerator: 'Alt+Command+I',
|
||||
click: function() { remote.getCurrentWindow().toggleDevTools(); }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Minimize',
|
||||
accelerator: 'Command+M',
|
||||
selector: 'performMiniaturize:'
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
accelerator: 'Command+W',
|
||||
selector: 'performClose:'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Bring All to Front',
|
||||
selector: 'arrangeInFront:'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
submenu: []
|
||||
}
|
||||
];
|
||||
|
||||
menu = Menu.buildFromTemplate(template);
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
</script>
|
||||
```
|
||||
|
||||
## Class: Menu
|
||||
|
||||
### new Menu()
|
||||
|
||||
Creates a new menu.
|
||||
|
||||
### Class Method: Menu.setApplicationMenu(menu)
|
||||
|
||||
* `menu` Menu
|
||||
|
||||
Sets `menu` as the application menu on OS X. On Windows and Linux, the `menu`
|
||||
will be set as each window's top menu.
|
||||
|
||||
### Class Method: Menu.sendActionToFirstResponder(action)
|
||||
|
||||
* `action` String
|
||||
|
||||
Sends the `action` to the first responder of application, this is used for
|
||||
emulating default Cocoa menu behaviors, usually you would just use the
|
||||
`selector` property of `MenuItem`.
|
||||
|
||||
**Note:** This method is OS X only.
|
||||
|
||||
### Class Method: Menu.buildFromTemplate(template)
|
||||
|
||||
* `template` Array
|
||||
|
||||
Generally, the `template` is just an array of `options` for constructing
|
||||
[MenuItem](menu-item-ko.md), the usage can be referenced above.
|
||||
|
||||
You can also attach other fields to element of the `template`, and they will
|
||||
become properties of the constructed menu items.
|
||||
|
||||
### Menu.popup(browserWindow, [x, y])
|
||||
|
||||
* `browserWindow` BrowserWindow
|
||||
* `x` Number
|
||||
* `y` Number
|
||||
|
||||
Popups this menu as a context menu in the `browserWindow`. You can optionally
|
||||
provide a `(x,y)` coordinate to place the menu at, otherwise it will be placed
|
||||
at the current mouse cursor position.
|
||||
|
||||
### Menu.append(menuItem)
|
||||
|
||||
* `menuItem` MenuItem
|
||||
|
||||
Appends the `menuItem` to the menu.
|
||||
|
||||
### Menu.insert(pos, menuItem)
|
||||
|
||||
* `pos` Integer
|
||||
* `menuItem` MenuItem
|
||||
|
||||
Inserts the `menuItem` to the `pos` position of the menu.
|
||||
|
||||
### Menu.items
|
||||
|
||||
Get the array containing the menu's items.
|
||||
|
||||
## Notes on OS X application menu
|
||||
|
||||
OS X has a completely different style of application menu from Windows and
|
||||
Linux, and here are some notes on making your app's menu more native-like.
|
||||
|
||||
### Standard menus
|
||||
|
||||
On OS X there are many system defined standard menus, like the `Services` and
|
||||
`Windows` menus. To make your menu a standard menu, you can just set your menu's
|
||||
label to one of followings, and Electron will recognize them and make them
|
||||
become standard menus:
|
||||
|
||||
* `Window`
|
||||
* `Help`
|
||||
* `Services`
|
||||
|
||||
### Standard menu item actions
|
||||
|
||||
OS X has provided standard actions for some menu items (which are called
|
||||
`selector`s), like `About xxx`, `Hide xxx`, and `Hide Others`. To set the action
|
||||
of a menu item to a standard action, you can set the `selector` attribute of the
|
||||
menu item.
|
||||
|
||||
### Main menu's name
|
||||
|
||||
On OS X the label of application menu's first item is always your app's name,
|
||||
no matter what label you set. To change it you have to change your app's name
|
||||
by modifying your app bundle's `Info.plist` file. See
|
||||
[About Information Property List Files](https://developer.apple.com/library/ios/documentation/general/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html)
|
||||
for more.
|
||||
|
||||
|
||||
## Menu item position
|
||||
|
||||
You can make use of `position` and `id` to control how the item would be placed
|
||||
when building a menu with `Menu.buildFromTemplate`.
|
||||
|
||||
The `position` attribute of `MenuItem` has the form `[placement]=[id]` where
|
||||
placement is one of `before`, `after`, or `endof` and `id` is the unique ID of
|
||||
an existing item in the menu:
|
||||
|
||||
* `before` - Inserts this item before the id referenced item. If the
|
||||
referenced item doesn't exist the item will be inserted at the end of
|
||||
the menu.
|
||||
* `after` - Inserts this item after id referenced item. If the referenced
|
||||
item doesn't exist the item will be inserted at the end of the menu.
|
||||
* `endof` - Inserts this item at the end of the logical group containing
|
||||
the id referenced item. (Groups are created by separator items). If
|
||||
the referenced item doesn't exist a new separator group is created with
|
||||
the given id and this item is inserted after that separator.
|
||||
|
||||
When an item is positioned following unpositioned items are inserted after
|
||||
it, until a new item is positioned. So if you want to position a group of
|
||||
menu items in the same location you only need to specify a position for
|
||||
the first item.
|
||||
|
||||
### Examples
|
||||
|
||||
Template:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{label: '4', id: '4'},
|
||||
{label: '5', id: '5'},
|
||||
{label: '1', id: '1', position: 'before=4'},
|
||||
{label: '2', id: '2'},
|
||||
{label: '3', id: '3'}
|
||||
]
|
||||
```
|
||||
|
||||
Menu:
|
||||
|
||||
```
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
```
|
||||
|
||||
Template:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{label: 'a', position: 'endof=letters'},
|
||||
{label: '1', position: 'endof=numbers'},
|
||||
{label: 'b', position: 'endof=letters'},
|
||||
{label: '2', position: 'endof=numbers'},
|
||||
{label: 'c', position: 'endof=letters'},
|
||||
{label: '3', position: 'endof=numbers'}
|
||||
]
|
||||
```
|
||||
|
||||
Menu:
|
||||
|
||||
```
|
||||
- ---
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- ---
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
127
docs/api/native-image-ko.md
Normal file
127
docs/api/native-image-ko.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# NativeImage
|
||||
|
||||
Electron은 파일 경로나 `NativeImage` 인스턴스를 전달하여 사용하는 이미지 API를 가지고 있습니다. `null`을 전달할 경우 빈 이미지가 사용됩니다.
|
||||
|
||||
예를 들어 트레이 메뉴를 만들거나 윈도우의 아이콘을 설정할 때 다음과 같이 `문자열`인 파일 경로를 전달할 수 있습니다:
|
||||
|
||||
```javascript
|
||||
var appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
var window = new BrowserWindow({icon: '/Users/somebody/images/window.png'});
|
||||
```
|
||||
|
||||
또는 클립보드로부터 이미지를 읽어올 수 있습니다:
|
||||
|
||||
```javascript
|
||||
var clipboard = require('clipboard');
|
||||
var image = clipboard.readImage();
|
||||
var appIcon = new Tray(image);
|
||||
```
|
||||
|
||||
## 지원하는 포맷
|
||||
|
||||
현재 `PNG` 와 `JPEG` 포맷을 지원하고 있습니다. 손실 없는 이미지 압축과 투명도 지원을 위해 `PNG` 사용을 권장합니다.
|
||||
|
||||
## 고해상도 이미지
|
||||
|
||||
플랫폼이 high-DPI를 지원하는 경우 `@2x`와 같이 이미지의 파일명 뒤에 접미사를 추가하여 고해상도 이미지로 지정할 수 있습니다.
|
||||
|
||||
예를 들어 `icon.png` 라는 기본 해상도의 이미지를 기준으로 크기를 두 배로 늘린 이미지를 `icon@2x.png`와 같이 이름을 지정하면 고해상도 이미지로 처리됩니다.
|
||||
|
||||
서로 다른 해상도(DPI)의 이미지를 지원하고 싶다면 다중 해상도의 이미지를 접미사를 붙여 한 폴더에 넣으면 됩니다. 이 이미지를 사용(로드)할 땐 접미사를 붙이지 않습니다:
|
||||
|
||||
```text
|
||||
images/
|
||||
├── icon.png
|
||||
├── icon@2x.png
|
||||
└── icon@3x.png
|
||||
```
|
||||
|
||||
|
||||
```javascript
|
||||
var appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
```
|
||||
|
||||
지원하는 DPI 접미사는 다음과 같습니다:
|
||||
|
||||
* `@1x`
|
||||
* `@1.25x`
|
||||
* `@1.33x`
|
||||
* `@1.4x`
|
||||
* `@1.5x`
|
||||
* `@1.8x`
|
||||
* `@2x`
|
||||
* `@2.5x`
|
||||
* `@3x`
|
||||
* `@4x`
|
||||
* `@5x`
|
||||
|
||||
## 템플릿 이미지
|
||||
|
||||
템플릿 이미지는 검은색과 명확한 색상(알파 채널)으로 이루어져 있습니다.
|
||||
템플릿 이미지는 단독 이미지로 사용되지 않고 다른 컨텐츠와 혼합되어 최종 외관 만드는데 사용됩니다.
|
||||
|
||||
가장 일반적으로 템플릿 이미지는 밝고 어두운 테마 색상으로 변경할 수 있는 메뉴 바 아이콘 등에 사용되고 있습니다.
|
||||
|
||||
템플릿 이미지는 Mac 운영체제만 지원합니다.
|
||||
|
||||
템플릿 이미지를 지정하려면 다음 예제와 같이 파일명에 `Template` 문자열을 추가해야 합니다:
|
||||
|
||||
* `xxxTemplate.png`
|
||||
* `xxxTemplate@2x.png`
|
||||
|
||||
## nativeImage.createEmpty()
|
||||
|
||||
빈 `NativeImage` 인스턴스를 만듭니다.
|
||||
|
||||
## nativeImage.createFromPath(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
`path`로부터 이미지를 로드하여 새로운 `NativeImage` 인스턴스를 만듭니다.
|
||||
|
||||
## nativeImage.createFromBuffer(buffer[, scaleFactor])
|
||||
|
||||
* `buffer` [Buffer][buffer]
|
||||
* `scaleFactor` Double
|
||||
|
||||
`buffer`로부터 이미지를 로드하여 새로운 `NativeImage` 인스턴스를 만듭니다. `scaleFactor`는 1.0이 기본입니다.
|
||||
|
||||
## nativeImage.createFromDataUrl(dataUrl)
|
||||
|
||||
* `dataUrl` String
|
||||
|
||||
`dataUrl`로부터 이미지를 로드하여 새로운 `NativeImage` 인스턴스를 만듭니다.
|
||||
|
||||
## Class: NativeImage
|
||||
|
||||
이미지를 표현한 클래스입니다.
|
||||
|
||||
### NativeImage.toPng()
|
||||
|
||||
`PNG` 이미지를 인코딩한 데이터를 [Buffer][buffer]로 반환합니다.
|
||||
|
||||
### NativeImage.toJpeg(quality)
|
||||
|
||||
* `quality` Integer
|
||||
|
||||
`JPEG` 이미지를 인코딩한 데이터를 [Buffer][buffer]로 반환합니다.
|
||||
|
||||
### NativeImage.toDataUrl()
|
||||
|
||||
이미지의 data URL을 반환합니다.
|
||||
|
||||
### NativeImage.isEmpty()
|
||||
|
||||
이미지가 비었는지를 체크합니다.
|
||||
|
||||
### NativeImage.getSize()
|
||||
|
||||
이미지의 사이즈를 반환합니다.
|
||||
|
||||
### NativeImage.setTemplateImage(option)
|
||||
|
||||
* `option` Boolean
|
||||
|
||||
해당 이미지를 템플릿 이미지로 설정합니다.
|
||||
|
||||
[buffer]: https://iojs.org/api/buffer.html#buffer_class_buffer
|
||||
@@ -114,7 +114,7 @@ Returns a [Buffer][buffer] that contains image's `PNG` encoded data.
|
||||
|
||||
### NativeImage.toJpeg(quality)
|
||||
|
||||
* `quality` Integer
|
||||
* `quality` Integer between 0 - 100 (required)
|
||||
|
||||
Returns a [Buffer][buffer] that contains image's `JPEG` encoded data.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# power-monitor
|
||||
# power-monitor
|
||||
|
||||
`power-monitor` 모듈은 PC의 파워 상태를 나타냅니다. (주로 노트북 등에서 사용됩니다)
|
||||
이 모듈은 메인 프로세스에서만 사용할 수 있으며, (remote 모듈(RPC)을 사용해도 작동이 됩니다)
|
||||
|
||||
43
docs/api/power-save-blocker-ko.md
Normal file
43
docs/api/power-save-blocker-ko.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# power-save-blocker
|
||||
|
||||
`power-save-blocker` 모듈은 시스템이 저전력 모드(슬립)로 진입하는 것을 막고 앱 및 화면이 항상 활성화되어 있는 상태를 만들 수 있도록 해줍니다.
|
||||
|
||||
예제:
|
||||
|
||||
```javascript
|
||||
var powerSaveBlocker = require('power-save-blocker');
|
||||
|
||||
var id = powerSaveBlocker.start('prevent-display-sleep');
|
||||
console.log(powerSaveBlocker.isStarted(id));
|
||||
|
||||
powerSaveBlocker.stop(id);
|
||||
```
|
||||
|
||||
## powerSaveBlocker.start(type)
|
||||
|
||||
* `type` String - Power save blocker 종류
|
||||
* `prevent-app-suspension` - 저전력 모드 등으로 인한 어플리케이션 작동 중단을 방지합니다.
|
||||
시스템을 항시 활성화 상태로 만듭니다, 하지만 화면은 자동으로 꺼질 수 있습니다. 사용 예시: 파일 다운로드, 음악 재생 등.
|
||||
* `prevent-display-sleep`- 슬립 모드 등으로 인한 어플리케이션의 작동 중단을 방지합니다.
|
||||
시스템을 항시 활성화 상태로 만들고 슬립 모드(화면 꺼짐)를 방지합니다. 사용 예시: 비디오 재생 등.
|
||||
|
||||
Power save blocker를 시작하고 시스템이 저전력 모드(슬립)로 진입하는 것을 막습니다. 정수로 된 식별 ID를 반환합니다.
|
||||
|
||||
**알림:**
|
||||
`prevent-display-sleep` 모드는 `prevent-app-suspension` 보다 우선순위가 높습니다.
|
||||
가장 높은 우선순위의 모드만 작동합니다. 다시 말해 `prevent-display-sleep` 모드는 언제나 `prevent-app-suspension` 모드의 효과를 덮어씌웁니다.
|
||||
|
||||
예를 들어 A-요청이 `prevent-app-suspension` 모드를 사용하고 B-요청이 `prevent-display-sleep`를 사용하는 API 호출이 있었다 치면
|
||||
`prevent-display-sleep` 모드를 사용하는 B의 작동이 중단(stop)되기 전까지 작동하다 B가 중단되면 `prevent-app-suspension` 모드를 사용하는 A가 작동하기 시작합니다.
|
||||
|
||||
## powerSaveBlocker.stop(id)
|
||||
|
||||
* `id` Integer - `powerSaveBlocker.start`로 부터 반환되는 power save blocker 식별 ID.
|
||||
|
||||
설정한 power save blocker를 중지합니다.
|
||||
|
||||
## powerSaveBlocker.isStarted(id)
|
||||
|
||||
* `id` Integer - `powerSaveBlocker.start`로 부터 반환되는 power save blocker 식별 ID.
|
||||
|
||||
해당하는 id의 `powerSaveBlocker`가 실행중인지 확인합니다.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user