From 92606579d3af5b23b79161d889a795ac13d8fb77 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 17:32:25 -0700 Subject: [PATCH 01/18] Generalize this mate converter for reuse. --- atom/common/api/atom_api_crash_reporter.cc | 19 +--------- .../string_map_converter.cc | 37 +++++++++++++++++++ .../string_map_converter.h | 28 ++++++++++++++ filenames.gypi | 2 + 4 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 atom/common/native_mate_converters/string_map_converter.cc create mode 100644 atom/common/native_mate_converters/string_map_converter.h diff --git a/atom/common/api/atom_api_crash_reporter.cc b/atom/common/api/atom_api_crash_reporter.cc index e1932ad7f5f..5db1461f88c 100644 --- a/atom/common/api/atom_api_crash_reporter.cc +++ b/atom/common/api/atom_api_crash_reporter.cc @@ -6,6 +6,7 @@ #include #include "atom/common/crash_reporter/crash_reporter.h" +#include "atom/common/native_mate_converters/string_map_converter.h" #include "base/bind.h" #include "native_mate/dictionary.h" @@ -15,24 +16,6 @@ using crash_reporter::CrashReporter; namespace mate { -template<> -struct Converter > { - static bool FromV8(v8::Isolate* isolate, - v8::Local val, - std::map* out) { - if (!val->IsObject()) - return false; - - v8::Local dict = val->ToObject(); - v8::Local keys = dict->GetOwnPropertyNames(); - for (uint32_t i = 0; i < keys->Length(); ++i) { - v8::Local key = keys->Get(i); - (*out)[V8ToString(key)] = V8ToString(dict->Get(key)); - } - return true; - } -}; - template<> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, diff --git a/atom/common/native_mate_converters/string_map_converter.cc b/atom/common/native_mate_converters/string_map_converter.cc new file mode 100644 index 00000000000..dce3c387f0e --- /dev/null +++ b/atom/common/native_mate_converters/string_map_converter.cc @@ -0,0 +1,37 @@ +// 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/common/native_mate_converters/string_map_converter.h" + +namespace mate { + +bool Converter>::FromV8(v8::Isolate* isolate, + v8::Local val, + std::map* out) { + + if (!val->IsObject()) + return false; + + v8::Local dict = val->ToObject(); + v8::Local keys = dict->GetOwnPropertyNames(); + for (uint32_t i = 0; i < keys->Length(); ++i) { + v8::Local key = keys->Get(i); + (*out)[V8ToString(key)] = V8ToString(dict->Get(key)); + } + return true; +} + +v8::Local Converter>::ToV8(v8::Isolate* isolate, + const std::map& in) { + + mate::Dictionary dict(isolate, v8::Object::New(isolate)); + + for (auto const &pair : in) { + dict.Set(pair.first, pair.second); + } + + return dict.GetHandle(); +} + +} // namespace mate \ No newline at end of file diff --git a/atom/common/native_mate_converters/string_map_converter.h b/atom/common/native_mate_converters/string_map_converter.h new file mode 100644 index 00000000000..bdab0be2b8a --- /dev/null +++ b/atom/common/native_mate_converters/string_map_converter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_ +#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_ + +#include +#include + +#include "native_mate/converter.h" +#include "native_mate/dictionary.h" + +namespace mate { + +template<> +struct Converter> { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + std::map* out); + + static v8::Local ToV8(v8::Isolate* isolate, + const std::map& in); +}; + +} // namespace mate + +#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_ \ No newline at end of file diff --git a/filenames.gypi b/filenames.gypi index 1c213949756..783b5cb33d0 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -361,6 +361,8 @@ 'atom/common/native_mate_converters/image_converter.h', 'atom/common/native_mate_converters/net_converter.cc', 'atom/common/native_mate_converters/net_converter.h', + 'atom/common/native_mate_converters/string_map_converter.cc', + 'atom/common/native_mate_converters/string_map_converter.h', 'atom/common/native_mate_converters/string16_converter.h', 'atom/common/native_mate_converters/v8_value_converter.cc', 'atom/common/native_mate_converters/v8_value_converter.h', From 6df4bb176dce4903e105690deb54627ad8c4d1d0 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 17:35:07 -0700 Subject: [PATCH 02/18] Implement app.setUserActivity(type, userInfo). --- atom/browser/api/atom_api_app.cc | 1 + atom/browser/browser.h | 4 ++++ atom/browser/browser_mac.mm | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 304e53a5554..8e7d8c3034a 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -460,6 +460,7 @@ void App::BuildPrototype( #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) + .SetMethod("setUserActivity", base::Bind(&Browser::SetUserActivity, browser)) #endif #if defined(OS_WIN) .SetMethod("setUserTasks", diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 8329b14bc2c..b0dd829ff4d 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -7,6 +7,7 @@ #include #include +#include #include "base/macros.h" #include "base/compiler_specific.h" @@ -92,6 +93,9 @@ class Browser : public WindowListObserver { // Show the application. void Show(); + // Creates an activity and sets it as the one currently in use. + void SetUserActivity(const std::string& type, const std::map& user_info); + // Bounce the dock icon. enum BounceType { BOUNCE_CRITICAL = 0, diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index c10369a2e7a..627ed65dccd 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -87,6 +87,23 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } +void Browser::SetUserActivity(const std::string& type, const std::map& user_info) { + NSString* type_ns = [NSString stringWithUTF8String:type.c_str()]; + NSUserActivity *user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns]; + + NSMutableArray* user_info_args = [[NSMutableArray alloc] init]; + for (auto const &pair : user_info) { + NSString* key_ns = [NSString stringWithUTF8String:pair.first.c_str()]; + NSString* value_ns = [NSString stringWithUTF8String:pair.second.c_str()]; + + [user_info_args addObject:key_ns]; + [user_info_args addObject:value_ns]; + } + + user_activity.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:user_info_args, nil]; + [user_activity becomeCurrent]; +} + std::string Browser::GetExecutableFileVersion() const { return brightray::GetApplicationVersion(); } From c20acb0361a4915e800d3e3281046f2199b383f9 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 17:36:04 -0700 Subject: [PATCH 03/18] Implement a "continue-activity" event on app for resuming from hand-off. --- atom/browser/api/atom_api_app.cc | 5 +++++ atom/browser/api/atom_api_app.h | 2 ++ atom/browser/browser.cc | 11 +++++++++++ atom/browser/browser.h | 3 +++ atom/browser/browser_observer.h | 6 ++++++ 5 files changed, 27 insertions(+) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 8e7d8c3034a..fb258e2f483 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -20,6 +20,7 @@ #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/net_converter.h" #include "atom/common/native_mate_converters/value_converter.h" +#include "atom/common/native_mate_converters/string_map_converter.h" #include "atom/common/node_includes.h" #include "atom/common/options_switches.h" #include "base/command_line.h" @@ -249,6 +250,10 @@ void App::OnFinishLaunching() { Emit("ready"); } +void App::OnContinueUserActivity(bool* handled, const std::string& type, const std::map& user_info) { + *handled = Emit("continue-activity", type, user_info); +} + void App::OnLogin(LoginHandler* login_handler) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index c99d5df7793..882f05376ca 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -6,6 +6,7 @@ #define ATOM_BROWSER_API_ATOM_API_APP_H_ #include +#include #include "atom/browser/api/event_emitter.h" #include "atom/browser/atom_browser_client.h" @@ -71,6 +72,7 @@ class App : public AtomBrowserClient::Delegate, void OnWillFinishLaunching() override; void OnFinishLaunching() override; void OnLogin(LoginHandler* login_handler) override; + void OnContinueUserActivity(bool* handled, const std::string& type, const std::map& user_info) override; // content::ContentBrowserClient: void AllowCertificateError( diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 093209ef7c4..94f3948c4a6 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -137,6 +137,17 @@ void Browser::Activate(bool has_visible_windows) { OnActivate(has_visible_windows)); } +#if defined(OS_MACOSX) +bool Browser::ContinueUserActivity(const std::string& type, const std::map& user_info) { + bool handled = false; + FOR_EACH_OBSERVER(BrowserObserver, + observers_, + OnContinueUserActivity(&handled, type, user_info)); + + return handled; +} +#endif + void Browser::WillFinishLaunching() { FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillFinishLaunching()); } diff --git a/atom/browser/browser.h b/atom/browser/browser.h index b0dd829ff4d..4fb140e79e8 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -96,6 +96,9 @@ class Browser : public WindowListObserver { // Creates an activity and sets it as the one currently in use. void SetUserActivity(const std::string& type, const std::map& user_info); + // Resumes an activity via hand-off. + bool ContinueUserActivity(const std::string& type, const std::map& user_info); + // Bounce the dock icon. enum BounceType { BOUNCE_CRITICAL = 0, diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index f6d76bc13fb..11346f36875 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -6,6 +6,7 @@ #define ATOM_BROWSER_BROWSER_OBSERVER_H_ #include +#include namespace atom { @@ -45,6 +46,11 @@ class BrowserObserver { // The browser requests HTTP login. virtual void OnLogin(LoginHandler* login_handler) {} + // The browser wants to resume a user activity via handoff. (OS X only) + virtual void OnContinueUserActivity(bool* handled, + const std::string& type, + const std::map& user_info) {} + protected: virtual ~BrowserObserver() {} }; From 3a9a1d35d7356c45bb292d3c176520d8c25119d2 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 17:37:01 -0700 Subject: [PATCH 04/18] Add the AppDelegate override for restoring from hand-off, and fire the app event. --- atom/browser/mac/atom_application_delegate.mm | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index 7662162ab61..33aa47ce240 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -59,4 +59,24 @@ return flag; } +- (BOOL)application:(NSApplication *)sender +continueUserActivity:(NSUserActivity *)userActivity + restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { + std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); + + std::map user_info; + + NSArray* keys = [userActivity.userInfo allKeys]; + for (NSString* key in keys) + { + NSString* value = [userActivity.userInfo objectForKey:key]; + std::string key_str(base::SysNSStringToUTF8(key)); + std::string value_str(base::SysNSStringToUTF8(value)); + user_info[key_str] = value_str; + } + + atom::Browser* browser = atom::Browser::Get(); + return browser->ContinueUserActivity(activity_type, user_info) ? YES : NO; +} + @end From 88805ec7e27884e05538e7043c883bef594d4b3a Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 22:05:36 -0700 Subject: [PATCH 05/18] Make the Linter happy. --- atom/browser/api/atom_api_app.cc | 7 +++++-- atom/browser/api/atom_api_app.h | 4 +++- atom/browser/browser.cc | 10 ++++++---- atom/browser/browser.h | 8 +++++--- atom/browser/browser_observer.h | 4 ++-- .../native_mate_converters/string_map_converter.cc | 9 ++++----- .../native_mate_converters/string_map_converter.h | 4 ++-- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index fb258e2f483..0e2a6e55626 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -250,7 +250,9 @@ void App::OnFinishLaunching() { Emit("ready"); } -void App::OnContinueUserActivity(bool* handled, const std::string& type, const std::map& user_info) { +void App::OnContinueUserActivity(bool* handled, + const std::string& type, + const std::map& user_info) { *handled = Emit("continue-activity", type, user_info); } @@ -465,7 +467,8 @@ void App::BuildPrototype( #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) - .SetMethod("setUserActivity", base::Bind(&Browser::SetUserActivity, browser)) + .SetMethod("setUserActivity", + base::Bind(&Browser::SetUserActivity, browser)) #endif #if defined(OS_WIN) .SetMethod("setUserTasks", diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 882f05376ca..3a13322cc08 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -72,7 +72,9 @@ class App : public AtomBrowserClient::Delegate, void OnWillFinishLaunching() override; void OnFinishLaunching() override; void OnLogin(LoginHandler* login_handler) override; - void OnContinueUserActivity(bool* handled, const std::string& type, const std::map& user_info) override; + void OnContinueUserActivity(bool* handled, + const std::string& type, + const std::map& user_info) override; // content::ContentBrowserClient: void AllowCertificateError( diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 94f3948c4a6..15980190030 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -138,12 +138,14 @@ void Browser::Activate(bool has_visible_windows) { } #if defined(OS_MACOSX) -bool Browser::ContinueUserActivity(const std::string& type, const std::map& user_info) { +bool Browser::ContinueUserActivity(const std::string& type, + const std::map& user_info) { bool handled = false; - FOR_EACH_OBSERVER(BrowserObserver, - observers_, + FOR_EACH_OBSERVER(BrowserObserver, + observers_, OnContinueUserActivity(&handled, type, user_info)); - + return handled; } #endif diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 4fb140e79e8..14f94a061e4 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -94,10 +94,12 @@ class Browser : public WindowListObserver { void Show(); // Creates an activity and sets it as the one currently in use. - void SetUserActivity(const std::string& type, const std::map& user_info); - + void SetUserActivity(const std::string& type, + const std::map& user_info); + // Resumes an activity via hand-off. - bool ContinueUserActivity(const std::string& type, const std::map& user_info); + bool ContinueUserActivity(const std::string& type, + const std::map& user_info); // Bounce the dock icon. enum BounceType { diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index 11346f36875..977f1a8dc12 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -48,8 +48,8 @@ class BrowserObserver { // The browser wants to resume a user activity via handoff. (OS X only) virtual void OnContinueUserActivity(bool* handled, - const std::string& type, - const std::map& user_info) {} + const std::string& type, + const std::map& user_info) {} protected: virtual ~BrowserObserver() {} diff --git a/atom/common/native_mate_converters/string_map_converter.cc b/atom/common/native_mate_converters/string_map_converter.cc index dce3c387f0e..475b052ba65 100644 --- a/atom/common/native_mate_converters/string_map_converter.cc +++ b/atom/common/native_mate_converters/string_map_converter.cc @@ -6,10 +6,9 @@ namespace mate { -bool Converter>::FromV8(v8::Isolate* isolate, +bool Converter>::FromV8(v8::Isolate* isolate, v8::Local val, std::map* out) { - if (!val->IsObject()) return false; @@ -22,9 +21,9 @@ bool Converter>::FromV8(v8::Isolate* isolate, return true; } -v8::Local Converter>::ToV8(v8::Isolate* isolate, +v8::Local Converter>::ToV8( + v8::Isolate* isolate, const std::map& in) { - mate::Dictionary dict(isolate, v8::Object::New(isolate)); for (auto const &pair : in) { @@ -34,4 +33,4 @@ v8::Local Converter>::ToV8(v8::Iso return dict.GetHandle(); } -} // namespace mate \ No newline at end of file +} // namespace mate diff --git a/atom/common/native_mate_converters/string_map_converter.h b/atom/common/native_mate_converters/string_map_converter.h index bdab0be2b8a..54e4297714c 100644 --- a/atom/common/native_mate_converters/string_map_converter.h +++ b/atom/common/native_mate_converters/string_map_converter.h @@ -20,9 +20,9 @@ struct Converter> { std::map* out); static v8::Local ToV8(v8::Isolate* isolate, - const std::map& in); + const std::map& in); }; } // namespace mate -#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_ \ No newline at end of file +#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_ From 195940292d2ba649e9ea840119fb5d84bdba4ce8 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 22:08:19 -0700 Subject: [PATCH 06/18] This is preventDefault by convention. --- atom/browser/api/atom_api_app.cc | 4 ++-- atom/browser/api/atom_api_app.h | 2 +- atom/browser/browser.cc | 6 +++--- atom/browser/browser_observer.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 0e2a6e55626..ec6cdce417c 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -250,10 +250,10 @@ void App::OnFinishLaunching() { Emit("ready"); } -void App::OnContinueUserActivity(bool* handled, +void App::OnContinueUserActivity(bool* prevent_default, const std::string& type, const std::map& user_info) { - *handled = Emit("continue-activity", type, user_info); + *prevent_default = Emit("continue-activity", type, user_info); } void App::OnLogin(LoginHandler* login_handler) { diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 3a13322cc08..321230d711e 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -72,7 +72,7 @@ class App : public AtomBrowserClient::Delegate, void OnWillFinishLaunching() override; void OnFinishLaunching() override; void OnLogin(LoginHandler* login_handler) override; - void OnContinueUserActivity(bool* handled, + void OnContinueUserActivity(bool* prevent_default, const std::string& type, const std::map& user_info) override; diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 15980190030..ee2a225246b 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -141,12 +141,12 @@ void Browser::Activate(bool has_visible_windows) { bool Browser::ContinueUserActivity(const std::string& type, const std::map& user_info) { - bool handled = false; + bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, observers_, - OnContinueUserActivity(&handled, type, user_info)); + OnContinueUserActivity(&prevent_default, type, user_info)); - return handled; + return prevent_default; } #endif diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index 977f1a8dc12..4ad001370c0 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -47,7 +47,7 @@ class BrowserObserver { virtual void OnLogin(LoginHandler* login_handler) {} // The browser wants to resume a user activity via handoff. (OS X only) - virtual void OnContinueUserActivity(bool* handled, + virtual void OnContinueUserActivity(bool* prevent_default, const std::string& type, const std::map& user_info) {} From cea1b49db06255745ab1329b78170220825ba473 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Fri, 29 Apr 2016 22:25:09 -0700 Subject: [PATCH 07/18] Add some documentation in app.md. --- docs/api/app.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 1f682933d23..04f91b3a904 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -108,8 +108,20 @@ Returns: * `event` Event * `hasVisibleWindows` Boolean -Emitted when the application is activated, which usually happens when clicks on -the applications's dock icon. +Emitted when the application is activated, which usually happens when the user clicks on +the application's dock icon. + +### Event: 'continue-activity' _OS X_ + +Returns: + +* `event` Event +* `type` String - A string identifying the event. Maps to [`NSUserActivity.activityType`](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType). +* `userInfo` Object - Contains app-specific state stored by the activity on another device. + +Emitted during [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) when an activity from a different device wants to be +resumed. You should call `event.preventDefault()` if you want to handle this +event. ### Event: 'browser-window-blur' @@ -386,12 +398,12 @@ default protocol handler. ### `app.isDefaultProtocolClient(protocol)` _OS X_ _Windows_ -* `protocol` String - The name of your protocol, without `://`. +* `protocol` String - The name of your protocol, without `://`. This method checks if the current executable is the default handler for a protocol -(aka URI scheme). If so, it will return true. Otherwise, it will return false. +(aka URI scheme). If so, it will return true. Otherwise, it will return false. -**Note:** On OS X, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the OS X machine. +**Note:** On OS X, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the OS X machine. Please refer to [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. @@ -482,6 +494,16 @@ app.on('ready', function() { }); ``` +### `app.setUserActivity(type, userInfo)` _OS X_ + +* `type` String - Uniquely identifies the activity. It's recommended to use a +reverse-DNS string. +* `userInfo` Object - Contains app-specific state stored by the activity on +another device. + +Creates an `NSUserActivity` and sets it as the current activity. The activity +is eligible for [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) to another device afterward. + ### `app.setAppUserModelId(id)` _Windows_ * `id` String @@ -568,5 +590,5 @@ Sets the `image` associated with this dock icon. [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx [CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115 -[LSCopyDefaultHandlerForURLScheme]: +[LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme From 05493502ea48e3bd2713be11ce62858d5d65b15c Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Sat, 30 Apr 2016 11:17:29 -0700 Subject: [PATCH 08/18] Value first, key second. --- atom/browser/browser_mac.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 627ed65dccd..a12bd8c8809 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -69,7 +69,7 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { return false; NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()]; - + CFStringRef bundle = LSCopyDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns)); NSString* bundleId = static_cast( @@ -77,7 +77,7 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { if (!bundleId) return false; - // Ensure the comparison is case-insensitive + // Ensure the comparison is case-insensitive // as LS does not persist the case of the bundle id. NSComparisonResult result = [bundleId caseInsensitiveCompare:identifier]; @@ -93,11 +93,11 @@ void Browser::SetUserActivity(const std::string& type, const std::map Date: Sat, 30 Apr 2016 13:03:10 -0700 Subject: [PATCH 09/18] :memo: about the plist changes. --- docs/api/app.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api/app.md b/docs/api/app.md index 04f91b3a904..61e86b70f9a 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -123,6 +123,11 @@ Emitted during [handoff](https://developer.apple.com/library/ios/documentation/U resumed. You should call `event.preventDefault()` if you want to handle this event. +A user activity can be continued only in an app that has the same developer +Team ID as the activity's source app and that supports the activity's type. +Supported activity types are specified in the app's Info.plist under the +`NSUserActivityTypes` key. + ### Event: 'browser-window-blur' Returns: From 2295f3a8327d6df4031dcfcd8daebb771d0e3530 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Mon, 2 May 2016 14:45:59 -0700 Subject: [PATCH 10/18] Add some shady methods to get V8 objects or arrays from NSDictionary or NSArray. --- atom/browser/mac/atom_application_delegate.mm | 4 +- atom/browser/mac/mac_native_converter.h | 11 +++++ atom/browser/mac/mac_native_converter.mm | 46 +++++++++++++++++++ filenames.gypi | 2 + 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 atom/browser/mac/mac_native_converter.h create mode 100644 atom/browser/mac/mac_native_converter.mm diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index 33aa47ce240..f08cc84c466 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -63,9 +63,9 @@ continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); - + std::map user_info; - + NSArray* keys = [userActivity.userInfo allKeys]; for (NSString* key in keys) { diff --git a/atom/browser/mac/mac_native_converter.h b/atom/browser/mac/mac_native_converter.h new file mode 100644 index 00000000000..ac1a9a608d7 --- /dev/null +++ b/atom/browser/mac/mac_native_converter.h @@ -0,0 +1,11 @@ +#import + +#include "base/values.h" +#include "base/strings/sys_string_conversions.h" + +@interface MacNativeConverter : NSObject + +- (base::ListValue*)arrayToV8:(NSArray*)nsArray; +- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary; + +@end diff --git a/atom/browser/mac/mac_native_converter.mm b/atom/browser/mac/mac_native_converter.mm new file mode 100644 index 00000000000..63244036017 --- /dev/null +++ b/atom/browser/mac/mac_native_converter.mm @@ -0,0 +1,46 @@ +#import "atom/browser/mac/mac_native_converter.h" + +@implementation MacNativeConverter + +- (base::ListValue*)arrayToV8:(NSArray*)nsArray { + scoped_ptr list(new base::ListValue); + + for (id value in nsArray) { + if ([value isKindOfClass:[NSArray class]]) { + list->Append([self arrayToV8:value]); + } else if ([value isKindOfClass:[NSDictionary class]]) { + list->Append([self dictionaryToV8:value]); + } else if ([value isKindOfClass:[NSString class]]) { + list->AppendString(base::SysNSStringToUTF8(value)); + } else if ([value isKindOfClass:[NSNumber class]]) { + list->AppendInteger(((NSNumber* )value).intValue); + } + } + + return list.get(); +} + +- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary { + scoped_ptr dict(new base::DictionaryValue); + + NSEnumerator *it = [nsDictionary keyEnumerator]; + while (NSString *key = [it nextObject]) { + id value = [nsDictionary objectForKey:key]; + + std::string key_str(base::SysNSStringToUTF8(key)); + + if ([value isKindOfClass:[NSArray class]]) { + dict->Set(key_str, [self arrayToV8:value]); + } else if ([value isKindOfClass:[NSDictionary class]]) { + dict->Set(key_str, [self dictionaryToV8:value]); + } else if ([value isKindOfClass:[NSString class]]) { + dict->SetString(key_str, base::SysNSStringToUTF8(value)); + } else if ([value isKindOfClass:[NSNumber class]]) { + dict->SetInteger(key_str, ((NSNumber* )value).intValue); + } + } + + return dict.get(); +} + +@end diff --git a/filenames.gypi b/filenames.gypi index 783b5cb33d0..033d4959454 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -183,6 +183,8 @@ 'atom/browser/mac/atom_application.mm', 'atom/browser/mac/atom_application_delegate.h', 'atom/browser/mac/atom_application_delegate.mm', + 'atom/browser/mac/mac_native_converter.h', + 'atom/browser/mac/mac_native_converter.mm', 'atom/browser/native_window.cc', 'atom/browser/native_window.h', 'atom/browser/native_window_views_win.cc', From 90cc10944a64d9a4998a3d12a2559baa7705b3af Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Mon, 2 May 2016 16:18:58 -0700 Subject: [PATCH 11/18] Use a DictionaryValue everywhere instead of a string map. --- atom/browser/api/atom_api_app.cc | 2 +- atom/browser/api/atom_api_app.h | 2 +- atom/browser/browser.cc | 3 +-- atom/browser/browser.h | 5 +++-- atom/browser/browser_mac.mm | 15 ++++---------- atom/browser/browser_observer.h | 4 +++- atom/browser/mac/atom_application_delegate.mm | 17 +++++----------- atom/browser/mac/mac_native_converter.h | 7 +++++-- atom/browser/mac/mac_native_converter.mm | 20 +++++++++++++------ 9 files changed, 37 insertions(+), 38 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index ec6cdce417c..ee73e5b2151 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -252,7 +252,7 @@ void App::OnFinishLaunching() { void App::OnContinueUserActivity(bool* prevent_default, const std::string& type, - const std::map& user_info) { + const base::DictionaryValue& user_info) { *prevent_default = Emit("continue-activity", type, user_info); } diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 321230d711e..db510640fa6 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -74,7 +74,7 @@ class App : public AtomBrowserClient::Delegate, void OnLogin(LoginHandler* login_handler) override; void OnContinueUserActivity(bool* prevent_default, const std::string& type, - const std::map& user_info) override; + const base::DictionaryValue& user_info) override; // content::ContentBrowserClient: void AllowCertificateError( diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index ee2a225246b..62e0467e5d2 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -139,8 +139,7 @@ void Browser::Activate(bool has_visible_windows) { #if defined(OS_MACOSX) bool Browser::ContinueUserActivity(const std::string& type, - const std::map& user_info) { + const base::DictionaryValue& user_info) { bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, observers_, diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 14f94a061e4..4e282ed409a 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -10,6 +10,7 @@ #include #include "base/macros.h" +#include "base/values.h" #include "base/compiler_specific.h" #include "base/observer_list.h" #include "base/strings/string16.h" @@ -95,11 +96,11 @@ class Browser : public WindowListObserver { // Creates an activity and sets it as the one currently in use. void SetUserActivity(const std::string& type, - const std::map& user_info); + const base::DictionaryValue& user_info); // Resumes an activity via hand-off. bool ContinueUserActivity(const std::string& type, - const std::map& user_info); + const base::DictionaryValue& user_info); // Bounce the dock icon. enum BounceType { diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index a12bd8c8809..594624f511c 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -6,6 +6,7 @@ #include "atom/browser/mac/atom_application.h" #include "atom/browser/mac/atom_application_delegate.h" +#include "atom/browser/mac/mac_native_converter.h" #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" #include "base/mac/bundle_locations.h" @@ -87,20 +88,12 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } -void Browser::SetUserActivity(const std::string& type, const std::map& user_info) { +void Browser::SetUserActivity(const std::string& type, const base::DictionaryValue& user_info) { NSString* type_ns = [NSString stringWithUTF8String:type.c_str()]; NSUserActivity *user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns]; + MacNativeConverter* converter = [[MacNativeConverter alloc] init]; - NSMutableArray* user_info_args = [[NSMutableArray alloc] init]; - for (auto const &pair : user_info) { - NSString* value_ns = [NSString stringWithUTF8String:pair.second.c_str()]; - NSString* key_ns = [NSString stringWithUTF8String:pair.first.c_str()]; - - [user_info_args addObject:value_ns]; - [user_info_args addObject:key_ns]; - } - - user_activity.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:user_info_args, nil]; + user_activity.userInfo = [converter dictionaryFromDictionaryValue:user_info]; [user_activity becomeCurrent]; } diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index 4ad001370c0..e297e16d80d 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -8,6 +8,8 @@ #include #include +#include "base/values.h" + namespace atom { class LoginHandler; @@ -49,7 +51,7 @@ class BrowserObserver { // The browser wants to resume a user activity via handoff. (OS X only) virtual void OnContinueUserActivity(bool* prevent_default, const std::string& type, - const std::map& user_info) {} + const base::DictionaryValue& user_info) {} protected: virtual ~BrowserObserver() {} diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index f08cc84c466..ffb0b988aa2 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -3,8 +3,9 @@ // found in the LICENSE file. #import "atom/browser/mac/atom_application_delegate.h" - #import "atom/browser/mac/atom_application.h" +#import "atom/browser/mac/mac_native_converter.h" + #include "atom/browser/browser.h" #include "base/strings/sys_string_conversions.h" @@ -64,19 +65,11 @@ continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); - std::map user_info; - - NSArray* keys = [userActivity.userInfo allKeys]; - for (NSString* key in keys) - { - NSString* value = [userActivity.userInfo objectForKey:key]; - std::string key_str(base::SysNSStringToUTF8(key)); - std::string value_str(base::SysNSStringToUTF8(value)); - user_info[key_str] = value_str; - } + MacNativeConverter* converter = [[MacNativeConverter alloc] init]; + const base::DictionaryValue* user_info = [converter dictionaryToDictionaryValue:userActivity.userInfo]; atom::Browser* browser = atom::Browser::Get(); - return browser->ContinueUserActivity(activity_type, user_info) ? YES : NO; + return browser->ContinueUserActivity(activity_type, *user_info) ? YES : NO; } @end diff --git a/atom/browser/mac/mac_native_converter.h b/atom/browser/mac/mac_native_converter.h index ac1a9a608d7..fcd1997fd1e 100644 --- a/atom/browser/mac/mac_native_converter.h +++ b/atom/browser/mac/mac_native_converter.h @@ -5,7 +5,10 @@ @interface MacNativeConverter : NSObject -- (base::ListValue*)arrayToV8:(NSArray*)nsArray; -- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary; +- (base::ListValue*)arrayToListValue:(NSArray*)nsArray; +- (base::DictionaryValue*)dictionaryToDictionaryValue:(NSDictionary*)nsDictionary; + +- (NSArray*)arrayFromListValue:(const base::ListValue&)list; +- (NSDictionary*)dictionaryFromDictionaryValue:(const base::DictionaryValue&)dict; @end diff --git a/atom/browser/mac/mac_native_converter.mm b/atom/browser/mac/mac_native_converter.mm index 63244036017..202f3e0ee4a 100644 --- a/atom/browser/mac/mac_native_converter.mm +++ b/atom/browser/mac/mac_native_converter.mm @@ -2,14 +2,14 @@ @implementation MacNativeConverter -- (base::ListValue*)arrayToV8:(NSArray*)nsArray { +- (base::ListValue*)arrayToListValue:(NSArray*)nsArray { scoped_ptr list(new base::ListValue); for (id value in nsArray) { if ([value isKindOfClass:[NSArray class]]) { - list->Append([self arrayToV8:value]); + list->Append([self arrayToListValue:value]); } else if ([value isKindOfClass:[NSDictionary class]]) { - list->Append([self dictionaryToV8:value]); + list->Append([self dictionaryToDictionaryValue:value]); } else if ([value isKindOfClass:[NSString class]]) { list->AppendString(base::SysNSStringToUTF8(value)); } else if ([value isKindOfClass:[NSNumber class]]) { @@ -20,7 +20,7 @@ return list.get(); } -- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary { +- (base::DictionaryValue*)dictionaryToDictionaryValue:(NSDictionary*)nsDictionary { scoped_ptr dict(new base::DictionaryValue); NSEnumerator *it = [nsDictionary keyEnumerator]; @@ -30,9 +30,9 @@ std::string key_str(base::SysNSStringToUTF8(key)); if ([value isKindOfClass:[NSArray class]]) { - dict->Set(key_str, [self arrayToV8:value]); + dict->Set(key_str, [self arrayToListValue:value]); } else if ([value isKindOfClass:[NSDictionary class]]) { - dict->Set(key_str, [self dictionaryToV8:value]); + dict->Set(key_str, [self dictionaryToDictionaryValue:value]); } else if ([value isKindOfClass:[NSString class]]) { dict->SetString(key_str, base::SysNSStringToUTF8(value)); } else if ([value isKindOfClass:[NSNumber class]]) { @@ -43,4 +43,12 @@ return dict.get(); } +- (NSArray*)arrayFromListValue:(const base::ListValue&)list { + return [[NSArray alloc] init]; +} + +- (NSDictionary*)dictionaryFromDictionaryValue:(const base::DictionaryValue&)dict { + return [[NSDictionary alloc] init]; +} + @end From f84a973d69ecfc25f7a131c2ff43c8ee99308cdb Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 10:31:53 -0700 Subject: [PATCH 12/18] Revert "Use a DictionaryValue everywhere instead of a string map." This reverts commit 90cc10944a64d9a4998a3d12a2559baa7705b3af. --- atom/browser/api/atom_api_app.cc | 2 +- atom/browser/api/atom_api_app.h | 2 +- atom/browser/browser.cc | 3 ++- atom/browser/browser.h | 5 ++--- atom/browser/browser_mac.mm | 15 ++++++++++---- atom/browser/browser_observer.h | 4 +--- atom/browser/mac/atom_application_delegate.mm | 17 +++++++++++----- atom/browser/mac/mac_native_converter.h | 7 ++----- atom/browser/mac/mac_native_converter.mm | 20 ++++++------------- 9 files changed, 38 insertions(+), 37 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index ee73e5b2151..ec6cdce417c 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -252,7 +252,7 @@ void App::OnFinishLaunching() { void App::OnContinueUserActivity(bool* prevent_default, const std::string& type, - const base::DictionaryValue& user_info) { + const std::map& user_info) { *prevent_default = Emit("continue-activity", type, user_info); } diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index db510640fa6..321230d711e 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -74,7 +74,7 @@ class App : public AtomBrowserClient::Delegate, void OnLogin(LoginHandler* login_handler) override; void OnContinueUserActivity(bool* prevent_default, const std::string& type, - const base::DictionaryValue& user_info) override; + const std::map& user_info) override; // content::ContentBrowserClient: void AllowCertificateError( diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 62e0467e5d2..ee2a225246b 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -139,7 +139,8 @@ void Browser::Activate(bool has_visible_windows) { #if defined(OS_MACOSX) bool Browser::ContinueUserActivity(const std::string& type, - const base::DictionaryValue& user_info) { + const std::map& user_info) { bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, observers_, diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 4e282ed409a..14f94a061e4 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -10,7 +10,6 @@ #include #include "base/macros.h" -#include "base/values.h" #include "base/compiler_specific.h" #include "base/observer_list.h" #include "base/strings/string16.h" @@ -96,11 +95,11 @@ class Browser : public WindowListObserver { // Creates an activity and sets it as the one currently in use. void SetUserActivity(const std::string& type, - const base::DictionaryValue& user_info); + const std::map& user_info); // Resumes an activity via hand-off. bool ContinueUserActivity(const std::string& type, - const base::DictionaryValue& user_info); + const std::map& user_info); // Bounce the dock icon. enum BounceType { diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 594624f511c..a12bd8c8809 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -6,7 +6,6 @@ #include "atom/browser/mac/atom_application.h" #include "atom/browser/mac/atom_application_delegate.h" -#include "atom/browser/mac/mac_native_converter.h" #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" #include "base/mac/bundle_locations.h" @@ -88,12 +87,20 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } -void Browser::SetUserActivity(const std::string& type, const base::DictionaryValue& user_info) { +void Browser::SetUserActivity(const std::string& type, const std::map& user_info) { NSString* type_ns = [NSString stringWithUTF8String:type.c_str()]; NSUserActivity *user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns]; - MacNativeConverter* converter = [[MacNativeConverter alloc] init]; - user_activity.userInfo = [converter dictionaryFromDictionaryValue:user_info]; + NSMutableArray* user_info_args = [[NSMutableArray alloc] init]; + for (auto const &pair : user_info) { + NSString* value_ns = [NSString stringWithUTF8String:pair.second.c_str()]; + NSString* key_ns = [NSString stringWithUTF8String:pair.first.c_str()]; + + [user_info_args addObject:value_ns]; + [user_info_args addObject:key_ns]; + } + + user_activity.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:user_info_args, nil]; [user_activity becomeCurrent]; } diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index e297e16d80d..4ad001370c0 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -8,8 +8,6 @@ #include #include -#include "base/values.h" - namespace atom { class LoginHandler; @@ -51,7 +49,7 @@ class BrowserObserver { // The browser wants to resume a user activity via handoff. (OS X only) virtual void OnContinueUserActivity(bool* prevent_default, const std::string& type, - const base::DictionaryValue& user_info) {} + const std::map& user_info) {} protected: virtual ~BrowserObserver() {} diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index ffb0b988aa2..f08cc84c466 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -3,9 +3,8 @@ // found in the LICENSE file. #import "atom/browser/mac/atom_application_delegate.h" -#import "atom/browser/mac/atom_application.h" -#import "atom/browser/mac/mac_native_converter.h" +#import "atom/browser/mac/atom_application.h" #include "atom/browser/browser.h" #include "base/strings/sys_string_conversions.h" @@ -65,11 +64,19 @@ continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); - MacNativeConverter* converter = [[MacNativeConverter alloc] init]; - const base::DictionaryValue* user_info = [converter dictionaryToDictionaryValue:userActivity.userInfo]; + std::map user_info; + + NSArray* keys = [userActivity.userInfo allKeys]; + for (NSString* key in keys) + { + NSString* value = [userActivity.userInfo objectForKey:key]; + std::string key_str(base::SysNSStringToUTF8(key)); + std::string value_str(base::SysNSStringToUTF8(value)); + user_info[key_str] = value_str; + } atom::Browser* browser = atom::Browser::Get(); - return browser->ContinueUserActivity(activity_type, *user_info) ? YES : NO; + return browser->ContinueUserActivity(activity_type, user_info) ? YES : NO; } @end diff --git a/atom/browser/mac/mac_native_converter.h b/atom/browser/mac/mac_native_converter.h index fcd1997fd1e..ac1a9a608d7 100644 --- a/atom/browser/mac/mac_native_converter.h +++ b/atom/browser/mac/mac_native_converter.h @@ -5,10 +5,7 @@ @interface MacNativeConverter : NSObject -- (base::ListValue*)arrayToListValue:(NSArray*)nsArray; -- (base::DictionaryValue*)dictionaryToDictionaryValue:(NSDictionary*)nsDictionary; - -- (NSArray*)arrayFromListValue:(const base::ListValue&)list; -- (NSDictionary*)dictionaryFromDictionaryValue:(const base::DictionaryValue&)dict; +- (base::ListValue*)arrayToV8:(NSArray*)nsArray; +- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary; @end diff --git a/atom/browser/mac/mac_native_converter.mm b/atom/browser/mac/mac_native_converter.mm index 202f3e0ee4a..63244036017 100644 --- a/atom/browser/mac/mac_native_converter.mm +++ b/atom/browser/mac/mac_native_converter.mm @@ -2,14 +2,14 @@ @implementation MacNativeConverter -- (base::ListValue*)arrayToListValue:(NSArray*)nsArray { +- (base::ListValue*)arrayToV8:(NSArray*)nsArray { scoped_ptr list(new base::ListValue); for (id value in nsArray) { if ([value isKindOfClass:[NSArray class]]) { - list->Append([self arrayToListValue:value]); + list->Append([self arrayToV8:value]); } else if ([value isKindOfClass:[NSDictionary class]]) { - list->Append([self dictionaryToDictionaryValue:value]); + list->Append([self dictionaryToV8:value]); } else if ([value isKindOfClass:[NSString class]]) { list->AppendString(base::SysNSStringToUTF8(value)); } else if ([value isKindOfClass:[NSNumber class]]) { @@ -20,7 +20,7 @@ return list.get(); } -- (base::DictionaryValue*)dictionaryToDictionaryValue:(NSDictionary*)nsDictionary { +- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary { scoped_ptr dict(new base::DictionaryValue); NSEnumerator *it = [nsDictionary keyEnumerator]; @@ -30,9 +30,9 @@ std::string key_str(base::SysNSStringToUTF8(key)); if ([value isKindOfClass:[NSArray class]]) { - dict->Set(key_str, [self arrayToListValue:value]); + dict->Set(key_str, [self arrayToV8:value]); } else if ([value isKindOfClass:[NSDictionary class]]) { - dict->Set(key_str, [self dictionaryToDictionaryValue:value]); + dict->Set(key_str, [self dictionaryToV8:value]); } else if ([value isKindOfClass:[NSString class]]) { dict->SetString(key_str, base::SysNSStringToUTF8(value)); } else if ([value isKindOfClass:[NSNumber class]]) { @@ -43,12 +43,4 @@ return dict.get(); } -- (NSArray*)arrayFromListValue:(const base::ListValue&)list { - return [[NSArray alloc] init]; -} - -- (NSDictionary*)dictionaryFromDictionaryValue:(const base::DictionaryValue&)dict { - return [[NSDictionary alloc] init]; -} - @end From 03d25ce6c074c28072bf7de147a32ad71e83958a Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 10:32:42 -0700 Subject: [PATCH 13/18] Revert "Add some shady methods to get V8 objects or arrays from NSDictionary or NSArray." This reverts commit 2295f3a8327d6df4031dcfcd8daebb771d0e3530. --- atom/browser/mac/atom_application_delegate.mm | 4 +- atom/browser/mac/mac_native_converter.h | 11 ----- atom/browser/mac/mac_native_converter.mm | 46 ------------------- filenames.gypi | 2 - 4 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 atom/browser/mac/mac_native_converter.h delete mode 100644 atom/browser/mac/mac_native_converter.mm diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index f08cc84c466..33aa47ce240 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -63,9 +63,9 @@ continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); - + std::map user_info; - + NSArray* keys = [userActivity.userInfo allKeys]; for (NSString* key in keys) { diff --git a/atom/browser/mac/mac_native_converter.h b/atom/browser/mac/mac_native_converter.h deleted file mode 100644 index ac1a9a608d7..00000000000 --- a/atom/browser/mac/mac_native_converter.h +++ /dev/null @@ -1,11 +0,0 @@ -#import - -#include "base/values.h" -#include "base/strings/sys_string_conversions.h" - -@interface MacNativeConverter : NSObject - -- (base::ListValue*)arrayToV8:(NSArray*)nsArray; -- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary; - -@end diff --git a/atom/browser/mac/mac_native_converter.mm b/atom/browser/mac/mac_native_converter.mm deleted file mode 100644 index 63244036017..00000000000 --- a/atom/browser/mac/mac_native_converter.mm +++ /dev/null @@ -1,46 +0,0 @@ -#import "atom/browser/mac/mac_native_converter.h" - -@implementation MacNativeConverter - -- (base::ListValue*)arrayToV8:(NSArray*)nsArray { - scoped_ptr list(new base::ListValue); - - for (id value in nsArray) { - if ([value isKindOfClass:[NSArray class]]) { - list->Append([self arrayToV8:value]); - } else if ([value isKindOfClass:[NSDictionary class]]) { - list->Append([self dictionaryToV8:value]); - } else if ([value isKindOfClass:[NSString class]]) { - list->AppendString(base::SysNSStringToUTF8(value)); - } else if ([value isKindOfClass:[NSNumber class]]) { - list->AppendInteger(((NSNumber* )value).intValue); - } - } - - return list.get(); -} - -- (base::DictionaryValue*)dictionaryToV8:(NSDictionary*)nsDictionary { - scoped_ptr dict(new base::DictionaryValue); - - NSEnumerator *it = [nsDictionary keyEnumerator]; - while (NSString *key = [it nextObject]) { - id value = [nsDictionary objectForKey:key]; - - std::string key_str(base::SysNSStringToUTF8(key)); - - if ([value isKindOfClass:[NSArray class]]) { - dict->Set(key_str, [self arrayToV8:value]); - } else if ([value isKindOfClass:[NSDictionary class]]) { - dict->Set(key_str, [self dictionaryToV8:value]); - } else if ([value isKindOfClass:[NSString class]]) { - dict->SetString(key_str, base::SysNSStringToUTF8(value)); - } else if ([value isKindOfClass:[NSNumber class]]) { - dict->SetInteger(key_str, ((NSNumber* )value).intValue); - } - } - - return dict.get(); -} - -@end diff --git a/filenames.gypi b/filenames.gypi index 033d4959454..783b5cb33d0 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -183,8 +183,6 @@ 'atom/browser/mac/atom_application.mm', 'atom/browser/mac/atom_application_delegate.h', 'atom/browser/mac/atom_application_delegate.mm', - 'atom/browser/mac/mac_native_converter.h', - 'atom/browser/mac/mac_native_converter.mm', 'atom/browser/native_window.cc', 'atom/browser/native_window.h', 'atom/browser/native_window_views_win.cc', From a5a2e204490d0af2ac80f5b99087a423a4edb302 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 10:34:59 -0700 Subject: [PATCH 14/18] :memo: on using only strings. --- docs/api/app.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 61e86b70f9a..4bd121adfb6 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -117,7 +117,8 @@ Returns: * `event` Event * `type` String - A string identifying the event. Maps to [`NSUserActivity.activityType`](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType). -* `userInfo` Object - Contains app-specific state stored by the activity on another device. +* `userInfo` Object - Contains app-specific state stored by the activity on +another device. Currently only string data is supported. Emitted during [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) when an activity from a different device wants to be resumed. You should call `event.preventDefault()` if you want to handle this @@ -503,8 +504,8 @@ app.on('ready', function() { * `type` String - Uniquely identifies the activity. It's recommended to use a reverse-DNS string. -* `userInfo` Object - Contains app-specific state stored by the activity on -another device. +* `userInfo` Object - App-specific state to store for use by another device. +Currently only string data is supported. Creates an `NSUserActivity` and sets it as the current activity. The activity is eligible for [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) to another device afterward. From 42768bcc2bf709a34f6c3b9ba4a65ba96da081d2 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 14:57:16 -0700 Subject: [PATCH 15/18] Save the activity on the application instance to ensure we hold a reference. Activities that enable search need to persist. --- atom/browser/browser_mac.mm | 4 +++- atom/browser/mac/atom_application.h | 5 +++++ atom/browser/mac/atom_application.mm | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index a12bd8c8809..58ee84613e4 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -89,7 +89,7 @@ void Browser::SetAppUserModelID(const base::string16& name) { void Browser::SetUserActivity(const std::string& type, const std::map& user_info) { NSString* type_ns = [NSString stringWithUTF8String:type.c_str()]; - NSUserActivity *user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns]; + NSUserActivity* user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns]; NSMutableArray* user_info_args = [[NSMutableArray alloc] init]; for (auto const &pair : user_info) { @@ -102,6 +102,8 @@ void Browser::SetUserActivity(const std::string& type, const std::map { @private BOOL handlingSendEvent_; + NSUserActivity* currentActivity_; } + (AtomApplication*)sharedApplication; @@ -18,4 +19,8 @@ // CrAppControlProtocol: - (void)setHandlingSendEvent:(BOOL)handlingSendEvent; +- (NSUserActivity*)getCurrentActivity; + +- (void)setCurrentActivity:(NSUserActivity*)userActivity; + @end diff --git a/atom/browser/mac/atom_application.mm b/atom/browser/mac/atom_application.mm index cc9c6accc83..3fe6658e4cf 100644 --- a/atom/browser/mac/atom_application.mm +++ b/atom/browser/mac/atom_application.mm @@ -28,6 +28,14 @@ handlingSendEvent_ = handlingSendEvent; } +- (void)setCurrentActivity:(NSUserActivity*)userActivity { + currentActivity_ = userActivity; +} + +- (NSUserActivity*)getCurrentActivity { + return currentActivity_; +} + - (void)awakeFromNib { [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self From 12764a66edb39af0332e1297d321a7045feb7b52 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 15:51:31 -0700 Subject: [PATCH 16/18] Add an accessor for the current activity type and write the simplest possible test. --- atom/browser/api/atom_api_app.cc | 2 ++ atom/browser/browser.h | 3 +++ atom/browser/browser_mac.mm | 15 ++++++++++----- atom/browser/mac/atom_application_delegate.mm | 7 +++---- docs/api/app.md | 4 ++++ spec/api-app-spec.js | 7 +++++++ 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index ec6cdce417c..f15fad9d47f 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -469,6 +469,8 @@ void App::BuildPrototype( .SetMethod("show", base::Bind(&Browser::Show, browser)) .SetMethod("setUserActivity", base::Bind(&Browser::SetUserActivity, browser)) + .SetMethod("getCurrentActivityType", + base::Bind(&Browser::GetCurrentActivityType, browser)) #endif #if defined(OS_WIN) .SetMethod("setUserTasks", diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 14f94a061e4..5c3dcaf5122 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -97,6 +97,9 @@ class Browser : public WindowListObserver { void SetUserActivity(const std::string& type, const std::map& user_info); + // Returns the type name of the current user activity. + std::string GetCurrentActivityType(); + // Resumes an activity via hand-off. bool ContinueUserActivity(const std::string& type, const std::map& user_info); diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 58ee84613e4..069579354bd 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -91,19 +91,24 @@ void Browser::SetUserActivity(const std::string& type, const std::map user_info; - + NSArray* keys = [userActivity.userInfo allKeys]; - for (NSString* key in keys) - { + for (NSString* key in keys) { NSString* value = [userActivity.userInfo objectForKey:key]; std::string key_str(base::SysNSStringToUTF8(key)); std::string value_str(base::SysNSStringToUTF8(value)); diff --git a/docs/api/app.md b/docs/api/app.md index 4bd121adfb6..3ade1da034f 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -510,6 +510,10 @@ Currently only string data is supported. Creates an `NSUserActivity` and sets it as the current activity. The activity is eligible for [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) to another device afterward. +### `app.getCurrentActivityType()` _OS X_ + +Returns the type of the currently running activity. + ### `app.setAppUserModelId(id)` _Windows_ * `id` String diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 1c20ef8e452..809e5520b93 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -89,6 +89,13 @@ describe('app module', function () { }) }) + describe('app.setUserActivity(type, userInfo)', function () { + it('sets the current activity', function () { + app.setUserActivity('com.electron.testActivity', {testData: '123'}); + assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity'); + }) + }) + describe('app.importCertificate', function () { if (process.platform !== 'linux') return From 7b207aa1b67b4068fd8cb1afd7dc715584faed9d Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Tue, 3 May 2016 23:33:40 -0700 Subject: [PATCH 17/18] Don't run this spec on platforms where the method is unavailable. --- spec/api-app-spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 809e5520b93..30df5361041 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -7,6 +7,7 @@ const remote = require('electron').remote const app = remote.require('electron').app const BrowserWindow = remote.require('electron').BrowserWindow +const isCI = remote.getGlobal('isCi') describe('electron module', function () { it('allows old style require by default', function () { @@ -90,6 +91,10 @@ describe('app module', function () { }) describe('app.setUserActivity(type, userInfo)', function () { + if (isCI && process.platform !== 'darwin') { + return + } + it('sets the current activity', function () { app.setUserActivity('com.electron.testActivity', {testData: '123'}); assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity'); From b2fb95f857453bf6eefbebbbfdff78951f6f7f9e Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Wed, 4 May 2016 11:28:49 -0700 Subject: [PATCH 18/18] Use scoped_nsobject to ensure our intermediate objects get cleaned up. --- atom/browser/browser_mac.mm | 8 ++++---- atom/browser/mac/atom_application.h | 3 ++- atom/browser/mac/atom_application.mm | 4 ++-- atom/browser/mac/atom_application_delegate.mm | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 069579354bd..42d10debd09 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -91,16 +91,16 @@ void Browser::SetUserActivity(const std::string& type, const std::map user_info_args([[NSMutableDictionary alloc] init]); for (auto const &pair : user_info) { NSString* value_ns = [NSString stringWithUTF8String:pair.second.c_str()]; NSString* key_ns = [NSString stringWithUTF8String:pair.first.c_str()]; - [user_info_args setObject:value_ns - forKey:key_ns]; + [user_info_args.get() setObject:value_ns + forKey:key_ns]; } - user_activity.userInfo = user_info_args; + user_activity.userInfo = user_info_args.get(); [user_activity becomeCurrent]; [[AtomApplication sharedApplication] setCurrentActivity:user_activity]; diff --git a/atom/browser/mac/atom_application.h b/atom/browser/mac/atom_application.h index e31a9926eb3..37074cae7bf 100644 --- a/atom/browser/mac/atom_application.h +++ b/atom/browser/mac/atom_application.h @@ -3,12 +3,13 @@ // found in the LICENSE file. #import "base/mac/scoped_sending_event.h" +#import "base/mac/scoped_nsobject.h" @interface AtomApplication : NSApplication { @private BOOL handlingSendEvent_; - NSUserActivity* currentActivity_; + base::scoped_nsobject currentActivity_; } + (AtomApplication*)sharedApplication; diff --git a/atom/browser/mac/atom_application.mm b/atom/browser/mac/atom_application.mm index 3fe6658e4cf..18115113047 100644 --- a/atom/browser/mac/atom_application.mm +++ b/atom/browser/mac/atom_application.mm @@ -29,11 +29,11 @@ } - (void)setCurrentActivity:(NSUserActivity*)userActivity { - currentActivity_ = userActivity; + currentActivity_ = base::scoped_nsobject(userActivity); } - (NSUserActivity*)getCurrentActivity { - return currentActivity_; + return currentActivity_.get(); } - (void)awakeFromNib { diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index 2309a7591a1..0904679c49a 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -65,9 +65,9 @@ continueUserActivity:(NSUserActivity *)userActivity std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType)); std::map user_info; + base::scoped_nsobject keys([userActivity.userInfo allKeys]); - NSArray* keys = [userActivity.userInfo allKeys]; - for (NSString* key in keys) { + for (NSString* key in keys.get()) { NSString* value = [userActivity.userInfo objectForKey:key]; std::string key_str(base::SysNSStringToUTF8(key)); std::string value_str(base::SysNSStringToUTF8(value));