diff --git a/README-ko.md b/README-ko.md index 83634370eba9..c722243eb589 100644 --- a/README-ko.md +++ b/README-ko.md @@ -8,7 +8,7 @@ :zap: *프레임워크 이름이 Atom Shell에서 Electron으로 변경되었습니다* :zap: -Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [io.js](http://iojs.org) 와 +Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [Node.js](https://nodejs.org) 와 [Chromium](http://www.chromium.org)을 기반으로 만들어 졌으며 [Atom Editor](https://github.com/atom/atom)에 사용되고 있습니다. Electron에 대한 중요한 알림을 받고 싶다면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 팔로우 하세요. @@ -41,7 +41,7 @@ Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문 ## 참조 문서 (번역) - [브라질 포르투칼어](https://github.com/atom/electron/tree/master/docs-translations/pt-BR) -- [한국어](https://github.com/atom/electron/tree/master/docs-translations/ko) +- [한국어](https://github.com/atom/electron/tree/master/docs-translations/ko-KR) - [일본어](https://github.com/atom/electron/tree/master/docs-translations/jp) - [스페인어](https://github.com/atom/electron/tree/master/docs-translations/es) - [중국어 간체](https://github.com/atom/electron/tree/master/docs-translations/zh-CN) diff --git a/README.md b/README.md index 54b6af5fa3fc..94d4c09982be 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ :zap: *Formerly known as Atom Shell* :zap: The Electron framework lets you write cross-platform desktop applications -using JavaScript, HTML and CSS. It is based on [io.js](http://iojs.org) and +using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org) and [Chromium](http://www.chromium.org) and is used in the [Atom editor](https://github.com/atom/atom). @@ -15,7 +15,7 @@ Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important announcements. This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0). -By participating, you are expected to uphold this code. Please report +By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com. ## Downloads @@ -47,7 +47,7 @@ contains documents describing how to build and contribute to Electron. ## Documentation Translations - [Brazilian Portuguese](https://github.com/atom/electron/tree/master/docs-translations/pt-BR) -- [Korean](https://github.com/atom/electron/tree/master/docs-translations/ko) +- [Korean](https://github.com/atom/electron/tree/master/docs-translations/ko-KR) - [Japanese](https://github.com/atom/electron/tree/master/docs-translations/jp) - [Spanish](https://github.com/atom/electron/tree/master/docs-translations/es) - [Simplified Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-CN) @@ -55,12 +55,12 @@ contains documents describing how to build and contribute to Electron. ## Community -You can ask questions and interact with the community in the following +You can ask questions and interact with the community in the following locations: -- [`electron`](http://discuss.atom.io/category/electron) category on the Atom +- [`electron`](http://discuss.atom.io/category/electron) category on the Atom forums - `#atom-shell` channel on Freenode - [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack -Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) +Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. diff --git a/atom.gyp b/atom.gyp index c9d5f924789f..f06a0c8251be 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.33.0', + 'version%': '0.33.6', }, 'includes': [ 'filenames.gypi', diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index 3c7d6b2e7034..fe3c0e09ae33 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -27,10 +27,10 @@ AtomMainDelegate::~AtomMainDelegate() { } bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { - // Disable logging out to debug.log on Windows logging::LoggingSettings settings; #if defined(OS_WIN) #if defined(DEBUG) + // Print logging to debug.log on Windows settings.logging_dest = logging::LOG_TO_ALL; settings.log_file = L"debug.log"; settings.lock_log = logging::LOCK_LOG_FILE; @@ -41,6 +41,12 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { #else // defined(OS_WIN) settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; #endif // !defined(OS_WIN) + + // Only enable logging when --enable-logging is specified. + auto command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(switches::kEnableLogging)) + settings.logging_dest = logging::LOG_NONE; + logging::InitLogging(settings); // Logging with pid and timestamp. diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc new file mode 100644 index 000000000000..ec4dcd84b285 --- /dev/null +++ b/atom/browser/api/atom_api_download_item.cc @@ -0,0 +1,201 @@ +// 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/api/atom_api_download_item.h" + +#include + +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/file_path_converter.h" +#include "atom/common/native_mate_converters/gurl_converter.h" +#include "atom/common/node_includes.h" +#include "base/memory/linked_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "native_mate/dictionary.h" +#include "net/base/filename_util.h" + +namespace mate { + +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + content::DownloadItem::DownloadState state) { + std::string download_state; + switch (state) { + case content::DownloadItem::COMPLETE: + download_state = "completed"; + break; + case content::DownloadItem::CANCELLED: + download_state = "cancelled"; + break; + case content::DownloadItem::INTERRUPTED: + download_state = "interrupted"; + break; + default: + break; + } + return ConvertToV8(isolate, download_state); + } +}; + +} // namespace mate + +namespace atom { + +namespace api { + +namespace { +// The wrapDownloadItem funtion which is implemented in JavaScript +using WrapDownloadItemCallback = base::Callback)>; +WrapDownloadItemCallback g_wrap_download_item; + +char kDownloadItemSavePathKey[] = "DownloadItemSavePathKey"; + +std::map>> g_download_item_objects; +} // namespace + +DownloadItem::SavePathData::SavePathData(const base::FilePath& path) : + path_(path) { +} + +const base::FilePath& DownloadItem::SavePathData::path() { + return path_; +} + +DownloadItem::DownloadItem(content::DownloadItem* download_item) : + download_item_(download_item) { + download_item_->AddObserver(this); +} + +DownloadItem::~DownloadItem() { + Destroy(); +} + +void DownloadItem::Destroy() { + if (download_item_) { + download_item_->RemoveObserver(this); + auto iter = g_download_item_objects.find(download_item_->GetId()); + if (iter != g_download_item_objects.end()) + g_download_item_objects.erase(iter); + download_item_ = nullptr; + } +} + +bool DownloadItem::IsDestroyed() const { + return download_item_ == nullptr; +} + +void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) { + download_item_->IsDone() ? Emit("done", item->GetState()) : Emit("updated"); +} + +void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download) { + Destroy(); +} + +int64 DownloadItem::GetReceivedBytes() { + return download_item_->GetReceivedBytes(); +} + +int64 DownloadItem::GetTotalBytes() { + return download_item_->GetTotalBytes(); +} + +const GURL& DownloadItem::GetUrl() { + return download_item_->GetURL(); +} + +std::string DownloadItem::GetMimeType() { + return download_item_->GetMimeType(); +} + +bool DownloadItem::HasUserGesture() { + return download_item_->HasUserGesture(); +} + +std::string DownloadItem::GetFilename() { + return base::UTF16ToUTF8(net::GenerateFileName(GetUrl(), + GetContentDisposition(), + std::string(), + download_item_->GetSuggestedFilename(), + GetMimeType(), + std::string()).LossyDisplayName()); +} + +std::string DownloadItem::GetContentDisposition() { + return download_item_->GetContentDisposition(); +} + +void DownloadItem::SetSavePath(const base::FilePath& path) { + download_item_->SetUserData(UserDataKey(), new SavePathData(path)); +} + +void DownloadItem::Pause() { + download_item_->Pause(); +} + +void DownloadItem::Resume() { + download_item_->Resume(); +} + +void DownloadItem::Cancel() { + download_item_->Cancel(true); +} + +mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return mate::ObjectTemplateBuilder(isolate) + .SetMethod("pause", &DownloadItem::Pause) + .SetMethod("resume", &DownloadItem::Resume) + .SetMethod("cancel", &DownloadItem::Cancel) + .SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes) + .SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes) + .SetMethod("getUrl", &DownloadItem::GetUrl) + .SetMethod("getMimeType", &DownloadItem::GetMimeType) + .SetMethod("hasUserGesture", &DownloadItem::HasUserGesture) + .SetMethod("getFilename", &DownloadItem::GetFilename) + .SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition) + .SetMethod("setSavePath", &DownloadItem::SetSavePath); +} + +void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) { + g_wrap_download_item = callback; +} + +void ClearWrapDownloadItem() { + g_wrap_download_item.Reset(); +} + +// static +mate::Handle DownloadItem::Create( + v8::Isolate* isolate, content::DownloadItem* item) { + auto handle = mate::CreateHandle(isolate, new DownloadItem(item)); + g_wrap_download_item.Run(handle.ToV8()); + g_download_item_objects[item->GetId()] = make_linked_ptr( + new v8::Global(isolate, handle.ToV8())); + return handle; +} + +// static +void* DownloadItem::UserDataKey() { + return &kDownloadItemSavePathKey; +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.SetMethod("_setWrapDownloadItem", &atom::api::SetWrapDownloadItem); + dict.SetMethod("_clearWrapDownloadItem", &atom::api::ClearWrapDownloadItem); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_download_item, Initialize); diff --git a/atom/browser/api/atom_api_download_item.h b/atom/browser/api/atom_api_download_item.h new file mode 100644 index 000000000000..14074a4bed0d --- /dev/null +++ b/atom/browser/api/atom_api_download_item.h @@ -0,0 +1,72 @@ +// 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_API_ATOM_API_DOWNLOAD_ITEM_H_ +#define ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_ + +#include + +#include "atom/browser/api/trackable_object.h" +#include "base/files/file_path.h" +#include "content/public/browser/download_item.h" +#include "native_mate/handle.h" +#include "url/gurl.h" + +namespace atom { + +namespace api { + +class DownloadItem : public mate::EventEmitter, + public content::DownloadItem::Observer { + public: + class SavePathData : public base::SupportsUserData::Data { + public: + explicit SavePathData(const base::FilePath& path); + const base::FilePath& path(); + private: + base::FilePath path_; + }; + + static mate::Handle Create(v8::Isolate* isolate, + content::DownloadItem* item); + static void* UserDataKey(); + + protected: + explicit DownloadItem(content::DownloadItem* download_item); + ~DownloadItem(); + + // Override content::DownloadItem::Observer methods + void OnDownloadUpdated(content::DownloadItem* download) override; + void OnDownloadDestroyed(content::DownloadItem* download) override; + + void Pause(); + void Resume(); + void Cancel(); + int64 GetReceivedBytes(); + int64 GetTotalBytes(); + std::string GetMimeType(); + bool HasUserGesture(); + std::string GetFilename(); + std::string GetContentDisposition(); + const GURL& GetUrl(); + void SetSavePath(const base::FilePath& path); + + private: + // mate::Wrappable: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + bool IsDestroyed() const override; + + void Destroy(); + + content::DownloadItem* download_item_; + + DISALLOW_COPY_AND_ASSIGN(DownloadItem); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_ diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index f07ab8b78045..7bc1a2153e3f 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -8,6 +8,7 @@ #include #include "atom/browser/api/atom_api_cookies.h" +#include "atom/browser/api/atom_api_download_item.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/api/atom_api_web_contents.h" #include "atom/common/native_mate_converters/callback.h" @@ -18,6 +19,8 @@ #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" +#include "brightray/browser/net/devtools_network_conditions.h" +#include "brightray/browser/net/devtools_network_controller.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" @@ -101,19 +104,6 @@ struct Converter { } }; -template<> -struct Converter { - static v8::Local ToV8(v8::Isolate* isolate, - content::DownloadItem* val) { - mate::Dictionary dict(isolate, v8::Object::New(isolate)); - dict.Set("url", val->GetURL()); - dict.Set("filename", val->GetSuggestedFilename()); - dict.Set("mimeType", val->GetMimeType()); - dict.Set("hasUserGesture", val->HasUserGesture()); - return dict.GetHandle(); - } -}; - } // namespace mate namespace atom { @@ -245,11 +235,12 @@ Session::~Session() { } void Session::OnDownloadCreated(content::DownloadManager* manager, - content::DownloadItem* item) { + content::DownloadItem* item) { auto web_contents = item->GetWebContents(); - bool prevent_default = Emit("will-download", item, - api::WebContents::CreateFrom(isolate(), - web_contents)); + bool prevent_default = Emit( + "will-download", + DownloadItem::Create(isolate(), item), + api::WebContents::CreateFrom(isolate(), web_contents)); if (prevent_default) { item->Cancel(true); item->Remove(); @@ -305,6 +296,43 @@ void Session::SetDownloadPath(const base::FilePath& path) { prefs::kDownloadDefaultDirectory, path); } +void Session::EnableNetworkEmulation(const mate::Dictionary& options) { + scoped_ptr conditions; + bool offline = false; + double latency, download_throughput, upload_throughput; + if (options.Get("offline", &offline) && offline) { + conditions.reset(new brightray::DevToolsNetworkConditions(offline)); + } else { + options.Get("latency", &latency); + options.Get("downloadThroughput", &download_throughput); + options.Get("uploadThroughput", &upload_throughput); + conditions.reset( + new brightray::DevToolsNetworkConditions(false, + latency, + download_throughput, + upload_throughput)); + } + auto controller = browser_context_->GetDevToolsNetworkController(); + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, + base::Unretained(controller), + std::string(), + base::Passed(&conditions))); +} + +void Session::DisableNetworkEmulation() { + scoped_ptr conditions( + new brightray::DevToolsNetworkConditions(false)); + auto controller = browser_context_->GetDevToolsNetworkController(); + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, + base::Unretained(controller), + std::string(), + base::Passed(&conditions))); +} + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = atom::api::Cookies::Create(isolate, browser_context()); @@ -321,6 +349,8 @@ mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder( .SetMethod("clearStorageData", &Session::ClearStorageData) .SetMethod("setProxy", &Session::SetProxy) .SetMethod("setDownloadPath", &Session::SetDownloadPath) + .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) + .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) .SetProperty("cookies", &Session::Cookies); } diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 14406e57af5b..68ee3634e6c4 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -20,6 +20,7 @@ class FilePath; namespace mate { class Arguments; +class Dictionary; } namespace atom { @@ -65,6 +66,8 @@ class Session: public mate::TrackableObject, void ClearStorageData(mate::Arguments* args); void SetProxy(const std::string& proxy, const base::Closure& callback); void SetDownloadPath(const base::FilePath& path); + void EnableNetworkEmulation(const mate::Dictionary& options); + void DisableNetworkEmulation(); v8::Local Cookies(v8::Isolate* isolate); // Cached object for cookies API. diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 9791a94bb77b..f6433ca635cd 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -7,6 +7,7 @@ #include #include "atom/browser/api/atom_api_session.h" +#include "atom/browser/api/atom_api_window.h" #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" @@ -26,6 +27,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "brightray/browser/inspectable_web_contents.h" +#include "brightray/browser/inspectable_web_contents_view.h" #include "chrome/browser/printing/print_view_manager_basic.h" #include "chrome/browser/printing/print_preview_message_handler.h" #include "content/common/view_messages.h" @@ -120,6 +122,33 @@ struct Converter { } }; +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + net::HttpResponseHeaders* headers) { + base::DictionaryValue response_headers; + if (headers) { + void* iter = nullptr; + std::string key; + std::string value; + while (headers->EnumerateHeaderLines(&iter, &key, &value)) { + key = base::StringToLowerASCII(key); + value = base::StringToLowerASCII(value); + if (response_headers.HasKey(key)) { + base::ListValue* values = nullptr; + if (response_headers.GetList(key, &values)) + values->AppendString(value); + } else { + scoped_ptr values(new base::ListValue()); + values->AppendString(value); + response_headers.Set(key, values.Pass()); + } + } + } + return ConvertToV8(isolate, response_headers); + } +}; + } // namespace mate @@ -131,7 +160,7 @@ namespace { v8::Persistent template_; -// The wrapWebContents funtion which is implemented in JavaScript +// The wrapWebContents function which is implemented in JavaScript using WrapWebContentsCallback = base::Callback)>; WrapWebContentsCallback g_wrap_web_contents; @@ -201,7 +230,9 @@ WebContents::WebContents(v8::Isolate* isolate, AttachAsUserData(web_contents); InitWithWebContents(web_contents); - // Save the preferences. + managed_web_contents()->GetView()->SetDelegate(this); + + // Save the preferences in C++. base::DictionaryValue web_preferences; mate::ConvertFromV8(isolate, options.GetHandle(), &web_preferences); new WebContentsPreferences(web_contents, &web_preferences); @@ -414,30 +445,6 @@ void WebContents::DidStopLoading() { void WebContents::DidGetResourceResponseStart( const content::ResourceRequestDetails& details) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - base::DictionaryValue response_headers; - - net::HttpResponseHeaders* headers = details.headers.get(); - if (!headers) - return; - void* iter = nullptr; - std::string key; - std::string value; - while (headers->EnumerateHeaderLines(&iter, &key, &value)) { - key = base::StringToLowerASCII(key); - value = base::StringToLowerASCII(value); - if (response_headers.HasKey(key)) { - base::ListValue* values = nullptr; - if (response_headers.GetList(key, &values)) - values->AppendString(value); - } else { - scoped_ptr values(new base::ListValue()); - values->AppendString(value); - response_headers.Set(key, values.Pass()); - } - } - Emit("did-get-response-details", details.socket_address.IsEmpty(), details.url, @@ -445,7 +452,7 @@ void WebContents::DidGetResourceResponseStart( details.http_response_code, details.method, details.referrer, - response_headers); + details.headers.get()); } void WebContents::DidGetRedirectForResourceRequest( @@ -454,7 +461,11 @@ void WebContents::DidGetRedirectForResourceRequest( Emit("did-get-redirect-request", details.url, details.new_url, - (details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME)); + (details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME), + details.http_response_code, + details.method, + details.referrer, + details.headers.get()); } void WebContents::DidNavigateMainFrame( @@ -484,6 +495,33 @@ void WebContents::DidUpdateFaviconURL( Emit("page-favicon-updated", unique_urls); } +void WebContents::DevToolsFocused() { + Emit("devtools-focused"); +} + +void WebContents::DevToolsOpened() { + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); + auto handle = WebContents::CreateFrom( + isolate(), managed_web_contents()->GetDevToolsWebContents()); + devtools_web_contents_.Reset(isolate(), handle.ToV8()); + + // Inherit owner window in devtools. + if (owner_window()) + handle->SetOwnerWindow(managed_web_contents()->GetDevToolsWebContents(), + owner_window()); + + Emit("devtools-opened"); +} + +void WebContents::DevToolsClosed() { + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); + devtools_web_contents_.Reset(); + + Emit("devtools-closed"); +} + bool WebContents::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(WebContents, message) @@ -691,10 +729,6 @@ void WebContents::InspectServiceWorker() { } } -v8::Local WebContents::Session(v8::Isolate* isolate) { - return v8::Local::New(isolate, session_); -} - void WebContents::HasServiceWorker( const base::Callback& callback) { auto context = GetServiceWorkerContext(web_contents()); @@ -880,6 +914,30 @@ bool WebContents::IsGuest() const { return type_ == WEB_VIEW; } +v8::Local WebContents::GetWebPreferences(v8::Isolate* isolate) { + WebContentsPreferences* web_preferences = + WebContentsPreferences::FromWebContents(web_contents()); + return mate::ConvertToV8(isolate, *web_preferences->web_preferences()); +} + +v8::Local WebContents::GetOwnerBrowserWindow() { + if (owner_window()) + return Window::From(isolate(), owner_window()); + else + return v8::Null(isolate()); +} + +v8::Local WebContents::Session(v8::Isolate* isolate) { + return v8::Local::New(isolate, session_); +} + +v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { + if (devtools_web_contents_.IsEmpty()) + return v8::Null(isolate); + else + return v8::Local::New(isolate, devtools_web_contents_); +} + mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( v8::Isolate* isolate) { if (template_.IsEmpty()) @@ -935,6 +993,8 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( .SetMethod("setSize", &WebContents::SetSize) .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) .SetMethod("isGuest", &WebContents::IsGuest) + .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) + .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) .SetMethod("unregisterServiceWorker", &WebContents::UnregisterServiceWorker) @@ -943,7 +1003,9 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( .SetMethod("_printToPDF", &WebContents::PrintToPDF) .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) - .SetProperty("session", &WebContents::Session) + .SetProperty("session", &WebContents::Session, true) + .SetProperty("devToolsWebContents", + &WebContents::DevToolsWebContents, true) .Build()); return mate::ObjectTemplateBuilder( @@ -988,7 +1050,7 @@ mate::Handle WebContents::CreateFrom( // static mate::Handle WebContents::Create( v8::Isolate* isolate, const mate::Dictionary& options) { - auto handle = mate::CreateHandle(isolate, new WebContents(isolate, options)); + auto handle = mate::CreateHandle(isolate, new WebContents(isolate, options)); g_wrap_web_contents.Run(handle.ToV8()); return handle; } diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index c8ea6908bc5b..bbf331848c55 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -83,7 +83,6 @@ class WebContents : public mate::TrackableObject, void DisableDeviceEmulation(); void InspectElement(int x, int y); void InspectServiceWorker(); - v8::Local Session(v8::Isolate* isolate); void HasServiceWorker(const base::Callback&); void UnregisterServiceWorker(const base::Callback&); void SetAudioMuted(bool muted); @@ -132,6 +131,16 @@ class WebContents : public mate::TrackableObject, void SetAllowTransparency(bool allow); bool IsGuest() const; + // Returns the web preferences of current WebContents. + v8::Local GetWebPreferences(v8::Isolate* isolate); + + // Returns the owner window. + v8::Local GetOwnerBrowserWindow(); + + // Properties. + v8::Local Session(v8::Isolate* isolate); + v8::Local DevToolsWebContents(v8::Isolate* isolate); + protected: explicit WebContents(content::WebContents* web_contents); WebContents(v8::Isolate* isolate, const mate::Dictionary& options); @@ -215,6 +224,11 @@ class WebContents : public mate::TrackableObject, void PluginCrashed(const base::FilePath& plugin_path, base::ProcessId plugin_pid) override; + // brightray::InspectableWebContentsViewDelegate: + void DevToolsFocused() override; + void DevToolsOpened() override; + void DevToolsClosed() override; + private: enum Type { BROWSER_WINDOW, // Used by BrowserWindow. @@ -234,6 +248,7 @@ class WebContents : public mate::TrackableObject, IPC::Message* message); v8::Global session_; + v8::Global devtools_web_contents_; scoped_ptr guest_delegate_; diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 3a44115da264..c4beaace6728 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -82,12 +82,17 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { web_contents_.Reset(isolate, web_contents.ToV8()); api_web_contents_ = web_contents.get(); + // Keep a copy of the options for later use. + mate::Dictionary(isolate, web_contents->GetWrapper(isolate)).Set( + "browserWindowOptions", options); + // Creates BrowserWindow. window_.reset(NativeWindow::Create(web_contents->managed_web_contents(), options)); web_contents->SetOwnerWindow(window_.get()); window_->InitFromOptions(options); window_->AddObserver(this); + AttachAsUserData(window_.get()); } Window::~Window() { @@ -180,28 +185,6 @@ void Window::OnRendererResponsive() { Emit("responsive"); } -void Window::OnDevToolsFocus() { - Emit("devtools-focused"); -} - -void Window::OnDevToolsOpened() { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - auto handle = WebContents::CreateFrom( - isolate(), api_web_contents_->GetDevToolsWebContents()); - devtools_web_contents_.Reset(isolate(), handle.ToV8()); - - Emit("devtools-opened"); -} - -void Window::OnDevToolsClosed() { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - devtools_web_contents_.Reset(); - - Emit("devtools-closed"); -} - void Window::OnExecuteWindowsCommand(const std::string& command_name) { Emit("app-command", command_name); } @@ -536,13 +519,6 @@ v8::Local Window::WebContents(v8::Isolate* isolate) { return v8::Local::New(isolate, web_contents_); } -v8::Local Window::DevToolsWebContents(v8::Isolate* isolate) { - if (devtools_web_contents_.IsEmpty()) - return v8::Null(isolate); - else - return v8::Local::New(isolate, devtools_web_contents_); -} - // static void Window::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { @@ -614,8 +590,17 @@ void Window::BuildPrototype(v8::Isolate* isolate, &Window::ShowDefinitionForSelection) #endif .SetProperty("id", &Window::ID, true) - .SetProperty("webContents", &Window::WebContents, true) - .SetProperty("devToolsWebContents", &Window::DevToolsWebContents, true); + .SetProperty("webContents", &Window::WebContents, true); +} + +// static +v8::Local Window::From(v8::Isolate* isolate, + NativeWindow* native_window) { + auto existing = TrackableObject::FromWrappedClass(isolate, native_window); + if (existing) + return existing->GetWrapper(isolate); + else + return v8::Null(isolate); } } // namespace api diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d60bf0d87ea0..d2886b5fac94 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -43,6 +43,10 @@ class Window : public mate::TrackableObject, static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); + // Returns the BrowserWindow object from |native_window|. + static v8::Local From(v8::Isolate* isolate, + NativeWindow* native_window); + NativeWindow* window() const { return window_.get(); } protected: @@ -69,9 +73,6 @@ class Window : public mate::TrackableObject, void OnWindowLeaveHtmlFullScreen() override; void OnRendererUnresponsive() override; void OnRendererResponsive() override; - void OnDevToolsFocus() override; - void OnDevToolsOpened() override; - void OnDevToolsClosed() override; void OnExecuteWindowsCommand(const std::string& command_name) override; // mate::Wrappable: @@ -150,10 +151,8 @@ class Window : public mate::TrackableObject, int32_t ID() const; v8::Local WebContents(v8::Isolate* isolate); - v8::Local DevToolsWebContents(v8::Isolate* isolate); v8::Global web_contents_; - v8::Global devtools_web_contents_; v8::Global menu_; api::WebContents* api_web_contents_; diff --git a/atom/browser/api/lib/app.coffee b/atom/browser/api/lib/app.coffee index f5b137eb0bb2..18c80dc2b1f4 100644 --- a/atom/browser/api/lib/app.coffee +++ b/atom/browser/api/lib/app.coffee @@ -2,6 +2,7 @@ EventEmitter = require('events').EventEmitter bindings = process.atomBinding 'app' sessionBindings = process.atomBinding 'session' +downloadItemBindings = process.atomBinding 'download_item' app = bindings.app app.__proto__ = EventEmitter.prototype @@ -10,6 +11,15 @@ wrapSession = (session) -> # session is an Event Emitter. session.__proto__ = EventEmitter.prototype +wrapDownloadItem = (download_item) -> + # download_item is an Event Emitter. + download_item.__proto__ = EventEmitter.prototype + # Be compatible with old APIs. + download_item.url = download_item.getUrl() + download_item.filename = download_item.getFilename() + download_item.mimeType = download_item.getMimeType() + download_item.hasUserGesture = download_item.hasUserGesture() + app.setApplicationMenu = (menu) -> require('menu').setApplicationMenu menu @@ -51,5 +61,8 @@ app.on 'activate', (event, hasVisibleWindows) -> @emit 'activate-with-no-open-wi sessionBindings._setWrapSession wrapSession process.once 'exit', sessionBindings._clearWrapSession +downloadItemBindings._setWrapDownloadItem wrapDownloadItem +process.once 'exit', downloadItemBindings._clearWrapDownloadItem + # Only one App object pemitted. module.exports = app diff --git a/atom/browser/api/lib/browser-window.coffee b/atom/browser/api/lib/browser-window.coffee index 2a92cfc55f88..6ffba50d34cb 100644 --- a/atom/browser/api/lib/browser-window.coffee +++ b/atom/browser/api/lib/browser-window.coffee @@ -48,6 +48,15 @@ BrowserWindow::_init = -> # Notify the creation of the window. app.emit 'browser-window-created', {}, this + # Be compatible with old APIs. + @webContents.on 'devtools-focused', => @emit 'devtools-focused' + @webContents.on 'devtools-opened', => @emit 'devtools-opened' + @webContents.on 'devtools-closed', => @emit 'devtools-closed' + Object.defineProperty this, 'devToolsWebContents', + enumerable: true, + configurable: false, + get: -> @webContents.devToolsWebContents + BrowserWindow.getFocusedWindow = -> windows = BrowserWindow.getAllWindows() return window for window in windows when window.isFocused() diff --git a/atom/browser/api/lib/menu.coffee b/atom/browser/api/lib/menu.coffee index 3595ba1fe7db..f66c1568c003 100644 --- a/atom/browser/api/lib/menu.coffee +++ b/atom/browser/api/lib/menu.coffee @@ -79,7 +79,11 @@ Menu::_init = -> v8Util.setHiddenValue group[0], 'checked', true unless checked Menu::popup = (window, x, y) -> - throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow + unless window?.constructor is BrowserWindow + # Shift. + y = x + x = window + window = BrowserWindow.getFocusedWindow() if x? and y? @_popupAt(window, x, y) else diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index e45caceab01e..4969ce47a679 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -12,6 +12,7 @@ #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_quota_permission_context.h" +#include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/atom_speech_recognition_manager_delegate.h" #include "atom/browser/browser.h" #include "atom/browser/native_window.h" @@ -30,6 +31,7 @@ #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/web_preferences.h" @@ -226,6 +228,13 @@ void AtomBrowserClient::SelectClientCertificate( delegate.Pass()); } +void AtomBrowserClient::ResourceDispatcherHostCreated() { + resource_dispatcher_host_delegate_.reset( + new AtomResourceDispatcherHostDelegate); + content::ResourceDispatcherHost::Get()->SetDelegate( + resource_dispatcher_host_delegate_.get()); +} + brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( const content::MainFunctionParams&) { v8::V8::Initialize(); // Init V8 before creating main parts. diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index a0217efede9f..ee4700456cc6 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -23,6 +23,8 @@ class SSLCertRequestInfo; namespace atom { +class AtomResourceDispatcherHostDelegate; + class AtomBrowserClient : public brightray::BrowserClient, public content::RenderProcessHostObserver { public: @@ -56,6 +58,7 @@ class AtomBrowserClient : public brightray::BrowserClient, content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, scoped_ptr delegate) override; + void ResourceDispatcherHostCreated() override; // brightray::BrowserClient: brightray::BrowserMainParts* OverrideCreateBrowserMainParts( @@ -68,6 +71,9 @@ class AtomBrowserClient : public brightray::BrowserClient, // pending_render_process => current_render_process. std::map pending_processes_; + scoped_ptr + resource_dispatcher_host_delegate_; + DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient); }; diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index d1ef09e70ff6..6823fbaee90c 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -6,6 +6,7 @@ #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_download_manager_delegate.h" +#include "atom/browser/atom_ssl_config_service.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" @@ -156,6 +157,10 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { return guest_manager_.get(); } +net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() { + return new AtomSSLConfigService; +} + void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory, base::FilePath()); diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index c99461ad9a86..839359c1ef50 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -27,6 +27,7 @@ class AtomBrowserContext : public brightray::BrowserContext { content::URLRequestInterceptorScopedVector* interceptors) override; net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory( const base::FilePath& base_path) override; + net::SSLConfigService* CreateSSLConfigService() override; // content::BrowserContext: content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index a1a1192b2768..4c11176997ca 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -52,13 +52,16 @@ void AtomBrowserMainParts::RegisterDestructionCallback( destruction_callbacks_.push_back(callback); } +void AtomBrowserMainParts::PreEarlyInitialization() { + brightray::BrowserMainParts::PreEarlyInitialization(); +#if defined(OS_POSIX) + HandleSIGCHLD(); +#endif +} + void AtomBrowserMainParts::PostEarlyInitialization() { brightray::BrowserMainParts::PostEarlyInitialization(); -#if defined(USE_X11) - SetDPIFromGSettings(); -#endif - { // Temporary set the bridge_task_runner_ as current thread's task runner, // so we can fool gin::PerIsolateData to use it as its task runner, instead @@ -116,6 +119,13 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { #endif } +void AtomBrowserMainParts::PostMainMessageLoopStart() { + brightray::BrowserMainParts::PostMainMessageLoopStart(); +#if defined(OS_POSIX) + HandleShutdownSignals(); +#endif +} + void AtomBrowserMainParts::PostMainMessageLoopRun() { brightray::BrowserMainParts::PostMainMessageLoopRun(); diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index bcebc86f16ca..65b142157dc1 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -39,8 +39,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { protected: // content::BrowserMainParts: + void PreEarlyInitialization() override; void PostEarlyInitialization() override; void PreMainMessageLoopRun() override; + void PostMainMessageLoopStart() override; void PostMainMessageLoopRun() override; #if defined(OS_MACOSX) void PreMainMessageLoopStart() override; @@ -48,8 +50,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { #endif private: -#if defined(USE_X11) - void SetDPIFromGSettings(); +#if defined(OS_POSIX) + // Set signal handlers. + void HandleSIGCHLD(); + void HandleShutdownSignals(); #endif // A fake BrowserProcess object that used to feed the source code from chrome. diff --git a/atom/browser/atom_browser_main_parts_linux.cc b/atom/browser/atom_browser_main_parts_linux.cc deleted file mode 100644 index 278e49ac1f1e..000000000000 --- a/atom/browser/atom_browser_main_parts_linux.cc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/atom_browser_main_parts.h" - -#include - -#include "base/command_line.h" -#include "base/strings/string_number_conversions.h" -#include "ui/gfx/switches.h" - -namespace atom { - -namespace { - -const char* kInterfaceSchema = "org.gnome.desktop.interface"; -const char* kScaleFactor = "scaling-factor"; - -bool SchemaExists(const char* schema_name) { - const gchar* const* schemas = g_settings_list_schemas(); - while (*schemas) { - if (strcmp(schema_name, static_cast(*schemas)) == 0) - return true; - schemas++; - } - return false; -} - -bool KeyExists(GSettings* client, const char* key) { - gchar** keys = g_settings_list_keys(client); - if (!keys) - return false; - - gchar** iter = keys; - while (*iter) { - if (strcmp(*iter, key) == 0) - break; - iter++; - } - - bool exists = *iter != NULL; - g_strfreev(keys); - return exists; -} - -void GetDPIFromGSettings(guint* scale_factor) { - GSettings* client = nullptr; - if (!SchemaExists(kInterfaceSchema) || - !(client = g_settings_new(kInterfaceSchema))) { - VLOG(1) << "Cannot create gsettings client."; - return; - } - - if (KeyExists(client, kScaleFactor)) - *scale_factor = g_settings_get_uint(client, kScaleFactor); - - g_object_unref(client); -} - -} // namespace - -void AtomBrowserMainParts::SetDPIFromGSettings() { - guint scale_factor = 1; - GetDPIFromGSettings(&scale_factor); - if (scale_factor == 0) - scale_factor = 1; - - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kForceDeviceScaleFactor, base::UintToString(scale_factor)); -} - -} // namespace atom diff --git a/atom/browser/atom_browser_main_parts_posix.cc b/atom/browser/atom_browser_main_parts_posix.cc new file mode 100644 index 000000000000..2a0dddc47483 --- /dev/null +++ b/atom/browser/atom_browser_main_parts_posix.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// Most code came from: chrome/browser/chrome_browser_main_posix.cc. + +#include "atom/browser/atom_browser_main_parts.h" + +#include +#include +#include +#include +#include +#include + +#include "atom/browser/browser.h" +#include "base/posix/eintr_wrapper.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace atom { + +namespace { + +// See comment in |PreEarlyInitialization()|, where sigaction is called. +void SIGCHLDHandler(int signal) { +} + +// The OSX fork() implementation can crash in the child process before +// fork() returns. In that case, the shutdown pipe will still be +// shared with the parent process. To prevent child crashes from +// causing parent shutdowns, |g_pipe_pid| is the pid for the process +// which registered |g_shutdown_pipe_write_fd|. +// See . +pid_t g_pipe_pid = -1; +int g_shutdown_pipe_write_fd = -1; +int g_shutdown_pipe_read_fd = -1; + +// Common code between SIG{HUP, INT, TERM}Handler. +void GracefulShutdownHandler(int signal) { + // Reinstall the default handler. We had one shot at graceful shutdown. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + RAW_CHECK(sigaction(signal, &action, NULL) == 0); + + RAW_CHECK(g_pipe_pid == getpid()); + RAW_CHECK(g_shutdown_pipe_write_fd != -1); + RAW_CHECK(g_shutdown_pipe_read_fd != -1); + size_t bytes_written = 0; + do { + int rv = HANDLE_EINTR( + write(g_shutdown_pipe_write_fd, + reinterpret_cast(&signal) + bytes_written, + sizeof(signal) - bytes_written)); + RAW_CHECK(rv >= 0); + bytes_written += rv; + } while (bytes_written < sizeof(signal)); +} + +// See comment in |PostMainMessageLoopStart()|, where sigaction is called. +void SIGHUPHandler(int signal) { + RAW_CHECK(signal == SIGHUP); + GracefulShutdownHandler(signal); +} + +// See comment in |PostMainMessageLoopStart()|, where sigaction is called. +void SIGINTHandler(int signal) { + RAW_CHECK(signal == SIGINT); + GracefulShutdownHandler(signal); +} + +// See comment in |PostMainMessageLoopStart()|, where sigaction is called. +void SIGTERMHandler(int signal) { + RAW_CHECK(signal == SIGTERM); + GracefulShutdownHandler(signal); +} + +class ShutdownDetector : public base::PlatformThread::Delegate { + public: + explicit ShutdownDetector(int shutdown_fd); + + void ThreadMain() override; + + private: + const int shutdown_fd_; + + DISALLOW_COPY_AND_ASSIGN(ShutdownDetector); +}; + +ShutdownDetector::ShutdownDetector(int shutdown_fd) + : shutdown_fd_(shutdown_fd) { + CHECK_NE(shutdown_fd_, -1); +} + +// These functions are used to help us diagnose crash dumps that happen +// during the shutdown process. +NOINLINE void ShutdownFDReadError() { + // Ensure function isn't optimized away. + asm(""); + sleep(UINT_MAX); +} + +NOINLINE void ShutdownFDClosedError() { + // Ensure function isn't optimized away. + asm(""); + sleep(UINT_MAX); +} + +NOINLINE void ExitPosted() { + // Ensure function isn't optimized away. + asm(""); + sleep(UINT_MAX); +} + +void ShutdownDetector::ThreadMain() { + base::PlatformThread::SetName("CrShutdownDetector"); + + int signal; + size_t bytes_read = 0; + ssize_t ret; + do { + ret = HANDLE_EINTR( + read(shutdown_fd_, + reinterpret_cast(&signal) + bytes_read, + sizeof(signal) - bytes_read)); + if (ret < 0) { + NOTREACHED() << "Unexpected error: " << strerror(errno); + ShutdownFDReadError(); + break; + } else if (ret == 0) { + NOTREACHED() << "Unexpected closure of shutdown pipe."; + ShutdownFDClosedError(); + break; + } + bytes_read += ret; + } while (bytes_read < sizeof(signal)); + VLOG(1) << "Handling shutdown for signal " << signal << "."; + base::Closure task = + base::Bind(&Browser::Quit, base::Unretained(Browser::Get())); + + if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task)) { + // Without a UI thread to post the exit task to, there aren't many + // options. Raise the signal again. The default handler will pick it up + // and cause an ungraceful exit. + RAW_LOG(WARNING, "No UI thread, exiting ungracefully."); + kill(getpid(), signal); + + // The signal may be handled on another thread. Give that a chance to + // happen. + sleep(3); + + // We really should be dead by now. For whatever reason, we're not. Exit + // immediately, with the exit status set to the signal number with bit 8 + // set. On the systems that we care about, this exit status is what is + // normally used to indicate an exit by this signal's default handler. + // This mechanism isn't a de jure standard, but even in the worst case, it + // should at least result in an immediate exit. + RAW_LOG(WARNING, "Still here, exiting really ungracefully."); + _exit(signal | (1 << 7)); + } + ExitPosted(); +} + +} // namespace + +void AtomBrowserMainParts::HandleSIGCHLD() { + // We need to accept SIGCHLD, even though our handler is a no-op because + // otherwise we cannot wait on children. (According to POSIX 2001.) + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIGCHLDHandler; + CHECK_EQ(sigaction(SIGCHLD, &action, NULL), 0); +} + +void AtomBrowserMainParts::HandleShutdownSignals() { + int pipefd[2]; + int ret = pipe(pipefd); + if (ret < 0) { + PLOG(DFATAL) << "Failed to create pipe"; + } else { + g_pipe_pid = getpid(); + g_shutdown_pipe_read_fd = pipefd[0]; + g_shutdown_pipe_write_fd = pipefd[1]; +#if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS) + const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 2; +#else + // ASan instrumentation and -finstrument-functions (used for keeping the + // shadow stacks) bloat the stack frames, so we need to increase the stack + // size to avoid hitting the guard page. + const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 4; +#endif + // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so + // if you change this, you'll probably need to change the suppression. + if (!base::PlatformThread::CreateNonJoinable( + kShutdownDetectorThreadStackSize, + new ShutdownDetector(g_shutdown_pipe_read_fd))) { + LOG(DFATAL) << "Failed to create shutdown detector task."; + } + } + // Setup signal handlers for shutdown AFTER shutdown pipe is setup because + // it may be called right away after handler is set. + + // If adding to this list of signal handlers, note the new signal probably + // needs to be reset in child processes. See + // base/process_util_posix.cc:LaunchProcess. + + // We need to handle SIGTERM, because that is how many POSIX-based distros ask + // processes to quit gracefully at shutdown time. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIGTERMHandler; + CHECK_EQ(sigaction(SIGTERM, &action, NULL), 0); + // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If + // the browser process is being debugged, GDB will catch the SIGINT first. + action.sa_handler = SIGINTHandler; + CHECK_EQ(sigaction(SIGINT, &action, NULL), 0); + // And SIGHUP, for when the terminal disappears. On shutdown, many Linux + // distros send SIGHUP, SIGTERM, and then SIGKILL. + action.sa_handler = SIGHUPHandler; + CHECK_EQ(sigaction(SIGHUP, &action, NULL), 0); +} + +} // namespace atom diff --git a/atom/browser/atom_download_manager_delegate.cc b/atom/browser/atom_download_manager_delegate.cc index b573a396332f..b6b656682554 100644 --- a/atom/browser/atom_download_manager_delegate.cc +++ b/atom/browser/atom_download_manager_delegate.cc @@ -6,6 +6,7 @@ #include +#include "atom/browser/api/atom_api_download_item.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/file_dialog.h" @@ -73,18 +74,19 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( if (relay) window = relay->window.get(); - file_dialog::Filters filters; base::FilePath path; - if (!file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path, - filters, &path)) { - return; + if (file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path, + file_dialog::Filters(), &path)) { + // Remember the last selected download directory. + AtomBrowserContext* browser_context = static_cast( + download_manager_->GetBrowserContext()); + browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory, + path.DirName()); } - // Remeber the last selected download directory. - AtomBrowserContext* browser_context = static_cast( - download_manager_->GetBrowserContext()); - browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory, - path.DirName()); + // Running the DownloadTargetCallback with an empty FilePath signals that the + // download should be cancelled. + // If user cancels the file save dialog, run the callback with empty FilePath. callback.Run(path, content::DownloadItem::TARGET_DISPOSITION_PROMPT, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path); @@ -100,6 +102,25 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget( const content::DownloadTargetCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!download->GetForcedFilePath().empty()) { + callback.Run(download->GetForcedFilePath(), + content::DownloadItem::TARGET_DISPOSITION_OVERWRITE, + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + download->GetForcedFilePath()); + return true; + } + base::SupportsUserData::Data* save_path = download->GetUserData( + atom::api::DownloadItem::UserDataKey()); + if (save_path) { + const base::FilePath& default_download_path = + static_cast(save_path)->path(); + callback.Run(default_download_path, + content::DownloadItem::TARGET_DISPOSITION_OVERWRITE, + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + default_download_path); + return true; + } + AtomBrowserContext* browser_context = static_cast( download_manager_->GetBrowserContext()); base::FilePath default_download_path = browser_context->prefs()->GetFilePath( @@ -110,14 +131,6 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget( default_download_path = path.Append(FILE_PATH_LITERAL("Downloads")); } - if (!download->GetForcedFilePath().empty()) { - callback.Run(download->GetForcedFilePath(), - content::DownloadItem::TARGET_DISPOSITION_OVERWRITE, - content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - download->GetForcedFilePath()); - return true; - } - CreateDownloadPathCallback download_path_callback = base::Bind(&AtomDownloadManagerDelegate::OnDownloadPathGenerated, weak_ptr_factory_.GetWeakPtr(), diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc new file mode 100644 index 000000000000..46904d2ff99d --- /dev/null +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -0,0 +1,32 @@ +// 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/atom_resource_dispatcher_host_delegate.h" + +#include "atom/common/platform_util.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/escape.h" +#include "url/gurl.h" + +using content::BrowserThread; + +namespace atom { + +AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() { +} + +bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol( + const GURL& url, + int render_process_id, + int render_view_id, + bool is_main_frame, + ui::PageTransition transition, + bool has_user_gesture) { + GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(platform_util::OpenExternal), escaped_url)); + return true; +} + +} // namespace atom diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.h b/atom/browser/atom_resource_dispatcher_host_delegate.h new file mode 100644 index 000000000000..876554f0f964 --- /dev/null +++ b/atom/browser/atom_resource_dispatcher_host_delegate.h @@ -0,0 +1,28 @@ +// 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_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#define ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ + +#include "content/public/browser/resource_dispatcher_host_delegate.h" + +namespace atom { + +class AtomResourceDispatcherHostDelegate + : public content::ResourceDispatcherHostDelegate { + public: + AtomResourceDispatcherHostDelegate(); + + // content::ResourceDispatcherHostDelegate: + bool HandleExternalProtocol(const GURL& url, + int render_process_id, + int render_view_id, + bool is_main_frame, + ui::PageTransition transition, + bool has_user_gesture) override; +}; + +} // namespace atom + +#endif // ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ diff --git a/atom/browser/atom_ssl_config_service.cc b/atom/browser/atom_ssl_config_service.cc new file mode 100644 index 000000000000..f19dbacf7dd3 --- /dev/null +++ b/atom/browser/atom_ssl_config_service.cc @@ -0,0 +1,47 @@ +// 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/atom_ssl_config_service.h" + +#include + +#include "base/command_line.h" +#include "atom/common/options_switches.h" +#include "content/public/browser/browser_thread.h" +#include "net/socket/ssl_client_socket.h" + +namespace atom { + +namespace { + +uint16 GetSSLProtocolVersion(const std::string& version_string) { + uint16 version = 0; // Invalid + if (version_string == "tls1") + version = net::SSL_PROTOCOL_VERSION_TLS1; + else if (version_string == "tls1.1") + version = net::SSL_PROTOCOL_VERSION_TLS1_1; + else if (version_string == "tls1.2") + version = net::SSL_PROTOCOL_VERSION_TLS1_2; + return version; +} + +} // namespace + +AtomSSLConfigService::AtomSSLConfigService() { + auto cmd_line = base::CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(switches::kSSLVersionFallbackMin)) { + auto version_string = + cmd_line->GetSwitchValueASCII(switches::kSSLVersionFallbackMin); + config_.version_fallback_min = GetSSLProtocolVersion(version_string); + } +} + +AtomSSLConfigService::~AtomSSLConfigService() { +} + +void AtomSSLConfigService::GetSSLConfig(net::SSLConfig* config) { + *config = config_; +} + +} // namespace atom diff --git a/atom/browser/atom_ssl_config_service.h b/atom/browser/atom_ssl_config_service.h new file mode 100644 index 000000000000..3fa91c62c438 --- /dev/null +++ b/atom/browser/atom_ssl_config_service.h @@ -0,0 +1,28 @@ +// 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_ATOM_SSL_CONFIG_SERVICE_H_ +#define ATOM_BROWSER_ATOM_SSL_CONFIG_SERVICE_H_ + +#include "net/ssl/ssl_config_service.h" + +namespace atom { + +class AtomSSLConfigService : public net::SSLConfigService { + public: + AtomSSLConfigService(); + ~AtomSSLConfigService() override; + + // net::SSLConfigService: + void GetSSLConfig(net::SSLConfig* config) override; + + private: + net::SSLConfig config_; + + DISALLOW_COPY_AND_ASSIGN(AtomSSLConfigService); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_ATOM_SSL_CONFIG_SERVICE_H_ diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 9d2a9fc1effb..d8bb94103cd9 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -16,7 +16,8 @@ namespace atom { Browser::Browser() : is_quiting_(false), - is_ready_(false) { + is_ready_(false), + is_shutdown_(false) { WindowList::AddObserver(this); } @@ -30,6 +31,9 @@ Browser* Browser::Get() { } void Browser::Quit() { + if (is_quiting_) + return; + is_quiting_ = HandleBeforeQuit(); if (!is_quiting_) return; @@ -42,9 +46,13 @@ void Browser::Quit() { } void Browser::Shutdown() { - FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit()); + if (is_shutdown_) + return; + is_shutdown_ = true; is_quiting_ = true; + + FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit()); base::MessageLoop::current()->PostTask( FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); } @@ -121,6 +129,9 @@ void Browser::ClientCertificateSelector( } void Browser::NotifyAndShutdown() { + if (is_shutdown_) + return; + bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillQuit(&prevent_default)); diff --git a/atom/browser/browser.h b/atom/browser/browser.h index d135556b8760..3c5abd2f0405 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -159,6 +159,9 @@ class Browser : public WindowListObserver { // Whether "ready" event has been emitted. bool is_ready_; + // The browse is being shutdown. + bool is_shutdown_; + std::string version_override_; std::string name_override_; diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 3cef7c6d68cc..8b7a159dd7d6 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -21,6 +21,14 @@ #include "content/public/browser/render_view_host.h" #include "storage/browser/fileapi/isolated_context.h" +#if defined(TOOLKIT_VIEWS) +#include "atom/browser/native_window_views.h" +#endif + +#if defined(USE_X11) +#include "atom/browser/browser.h" +#endif + using content::BrowserThread; namespace atom { @@ -128,7 +136,11 @@ void CommonWebContentsDelegate::InitWithWebContents( } void CommonWebContentsDelegate::SetOwnerWindow(NativeWindow* owner_window) { - content::WebContents* web_contents = GetWebContents(); + SetOwnerWindow(GetWebContents(), owner_window); +} + +void CommonWebContentsDelegate::SetOwnerWindow( + content::WebContents* web_contents, NativeWindow* owner_window) { owner_window_ = owner_window->GetWeakPtr(); NativeWindowRelay* relay = new NativeWindowRelay(owner_window_); web_contents->SetUserData(relay->key, relay); @@ -355,6 +367,23 @@ void CommonWebContentsDelegate::OnDevToolsAppendToFile( "DevToolsAPI.appendedToURL", &url_value, nullptr, nullptr); } +#if defined(TOOLKIT_VIEWS) +gfx::ImageSkia CommonWebContentsDelegate::GetDevToolsWindowIcon() { + if (!owner_window()) + return gfx::ImageSkia(); + return static_cast(static_cast( + owner_window()))->GetWindowAppIcon(); +} +#endif + +#if defined(USE_X11) +void CommonWebContentsDelegate::GetDevToolsWindowWMClass( + std::string* name, std::string* class_name) { + *class_name = Browser::Get()->GetName(); + *name = base::StringToLowerASCII(*class_name); +} +#endif + void CommonWebContentsDelegate::SetHtmlApiFullscreen(bool enter_fullscreen) { // Window is already in fullscreen mode, save the state. if (enter_fullscreen && owner_window_->IsFullscreen()) { diff --git a/atom/browser/common_web_contents_delegate.h b/atom/browser/common_web_contents_delegate.h index 495b5501a0d1..ee18f36660ee 100644 --- a/atom/browser/common_web_contents_delegate.h +++ b/atom/browser/common_web_contents_delegate.h @@ -12,6 +12,7 @@ #include "brightray/browser/default_web_contents_delegate.h" #include "brightray/browser/inspectable_web_contents_impl.h" #include "brightray/browser/inspectable_web_contents_delegate.h" +#include "brightray/browser/inspectable_web_contents_view_delegate.h" namespace atom { @@ -21,7 +22,8 @@ class WebDialogHelper; class CommonWebContentsDelegate : public brightray::DefaultWebContentsDelegate, - public brightray::InspectableWebContentsDelegate { + public brightray::InspectableWebContentsDelegate, + public brightray::InspectableWebContentsViewDelegate { public: CommonWebContentsDelegate(); virtual ~CommonWebContentsDelegate(); @@ -32,6 +34,8 @@ class CommonWebContentsDelegate // Set the window as owner window. void SetOwnerWindow(NativeWindow* owner_window); + void SetOwnerWindow(content::WebContents* web_contents, + NativeWindow* owner_window); // Destroy the managed InspectableWebContents object. void DestroyWebContents(); @@ -86,6 +90,15 @@ class CommonWebContentsDelegate void DevToolsRemoveFileSystem( const base::FilePath& file_system_path) override; + // brightray::InspectableWebContentsViewDelegate: +#if defined(TOOLKIT_VIEWS) + gfx::ImageSkia GetDevToolsWindowIcon() override; +#endif +#if defined(USE_X11) + void GetDevToolsWindowWMClass( + std::string* name, std::string* class_name) override; +#endif + private: // Callback for when DevToolsSaveToFile has completed. void OnDevToolsSaveToFile(const std::string& url); diff --git a/atom/browser/default_app/main.js b/atom/browser/default_app/main.js index 4d854dc35cbc..1b92685d13a2 100644 --- a/atom/browser/default_app/main.js +++ b/atom/browser/default_app/main.js @@ -13,8 +13,8 @@ app.on('window-all-closed', function() { // Parse command line options. var argv = process.argv.slice(1); -var option = { file: null, help: null, version: null, webdriver: null }; -for (var i in argv) { +var option = { file: null, help: null, version: null, webdriver: null, modules: [] }; +for (var i = 0; i < argv.length; i++) { if (argv[i] == '--version' || argv[i] == '-v') { option.version = true; break; @@ -23,6 +23,9 @@ for (var i in argv) { break; } else if (argv[i] == '--test-type=webdriver') { option.webdriver = true; + } else if (argv[i] == '--require' || argv[i] == '-r') { + option.modules.push(argv[++i]); + continue; } else if (argv[i][0] == '-') { continue; } else { @@ -212,6 +215,10 @@ app.once('ready', function() { Menu.setApplicationMenu(menu); }); +if (option.modules.length > 0) { + require('module')._preloadModules(option.modules); +} + // Start the specified app if there is one specified in command line, otherwise // start the default app. if (option.file && !option.webdriver) { @@ -253,6 +260,7 @@ if (option.file && !option.webdriver) { helpMessage += "A path to an Electron application may be specified. The path must be to \n"; helpMessage += "an index.js file or to a folder containing a package.json or index.js file.\n\n"; helpMessage += "Options:\n"; + helpMessage += " -r, --require Module to preload (option can be repeated)"; helpMessage += " -h, --help Print this usage message.\n"; helpMessage += " -v, --version Print the version."; console.log(helpMessage); diff --git a/atom/browser/lib/guest-view-manager.coffee b/atom/browser/lib/guest-view-manager.coffee index 1c05b65b2dc0..455e969812f7 100644 --- a/atom/browser/lib/guest-view-manager.coffee +++ b/atom/browser/lib/guest-view-manager.coffee @@ -84,6 +84,8 @@ createGuest = (embedder, params) -> if params.allowtransparency? @setAllowTransparency params.allowtransparency + guest.allowPopups = params.allowpopups + # Dispatch events to embedder. for event in supportedWebViewEvents do (event) -> diff --git a/atom/browser/lib/guest-window-manager.coffee b/atom/browser/lib/guest-window-manager.coffee index 861b0d10aa31..9a5c0ca349ae 100644 --- a/atom/browser/lib/guest-window-manager.coffee +++ b/atom/browser/lib/guest-window-manager.coffee @@ -4,6 +4,17 @@ BrowserWindow = require 'browser-window' frameToGuest = {} +# Merge |options| with the |embedder|'s window's options. +mergeBrowserWindowOptions = (embedder, options) -> + if embedder.browserWindowOptions? + # Inherit the original options if it is a BrowserWindow. + options.__proto__ = embedder.browserWindowOptions + else + # Or only inherit web-preferences if it is a webview. + options['web-preferences'] ?= {} + options['web-preferences'].__proto__ = embedder.getWebPreferences() + options + # Create a new guest created by |embedder| with |options|. createGuest = (embedder, url, frameName, options) -> guest = frameToGuest[frameName] @@ -40,11 +51,12 @@ createGuest = (embedder, url, frameName, options) -> # Routed window.open messages. ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) -> [url, frameName, options] = args - event.sender.emit 'new-window', event, url, frameName, 'new-window' - if event.sender.isGuest() or event.defaultPrevented + options = mergeBrowserWindowOptions event.sender, options + event.sender.emit 'new-window', event, url, frameName, 'new-window', options + if (event.sender.isGuest() and not event.sender.allowPopups) or event.defaultPrevented event.returnValue = null else - event.returnValue = createGuest event.sender, args... + event.returnValue = createGuest event.sender, url, frameName, options ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) -> BrowserWindow.fromId(guestId)?.destroy() diff --git a/atom/browser/lib/init.coffee b/atom/browser/lib/init.coffee index 1299364d2fa6..b394c0fecc28 100644 --- a/atom/browser/lib/init.coffee +++ b/atom/browser/lib/init.coffee @@ -7,14 +7,17 @@ Module = require 'module' # we need to restore it here. process.argv.splice 1, 1 +# Clear search paths. +require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths') + +# Import common settings. +require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init') + # Add browser/api/lib to module search paths, which contains javascript part of # Electron's built-in libraries. globalPaths = Module.globalPaths globalPaths.push path.resolve(__dirname, '..', 'api', 'lib') -# Import common settings. -require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init') - if process.platform is 'win32' # Redirect node's console to use our own implementations, since node can not # handle console output when running as GUI program. @@ -64,7 +67,9 @@ for packagePath in searchPaths catch e continue -throw new Error("Unable to find a valid app") unless packageJson? +unless packageJson? + process.nextTick -> process.exit 1 + throw new Error("Unable to find a valid app") # Set application's version. app.setVersion packageJson.version if packageJson.version? diff --git a/atom/browser/lib/objects-registry.coffee b/atom/browser/lib/objects-registry.coffee index ccfe2dbe0ad2..667adbc24e9e 100644 --- a/atom/browser/lib/objects-registry.coffee +++ b/atom/browser/lib/objects-registry.coffee @@ -34,6 +34,7 @@ class ObjectsRegistry extends EventEmitter @dereference id, 1 # Also reduce the count in owner. pointer = @owners[webContentsId] + return unless pointer? --pointer[id] delete pointer[id] if pointer[id] is 0 @@ -57,6 +58,7 @@ class ObjectsRegistry extends EventEmitter # Private: Dereference the object from store. dereference: (id, count) -> pointer = @storage[id] + return unless pointer? pointer.count -= count if pointer.count is 0 v8Util.deleteHiddenValue pointer.object, 'atomId' diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index 0a28d350e8a9..149b208b409d 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -106,16 +106,9 @@ ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) -> catch e event.returnValue = errorToMeta e -ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, guestInstanceId) -> +ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) -> try - BrowserWindow = require 'browser-window' - if guestInstanceId? - guestViewManager = require './guest-view-manager' - window = BrowserWindow.fromWebContents guestViewManager.getEmbedder(guestInstanceId) - else - window = BrowserWindow.fromWebContents event.sender - window = BrowserWindow.fromDevToolsWebContents event.sender unless window? - event.returnValue = valueToMeta event.sender, window + event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow() catch e event.returnValue = errorToMeta e diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 4d5f273340ab..dd4694b3bd6b 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -68,6 +68,7 @@ NativeWindow::NativeWindow( const mate::Dictionary& options) : content::WebContentsObserver(inspectable_web_contents->GetWebContents()), has_frame_(true), + force_using_draggable_region_(false), transparent_(false), enable_larger_than_screen_(false), is_closed_(false), @@ -75,8 +76,6 @@ NativeWindow::NativeWindow( aspect_ratio_(0.0), inspectable_web_contents_(inspectable_web_contents), weak_factory_(this) { - inspectable_web_contents->GetView()->SetDelegate(this); - options.Get(switches::kFrame, &has_frame_); options.Get(switches::kTransparent, &transparent_); options.Get(switches::kEnableLargerThanScreen, &enable_larger_than_screen_); @@ -113,27 +112,34 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) { int x = -1, y = -1; bool center; if (options.Get(switches::kX, &x) && options.Get(switches::kY, &y)) { - int width = -1, height = -1; - options.Get(switches::kWidth, &width); - options.Get(switches::kHeight, &height); - SetBounds(gfx::Rect(x, y, width, height)); + SetPosition(gfx::Point(x, y)); } else if (options.Get(switches::kCenter, ¢er) && center) { Center(); } + extensions::SizeConstraints size_constraints; int min_height = 0, min_width = 0; if (options.Get(switches::kMinHeight, &min_height) | options.Get(switches::kMinWidth, &min_width)) { - SetMinimumSize(gfx::Size(min_width, min_height)); + size_constraints.set_minimum_size(gfx::Size(min_width, min_height)); } int max_height = INT_MAX, max_width = INT_MAX; if (options.Get(switches::kMaxHeight, &max_height) | options.Get(switches::kMaxWidth, &max_width)) { - SetMaximumSize(gfx::Size(max_width, max_height)); + size_constraints.set_maximum_size(gfx::Size(max_width, max_height)); } + bool use_content_size = false; + options.Get(switches::kUseContentSize, &use_content_size); + if (use_content_size) { + SetContentSizeConstraints(size_constraints); + } else { + SetSizeConstraints(size_constraints); + } +#if defined(OS_WIN) || defined(USE_X11) bool resizable; if (options.Get(switches::kResizable, &resizable)) { SetResizable(resizable); } +#endif bool top; if (options.Get(switches::kAlwaysOnTop, &top) && top) { SetAlwaysOnTop(true); @@ -179,6 +185,67 @@ gfx::Point NativeWindow::GetPosition() { return GetBounds().origin(); } +void NativeWindow::SetContentSize(const gfx::Size& size) { + SetSize(ContentSizeToWindowSize(size)); +} + +gfx::Size NativeWindow::GetContentSize() { + return WindowSizeToContentSize(GetSize()); +} + +void NativeWindow::SetSizeConstraints( + const extensions::SizeConstraints& window_constraints) { + extensions::SizeConstraints content_constraints; + if (window_constraints.HasMaximumSize()) + content_constraints.set_maximum_size( + WindowSizeToContentSize(window_constraints.GetMaximumSize())); + if (window_constraints.HasMinimumSize()) + content_constraints.set_minimum_size( + WindowSizeToContentSize(window_constraints.GetMinimumSize())); + SetContentSizeConstraints(content_constraints); +} + +extensions::SizeConstraints NativeWindow::GetSizeConstraints() { + extensions::SizeConstraints content_constraints = GetContentSizeConstraints(); + extensions::SizeConstraints window_constraints; + if (content_constraints.HasMaximumSize()) + window_constraints.set_maximum_size( + ContentSizeToWindowSize(content_constraints.GetMaximumSize())); + if (content_constraints.HasMinimumSize()) + window_constraints.set_minimum_size( + ContentSizeToWindowSize(content_constraints.GetMinimumSize())); + return window_constraints; +} + +void NativeWindow::SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints) { + size_constraints_ = size_constraints; +} + +extensions::SizeConstraints NativeWindow::GetContentSizeConstraints() { + return size_constraints_; +} + +void NativeWindow::SetMinimumSize(const gfx::Size& size) { + extensions::SizeConstraints size_constraints; + size_constraints.set_minimum_size(size); + SetSizeConstraints(size_constraints); +} + +gfx::Size NativeWindow::GetMinimumSize() { + return GetSizeConstraints().GetMinimumSize(); +} + +void NativeWindow::SetMaximumSize(const gfx::Size& size) { + extensions::SizeConstraints size_constraints; + size_constraints.set_maximum_size(size); + SetSizeConstraints(size_constraints); +} + +gfx::Size NativeWindow::GetMaximumSize() { + return GetSizeConstraints().GetMaximumSize(); +} + void NativeWindow::SetRepresentedFilename(const std::string& filename) { } @@ -412,18 +479,6 @@ void NativeWindow::NotifyWindowExecuteWindowsCommand( OnExecuteWindowsCommand(command)); } -void NativeWindow::DevToolsFocused() { - FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnDevToolsFocus()); -} - -void NativeWindow::DevToolsOpened() { - FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnDevToolsOpened()); -} - -void NativeWindow::DevToolsClosed() { - FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnDevToolsClosed()); -} - void NativeWindow::RenderViewCreated( content::RenderViewHost* render_view_host) { if (!transparent_) @@ -468,7 +523,7 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) { void NativeWindow::UpdateDraggableRegions( const std::vector& regions) { // Draggable region is not supported for non-frameless window. - if (has_frame_) + if (has_frame_ && !force_using_draggable_region_) return; draggable_region_ = DraggableRegionsToSkRegion(regions); } diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index e9a2b9433d13..379c23837b19 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -15,10 +15,11 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" -#include "brightray/browser/inspectable_web_contents_view_delegate.h" +#include "base/supports_user_data.h" #include "content/public/browser/readback_types.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" +#include "extensions/browser/app_window/size_constraints.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" @@ -50,8 +51,8 @@ namespace atom { struct DraggableRegion; -class NativeWindow : public content::WebContentsObserver, - public brightray::InspectableWebContentsViewDelegate { +class NativeWindow : public base::SupportsUserData, + public content::WebContentsObserver { public: using CapturePageCallback = base::Callback; @@ -110,12 +111,18 @@ class NativeWindow : public content::WebContentsObserver, virtual gfx::Size GetSize(); virtual void SetPosition(const gfx::Point& position); virtual gfx::Point GetPosition(); - virtual void SetContentSize(const gfx::Size& size) = 0; - virtual gfx::Size GetContentSize() = 0; - virtual void SetMinimumSize(const gfx::Size& size) = 0; - virtual gfx::Size GetMinimumSize() = 0; - virtual void SetMaximumSize(const gfx::Size& size) = 0; - virtual gfx::Size GetMaximumSize() = 0; + virtual void SetContentSize(const gfx::Size& size); + virtual gfx::Size GetContentSize(); + virtual void SetSizeConstraints( + const extensions::SizeConstraints& size_constraints); + virtual extensions::SizeConstraints GetSizeConstraints(); + virtual void SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints); + virtual extensions::SizeConstraints GetContentSizeConstraints(); + virtual void SetMinimumSize(const gfx::Size& size); + virtual gfx::Size GetMinimumSize(); + virtual void SetMaximumSize(const gfx::Size& size); + virtual gfx::Size GetMaximumSize(); virtual void SetResizable(bool resizable) = 0; virtual bool IsResizable() = 0; virtual void SetAlwaysOnTop(bool top) = 0; @@ -219,6 +226,13 @@ class NativeWindow : public content::WebContentsObserver, bool enable_larger_than_screen() const { return enable_larger_than_screen_; } gfx::ImageSkia icon() const { return icon_; } + bool force_using_draggable_region() const { + return force_using_draggable_region_; + } + void set_force_using_draggable_region(bool force) { + force_using_draggable_region_ = true; + } + void set_has_dialog_attached(bool has_dialog_attached) { has_dialog_attached_ = has_dialog_attached; } @@ -227,10 +241,9 @@ class NativeWindow : public content::WebContentsObserver, NativeWindow(brightray::InspectableWebContents* inspectable_web_contents, const mate::Dictionary& options); - // brightray::InspectableWebContentsViewDelegate: - void DevToolsFocused() override; - void DevToolsOpened() override; - void DevToolsClosed() override; + // Converts between content size to window size. + virtual gfx::Size ContentSizeToWindowSize(const gfx::Size& size) = 0; + virtual gfx::Size WindowSizeToContentSize(const gfx::Size& size) = 0; // content::WebContentsObserver: void RenderViewCreated(content::RenderViewHost* render_view_host) override; @@ -257,6 +270,9 @@ class NativeWindow : public content::WebContentsObserver, // Whether window has standard frame. bool has_frame_; + // Force the window to be aware of draggable regions. + bool force_using_draggable_region_; + // Whether window is transparent. bool transparent_; @@ -264,6 +280,9 @@ class NativeWindow : public content::WebContentsObserver, // has to been explicitly provided. scoped_ptr draggable_region_; // used in custom drag. + // Minimum and maximum size, stored as content size. + extensions::SizeConstraints size_constraints_; + // Whether window can be resized larger than screen. bool enable_larger_than_screen_; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 20ad60531498..60581a2d977b 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -44,12 +44,8 @@ class NativeWindowMac : public NativeWindow { bool IsFullscreen() const override; void SetBounds(const gfx::Rect& bounds) override; gfx::Rect GetBounds() override; - void SetContentSize(const gfx::Size& size) override; - gfx::Size GetContentSize() override; - void SetMinimumSize(const gfx::Size& size) override; - gfx::Size GetMinimumSize() override; - void SetMaximumSize(const gfx::Size& size) override; - gfx::Size GetMaximumSize() override; + void SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; bool IsResizable() override; void SetAlwaysOnTop(bool top) override; @@ -89,6 +85,10 @@ class NativeWindowMac : public NativeWindow { const content::NativeWebKeyboardEvent&) override; private: + // NativeWindow: + gfx::Size ContentSizeToWindowSize(const gfx::Size& size) override; + gfx::Size WindowSizeToContentSize(const gfx::Size& size) override; + void InstallView(); void UninstallView(); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 9555914c899d..6420345da9b6 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -350,22 +350,25 @@ NativeWindowMac::NativeWindowMac( bool useStandardWindow = true; options.Get(switches::kStandardWindow, &useStandardWindow); + bool resizable = true; + options.Get(switches::kResizable, &resizable); + + // New title bar styles are available in Yosemite or newer + std::string titleBarStyle; + if (base::mac::IsOSYosemiteOrLater()) + options.Get(switches::kTitleBarStyle, &titleBarStyle); NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | NSResizableWindowMask; + NSMiniaturizableWindowMask; if (!useStandardWindow || transparent() || !has_frame()) { styleMask |= NSTexturedBackgroundWindowMask; } - - std::string titleBarStyle = "default"; - options.Get(switches::kTitleBarStyle, &titleBarStyle); - - if (base::mac::IsOSYosemiteOrLater()) { - // New title bar styles are available in Yosemite or newer - if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) { - styleMask |= NSFullSizeContentViewWindowMask; - styleMask |= NSUnifiedTitleAndToolbarWindowMask; - } + if (resizable) { + styleMask |= NSResizableWindowMask; + } + if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) { + styleMask |= NSFullSizeContentViewWindowMask; + styleMask |= NSUnifiedTitleAndToolbarWindowMask; } window_.reset([[AtomNSWindow alloc] @@ -393,18 +396,18 @@ NativeWindowMac::NativeWindowMac( // We will manage window's lifetime ourselves. [window_ setReleasedWhenClosed:NO]; - // Configure title bar look on Yosemite or newer - if (base::mac::IsOSYosemiteOrLater()) { - if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) { - [window_ setTitlebarAppearsTransparent:YES]; - [window_ setTitleVisibility:NSWindowTitleHidden]; - if (titleBarStyle == "hidden-inset") { - NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]; - toolbar.showsBaselineSeparator = NO; - [window_ setToolbar:toolbar]; - [toolbar release]; - } + // Hide the title bar. + if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) { + [window_ setTitlebarAppearsTransparent:YES]; + [window_ setTitleVisibility:NSWindowTitleHidden]; + if (titleBarStyle == "hidden-inset") { + base::scoped_nsobject toolbar( + [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]); + [toolbar setShowsBaselineSeparator:NO]; + [window_ setToolbar:toolbar]; } + // We should be aware of draggable regions when using hidden titlebar. + set_force_using_draggable_region(true); } // On OS X the initial window size doesn't include window frame. @@ -436,6 +439,11 @@ NativeWindowMac::NativeWindowMac( [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; InstallView(); + + // Install the DraggableRegionView if it is forced to use draggable regions + // for normal window. + if (has_frame() && force_using_draggable_region()) + InstallDraggableRegionView(); } NativeWindowMac::~NativeWindowMac() { @@ -546,56 +554,30 @@ gfx::Rect NativeWindowMac::GetBounds() { return bounds; } -void NativeWindowMac::SetContentSize(const gfx::Size& size) { - if (!has_frame()) { - SetSize(size); - return; +void NativeWindowMac::SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints) { + auto convertSize = [this](const gfx::Size& size) { + // Our frameless window still has titlebar attached, so setting contentSize + // will result in actual content size being larger. + if (!has_frame()) { + NSRect frame = NSMakeRect(0, 0, size.width(), size.height()); + NSRect content = [window_ contentRectForFrameRect:frame]; + return content.size; + } else { + return NSMakeSize(size.width(), size.height()); + } + }; + + NSView* content = [window_ contentView]; + if (size_constraints.HasMinimumSize()) { + NSSize min_size = convertSize(size_constraints.GetMinimumSize()); + [window_ setContentMinSize:[content convertSize:min_size toView:nil]]; } - - NSRect frame_nsrect = [window_ frame]; - NSSize frame = frame_nsrect.size; - NSSize content = [window_ contentRectForFrameRect:frame_nsrect].size; - - int width = size.width() + frame.width - content.width; - int height = size.height() + frame.height - content.height; - frame_nsrect.origin.y -= height - frame_nsrect.size.height; - frame_nsrect.size.width = width; - frame_nsrect.size.height = height; - [window_ setFrame:frame_nsrect display:YES]; -} - -gfx::Size NativeWindowMac::GetContentSize() { - if (!has_frame()) - return GetSize(); - - NSRect bounds = [[window_ contentView] bounds]; - return gfx::Size(bounds.size.width, bounds.size.height); -} - -void NativeWindowMac::SetMinimumSize(const gfx::Size& size) { - NSSize min_size = NSMakeSize(size.width(), size.height()); - NSView* content = [window_ contentView]; - [window_ setContentMinSize:[content convertSize:min_size toView:nil]]; -} - -gfx::Size NativeWindowMac::GetMinimumSize() { - NSView* content = [window_ contentView]; - NSSize min_size = [content convertSize:[window_ contentMinSize] - fromView:nil]; - return gfx::Size(min_size.width, min_size.height); -} - -void NativeWindowMac::SetMaximumSize(const gfx::Size& size) { - NSSize max_size = NSMakeSize(size.width(), size.height()); - NSView* content = [window_ contentView]; - [window_ setContentMaxSize:[content convertSize:max_size toView:nil]]; -} - -gfx::Size NativeWindowMac::GetMaximumSize() { - NSView* content = [window_ contentView]; - NSSize max_size = [content convertSize:[window_ contentMaxSize] - fromView:nil]; - return gfx::Size(max_size.width, max_size.height); + if (size_constraints.HasMaximumSize()) { + NSSize max_size = convertSize(size_constraints.GetMaximumSize()); + [window_ setContentMaxSize:[content convertSize:max_size toView:nil]]; + } + NativeWindow::SetContentSizeConstraints(size_constraints); } void NativeWindowMac::SetResizable(bool resizable) { @@ -818,6 +800,24 @@ void NativeWindowMac::HandleKeyboardEvent( } } +gfx::Size NativeWindowMac::ContentSizeToWindowSize(const gfx::Size& size) { + if (!has_frame()) + return size; + + NSRect content = NSMakeRect(0, 0, size.width(), size.height()); + NSRect frame = [window_ frameRectForContentRect:content]; + return gfx::Size(frame.size); +} + +gfx::Size NativeWindowMac::WindowSizeToContentSize(const gfx::Size& size) { + if (!has_frame()) + return size; + + NSRect frame = NSMakeRect(0, 0, size.width(), size.height()); + NSRect content = [window_ contentRectForFrameRect:frame]; + return gfx::Size(content.size); +} + void NativeWindowMac::InstallView() { // Make sure the bottom corner is rounded: http://crbug.com/396264. [[window_ contentView] setWantsLayer:YES]; diff --git a/atom/browser/native_window_observer.h b/atom/browser/native_window_observer.h index 5b0a0c56b3de..33ab1ecb6b3d 100644 --- a/atom/browser/native_window_observer.h +++ b/atom/browser/native_window_observer.h @@ -55,11 +55,6 @@ class NativeWindowObserver { virtual void OnWindowEnterHtmlFullScreen() {} virtual void OnWindowLeaveHtmlFullScreen() {} - // Redirect devtools events. - virtual void OnDevToolsFocus() {} - virtual void OnDevToolsOpened() {} - virtual void OnDevToolsClosed() {} - // Called when renderer is hung. virtual void OnRendererUnresponsive() {} diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 45697c96683c..4c8d014b1ec9 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -77,70 +77,6 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) { (modifiers == (Modifiers::AltKey | Modifiers::IsRight)); } -#if defined(OS_WIN) -// Convert Win32 WM_APPCOMMANDS to strings. -const char* AppCommandToString(int command_id) { - switch (command_id) { - case APPCOMMAND_BROWSER_BACKWARD : return "browser-backward"; - case APPCOMMAND_BROWSER_FORWARD : return "browser-forward"; - case APPCOMMAND_BROWSER_REFRESH : return "browser-refresh"; - case APPCOMMAND_BROWSER_STOP : return "browser-stop"; - case APPCOMMAND_BROWSER_SEARCH : return "browser-search"; - case APPCOMMAND_BROWSER_FAVORITES : return "browser-favorites"; - case APPCOMMAND_BROWSER_HOME : return "browser-home"; - case APPCOMMAND_VOLUME_MUTE : return "volume-mute"; - case APPCOMMAND_VOLUME_DOWN : return "volume-down"; - case APPCOMMAND_VOLUME_UP : return "volume-up"; - case APPCOMMAND_MEDIA_NEXTTRACK : return "media-nexttrack"; - case APPCOMMAND_MEDIA_PREVIOUSTRACK : return "media-previoustrack"; - case APPCOMMAND_MEDIA_STOP : return "media-stop"; - case APPCOMMAND_MEDIA_PLAY_PAUSE : return "media-play_pause"; - case APPCOMMAND_LAUNCH_MAIL : return "launch-mail"; - case APPCOMMAND_LAUNCH_MEDIA_SELECT : return "launch-media-select"; - case APPCOMMAND_LAUNCH_APP1 : return "launch-app1"; - case APPCOMMAND_LAUNCH_APP2 : return "launch-app2"; - case APPCOMMAND_BASS_DOWN : return "bass-down"; - case APPCOMMAND_BASS_BOOST : return "bass-boost"; - case APPCOMMAND_BASS_UP : return "bass-up"; - case APPCOMMAND_TREBLE_DOWN : return "treble-down"; - case APPCOMMAND_TREBLE_UP : return "treble-up"; - case APPCOMMAND_MICROPHONE_VOLUME_MUTE : return "microphone-volume-mute"; - case APPCOMMAND_MICROPHONE_VOLUME_DOWN : return "microphone-volume-down"; - case APPCOMMAND_MICROPHONE_VOLUME_UP : return "microphone-volume-up"; - case APPCOMMAND_HELP : return "help"; - case APPCOMMAND_FIND : return "find"; - case APPCOMMAND_NEW : return "new"; - case APPCOMMAND_OPEN : return "open"; - case APPCOMMAND_CLOSE : return "close"; - case APPCOMMAND_SAVE : return "save"; - case APPCOMMAND_PRINT : return "print"; - case APPCOMMAND_UNDO : return "undo"; - case APPCOMMAND_REDO : return "redo"; - case APPCOMMAND_COPY : return "copy"; - case APPCOMMAND_CUT : return "cut"; - case APPCOMMAND_PASTE : return "paste"; - case APPCOMMAND_REPLY_TO_MAIL : return "reply-to-mail"; - case APPCOMMAND_FORWARD_MAIL : return "forward-mail"; - case APPCOMMAND_SEND_MAIL : return "send-mail"; - case APPCOMMAND_SPELL_CHECK : return "spell-check"; - case APPCOMMAND_MIC_ON_OFF_TOGGLE : return "mic-on-off-toggle"; - case APPCOMMAND_CORRECTION_LIST : return "correction-list"; - case APPCOMMAND_MEDIA_PLAY : return "media-play"; - case APPCOMMAND_MEDIA_PAUSE : return "media-pause"; - case APPCOMMAND_MEDIA_RECORD : return "media-record"; - case APPCOMMAND_MEDIA_FAST_FORWARD : return "media-fast-forward"; - case APPCOMMAND_MEDIA_REWIND : return "media-rewind"; - case APPCOMMAND_MEDIA_CHANNEL_UP : return "media-channel-up"; - case APPCOMMAND_MEDIA_CHANNEL_DOWN : return "media-channel-down"; - case APPCOMMAND_DELETE : return "delete"; - case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE: - return "dictate-or-command-control-toggle"; - default: - return "unknown"; - } -} -#endif - class NativeWindowClientView : public views::ClientView { public: NativeWindowClientView(views::Widget* widget, @@ -169,9 +105,6 @@ NativeWindowViews::NativeWindowViews( menu_bar_autohide_(false), menu_bar_visible_(false), menu_bar_alt_pressed_(false), -#if defined(OS_WIN) - is_minimized_(false), -#endif keyboard_event_handler_(new views::UnhandledKeyboardEventHandler), use_content_size_(false), resizable_(true) { @@ -189,7 +122,8 @@ NativeWindowViews::NativeWindowViews( // will not allow us to resize the window larger than scree. // Setting directly to INT_MAX somehow doesn't work, so we just devide // by 10, which should still be large enough. - maximum_size_.SetSize(INT_MAX / 10, INT_MAX / 10); + SetContentSizeConstraints(extensions::SizeConstraints( + gfx::Size(), gfx::Size(INT_MAX / 10, INT_MAX / 10))); int width = 800, height = 600; options.Get(switches::kWidth, &width); @@ -228,6 +162,9 @@ NativeWindowViews::NativeWindowViews( window_->Init(params); + bool fullscreen = false; + options.Get(switches::kFullscreen, &fullscreen); + #if defined(USE_X11) // Start monitoring window states. window_state_watcher_.reset(new WindowStateWatcher(this)); @@ -253,8 +190,7 @@ NativeWindowViews::NativeWindowViews( } // Before the window is mapped, there is no SHOW_FULLSCREEN_STATE. - bool fullscreen = false; - if (options.Get(switches::kFullscreen, & fullscreen) && fullscreen) { + if (fullscreen) { state_atom_list.push_back(GetAtom("_NET_WM_STATE_FULLSCREEN")); } @@ -272,12 +208,15 @@ NativeWindowViews::NativeWindowViews( set_background(views::Background::CreateStandardPanelBackground()); AddChildView(web_view_); - if (has_frame() && - options.Get(switches::kUseContentSize, &use_content_size_) && - use_content_size_) - bounds = ContentBoundsToWindowBounds(bounds); - #if defined(OS_WIN) + // Save initial window state. + if (fullscreen) + last_window_state_ = ui::SHOW_STATE_FULLSCREEN; + else + last_window_state_ = ui::SHOW_STATE_NORMAL; + + last_normal_size_ = gfx::Size(widget_size_); + if (!has_frame()) { // Set Window style so that we get a minimize and maximize animation when // frameless. @@ -309,8 +248,14 @@ NativeWindowViews::NativeWindowViews( if (transparent() && !has_frame()) wm::SetShadowType(GetNativeWindow(), wm::SHADOW_TYPE_NONE); + gfx::Size size = bounds.size(); + if (has_frame() && + options.Get(switches::kUseContentSize, &use_content_size_) && + use_content_size_) + size = ContentSizeToWindowSize(size); + window_->UpdateWindowIcon(); - window_->CenterWindow(bounds.size()); + window_->CenterWindow(size); Layout(); } @@ -388,11 +333,16 @@ 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) + if (fullscreen) { + last_window_state_ = ui::SHOW_STATE_FULLSCREEN; NotifyWindowEnterFullScreen(); - else + } else { + last_window_state_ = ui::SHOW_STATE_NORMAL; NotifyWindowLeaveFullScreen(); + } + // We set the new value after notifying, so we can handle the size event + // correctly. + window_->SetFullscreen(fullscreen); #else if (IsVisible()) window_->SetFullscreen(fullscreen); @@ -428,42 +378,23 @@ gfx::Rect NativeWindowViews::GetBounds() { return window_->GetWindowBoundsInScreen(); } -void NativeWindowViews::SetContentSize(const gfx::Size& size) { - if (!has_frame()) { - NativeWindow::SetSize(size); - return; - } - - gfx::Rect bounds = window_->GetWindowBoundsInScreen(); - bounds.set_size(size); - SetBounds(ContentBoundsToWindowBounds(bounds)); -} - gfx::Size NativeWindowViews::GetContentSize() { - if (!has_frame()) - return GetSize(); +#if defined(OS_WIN) + if (IsMinimized()) + return NativeWindow::GetContentSize(); +#endif - gfx::Size content_size = - window_->non_client_view()->frame_view()->GetBoundsForClientView().size(); - if (menu_bar_ && menu_bar_visible_) - content_size.set_height(content_size.height() - kMenuBarHeight); - return content_size; + return web_view_->size(); } -void NativeWindowViews::SetMinimumSize(const gfx::Size& size) { - minimum_size_ = size; -} - -gfx::Size NativeWindowViews::GetMinimumSize() { - return minimum_size_; -} - -void NativeWindowViews::SetMaximumSize(const gfx::Size& size) { - maximum_size_ = size; -} - -gfx::Size NativeWindowViews::GetMaximumSize() { - return maximum_size_; +void NativeWindowViews::SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints) { + NativeWindow::SetContentSizeConstraints(size_constraints); + window_->OnSizeConstraintsChanged(); +#if defined(USE_X11) + if (resizable_) + old_size_constraints_ = size_constraints; +#endif } void NativeWindowViews::SetResizable(bool resizable) { @@ -482,11 +413,13 @@ void NativeWindowViews::SetResizable(bool resizable) { // On Linux there is no "resizable" property of a window, we have to set // both the minimum and maximum size to the window size to achieve it. if (resizable) { - SetMaximumSize(gfx::Size()); - SetMinimumSize(gfx::Size()); + SetContentSizeConstraints(old_size_constraints_); } else { - SetMaximumSize(GetSize()); - SetMinimumSize(GetSize()); + old_size_constraints_ = GetContentSizeConstraints(); + resizable_ = false; + gfx::Size content_size = GetContentSize(); + SetContentSizeConstraints( + extensions::SizeConstraints(content_size, content_size)); } } #endif @@ -598,8 +531,24 @@ void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) { if (!menu_bar_autohide_) { SetMenuBarVisibility(true); - if (use_content_size_) + if (use_content_size_) { + // Enlarge the size constraints for the menu. + extensions::SizeConstraints constraints = GetContentSizeConstraints(); + if (constraints.HasMinimumSize()) { + gfx::Size min_size = constraints.GetMinimumSize(); + min_size.set_height(min_size.height() + kMenuBarHeight); + constraints.set_minimum_size(min_size); + } + if (constraints.HasMaximumSize()) { + gfx::Size max_size = constraints.GetMaximumSize(); + max_size.set_height(max_size.height() + kMenuBarHeight); + constraints.set_maximum_size(max_size); + } + SetContentSizeConstraints(constraints); + + // Resize the window to make sure content size is not changed. SetContentSize(content_size); + } } } @@ -802,53 +751,47 @@ void NativeWindowViews::OnWidgetMove() { NotifyWindowMove(); } +gfx::Size NativeWindowViews::ContentSizeToWindowSize(const gfx::Size& size) { + if (!has_frame()) + return size; + + gfx::Size window_size(size); #if defined(OS_WIN) -bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { - // Windows uses the 4 lower order bits of |command_id| for type-specific - // information so we must exclude this when comparing. - static const int sc_mask = 0xFFF0; - if ((command_id & sc_mask) == SC_MINIMIZE) { - NotifyWindowMinimize(); - is_minimized_ = true; - } else if ((command_id & sc_mask) == SC_RESTORE) { - if (is_minimized_) - NotifyWindowRestore(); - else - NotifyWindowUnmaximize(); - is_minimized_ = false; - } else if ((command_id & sc_mask) == SC_MAXIMIZE) { - NotifyWindowMaximize(); - } else { - std::string command = AppCommandToString(command_id); - NotifyWindowExecuteWindowsCommand(command); - } - return false; -} + gfx::Rect dpi_bounds = + gfx::Rect(gfx::Point(), gfx::win::DIPToScreenSize(size)); + gfx::Rect window_bounds = gfx::win::ScreenToDIPRect( + window_->non_client_view()->GetWindowBoundsForClientBounds(dpi_bounds)); + window_size = window_bounds.size(); #endif -gfx::ImageSkia NativeWindowViews::GetDevToolsWindowIcon() { - return GetWindowAppIcon(); + if (menu_bar_ && menu_bar_visible_) + window_size.set_height(window_size.height() + kMenuBarHeight); + return window_size; } -#if defined(USE_X11) -void NativeWindowViews::GetDevToolsWindowWMClass( - std::string* name, std::string* class_name) { - *class_name = Browser::Get()->GetName(); - *name = base::StringToLowerASCII(*class_name); -} -#endif +gfx::Size NativeWindowViews::WindowSizeToContentSize(const gfx::Size& size) { + if (!has_frame()) + return size; + gfx::Size content_size(size); #if defined(OS_WIN) -bool NativeWindowViews::PreHandleMSG( - UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { - // Handle thumbar button click message. - if (message == WM_COMMAND && HIWORD(w_param) == THBN_CLICKED) - return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param)); - else - return false; -} + content_size = gfx::win::DIPToScreenSize(content_size); + RECT rect; + SetRectEmpty(&rect); + HWND hwnd = GetAcceleratedWidget(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + DWORD ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + AdjustWindowRectEx(&rect, style, FALSE, ex_style); + content_size.set_width(content_size.width() - (rect.right - rect.left)); + content_size.set_height(content_size.height() - (rect.bottom - rect.top)); + content_size = gfx::win::ScreenToDIPSize(content_size); #endif + if (menu_bar_ && menu_bar_visible_) + content_size.set_height(content_size.height() - kMenuBarHeight); + return content_size; +} + void NativeWindowViews::HandleKeyboardEvent( content::WebContents*, const content::NativeWebKeyboardEvent& event) { @@ -880,9 +823,6 @@ void NativeWindowViews::HandleKeyboardEvent( // When a single Alt is pressed: menu_bar_alt_pressed_ = true; } else if (event.type == blink::WebInputEvent::KeyUp && IsAltKey(event) && -#if defined(USE_X11) - event.modifiers == 0 && -#endif menu_bar_alt_pressed_) { // When a single Alt is released right after a Alt is pressed: menu_bar_alt_pressed_ = false; @@ -893,6 +833,14 @@ void NativeWindowViews::HandleKeyboardEvent( } } +gfx::Size NativeWindowViews::GetMinimumSize() { + return NativeWindow::GetMinimumSize(); +} + +gfx::Size NativeWindowViews::GetMaximumSize() { + return NativeWindow::GetMaximumSize(); +} + bool NativeWindowViews::AcceleratorPressed(const ui::Accelerator& accelerator) { return accelerator_util::TriggerAcceleratorTableCommand( &accelerator_table_, accelerator); @@ -915,26 +863,6 @@ void NativeWindowViews::RegisterAccelerators(ui::MenuModel* menu_model) { } } -gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds( - const gfx::Rect& bounds) { - gfx::Point origin = bounds.origin(); -#if defined(OS_WIN) - gfx::Rect dpi_bounds = gfx::win::DIPToScreenRect(bounds); - gfx::Rect window_bounds = gfx::win::ScreenToDIPRect( - window_->non_client_view()->GetWindowBoundsForClientBounds(dpi_bounds)); -#else - gfx::Rect window_bounds = - window_->non_client_view()->GetWindowBoundsForClientBounds(bounds); -#endif - // The window's position would also be changed, but we only want to change - // the size. - window_bounds.set_origin(origin); - - if (menu_bar_ && menu_bar_visible_) - window_bounds.set_height(window_bounds.height() + kMenuBarHeight); - return window_bounds; -} - ui::WindowShowState NativeWindowViews::GetRestoredState() { if (IsMaximized()) return ui::SHOW_STATE_MAXIMIZED; diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 355f5bd38ef3..0014acd073c1 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -63,12 +63,9 @@ class NativeWindowViews : public NativeWindow, bool IsFullscreen() const override; void SetBounds(const gfx::Rect& bounds) override; gfx::Rect GetBounds() override; - void SetContentSize(const gfx::Size& size) override; gfx::Size GetContentSize() override; - void SetMinimumSize(const gfx::Size& size) override; - gfx::Size GetMinimumSize() override; - void SetMaximumSize(const gfx::Size& size) override; - gfx::Size GetMaximumSize() override; + void SetContentSizeConstraints( + const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; bool IsResizable() override; void SetAlwaysOnTop(bool top) override; @@ -131,34 +128,29 @@ class NativeWindowViews : public NativeWindow, bool ExecuteWindowsCommand(int command_id) override; #endif - // brightray::InspectableWebContentsViewDelegate: - gfx::ImageSkia GetDevToolsWindowIcon() override; -#if defined(USE_X11) - void GetDevToolsWindowWMClass( - std::string* name, std::string* class_name) override; -#endif - #if defined(OS_WIN) // MessageHandlerDelegate: bool PreHandleMSG( UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) override; + + void HandleSizeEvent(WPARAM w_param, LPARAM l_param); #endif // NativeWindow: + gfx::Size ContentSizeToWindowSize(const gfx::Size& size) override; + gfx::Size WindowSizeToContentSize(const gfx::Size& size) override; void HandleKeyboardEvent( content::WebContents*, const content::NativeWebKeyboardEvent& event) override; // views::View: + gfx::Size GetMinimumSize() override; + gfx::Size GetMaximumSize() override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; // Register accelerators supported by the menu model. void RegisterAccelerators(ui::MenuModel* menu_model); - // Converts between client area and window area, since we include the menu bar - // in client area we need to substract/add menu bar's height in convertions. - gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& content_bounds); - // Returns the restore state for the window. ui::WindowShowState GetRestoredState(); @@ -175,12 +167,23 @@ class NativeWindowViews : public NativeWindow, // Handles window state events. scoped_ptr window_state_watcher_; + + // The "resizable" flag on Linux is implemented by setting size constraints, + // we need to make sure size constraints are restored when window becomes + // resizable again. + extensions::SizeConstraints old_size_constraints_; #elif defined(OS_WIN) // Weak ref. AtomDesktopWindowTreeHostWin* atom_desktop_window_tree_host_win_; - // Records window was whether restored from minimized state or maximized - // state. - bool is_minimized_; + + ui::WindowShowState last_window_state_; + + // There's an issue with restore on Windows, that sometimes causes the Window + // to receive the wrong size (#2498). To circumvent that, we keep tabs on the + // size of the window while in the normal state (not maximized, minimized or + // fullscreen), so we restore it correctly. + gfx::Size last_normal_size_; + // In charge of running taskbar related APIs. TaskbarHost taskbar_host_; #endif @@ -194,8 +197,6 @@ class NativeWindowViews : public NativeWindow, bool use_content_size_; bool resizable_; std::string title_; - gfx::Size minimum_size_; - gfx::Size maximum_size_; gfx::Size widget_size_; DISALLOW_COPY_AND_ASSIGN(NativeWindowViews); diff --git a/atom/browser/native_window_views_win.cc b/atom/browser/native_window_views_win.cc new file mode 100644 index 000000000000..6092a2242ac0 --- /dev/null +++ b/atom/browser/native_window_views_win.cc @@ -0,0 +1,145 @@ +// 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/native_window_views.h" + +namespace atom { + +namespace { + +// Convert Win32 WM_APPCOMMANDS to strings. +const char* AppCommandToString(int command_id) { + switch (command_id) { + case APPCOMMAND_BROWSER_BACKWARD : return "browser-backward"; + case APPCOMMAND_BROWSER_FORWARD : return "browser-forward"; + case APPCOMMAND_BROWSER_REFRESH : return "browser-refresh"; + case APPCOMMAND_BROWSER_STOP : return "browser-stop"; + case APPCOMMAND_BROWSER_SEARCH : return "browser-search"; + case APPCOMMAND_BROWSER_FAVORITES : return "browser-favorites"; + case APPCOMMAND_BROWSER_HOME : return "browser-home"; + case APPCOMMAND_VOLUME_MUTE : return "volume-mute"; + case APPCOMMAND_VOLUME_DOWN : return "volume-down"; + case APPCOMMAND_VOLUME_UP : return "volume-up"; + case APPCOMMAND_MEDIA_NEXTTRACK : return "media-nexttrack"; + case APPCOMMAND_MEDIA_PREVIOUSTRACK : return "media-previoustrack"; + case APPCOMMAND_MEDIA_STOP : return "media-stop"; + case APPCOMMAND_MEDIA_PLAY_PAUSE : return "media-play_pause"; + case APPCOMMAND_LAUNCH_MAIL : return "launch-mail"; + case APPCOMMAND_LAUNCH_MEDIA_SELECT : return "launch-media-select"; + case APPCOMMAND_LAUNCH_APP1 : return "launch-app1"; + case APPCOMMAND_LAUNCH_APP2 : return "launch-app2"; + case APPCOMMAND_BASS_DOWN : return "bass-down"; + case APPCOMMAND_BASS_BOOST : return "bass-boost"; + case APPCOMMAND_BASS_UP : return "bass-up"; + case APPCOMMAND_TREBLE_DOWN : return "treble-down"; + case APPCOMMAND_TREBLE_UP : return "treble-up"; + case APPCOMMAND_MICROPHONE_VOLUME_MUTE : return "microphone-volume-mute"; + case APPCOMMAND_MICROPHONE_VOLUME_DOWN : return "microphone-volume-down"; + case APPCOMMAND_MICROPHONE_VOLUME_UP : return "microphone-volume-up"; + case APPCOMMAND_HELP : return "help"; + case APPCOMMAND_FIND : return "find"; + case APPCOMMAND_NEW : return "new"; + case APPCOMMAND_OPEN : return "open"; + case APPCOMMAND_CLOSE : return "close"; + case APPCOMMAND_SAVE : return "save"; + case APPCOMMAND_PRINT : return "print"; + case APPCOMMAND_UNDO : return "undo"; + case APPCOMMAND_REDO : return "redo"; + case APPCOMMAND_COPY : return "copy"; + case APPCOMMAND_CUT : return "cut"; + case APPCOMMAND_PASTE : return "paste"; + case APPCOMMAND_REPLY_TO_MAIL : return "reply-to-mail"; + case APPCOMMAND_FORWARD_MAIL : return "forward-mail"; + case APPCOMMAND_SEND_MAIL : return "send-mail"; + case APPCOMMAND_SPELL_CHECK : return "spell-check"; + case APPCOMMAND_MIC_ON_OFF_TOGGLE : return "mic-on-off-toggle"; + case APPCOMMAND_CORRECTION_LIST : return "correction-list"; + case APPCOMMAND_MEDIA_PLAY : return "media-play"; + case APPCOMMAND_MEDIA_PAUSE : return "media-pause"; + case APPCOMMAND_MEDIA_RECORD : return "media-record"; + case APPCOMMAND_MEDIA_FAST_FORWARD : return "media-fast-forward"; + case APPCOMMAND_MEDIA_REWIND : return "media-rewind"; + case APPCOMMAND_MEDIA_CHANNEL_UP : return "media-channel-up"; + case APPCOMMAND_MEDIA_CHANNEL_DOWN : return "media-channel-down"; + case APPCOMMAND_DELETE : return "delete"; + case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE: + return "dictate-or-command-control-toggle"; + default: + return "unknown"; + } +} + +} // namespace + +bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { + std::string command = AppCommandToString(command_id); + NotifyWindowExecuteWindowsCommand(command); + return false; +} + +bool NativeWindowViews::PreHandleMSG( + UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { + switch (message) { + case WM_COMMAND: + // Handle thumbar button click message. + if (HIWORD(w_param) == THBN_CLICKED) + return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param)); + return false; + case WM_SIZE: + // Handle window state change. + HandleSizeEvent(w_param, l_param); + return false; + default: + return false; + } +} + +void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) { + // Here we handle the WM_SIZE event in order to figure out what is the current + // window state and notify the user accordingly. + switch (w_param) { + case SIZE_MAXIMIZED: + last_window_state_ = ui::SHOW_STATE_MAXIMIZED; + NotifyWindowMaximize(); + break; + case SIZE_MINIMIZED: + last_window_state_ = ui::SHOW_STATE_MINIMIZED; + NotifyWindowMinimize(); + break; + case SIZE_RESTORED: + if (last_window_state_ == ui::SHOW_STATE_NORMAL) { + // Window was resized so we save it's new size. + last_normal_size_ = GetSize(); + } else { + switch (last_window_state_) { + case ui::SHOW_STATE_MAXIMIZED: + last_window_state_ = ui::SHOW_STATE_NORMAL; + + // When the window is restored we resize it to the previous known + // normal size. + NativeWindow::SetSize(last_normal_size_); + + NotifyWindowUnmaximize(); + break; + case ui::SHOW_STATE_MINIMIZED: + if (IsFullscreen()) { + last_window_state_ = ui::SHOW_STATE_FULLSCREEN; + NotifyWindowEnterFullScreen(); + } else { + last_window_state_ = ui::SHOW_STATE_NORMAL; + + // When the window is restored we resize it to the previous known + // normal size. + NativeWindow::SetSize(last_normal_size_); + + NotifyWindowRestore(); + } + break; + } + } + break; + } +} + +} // namespace atom diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 54ba3546a75f..50e019c1c77e 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,7 +17,7 @@ CFBundleIconFile atom.icns CFBundleVersion - 0.33.0 + 0.33.6 LSMinimumSystemVersion 10.8.0 NSMainNibFile diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 8863d8be7677..b1aba451dcf6 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,33,0,0 - PRODUCTVERSION 0,33,0,0 + FILEVERSION 0,33,6,0 + PRODUCTVERSION 0,33,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "0.33.0" + VALUE "FileVersion", "0.33.6" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "0.33.0" + VALUE "ProductVersion", "0.33.6" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index b169471c6e99..da00dc54e2fc 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -79,6 +79,9 @@ class FileDialog { if (!title.empty()) GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str()); + if (!filterspec.empty()) + GetPtr()->SetDefaultExtension(filterspec.front().pszSpec); + SetDefaultFolder(default_path); } diff --git a/atom/browser/ui/views/frameless_view.cc b/atom/browser/ui/views/frameless_view.cc index 03a31e082874..2ec4459f6b45 100644 --- a/atom/browser/ui/views/frameless_view.cc +++ b/atom/browser/ui/views/frameless_view.cc @@ -104,11 +104,11 @@ gfx::Size FramelessView::GetPreferredSize() const { } gfx::Size FramelessView::GetMinimumSize() const { - return window_->GetMinimumSize(); + return window_->GetContentSizeConstraints().GetMinimumSize(); } gfx::Size FramelessView::GetMaximumSize() const { - return window_->GetMaximumSize(); + return window_->GetContentSizeConstraints().GetMaximumSize(); } const char* FramelessView::GetClassName() const { diff --git a/atom/browser/ui/views/native_frame_view.cc b/atom/browser/ui/views/native_frame_view.cc index a434fb434961..134255f48458 100644 --- a/atom/browser/ui/views/native_frame_view.cc +++ b/atom/browser/ui/views/native_frame_view.cc @@ -4,7 +4,7 @@ #include "atom/browser/ui/views/native_frame_view.h" -#include "atom/browser/native_window_views.h" +#include "atom/browser/native_window.h" namespace atom { @@ -14,8 +14,7 @@ const char kViewClassName[] = "AtomNativeFrameView"; } // namespace -NativeFrameView::NativeFrameView(NativeWindowViews* window, - views::Widget* widget) +NativeFrameView::NativeFrameView(NativeWindow* window, views::Widget* widget) : views::NativeFrameView(widget), window_(window) { } diff --git a/atom/browser/ui/views/native_frame_view.h b/atom/browser/ui/views/native_frame_view.h index acbe9cddc8dc..670459f1cbd0 100644 --- a/atom/browser/ui/views/native_frame_view.h +++ b/atom/browser/ui/views/native_frame_view.h @@ -9,13 +9,13 @@ namespace atom { -class NativeWindowViews; +class NativeWindow; // 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); + NativeFrameView(NativeWindow* window, views::Widget* widget); protected: // views::View: @@ -24,7 +24,7 @@ class NativeFrameView : public views::NativeFrameView { const char* GetClassName() const override; private: - NativeWindowViews* window_; // weak ref. + NativeWindow* window_; // weak ref. DISALLOW_COPY_AND_ASSIGN(NativeFrameView); }; diff --git a/atom/browser/ui/views/win_frame_view.cc b/atom/browser/ui/views/win_frame_view.cc index db74661932b7..fca7cb23347c 100644 --- a/atom/browser/ui/views/win_frame_view.cc +++ b/atom/browser/ui/views/win_frame_view.cc @@ -5,7 +5,6 @@ #include "atom/browser/ui/views/win_frame_view.h" #include "atom/browser/native_window_views.h" -#include "ui/gfx/win/dpi.h" #include "ui/views/widget/widget.h" #include "ui/views/win/hwnd_util.h" @@ -39,16 +38,6 @@ int WinFrameView::NonClientHitTest(const gfx::Point& point) { return FramelessView::NonClientHitTest(point); } -gfx::Size WinFrameView::GetMinimumSize() const { - gfx::Size size = FramelessView::GetMinimumSize(); - return gfx::win::DIPToScreenSize(size); -} - -gfx::Size WinFrameView::GetMaximumSize() const { - gfx::Size size = FramelessView::GetMaximumSize(); - return gfx::win::DIPToScreenSize(size); -} - const char* WinFrameView::GetClassName() const { return kViewClassName; } diff --git a/atom/browser/ui/views/win_frame_view.h b/atom/browser/ui/views/win_frame_view.h index 825677bff310..b2c1ef3a15de 100644 --- a/atom/browser/ui/views/win_frame_view.h +++ b/atom/browser/ui/views/win_frame_view.h @@ -20,8 +20,6 @@ class WinFrameView : public FramelessView { int NonClientHitTest(const gfx::Point& point) override; // views::View: - gfx::Size GetMinimumSize() const override; - gfx::Size GetMaximumSize() const override; const char* GetClassName() const override; private: diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index 4026d9ec4a6b..c88d4c810ef8 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -4,10 +4,7 @@ #include "atom/browser/ui/win/notify_icon.h" -#include - #include "atom/browser/ui/win/notify_icon_host.h" -#include "base/md5.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/win/windows_version.h" @@ -28,31 +25,7 @@ NotifyIcon::NotifyIcon(NotifyIconHost* host, icon_id_(id), window_(window), message_id_(message), - menu_model_(NULL), - has_tray_app_id_hash_(false) { - // NB: If we have an App Model ID, we should propagate that to the tray. - // Doing this prevents duplicate items from showing up in the notification - // preferences (i.e. "Always Show / Show notifications only / etc") - PWSTR explicit_app_id; - if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&explicit_app_id))) { - // GUIDs and MD5 hashes are the same length. So convenient! - base::MD5Sum(explicit_app_id, - sizeof(wchar_t) * wcslen(explicit_app_id), - reinterpret_cast(&tray_app_id_hash_)); - - // Set the GUID to version 4 as described in RFC 4122, section 4.4. - // The format of GUID version 4 must be like - // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, where y is one of [8, 9, A, B]. - tray_app_id_hash_.Data3 &= 0x0fff; - tray_app_id_hash_.Data3 |= 0x4000; - - // Set y to one of [8, 9, A, B]. - tray_app_id_hash_.Data4[0] = 1; - - has_tray_app_id_hash_ = true; - CoTaskMemFree(explicit_app_id); - } - + menu_model_(NULL) { NOTIFYICONDATA icon_data; InitIconData(&icon_data); icon_data.uFlags |= NIF_MESSAGE; @@ -81,10 +54,6 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos, icon_id.uID = icon_id_; icon_id.hWnd = window_; icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); - if (has_tray_app_id_hash_) - memcpy(reinterpret_cast(&icon_id.guidItem), - &tray_app_id_hash_, - sizeof(GUID)); RECT rect = { 0 }; Shell_NotifyIconGetRect(&icon_id, &rect); @@ -202,13 +171,6 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) { icon_data->cbSize = sizeof(NOTIFYICONDATA); icon_data->hWnd = window_; icon_data->uID = icon_id_; - - if (has_tray_app_id_hash_) { - icon_data->uFlags |= NIF_GUID; - memcpy(reinterpret_cast(&icon_data->guidItem), - &tray_app_id_hash_, - sizeof(GUID)); - } } } // namespace atom diff --git a/atom/browser/ui/win/notify_icon.h b/atom/browser/ui/win/notify_icon.h index 136186b689b9..d368dec71327 100644 --- a/atom/browser/ui/win/notify_icon.h +++ b/atom/browser/ui/win/notify_icon.h @@ -79,10 +79,6 @@ class NotifyIcon : public TrayIcon { // The context menu. ui::SimpleMenuModel* menu_model_; - // A hash of the app model ID - GUID tray_app_id_hash_; - bool has_tray_app_id_hash_; - DISALLOW_COPY_AND_ASSIGN(NotifyIcon); }; diff --git a/atom/browser/ui/x/x_window_utils.cc b/atom/browser/ui/x/x_window_utils.cc index e57122839cd8..f5c3f54ec123 100644 --- a/atom/browser/ui/x/x_window_utils.cc +++ b/atom/browser/ui/x/x_window_utils.cc @@ -6,6 +6,7 @@ #include +#include "base/environment.h" #include "base/strings/string_util.h" #include "dbus/bus.h" #include "dbus/object_proxy.h" @@ -50,6 +51,10 @@ void SetWindowType(::Window xwindow, const std::string& type) { } bool ShouldUseGlobalMenuBar() { + scoped_ptr env(base::Environment::Create()); + if (env->HasVar("ELECTRON_FORCE_WINDOW_MENU_BAR")) + return false; + dbus::Bus::Options options; scoped_refptr bus(new dbus::Bus(options)); diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index db55eddf7e2d..2856598c0bc0 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -40,6 +40,9 @@ WebContentsPreferences::WebContentsPreferences( base::DictionaryValue* web_preferences) { web_preferences_.Swap(web_preferences); web_contents->SetUserData(UserDataKey(), this); + + // The "isGuest" is not a preferences field. + web_preferences_.Remove("isGuest", nullptr); } WebContentsPreferences::~WebContentsPreferences() { @@ -94,7 +97,7 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( if (base::FilePath(preload).IsAbsolute()) command_line->AppendSwitchNative(switches::kPreloadScript, preload); else - LOG(ERROR) << "preload script must have abosulute path."; + LOG(ERROR) << "preload script must have absolute path."; } else if (web_preferences.GetString(switches::kPreloadUrl, &preload)) { // Translate to file path if there is "preload-url" option. base::FilePath preload_path; diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index 83b485f449bc..3e36df021478 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -37,6 +37,9 @@ class WebContentsPreferences // $.extend(|web_preferences_|, |new_web_preferences|). void Merge(const base::DictionaryValue& new_web_preferences); + // Returns the web preferences. + base::DictionaryValue* web_preferences() { return &web_preferences_; } + private: friend class content::WebContentsUserData; diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index 0b2f59ae0f18..969f958956ca 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -115,6 +115,14 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, Archive::Archive(const base::FilePath& path) : path_(path), file_(path_, base::File::FLAG_OPEN | base::File::FLAG_READ), +#if defined(OS_WIN) + fd_(_open_osfhandle( + reinterpret_cast(file_.GetPlatformFile()), 0)), +#elif defined(OS_POSIX) + fd_(file_.GetPlatformFile()), +#else + fd_(-1), +#endif header_size_(0) { } @@ -271,17 +279,7 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { } int Archive::GetFD() const { - if (!file_.IsValid()) - return -1; - -#if defined(OS_WIN) - return - _open_osfhandle(reinterpret_cast(file_.GetPlatformFile()), 0); -#elif defined(OS_POSIX) - return file_.GetPlatformFile(); -#else - return -1; -#endif + return fd_; } } // namespace asar diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index dda7aa78e0c0..f2ff2f76d676 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -69,6 +69,7 @@ class Archive { private: base::FilePath path_; base::File file_; + int fd_; uint32 header_size_; scoped_ptr header_; diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index deb09585434e..8078551b90ed 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 0 #define ATOM_MINOR_VERSION 33 -#define ATOM_PATCH_VERSION 0 +#define ATOM_PATCH_VERSION 6 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/keyboad_util.cc b/atom/common/keyboad_util.cc index 1baa829ff74a..29d1a800c859 100644 --- a/atom/common/keyboad_util.cc +++ b/atom/common/keyboad_util.cc @@ -10,9 +10,10 @@ namespace atom { ui::KeyboardCode KeyboardCodeFromCharCode(char c, bool* shifted) { *shifted = false; switch (c) { - case 8: case 0x7F: return ui::VKEY_BACK; - case 9: return ui::VKEY_TAB; - case 0xD: case 3: return ui::VKEY_RETURN; + case 0x08: return ui::VKEY_BACK; + case 0x7F: return ui::VKEY_DELETE; + case 0x09: return ui::VKEY_TAB; + case 0x0D: return ui::VKEY_RETURN; case 0x1B: return ui::VKEY_ESCAPE; case ' ': return ui::VKEY_SPACE; diff --git a/atom/common/lib/asar.coffee b/atom/common/lib/asar.coffee index 900a686d44a2..22d0e70b34fa 100644 --- a/atom/common/lib/asar.coffee +++ b/atom/common/lib/asar.coffee @@ -329,7 +329,7 @@ exports.wrapFsWithAsar = (fs) -> buffer = new Buffer(info.size) fd = archive.getFd() - retrun undefined unless fd >= 0 + return undefined unless fd >= 0 fs.readSync fd, buffer, 0, info.size, info.offset buffer.toString 'utf8' diff --git a/atom/common/lib/init.coffee b/atom/common/lib/init.coffee index 4bc3e36986c0..1c80e6171a4d 100644 --- a/atom/common/lib/init.coffee +++ b/atom/common/lib/init.coffee @@ -1,8 +1,7 @@ -process = global.process -fs = require 'fs' -path = require 'path' -timers = require 'timers' -Module = require 'module' +fs = require 'fs' +path = require 'path' +timers = require 'timers' +Module = require 'module' process.atomBinding = (name) -> try @@ -10,21 +9,8 @@ process.atomBinding = (name) -> catch e process.binding "atom_common_#{name}" if /No such module/.test e.message -# Global module search paths. -globalPaths = Module.globalPaths - -# Don't lookup modules in user-defined search paths, see http://git.io/vf8sF. -homeDir = - if process.platform is 'win32' - process.env.USERPROFILE - else - process.env.HOME -if homeDir # Node only add user-defined search paths when $HOME is defined. - userModulePath = path.resolve homeDir, '.node_modules' - globalPaths.splice globalPaths.indexOf(userModulePath), 2 - # Add common/api/lib to module search paths. -globalPaths.push path.resolve(__dirname, '..', 'api', 'lib') +Module.globalPaths.push path.resolve(__dirname, '..', 'api', 'lib') # setImmediate and process.nextTick makes use of uv_check and uv_prepare to # run the callbacks, however since we only run uv loop on requests, the @@ -37,6 +23,8 @@ wrapWithActivateUvLoop = (func) -> process.activateUvLoop() func.apply this, arguments process.nextTick = wrapWithActivateUvLoop process.nextTick +global.setImmediate = wrapWithActivateUvLoop timers.setImmediate +global.clearImmediate = timers.clearImmediate if process.type is 'browser' # setTimeout needs to update the polling timeout of the event loop, when @@ -45,10 +33,3 @@ if process.type is 'browser' # recalculate the timeout in browser process. global.setTimeout = wrapWithActivateUvLoop timers.setTimeout global.setInterval = wrapWithActivateUvLoop timers.setInterval - global.setImmediate = wrapWithActivateUvLoop timers.setImmediate - global.clearImmediate = wrapWithActivateUvLoop timers.clearImmediate -else - # There are no setImmediate under renderer process by default, so we need to - # manually setup them here. - global.setImmediate = setImmediate - global.clearImmediate = clearImmediate diff --git a/atom/common/lib/reset-search-paths.coffee b/atom/common/lib/reset-search-paths.coffee new file mode 100644 index 000000000000..7061103306aa --- /dev/null +++ b/atom/common/lib/reset-search-paths.coffee @@ -0,0 +1,29 @@ +path = require 'path' +Module = require 'module' + +# Clear Node's global search paths. +Module.globalPaths.length = 0 + +# Clear current and parent(init.coffee)'s search paths. +module.paths = [] +module.parent.paths = [] + +# Prevent Node from adding paths outside this app to search paths. +Module._nodeModulePaths = (from) -> + from = path.resolve from + + # If "from" is outside the app then we do nothing. + skipOutsidePaths = from.startsWith process.resourcesPath + + # Following logoic is copied from module.js. + splitRe = if process.platform is 'win32' then /[\/\\]/ else /\// + paths = [] + + parts = from.split splitRe + for part, tip in parts by -1 + continue if part is 'node_modules' + dir = parts.slice(0, tip + 1).join path.sep + break if skipOutsidePaths and not dir.startsWith process.resourcesPath + paths.push path.join(dir, 'node_modules') + + paths diff --git a/atom/common/native_mate_converters/blink_converter.cc b/atom/common/native_mate_converters/blink_converter.cc index 67c7e7e95fd8..fcfc8905b3e2 100644 --- a/atom/common/native_mate_converters/blink_converter.cc +++ b/atom/common/native_mate_converters/blink_converter.cc @@ -9,6 +9,7 @@ #include "atom/common/keyboad_util.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "content/public/browser/native_web_keyboard_event.h" #include "native_mate/dictionary.h" #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" @@ -29,10 +30,10 @@ int VectorToBitArray(const std::vector& vec) { namespace mate { template<> -struct Converter { +struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, - char* out) { - std::string code = base::StringToLowerASCII(V8ToString(val)); + base::char16* out) { + base::string16 code = base::UTF8ToUTF16(V8ToString(val)); if (code.length() != 1) return false; *out = code[0]; @@ -60,7 +61,7 @@ struct Converter { else if (type == "mousewheel") *out = blink::WebInputEvent::MouseWheel; else if (type == "keydown") - *out = blink::WebInputEvent::KeyDown; + *out = blink::WebInputEvent::RawKeyDown; else if (type == "keyup") *out = blink::WebInputEvent::KeyUp; else if (type == "char") @@ -77,6 +78,21 @@ struct Converter { } }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Handle val, + blink::WebMouseEvent::Button* out) { + std::string button = base::StringToLowerASCII(V8ToString(val)); + if (button == "left") + *out = blink::WebMouseEvent::Button::ButtonLeft; + else if (button == "middle") + *out = blink::WebMouseEvent::Button::ButtonMiddle; + else if (button == "right") + *out = blink::WebMouseEvent::Button::ButtonRight; + return true; + } +}; + template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, @@ -142,16 +158,19 @@ bool Converter::FromV8( return false; if (!ConvertFromV8(isolate, val, static_cast(out))) return false; - char code; + base::char16 code; if (!dict.Get("keyCode", &code)) return false; bool shifted = false; out->windowsKeyCode = atom::KeyboardCodeFromCharCode(code, &shifted); - if (out->windowsKeyCode == ui::VKEY_UNKNOWN) - return false; if (shifted) out->modifiers |= blink::WebInputEvent::ShiftKey; out->setKeyIdentifierFromWindowsKeyCode(); + if (out->type == blink::WebInputEvent::Char || + out->type == blink::WebInputEvent::RawKeyDown) { + out->text[0] = code; + out->unmodifiedText[0] = code; + } return true; } @@ -176,6 +195,7 @@ bool Converter::FromV8( return false; if (!dict.Get("x", &out->x) || !dict.Get("y", &out->y)) return false; + dict.Get("button", &out->button); dict.Get("globalX", &out->globalX); dict.Get("globalY", &out->globalY); dict.Get("movementX", &out->movementX); diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 5aec200550ad..2da68854ad14 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -34,6 +34,7 @@ REFERENCE_MODULE(atom_browser_app); REFERENCE_MODULE(atom_browser_auto_updater); REFERENCE_MODULE(atom_browser_content_tracing); REFERENCE_MODULE(atom_browser_dialog); +REFERENCE_MODULE(atom_browser_download_item); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_power_save_blocker); diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index c70e1ba4afa6..46687becf84a 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -113,6 +113,10 @@ const char kDisableHttpCache[] = "disable-http-cache"; // Register schemes to standard. const char kRegisterStandardSchemes[] = "register-standard-schemes"; +// The minimum SSL/TLS version ("tls1", "tls1.1", or "tls1.2") that +// TLS fallback will accept. +const char kSSLVersionFallbackMin[] = "ssl-version-fallback-min"; + // The browser process app model ID const char kAppUserModelId[] = "app-user-model-id"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index e62f3116661a..16046d19c822 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -59,6 +59,7 @@ extern const char kPageVisibility[]; extern const char kDisableHttpCache[]; extern const char kRegisterStandardSchemes[]; +extern const char kSSLVersionFallbackMin[]; extern const char kAppUserModelId[]; diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index 6e2054453a79..4506658588c2 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -98,6 +98,16 @@ void WebFrame::RegisterURLSchemeAsBypassingCsp(const std::string& scheme) { blink::WebString::fromUTF8(scheme)); } +void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme) { + // Register scheme to privileged list (https, wss, data, chrome-extension) + blink::WebString privileged_scheme(blink::WebString::fromUTF8(scheme)); + blink::WebSecurityPolicy::registerURLSchemeAsSecure(privileged_scheme); + blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy( + privileged_scheme); + blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers( + privileged_scheme); +} + mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( v8::Isolate* isolate) { return mate::ObjectTemplateBuilder(isolate) @@ -116,7 +126,9 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( .SetMethod("registerUrlSchemeAsSecure", &WebFrame::RegisterURLSchemeAsSecure) .SetMethod("registerUrlSchemeAsBypassingCsp", - &WebFrame::RegisterURLSchemeAsBypassingCsp); + &WebFrame::RegisterURLSchemeAsBypassingCsp) + .SetMethod("registerUrlSchemeAsPrivileged", + &WebFrame::RegisterURLSchemeAsPrivileged); } // static diff --git a/atom/renderer/api/atom_api_web_frame.h b/atom/renderer/api/atom_api_web_frame.h index f3895353b97c..a3dec6cb7689 100644 --- a/atom/renderer/api/atom_api_web_frame.h +++ b/atom/renderer/api/atom_api_web_frame.h @@ -58,6 +58,7 @@ class WebFrame : public mate::Wrappable { void RegisterURLSchemeAsSecure(const std::string& scheme); void RegisterURLSchemeAsBypassingCsp(const std::string& scheme); + void RegisterURLSchemeAsPrivileged(const std::string& scheme); // mate::Wrappable: virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index abd86e7eee0c..1f17cf340020 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -1,4 +1,3 @@ -process = global.process ipc = require 'ipc' v8Util = process.atomBinding 'v8_util' CallbacksRegistry = require 'callbacks-registry' @@ -131,7 +130,7 @@ exports.require = (module) -> windowCache = null exports.getCurrentWindow = -> return windowCache if windowCache? - meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId + meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WINDOW' windowCache = metaToValue meta # Get current WebContents object. diff --git a/atom/renderer/lib/init.coffee b/atom/renderer/lib/init.coffee index 9825f75be928..b7224b39aeed 100644 --- a/atom/renderer/lib/init.coffee +++ b/atom/renderer/lib/init.coffee @@ -1,23 +1,22 @@ -process = global.process -events = require 'events' -path = require 'path' -url = require 'url' -Module = require 'module' +events = require 'events' +path = require 'path' +url = require 'url' +Module = require 'module' # We modified the original process.argv to let node.js load the # atom-renderer.js, we need to restore it here. process.argv.splice 1, 1 +# Clear search paths. +require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths') + +# Import common settings. +require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init') + # Add renderer/api/lib to require's search paths, which contains javascript part # of Atom's built-in libraries. globalPaths = Module.globalPaths globalPaths.push path.resolve(__dirname, '..', 'api', 'lib') -# And also app. -globalPaths.push path.join(process.resourcesPath, 'app') -globalPaths.push path.join(process.resourcesPath, 'app.asar') - -# Import common settings. -require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init') # The global variable will be used by ipc for event dispatching v8Util = process.atomBinding 'v8_util' diff --git a/atom/renderer/lib/inspector.coffee b/atom/renderer/lib/inspector.coffee index 126f68f9608e..5f08a2acf8d8 100644 --- a/atom/renderer/lib/inspector.coffee +++ b/atom/renderer/lib/inspector.coffee @@ -44,7 +44,7 @@ createMenu = (x, y, items, document) -> showFileChooserDialog = (callback) -> remote = require 'remote' dialog = remote.require 'dialog' - files = dialog.showOpenDialog remote.getCurrentWindow(), null + files = dialog.showOpenDialog {} callback pathToHtml5FileObject files[0] if files? pathToHtml5FileObject = (path) -> diff --git a/atom/renderer/lib/override.coffee b/atom/renderer/lib/override.coffee index b60374e04754..93cf8b8357e8 100644 --- a/atom/renderer/lib/override.coffee +++ b/atom/renderer/lib/override.coffee @@ -1,4 +1,3 @@ -process = global.process ipc = require 'ipc' remote = require 'remote' @@ -71,7 +70,6 @@ window.open = (url, frameName='', features='') -> if guestId new BrowserWindowProxy(guestId) else - console.error 'It is not allowed to open new window from this WebContents' null # Use the dialog API to implement alert(). @@ -124,3 +122,7 @@ window.history.go = (offset) -> sendHistoryOperation 'goToOffset', offset Object.defineProperty window.history, 'length', get: -> getHistoryOperation 'length' + +# Make document.hidden return the correct value. +Object.defineProperty document, 'hidden', + get: -> !remote.getCurrentWindow().isVisible() diff --git a/atom/renderer/lib/web-view/guest-view-internal.coffee b/atom/renderer/lib/web-view/guest-view-internal.coffee index b491184fb8d7..2852d1122874 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.coffee +++ b/atom/renderer/lib/web-view/guest-view-internal.coffee @@ -16,7 +16,7 @@ WEB_VIEW_EVENTS = 'did-get-redirect-request': ['oldUrl', 'newUrl', 'isMainFrame'] 'dom-ready': [] 'console-message': ['level', 'message', 'line', 'sourceId'] - 'new-window': ['url', 'frameName', 'disposition'] + 'new-window': ['url', 'frameName', 'disposition', 'options'] 'close': [] 'crashed': [] 'gpu-crashed': [] diff --git a/atom/renderer/lib/web-view/web-view-attributes.coffee b/atom/renderer/lib/web-view/web-view-attributes.coffee index acca826a9e3d..e980e6c96bfc 100644 --- a/atom/renderer/lib/web-view/web-view-attributes.coffee +++ b/atom/renderer/lib/web-view/web-view-attributes.coffee @@ -216,6 +216,7 @@ WebViewImpl::setupWebViewAttributes = -> @attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this) @attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this) @attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this) + @attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this) @attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) autosizeAttributes = [ diff --git a/atom/renderer/lib/web-view/web-view-constants.coffee b/atom/renderer/lib/web-view/web-view-constants.coffee index deb678e6a1e8..bfb9376fa7ea 100644 --- a/atom/renderer/lib/web-view/web-view-constants.coffee +++ b/atom/renderer/lib/web-view/web-view-constants.coffee @@ -13,6 +13,7 @@ module.exports = ATTRIBUTE_NODEINTEGRATION: 'nodeintegration' ATTRIBUTE_PLUGINS: 'plugins' ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity' + ATTRIBUTE_ALLOWPOPUPS: 'allowpopups' ATTRIBUTE_PRELOAD: 'preload' ATTRIBUTE_USERAGENT: 'useragent' diff --git a/atom/renderer/lib/web-view/web-view.coffee b/atom/renderer/lib/web-view/web-view.coffee index 65e4501975fe..3a563101f003 100644 --- a/atom/renderer/lib/web-view/web-view.coffee +++ b/atom/renderer/lib/web-view/web-view.coffee @@ -46,6 +46,7 @@ class WebViewImpl # that we don't end up allocating a second guest. if @guestInstanceId guestViewInternal.destroyGuest @guestInstanceId + @webContents = null @guestInstanceId = undefined @beforeFirstNavigation = true @attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true @@ -188,6 +189,7 @@ class WebViewImpl attachWindow: (guestInstanceId) -> @guestInstanceId = guestInstanceId + @webContents = remote.getGuestWebContents @guestInstanceId return true unless @internalInstanceId guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildParams() @@ -299,7 +301,7 @@ registerWebViewElement = -> createHandler = (m) -> (args...) -> internal = v8Util.getHiddenValue this, 'internal' - remote.getGuestWebContents(internal.guestInstanceId)[m] args... + internal.webContents[m] args... proto[m] = createHandler m for m in methods window.WebView = webFrame.registerEmbedderCustomElement 'webview', diff --git a/chromium_src/extensions/browser/app_window/size_constraints.cc b/chromium_src/extensions/browser/app_window/size_constraints.cc new file mode 100644 index 000000000000..6d248c16017a --- /dev/null +++ b/chromium_src/extensions/browser/app_window/size_constraints.cc @@ -0,0 +1,83 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/app_window/size_constraints.h" + +#include + +#include "ui/gfx/geometry/insets.h" + +namespace extensions { + +SizeConstraints::SizeConstraints() + : maximum_size_(kUnboundedSize, kUnboundedSize) {} + +SizeConstraints::SizeConstraints(const gfx::Size& min_size, + const gfx::Size& max_size) + : minimum_size_(min_size), maximum_size_(max_size) {} + +SizeConstraints::~SizeConstraints() {} + +// static +gfx::Size SizeConstraints::AddFrameToConstraints( + const gfx::Size& size_constraints, + const gfx::Insets& frame_insets) { + return gfx::Size( + size_constraints.width() == kUnboundedSize + ? kUnboundedSize + : size_constraints.width() + frame_insets.width(), + size_constraints.height() == kUnboundedSize + ? kUnboundedSize + : size_constraints.height() + frame_insets.height()); +} + +gfx::Size SizeConstraints::ClampSize(gfx::Size size) const { + const gfx::Size max_size = GetMaximumSize(); + if (max_size.width() != kUnboundedSize) + size.set_width(std::min(size.width(), max_size.width())); + if (max_size.height() != kUnboundedSize) + size.set_height(std::min(size.height(), max_size.height())); + size.SetToMax(GetMinimumSize()); + return size; +} + +bool SizeConstraints::HasMinimumSize() const { + const gfx::Size min_size = GetMinimumSize(); + return min_size.width() != kUnboundedSize || + min_size.height() != kUnboundedSize; +} + +bool SizeConstraints::HasMaximumSize() const { + const gfx::Size max_size = GetMaximumSize(); + return max_size.width() != kUnboundedSize || + max_size.height() != kUnboundedSize; +} + +bool SizeConstraints::HasFixedSize() const { + return !GetMinimumSize().IsEmpty() && GetMinimumSize() == GetMaximumSize(); +} + +gfx::Size SizeConstraints::GetMinimumSize() const { + return minimum_size_; +} + +gfx::Size SizeConstraints::GetMaximumSize() const { + return gfx::Size( + maximum_size_.width() == kUnboundedSize + ? kUnboundedSize + : std::max(maximum_size_.width(), minimum_size_.width()), + maximum_size_.height() == kUnboundedSize + ? kUnboundedSize + : std::max(maximum_size_.height(), minimum_size_.height())); +} + +void SizeConstraints::set_minimum_size(const gfx::Size& min_size) { + minimum_size_ = min_size; +} + +void SizeConstraints::set_maximum_size(const gfx::Size& max_size) { + maximum_size_ = max_size; +} + +} // namespace extensions diff --git a/chromium_src/extensions/browser/app_window/size_constraints.h b/chromium_src/extensions/browser/app_window/size_constraints.h new file mode 100644 index 000000000000..ecacf1e5eb13 --- /dev/null +++ b/chromium_src/extensions/browser/app_window/size_constraints.h @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_APP_WINDOW_SIZE_CONSTRAINTS_H_ +#define EXTENSIONS_BROWSER_APP_WINDOW_SIZE_CONSTRAINTS_H_ + +#include "ui/gfx/geometry/size.h" + +namespace gfx { +class Insets; +} + +namespace extensions { + +class SizeConstraints { + public: + // The value SizeConstraints uses to represent an unbounded width or height. + // This is an enum so that it can be declared inline here. + enum { kUnboundedSize = 0 }; + + SizeConstraints(); + SizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size); + ~SizeConstraints(); + + // Adds frame insets to a size constraint. + static gfx::Size AddFrameToConstraints(const gfx::Size& size_constraints, + const gfx::Insets& frame_insets); + + // Returns the bounds with its size clamped to the min/max size. + gfx::Size ClampSize(gfx::Size size) const; + + // When gfx::Size is used as a min/max size, a zero represents an unbounded + // component. This method checks whether either component is specified. + // Note we can't use gfx::Size::IsEmpty as it returns true if either width + // or height is zero. + bool HasMinimumSize() const; + bool HasMaximumSize() const; + + // This returns true if all components are specified, and min and max are + // equal. + bool HasFixedSize() const; + + gfx::Size GetMaximumSize() const; + gfx::Size GetMinimumSize() const; + + void set_minimum_size(const gfx::Size& min_size); + void set_maximum_size(const gfx::Size& max_size); + + private: + gfx::Size minimum_size_; + gfx::Size maximum_size_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_APP_WINDOW_SIZE_CONSTRAINTS_H_ diff --git a/docs-translations/ko/README.md b/docs-translations/ko-KR/README.md similarity index 90% rename from docs-translations/ko/README.md rename to docs-translations/ko-KR/README.md index 2fb242fae906..9b88aa6e1780 100644 --- a/docs-translations/ko/README.md +++ b/docs-translations/ko-KR/README.md @@ -1,5 +1,6 @@ ## 개발 가이드 +* [지원하는 플랫폼](tutorial/supported-platforms.md) * [어플리케이션 배포](tutorial/application-distribution.md) * [어플리케이션 패키징](tutorial/application-packaging.md) * [네이티브 Node 모듈 사용하기](tutorial/using-native-node-modules.md) @@ -26,7 +27,7 @@ * [`` 태그](api/web-view-tag.md) * [`window.open` 함수](api/window-open.md) -### 메인 프로세스를 위한 모듈들: +### 메인 프로세스에서 사용할 수 있는 모듈: * [app (0% 번역됨)](api/app.md) * [auto-updater](api/auto-updater.md) @@ -44,13 +45,13 @@ * [web-contents (0% 번역됨)](api/web-contents.md) * [tray](api/tray.md) -### 랜더러 프로세스를 위한 모듈들 (웹 페이지): +### 랜더러 프로세스에서 사용할 수 있는 모듈 (웹 페이지): * [ipc (renderer)](api/ipc-renderer.md) * [remote](api/remote.md) * [web-frame](api/web-frame.md) -### 두 프로세스에서 모두 사용 가능한 모듈들: +### 두 프로세스 모두 사용할 수 있는 모듈: * [clipboard](api/clipboard.md) * [crash-reporter](api/crash-reporter.md) diff --git a/docs-translations/ko/api/accelerator.md b/docs-translations/ko-KR/api/accelerator.md similarity index 100% rename from docs-translations/ko/api/accelerator.md rename to docs-translations/ko-KR/api/accelerator.md diff --git a/docs-translations/ko/api/auto-updater.md b/docs-translations/ko-KR/api/auto-updater.md similarity index 100% rename from docs-translations/ko/api/auto-updater.md rename to docs-translations/ko-KR/api/auto-updater.md diff --git a/docs-translations/ko/api/chrome-command-line-switches.md b/docs-translations/ko-KR/api/chrome-command-line-switches.md similarity index 78% rename from docs-translations/ko/api/chrome-command-line-switches.md rename to docs-translations/ko-KR/api/chrome-command-line-switches.md index 5b9bf4722042..777ce4612d17 100644 --- a/docs-translations/ko/api/chrome-command-line-switches.md +++ b/docs-translations/ko-KR/api/chrome-command-line-switches.md @@ -1,6 +1,6 @@ # 크롬 Command-Line 스위치 지원 -다음 Command-Line 스위치들은 크롬 브라우저에서 제공되는 추가 옵션이며 Electron에서도 지원합니다. +크롬 Command-Line 스위치는 크롬 브라우저에서 제공되는 추가 옵션이며 Electron에서도 지원합니다. [app][app]의 [ready][ready]이벤트가 작동하기 전에 [app.commandLine.appendSwitch][append-switch] API를 사용하면 어플리케이션 내부에서 스위치들을 추가할 수 있습니다: @@ -80,26 +80,30 @@ Pepper 플래시 플러그인의 버전을 설정합니다. ## --log-net-log=`path` -Net log 이벤트를 지정한 `path`에 로그로 기록합니다. +Net log 이벤트를 활성화하고 `path`에 로그를 기록합니다. + +## --ssl-version-fallback-min=`version` + +TLS fallback에서 사용할 SSL/TLS 최소 버전을 지정합니다. ("tls1", "tls1.1", "tls1.2") + +## --enable-logging + +Chromium의 로그를 콘솔에 출력합니다. + +이 스위치는 어플리케이션이 로드되기 전에 파싱 되므로 `app.commandLine.appendSwitch`에서 사용할 수 없습니다. ## --v=`log_level` 기본 V-logging 최대 활성화 레벨을 지정합니다. 기본값은 0입니다. 기본적으로 양수를 레벨로 사용합니다. -`--v=-1`를 사용하면 로깅이 비활성화 됩니다. +이 스위치는 `--enable-logging` 스위치를 같이 지정해야 작동합니다. ## --vmodule=`pattern` `--v` 옵션에 전달된 값을 덮어쓰고 모듈당 최대 V-logging 레벨을 지정합니다. 예를 들어 `my_module=2,foo*=3`는 `my_module.*`, `foo*.*`와 같은 파일 이름 패턴을 가진 모든 소스 코드들의 로깅 레벨을 각각 2와 3으로 설정합니다. -슬래시(`/`), 백슬래시(`\`)를 포함하는 모든 패턴은 모듈뿐만 아니라 모든 경로명에 대해서도 테스트 됩니다. +또한 슬래시(`/`) 또는 백슬래시(`\`)를 포함하는 패턴은 지정한 경로에 대해 패턴을 테스트 합니다. 예를 들어 `*/foo/bar/*=2` 표현식은 `foo/bar` 디렉터리 안의 모든 소스 코드의 로깅 레벨을 2로 지정합니다. -모든 크로미움과 관련된 로그를 비활성화하고 어플리케이션의 로그만 활성화 하려면 다음과 같이 코드를 작성하면 됩니다: - - -```javascript -app.commandLine.appendSwitch('v', -1); -app.commandLine.appendSwitch('vmodule', 'console=0'); -``` +이 스위치는 `--enable-logging` 스위치를 같이 지정해야 작동합니다. diff --git a/docs-translations/ko/api/clipboard.md b/docs-translations/ko-KR/api/clipboard.md similarity index 100% rename from docs-translations/ko/api/clipboard.md rename to docs-translations/ko-KR/api/clipboard.md diff --git a/docs-translations/ko/api/content-tracing.md b/docs-translations/ko-KR/api/content-tracing.md similarity index 100% rename from docs-translations/ko/api/content-tracing.md rename to docs-translations/ko-KR/api/content-tracing.md diff --git a/docs-translations/ko/api/crash-reporter.md b/docs-translations/ko-KR/api/crash-reporter.md similarity index 100% rename from docs-translations/ko/api/crash-reporter.md rename to docs-translations/ko-KR/api/crash-reporter.md diff --git a/docs-translations/ko/api/dialog.md b/docs-translations/ko-KR/api/dialog.md similarity index 100% rename from docs-translations/ko/api/dialog.md rename to docs-translations/ko-KR/api/dialog.md diff --git a/docs-translations/ko/api/file-object.md b/docs-translations/ko-KR/api/file-object.md similarity index 100% rename from docs-translations/ko/api/file-object.md rename to docs-translations/ko-KR/api/file-object.md diff --git a/docs-translations/ko/api/frameless-window.md b/docs-translations/ko-KR/api/frameless-window.md similarity index 100% rename from docs-translations/ko/api/frameless-window.md rename to docs-translations/ko-KR/api/frameless-window.md diff --git a/docs-translations/ko/api/global-shortcut.md b/docs-translations/ko-KR/api/global-shortcut.md similarity index 100% rename from docs-translations/ko/api/global-shortcut.md rename to docs-translations/ko-KR/api/global-shortcut.md diff --git a/docs-translations/ko/api/ipc-main-process.md b/docs-translations/ko-KR/api/ipc-main-process.md similarity index 100% rename from docs-translations/ko/api/ipc-main-process.md rename to docs-translations/ko-KR/api/ipc-main-process.md diff --git a/docs-translations/ko/api/ipc-renderer.md b/docs-translations/ko-KR/api/ipc-renderer.md similarity index 100% rename from docs-translations/ko/api/ipc-renderer.md rename to docs-translations/ko-KR/api/ipc-renderer.md diff --git a/docs-translations/ko/api/menu-item.md b/docs-translations/ko-KR/api/menu-item.md similarity index 92% rename from docs-translations/ko/api/menu-item.md rename to docs-translations/ko-KR/api/menu-item.md index 8f1021009954..e4c5c677676d 100644 --- a/docs-translations/ko/api/menu-item.md +++ b/docs-translations/ko-KR/api/menu-item.md @@ -1,6 +1,6 @@ # MenuItem -`menu-item` 모듈은 어플리케이션 또는 컨텐츠 [`menu`](menu.md)에 아이템을 추가할 수 있도록 관련 클래스를 제공합니다. +`menu-item` 모듈은 어플리케이션 또는 컨텍스트 [`menu`](menu.md)에 아이템을 추가할 수 있도록 관련 클래스를 제공합니다. [`menu`](menu.md)에서 예제를 확인할 수 있습니다. diff --git a/docs-translations/ko/api/menu.md b/docs-translations/ko-KR/api/menu.md similarity index 98% rename from docs-translations/ko/api/menu.md rename to docs-translations/ko-KR/api/menu.md index a8ac499f5768..56b7f4a198e5 100644 --- a/docs-translations/ko/api/menu.md +++ b/docs-translations/ko-KR/api/menu.md @@ -229,11 +229,11 @@ Menu.setApplicationMenu(menu); 또한 `template`에는 다른 속성도 추가할 수 있으며 메뉴가 만들어질 때 해당 메뉴 아이템의 프로퍼티로 변환됩니다. -### `Menu.popup(browserWindow[, x, y])` +### `Menu.popup([browserWindow, x, y])` -* `browserWindow` BrowserWindow +* `browserWindow` BrowserWindow (optional) * `x` Number (optional) -* `y` Number (만약 `x`를 지정했을 경우 `y`도 필수로 지정해야 합니다) +* `y` Number (만약 `x`를 지정했을 경우 반드시 `y`도 지정해야 합니다) 메뉴를 `browserWindow` 내부 팝업으로 표시합니다. 옵션으로 메뉴를 표시할 `(x,y)` 좌표를 지정할 수 있습니다. diff --git a/docs-translations/ko/api/native-image.md b/docs-translations/ko-KR/api/native-image.md similarity index 98% rename from docs-translations/ko/api/native-image.md rename to docs-translations/ko-KR/api/native-image.md index 423cedb98300..485ab7bc71d4 100644 --- a/docs-translations/ko/api/native-image.md +++ b/docs-translations/ko-KR/api/native-image.md @@ -142,4 +142,4 @@ var image = NativeImage.createFromPath('/Users/somebody/images/icon.png'); 이미지가 템플릿 이미지인지 확인합니다. -[buffer]: https://iojs.org/api/buffer.html#buffer_class_buffer +[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer diff --git a/docs-translations/ko/api/power-monitor.md b/docs-translations/ko-KR/api/power-monitor.md similarity index 100% rename from docs-translations/ko/api/power-monitor.md rename to docs-translations/ko-KR/api/power-monitor.md diff --git a/docs-translations/ko/api/power-save-blocker.md b/docs-translations/ko-KR/api/power-save-blocker.md similarity index 100% rename from docs-translations/ko/api/power-save-blocker.md rename to docs-translations/ko-KR/api/power-save-blocker.md diff --git a/docs-translations/ko/api/process.md b/docs-translations/ko-KR/api/process.md similarity index 58% rename from docs-translations/ko/api/process.md rename to docs-translations/ko-KR/api/process.md index f557f8cc6be5..a35c8b4e93d8 100644 --- a/docs-translations/ko/api/process.md +++ b/docs-translations/ko-KR/api/process.md @@ -7,6 +7,24 @@ Electron의 `process` 객체는 기존의 node와는 달리 약간의 차이점 * `process.versions['chrome']` String - Chromium의 버전. * `process.resourcesPath` String - JavaScript 소스코드의 경로. +## Events + +### Event: 'loaded' + +Electron 내부 초기화 스크립트의 로드가 완료되고, 웹 페이지나 메인 스크립트를 로드하기 시작할 때 발생하는 이벤트입니다. + +이 이벤트는 preload 스크립트를 통해 node 통합이 꺼져있는 전역 스코프에 node의 전역 심볼들을 다시 추가할 때 사용할 수 있습니다: + +```javascript +// preload.js +var _setImmediate = setImmediate; +var _clearImmediate = clearImmediate; +process.once('loaded', function() { + global.setImmediate = _setImmediate; + global.clearImmediate = _clearImmediate; +}); +``` + ## Methods `process` 객체는 다음과 같은 메서드를 가지고 있습니다: diff --git a/docs-translations/ko/api/protocol.md b/docs-translations/ko-KR/api/protocol.md similarity index 100% rename from docs-translations/ko/api/protocol.md rename to docs-translations/ko-KR/api/protocol.md diff --git a/docs-translations/ko/api/remote.md b/docs-translations/ko-KR/api/remote.md similarity index 99% rename from docs-translations/ko/api/remote.md rename to docs-translations/ko-KR/api/remote.md index 15c82ff0d21e..756acd429a1b 100644 --- a/docs-translations/ko/api/remote.md +++ b/docs-translations/ko-KR/api/remote.md @@ -62,7 +62,7 @@ exports.withLocalCallback = function() { ```javascript // 랜더러 프로세스 -var mapNumbers = require("remote").require("mapNumbers"); +var mapNumbers = require("remote").require("./mapNumbers"); var withRendererCb = mapNumbers.withRendererCallback(function(x) { return x + 1; diff --git a/docs-translations/ko/api/screen.md b/docs-translations/ko-KR/api/screen.md similarity index 86% rename from docs-translations/ko/api/screen.md rename to docs-translations/ko-KR/api/screen.md index 26975cbb1e27..c65540eba228 100644 --- a/docs-translations/ko/api/screen.md +++ b/docs-translations/ko-KR/api/screen.md @@ -6,7 +6,7 @@ `screen`은 [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)를 상속 받았습니다. **참고:** 랜더러 / DevTools에선 이미 DOM 속성이 `window.screen`을 가지고 있으므로 `screen = require('screen')` 형식으로 모듈을 사용할 수 없습니다. -밑의 예제와 같이 `atomScreen` 같은 이름으로 모듈 이름을 대체하여 사용해야 합니다. +아래의 예제와 같이 `electronScreen` 같은 이름으로 모듈 이름을 대체하여 사용해야 합니다. 다음 예제는 화면 전체를 채우는 윈도우 창을 생성합니다: @@ -17,8 +17,8 @@ var BrowserWindow = require('browser-window'); var mainWindow; app.on('ready', function() { - var atomScreen = require('screen'); - var size = atomScreen.getPrimaryDisplay().workAreaSize; + var electronScreen = require('screen'); + var size = electronScreen.getPrimaryDisplay().workAreaSize; mainWindow = new BrowserWindow({ width: size.width, height: size.height }); }); ``` @@ -32,11 +32,11 @@ var BrowserWindow = require('browser-window'); var mainWindow; app.on('ready', function() { - var atomScreen = require('screen'); - var displays = atomScreen.getAllDisplays(); + var electronScreen = require('screen'); + var displays = electronScreen.getAllDisplays(); var externalDisplay = null; for (var i in displays) { - if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) { + if (displays[i].bounds.x != 0 || displays[i].bounds.y != 0) { externalDisplay = displays[i]; break; } @@ -45,7 +45,7 @@ app.on('ready', function() { if (externalDisplay) { mainWindow = new BrowserWindow({ x: externalDisplay.bounds.x + 50, - y: externalDisplay.bounds.y + 50, + y: externalDisplay.bounds.y + 50 }); } }); diff --git a/docs-translations/ko/api/session.md b/docs-translations/ko-KR/api/session.md similarity index 88% rename from docs-translations/ko/api/session.md rename to docs-translations/ko-KR/api/session.md index cb912aabb100..96d38dda5d62 100644 --- a/docs-translations/ko/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -17,11 +17,7 @@ var session = win.webContents.session ### Event: 'will-download' * `event` Event -* `item` Object - * `url` String - * `filename` String - * `mimeType` String - * `hasUserGesture` Boolean +* `item` [DownloadItem](download-item.md) * `webContents` [WebContents](web-contents.md) Electron의 `webContents`에서 `item`을 다운로드할 때 발생하는 이벤트입니다. @@ -31,7 +27,7 @@ Electron의 `webContents`에서 `item`을 다운로드할 때 발생하는 이 ```javascript session.on('will-download', function(event, item, webContents) { event.preventDefault(); - require('request')(item.url, function(data) { + require('request')(item.getUrl(), function(data) { require('fs').writeFileSync('/somewhere', data); }); }); @@ -185,3 +181,29 @@ proxy-uri = ["://"][":"] * `path` String - 다운로드 위치 다운로드 저장 위치를 지정합니다. 기본 다운로드 위치는 각 어플리케이션 데이터 디렉터리의 `Downloads` 폴더입니다. + +### `session.enableNetworkEmulation(options)` + +* `options` Object + * `offline` Boolean - 네트워크의 오프라인 상태 여부 + * `latency` Double - 밀리세컨드 단위의 RTT + * `downloadThroughput` Double - Bps 단위의 다운로드 주기 + * `uploadThroughput` Double - Bps 단위의 업로드 주기 + +제공된 설정으로 `session`의 네트워크를 에뮬레이트합니다. + +```javascript +// 50kbps의 처리량과 함께 500ms의 레이턴시로 GPRS 연결을 에뮬레이트합니다. +window.webContents.session.enableNetworkEmulation({ + latency: 500, + downloadThroughput: 6400, + uploadThroughput: 6400 +}); + +// 네트워크가 끊긴 상태를 에뮬레이트합니다. +window.webContents.session.enableNetworkEmulation({offline: true}); +``` + +### `session.disableNetworkEmulation` + +활성화된 `session`의 에뮬레이션을 비활성화합니다. 기본 네트워크 설정으로 돌아갑니다. diff --git a/docs-translations/ko/api/shell.md b/docs-translations/ko-KR/api/shell.md similarity index 100% rename from docs-translations/ko/api/shell.md rename to docs-translations/ko-KR/api/shell.md diff --git a/docs-translations/ko/api/synopsis.md b/docs-translations/ko-KR/api/synopsis.md similarity index 100% rename from docs-translations/ko/api/synopsis.md rename to docs-translations/ko-KR/api/synopsis.md diff --git a/docs-translations/ko/api/tray.md b/docs-translations/ko-KR/api/tray.md similarity index 100% rename from docs-translations/ko/api/tray.md rename to docs-translations/ko-KR/api/tray.md diff --git a/docs-translations/ko/api/web-frame.md b/docs-translations/ko-KR/api/web-frame.md similarity index 100% rename from docs-translations/ko/api/web-frame.md rename to docs-translations/ko-KR/api/web-frame.md diff --git a/docs-translations/ko/api/web-view-tag.md b/docs-translations/ko-KR/api/web-view-tag.md similarity index 95% rename from docs-translations/ko/api/web-view-tag.md rename to docs-translations/ko-KR/api/web-view-tag.md index 90f5acb9381f..69b94465aede 100644 --- a/docs-translations/ko/api/web-view-tag.md +++ b/docs-translations/ko-KR/api/web-view-tag.md @@ -135,6 +135,14 @@ 즉. 작동중인 랜더러 프로세스의 세션은 변경할 수 없습니다. 이후 이 값을 바꾸려고 시도하면 DOM 예외를 발생시킵니다. +### `allowpopups` + +```html + +``` + +"on"으로 지정하면 페이지에서 새로운 창을 열 수 있도록 허용합니다. + ## Methods `webview` 태그는 다음과 같은 메서드를 가지고 있습니다: @@ -335,6 +343,14 @@ Webview 페이지를 PDF 형식으로 인쇄합니다. `webContents.printToPDF(o 예제는 [WebContents.send](web-contents.md#webcontentssendchannel-args)를 참고하세요. +### `.sendInputEvent(event)` + +* `event` Object + +페이지에 input `event`를 보냅니다. + +`event` 객체에 대해 자세한 내용을 알아보려면 [WebContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent)를 참고하세요. + ## DOM 이벤트 `webview` 태그는 다음과 같은 DOM 이벤트를 가지고 있습니다: @@ -459,8 +475,8 @@ Returns: * `url` String * `frameName` String -* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, - `new-window` and `other` +* `disposition` String - `default`, `foreground-tab`, `background-tab`, `new-window`, `other`를 사용할 수 있습니다. +* `options` Object - 새로운 `BrowserWindow`를 만들 때 사용되어야 하는 옵션. 페이지가 새로운 브라우저 창을 생성할 때 발생하는 이벤트입니다. diff --git a/docs-translations/ko/api/window-open.md b/docs-translations/ko-KR/api/window-open.md similarity index 86% rename from docs-translations/ko/api/window-open.md rename to docs-translations/ko-KR/api/window-open.md index a4cf3bce5c93..828c46e679e7 100644 --- a/docs-translations/ko/api/window-open.md +++ b/docs-translations/ko-KR/api/window-open.md @@ -6,6 +6,8 @@ 프록시 객체는 브라우저의 웹 페이지 창과 호환될 수 있도록 일부 제한된 표준 기능만 가지고 있습니다. 창의 모든 컨트롤 권한을 가지려면 `BrowserWindow`를 직접 생성해서 사용해야 합니다. +새롭게 생성된 `BrowserWindow`는 기본적으로 부모 창의 옵션을 상속합니다. 이 옵션을 변경하려면 새 창을 열 때 `features` 인자를 지정해야 합니다. + ### `window.open(url[, frameName][, features])` * `url` String @@ -14,6 +16,8 @@ `BrowserWindowProxy` 클래스의 객체를 반환하는 새로운 윈도우를 생성합니다. +`features` 문자열은 표준 브라우저의 포맷을 따르고 있지만, 각 기능은 `BrowserWindow`의 옵션이어야 합니다. + ### `window.opener.postMessage(message, targetOrigin)` * `message` String diff --git a/docs-translations/ko/development/atom-shell-vs-node-webkit.md b/docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md similarity index 100% rename from docs-translations/ko/development/atom-shell-vs-node-webkit.md rename to docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md diff --git a/docs-translations/ko/development/build-instructions-linux.md b/docs-translations/ko-KR/development/build-instructions-linux.md similarity index 100% rename from docs-translations/ko/development/build-instructions-linux.md rename to docs-translations/ko-KR/development/build-instructions-linux.md diff --git a/docs-translations/ko/development/build-instructions-osx.md b/docs-translations/ko-KR/development/build-instructions-osx.md similarity index 100% rename from docs-translations/ko/development/build-instructions-osx.md rename to docs-translations/ko-KR/development/build-instructions-osx.md diff --git a/docs-translations/ko/development/build-instructions-windows.md b/docs-translations/ko-KR/development/build-instructions-windows.md similarity index 100% rename from docs-translations/ko/development/build-instructions-windows.md rename to docs-translations/ko-KR/development/build-instructions-windows.md diff --git a/docs-translations/ko/development/build-system-overview.md b/docs-translations/ko-KR/development/build-system-overview.md similarity index 100% rename from docs-translations/ko/development/build-system-overview.md rename to docs-translations/ko-KR/development/build-system-overview.md diff --git a/docs-translations/ko/development/coding-style.md b/docs-translations/ko-KR/development/coding-style.md similarity index 100% rename from docs-translations/ko/development/coding-style.md rename to docs-translations/ko-KR/development/coding-style.md diff --git a/docs-translations/ko/development/setting-up-symbol-server.md b/docs-translations/ko-KR/development/setting-up-symbol-server.md similarity index 100% rename from docs-translations/ko/development/setting-up-symbol-server.md rename to docs-translations/ko-KR/development/setting-up-symbol-server.md diff --git a/docs-translations/ko/development/source-code-directory-structure.md b/docs-translations/ko-KR/development/source-code-directory-structure.md similarity index 100% rename from docs-translations/ko/development/source-code-directory-structure.md rename to docs-translations/ko-KR/development/source-code-directory-structure.md diff --git a/docs-translations/ko/styleguide.md b/docs-translations/ko-KR/styleguide.md similarity index 100% rename from docs-translations/ko/styleguide.md rename to docs-translations/ko-KR/styleguide.md diff --git a/docs-translations/ko/tutorial/application-distribution.md b/docs-translations/ko-KR/tutorial/application-distribution.md similarity index 100% rename from docs-translations/ko/tutorial/application-distribution.md rename to docs-translations/ko-KR/tutorial/application-distribution.md diff --git a/docs-translations/ko/tutorial/application-packaging.md b/docs-translations/ko-KR/tutorial/application-packaging.md similarity index 100% rename from docs-translations/ko/tutorial/application-packaging.md rename to docs-translations/ko-KR/tutorial/application-packaging.md diff --git a/docs-translations/ko/tutorial/debugging-main-process.md b/docs-translations/ko-KR/tutorial/debugging-main-process.md similarity index 100% rename from docs-translations/ko/tutorial/debugging-main-process.md rename to docs-translations/ko-KR/tutorial/debugging-main-process.md diff --git a/docs-translations/ko/tutorial/desktop-environment-integration.md b/docs-translations/ko-KR/tutorial/desktop-environment-integration.md similarity index 100% rename from docs-translations/ko/tutorial/desktop-environment-integration.md rename to docs-translations/ko-KR/tutorial/desktop-environment-integration.md diff --git a/docs-translations/ko/tutorial/devtools-extension.md b/docs-translations/ko-KR/tutorial/devtools-extension.md similarity index 100% rename from docs-translations/ko/tutorial/devtools-extension.md rename to docs-translations/ko-KR/tutorial/devtools-extension.md diff --git a/docs-translations/ko/tutorial/online-offline-events.md b/docs-translations/ko-KR/tutorial/online-offline-events.md similarity index 100% rename from docs-translations/ko/tutorial/online-offline-events.md rename to docs-translations/ko-KR/tutorial/online-offline-events.md diff --git a/docs-translations/ko/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md similarity index 88% rename from docs-translations/ko/tutorial/quick-start.md rename to docs-translations/ko-KR/tutorial/quick-start.md index 8c2e043061ec..6eaad7476958 100644 --- a/docs-translations/ko/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -3,7 +3,7 @@ ## 소개 Electron은 자바스크립트와 함께 제공된 풍부한 네이티브 API를 사용하여 멋진 데스크탑 어플리케이션을 만들 수 있도록 해주는 프레임워크입니다. -이 프레임워크의 io.js(node.js)는 웹 서버 개발이 아닌 데스크탑 어플리케이션 개발에 초점을 맞췄습니다. +이 프레임워크의 Node.js는 웹 서버 개발이 아닌 데스크탑 어플리케이션 개발에 초점을 맞췄습니다. 이 말은 Electron이 GUI 라이브러리의 자바스크립트 바인딩이라는 뜻이 아닙니다. 대신, Electron은 웹 페이지의 GUI를 사용합니다. 쉽게 말해 Electron은 자바스크립트를 사용하여 조작하는 작은 Chromium 브라우저로 볼 수 있습니다. @@ -19,7 +19,7 @@ Electron이 웹페이지를 보여줄 때 Chromium의 multi-processes 구조도 Electron 프로세스 내에서 작동하는 웹 페이지를 __랜더러 프로세스__ 라고 불립니다. 보통 일반 브라우저의 웹 페이지들은 샌드박스가 적용된 환경에서 작동하며 네이티브 리소스에는 접근할 수 없도록 되어 있습니다. -하지만 Electron은 웹 페이지 내에서 io.js(node.js) API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수 있습니다. +하지만 Electron은 웹 페이지 내에서 Node.js API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수 있습니다. ### 메인 프로세스와 랜더러 프로세스의 차이점 @@ -111,11 +111,12 @@ app.on('ready', function() { + 헬로 월드!

헬로 월드!

- 이 어플리케이션은 io.js 과 + 이 어플리케이션은 Node.js 과 Electron 을 사용합니다. @@ -126,31 +127,31 @@ app.on('ready', function() { 앱을 작성한 후 [어플리케이션 배포](application-distribution.md) 가이드를 따라 앱을 패키징 하고 패키징한 앱을 실행할 수 있습니다. 또한 Electron 실행파일을 다운로드 받아 바로 실행해 볼 수도 있습니다. -### electron-prebuilt +### electron-prebuilt 사용 `npm`을 통해 `electron-prebuilt` 패키지를 전역에 설치하면 간단한 명령으로 앱을 실행할 수 있습니다. -앱 디렉터리 내에서 이렇게 실행합니다: +앱 디렉터리 내에서 다음 명령으로 실행할 수 있습니다: ```bash electron . ``` -또는 앱 디렉터리 밖에서 앱 디렉터리를 입력해도 됩니다: +또는 앱 디렉터리 밖에서 앱을 실행할 수도 있습니다: ```bash electron app ``` -npm 모듈을 로컬에 설치했다면 이렇게 실행할 수 있습니다: +npm 모듈을 로컬에 설치했다면 다음 명령으로 실행할 수 있습니다: ```bash ./node_modules/.bin/electron . ``` -### 임의로 다운로드 받은 Electron +### 다운로드 받은 Electron 바이너리 사용 -만약 Electron 바이너리를 임의로 다운로드 받았다면 다음과 같이 앱 디렉터리에 놓고 실행하면 됩니다. +따로 Electron 바이너리를 다운로드 받았다면 다음 예시와 같이 실행하면 됩니다. #### Windows diff --git a/docs-translations/ko-KR/tutorial/supported-platforms.md b/docs-translations/ko-KR/tutorial/supported-platforms.md new file mode 100644 index 000000000000..7a35da0c129a --- /dev/null +++ b/docs-translations/ko-KR/tutorial/supported-platforms.md @@ -0,0 +1,26 @@ +# 지원하는 플랫폼 + +Electron에선 다음과 같은 플랫폼을 지원합니다: + +### OS X + +OS X는 64비트 바이너리만 제공됩니다. 그리고 최소 OS X 지원 버전은 10.8입니다. + +### Windows + +Windows 7 이후 버전만 지원됩니다. Windows Vista에서도 작동할 수 있지만 아직 모든 작동 테스트가 완료되지 않았습니다. + +윈도우용 바이너리는 `x86`과 `x64` 모두 제공됩니다. 그리고 `ARM` 버전 윈도우는 아직 지원하지 않습니다. (역주: 추후 지원할 가능성이 있습니다) + +### Linux + +Ubuntu 12.04 버전에서 빌드된 `ia32`(`i686`), `x64`(`amd64`) 바이너리가 제공됩니다. +그리고 `arm` 버전 바이너리는 ARM v7 hard-float ABI와 Debian Wheezy용 NEON에 맞춰 제공됩니다. + +미리 빌드된 바이너리가 배포판에서 작동할 수 있는지 여부는 Electron이 빌드된 플랫폼에서 링크된 라이브러리에 따라 달라집니다. +그래서 현재 Linux 바이너리는 Ubuntu 12.04 버전만 정상적인 작동이 보장됩니다. +하지만 다음 플랫폼들은 미리 빌드된 Electron 바이너리가 정상적으로 작동하는 것을 확인했습니다: + +* Ubuntu 12.04 이후 버전 +* Fedora 21 +* Debian 8 diff --git a/docs-translations/ko/tutorial/using-native-node-modules.md b/docs-translations/ko-KR/tutorial/using-native-node-modules.md similarity index 61% rename from docs-translations/ko/tutorial/using-native-node-modules.md rename to docs-translations/ko-KR/tutorial/using-native-node-modules.md index 913c300be881..6a0c67770b0b 100644 --- a/docs-translations/ko/tutorial/using-native-node-modules.md +++ b/docs-translations/ko-KR/tutorial/using-native-node-modules.md @@ -5,14 +5,16 @@ Electron에선 node.js 네이티브 모듈이 지원됩니다. 하지만 Electro ## 네이티브 node 모듈 호환성 -Node v0.11.x 버전부터는 V8 API의 중대한 변경이 있었습니다. 하지만 대부분의 네이티브 모듈은 Node v0.10.x 버전을 타겟으로 작성 되었기 때문에 -새로운 Node 또는 io.js 버전에서 작동하지 않을 수 있습니다. Electron은 내부적으로 __io.js v3.1.0__ 버전을 사용하기 때문에 호환성 문제가 발생할 수 있습니다. +네이티브 모듈은 node.js가 새로운 V8 버전을 사용함으로 인해 작동하지 않을 수 있습니다. +사용하는 네이티브 모듈이 Electron에 맞춰 작동할 수 있도록 하려면 Electron에서 사용하는 node.js의 버전을 확인할 필요가 있습니다. +Electron에서 사용하는 node 버전은 [releases](https://github.com/atom/electron/releases)에서 확인할 수 있으며 +`process.version`을 출력하여 버전을 확인할 수도 있습니다. ([시작하기](https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md)의 예제를 참고하세요) -이 문제를 해결하기 위해선 모듈이 v0.11.x 또는 최신 버전을 지원할 수 있도록 변경해야 합니다. -현재 [많은 모듈들](https://www.npmjs.org/browse/depended/nan)이 안정적으로 두 버전 모두 지원하고 있지만 오래된 모듈의 경우 여전히 Node v0.10.x 버전만을 지원하고 있습니다. -예를 들어 [nan](https://github.com/rvagg/nan) 모듈을 사용해야 한다면 Node v0.11.x 또는 최신 버전의 Node와 io.js로 포팅 할 필요가 있습니다. +혹시 직접 만든 네이티브 모듈이 있다면 [NAN](https://github.com/nodejs/nan/) 모듈을 사용하는 것을 고려해보는 것이 좋습니다. +이 모듈은 다중 버전의 node.js를 지원하기 쉽게 해줍니다. 이를 통해 오래된 모듈을 새 버전의 node.js에 맞게 포팅할 수 있습니다. +Electron도 이 모듈을 통해 포팅된 네이티브 모듈을 사용할 수 있습니다. -## 네이티브 모듈 설치하는 방법 +## 네이티브 모듈을 설치하는 방법 네이티브 모듈을 설치하는 방법은 세 가지 종류가 있습니다. @@ -29,7 +31,20 @@ npm install --save-dev electron-rebuild node ./node_modules/.bin/electron-rebuild ``` -### node-gyp을 이용한 방법 +### `npm`을 이용한 방법 + +또한 `npm`을 통해 설치할 수도 있습니다. +환경변수가 필요한 것을 제외하고 일반 Node 모듈을 설치하는 방법과 완전히 똑같습니다: + +```bash +export npm_config_disturl=https://atom.io/download/atom-shell +export npm_config_target=0.33.1 +export npm_config_arch=x64 +export npm_config_runtime=electron +HOME=~/.electron-gyp npm install module-name +``` + +### `node-gyp`를 이용한 방법 Node 모듈을 `node-gyp`를 사용하여 Electron을 타겟으로 빌드할 때는 `node-gyp`에 헤더 다운로드 주소와 버전을 알려주어야 합니다: @@ -40,15 +55,3 @@ $ HOME=~/.electron-gyp node-gyp rebuild --target=0.29.1 --arch=x64 --dist-url=ht `HOME=~/.electron-gyp`은 변경할 헤더의 위치를 찾습니다. `--target=0.29.1`은 Electron의 버전입니다. `--dist-url=...`은 헤더를 다운로드 하는 주소입니다. `--arch=x64`는 64비트 시스템을 타겟으로 빌드 한다는 것을 `node-gyp`에게 알려줍니다. - -### npm을 이용한 방법 - -또한 `npm`을 통해 설치할 수도 있습니다. -환경변수가 필요한 것을 제외하고 일반 Node 모듈을 설치하는 방법과 완전히 똑같습니다: - -```bash -export npm_config_disturl=https://atom.io/download/atom-shell -export npm_config_target=0.29.1 -export npm_config_arch=x64 -HOME=~/.electron-gyp npm install module-name -``` diff --git a/docs-translations/ko/tutorial/using-pepper-flash-plugin.md b/docs-translations/ko-KR/tutorial/using-pepper-flash-plugin.md similarity index 100% rename from docs-translations/ko/tutorial/using-pepper-flash-plugin.md rename to docs-translations/ko-KR/tutorial/using-pepper-flash-plugin.md diff --git a/docs-translations/ko/tutorial/using-selenium-and-webdriver.md b/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md similarity index 98% rename from docs-translations/ko/tutorial/using-selenium-and-webdriver.md rename to docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md index 33dc6311ce35..a9fd84a68bc2 100644 --- a/docs-translations/ko/tutorial/using-selenium-and-webdriver.md +++ b/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md @@ -71,7 +71,7 @@ driver.quit(); 먼저, `chromedriver` 바이너리를 다운로드 받고 실행합니다: ```bash -$ chromedriver --url-base=/wd/hub --port=9515 +$ chromedriver --url-base=wd/hub --port=9515 Starting ChromeDriver (v2.10.291558) on port 9515 Only local connections are allowed. ``` diff --git a/docs/README.md b/docs/README.md index b2f95fe4b579..eb6e9d6e36f0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,6 @@ ## Guides +* [Supported Platforms](tutorial/supported-platforms.md) * [Application Distribution](tutorial/application-distribution.md) * [Application Packaging](tutorial/application-packaging.md) * [Using Native Node Modules](tutorial/using-native-node-modules.md) diff --git a/docs/api/app.md b/docs/api/app.md index e4a8d561e2f5..0b5780aab7bf 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -197,12 +197,6 @@ You can request the following paths by the name: * `~/Library/Application Support` on OS X * `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, 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 - `cache` directory appended with your app's name. * `temp` Temporary directory. * `userDesktop` The current user's Desktop directory. * `exe` The current executable file. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index f1705c291756..1a2870f205ae 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -24,14 +24,13 @@ You can also create a window without chrome by using [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). It creates a new `BrowserWindow` with native properties as set by the `options`. -Properties `width` and `height` are required. ### `new BrowserWindow(options)` `options` Object, properties: -* `width` Integer (**required**) - Window's width. -* `height` Integer (**required**) - Window's height. +* `width` Integer - Window's width. +* `height` Integer - Window's height. * `x` Integer - Window's left offset from screen. * `y` Integer - Window's top offset from screen. * `use-content-size` Boolean - The `width` and `height` would be used as web @@ -232,18 +231,6 @@ Emitted when the window enters full screen state triggered by html api. Emitted when the window leaves full screen state triggered by html api. -### Event: 'devtools-opened' - -Emitted when DevTools is opened. - -### Event: 'devtools-closed' - -Emitted when DevTools is closed. - -### Event: 'devtools-focused' - -Emitted when DevTools is focused / opened. - ### Event: 'app-command': Emitted when an [App Command](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646275(v=vs.85).aspx) @@ -318,16 +305,6 @@ operations will be done via it. See the [`webContents` documentation](web-contents.md) for its methods and events. -**Note:** Users should never store this object because it may become `null` -when the renderer process (web page) has crashed. - -### `win.devToolsWebContents` - -Get the `WebContents` of DevTools for this window. - -**Note:** Users should never store this object because it may become `null` -when the DevTools has been closed. - ### `win.id` The unique ID of this window. @@ -596,41 +573,6 @@ bar will become grey when set to `true`. Whether the window's document has been edited. -### `win.openDevTools([options])` - -* `options` Object (optional). Properties: - * `detach` Boolean - opens DevTools in a new window - -Opens the developer tools. - -### `win.closeDevTools()` - -Closes the developer tools. - -### `win.isDevToolsOpened()` - -Returns whether the developer tools are opened. - -### `win.toggleDevTools()` - -Toggles the developer tools. - -### `win.isDevToolsFocused()` - -Returns whether the developer tools is focused. - -### `win.inspectElement(x, y)` - -* `x` Integer -* `y` Integer - -Starts inspecting element at position (`x`, `y`). - -### `win.inspectServiceWorker()` - -Opens the developer tools for the service worker context present in the web -contents. - ### `win.focusOnWebView()` ### `win.blurWebView()` @@ -665,7 +607,7 @@ Same as `webContents.loadUrl(url[, options])`. Same as `webContents.reload`. -### `win.setMenu(menu)` _OS X_ +### `win.setMenu(menu)` _Linux_ _Windows_ * `menu` Menu diff --git a/docs/api/chrome-command-line-switches.md b/docs/api/chrome-command-line-switches.md index 2f995c99b219..c2a39126f63e 100644 --- a/docs/api/chrome-command-line-switches.md +++ b/docs/api/chrome-command-line-switches.md @@ -87,12 +87,23 @@ Sets the `version` of the pepper flash plugin. Enables net log events to be saved and writes them to `path`. +## --ssl-version-fallback-min=`version` + +Sets the minimum SSL/TLS version ("tls1", "tls1.1" or "tls1.2") that TLS +fallback will accept. + +## --enable-logging + +Prints Chromium's logging into console. + +This switch can not be used in `app.commandLine.appendSwitch` since it is parsed earlier than user's app is loaded. + ## --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. +This switch only works when `--enable-logging` is also passed. ## --vmodule=`pattern` @@ -104,10 +115,4 @@ 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 the 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'); -``` +This switch only works when `--enable-logging` is also passed. diff --git a/docs/api/download-item.md b/docs/api/download-item.md new file mode 100644 index 000000000000..53cd56cca9e5 --- /dev/null +++ b/docs/api/download-item.md @@ -0,0 +1,101 @@ +# DownloadItem + +`DownloadItem` is an EventEmitter represents a download item in Electron. It +is used in `will-download` event of `Session` module, and allows users to +control the download item. + +```javascript +// In the main process. +win.webContents.session.on('will-download', function(event, item, webContents) { + // Set the save path, making Electron not to prompt a save dialog. + item.setSavePath('/tmp/save.pdf'); + console.log(item.getMimeType()); + console.log(item.getFilename()); + console.log(item.getTotalBytes()); + item.on('updated', function() { + console.log('Received bytes: ' + item.getReceivedBytes()); + }); + item.on('done', function(e, state) { + if (state == "completed") { + console.log("Download successfully"); + } else { + console.log("Download is cancelled or interrupted that can't be resumed"); + } + }); +``` + +## Events + +### Event: 'updated' + +Emits when the `downloadItem` gets updated. + +### Event: 'done' + +* `event` Event +* `state` String + * `completed` - The download completed successfully. + * `cancelled` - The download has been cancelled. + * `interrupted` - An error broke the connection with the file server. + +Emits when the download is in a terminal state. This includes a completed +download, a cancelled download(via `downloadItem.cancel()`), and interrupted +download that can't be resumed. + +## Methods + +The `downloadItem` object has the following methods: + +### `downloadItem.setSavePath(path)` + +* `path` String - Set the save file path of the download item. + +The API is only available in session's `will-download` callback function. +If user doesn't set the save path via the API, Electron will use the original +routine to determine the save path(Usually prompts a save dialog). + +### `downloadItem.pause()` + +Pauses the download. + +### `downloadItem.resume()` + +Resumes the download that has been paused. + +### `downloadItem.cancel()` + +Cancels the download operation. + +### `downloadItem.getUrl()` + +Returns a `String` represents the origin url where the item is downloaded from. + +### `downloadItem.getMimeType()` + +Returns a `String` represents the mime type. + +### `downloadItem.hasUserGesture()` + +Returns a `Boolean` indicates whether the download has user gesture. + +### `downloadItem.getFilename()` + +Returns a `String` represents the file name of the download item. + +**Note:** The file name is not always the same as the actual one saved in local +disk. If user changes the file name in a prompted download saving dialog, the +actual name of saved file will be different. + +### `downloadItem.getTotalBytes()` + +Returns a `Integer` represents the total size in bytes of the download item. +If the size is unknown, it returns 0. + +### `downloadItem.getReceivedBytes()` + +Returns a `Integer` represents the received bytes of the download item. + +### `downloadItem.getContentDisposition()` + +Returns a `String` represents the Content-Disposition field from the response +header. diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 89524d9f2352..37079233fc0c 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -1,6 +1,6 @@ # MenuItem -The `menu-item` module allows you to add items to an application or content +The `menu-item` module allows you to add items to an application or context [`menu`](menu.md). See [`menu`](menu.md) for examples. diff --git a/docs/api/menu.md b/docs/api/menu.md index f48b07e7e40a..cabd04ca8550 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -237,9 +237,9 @@ Generally, the `template` is just an array of `options` for constructing a You can also attach other fields to the element of the `template` and they will become properties of the constructed menu items. -### `Menu.popup(browserWindow[, x, y])` +### `Menu.popup([browserWindow, x, y])` -* `browserWindow` BrowserWindow +* `browserWindow` BrowserWindow (optional) * `x` Number (optional) * `y` Number (**required** if `x` is used) diff --git a/docs/api/native-image.md b/docs/api/native-image.md index df2bb96ff9da..097a8130c243 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -142,7 +142,7 @@ Returns a boolean whether the image is empty. Returns the size of the image. -[buffer]: https://iojs.org/api/buffer.html#buffer_class_buffer +[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer ### `image.setTemplateImage(option)` diff --git a/docs/api/process.md b/docs/api/process.md index abd3c4fe574c..a2157886bc61 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -9,7 +9,27 @@ upstream node: * `process.versions['chrome']` String - Version of Chromium. * `process.resourcesPath` String - Path to JavaScript source code. -# Methods +## Events + +### Event: 'loaded' + +Emitted when Electron has loaded its internal initialization script and is +beginning to load the web page or the main script. + +It can be used by the preload script to add removed Node global symbols back to +the global scope when node integration is turned off: + +```js +// preload.js +var _setImmediate = setImmediate; +var _clearImmediate = clearImmediate; +process.once('loaded', function() { + global.setImmediate = _setImmediate; + global.clearImmediate = _clearImmediate; +}); +``` + +## Methods The `process` object has the following method: @@ -17,7 +37,7 @@ The `process` object has the following method: Causes the main thread of the current process hang. -## process.setFdLimit(maxDescriptors) _OS X_ _Linux_ +### process.setFdLimit(maxDescriptors) _OS X_ _Linux_ * `maxDescriptors` Integer diff --git a/docs/api/screen.md b/docs/api/screen.md index 934e3eaf5a70..42468da16005 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -8,7 +8,7 @@ position, etc. You should not use this module until the `ready` event of the **Note:** In the renderer / DevTools, `window.screen` is a reserved DOM property, so writing `var screen = require('screen')` will not work. In our -examples below, we use `atomScreen` as the variable name instead. +examples below, we use `electronScreen` as the variable name instead. An example of creating a window that fills the whole screen: @@ -19,8 +19,8 @@ var BrowserWindow = require('browser-window'); var mainWindow; app.on('ready', function() { - var atomScreen = require('screen'); - var size = atomScreen.getPrimaryDisplay().workAreaSize; + var electronScreen = require('screen'); + var size = electronScreen.getPrimaryDisplay().workAreaSize; mainWindow = new BrowserWindow({ width: size.width, height: size.height }); }); ``` @@ -34,11 +34,11 @@ var BrowserWindow = require('browser-window'); var mainWindow; app.on('ready', function() { - var atomScreen = require('screen'); - var displays = atomScreen.getAllDisplays(); + var electronScreen = require('screen'); + var displays = electronScreen.getAllDisplays(); var externalDisplay = null; for (var i in displays) { - if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) { + if (displays[i].bounds.x != 0 || displays[i].bounds.y != 0) { externalDisplay = displays[i]; break; } @@ -47,7 +47,7 @@ app.on('ready', function() { if (externalDisplay) { mainWindow = new BrowserWindow({ x: externalDisplay.bounds.x + 50, - y: externalDisplay.bounds.y + 50, + y: externalDisplay.bounds.y + 50 }); } }); diff --git a/docs/api/session.md b/docs/api/session.md index 439cf8514e93..392b6826414d 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -18,11 +18,7 @@ var session = win.webContents.session ### Event: 'will-download' * `event` Event -* `item` Object - * `url` String - * `filename` String - * `mimeType` String - * `hasUserGesture` Boolean +* `item` [DownloadItem](download-item.md) * `webContents` [WebContents](web-contents.md) Fired when Electron is about to download `item` in `webContents`. @@ -32,7 +28,7 @@ Calling `event.preventDefault()` will cancel the download. ```javascript session.on('will-download', function(event, item, webContents) { event.preventDefault(); - require('request')(item.url, function(data) { + require('request')(item.getUrl(), function(data) { require('fs').writeFileSync('/somewhere', data); }); }); @@ -195,3 +191,30 @@ proxy-uri = ["://"][":"] Sets download saving directory. By default, the download directory will be the `Downloads` under the respective app folder. + +### `session.enableNetworkEmulation(options)` + +* `options` Object + * `offline` Boolean - Whether to emulate network outage. + * `latency` Double - RTT in ms + * `downloadThroughput` Double - Download rate in Bps + * `uploadThroughput` Double - Upload rate in Bps + +Emulates network with the given configuration for the `session`. + +```javascript +// To emulate a GPRS connection with 50kbps throughput and 500 ms latency. +window.webContents.session.enableNetworkEmulation({ + latency: 500, + downloadThroughput: 6400, + uploadThroughput: 6400 +}); + +// To emulate a network outage. +window.webContents.session.enableNetworkEmulation({offline: true}); +``` + +### `session.disableNetworkEmulation` + +Disables any network emulation already active for the `session`. Resets to +the original network configuration. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index e8f72cd24bf2..08670bad9df7 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -78,6 +78,10 @@ Returns: * `oldUrl` String * `newUrl` String * `isMainFrame` Boolean +* `httpResponseCode` Integer +* `requestMethod` String +* `referrer` String +* `headers` Object Emitted when a redirect is received while requesting a resource. @@ -107,6 +111,8 @@ Returns: * `frameName` String * `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, `new-window` and `other`. +* `options` Object - The options which will be used for creating the new + `BrowserWindow`. Emitted when the page requests to open a new window for a `url`. It could be requested by `window.open` or an external link like ``. @@ -148,6 +154,18 @@ Emitted when a plugin process has crashed. Emitted when `webContents` is destroyed. +### Event: 'devtools-opened' + +Emitted when DevTools is opened. + +### Event: 'devtools-closed' + +Emitted when DevTools is closed. + +### Event: 'devtools-focused' + +Emitted when DevTools is focused / opened. + ## Instance Methods The `webContents` object has the following instance methods: @@ -415,7 +433,7 @@ win.webContents.on("did-finish-load", function() { win.webContents.printToPDF({}, function(error, data) { if (error) throw error; fs.writeFile("/tmp/print.pdf", data, function(error) { - if (err) + if (error) throw error; console.log("Write PDF successfully."); }) @@ -435,6 +453,40 @@ Adds the specified path to DevTools workspace. Removes the specified path from DevTools workspace. +### `webContents.openDevTools([options])` + +* `options` Object (optional). Properties: + * `detach` Boolean - opens DevTools in a new window + +Opens the developer tools. + +### `webContents.closeDevTools()` + +Closes the developer tools. + +### `webContents.isDevToolsOpened()` + +Returns whether the developer tools are opened. + +### `webContents.toggleDevTools()` + +Toggles the developer tools. + +### `webContents.isDevToolsFocused()` + +Returns whether the developer tools is focused. + +### `webContents.inspectElement(x, y)` + +* `x` Integer +* `y` Integer + +Starts inspecting element at position (`x`, `y`). + +### `webContents.inspectServiceWorker()` + +Opens the developer tools for the service worker context. + ### `webContents.send(channel[, args...])` * `channel` String @@ -528,13 +580,13 @@ Sends an input `event` to the page. For keyboard events, the `event` object also have following properties: * `keyCode` String (**required**) - A single character that will be sent as - keyboard event. Can be any ASCII character on the keyboard, like `a`, `1` - and `=`. + keyboard event. Can be any UTF-8 character. For mouse events, the `event` object also have following properties: * `x` Integer (**required**) * `y` Integer (**required**) +* `button` String - The button pressed, can be `left`, `middle`, `right` * `globalX` Integer * `globalY` Integer * `movementX` Integer @@ -559,9 +611,23 @@ For the `mouseWheel` event, the `event` object also have following properties: Begin subscribing for presentation events and captured frames, the `callback` will be called with `callback(frameBuffer)` when there is a presentation event. -The `frameBuffer` is a `Buffer` that contains raw pixel data, in the format of -32bit ARGB. +The `frameBuffer` is a `Buffer` that contains raw pixel data. On most machines, +the pixel data is effectively stored in 32bit BGRA format, but the actual +representation depends on the endianness of the processor (most modern +processors are little-endian, on machines with big-endian processors the data +is in 32bit ARGB format). ### `webContents.endFrameSubscription()` End subscribing for frame presentation events. + +## Instance Properties + +`WebContents` objects also have the following properties: + +### `webContents.devToolsWebContents` + +Get the `WebContents` of DevTools for this `WebContents`. + +**Note:** Users should never store this object because it may become `null` +when the DevTools has been closed. diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index bdf42da3003a..33597543b773 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -83,4 +83,11 @@ attackers. Resources will be loaded from this `scheme` regardless of the current page's Content Security Policy. +### `webFrame.registerUrlSchemeAsPrivileged(scheme)` + +* `scheme` String + +Registers the `scheme` as secure, bypasses content security policy for resources and +allows registering ServiceWorker. + [spellchecker]: https://github.com/atom/node-spellchecker diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 8eb3857ac970..3fda3a98edb5 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -149,6 +149,14 @@ This value can only be modified before the first navigation, since the session of an active renderer process cannot change. Subsequent attempts to modify the value will fail with a DOM exception. +### `allowpopups` + +```html + +``` + +If "on", the guest page will be allowed to open new windows. + ## Methods The `webview` tag has the following methods: @@ -496,7 +504,9 @@ Returns: * `url` String * `frameName` String * `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, - `new-window` and `other` + `new-window` and `other`. +* `options` Object - The options which should be used for creating the new + `BrowserWindow`. Fired when the guest page attempts to open a new browser window. diff --git a/docs/api/window-open.md b/docs/api/window-open.md index c574d5d036ee..5d298e61e75b 100644 --- a/docs/api/window-open.md +++ b/docs/api/window-open.md @@ -8,6 +8,10 @@ The proxy has limited standard functionality implemented to be compatible with traditional web pages. For full control of the new window you should create a `BrowserWindow` directly. +The newly created `BrowserWindow` will inherit parent window's options by +default, to override inherited options you can set them in the `features` +string. + ### `window.open(url[, frameName][, features])` * `url` String @@ -16,6 +20,9 @@ you should create a `BrowserWindow` directly. Creates a new window and returns an instance of `BrowserWindowProxy` class. +The `features` string follows the format of standard browser, but each feature +has to be a field of `BrowserWindow`'s options. + ### `window.opener.postMessage(message, targetOrigin)` * `message` String diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 93cd0ebc5bd9..5300d754a69b 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -2,7 +2,7 @@ Electron enables you to create desktop applications with pure JavaScript by providing a runtime with rich native (operating system) APIs. You could see it -as a variant of the io.js runtime that is focused on desktop applications +as a variant of the Node.js runtime that is focused on desktop applications instead of web servers. This doesn't mean Electron is a JavaScript binding to graphical user interface @@ -22,8 +22,9 @@ multi-process architecture is also used. Each web page in Electron runs in its own process, which is called __the renderer process__. In normal browsers, web pages usually run in a sandboxed environment and are not -allowed access to native resources. Electron users, however, have the power to use -io.js APIs in web pages allowing lower level operating system interactions. +allowed access to native resources. Electron users, however, have the power to +use Node.js APIs in web pages allowing lower level operating system +interactions. ### Differences Between Main Process and Renderer Process @@ -124,11 +125,12 @@ Finally the `index.html` is the web page you want to show: + Hello World!

Hello World!

- We are using io.js + We are using Node.js and Electron . @@ -142,8 +144,8 @@ working as expected. ### electron-prebuilt -If you've installed `electron-prebuilt` globally with `npm`, then you need only -run the following in your app's source directory: +If you've installed `electron-prebuilt` globally with `npm`, then you will only need +to run the following in your app's source directory: ```bash electron . @@ -157,7 +159,7 @@ If you've installed it locally, then run: ### Manually Downloaded Electron Binary -If you downloaded Electron manually, you can also just use the included +If you downloaded Electron manually, you can also use the included binary to execute your app directly. #### Windows diff --git a/docs/tutorial/supported-platforms.md b/docs/tutorial/supported-platforms.md new file mode 100644 index 000000000000..9d1a293d5f5d --- /dev/null +++ b/docs/tutorial/supported-platforms.md @@ -0,0 +1,31 @@ +# Supported Platforms + +Following platforms are supported by Electron: + +### OS X + +Only 64bit binaries are provided for OS X, and the minimum OS X version +supported is OS X 10.8. + +### Windows + +Windows 7 and later are supported, older operating systems are not supported +(and do not work). + +Both `x86` and `amd64` (x64) binaries are provided for Windows. Please note, the +`ARM` version of Windows is not supported for now. + +### Linux + +The prebuilt `ia32`(`i686`) and `x64`(`amd64`) binaries of Electron are built on +Ubuntu 12.04, the `arm` binary is built against ARM v7 with hard-float ABI and +NEON for Debian Wheezy. + +Whether the prebuilt binary can run on a distribution depends on whether the +distribution includes the libraries that Electron is linked to on the building +platform, so only Ubuntu 12.04 is guaranteed to work, but following platforms +are also verified to be able to run the prebuilt binaries of Electron: + +* Ubuntu 12.04 and later +* Fedora 21 +* Debian 8 diff --git a/docs/tutorial/using-native-node-modules.md b/docs/tutorial/using-native-node-modules.md index c338494cde2b..491415b900ac 100644 --- a/docs/tutorial/using-native-node-modules.md +++ b/docs/tutorial/using-native-node-modules.md @@ -6,16 +6,17 @@ the location of Electron's headers when building native modules. ## Native Node Module Compatibility -Since Node v0.11.x there were vital changes in the V8 API. So generally all -native modules written for Node v0.10.x won't work for newer Node or io.js -versions. And because Electron internally uses __io.js v3.1.0__, it has the -same problem. +Native modules might break when Node starts using a new version of V8. +To make sure the module you're interested in will work with Electron, you should +check if it supports the internal Node version used by Electron. +You can check what version of Node is used in Electron by looking it up in +the [releases](https://github.com/atom/electron/releases) page or by using +`process.version` (see [Quick Start](https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md) +for example). -To solve this, you should use modules that support Node v0.11.x or later, -[many modules](https://www.npmjs.org/browse/depended/nan) do support both now. -For old modules that only support Node v0.10.x, you should use the -[nan](https://github.com/rvagg/nan) module to port it to v0.11.x or later -versions of Node or io.js. +Consider using [NAN](https://github.com/nodejs/nan/) for your own modules, since +it makes it easier to support multiple versions of Node. It's also helpful for +porting old modules to newer versions of Node so they can work with Electron. ## How to Install Native Modules @@ -34,6 +35,19 @@ npm install --save-dev electron-rebuild node ./node_modules/.bin/electron-rebuild ``` +### The npm Way + +You can also use `npm` to install modules. The steps are exactly the same with +Node modules, except that you need to setup some environment variables: + +```bash +export npm_config_disturl=https://atom.io/download/atom-shell +export npm_config_target=0.33.1 +export npm_config_arch=x64 +export npm_config_runtime=electron +HOME=~/.electron-gyp npm install module-name +``` + ### The node-gyp Way To build Node modules with headers of Electron, you need to tell `node-gyp` @@ -48,15 +62,3 @@ The `HOME=~/.electron-gyp` changes where to find development headers. The `--target=0.29.1` is version of Electron. The `--dist-url=...` specifies where to download the headers. The `--arch=x64` says the module is built for 64bit system. - -### The npm Way - -You can also use `npm` to install modules. The steps are exactly the same with -Node modules, except that you need to setup some environment variables: - -```bash -export npm_config_disturl=https://atom.io/download/atom-shell -export npm_config_target=0.29.1 -export npm_config_arch=x64 -HOME=~/.electron-gyp npm install module-name -``` diff --git a/docs/tutorial/using-selenium-and-webdriver.md b/docs/tutorial/using-selenium-and-webdriver.md index ebc6d2fa7801..b87f8f11dac9 100644 --- a/docs/tutorial/using-selenium-and-webdriver.md +++ b/docs/tutorial/using-selenium-and-webdriver.md @@ -74,7 +74,7 @@ driver. First you need to download the `chromedriver` binary, and run it: ```bash -$ chromedriver --url-base=/wd/hub --port=9515 +$ chromedriver --url-base=wd/hub --port=9515 Starting ChromeDriver (v2.10.291558) on port 9515 Only local connections are allowed. ``` diff --git a/filenames.gypi b/filenames.gypi index 99d6bc6d50d3..6418ba287214 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -37,6 +37,7 @@ 'atom/common/api/lib/native-image.coffee', 'atom/common/api/lib/shell.coffee', 'atom/common/lib/init.coffee', + 'atom/common/lib/reset-search-paths.coffee', 'atom/renderer/lib/chrome-api.coffee', 'atom/renderer/lib/init.coffee', 'atom/renderer/lib/inspector.coffee', @@ -71,6 +72,8 @@ 'atom/browser/api/atom_api_content_tracing.cc', 'atom/browser/api/atom_api_cookies.cc', 'atom/browser/api/atom_api_cookies.h', + 'atom/browser/api/atom_api_download_item.cc', + 'atom/browser/api/atom_api_download_item.h', 'atom/browser/api/atom_api_dialog.cc', 'atom/browser/api/atom_api_global_shortcut.cc', 'atom/browser/api/atom_api_global_shortcut.h', @@ -121,14 +124,18 @@ 'atom/browser/atom_download_manager_delegate.h', 'atom/browser/atom_browser_main_parts.cc', 'atom/browser/atom_browser_main_parts.h', - 'atom/browser/atom_browser_main_parts_linux.cc', 'atom/browser/atom_browser_main_parts_mac.mm', + 'atom/browser/atom_browser_main_parts_posix.cc', 'atom/browser/atom_javascript_dialog_manager.cc', 'atom/browser/atom_javascript_dialog_manager.h', 'atom/browser/atom_quota_permission_context.cc', 'atom/browser/atom_quota_permission_context.h', + 'atom/browser/atom_resource_dispatcher_host_delegate.cc', + 'atom/browser/atom_resource_dispatcher_host_delegate.h', 'atom/browser/atom_speech_recognition_manager_delegate.cc', 'atom/browser/atom_speech_recognition_manager_delegate.h', + 'atom/browser/atom_ssl_config_service.cc', + 'atom/browser/atom_ssl_config_service.h', 'atom/browser/bridge_task_runner.cc', 'atom/browser/bridge_task_runner.h', 'atom/browser/browser.cc', @@ -147,6 +154,7 @@ 'atom/browser/mac/atom_application_delegate.mm', 'atom/browser/native_window.cc', 'atom/browser/native_window.h', + 'atom/browser/native_window_views_win.cc', 'atom/browser/native_window_views.cc', 'atom/browser/native_window_views.h', 'atom/browser/native_window_mac.h', @@ -422,6 +430,8 @@ 'chromium_src/chrome/renderer/tts_dispatcher.cc', 'chromium_src/chrome/renderer/tts_dispatcher.h', 'chromium_src/chrome/utility/utility_message_handler.h', + 'chromium_src/extensions/browser/app_window/size_constraints.cc', + 'chromium_src/extensions/browser/app_window/size_constraints.h', 'chromium_src/library_loaders/libspeechd_loader.cc', 'chromium_src/library_loaders/libspeechd.h', 'chromium_src/net/test/embedded_test_server/stream_listen_socket.cc', diff --git a/package.json b/package.json index 101fdeee257a..a125d13b68ef 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "electron", "devDependencies": { - "asar": "0.8.x", + "asar": "^0.8.0", "coffee-script": "^1.9.2", "coffeelint": "^1.9.4", "request": "*", - "runas": "3.x" + "runas": "^3.0.0" }, "private": true, "scripts": { diff --git a/script/create-dist.py b/script/create-dist.py index c978641df320..ca7e21642872 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -126,6 +126,8 @@ def copy_chrome_binary(binary): def copy_license(): + shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'LICENSES.chromium.html'), + DIST_DIR) shutil.copy2(os.path.join(SOURCE_ROOT, 'LICENSE'), DIST_DIR) diff --git a/script/lib/config.py b/script/lib/config.py index 0df68bd773bf..68f216785d60 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -8,7 +8,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '8482fe555913dea3bde8a74f754524e2cfb02bc5' +LIBCHROMIUMCONTENT_COMMIT = '04523758cda2a96d2454f9056fb1fb9a1c1f95f1' PLATFORM = { 'cygwin': 'win32', diff --git a/script/update-external-binaries.py b/script/update-external-binaries.py index 49e73435ab51..fae268ea8cb7 100755 --- a/script/update-external-binaries.py +++ b/script/update-external-binaries.py @@ -8,7 +8,7 @@ from lib.config import get_target_arch from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download -VERSION = 'v0.7.0' +VERSION = 'v0.8.0' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) FRAMEWORKS_URL = 'http://github.com/atom/atom-shell-frameworks/releases' \ '/download/' + VERSION diff --git a/spec/api-browser-window-spec.coffee b/spec/api-browser-window-spec.coffee index bb4d782e7ffb..2e4a66b92b9a 100644 --- a/spec/api-browser-window-spec.coffee +++ b/spec/api-browser-window-spec.coffee @@ -294,17 +294,7 @@ describe 'browser-window module', -> w.show() w.minimize() - describe 'will-navigate event', -> - @timeout 10000 - it 'emits when user starts a navigation', (done) -> - url = "file://#{fixtures}/pages/will-navigate.html" - w.webContents.on 'will-navigate', (event, u) -> - event.preventDefault() - assert.equal u, url - done() - w.loadUrl url - - describe 'beginFrameSubscription method', -> + xdescribe 'beginFrameSubscription method', -> it 'subscribes frame updates', (done) -> w.loadUrl "file://#{fixtures}/api/blank.html" w.webContents.beginFrameSubscription (data) -> diff --git a/spec/api-session-spec.coffee b/spec/api-session-spec.coffee index 34a08ee50f09..9e083d27c0f7 100644 --- a/spec/api-session-spec.coffee +++ b/spec/api-session-spec.coffee @@ -2,6 +2,7 @@ assert = require 'assert' remote = require 'remote' http = require 'http' path = require 'path' +fs = require 'fs' app = remote.require 'app' BrowserWindow = remote.require 'browser-window' @@ -72,3 +73,51 @@ describe 'session module', -> quotas: ['persistent'], w.webContents.session.clearStorageData options, -> w.webContents.send 'getcount' + + describe 'DownloadItem', -> + # A 5 MB mock pdf. + mockPDF = new Buffer 1024 * 1024 * 5 + contentDisposition = 'inline; filename="mock.pdf"' + ipc = require 'ipc' + downloadFilePath = path.join fixtures, 'mock.pdf' + downloadServer = http.createServer (req, res) -> + res.writeHead 200, { + 'Content-Length': mockPDF.length, + 'Content-Type': 'application/pdf', + 'Content-Disposition': contentDisposition + } + res.end mockPDF + downloadServer.close() + + it 'can download successfully', (done) -> + downloadServer.listen 0, '127.0.0.1', -> + {port} = downloadServer.address() + ipc.sendSync 'set-download-option', false + w.loadUrl "#{url}:#{port}" + ipc.once 'download-done', (state, url, mimeType, receivedBytes, + totalBytes, disposition, filename) -> + assert.equal state, 'completed' + assert.equal filename, 'mock.pdf' + assert.equal url, "http://127.0.0.1:#{port}/" + assert.equal mimeType, 'application/pdf' + assert.equal receivedBytes, mockPDF.length + assert.equal totalBytes, mockPDF.length + assert.equal disposition, contentDisposition + assert fs.existsSync downloadFilePath + fs.unlinkSync downloadFilePath + done() + + it 'can cancel download', (done) -> + downloadServer.listen 0, '127.0.0.1', -> + {port} = downloadServer.address() + ipc.sendSync 'set-download-option', true + w.loadUrl "#{url}:#{port}/" + ipc.once 'download-done', (state, url, mimeType, receivedBytes, + totalBytes, disposition, filename) -> + assert.equal state, 'cancelled' + assert.equal filename, 'mock.pdf' + assert.equal mimeType, 'application/pdf' + assert.equal receivedBytes, 0 + assert.equal totalBytes, mockPDF.length + assert.equal disposition, contentDisposition + done() diff --git a/spec/asar-spec.coffee b/spec/asar-spec.coffee index 4ef9337f3abe..1e6ee6910369 100644 --- a/spec/asar-spec.coffee +++ b/spec/asar-spec.coffee @@ -8,6 +8,10 @@ describe 'asar package', -> describe 'node api', -> describe 'fs.readFileSync', -> + it 'does not leak fd', -> + for i in [1..10000] + fs.readFileSync(path.join(process.resourcesPath, 'atom.asar', 'renderer', 'api', 'lib', 'ipc.js')) + it 'reads a normal file', -> file1 = path.join fixtures, 'asar', 'a.asar', 'file1' assert.equal fs.readFileSync(file1).toString().trim(), 'file1' diff --git a/spec/chromium-spec.coffee b/spec/chromium-spec.coffee index 2c42cd9a4cf0..a034f9378235 100644 --- a/spec/chromium-spec.coffee +++ b/spec/chromium-spec.coffee @@ -4,10 +4,17 @@ https = require 'https' path = require 'path' ws = require 'ws' remote = require 'remote' +BrowserWindow = remote.require 'browser-window' describe 'chromium feature', -> fixtures = path.resolve __dirname, 'fixtures' + listener = null + afterEach -> + if listener? + window.removeEventListener 'message', listener + listener = null + xdescribe 'heap snapshot', -> it 'does not crash', -> process.atomBinding('v8_util').takeHeapSnapshot() @@ -23,6 +30,20 @@ describe 'chromium feature', -> {port} = server.address() $.get "http://127.0.0.1:#{port}" + describe 'document.hidden', -> + url = "file://#{fixtures}/pages/document-hidden.html" + w = null + + afterEach -> + w?.destroy() + + it 'is set correctly when window is not shown', (done) -> + w = new BrowserWindow(show:false) + w.webContents.on 'ipc-message', (event, args) -> + assert.deepEqual args, ['hidden', true] + done() + w.loadUrl url + describe 'navigator.webkitGetUserMedia', -> it 'calls its callbacks', (done) -> @timeout 5000 @@ -35,6 +56,8 @@ describe 'chromium feature', -> assert.notEqual navigator.language, '' describe 'window.open', -> + @timeout 10000 + it 'returns a BrowserWindowProxy object', -> b = window.open 'about:blank', '', 'show=no' assert.equal b.closed, false @@ -43,36 +66,43 @@ describe 'chromium feature', -> it 'accepts "node-integration" as feature', (done) -> listener = (event) -> - window.removeEventListener 'message', listener - b.close() assert.equal event.data, 'undefined' + b.close() done() window.addEventListener 'message', listener b = window.open "file://#{fixtures}/pages/window-opener-node.html", '', 'node-integration=no,show=no' + it 'inherit options of parent window', (done) -> + listener = (event) -> + size = remote.getCurrentWindow().getSize() + assert.equal event.data, "size: #{size.width} #{size.height}" + b.close() + done() + window.addEventListener 'message', listener + b = window.open "file://#{fixtures}/pages/window-open-size.html", '', 'show=no' + describe 'window.opener', -> @timeout 10000 - ipc = remote.require 'ipc' url = "file://#{fixtures}/pages/window-opener.html" w = null afterEach -> w?.destroy() - ipc.removeAllListeners 'opener' it 'is null for main window', (done) -> - ipc.on 'opener', (event, opener) -> - assert.equal opener, null - done() - BrowserWindow = remote.require 'browser-window' w = new BrowserWindow(show: false) + w.webContents.on 'ipc-message', (event, args) -> + assert.deepEqual args, ['opener', null] + done() w.loadUrl url it 'is not null for window opened by window.open', (done) -> - ipc.on 'opener', (event, opener) -> + listener = (event) -> + assert.equal event.data, 'object' b.close() - done(if opener isnt null then undefined else opener) + done() + window.addEventListener 'message', listener b = window.open url, '', 'show=no' describe 'window.opener.postMessage', -> diff --git a/spec/fixtures/pages/document-hidden.html b/spec/fixtures/pages/document-hidden.html new file mode 100644 index 000000000000..17920bff0793 --- /dev/null +++ b/spec/fixtures/pages/document-hidden.html @@ -0,0 +1,7 @@ + + + + + diff --git a/spec/fixtures/pages/window-open-hide.html b/spec/fixtures/pages/window-open-hide.html new file mode 100644 index 000000000000..d6eb7403ac66 --- /dev/null +++ b/spec/fixtures/pages/window-open-hide.html @@ -0,0 +1,12 @@ + + + + + + diff --git a/spec/fixtures/pages/window-open-size.html b/spec/fixtures/pages/window-open-size.html new file mode 100644 index 000000000000..7b06cfddf551 --- /dev/null +++ b/spec/fixtures/pages/window-open-size.html @@ -0,0 +1,8 @@ + + + + + diff --git a/spec/fixtures/pages/window-opener.html b/spec/fixtures/pages/window-opener.html index 0b5ecd556c9b..226b57dbd709 100644 --- a/spec/fixtures/pages/window-opener.html +++ b/spec/fixtures/pages/window-opener.html @@ -1,7 +1,10 @@ diff --git a/spec/modules-spec.coffee b/spec/modules-spec.coffee index e7bdac36b3e3..1cdc6cf0ea5c 100644 --- a/spec/modules-spec.coffee +++ b/spec/modules-spec.coffee @@ -22,6 +22,12 @@ describe 'third-party module', -> assert.equal msg, 'ok' done() + describe 'ffi', -> + it 'does not crash', -> + ffi = require 'ffi' + libm = ffi.Library('libm', ceil: [ 'double', [ 'double' ] ]) + assert.equal libm.ceil(1.5), 2 + describe 'q', -> Q = require 'q' diff --git a/spec/node-spec.coffee b/spec/node-spec.coffee index c8d569e01ada..969fc76f41ff 100644 --- a/spec/node-spec.coffee +++ b/spec/node-spec.coffee @@ -65,6 +65,7 @@ describe 'node feature', -> describe 'contexts', -> describe 'setTimeout in fs callback', -> + return if process.env.TRAVIS is 'true' it 'does not crash', (done) -> fs.readFile __filename, -> setTimeout done, 0 diff --git a/spec/package.json b/spec/package.json index a3b0d590effa..8f43b711f196 100644 --- a/spec/package.json +++ b/spec/package.json @@ -5,6 +5,7 @@ "version": "0.1.0", "devDependencies": { "basic-auth": "^1.0.0", + "ffi": "2.0.0", "formidable": "1.0.16", "graceful-fs": "3.0.5", "mocha": "2.1.0", diff --git a/spec/static/main.js b/spec/static/main.js index 38ba7cc089de..5b10bb6d43d7 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -1,6 +1,7 @@ var app = require('app'); var ipc = require('ipc'); var dialog = require('dialog'); +var path = require('path'); var BrowserWindow = require('browser-window'); var window = null; @@ -73,4 +74,27 @@ app.on('ready', function() { }); if (chosen == 0) window.destroy(); }); + + // For session's download test, listen 'will-download' event in browser, and + // reply the result to renderer for verifying + var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf'); + require('ipc').on('set-download-option', function(event, need_cancel) { + window.webContents.session.once('will-download', + function(e, item, webContents) { + item.setSavePath(downloadFilePath); + item.on('done', function(e, state) { + window.webContents.send('download-done', + state, + item.getUrl(), + item.getMimeType(), + item.getReceivedBytes(), + item.getTotalBytes(), + item.getContentDisposition(), + item.getFilename()); + }); + if (need_cancel) + item.cancel(); + }); + event.returnValue = "done"; + }); }); diff --git a/spec/webview-spec.coffee b/spec/webview-spec.coffee index b310b7b129f8..c60af8d74d1f 100644 --- a/spec/webview-spec.coffee +++ b/spec/webview-spec.coffee @@ -86,7 +86,7 @@ describe ' tag', -> it 'preload script can still use "process" in required modules when nodeintegration is off', (done) -> webview.addEventListener 'console-message', (e) -> - assert.equal e.message, 'object function object' + assert.equal e.message, 'object undefined object' done() webview.setAttribute 'preload', "#{fixtures}/module/preload-node-off.js" webview.src = "file://#{fixtures}/api/blank.html" @@ -201,6 +201,26 @@ describe ' tag', -> webview.src = "file://#{fixtures}/pages/partition/one.html" document.body.appendChild webview + describe 'allowpopups attribute', -> + it 'can not open new window when not set', (done) -> + listener = (e) -> + assert.equal e.message, 'null' + webview.removeEventListener 'console-message', listener + done() + webview.addEventListener 'console-message', listener + webview.src = "file://#{fixtures}/pages/window-open-hide.html" + document.body.appendChild webview + + it 'can open new window when set', (done) -> + listener = (e) -> + assert.equal e.message, 'window' + webview.removeEventListener 'console-message', listener + done() + webview.addEventListener 'console-message', listener + webview.setAttribute 'allowpopups', 'on' + webview.src = "file://#{fixtures}/pages/window-open-hide.html" + document.body.appendChild webview + describe 'new-window event', -> it 'emits when window.open is called', (done) -> webview.addEventListener 'new-window', (e) -> diff --git a/vendor/brightray b/vendor/brightray index 25f3a9d0a5b7..c25b9b27845a 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 25f3a9d0a5b73ec170a65f4e2e4c9ad91e23fc8c +Subproject commit c25b9b27845a308e6a6a5966dad057d721b1f3d1 diff --git a/vendor/node b/vendor/node index aa9c7a2316ba..f4243f5c84a3 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit aa9c7a2316ba7762f1d04d091585695be3e6be22 +Subproject commit f4243f5c84a371632d8d72a1a2210a0e994afdcc