diff --git a/atom.gyp b/atom.gyp index f5933b8631e..d6d9e2e2268 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.33.7', + 'version%': '0.34.0', }, 'includes': [ 'filenames.gypi', @@ -64,9 +64,6 @@ 'files': [ '<(PRODUCT_DIR)/<(product_name) Helper.app', '<(PRODUCT_DIR)/<(product_name) Framework.framework', - 'external_binaries/Squirrel.framework', - 'external_binaries/ReactiveCocoa.framework', - 'external_binaries/Mantle.framework', ], }, { @@ -109,7 +106,21 @@ '<@(locale_dirs)', ], }, - ] + ], + 'conditions': [ + ['mas_build==0', { + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Frameworks', + 'files': [ + 'external_binaries/Squirrel.framework', + 'external_binaries/ReactiveCocoa.framework', + 'external_binaries/Mantle.framework', + ], + }, + ], + }], + ], }, { # OS=="mac" 'dependencies': [ 'make_locale_paks', @@ -285,12 +296,28 @@ 'vendor/breakpad/breakpad.gyp:breakpad_sender', ], }], # OS=="win" - ['OS=="mac"', { + ['OS=="mac" and mas_build==0', { 'dependencies': [ 'vendor/crashpad/client/client.gyp:crashpad_client', 'vendor/crashpad/handler/handler.gyp:crashpad_handler', ], - }], # OS=="mac" + 'link_settings': { + # Do not link with QTKit for mas build. + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/QTKit.framework', + ], + }, + }], # OS=="mac" and mas_build==0 + ['OS=="mac" and mas_build==1', { + 'defines': [ + 'MAS_BUILD', + ], + 'sources!': [ + 'atom/browser/auto_updater_mac.mm', + 'atom/common/crash_reporter/crash_reporter_mac.h', + 'atom/common/crash_reporter/crash_reporter_mac.mm', + ], + }], # OS=="mac" and mas_build==1 ['OS=="linux"', { 'link_settings': { 'ldflags': [ @@ -393,9 +420,6 @@ 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework', - 'external_binaries/Squirrel.framework', - 'external_binaries/ReactiveCocoa.framework', - 'external_binaries/Mantle.framework', ], }, 'mac_bundle': 1, @@ -439,12 +463,6 @@ '<@(copied_libraries)', ], }, - { - 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources', - 'files': [ - '<(PRODUCT_DIR)/crashpad_handler', - ], - }, ], 'postbuilds': [ { @@ -476,6 +494,25 @@ ], }, ], + 'conditions': [ + ['mas_build==0', { + 'link_settings': { + 'libraries': [ + 'external_binaries/Squirrel.framework', + 'external_binaries/ReactiveCocoa.framework', + 'external_binaries/Mantle.framework', + ], + }, + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources', + 'files': [ + '<(PRODUCT_DIR)/crashpad_handler', + ], + }, + ], + }], + ], }, # target framework { 'target_name': '<(project_name)_helper', diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index 6ba1b89837c..44f2048c091 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -21,6 +21,15 @@ namespace atom { +namespace { + +bool IsBrowserProcess(base::CommandLine* cmd) { + std::string process_type = cmd->GetSwitchValueASCII(switches::kProcessType); + return process_type.empty(); +} + +} // namespace + AtomMainDelegate::AtomMainDelegate() { } @@ -28,11 +37,14 @@ AtomMainDelegate::~AtomMainDelegate() { } bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { + auto command_line = base::CommandLine::ForCurrentProcess(); + logging::LoggingSettings settings; #if defined(OS_WIN) // On Windows the terminal returns immediately, so we add a new line to // prevent output in the same line as the prompt. - std::wcout << std::endl; + if (IsBrowserProcess(command_line)) + std::wcout << std::endl; #if defined(DEBUG) // Print logging to debug.log on Windows settings.logging_dest = logging::LOG_TO_ALL; @@ -47,9 +59,10 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { #endif // !defined(OS_WIN) // Only enable logging when --enable-logging is specified. - auto command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(switches::kEnableLogging)) + if (!command_line->HasSwitch(switches::kEnableLogging)) { settings.logging_dest = logging::LOG_NONE; + logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES); + } logging::InitLogging(settings); @@ -81,7 +94,7 @@ void AtomMainDelegate::PreSandboxStartup() { } // Only append arguments for browser process. - if (!process_type.empty()) + if (!IsBrowserProcess(command_line)) return; #if defined(OS_WIN) diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 7bc1a2153e3..3c92266be33 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -11,6 +11,7 @@ #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/browser/api/save_page_handler.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/file_path_converter.h" @@ -237,6 +238,8 @@ Session::~Session() { void Session::OnDownloadCreated(content::DownloadManager* manager, content::DownloadItem* item) { auto web_contents = item->GetWebContents(); + if (SavePageHandler::IsSavePageTypes(item->GetMimeType())) + return; bool prevent_default = Emit( "will-download", DownloadItem::Create(isolate(), item), diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 0b8a9882cd1..5b3ef2f4aa7 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -162,6 +162,26 @@ struct Converter { } }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + content::SavePageType* out) { + std::string save_type; + if (!ConvertFromV8(isolate, val, &save_type)) + return false; + if (save_type == "HTMLOnly") { + *out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML; + } else if (save_type == "HTMLComplete") { + *out = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML; + } else if (save_type == "MHTML") { + *out = content::SAVE_PAGE_TYPE_AS_MHTML; + } else { + return false; + } + return true; + } +}; + } // namespace mate @@ -665,6 +685,13 @@ void WebContents::InsertCSS(const std::string& css) { web_contents()->InsertCSS(css); } +bool WebContents::SavePage(const base::FilePath& full_file_path, + const content::SavePageType& save_type, + const SavePageHandler::SavePageCallback& callback) { + auto handler = new SavePageHandler(web_contents(), callback); + return handler->Handle(full_file_path, save_type); +} + void WebContents::ExecuteJavaScript(const base::string16& code, bool has_user_gesture) { Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture)); @@ -976,6 +1003,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( .SetMethod("setUserAgent", &WebContents::SetUserAgent) .SetMethod("getUserAgent", &WebContents::GetUserAgent) .SetMethod("insertCSS", &WebContents::InsertCSS) + .SetMethod("savePage", &WebContents::SavePage) .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) .SetMethod("openDevTools", &WebContents::OpenDevTools) .SetMethod("closeDevTools", &WebContents::CloseDevTools) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 01075c450a5..7fa8951106b 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -9,6 +9,7 @@ #include #include "atom/browser/api/frame_subscriber.h" +#include "atom/browser/api/save_page_handler.h" #include "atom/browser/api/trackable_object.h" #include "atom/browser/common_web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" @@ -73,6 +74,9 @@ class WebContents : public mate::TrackableObject, void SetUserAgent(const std::string& user_agent); std::string GetUserAgent(); void InsertCSS(const std::string& css); + bool SavePage(const base::FilePath& full_file_path, + const content::SavePageType& save_type, + const SavePageHandler::SavePageCallback& callback); void ExecuteJavaScript(const base::string16& code, bool has_user_gesture); void OpenDevTools(mate::Arguments* args); diff --git a/atom/browser/api/save_page_handler.cc b/atom/browser/api/save_page_handler.cc new file mode 100644 index 00000000000..42b93befddf --- /dev/null +++ b/atom/browser/api/save_page_handler.cc @@ -0,0 +1,78 @@ +// 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/save_page_handler.h" + +#include + +#include "atom/browser/atom_browser_context.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "content/public/browser/web_contents.h" + +namespace atom { + +namespace api { + +SavePageHandler::SavePageHandler(content::WebContents* web_contents, + const SavePageCallback& callback) + : web_contents_(web_contents), + callback_(callback) { +} + +SavePageHandler::~SavePageHandler() { +} + +void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) { + // OnDownloadCreated is invoked during WebContents::SavePage, so the |item| + // here is the one stated by WebContents::SavePage. + item->AddObserver(this); +} + +bool SavePageHandler::Handle(const base::FilePath& full_path, + const content::SavePageType& save_type) { + auto download_manager = content::BrowserContext::GetDownloadManager( + web_contents_->GetBrowserContext()); + download_manager->AddObserver(this); + bool result = web_contents_->SavePage(full_path, + full_path.DirName(), + save_type); + download_manager->RemoveObserver(this); + // If initialization fails which means fail to create |DownloadItem|, we need + // to delete the |SavePageHandler| instance to avoid memory-leak. + if (!result) + delete this; + return result; +} + +void SavePageHandler::OnDownloadUpdated(content::DownloadItem* item) { + if (item->IsDone()) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + if (item->GetState() == content::DownloadItem::COMPLETE) { + callback_.Run(v8::Null(isolate)); + } else { + v8::Local error_message = v8::String::NewFromUtf8( + isolate, "Fail to save page"); + callback_.Run(v8::Exception::Error(error_message)); + } + Destroy(item); + } +} + +void SavePageHandler::Destroy(content::DownloadItem* item) { + item->RemoveObserver(this); + delete this; +} + +// static +bool SavePageHandler::IsSavePageTypes(const std::string& type) { + return type == "multipart/related" || type == "text/html"; +} + +} // namespace api + +} // namespace atom diff --git a/atom/browser/api/save_page_handler.h b/atom/browser/api/save_page_handler.h new file mode 100644 index 00000000000..dd1692a8bad --- /dev/null +++ b/atom/browser/api/save_page_handler.h @@ -0,0 +1,60 @@ +// 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_SAVE_PAGE_HANDLER_H_ +#define ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_ + +#include + +#include "content/public/browser/download_item.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/save_page_type.h" +#include "v8/include/v8.h" + +namespace base { +class FilePath; +} + +namespace content { +class WebContents; +} + +namespace atom { + +namespace api { + +// A self-destroyed class for handling save page request. +class SavePageHandler : public content::DownloadManager::Observer, + public content::DownloadItem::Observer { + public: + using SavePageCallback = base::Callback)>; + + SavePageHandler(content::WebContents* web_contents, + const SavePageCallback& callback); + ~SavePageHandler(); + + bool Handle(const base::FilePath& full_path, + const content::SavePageType& save_type); + + static bool IsSavePageTypes(const std::string& type); + + private: + void Destroy(content::DownloadItem* item); + + // content::DownloadManager::Observer: + void OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) override; + + // content::DownloadItem::Observer: + void OnDownloadUpdated(content::DownloadItem* item) override; + + content::WebContents* web_contents_; // weak + SavePageCallback callback_; +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_ diff --git a/atom/browser/auto_updater.cc b/atom/browser/auto_updater.cc index fd3d412f9bb..7ebae510e9f 100644 --- a/atom/browser/auto_updater.cc +++ b/atom/browser/auto_updater.cc @@ -16,4 +16,12 @@ void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) { delegate_ = delegate; } +#if defined(OS_MACOSX) && defined(MAS_BUILD) +void AutoUpdater::SetFeedURL(const std::string& url) { +} + +void AutoUpdater::CheckForUpdates() { +} +#endif + } // namespace auto_updater diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index 149b208b409..a1e159a16a7 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -69,7 +69,9 @@ unwrapArgs = (sender, args) -> rendererReleased = true ret = -> - throw new Error('Calling a callback of released renderer view') if rendererReleased + if rendererReleased + throw new Error("Attempting to call a function in a renderer window + that has been closed or released. Function provided here: #{meta.id}.") sender.send 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments) v8Util.setDestructor ret, -> return if rendererReleased diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index dd4694b3bd6..7f30aa00389 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -116,7 +116,8 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) { } else if (options.Get(switches::kCenter, ¢er) && center) { Center(); } - extensions::SizeConstraints size_constraints; + // On Linux and Window we may already have maximum size defined. + extensions::SizeConstraints size_constraints(GetContentSizeConstraints()); int min_height = 0, min_width = 0; if (options.Get(switches::kMinHeight, &min_height) | options.Get(switches::kMinWidth, &min_width)) { diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 4c8d014b1ec..e3688235bd4 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -390,7 +390,10 @@ gfx::Size NativeWindowViews::GetContentSize() { void NativeWindowViews::SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) { NativeWindow::SetContentSizeConstraints(size_constraints); - window_->OnSizeConstraintsChanged(); + // widget_delegate() is only available after Init() is called, we make use of + // this to determine whether native widget has initialized. + if (window_ && window_->widget_delegate()) + window_->OnSizeConstraintsChanged(); #if defined(USE_X11) if (resizable_) old_size_constraints_ = size_constraints; diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 77a5502de1e..01c4bc7e628 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,7 +17,11 @@ CFBundleIconFile atom.icns CFBundleVersion - 0.33.7 + 0.34.0 + CFBundleShortVersionString + 0.34.0 + LSApplicationCategoryType + public.app-category.developer-tools LSMinimumSystemVersion 10.8.0 NSMainNibFile diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 39085d2ed8a..5a25256831c 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,7,0 - PRODUCTVERSION 0,33,7,0 + FILEVERSION 0,34,0,0 + PRODUCTVERSION 0,34,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "0.33.7" + VALUE "FileVersion", "0.34.0" 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.7" + VALUE "ProductVersion", "0.34.0" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 2856598c0bc..14e32353af6 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -26,7 +26,6 @@ namespace { const char* kWebRuntimeFeatures[] = { switches::kExperimentalFeatures, switches::kExperimentalCanvasFeatures, - switches::kSubpixelFontScaling, switches::kOverlayScrollbars, switches::kOverlayFullscreenVideo, switches::kSharedWorker, diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index 8fb2efd3a3f..06fc30e7e3c 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -72,6 +72,10 @@ void AtomBindings::BindTo(v8::Isolate* isolate, // Do not warn about deprecated APIs. dict.Set("noDeprecation", true); +#if defined(MAS_BUILD) + dict.Set("mas", true); +#endif + mate::Dictionary versions; if (dict.Get("versions", &versions)) { versions.Set(ATOM_PROJECT_NAME, ATOM_VERSION_STRING); diff --git a/atom/common/api/lib/callbacks-registry.coffee b/atom/common/api/lib/callbacks-registry.coffee index 8f5eb62916c..d4c37f087b1 100644 --- a/atom/common/api/lib/callbacks-registry.coffee +++ b/atom/common/api/lib/callbacks-registry.coffee @@ -1,16 +1,25 @@ -savedGlobal = global # the "global.global" might be deleted later - module.exports = class CallbacksRegistry constructor: -> - @emptyFunc = -> throw new Error "Browser trying to call a non-exist callback - in renderer, this usually happens when renderer code forgot to release - a callback installed on objects in browser when renderer was going to be - unloaded or released." + @nextId = 0 @callbacks = {} add: (callback) -> - id = Math.random().toString() + id = ++@nextId + + # Capture the location of the function and put it in the ID string, + # so that release errors can be tracked down easily. + regexp = /at (.*)/gi + stackString = (new Error).stack + + while (match = regexp.exec(stackString)) isnt null + [x, location] = match + continue if location.indexOf('(native)') isnt -1 + continue if location.indexOf('atom.asar') isnt -1 + [x, filenameAndLine] = /([^/^\)]*)\)?$/gi.exec(location) + id = "#{filenameAndLine} (#{id})" + break + @callbacks[id] = callback id @@ -18,10 +27,10 @@ class CallbacksRegistry @callbacks[id] ? -> call: (id, args...) -> - @get(id).call savedGlobal, args... + @get(id).call global, args... apply: (id, args...) -> - @get(id).apply savedGlobal, args... + @get(id).apply global, args... remove: (id) -> delete @callbacks[id] diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 995a6f916b1..8351fff18ca 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -6,8 +6,8 @@ #define ATOM_VERSION_H #define ATOM_MAJOR_VERSION 0 -#define ATOM_MINOR_VERSION 33 -#define ATOM_PATCH_VERSION 7 +#define ATOM_MINOR_VERSION 34 +#define ATOM_PATCH_VERSION 0 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/crash_reporter/crash_reporter.cc b/atom/common/crash_reporter/crash_reporter.cc index 59b7fd51e45..b87ce54acd5 100644 --- a/atom/common/crash_reporter/crash_reporter.cc +++ b/atom/common/crash_reporter/crash_reporter.cc @@ -64,4 +64,23 @@ CrashReporter::GetUploadedReports(const std::string& path) { return result; } +void CrashReporter::InitBreakpad(const std::string& product_name, + const std::string& version, + const std::string& company_name, + const std::string& submit_url, + bool auto_submit, + bool skip_system_crash_handler) { +} + +void CrashReporter::SetUploadParameters() { +} + +#if defined(OS_MACOSX) && defined(MAS_BUILD) +// static +CrashReporter* CrashReporter::GetInstance() { + static CrashReporter crash_reporter; + return &crash_reporter; +} +#endif + } // namespace crash_reporter diff --git a/atom/common/crash_reporter/crash_reporter.h b/atom/common/crash_reporter/crash_reporter.h index c7d58ca3aa7..98832fea45d 100644 --- a/atom/common/crash_reporter/crash_reporter.h +++ b/atom/common/crash_reporter/crash_reporter.h @@ -40,8 +40,8 @@ class CrashReporter { const std::string& company_name, const std::string& submit_url, bool auto_submit, - bool skip_system_crash_handler) = 0; - virtual void SetUploadParameters() = 0; + bool skip_system_crash_handler); + virtual void SetUploadParameters(); StringMap upload_parameters_; bool is_browser_; diff --git a/atom/common/crash_reporter/crash_reporter_win.cc b/atom/common/crash_reporter/crash_reporter_win.cc index be096da80e2..78bd196f6cc 100644 --- a/atom/common/crash_reporter/crash_reporter_win.cc +++ b/atom/common/crash_reporter/crash_reporter_win.cc @@ -11,6 +11,25 @@ #include "base/memory/singleton.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "content/public/common/result_codes.h" +#include "gin/public/debug.h" +#include "sandbox/win/src/nt_internals.h" + +#pragma intrinsic(_AddressOfReturnAddress) +#pragma intrinsic(_ReturnAddress) + +#ifdef _WIN64 +// See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx +typedef struct _UNWIND_INFO { + unsigned char Version : 3; + unsigned char Flags : 5; + unsigned char SizeOfProlog; + unsigned char CountOfCodes; + unsigned char FrameRegister : 4; + unsigned char FrameOffset : 4; + ULONG ExceptionHandler; +} UNWIND_INFO, *PUNWIND_INFO; +#endif namespace crash_reporter { @@ -24,6 +43,94 @@ const MINIDUMP_TYPE kSmallDumpType = static_cast( const wchar_t kWaitEventFormat[] = L"$1CrashServiceWaitEvent"; const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\$1 Crash Service"; +typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, + NTSTATUS ExitStatus); +char* g_real_terminate_process_stub = NULL; + +void TerminateProcessWithoutDump() { + // Patched stub exists based on conditions (See InitCrashReporter). + // As a side note this function also gets called from + // WindowProcExceptionFilter. + if (g_real_terminate_process_stub == NULL) { + ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); + } else { + NtTerminateProcessPtr real_terminate_proc = + reinterpret_cast( + static_cast(g_real_terminate_process_stub)); + real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED); + } +} + +#ifdef _WIN64 +int CrashForExceptionInNonABICompliantCodeRange( + PEXCEPTION_RECORD ExceptionRecord, + ULONG64 EstablisherFrame, + PCONTEXT ContextRecord, + PDISPATCHER_CONTEXT DispatcherContext) { + EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord }; + if (!CrashReporter::GetInstance()) + return EXCEPTION_CONTINUE_SEARCH; + return static_cast(CrashReporter::GetInstance())-> + CrashForException(&info); +} + +struct ExceptionHandlerRecord { + RUNTIME_FUNCTION runtime_function; + UNWIND_INFO unwind_info; + unsigned char thunk[12]; +}; + +void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { + ExceptionHandlerRecord* record = + reinterpret_cast(start); + + // We assume that the first page of the code range is executable and + // committed and reserved for breakpad. What could possibly go wrong? + + // All addresses are 32bit relative offsets to start. + record->runtime_function.BeginAddress = 0; + record->runtime_function.EndAddress = + base::checked_cast(size_in_bytes); + record->runtime_function.UnwindData = + offsetof(ExceptionHandlerRecord, unwind_info); + + // Create unwind info that only specifies an exception handler. + record->unwind_info.Version = 1; + record->unwind_info.Flags = UNW_FLAG_EHANDLER; + record->unwind_info.SizeOfProlog = 0; + record->unwind_info.CountOfCodes = 0; + record->unwind_info.FrameRegister = 0; + record->unwind_info.FrameOffset = 0; + record->unwind_info.ExceptionHandler = + offsetof(ExceptionHandlerRecord, thunk); + + // Hardcoded thunk. + // mov imm64, rax + record->thunk[0] = 0x48; + record->thunk[1] = 0xb8; + void* handler = &CrashForExceptionInNonABICompliantCodeRange; + memcpy(&record->thunk[2], &handler, 8); + + // jmp rax + record->thunk[10] = 0xff; + record->thunk[11] = 0xe0; + + // Protect reserved page against modifications. + DWORD old_protect; + CHECK(VirtualProtect( + start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect)); + CHECK(RtlAddFunctionTable( + &record->runtime_function, 1, reinterpret_cast(start))); +} + +void UnregisterNonABICompliantCodeRange(void* start) { + ExceptionHandlerRecord* record = + reinterpret_cast(start); + + CHECK(RtlDeleteFunctionTable(&record->runtime_function)); +} +#endif // _WIN64 + } // namespace CrashReporterWin::CrashReporterWin() { @@ -63,26 +170,46 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name, // to allow any previous handler to detach in the correct order. breakpad_.reset(); - int handler_types = google_breakpad::ExceptionHandler::HANDLER_EXCEPTION | - google_breakpad::ExceptionHandler::HANDLER_PURECALL; breakpad_.reset(new google_breakpad::ExceptionHandler( temp_dir.value(), FilterCallback, MinidumpCallback, this, - handler_types, + google_breakpad::ExceptionHandler::HANDLER_ALL, kSmallDumpType, pipe_name.c_str(), GetCustomInfo(product_name, version, company_name))); if (!breakpad_->IsOutOfProcess()) LOG(ERROR) << "Cannot initialize out-of-process crash handler"; + +#ifdef _WIN64 + // Hook up V8 to breakpad. + { + // gin::Debug::SetCodeRangeCreatedCallback only runs the callback when + // Isolate is just created, so we have to manually run following code here. + void* code_range = nullptr; + size_t size = 0; + v8::Isolate::GetCurrent()->GetCodeRange(&code_range, &size); + if (code_range && size) + RegisterNonABICompliantCodeRange(code_range, size); + } + gin::Debug::SetCodeRangeDeletedCallback(UnregisterNonABICompliantCodeRange); +#endif } void CrashReporterWin::SetUploadParameters() { upload_parameters_["platform"] = "win32"; } +int CrashReporterWin::CrashForException(EXCEPTION_POINTERS* info) { + if (breakpad_) { + breakpad_->WriteMinidumpForException(info); + TerminateProcessWithoutDump(); + } + return EXCEPTION_CONTINUE_SEARCH; +} + // static bool CrashReporterWin::FilterCallback(void* context, EXCEPTION_POINTERS* exinfo, diff --git a/atom/common/crash_reporter/crash_reporter_win.h b/atom/common/crash_reporter/crash_reporter_win.h index 72b9411d219..09c7ff4eaad 100644 --- a/atom/common/crash_reporter/crash_reporter_win.h +++ b/atom/common/crash_reporter/crash_reporter_win.h @@ -29,6 +29,9 @@ class CrashReporterWin : public CrashReporter { bool skip_system_crash_handler) override; void SetUploadParameters() override; + // Crashes the process after generating a dump for the provided exception. + int CrashForException(EXCEPTION_POINTERS* info); + private: friend struct DefaultSingletonTraits; diff --git a/atom/common/crash_reporter/win/crash_service.cc b/atom/common/crash_reporter/win/crash_service.cc index d315b0b9419..9b6ba7e03a6 100644 --- a/atom/common/crash_reporter/win/crash_service.cc +++ b/atom/common/crash_reporter/win/crash_service.cc @@ -311,7 +311,7 @@ bool CrashService::Initialize(const base::string16& application_name, // service is initialized. base::string16 wait_name = ReplaceStringPlaceholders( kWaitEventFormat, application_name, NULL); - HANDLE wait_event = ::CreateEventW(NULL, TRUE, FALSE, wait_name.c_str()); + HANDLE wait_event = ::CreateEventW(NULL, TRUE, TRUE, wait_name.c_str()); ::SetEvent(wait_event); return true; diff --git a/atom/common/native_mate_converters/v8_value_converter.cc b/atom/common/native_mate_converters/v8_value_converter.cc index a91e614fc6d..7d3a1277cb8 100644 --- a/atom/common/native_mate_converters/v8_value_converter.cc +++ b/atom/common/native_mate_converters/v8_value_converter.cc @@ -152,6 +152,10 @@ v8::Local V8ValueConverter::ToV8ValueImpl( return ToV8Object(isolate, static_cast(value)); + case base::Value::TYPE_BINARY: + return ToArrayBuffer(isolate, + static_cast(value)); + default: LOG(ERROR) << "Unexpected value type: " << value->GetType(); return v8::Null(isolate); @@ -200,6 +204,13 @@ v8::Local V8ValueConverter::ToV8Object( return result.GetHandle(); } +v8::Local V8ValueConverter::ToArrayBuffer( + v8::Isolate* isolate, const base::BinaryValue* value) const { + return node::Buffer::Copy(isolate, + value->GetBuffer(), + value->GetSize()).ToLocalChecked(); +} + base::Value* V8ValueConverter::FromV8ValueImpl( FromV8ValueState* state, v8::Local val, diff --git a/atom/common/native_mate_converters/v8_value_converter.h b/atom/common/native_mate_converters/v8_value_converter.h index db108ad9b04..2b695b43747 100644 --- a/atom/common/native_mate_converters/v8_value_converter.h +++ b/atom/common/native_mate_converters/v8_value_converter.h @@ -41,6 +41,9 @@ class V8ValueConverter { v8::Local ToV8Object( v8::Isolate* isolate, const base::DictionaryValue* dictionary) const; + v8::Local ToArrayBuffer( + v8::Isolate* isolate, + const base::BinaryValue* value) const; base::Value* FromV8ValueImpl(FromV8ValueState* state, v8::Local value, diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 46687becf84..e05768b5230 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -99,7 +99,6 @@ const char kClientCertificate[] = "client-certificate"; // Web runtime features. const char kExperimentalFeatures[] = "experimental-features"; const char kExperimentalCanvasFeatures[] = "experimental-canvas-features"; -const char kSubpixelFontScaling[] = "subpixel-font-scaling"; const char kOverlayScrollbars[] = "overlay-scrollbars"; const char kOverlayFullscreenVideo[] = "overlay-fullscreen-video"; const char kSharedWorker[] = "shared-worker"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 16046d19c82..c568804c4df 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -51,7 +51,6 @@ extern const char kClientCertificate[]; extern const char kExperimentalFeatures[]; extern const char kExperimentalCanvasFeatures[]; -extern const char kSubpixelFontScaling[]; extern const char kOverlayScrollbars[]; extern const char kOverlayFullscreenVideo[]; extern const char kSharedWorker[]; diff --git a/atom/common/resources/mac/Info.plist b/atom/common/resources/mac/Info.plist index 332babe979e..7b56a46470e 100644 --- a/atom/common/resources/mac/Info.plist +++ b/atom/common/resources/mac/Info.plist @@ -2,12 +2,12 @@ - CFBundleExecutable - ${PRODUCT_NAME} Framework CFBundleIdentifier ${ATOM_BUNDLE_ID} CFBundleName - ${PRODUCT_NAME} Framework + ${PRODUCT_NAME} + CFBundleExecutable + ${PRODUCT_NAME} CFBundlePackageType FMWK NSSupportsAutomaticGraphicsSwitching diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index eeadabcba73..10dd2541b9c 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -234,8 +234,6 @@ void AtomRendererClient::EnableWebRuntimeFeatures() { blink::WebRuntimeFeatures::enableExperimentalFeatures(b); if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures, &b)) blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(b); - if (IsSwitchEnabled(command_line, switches::kSubpixelFontScaling, &b)) - blink::WebRuntimeFeatures::enableSubpixelFontScaling(b); if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars, &b)) blink::WebRuntimeFeatures::enableOverlayScrollbars(b); if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo, &b)) diff --git a/docs-translations/ko-KR/api/global-shortcut.md b/docs-translations/ko-KR/api/global-shortcut.md index 84c1c70798d..f000a3bdb6a 100644 --- a/docs-translations/ko-KR/api/global-shortcut.md +++ b/docs-translations/ko-KR/api/global-shortcut.md @@ -12,7 +12,9 @@ var globalShortcut = require('global-shortcut'); app.on('ready', function() { // 'ctrl+x' 단축키를 리스너에 등록합니다. - var ret = globalShortcut.register('ctrl+x', function() { console.log('ctrl+x is pressed'); }) + var ret = globalShortcut.register('ctrl+x', function() { + console.log('ctrl+x is pressed'); + }); if (!ret) { console.log('registration failed'); @@ -56,4 +58,4 @@ app.on('will-quit', function() { ### `globalShortcut.unregisterAll()` -모든 전역 단축키 등록을 해제합니다. +모든 전역 단축키의 등록을 해제합니다. diff --git a/docs-translations/ko-KR/styleguide.md b/docs-translations/ko-KR/styleguide.md index aaa9274cd0b..92b68e34550 100644 --- a/docs-translations/ko-KR/styleguide.md +++ b/docs-translations/ko-KR/styleguide.md @@ -56,9 +56,13 @@ Electron 문서 구조를 이해하는 데 참고할 수 있는 유용한 도움 메서드 이름은 인수가 무엇을 받는지에 따라 결정됩니다. 선택적 인수는 브라켓([, ])으로 묶어 이 인수가 다른 인수뒤에서 선택적으로 사용될 수 있다는 것을 표시합니다. -메서드의 밑에선 각 인수에 대해 자세한 설명을 합니다. 인수의 타입은 일반적인 타입 중 하나를 받거나: -[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) -Electron의 [`webContent`](api/web-content.md)같은 커스텀 타입을 받습니다. +메서드 이름 하단에선 각 인수에 대해 자세한 설명을 합니다. +인수의 타입은 일반적인 타입 중 하나를 받거나: +[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), +[`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), +[`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), +[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +와 같은 일반적으로 쓰이는 타입 중 하나를 받거나 Electron의 [`webContent`](api/web-content.md)같은 커스텀 타입을 받습니다. ### Events diff --git a/docs-translations/ko-KR/tutorial/devtools-extension.md b/docs-translations/ko-KR/tutorial/devtools-extension.md index e6a9b559c35..966431dd72b 100644 --- a/docs-translations/ko-KR/tutorial/devtools-extension.md +++ b/docs-translations/ko-KR/tutorial/devtools-extension.md @@ -14,13 +14,17 @@ $ cd /some-directory $ git clone --recursive https://github.com/facebook/react-devtools.git ``` -그리고 개발자 콘솔이 열린 창에서 다음의 코드를 콘솔에 입력하면 확장 기능을 로드할 수 있습니다: +[`react-devtools/shells/chrome/Readme.md`](https://github.com/facebook/react-devtools/blob/master/shells/chrome/Readme.md) +가이드를 통해 확장 기능을 개발하는 방법을 알아볼 수 있습니다. + + +그리고 개발자 콘솔에서 다음 코드를 입력하면 확장 기능을 로드할 수 있습니다: ```javascript -require('remote').require('browser-window').addDevToolsExtension('/some-directory/react-devtools'); +require('remote').require('browser-window').addDevToolsExtension('/some-directory/react-devtools/shells/chrome'); ``` -확장 기능을 unload 하고 콘솔을 다시 열 때 해당 확장 기능이 로드되지 않도록 하려면 `BrowserWindow.removeDevToolsExtension` API를 사용하면 됩니다: +확장 기능을 언로드 하고 콘솔을 다시 열 때 해당 확장 기능이 로드되지 않도록 하려면 `BrowserWindow.removeDevToolsExtension` API를 사용하면 됩니다: ```javascript require('remote').require('browser-window').removeDevToolsExtension('React Developer Tools'); @@ -29,13 +33,13 @@ require('remote').require('browser-window').removeDevToolsExtension('React Devel ## 개발자 콘솔 확장 기능의 구성 형식 모든 개발자 콘솔 확장은 완벽히 Chrome 브라우저를 위해 작성되었기 때문에 Electron에서도 로드할 수 있습니다. -하지만 반드시 확장 기능은 소스코드 그대로의 디렉터리(폴더) 형태여야 합니다. 그래서 `crx` 등의 포맷으로 패키징된 확장 기능의 경우 -사용자가 직접 해당 패키지의 압축을 풀어서 로드하지 않는 이상은 Electron에서 해당 확장 기능의 압축을 풀 방법이 없습니다. +하지만 반드시 확장 기능은 소스 코드 디렉터리(폴더) 형태여야 합니다. 그래서 `crx` 등의 포맷으로 패키징된 확장 기능의 경우 +사용자가 직접 해당 패키지의 압축을 풀어서 로드하지 않는 이상 Electron에서 해당 확장 기능의 압축을 풀 방법이 없습니다. ## 백그라운드 페이지 현재 Electron은 Chrome에서 지원하는 백그라운드 페이지(background pages)를 지원하지 않습니다. -몇몇 확장 기능은 이 기능에 의존하는 경우가 있는데 이 경우 해당 확장 기능은 Electron에서 작동하지 않을 수 있습니다. +몇몇 확장 기능은 이 기능에 의존하는 경우가 있는데, 이 때 해당 확장 기능은 Electron에서 작동하지 않을 수 있습니다. ## `chrome.*` API diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index e60d9505a24..db88edaf77e 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -1,18 +1,18 @@ ## Guias -* [Distribuir Aplicação](tutorial/application-distribution.md) +* [Distribuição de Aplicações](tutorial/application-distribution.md) * [Empacotamento da aplicação](tutorial/application-packaging.md) -* [Usando módulos nativos](../../docs/tutorial/using-native-node-modules.md) -* [Depuração do processo principal](../../docs/tutorial/debugging-main-process.md) +* [Usando módulos nativos](tutorial/using-native-node-modules.md) +* [Depuração do processo principal](tutorial/debugging-main-process.md) * [Usando Selenium e WebDriver](../../docs/tutorial/using-selenium-and-webdriver.md) * [Extensão DevTools](../../docs/tutorial/devtools-extension.md) -* [Usando o plugin papper flash](../../docs/tutorial/using-pepper-flash-plugin.md) +* [Usando o plugin papper flash](tutorial/using-pepper-flash-plugin.md) ## Tutoriais -* [Introdução](../../docs/tutorial/quick-start.md) -* [A integração com o ambiente de desenvolvimento](../../docs/tutorial/desktop-environment-integration.md) -* [Evento de detecção on-line/off-line](../../docs/tutorial/online-offline-events.md) +* [Introdução](tutorial/quick-start.md) +* [A integração com o ambiente de desenvolvimento](tutorial/desktop-environment-integration.md) +* [Evento de detecção on-line/off-line](tutorial/online-offline-events.md) ## API - Referencias @@ -68,4 +68,4 @@ Módulos de ambos os processos: * [Instrução de build (Mac)](../../docs/development/build-instructions-osx.md) * [Instrução de build (Windows)](../../docs/development/build-instructions-windows.md) * [Instrução de build (Linux)](../../docs/development/build-instructions-linux.md) -* [Configurando um symbol server no debugger](../../docs/development/setting-up-symbol-server.md) \ No newline at end of file +* [Configurando um symbol server no debugger](../../docs/development/setting-up-symbol-server.md) diff --git a/docs-translations/pt-BR/tutorial/debugging-main-process.md b/docs-translations/pt-BR/tutorial/debugging-main-process.md new file mode 100644 index 00000000000..ee79a523e92 --- /dev/null +++ b/docs-translations/pt-BR/tutorial/debugging-main-process.md @@ -0,0 +1,53 @@ +# Depurando o Processo Principal + +A janela do navegador, DevTools, pode somente depurar o processo de renderização +de scripts (por exemplo, as páginas da web). Para providenciar um modo de +depurar os scripts através do processo principal, o Electron criou as opções +`--debug` e `--debug-brk`. + +## Opções da Linha de Comando + +Use a seguinte opção na linha de comando para depurar o processo principal do +Electron: + +### `--debug=[porta]` + +Quando este comando é usado, o Electron irá executar o protocolo de depuração +V8 mandando as mensagens na `porta`. A `porta` padrão é `5858`. + +### `--debug-brk=[porta]` + +Semelhante ao `--debug`, porém pausa o script na primeira linha. + +## Usando node-inspector para depurar + +__Nota:__ O Electron usa a versão v0.11.13 do Node, a qual, atualmenta não +funciona muito bem com node-inspector, e o processo principal irá quebrar se +você inspecionar o objeto `process` pelo console do node-inspector. + +### 1. Inicie o servidor [node-inspector][node-inspector] + +```bash +$ node-inspector +``` + +### 2. Habilite o modo de depuração para o Electron + +Você pode também iniciar o Electron com um ponto de depuração, desta maneira: + +```bash +$ electron --debug=5858 sua/aplicacao +``` + +ou para pausar o script na primeira linha: + +```bash +$ electron --debug-brk=5858 sua/aplicacao +``` + +### 3. Carregue o debugger UI + +Abra este endereço http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 +usando o Chrome. + +[node-inspector]: https://github.com/node-inspector/node-inspector diff --git a/docs-translations/pt-BR/tutorial/desktop-environment-integration.md b/docs-translations/pt-BR/tutorial/desktop-environment-integration.md new file mode 100644 index 00000000000..20a78d32d53 --- /dev/null +++ b/docs-translations/pt-BR/tutorial/desktop-environment-integration.md @@ -0,0 +1,260 @@ +# Integração com o ambiente desktop + +Diferentes sistemas operacionais possuem diferentes formas de integrar +aplicacões desktop em seus ambientes. Por exemplo, no Windows, as aplicações podem +inserir atalhos no JumpList da barra de tarefas, no Mac, aplicações podem implementar um +menu customizado na dock. + +Este guia explica como integrar suas aplicações no ambiente desktop com a API +do Electron. + +## Documentos Recentes (Windows & OS X) + +O Windows e o OS X disponibilizam um acesso fácil para a lista de arquivos +abertos recentemente pela aplicação através do JumpList ou Dock Menu respectivamente. + +__JumpList:__ + +![JumpList Recent Files](http://i.msdn.microsoft.com/dynimg/IC420538.png) + +__Dock menu da Aplicação:__ + + + +Para adicionar um arquivo para os documentos recentes, você pode usar a API +[app.addRecentDocument][addrecentdocument]: + +```javascript +var app = require('app'); +app.addRecentDocument('/Users/USERNAME/Desktop/work.type'); +``` + +E você pode usar a API [app.clearRecentDocuments][clearrecentdocuments] para +limpar a lista de documentos recentes. + +```javascript +app.clearRecentDocuments(); +``` + +### Notas para Windows + +A fim de ser possível usar estas funcionalidades no Windows, sua aplicação deve +estar registrada como um handler daquele tipo de documento, caso contrário, o +arquivo não será exibido no JumpList mesmo depois de você ter adicionado isto. +Você pode encontrar qualquer coisa sobre o registro da aplicacão em +[Application Registration][app-registration]. + +Quando um usuário clica em um arquivo na JumpList, uma nova instância da sua aplicacão +deve ser iniciada com o caminho do arquivo adicionado como um argumento de +linha de comando. + +### Notas para OS X + +Quando um arquivo for requisitado pelo menu de documentos recentes, o evento `open-file` +do módulo `app` irá ser emitido. + +## Dock Menu customizado (OS X) + +OS X permite que desenvolvedores especifiquem um menu customizado para a dock, +que normalmente contém alguns atalhos para as funcionalidades mais utilizadas +da sua aplicação. + +__Dock menu do Terminal.app:__ + + + +Para criar seu Dock Menu customizado, você pode usar a API `app.dock.setMenu`, +ela está disponível apenas no OS X: + +```javascript +var app = require('app'); +var Menu = require('menu'); +var dockMenu = Menu.buildFromTemplate([ + { label: 'New Window', click: function() { console.log('New Window'); } }, + { label: 'New Window with Settings', submenu: [ + { label: 'Basic' }, + { label: 'Pro'} + ]}, + { label: 'New Command...'} +]); +app.dock.setMenu(dockMenu); +``` + +## Tarefas do Usuário (Windows) + +No Windows você pode especificar ações customizadas na categoria `Tarefas` do JumpList, +esse texto foi copiado do MSDN: + +> Applications define tasks based on both the program's features and the key +> things a user is expected to do with them. Tasks should be context-free, in +> that the application does not need to be running for them to work. They +> should also be the statistically most common actions that a normal user would +> perform in an application, such as compose an email message or open the +> calendar in a mail program, create a new document in a word processor, launch +> an application in a certain mode, or launch one of its subcommands. An +> application should not clutter the menu with advanced features that standard +> users won't need or one-time actions such as registration. Do not use tasks +> for promotional items such as upgrades or special offers. +> +> It is strongly recommended that the task list be static. It should remain the +> same regardless of the state or status of the application. While it is +> possible to vary the list dynamically, you should consider that this could +> confuse the user who does not expect that portion of the destination list to +> change. + +__Tarefas do Internet Explorer:__ + +![IE](http://i.msdn.microsoft.com/dynimg/IC420539.png) + +Ao contrário do Menu Dock no OS X que é um verdadeiro menu, tarefas do usuário no Windows +funcionam como atalhos, de uma forma que quando o usuário clica em uma tarefa, um programa +deve ser executado com os argumentos especificados. + +Para setar tarefas do usuário para sua aplicação, você pode usar a API +[app.setUserTasks][setusertaskstasks]: + +```javascript +var app = require('app'); +app.setUserTasks([ + { + program: process.execPath, + arguments: '--new-window', + iconPath: process.execPath, + iconIndex: 0, + title: 'New Window', + description: 'Create a new window' + } +]); +``` + +Para limpar sua lista de tarefas, apenas chame `app.setUserTasks` com um +array vazio. + +```javascript +app.setUserTasks([]); +``` + +As tarefas do usuário são exibidas mesmo depois da aplicação ser fechada, +então o ícone e o caminho do programa especificado pela tarefa deve existir +até sua aplicação ser desinstalada. + +## Miniaturas na Barra de Ferramentas + +No Windows você pode adicionar uma miniatura na barra de ferramentas com botões +específicos para a janela e barra de tarefas para aplicação. Isso provê ao usuário +uma forma de acessar um comando específico para janela sem ser necessário restaurar +ou ativar a janela. + +Isto é ilustrado no MSDN: + +> This toolbar is simply the familiar standard toolbar common control. It has a +> maximum of seven buttons. Each button's ID, image, tooltip, and state are defined +> in a structure, which is then passed to the taskbar. The application can show, +> enable, disable, or hide buttons from the thumbnail toolbar as required by its +> current state. +> +> For example, Windows Media Player might offer standard media transport controls +> such as play, pause, mute, and stop. + +__Miniaturas da barra de tarefas do Windows Media Player:__ + +![player](https://i-msdn.sec.s-msft.com/dynimg/IC420540.png) + +Você pode usar [BrowserWindow.setThumbarButtons][setthumbarbuttons] para criar +miniaturas na barra de ferramentas para sua aplicação. + +``` +var BrowserWindow = require('browser-window'); +var path = require('path'); +var win = new BrowserWindow({ + width: 800, + height: 600 +}); +win.setThumbarButtons([ + { + tooltip: "button1", + icon: path.join(__dirname, 'button1.png'), + click: function() { console.log("button2 clicked"); } + }, + { + tooltip: "button2", + icon: path.join(__dirname, 'button2.png'), + flags:['enabled', 'dismissonclick'], + click: function() { console.log("button2 clicked."); } + } +]); +``` + +Para limpar os botões na miniatura da barra de ferramentas, apenas chame +`BrowserWindow.setThumbarButtons` com um array vazio. + +```javascript +win.setThumbarButtons([]); +``` + +## Unity Launcher Shortcuts (Linux) + +No Unity, você pode adicionar entradas customizadas para estes lançadores modificando +o arquivo `.desktop`, veja [Adding Shortcuts to a Launcher][unity-launcher]. + +__Launcher shortcuts do Audacious:__ + +![audacious](https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles?action=AttachFile&do=get&target=shortcuts.png) + +## Barra de Progresso na Barra de Tarefas (Windows & Unity) + +No Windows o botão na barra de tarefas pode ser usado para exibir uma barra de progresso. +Isto permite que a janela exiba informação sobre o progresso de algum processo sem +a necessidade do usuário mudar de janela. + +A Unity DE também tem uma funcionalidade parecida que permite especificar uma barra +de progresso no ícone do lançador. + +__Barra de Progresso no botão da barra de tarefas:__ + +![Barra de Progresso na Barra de Tarefas](https://cloud.githubusercontent.com/assets/639601/5081682/16691fda-6f0e-11e4-9676-49b6418f1264.png) + +__Barra de progresso no Unity launcher:__ + +![Unity Launcher](https://cloud.githubusercontent.com/assets/639601/5081747/4a0a589e-6f0f-11e4-803f-91594716a546.png) + +Para adicionar uma barra de progresso para uma janela, você pode ver a API: +[BrowserWindow.setProgressBar][setprogressbar]: + +```javascript +var window = new BrowserWindow({...}); +window.setProgressBar(0.5); +``` + +## Representação do arquivo na janela (OS X) + +No OS X, uma janela pode possuir a representação de um arquivo na barra de título, +permitindo que ao usuário acionar um Command-Click ou Control-Click sobre o título da janela, +uma pop-up de navegação entre arquivos é exibida. + +Você também pode inserir um estado de edição na janela para que o ícone do arquivo +possa indicar se o documento nesta janela foi modificado. + +__Menu popup da representação de arquivo:__ + + + +Para inserir o arquivo de representacão da janela, você pode usar as API +[BrowserWindow.setRepresentedFilename][setrepresentedfilename] e +[BrowserWindow.setDocumentEdited][setdocumentedited]: + +```javascript +var window = new BrowserWindow({...}); +window.setRepresentedFilename('/etc/passwd'); +window.setDocumentEdited(true); +``` + +[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath +[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments +[setusertaskstasks]: ../api/app.md#appsetusertaskstasks +[setprogressbar]: ../api/browser-window.md#browserwindowsetprogressbarprogress +[setrepresentedfilename]: ../api/browser-window.md#browserwindowsetrepresentedfilenamefilename +[setdocumentedited]: ../api/browser-window.md#browserwindowsetdocumenteditededited +[app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx +[unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher +[setthumbarbuttons]: ../api/browser-window.md#browserwindowsetthumbarbuttonsbuttons diff --git a/docs-translations/pt-BR/tutorial/online-offline-events.md b/docs-translations/pt-BR/tutorial/online-offline-events.md new file mode 100644 index 00000000000..294a62e7a81 --- /dev/null +++ b/docs-translations/pt-BR/tutorial/online-offline-events.md @@ -0,0 +1,83 @@ +# Online/Offline Event Detection + +Os eventos de detecão Online e Offile podem ser implementados no processo +de renderização utilizando a API padrão do HTML, como é mostrado no exemplo +a seguir. + +_main.js_ + +```javascript +var app = require('app'); +var BrowserWindow = require('browser-window'); +var onlineStatusWindow; + +app.on('ready', function() { + onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); + onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); +}); +``` + +_online-status.html_ + +```html + + + + + + +``` + +Pode haver casos onde você também deseja responder a estes eventos no processo principal. +Mas o processo principal não consegue detectar esses eventos diretamente, pois não possui +um objeto `navigator`. Utilizando a ferramentas para comunicação entre processos, os eventos +podem ser direcionados para o processo principal e manipulados quando necessário. Você +pode ver isto no exemplo abaixo. + +_main.js_ + +```javascript +var app = require('app'); +var ipc = require('ipc'); +var BrowserWindow = require('browser-window'); +var onlineStatusWindow; + +app.on('ready', function() { + onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); + onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); +}); + +ipc.on('online-status-changed', function(event, status) { + console.log(status); +}); +``` + +_online-status.html_ + +```html + + + + + + +``` diff --git a/docs-translations/pt-BR/tutorial/using-native-node-modules.md b/docs-translations/pt-BR/tutorial/using-native-node-modules.md new file mode 100644 index 00000000000..c5e9eeddd61 --- /dev/null +++ b/docs-translations/pt-BR/tutorial/using-native-node-modules.md @@ -0,0 +1,68 @@ +# Usando Módulos Nativos do Node + +Os módulos nativos do Node são suportados pelo Electron, desde que o Electron +esteja usando uma versão diferente da oficial V8 do Node, você tem de +especificar manualmente a localização das headers do Electron quando compilar os +módulos nativos. + +## Compatibilidade de Módulos Nativos do Node + +Módulos nativos podem quebrar quando utilizar a nova versão do Node, V8. +Para ter certeza que o módulo que você está interessado em trabalhar com o +Electron, você deve checar se a versão do Node utilizada é compatível com a +usada pelo Electron. +Você pode verificar qual versão do Node foi utilizada no Electron olhando na +página [releases](https://github.com/atom/electron/releases) ou usando +`process.version` (veja [Quick Start](https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md) +por exemplo). + +Considere usar [NAN](https://github.com/nodejs/nan/) para seus próprios +módulos, caso isso facilite o suporte da múltiplas versões do Node. Isso é +também de grande ajuda para fazer a portabilidade dos módulos antigos para as +versões mais novas do Node, assim podendo trabalhar com o Electron. + +## Como Instalar os Módulos Nativos + +Existem três maneiras de instalar os módulos nativos: + +### O Modo Fácil + +O modo mais direto para recompilar os módulos é pelo pacote +[`electron-rebuild`](https://github.com/paulcbetts/electron-rebuild), +o que lida com passos manuais para baixar as headers e construir os módulos +nativos: + +```sh +npm install --save-dev electron-rebuild + +# Sempre que rodar npm install, execute também: +node ./node_modules/.bin/electron-rebuild +``` + +### Via npm + +Você pode usar também `npm` para instalar os módulos. Os passos são exatamente +os mesmos com os módulos Node, exceto que você precisa configurar algumas +variáveis da ambiente: + +```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 +``` + +### O modo node-gyp + +Para compilar os módulos do Node com as headers do Electron, você precisa dizer +ao `node-gyp` onde baixar as headers e qual versão usar: + +```bash +$ cd /path-to-module/ +$ HOME=~/.electron-gyp node-gyp rebuild --target=0.29.1 --arch=x64 --dist-url=https://atom.io/download/atom-shell +``` + +A tag `HOME=~/.electron-gyp` altera onde encontrar as headers de desenvolvimento. +A tag `--target=0.29.1` é a versão do Electron. A tag `--dist-url=...` especifica +onde baixar as headers. A tag `--arch=x64` diz ao módulo que é feito em 64bit. diff --git a/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md b/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md new file mode 100644 index 00000000000..a92f61c78e2 --- /dev/null +++ b/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md @@ -0,0 +1,68 @@ +# Usando o Plugin Pepper Flash + +Electron atualmente suporta o plugin Pepper Flash. Para usá-lo no Electron, +você deve especificar manualmente a localização do plugin e então ele será +habilitado em sua aplicação. + +## Prepare uma cópia do plugin Flash + +Tanto no OS X como no Linux, os detalhes do plugin Pepper Flash podem ser +encontrados navegando por `chrome://plugins` no navegador Chrome. Essa +localização e versão são úteis para o suporte do plugin Electron's Pepper Flash. +Você pode também copiar para outra localização. + +## Adicionando a opçao Electron + +Você pode adicionar diretamente `--ppapi-flash-path` e `ppapi-flash-version` +para a linha de comando do Electron ou usando o método +`app.commandLine.appendSwitch` após o evento pronto. Também, adicione a opção +`plugins` em `browser-window`. +Por exemplo: + +```javascript +var app = require('app'); +var BrowserWindow = require('browser-window'); + +// Informa os erros ao ao servidor. +require('crash-reporter').start(); + +// Mantém uma referência global da janela, se não manter, a janela irá fechar +// automaticamente quando o objeto javascript for GCed. +var mainWindow = null; + +// Sai assim que todas as janelas forem fechadas. +app.on('window-all-closed', function() { + if (process.platform != 'darwin') { + app.quit(); + } +}); + +// Epecifica o caminho do flash. +// No Windows, deve ser /path/to/pepflashplayer.dll +// No Mac, /path/to/PepperFlashPlayer.plugin +// No Linux, /path/to/libpepflashplayer.so +app.commandLine.appendSwitch('ppapi-flash-path', '/path/to/libpepflashplayer.so'); + +// Especifica a versão do flash, por exemplo, v17.0.0.169 +app.commandLine.appendSwitch('ppapi-flash-version', '17.0.0.169'); + +app.on('ready', function() { + mainWindow = new BrowserWindow({ + 'width': 800, + 'height': 600, + 'web-preferences': { + 'plugins': true + } + }); + mainWindow.loadUrl('file://' + __dirname + '/index.html'); + // Algo mais +}); +``` + +## Ative o plugin Flash na tag `` + +Adicione o atributo `plugins` na tag ``. + +```html + +``` diff --git a/docs-translations/zh-CN/README.md b/docs-translations/zh-CN/README.md index 124d030c46e..6085cbfb2c6 100644 --- a/docs-translations/zh-CN/README.md +++ b/docs-translations/zh-CN/README.md @@ -1,5 +1,6 @@ ## 向导 +* [支持平台](tutorial/supported-platforms.md) * [应用部署](tutorial/application-distribution.md) * [应用打包](tutorial/application-packaging.md) * [使用原生模块](tutorial/using-native-node-modules.md) diff --git a/docs-translations/zh-CN/api/ipc-main-process.md b/docs-translations/zh-CN/api/ipc-main-process.md new file mode 100644 index 00000000000..75d5785b2e5 --- /dev/null +++ b/docs-translations/zh-CN/api/ipc-main-process.md @@ -0,0 +1,68 @@ +# ipc (主进程) + +在主进程使用`ipc`模块时,`ipc`负责捕获从渲染进程(网页)发送的同步或者是异步消息. + +## 发送消息 + +主进程也可以向渲染进程发送信息,具体可以看[WebContents.send](web-contents.md#webcontentssendchannel-args). + +- 当发送消息的时候,事件名字为`channel`. +- 回复一个同步消息的时候,你需要使用`event.returnValue` +- 回复一个异步消息的时候,使用`event.sender.send(...)` + +下面是一个主进程和渲染进程的通信例子. + +```javascript +// 在主进程中. +var ipc = require('ipc'); +ipc.on('asynchronous-message', function(event, arg) { + console.log(arg); // 打印 "ping" + event.sender.send('asynchronous-reply', 'pong'); +}); + +ipc.on('synchronous-message', function(event, arg) { + console.log(arg); // 打印 "ping" + event.returnValue = 'pong'; +}); +``` + +```javascript +// 在渲染进程(网页). +var ipc = require('ipc'); +console.log(ipc.sendSync('synchronous-message', 'ping')); // 打印 "pong" + +ipc.on('asynchronous-reply', function(arg) { + console.log(arg); // 打印 "pong" +}); +ipc.send('asynchronous-message', 'ping'); +``` + +## 监听消息 + +`ipc`模块有下列几种方法来监听事件. + +### `ipc.on(channel, callback)` + +* `channel` - 事件名称. +* `callback` - 回调函数. + +当事件发生的时候,会传入`callback` `event`和`arg`参数. + +## IPC 事件 + +传入`callback`的`event`对象含有下列方法. + +### `Event.returnValue` + +在同步消息中,设置这个值将会被返回. + +### `Event.sender` + +返回一个可以发送消息的`WebContents`. + +### `Event.sender.send(channel[.arg1][,arg2][,...])` + +* `channel` - 事件名称. +* `arg` (选用) + +这个可以发送一个可带参数的异步消息回渲染进程. diff --git a/docs-translations/zh-CN/tutorial/application-distribution.md b/docs-translations/zh-CN/tutorial/application-distribution.md new file mode 100644 index 00000000000..c1fddce15ea --- /dev/null +++ b/docs-translations/zh-CN/tutorial/application-distribution.md @@ -0,0 +1,109 @@ +# 应用部署 + +为了使用Electron部署你的应用程序,你存放应用程序的文件夹需要叫做 `app` 并且需要放在 Electron 的资源文件夹下(在 OS X 中是指 `Electron.app/Contents/Resources/`,在 Linux 和 Windows 中是指 `resources/`) +就像这样: + +在 OS X 中: + +```text +electron/Electron.app/Contents/Resources/app/ +├── package.json +├── main.js +└── index.html +``` + +在 Windows 和 Linux 中: + +```text +electron/resources/app +├── package.json +├── main.js +└── index.html +``` + +然后运行 `Electron.app` (或者 Linux 中的 `electron`,Windows 中的 `electron.exe`), +接着 Electron 就会以你的应用程序的方式启动。`electron` 文件夹将被部署并可以分发给最终的使用者。 + +## 将你的应用程序打包成一个文件 + +除了通过拷贝所有的资源文件来分发你的应用程序之外,你可以可以通过打包你的应用程序为一个 [asar](https://github.com/atom/asar) 库文件以避免暴露你的源代码。 + +为了使用一个 `asar` 库文件代替 `app` 文件夹,你需要修改这个库文件的名字为 `app.asar` , +然后将其放到 Electron 的资源文件夹下,然后 Electron 就会试图读取这个库文件并从中启动。 +如下所示: + +在 OS X 中: + +```text +electron/Electron.app/Contents/Resources/ +└── app.asar +``` + +在 Windows 和 Linux 中: + +```text +electron/resources/ +└── app.asar +``` + +更多的细节请见 [Application packaging](application-packaging.md). + +## 更换名称与下载二进制文件 + +在使用 Electron 打包你的应用程序之后,你可能需要在分发给用户之前修改打包的名字。 + +### Windows + +你可以将 `electron.exe` 改成任意你喜欢的名字,然后可以使用像 +[rcedit](https://github.com/atom/rcedit) 或者[ResEdit](http://www.resedit.net) +编辑它的icon和其他信息。 + +### OS X + +你可以将 `Electron.app` 改成任意你喜欢的名字,然后你也需要修改这些文件中的 +`CFBundleDisplayName`, `CFBundleIdentifier` 以及 `CFBundleName` 字段。 +这些文件如下: + +* `Electron.app/Contents/Info.plist` +* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist` + +你也可以重命名帮助应用程序以避免在应用程序监视器中显示 `Electron Helper`, +但是请确保你已经修改了帮助应用的可执行文件的名字。 + +一个改过名字的应用程序的构造可能是这样的: + +``` +MyApp.app/Contents +├── Info.plist +├── MacOS/ +│   └── MyApp +└── Frameworks/ + ├── MyApp Helper EH.app + | ├── Info.plist + | └── MacOS/ + |    └── MyApp Helper EH + ├── MyApp Helper NP.app + | ├── Info.plist + | └── MacOS/ + |    └── MyApp Helper NP + └── MyApp Helper.app + ├── Info.plist + └── MacOS/ +    └── MyApp Helper +``` + +### Linux + +你可以将 `electron` 改成任意你喜欢的名字。 + +## 通过重编译源代码来更换名称 + +通过修改产品名称并重编译源代码来更换 Electron 的名称也是可行的。 +你需要修改 `atom.gyp` 文件并彻底重编译一次。 + +### grunt打包脚本 + +手动的检查 Electron 代码并重编译是很复杂晦涩的,因此有一个 Grunt任务可以自动自动的处理 +这些内容 [grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell). + +这个任务会自动的处理编辑 `.gyp` 文件,从源代码进行编译,然后重编译你的应用程序的本地 Node 模块以匹配这个新的可执行文件的名称。 diff --git a/docs-translations/zh-CN/tutorial/debugging-main-process.md b/docs-translations/zh-CN/tutorial/debugging-main-process.md new file mode 100644 index 00000000000..48f3579394e --- /dev/null +++ b/docs-translations/zh-CN/tutorial/debugging-main-process.md @@ -0,0 +1,48 @@ +# 主进程调试 + +浏览器窗口的开发工具仅能调试渲染器的进程脚本(比如web 页面)。为了提供一个可以调试主进程 +的方法,Electron 提供了 `--debug` 和 `--debug-brk` 开关。 + +## 命令行开关 + +使用如下的命令行开关来调试 Electron 的主进程: + +### `--debug=[port]` + +当这个开关用于 Electron 时,它将会监听 V8 引擎中有关 `port` 的调试器协议信息。 +默认的 `port` 是 `5858`。 + +### `--debug-brk=[port]` + +就像 `--debug` 一样,但是会在第一行暂停脚本运行。 + +## 使用 node-inspector 来调试 + +__备注:__ Electron 使用 node v0.11.13 版本,目前对 node-inspector支持的不是特别好, +如果你通过 node-inspector 的 console 来检查 `process` 对象,主进程就会崩溃。 + +### 1. 开始 [node-inspector][node-inspector] 服务 + +```bash +$ node-inspector +``` + +### 2. 打开 Electron 的调试模式 + +你也可以用调试参数来运行 Electron : + +```bash +$ electron --debug=5858 your/app +``` + +或者,在第一行暂停你的脚本: + +```bash +$ electron --debug-brk=5858 your/app +``` + +### 3. 加载调试器界面 + +在 Chrome 中打开 http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 + +[node-inspector]: https://github.com/node-inspector/node-inspector diff --git a/docs-translations/zh-CN/tutorial/supported-platforms.md b/docs-translations/zh-CN/tutorial/supported-platforms.md new file mode 100644 index 00000000000..a819e3a0817 --- /dev/null +++ b/docs-translations/zh-CN/tutorial/supported-platforms.md @@ -0,0 +1,27 @@ +# 支持的平台 + +以下的平台是 Electron 目前支持的: + +### OS X + +对于 OS X 系统仅有64位的二进制文档,支持的最低版本是 OS X 10.8。 + +### Windows + +仅支持 Windows 7 及其以后的版本,之前的版本中是不能工作的。 + +对于 Windows 提供 `x86` 和 `amd64` (x64) 版本的二进制文件。需要注意的是 +`ARM` 版本的 Windows 目前尚不支持. + +### Linux + +预编译的 `ia32`(`i686`) 和 `x64`(`amd64`) 版本 Electron 二进制文件都是在 +Ubuntu 12.04 下编译的,`arm` 版的二进制文件是在 ARM v7(硬浮点 ABI 与 +Debian Wheezy 版本的 NEON)下完成的。 + +预编译二进制文件是否能够运行,取决于其中是否包括了编译平台链接的库,所以只有 Ubuntu 12.04 +可以保证正常工作,但是以下的平台也被正事可以运行 Electron的预编译版本: + +* Ubuntu 12.04 及更新 +* Fedora 21 +* Debian 8 diff --git a/docs/README.md b/docs/README.md index eb6e9d6e36f..a0d91a5459c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,6 +2,7 @@ * [Supported Platforms](tutorial/supported-platforms.md) * [Application Distribution](tutorial/application-distribution.md) +* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md) * [Application Packaging](tutorial/application-packaging.md) * [Using Native Node Modules](tutorial/using-native-node-modules.md) * [Debugging Main Process](tutorial/debugging-main-process.md) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 1a2870f205a..fd25f8cdc98 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -111,7 +111,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `plugins` Boolean - Whether plugins should be enabled. * `experimental-features` Boolean * `experimental-canvas-features` Boolean - * `subpixel-font-scaling` Boolean * `overlay-scrollbars` Boolean * `overlay-fullscreen-video` Boolean * `shared-worker` Boolean diff --git a/docs/api/process.md b/docs/api/process.md index a2157886bc6..22fe452b0ca 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -8,6 +8,8 @@ upstream node: * `process.versions['electron']` String - Version of Electron. * `process.versions['chrome']` String - Version of Chromium. * `process.resourcesPath` String - Path to JavaScript source code. +* `process.mas` Boolean - For Mac App Store build, this value is `true`, for + other builds it is `undefined`. ## Events @@ -37,7 +39,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/web-contents.md b/docs/api/web-contents.md index 08670bad9df..006d7e3934b 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -631,3 +631,26 @@ 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. + +### `webContents.savePage(fullPath, saveType, callback)` + +* `fullPath` String - The full file path. +* `saveType` String - Specify the save type. + * `HTMLOnly` - Save only the HTML of the page. + * `HTMLComplete` - Save complete-html page. + * `MHTML` - Save complete-html page as MHTML. +* `callback` Function - `function(error) {}`. + * `error` Error + +Returns true if the process of saving page has been initiated successfully. + +```javascript +win.loadUrl('https://github.com'); + +win.webContents.on('did-finish-load', function() { + win.webContents.savePage('/tmp/test.html', 'HTMLComplete', function(error) { + if (!error) + console.log("Save page successfully"); + }); +}); +``` diff --git a/docs/tutorial/application-distribution.md b/docs/tutorial/application-distribution.md index 4f1f5202a00..d65bc08ca87 100644 --- a/docs/tutorial/application-distribution.md +++ b/docs/tutorial/application-distribution.md @@ -35,7 +35,7 @@ exposing your app's source code to users. To use an `asar` archive to replace the `app` folder, you need to rename the archive to `app.asar`, and put it under Electron's resources directory like -below, and Electron will then try read the archive and start from it. +below, and Electron will then try to read the archive and start from it. On OS X: diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index 3132edffcc2..78067f3d8a1 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -223,7 +223,7 @@ window.setProgressBar(0.5); ## Represented File of Window (OS X) On OS X a window can set its represented file, so the file's icon can show in -the title bar and when users Command-Click or Control-Click on the tile a path +the title bar and when users Command-Click or Control-Click on the title a path popup will show. You can also set the edited state of a window so that the file icon can indicate diff --git a/docs/tutorial/devtools-extension.md b/docs/tutorial/devtools-extension.md index e9466f14b64..20ba7031d8a 100644 --- a/docs/tutorial/devtools-extension.md +++ b/docs/tutorial/devtools-extension.md @@ -16,11 +16,13 @@ $ cd /some-directory $ git clone --recursive https://github.com/facebook/react-devtools.git ``` +Follow the instructions in [`react-devtools/shells/chrome/Readme.md`](https://github.com/facebook/react-devtools/blob/master/shells/chrome/Readme.md) to build the extension. + Then you can load the extension in Electron by opening DevTools in any window, and running the following code in the DevTools console: ```javascript -require('remote').require('browser-window').addDevToolsExtension('/some-directory/react-devtools'); +require('remote').require('browser-window').addDevToolsExtension('/some-directory/react-devtools/shells/chrome'); ``` To unload the extension, you can call the `BrowserWindow.removeDevToolsExtension` diff --git a/docs/tutorial/mac-app-store-submission-guide.md b/docs/tutorial/mac-app-store-submission-guide.md new file mode 100644 index 00000000000..ccd0ead8584 --- /dev/null +++ b/docs/tutorial/mac-app-store-submission-guide.md @@ -0,0 +1,115 @@ +# Mac App Store Submission Guide + +Since v0.34.0, Electron allows submitting packaged apps to Mac App Store (MAS), +this guide provides information on how to submit your app, and the limitations +of the MAS build. + +## How to submit your app + +Following steps introduces a simple way to submit your app to Mac App Store, but +it doesn't make sure your app gets approved by Apple, you still have to read +apple's [Submitting Your App][submitting-your-app] guide on how to meet Mac +App Store's requirements. + +### Get certificate + +To submit your app to Mac App Store, you have to get a certificate from Apple +first, you can follow [existing guides][nwjs-guide] on web. + +### Sign your app + +After getting the certificate, you can package your app by following +[Application Distribution](application-distribution.md), and then sign your app. +The step is basically the same with other programs, the key is to sign every +dependency of Electron one by one. + +First you need to prepare two entitlements files. + +`child.plist`: + +```xml + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + +``` + +`parent.plist`: + +```xml + + + + + com.apple.security.app-sandbox + + + +``` + +And then sign your app with following script: + +```bash +#!/bin/bash + +# Name of your app. +APP="YourApp" +# The path of you app to sign. +APP_PATH="/path/to/YouApp.app" +# The path to the location you want to put the signed package. +RESULT_PATH="~/Desktop/$APP.pkg" +# The name of certificates you requested. +APP_KEY="3rd Party Mac Developer Application: Company Name (APPIDENTITY)" +INSTALLER_KEY="3rd Party Mac Developer Installer: Company Name (APPIDENTITY)" + +FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks" + +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Libraries/libnode.dylib" +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Electron Framework" +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/" +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/" +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/" +codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/" +codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH" +productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$APP_PATH" +``` + +If you are new to app sandboxing of OS X, you should also go through Apple's +[Enabling App Sandbox][enable-app-sandbox] to have a basic idea, and add keys +for the permissions needed by your app to the entitlements files. + +### Upload your app and submit for review + +After signing your app you can use Application Loader to upload it to iTunes +Connect for processing, make sure you have [created a record][create-record] +before uploading. Then you can [submit your app for review][submit-for-review]. + +## Limitations of MAS build + +In order to satisfy requirements for app sandboxing, following modules have been +disabled in MAS build: + +* `crash-reporter` +* `auto-updater` + +and following behaviors have been changed: + +* Video capture may not work for some machines. +* Certain accessibility features may not work. +* Apps will not be aware of DNS changes. + +Also due to the usage of app sandboxing, the resources can be accessed by the +app is strictly limited, you can read [App Sandboxing][app-sandboxing] for more. + +[submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html +[nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps +[enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html +[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html +[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html +[app-sandboxing]: https://developer.apple.com/app-sandboxing/ diff --git a/filenames.gypi b/filenames.gypi index 6418ba28721..aadc1a092f6 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -108,6 +108,8 @@ 'atom/browser/api/trackable_object.h', 'atom/browser/api/frame_subscriber.cc', 'atom/browser/api/frame_subscriber.h', + 'atom/browser/api/save_page_handler.cc', + 'atom/browser/api/save_page_handler.h', 'atom/browser/auto_updater.cc', 'atom/browser/auto_updater.h', 'atom/browser/auto_updater_delegate.h', diff --git a/script/bump-version.py b/script/bump-version.py index f910ae3ef95..3ee0b23df3b 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -106,10 +106,11 @@ def update_info_plist(version): line = lines[i] if 'CFBundleVersion' in line: lines[i + 1] = ' {0}\n'.format(version) + if 'CFBundleShortVersionString' in line: + lines[i + 1] = ' {0}\n'.format(version) - with open(info_plist, 'w') as f: - f.write(''.join(lines)) - return + with open(info_plist, 'w') as f: + f.write(''.join(lines)) def tag_version(version): diff --git a/script/create-dist.py b/script/create-dist.py index ca7e2164287..5f47b2d358f 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -8,7 +8,8 @@ import sys import stat from lib.config import LIBCHROMIUMCONTENT_COMMIT, BASE_URL, PLATFORM, \ - get_target_arch, get_chromedriver_version + get_target_arch, get_chromedriver_version, \ + get_platform_key from lib.util import scoped_cwd, rm_rf, get_atom_shell_version, make_zip, \ execute, atom_gyp @@ -170,7 +171,8 @@ def create_symbols(): def create_dist_zip(): dist_name = '{0}-{1}-{2}-{3}.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, - PLATFORM, get_target_arch()) + get_platform_key(), + get_target_arch()) zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name) with scoped_cwd(DIST_DIR): @@ -182,7 +184,7 @@ def create_dist_zip(): def create_chrome_binary_zip(binary, version): - dist_name = '{0}-{1}-{2}-{3}.zip'.format(binary, version, PLATFORM, + dist_name = '{0}-{1}-{2}-{3}.zip'.format(binary, version, get_platform_key(), get_target_arch()) zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name) @@ -198,7 +200,7 @@ def create_chrome_binary_zip(binary, version): def create_symbols_zip(): dist_name = '{0}-{1}-{2}-{3}-symbols.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, - PLATFORM, + get_platform_key(), get_target_arch()) zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name) diff --git a/script/lib/config.py b/script/lib/config.py index 68f216785d6..83468f81994 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -7,8 +7,8 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ - 'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '04523758cda2a96d2454f9056fb1fb9a1c1f95f1' + 'http://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' +LIBCHROMIUMCONTENT_COMMIT = '78e54bc39a04b758ed5167cd980cc4d9951bd629' PLATFORM = { 'cygwin': 'win32', @@ -20,6 +20,13 @@ PLATFORM = { verbose_mode = False +def get_platform_key(): + if os.environ.has_key('MAS_BUILD'): + return 'mas' + else: + return PLATFORM + + def get_target_arch(): try: target_arch_path = os.path.join(__file__, '..', '..', '..', 'vendor', diff --git a/script/update.py b/script/update.py index abb3756ca36..e91e8401cbf 100755 --- a/script/update.py +++ b/script/update.py @@ -55,11 +55,17 @@ def run_gyp(target_arch, component): # Avoid using the old gyp lib in system. env['PYTHONPATH'] = os.path.pathsep.join([gyp_pylib, env.get('PYTHONPATH', '')]) + # Whether to build for Mac App Store. + if os.environ.has_key('MAS_BUILD'): + mas_build = 1 + else: + mas_build = 0 defines = [ '-Dlibchromiumcontent_component={0}'.format(component), '-Dtarget_arch={0}'.format(target_arch), '-Dhost_arch={0}'.format(get_host_arch()), '-Dlibrary=static_library', + '-Dmas_build={0}'.format(mas_build), ] return subprocess.call([python, gyp, '-f', 'ninja', '--depth', '.', 'atom.gyp', '-Icommon.gypi'] + defines, env=env) diff --git a/script/upload.py b/script/upload.py index 6fc421e6b7a..318bbb594a1 100755 --- a/script/upload.py +++ b/script/upload.py @@ -7,7 +7,8 @@ import subprocess import sys import tempfile -from lib.config import PLATFORM, get_target_arch, get_chromedriver_version +from lib.config import PLATFORM, get_target_arch, get_chromedriver_version, \ + get_platform_key from lib.util import atom_gyp, execute, get_atom_shell_version, parse_version, \ scoped_cwd from lib.github import GitHub @@ -24,14 +25,14 @@ OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R') DIST_DIR = os.path.join(SOURCE_ROOT, 'dist') DIST_NAME = '{0}-{1}-{2}-{3}.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, - PLATFORM, + get_platform_key(), get_target_arch()) SYMBOLS_NAME = '{0}-{1}-{2}-{3}-symbols.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, - PLATFORM, + get_platform_key(), get_target_arch()) MKSNAPSHOT_NAME = 'mksnapshot-{0}-{1}-{2}.zip'.format(ATOM_SHELL_VERSION, - PLATFORM, + get_platform_key(), get_target_arch()) @@ -85,7 +86,7 @@ def main(): # Upload chromedriver and mksnapshot for minor version update. if parse_version(args.version)[2] == '0': chromedriver = 'chromedriver-{0}-{1}-{2}.zip'.format( - get_chromedriver_version(), PLATFORM, get_target_arch()) + get_chromedriver_version(), get_platform_key(), get_target_arch()) upload_atom_shell(github, release, os.path.join(DIST_DIR, chromedriver)) upload_atom_shell(github, release, os.path.join(DIST_DIR, MKSNAPSHOT_NAME)) diff --git a/spec/api-browser-window-spec.coffee b/spec/api-browser-window-spec.coffee index 2e4a66b92b9..0f9b376784f 100644 --- a/spec/api-browser-window-spec.coffee +++ b/spec/api-browser-window-spec.coffee @@ -301,3 +301,15 @@ describe 'browser-window module', -> assert.notEqual data.length, 0 w.webContents.endFrameSubscription() done() + + describe 'save page', -> + savePagePath = path.join fixtures, 'save_page.html' + it 'should save page', (done) -> + w.webContents.on 'did-finish-load', -> + w.webContents.savePage savePagePath, 'HTMLComplete', (error) -> + assert.equal error, null + assert fs.existsSync savePagePath + fs.unlinkSync savePagePath + done() + + w.loadUrl "file://#{fixtures}/api/blank.html" diff --git a/spec/api-crash-reporter-spec.coffee b/spec/api-crash-reporter-spec.coffee index 60b630bc2ff..9aedb0c8ea1 100644 --- a/spec/api-crash-reporter-spec.coffee +++ b/spec/api-crash-reporter-spec.coffee @@ -15,8 +15,8 @@ describe 'crash-reporter module', -> beforeEach -> w = new BrowserWindow(show: false) afterEach -> w.destroy() - # It is not working on 64bit Windows. - return if process.platform is 'win32' and process.arch is 'x64' + # It is not working for mas build. + return if process.mas # The crash-reporter test is not reliable on CI machine. isCI = remote.process.argv[2] == '--ci' diff --git a/spec/asar-spec.coffee b/spec/asar-spec.coffee index 1e6ee691036..75f0cec63ad 100644 --- a/spec/asar-spec.coffee +++ b/spec/asar-spec.coffee @@ -450,10 +450,10 @@ describe 'asar package', -> w = new BrowserWindow(show: false, width: 400, height: 400) p = path.resolve fixtures, 'asar', 'web.asar', 'index.html' u = url.format protocol: 'file', slashed: true, pathname: p - w.loadUrl u ipc.once 'dirname', (event, dirname) -> assert.equal dirname, path.dirname(p) done() + w.loadUrl u it 'loads script tag in html', (done) -> after -> diff --git a/vendor/brightray b/vendor/brightray index 375436a777a..fe2dd437c9e 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 375436a777a793f4815f38c13a5226fcd82de567 +Subproject commit fe2dd437c9ef7877bf9d454db8ae401965cd7cb0