refactor: rename the atom directory to shell
This commit is contained in:
parent
4575a4aae3
commit
d7f07e8a80
631 changed files with 0 additions and 0 deletions
139
shell/browser/mac/atom_application.h
Normal file
139
shell/browser/mac/atom_application.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/mac/scoped_sending_event.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <LocalAuthentication/LocalAuthentication.h>
|
||||
|
||||
// Forward Declare Appearance APIs
|
||||
@interface NSApplication (HighSierraSDK)
|
||||
@property(copy, readonly)
|
||||
NSAppearance* effectiveAppearance API_AVAILABLE(macosx(10.14));
|
||||
@property(copy, readonly) NSAppearance* appearance API_AVAILABLE(macosx(10.14));
|
||||
- (void)setAppearance:(NSAppearance*)appearance API_AVAILABLE(macosx(10.14));
|
||||
@end
|
||||
|
||||
#if !defined(MAC_OS_X_VERSION_10_13_2)
|
||||
|
||||
// forward declare Touch ID APIs
|
||||
typedef NS_ENUM(NSInteger, LABiometryType) {
|
||||
LABiometryTypeNone = 0,
|
||||
LABiometryTypeFaceID = 1,
|
||||
LABiometryTypeTouchID = 2,
|
||||
} API_AVAILABLE(macosx(10.13.2));
|
||||
|
||||
@interface LAContext (HighSierraPointTwoSDK)
|
||||
@property(nonatomic, readonly)
|
||||
LABiometryType biometryType API_AVAILABLE(macosx(10.13.2));
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
// forward declare Access APIs
|
||||
typedef NSString* AVMediaType NS_EXTENSIBLE_STRING_ENUM;
|
||||
|
||||
AVF_EXPORT AVMediaType const AVMediaTypeVideo;
|
||||
AVF_EXPORT AVMediaType const AVMediaTypeAudio;
|
||||
|
||||
typedef NS_ENUM(NSInteger, AVAuthorizationStatusMac) {
|
||||
AVAuthorizationStatusNotDeterminedMac = 0,
|
||||
AVAuthorizationStatusRestrictedMac = 1,
|
||||
AVAuthorizationStatusDeniedMac = 2,
|
||||
AVAuthorizationStatusAuthorizedMac = 3,
|
||||
};
|
||||
|
||||
@interface NSMenuItem (HighSierraSDK)
|
||||
@property(atomic, readwrite)
|
||||
BOOL allowsKeyEquivalentWhenHidden API_AVAILABLE(macosx(10.13));
|
||||
- (void)setAllowsKeyEquivalentWhenHidden:(BOOL)arg1
|
||||
API_AVAILABLE(macosx(10.13));
|
||||
@end
|
||||
|
||||
@interface AVCaptureDevice (MojaveSDK)
|
||||
+ (void)requestAccessForMediaType:(AVMediaType)mediaType
|
||||
completionHandler:(void (^)(BOOL granted))handler
|
||||
API_AVAILABLE(macosx(10.14));
|
||||
+ (AVAuthorizationStatusMac)authorizationStatusForMediaType:
|
||||
(AVMediaType)mediaType API_AVAILABLE(macosx(10.14));
|
||||
@end
|
||||
|
||||
@interface NSColor (MojaveSDK)
|
||||
@property(class, strong, readonly)
|
||||
NSColor* controlAccentColor API_AVAILABLE(macosx(10.14));
|
||||
|
||||
// macOS system colors
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemBlueColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemBrownColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemGrayColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemGreenColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemOrangeColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemPinkColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemPurpleColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemRedColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* systemYellowColor API_AVAILABLE(macosx(10.10));
|
||||
|
||||
// misc dynamic colors declarations
|
||||
@property(class, strong, readonly)
|
||||
NSColor* linkColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* placeholderTextColor API_AVAILABLE(macosx(10.10));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* findHighlightColor API_AVAILABLE(macosx(10.13));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* separatorColor API_AVAILABLE(macosx(10.14));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* selectedContentBackgroundColor API_AVAILABLE(macosx(10.14));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* unemphasizedSelectedContentBackgroundColor API_AVAILABLE(
|
||||
macosx(10.14));
|
||||
@property(class, strong, readonly)
|
||||
NSColor* unemphasizedSelectedTextBackgroundColor API_AVAILABLE(macosx(10.14)
|
||||
);
|
||||
@property(class, strong, readonly)
|
||||
NSColor* unemphasizedSelectedTextColor API_AVAILABLE(macosx(10.14));
|
||||
@end
|
||||
|
||||
@interface AtomApplication : NSApplication <CrAppProtocol,
|
||||
CrAppControlProtocol,
|
||||
NSUserActivityDelegate> {
|
||||
@private
|
||||
BOOL handlingSendEvent_;
|
||||
base::scoped_nsobject<NSUserActivity> currentActivity_;
|
||||
NSCondition* handoffLock_;
|
||||
BOOL updateReceived_;
|
||||
base::Callback<bool()> shouldShutdown_;
|
||||
}
|
||||
|
||||
+ (AtomApplication*)sharedApplication;
|
||||
|
||||
- (void)setShutdownHandler:(base::Callback<bool()>)handler;
|
||||
|
||||
// CrAppProtocol:
|
||||
- (BOOL)isHandlingSendEvent;
|
||||
|
||||
// CrAppControlProtocol:
|
||||
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;
|
||||
|
||||
- (NSUserActivity*)getCurrentActivity;
|
||||
- (void)setCurrentActivity:(NSString*)type
|
||||
withUserInfo:(NSDictionary*)userInfo
|
||||
withWebpageURL:(NSURL*)webpageURL;
|
||||
- (void)invalidateCurrentActivity;
|
||||
- (void)resignCurrentActivity;
|
||||
- (void)updateCurrentActivity:(NSString*)type
|
||||
withUserInfo:(NSDictionary*)userInfo;
|
||||
|
||||
@end
|
219
shell/browser/mac/atom_application.mm
Normal file
219
shell/browser/mac/atom_application.mm
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "atom/browser/mac/atom_application.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#import "atom/browser/mac/atom_application_delegate.h"
|
||||
#include "atom/browser/mac/dict_util.h"
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "content/public/browser/browser_accessibility_state.h"
|
||||
#include "content/public/browser/native_event_processor_mac.h"
|
||||
#include "content/public/browser/native_event_processor_observer_mac.h"
|
||||
|
||||
namespace {
|
||||
|
||||
inline void dispatch_sync_main(dispatch_block_t block) {
|
||||
if ([NSThread isMainThread])
|
||||
block();
|
||||
else
|
||||
dispatch_sync(dispatch_get_main_queue(), block);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface AtomApplication () <NativeEventProcessor> {
|
||||
base::ObserverList<content::NativeEventProcessorObserver>::Unchecked
|
||||
observers_;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AtomApplication
|
||||
|
||||
+ (AtomApplication*)sharedApplication {
|
||||
return (AtomApplication*)[super sharedApplication];
|
||||
}
|
||||
|
||||
- (void)terminate:(id)sender {
|
||||
if (shouldShutdown_ && !shouldShutdown_.Run())
|
||||
return; // User will call Quit later.
|
||||
|
||||
// We simply try to close the browser, which in turn will try to close the
|
||||
// windows. Termination can proceed if all windows are closed or window close
|
||||
// can be cancelled which will abort termination.
|
||||
atom::Browser::Get()->Quit();
|
||||
}
|
||||
|
||||
- (void)setShutdownHandler:(base::Callback<bool()>)handler {
|
||||
shouldShutdown_ = std::move(handler);
|
||||
}
|
||||
|
||||
- (BOOL)isHandlingSendEvent {
|
||||
return handlingSendEvent_;
|
||||
}
|
||||
|
||||
- (void)sendEvent:(NSEvent*)event {
|
||||
base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES);
|
||||
content::ScopedNotifyNativeEventProcessorObserver scopedObserverNotifier(
|
||||
&observers_, event);
|
||||
[super sendEvent:event];
|
||||
}
|
||||
|
||||
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
|
||||
handlingSendEvent_ = handlingSendEvent;
|
||||
}
|
||||
|
||||
- (void)setCurrentActivity:(NSString*)type
|
||||
withUserInfo:(NSDictionary*)userInfo
|
||||
withWebpageURL:(NSURL*)webpageURL {
|
||||
currentActivity_ = base::scoped_nsobject<NSUserActivity>(
|
||||
[[NSUserActivity alloc] initWithActivityType:type]);
|
||||
[currentActivity_ setUserInfo:userInfo];
|
||||
[currentActivity_ setWebpageURL:webpageURL];
|
||||
[currentActivity_ setDelegate:self];
|
||||
[currentActivity_ becomeCurrent];
|
||||
[currentActivity_ setNeedsSave:YES];
|
||||
}
|
||||
|
||||
- (NSUserActivity*)getCurrentActivity {
|
||||
return currentActivity_.get();
|
||||
}
|
||||
|
||||
- (void)invalidateCurrentActivity {
|
||||
if (currentActivity_) {
|
||||
[currentActivity_ invalidate];
|
||||
currentActivity_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resignCurrentActivity {
|
||||
if (@available(macOS 10.11, *)) {
|
||||
if (currentActivity_)
|
||||
[currentActivity_ resignCurrent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateCurrentActivity:(NSString*)type
|
||||
withUserInfo:(NSDictionary*)userInfo {
|
||||
if (currentActivity_) {
|
||||
[currentActivity_ addUserInfoEntriesFromDictionary:userInfo];
|
||||
}
|
||||
|
||||
[handoffLock_ lock];
|
||||
updateReceived_ = YES;
|
||||
[handoffLock_ signal];
|
||||
[handoffLock_ unlock];
|
||||
}
|
||||
|
||||
- (void)userActivityWillSave:(NSUserActivity*)userActivity {
|
||||
__block BOOL shouldWait = NO;
|
||||
dispatch_sync_main(^{
|
||||
std::string activity_type(
|
||||
base::SysNSStringToUTF8(userActivity.activityType));
|
||||
std::unique_ptr<base::DictionaryValue> user_info =
|
||||
atom::NSDictionaryToDictionaryValue(userActivity.userInfo);
|
||||
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
shouldWait =
|
||||
browser->UpdateUserActivityState(activity_type, *user_info) ? YES : NO;
|
||||
});
|
||||
|
||||
if (shouldWait) {
|
||||
[handoffLock_ lock];
|
||||
updateReceived_ = NO;
|
||||
while (!updateReceived_) {
|
||||
BOOL isSignaled =
|
||||
[handoffLock_ waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
||||
if (!isSignaled)
|
||||
break;
|
||||
}
|
||||
[handoffLock_ unlock];
|
||||
}
|
||||
|
||||
[userActivity setNeedsSave:YES];
|
||||
}
|
||||
|
||||
- (void)userActivityWasContinued:(NSUserActivity*)userActivity {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
std::string activity_type(
|
||||
base::SysNSStringToUTF8(userActivity.activityType));
|
||||
std::unique_ptr<base::DictionaryValue> user_info =
|
||||
atom::NSDictionaryToDictionaryValue(userActivity.userInfo);
|
||||
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
browser->UserActivityWasContinued(activity_type, *user_info);
|
||||
});
|
||||
[userActivity setNeedsSave:YES];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[[NSAppleEventManager sharedAppleEventManager]
|
||||
setEventHandler:self
|
||||
andSelector:@selector(handleURLEvent:withReplyEvent:)
|
||||
forEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
|
||||
handoffLock_ = [NSCondition new];
|
||||
}
|
||||
|
||||
- (void)handleURLEvent:(NSAppleEventDescriptor*)event
|
||||
withReplyEvent:(NSAppleEventDescriptor*)replyEvent {
|
||||
NSString* url =
|
||||
[[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||
atom::Browser::Get()->OpenURL(base::SysNSStringToUTF8(url));
|
||||
}
|
||||
|
||||
- (bool)voiceOverEnabled {
|
||||
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults addSuiteNamed:@"com.apple.universalaccess"];
|
||||
[defaults synchronize];
|
||||
|
||||
return [defaults boolForKey:@"voiceOverOnOffKey"];
|
||||
}
|
||||
|
||||
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
|
||||
// Undocumented attribute that VoiceOver happens to set while running.
|
||||
// Chromium uses this too, even though it's not exactly right.
|
||||
if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) {
|
||||
bool enableAccessibility = ([self voiceOverEnabled] && [value boolValue]);
|
||||
[self updateAccessibilityEnabled:enableAccessibility];
|
||||
} else if ([attribute isEqualToString:@"AXManualAccessibility"]) {
|
||||
[self updateAccessibilityEnabled:[value boolValue]];
|
||||
}
|
||||
return [super accessibilitySetValue:value forAttribute:attribute];
|
||||
}
|
||||
|
||||
- (void)updateAccessibilityEnabled:(BOOL)enabled {
|
||||
auto* ax_state = content::BrowserAccessibilityState::GetInstance();
|
||||
|
||||
if (enabled) {
|
||||
ax_state->OnScreenReaderDetected();
|
||||
} else {
|
||||
ax_state->DisableAccessibility();
|
||||
}
|
||||
|
||||
atom::Browser::Get()->OnAccessibilitySupportChanged();
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanel:(id)sender {
|
||||
atom::Browser::Get()->ShowAboutPanel();
|
||||
}
|
||||
|
||||
- (void)addNativeEventProcessorObserver:
|
||||
(content::NativeEventProcessorObserver*)observer {
|
||||
observers_.AddObserver(observer);
|
||||
}
|
||||
|
||||
- (void)removeNativeEventProcessorObserver:
|
||||
(content::NativeEventProcessorObserver*)observer {
|
||||
observers_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
@end
|
17
shell/browser/mac/atom_application_delegate.h
Normal file
17
shell/browser/mac/atom_application_delegate.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "atom/browser/ui/cocoa/atom_menu_controller.h"
|
||||
|
||||
@interface AtomApplicationDelegate : NSObject <NSApplicationDelegate> {
|
||||
@private
|
||||
base::scoped_nsobject<AtomMenuController> menu_controller_;
|
||||
}
|
||||
|
||||
// Sets the menu that will be returned in "applicationDockMenu:".
|
||||
- (void)setApplicationDockMenu:(atom::AtomMenuModel*)model;
|
||||
|
||||
@end
|
143
shell/browser/mac/atom_application_delegate.mm
Normal file
143
shell/browser/mac/atom_application_delegate.mm
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "atom/browser/mac/atom_application_delegate.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#import "atom/browser/mac/atom_application.h"
|
||||
#include "atom/browser/mac/dict_util.h"
|
||||
#include "base/allocator/allocator_shim.h"
|
||||
#include "base/allocator/buildflags.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_objc_class_swizzler.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
|
||||
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
// On macOS 10.12, the IME system attempts to allocate a 2^64 size buffer,
|
||||
// which would typically cause an OOM crash. To avoid this, the problematic
|
||||
// method is swizzled out and the make-OOM-fatal bit is disabled for the
|
||||
// duration of the original call. https://crbug.com/654695
|
||||
static base::mac::ScopedObjCClassSwizzler* g_swizzle_imk_input_session;
|
||||
@interface OOMDisabledIMKInputSession : NSObject
|
||||
@end
|
||||
@implementation OOMDisabledIMKInputSession
|
||||
- (void)_coreAttributesFromRange:(NSRange)range
|
||||
whichAttributes:(long long)attributes // NOLINT(runtime/int)
|
||||
completionHandler:(void (^)(void))block {
|
||||
// The allocator flag is per-process, so other threads may temporarily
|
||||
// not have fatal OOM occur while this method executes, but it is better
|
||||
// than crashing when using IME.
|
||||
base::allocator::SetCallNewHandlerOnMallocFailure(false);
|
||||
g_swizzle_imk_input_session->GetOriginalImplementation()(self, _cmd, range,
|
||||
attributes, block);
|
||||
base::allocator::SetCallNewHandlerOnMallocFailure(true);
|
||||
}
|
||||
@end
|
||||
#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
|
||||
@implementation AtomApplicationDelegate
|
||||
|
||||
- (void)setApplicationDockMenu:(atom::AtomMenuModel*)model {
|
||||
menu_controller_.reset([[AtomMenuController alloc] initWithModel:model
|
||||
useDefaultAccelerator:NO]);
|
||||
}
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification*)notify {
|
||||
// Don't add the "Enter Full Screen" menu item automatically.
|
||||
[[NSUserDefaults standardUserDefaults]
|
||||
setBool:NO
|
||||
forKey:@"NSFullScreenMenuItemEverywhere"];
|
||||
|
||||
atom::Browser::Get()->WillFinishLaunching();
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)notify {
|
||||
NSUserNotification* user_notification =
|
||||
[notify userInfo][(id) @"NSApplicationLaunchUserNotificationKey"];
|
||||
|
||||
if (user_notification.userInfo != nil) {
|
||||
std::unique_ptr<base::DictionaryValue> launch_info =
|
||||
atom::NSDictionaryToDictionaryValue(user_notification.userInfo);
|
||||
atom::Browser::Get()->DidFinishLaunching(*launch_info);
|
||||
} else {
|
||||
atom::Browser::Get()->DidFinishLaunching(base::DictionaryValue());
|
||||
}
|
||||
|
||||
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
// Disable fatal OOM to hack around an OS bug https://crbug.com/654695.
|
||||
if (base::mac::IsOS10_12()) {
|
||||
g_swizzle_imk_input_session = new base::mac::ScopedObjCClassSwizzler(
|
||||
NSClassFromString(@"IMKInputSession"),
|
||||
[OOMDisabledIMKInputSession class],
|
||||
@selector(_coreAttributesFromRange:whichAttributes:completionHandler:));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
|
||||
if (menu_controller_)
|
||||
return [menu_controller_ menu];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename {
|
||||
std::string filename_str(base::SysNSStringToUTF8(filename));
|
||||
return atom::Browser::Get()->OpenFile(filename_str) ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
|
||||
hasVisibleWindows:(BOOL)flag {
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
browser->Activate(static_cast<bool>(flag));
|
||||
return flag;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication*)sender
|
||||
continueUserActivity:(NSUserActivity*)userActivity
|
||||
restorationHandler:
|
||||
#ifdef MAC_OS_X_VERSION_10_14
|
||||
(void (^)(NSArray<id<NSUserActivityRestoring>>* restorableObjects))
|
||||
#else
|
||||
(void (^)(NSArray* restorableObjects))
|
||||
#endif
|
||||
restorationHandler {
|
||||
std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType));
|
||||
std::unique_ptr<base::DictionaryValue> user_info =
|
||||
atom::NSDictionaryToDictionaryValue(userActivity.userInfo);
|
||||
if (!user_info)
|
||||
return NO;
|
||||
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
return browser->ContinueUserActivity(activity_type, *user_info) ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication*)application
|
||||
willContinueUserActivityWithType:(NSString*)userActivityType {
|
||||
std::string activity_type(base::SysNSStringToUTF8(userActivityType));
|
||||
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
return browser->WillContinueUserActivity(activity_type) ? YES : NO;
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication*)application
|
||||
didFailToContinueUserActivityWithType:(NSString*)userActivityType
|
||||
error:(NSError*)error {
|
||||
std::string activity_type(base::SysNSStringToUTF8(userActivityType));
|
||||
std::string error_message(
|
||||
base::SysNSStringToUTF8([error localizedDescription]));
|
||||
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
browser->DidFailToContinueUserActivity(activity_type, error_message);
|
||||
}
|
||||
|
||||
- (IBAction)newWindowForTab:(id)sender {
|
||||
atom::Browser::Get()->NewWindowForTab();
|
||||
}
|
||||
|
||||
@end
|
30
shell/browser/mac/dict_util.h
Normal file
30
shell/browser/mac/dict_util.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_MAC_DICT_UTIL_H_
|
||||
#define ATOM_BROWSER_MAC_DICT_UTIL_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
namespace base {
|
||||
class ListValue;
|
||||
class DictionaryValue;
|
||||
} // namespace base
|
||||
|
||||
namespace atom {
|
||||
|
||||
NSArray* ListValueToNSArray(const base::ListValue& value);
|
||||
|
||||
std::unique_ptr<base::ListValue> NSArrayToListValue(NSArray* arr);
|
||||
|
||||
NSDictionary* DictionaryValueToNSDictionary(const base::DictionaryValue& value);
|
||||
|
||||
std::unique_ptr<base::DictionaryValue> NSDictionaryToDictionaryValue(
|
||||
NSDictionary* dict);
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_MAC_DICT_UTIL_H_
|
130
shell/browser/mac/dict_util.mm
Normal file
130
shell/browser/mac/dict_util.mm
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/mac/dict_util.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/json/json_writer.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
NSArray* ListValueToNSArray(const base::ListValue& value) {
|
||||
std::string json;
|
||||
if (!base::JSONWriter::Write(value, &json))
|
||||
return nil;
|
||||
NSData* jsonData = [NSData dataWithBytes:json.c_str() length:json.length()];
|
||||
id obj = [NSJSONSerialization JSONObjectWithData:jsonData
|
||||
options:0
|
||||
error:nil];
|
||||
if (![obj isKindOfClass:[NSArray class]])
|
||||
return nil;
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::unique_ptr<base::ListValue> NSArrayToListValue(NSArray* arr) {
|
||||
if (!arr)
|
||||
return nullptr;
|
||||
|
||||
auto result = std::make_unique<base::ListValue>();
|
||||
for (id value in arr) {
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
result->AppendString(base::SysNSStringToUTF8(value));
|
||||
} else if ([value isKindOfClass:[NSNumber class]]) {
|
||||
const char* objc_type = [value objCType];
|
||||
if (strcmp(objc_type, @encode(BOOL)) == 0 ||
|
||||
strcmp(objc_type, @encode(char)) == 0)
|
||||
result->AppendBoolean([value boolValue]);
|
||||
else if (strcmp(objc_type, @encode(double)) == 0 ||
|
||||
strcmp(objc_type, @encode(float)) == 0)
|
||||
result->AppendDouble([value doubleValue]);
|
||||
else
|
||||
result->AppendInteger([value intValue]);
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
std::unique_ptr<base::ListValue> sub_arr = NSArrayToListValue(value);
|
||||
if (sub_arr)
|
||||
result->Append(std::move(sub_arr));
|
||||
else
|
||||
result->Append(std::make_unique<base::Value>());
|
||||
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
std::unique_ptr<base::DictionaryValue> sub_dict =
|
||||
NSDictionaryToDictionaryValue(value);
|
||||
if (sub_dict)
|
||||
result->Append(std::move(sub_dict));
|
||||
else
|
||||
result->Append(std::make_unique<base::Value>());
|
||||
} else {
|
||||
result->AppendString(base::SysNSStringToUTF8([value description]));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NSDictionary* DictionaryValueToNSDictionary(
|
||||
const base::DictionaryValue& value) {
|
||||
std::string json;
|
||||
if (!base::JSONWriter::Write(value, &json))
|
||||
return nil;
|
||||
NSData* jsonData = [NSData dataWithBytes:json.c_str() length:json.length()];
|
||||
id obj = [NSJSONSerialization JSONObjectWithData:jsonData
|
||||
options:0
|
||||
error:nil];
|
||||
if (![obj isKindOfClass:[NSDictionary class]])
|
||||
return nil;
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::unique_ptr<base::DictionaryValue> NSDictionaryToDictionaryValue(
|
||||
NSDictionary* dict) {
|
||||
if (!dict)
|
||||
return nullptr;
|
||||
|
||||
auto result = std::make_unique<base::DictionaryValue>();
|
||||
for (id key in dict) {
|
||||
std::string str_key = base::SysNSStringToUTF8(
|
||||
[key isKindOfClass:[NSString class]] ? key : [key description]);
|
||||
|
||||
id value = [dict objectForKey:key];
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
result->SetKey(str_key, base::Value(base::SysNSStringToUTF8(value)));
|
||||
} else if ([value isKindOfClass:[NSNumber class]]) {
|
||||
const char* objc_type = [value objCType];
|
||||
if (strcmp(objc_type, @encode(BOOL)) == 0 ||
|
||||
strcmp(objc_type, @encode(char)) == 0)
|
||||
result->SetKey(str_key, base::Value([value boolValue]));
|
||||
else if (strcmp(objc_type, @encode(double)) == 0 ||
|
||||
strcmp(objc_type, @encode(float)) == 0)
|
||||
result->SetKey(str_key, base::Value([value doubleValue]));
|
||||
else
|
||||
result->SetKey(str_key, base::Value([value intValue]));
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
std::unique_ptr<base::ListValue> sub_arr = NSArrayToListValue(value);
|
||||
if (sub_arr)
|
||||
result->SetWithoutPathExpansion(str_key, std::move(sub_arr));
|
||||
else
|
||||
result->SetWithoutPathExpansion(str_key,
|
||||
std::make_unique<base::Value>());
|
||||
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
std::unique_ptr<base::DictionaryValue> sub_dict =
|
||||
NSDictionaryToDictionaryValue(value);
|
||||
if (sub_dict)
|
||||
result->SetWithoutPathExpansion(str_key, std::move(sub_dict));
|
||||
else
|
||||
result->SetWithoutPathExpansion(str_key,
|
||||
std::make_unique<base::Value>());
|
||||
} else {
|
||||
result->SetKey(str_key,
|
||||
base::Value(base::SysNSStringToUTF8([value description])));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace atom
|
34
shell/browser/mac/in_app_purchase.h
Normal file
34
shell/browser/mac/in_app_purchase.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2017 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_MAC_IN_APP_PURCHASE_H_
|
||||
#define ATOM_BROWSER_MAC_IN_APP_PURCHASE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
// --------------------------- Typedefs ---------------------------
|
||||
|
||||
typedef base::OnceCallback<void(bool isProductValid)> InAppPurchaseCallback;
|
||||
|
||||
// --------------------------- Functions ---------------------------
|
||||
|
||||
bool CanMakePayments(void);
|
||||
|
||||
void FinishAllTransactions(void);
|
||||
|
||||
void FinishTransactionByDate(const std::string& date);
|
||||
|
||||
std::string GetReceiptURL(void);
|
||||
|
||||
void PurchaseProduct(const std::string& productID,
|
||||
int quantity,
|
||||
InAppPurchaseCallback callback);
|
||||
|
||||
} // namespace in_app_purchase
|
||||
|
||||
#endif // ATOM_BROWSER_MAC_IN_APP_PURCHASE_H_
|
191
shell/browser/mac/in_app_purchase.mm
Normal file
191
shell/browser/mac/in_app_purchase.mm
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) 2017 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/mac/in_app_purchase.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
// ============================================================================
|
||||
// InAppPurchase
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------- Interface --------------------------------
|
||||
|
||||
@interface InAppPurchase : NSObject <SKProductsRequestDelegate> {
|
||||
@private
|
||||
in_app_purchase::InAppPurchaseCallback callback_;
|
||||
NSInteger quantity_;
|
||||
}
|
||||
|
||||
- (id)initWithCallback:(in_app_purchase::InAppPurchaseCallback)callback
|
||||
quantity:(NSInteger)quantity;
|
||||
|
||||
- (void)purchaseProduct:(NSString*)productID;
|
||||
|
||||
@end
|
||||
|
||||
// ------------------------------- Implementation -----------------------------
|
||||
|
||||
@implementation InAppPurchase
|
||||
|
||||
/**
|
||||
* Init with a callback.
|
||||
*
|
||||
* @param callback - The callback that will be called when the payment is added
|
||||
* to the queue.
|
||||
*/
|
||||
- (id)initWithCallback:(in_app_purchase::InAppPurchaseCallback)callback
|
||||
quantity:(NSInteger)quantity {
|
||||
if ((self = [super init])) {
|
||||
callback_ = std::move(callback);
|
||||
quantity_ = quantity;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the in-app purchase process.
|
||||
*
|
||||
* @param productID - The id of the product to purchase (the id of
|
||||
* com.example.app.product1 is product1).
|
||||
*/
|
||||
- (void)purchaseProduct:(NSString*)productID {
|
||||
// Retrieve the product information. (The products request retrieves,
|
||||
// information about valid products along with a list of the invalid product
|
||||
// identifiers, and then calls its delegate to process the result).
|
||||
SKProductsRequest* productsRequest;
|
||||
productsRequest = [[SKProductsRequest alloc]
|
||||
initWithProductIdentifiers:[NSSet setWithObject:productID]];
|
||||
|
||||
productsRequest.delegate = self;
|
||||
[productsRequest start];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process product informations and start the payment.
|
||||
*
|
||||
* @param request - The product request.
|
||||
* @param response - The informations about the list of products.
|
||||
*/
|
||||
- (void)productsRequest:(SKProductsRequest*)request
|
||||
didReceiveResponse:(SKProductsResponse*)response {
|
||||
// Release request object.
|
||||
[request release];
|
||||
|
||||
// Get the first product.
|
||||
NSArray* products = response.products;
|
||||
SKProduct* product = [products count] == 1 ? [products firstObject] : nil;
|
||||
|
||||
// Return if the product is not found or invalid.
|
||||
if (product == nil) {
|
||||
[self runCallback:false];
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the payment process.
|
||||
[self checkout:product];
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a payment request to the App Store.
|
||||
*
|
||||
* @param product - The product to purchase.
|
||||
*/
|
||||
- (void)checkout:(SKProduct*)product {
|
||||
// Add the payment to the transaction queue. (The observer will be called
|
||||
// when the transaction is finished).
|
||||
SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:product];
|
||||
payment.quantity = quantity_;
|
||||
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
|
||||
// Notify that the payment has been added to the queue with success.
|
||||
[self runCallback:true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a payment request to the App Store.
|
||||
*
|
||||
* @param product - The product to purchase.
|
||||
*/
|
||||
- (void)runCallback:(bool)isProductValid {
|
||||
if (callback_) {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(std::move(callback_), isProductValid));
|
||||
}
|
||||
// Release this delegate.
|
||||
[self release];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// ============================================================================
|
||||
// C++ in_app_purchase
|
||||
// ============================================================================
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
bool CanMakePayments() {
|
||||
return [SKPaymentQueue canMakePayments];
|
||||
}
|
||||
|
||||
void FinishAllTransactions() {
|
||||
for (SKPaymentTransaction* transaction in SKPaymentQueue.defaultQueue
|
||||
.transactions) {
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
}
|
||||
|
||||
void FinishTransactionByDate(const std::string& date) {
|
||||
// Create the date formatter.
|
||||
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
|
||||
NSLocale* enUSPOSIXLocale =
|
||||
[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||
[dateFormatter setLocale:enUSPOSIXLocale];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
|
||||
|
||||
// Remove the transaction.
|
||||
NSString* transactionDate = base::SysUTF8ToNSString(date);
|
||||
|
||||
for (SKPaymentTransaction* transaction in SKPaymentQueue.defaultQueue
|
||||
.transactions) {
|
||||
if ([transactionDate
|
||||
isEqualToString:[dateFormatter
|
||||
stringFromDate:transaction.transactionDate]]) {
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetReceiptURL() {
|
||||
NSURL* receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
|
||||
if (receiptURL != nil) {
|
||||
return std::string([[receiptURL path] UTF8String]);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void PurchaseProduct(const std::string& productID,
|
||||
int quantity,
|
||||
InAppPurchaseCallback callback) {
|
||||
auto* iap = [[InAppPurchase alloc] initWithCallback:std::move(callback)
|
||||
quantity:quantity];
|
||||
|
||||
[iap purchaseProduct:base::SysUTF8ToNSString(productID)];
|
||||
}
|
||||
|
||||
} // namespace in_app_purchase
|
63
shell/browser/mac/in_app_purchase_observer.h
Normal file
63
shell/browser/mac/in_app_purchase_observer.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2017 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_MAC_IN_APP_PURCHASE_OBSERVER_H_
|
||||
#define ATOM_BROWSER_MAC_IN_APP_PURCHASE_OBSERVER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
||||
#if defined(__OBJC__)
|
||||
@class InAppTransactionObserver;
|
||||
#else // __OBJC__
|
||||
class InAppTransactionObserver;
|
||||
#endif // __OBJC__
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
// --------------------------- Structures ---------------------------
|
||||
|
||||
struct Payment {
|
||||
std::string productIdentifier = "";
|
||||
int quantity = 1;
|
||||
};
|
||||
|
||||
struct Transaction {
|
||||
std::string transactionIdentifier = "";
|
||||
std::string transactionDate = "";
|
||||
std::string originalTransactionIdentifier = "";
|
||||
int errorCode = 0;
|
||||
std::string errorMessage = "";
|
||||
std::string transactionState = "";
|
||||
Payment payment;
|
||||
|
||||
Transaction();
|
||||
Transaction(const Transaction&);
|
||||
~Transaction();
|
||||
};
|
||||
|
||||
// --------------------------- Classes ---------------------------
|
||||
|
||||
class TransactionObserver {
|
||||
public:
|
||||
TransactionObserver();
|
||||
virtual ~TransactionObserver();
|
||||
|
||||
virtual void OnTransactionsUpdated(
|
||||
const std::vector<Transaction>& transactions) = 0;
|
||||
|
||||
private:
|
||||
InAppTransactionObserver* obeserver_;
|
||||
|
||||
base::WeakPtrFactory<TransactionObserver> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TransactionObserver);
|
||||
};
|
||||
|
||||
} // namespace in_app_purchase
|
||||
|
||||
#endif // ATOM_BROWSER_MAC_IN_APP_PURCHASE_OBSERVER_H_
|
196
shell/browser/mac/in_app_purchase_observer.mm
Normal file
196
shell/browser/mac/in_app_purchase_observer.mm
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2017 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/mac/in_app_purchase_observer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
// ============================================================================
|
||||
// InAppTransactionObserver
|
||||
// ============================================================================
|
||||
|
||||
namespace {
|
||||
|
||||
using InAppTransactionCallback = base::RepeatingCallback<void(
|
||||
const std::vector<in_app_purchase::Transaction>&)>;
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface InAppTransactionObserver : NSObject <SKPaymentTransactionObserver> {
|
||||
@private
|
||||
InAppTransactionCallback callback_;
|
||||
}
|
||||
|
||||
- (id)initWithCallback:(const InAppTransactionCallback&)callback;
|
||||
|
||||
@end
|
||||
|
||||
@implementation InAppTransactionObserver
|
||||
|
||||
/**
|
||||
* Init with a callback.
|
||||
*
|
||||
* @param callback - The callback that will be called for each transaction
|
||||
* update.
|
||||
*/
|
||||
- (id)initWithCallback:(const InAppTransactionCallback&)callback {
|
||||
if ((self = [super init])) {
|
||||
callback_ = callback;
|
||||
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup.
|
||||
*/
|
||||
- (void)dealloc {
|
||||
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the callback in the browser thread.
|
||||
*
|
||||
* @param transaction - The transaction to pass to the callback.
|
||||
*/
|
||||
- (void)runCallback:(NSArray*)transactions {
|
||||
// Convert the transaction.
|
||||
std::vector<in_app_purchase::Transaction> converted;
|
||||
converted.reserve([transactions count]);
|
||||
for (SKPaymentTransaction* transaction in transactions) {
|
||||
converted.push_back([self skPaymentTransactionToStruct:transaction]);
|
||||
}
|
||||
|
||||
// Send the callback to the browser thread.
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(callback_, converted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an NSDate to ISO String.
|
||||
*
|
||||
* @param date - The date to convert.
|
||||
*/
|
||||
- (NSString*)dateToISOString:(NSDate*)date {
|
||||
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
|
||||
NSLocale* enUSPOSIXLocale =
|
||||
[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||
[dateFormatter setLocale:enUSPOSIXLocale];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
|
||||
|
||||
return [dateFormatter stringFromDate:date];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a SKPayment object to a Payment structure.
|
||||
*
|
||||
* @param payment - The SKPayment object to convert.
|
||||
*/
|
||||
- (in_app_purchase::Payment)skPaymentToStruct:(SKPayment*)payment {
|
||||
in_app_purchase::Payment paymentStruct;
|
||||
|
||||
if (payment.productIdentifier != nil) {
|
||||
paymentStruct.productIdentifier = [payment.productIdentifier UTF8String];
|
||||
}
|
||||
|
||||
if (payment.quantity >= 1) {
|
||||
paymentStruct.quantity = (int)payment.quantity;
|
||||
}
|
||||
|
||||
return paymentStruct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a SKPaymentTransaction object to a Transaction structure.
|
||||
*
|
||||
* @param transaction - The SKPaymentTransaction object to convert.
|
||||
*/
|
||||
- (in_app_purchase::Transaction)skPaymentTransactionToStruct:
|
||||
(SKPaymentTransaction*)transaction {
|
||||
in_app_purchase::Transaction transactionStruct;
|
||||
|
||||
if (transaction.transactionIdentifier != nil) {
|
||||
transactionStruct.transactionIdentifier =
|
||||
[transaction.transactionIdentifier UTF8String];
|
||||
}
|
||||
|
||||
if (transaction.transactionDate != nil) {
|
||||
transactionStruct.transactionDate =
|
||||
[[self dateToISOString:transaction.transactionDate] UTF8String];
|
||||
}
|
||||
|
||||
if (transaction.originalTransaction != nil) {
|
||||
transactionStruct.originalTransactionIdentifier =
|
||||
[transaction.originalTransaction.transactionIdentifier UTF8String];
|
||||
}
|
||||
|
||||
if (transaction.error != nil) {
|
||||
transactionStruct.errorCode = (int)transaction.error.code;
|
||||
transactionStruct.errorMessage =
|
||||
[[transaction.error localizedDescription] UTF8String];
|
||||
}
|
||||
|
||||
if (transaction.transactionState < 5) {
|
||||
transactionStruct.transactionState =
|
||||
[[@[ @"purchasing", @"purchased", @"failed", @"restored", @"deferred" ]
|
||||
objectAtIndex:transaction.transactionState] UTF8String];
|
||||
}
|
||||
|
||||
if (transaction.payment != nil) {
|
||||
transactionStruct.payment = [self skPaymentToStruct:transaction.payment];
|
||||
}
|
||||
|
||||
return transactionStruct;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark SKPaymentTransactionObserver methods
|
||||
|
||||
/**
|
||||
* Executed when a transaction is updated.
|
||||
*
|
||||
* @param queue - The payment queue.
|
||||
* @param transactions - The list of transactions updated.
|
||||
*/
|
||||
- (void)paymentQueue:(SKPaymentQueue*)queue
|
||||
updatedTransactions:(NSArray*)transactions {
|
||||
[self runCallback:transactions];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// ============================================================================
|
||||
// C++ in_app_purchase
|
||||
// ============================================================================
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
Transaction::Transaction() = default;
|
||||
Transaction::Transaction(const Transaction&) = default;
|
||||
Transaction::~Transaction() = default;
|
||||
|
||||
TransactionObserver::TransactionObserver() : weak_ptr_factory_(this) {
|
||||
obeserver_ = [[InAppTransactionObserver alloc]
|
||||
initWithCallback:base::BindRepeating(
|
||||
&TransactionObserver::OnTransactionsUpdated,
|
||||
weak_ptr_factory_.GetWeakPtr())];
|
||||
}
|
||||
|
||||
TransactionObserver::~TransactionObserver() {
|
||||
[obeserver_ release];
|
||||
}
|
||||
|
||||
} // namespace in_app_purchase
|
51
shell/browser/mac/in_app_purchase_product.h
Normal file
51
shell/browser/mac/in_app_purchase_product.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2018 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_MAC_IN_APP_PURCHASE_PRODUCT_H_
|
||||
#define ATOM_BROWSER_MAC_IN_APP_PURCHASE_PRODUCT_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
// --------------------------- Structures ---------------------------
|
||||
|
||||
struct Product {
|
||||
// Product Identifier
|
||||
std::string productIdentifier;
|
||||
|
||||
// Product Attributes
|
||||
std::string localizedDescription;
|
||||
std::string localizedTitle;
|
||||
std::string contentVersion;
|
||||
std::vector<uint32_t> contentLengths;
|
||||
|
||||
// Pricing Information
|
||||
double price = 0.0;
|
||||
std::string formattedPrice;
|
||||
|
||||
// Downloadable Content Information
|
||||
bool isDownloadable = false;
|
||||
|
||||
Product(const Product&);
|
||||
Product();
|
||||
~Product();
|
||||
};
|
||||
|
||||
// --------------------------- Typedefs ---------------------------
|
||||
|
||||
typedef base::OnceCallback<void(std::vector<in_app_purchase::Product>)>
|
||||
InAppPurchaseProductsCallback;
|
||||
|
||||
// --------------------------- Functions ---------------------------
|
||||
|
||||
void GetProducts(const std::vector<std::string>& productIDs,
|
||||
InAppPurchaseProductsCallback callback);
|
||||
|
||||
} // namespace in_app_purchase
|
||||
|
||||
#endif // ATOM_BROWSER_MAC_IN_APP_PURCHASE_PRODUCT_H_
|
187
shell/browser/mac/in_app_purchase_product.mm
Normal file
187
shell/browser/mac/in_app_purchase_product.mm
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright (c) 2017 Amaplex Software, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/mac/in_app_purchase_product.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
// ============================================================================
|
||||
// InAppPurchaseProduct
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------- Interface --------------------------------
|
||||
|
||||
@interface InAppPurchaseProduct : NSObject <SKProductsRequestDelegate> {
|
||||
@private
|
||||
in_app_purchase::InAppPurchaseProductsCallback callback_;
|
||||
}
|
||||
|
||||
- (id)initWithCallback:(in_app_purchase::InAppPurchaseProductsCallback)callback;
|
||||
|
||||
@end
|
||||
|
||||
// ------------------------------- Implementation -----------------------------
|
||||
|
||||
@implementation InAppPurchaseProduct
|
||||
|
||||
/**
|
||||
* Init with a callback.
|
||||
*
|
||||
* @param callback - The callback that will be called to return the products.
|
||||
*/
|
||||
- (id)initWithCallback:
|
||||
(in_app_purchase::InAppPurchaseProductsCallback)callback {
|
||||
if ((self = [super init])) {
|
||||
callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return products.
|
||||
*
|
||||
* @param productIDs - The products' id to fetch.
|
||||
*/
|
||||
- (void)getProducts:(NSSet*)productIDs {
|
||||
SKProductsRequest* productsRequest;
|
||||
productsRequest =
|
||||
[[SKProductsRequest alloc] initWithProductIdentifiers:productIDs];
|
||||
|
||||
productsRequest.delegate = self;
|
||||
[productsRequest start];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SKProductsRequestDelegate
|
||||
*/
|
||||
- (void)productsRequest:(SKProductsRequest*)request
|
||||
didReceiveResponse:(SKProductsResponse*)response {
|
||||
// Release request object.
|
||||
[request release];
|
||||
|
||||
// Get the products.
|
||||
NSArray* products = response.products;
|
||||
|
||||
// Convert the products.
|
||||
std::vector<in_app_purchase::Product> converted;
|
||||
converted.reserve([products count]);
|
||||
|
||||
for (SKProduct* product in products) {
|
||||
converted.push_back([self skProductToStruct:product]);
|
||||
}
|
||||
|
||||
// Send the callback to the browser thread.
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(std::move(callback_), converted));
|
||||
|
||||
[self release];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format local price.
|
||||
*
|
||||
* @param price - The price to format.
|
||||
* @param priceLocal - The local format.
|
||||
*/
|
||||
- (NSString*)formatPrice:(NSDecimalNumber*)price
|
||||
withLocal:(NSLocale*)priceLocal {
|
||||
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
|
||||
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
|
||||
[numberFormatter setLocale:priceLocal];
|
||||
|
||||
return [numberFormatter stringFromNumber:price];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a skProduct object to Product structure.
|
||||
*
|
||||
* @param product - The SKProduct object to convert.
|
||||
*/
|
||||
- (in_app_purchase::Product)skProductToStruct:(SKProduct*)product {
|
||||
in_app_purchase::Product productStruct;
|
||||
|
||||
// Product Identifier
|
||||
if (product.productIdentifier != nil) {
|
||||
productStruct.productIdentifier = [product.productIdentifier UTF8String];
|
||||
}
|
||||
|
||||
// Product Attributes
|
||||
if (product.localizedDescription != nil) {
|
||||
productStruct.localizedDescription =
|
||||
[product.localizedDescription UTF8String];
|
||||
}
|
||||
if (product.localizedTitle != nil) {
|
||||
productStruct.localizedTitle = [product.localizedTitle UTF8String];
|
||||
}
|
||||
if (product.contentVersion != nil) {
|
||||
productStruct.contentVersion = [product.contentVersion UTF8String];
|
||||
}
|
||||
if (product.contentLengths != nil) {
|
||||
productStruct.contentLengths.reserve([product.contentLengths count]);
|
||||
|
||||
for (NSNumber* contentLength in product.contentLengths) {
|
||||
productStruct.contentLengths.push_back([contentLength longLongValue]);
|
||||
}
|
||||
}
|
||||
|
||||
// Pricing Information
|
||||
if (product.price != nil) {
|
||||
productStruct.price = [product.price doubleValue];
|
||||
|
||||
if (product.priceLocale != nil) {
|
||||
productStruct.formattedPrice =
|
||||
[[self formatPrice:product.price
|
||||
withLocal:product.priceLocale] UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
// Downloadable Content Information
|
||||
productStruct.isDownloadable = [product downloadable];
|
||||
|
||||
return productStruct;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// ============================================================================
|
||||
// C++ in_app_purchase
|
||||
// ============================================================================
|
||||
|
||||
namespace in_app_purchase {
|
||||
|
||||
Product::Product() = default;
|
||||
Product::Product(const Product&) = default;
|
||||
Product::~Product() = default;
|
||||
|
||||
void GetProducts(const std::vector<std::string>& productIDs,
|
||||
InAppPurchaseProductsCallback callback) {
|
||||
auto* iapProduct =
|
||||
[[InAppPurchaseProduct alloc] initWithCallback:std::move(callback)];
|
||||
|
||||
// Convert the products' id to NSSet.
|
||||
NSMutableSet* productsIDSet =
|
||||
[NSMutableSet setWithCapacity:productIDs.size()];
|
||||
|
||||
for (auto& productID : productIDs) {
|
||||
[productsIDSet addObject:base::SysUTF8ToNSString(productID)];
|
||||
}
|
||||
|
||||
// Fetch the products.
|
||||
[iapProduct getProducts:productsIDSet];
|
||||
}
|
||||
|
||||
} // namespace in_app_purchase
|
Loading…
Add table
Add a link
Reference in a new issue