blob: e6fcd7a4de4697154b8d093b0c9db147b70a7590 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/web/mailto_url_rewriter.h"
#import "base/logging.h"
#import "ios/chrome/browser/web/mailto_handler.h"
#import "ios/chrome/browser/web/mailto_handler_gmail.h"
#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
#import <UIKit/UIKit.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The key for NSUserDefaults to store the Mail client selected to handle
// mailto: URL scheme. If this key is not set, user has not made an explicit
// choice for default mailto: handler and system-provided Mail client app will
// be used.
NSString* const kMailtoDefaultHandlerKey = @"MailtoHandlerDefault";
} // namespace
@interface MailtoURLRewriter ()
// Dictionary keyed by the App Store ID of the Mail client and the value is
// the MailtoHandler object that can rewrite a mailto: URL.
@property(nonatomic, strong)
NSMutableDictionary<NSString*, MailtoHandler*>* handlers;
// Private method for testing to clear the default state.
+ (void)resetDefaultHandlerIDForTesting;
// Private method to add one or more |handlerApp| objects to the list of known
// Mail client apps.
- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
// Custom logic to handle the migration from Google Native App Launcher options
// to this simplified mailto: URL only system. This must be called after
// -addMailtoApp: has been called to add all the known Mail client apps.
// TODO(crbug.com/718601): At some point in the future when almost all users
// have upgraded, this method can be removed.
- (void)migrateLegacyOptions;
// For users who have not made an explicit choice (kMailtoDefaultHandlerKey
// not set), if Gmail is detected, make an explicit choice for the user to
// use Gmail app as the default mailto: handler.
- (void)autoDefaultToGmailIfInstalled;
@end
@implementation MailtoURLRewriter
@synthesize observer = _observer;
@synthesize handlers = _handlers;
+ (NSString*)systemMailApp {
// This is the App Store ID for Apple Mail app.
// See https://itunes.apple.com/us/app/mail/id1108187098?mt=8
return @"1108187098";
}
- (instancetype)init {
self = [super init];
if (self) {
_handlers = [NSMutableDictionary dictionary];
}
return self;
}
- (instancetype)initWithStandardHandlers {
self = [self init];
if (self) {
[self addMailtoApps:@[
[[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init]
]];
}
return self;
}
- (NSArray<MailtoHandler*>*)defaultHandlers {
return [[_handlers allValues]
sortedArrayUsingComparator:^NSComparisonResult(
MailtoHandler* _Nonnull obj1, MailtoHandler* _Nonnull obj2) {
return [[obj1 appName] compare:[obj2 appName]];
}];
}
- (NSString*)defaultHandlerID {
NSString* value = [[NSUserDefaults standardUserDefaults]
stringForKey:kMailtoDefaultHandlerKey];
if ([_handlers[value] isAvailable])
return value;
return [[self class] systemMailApp];
}
- (void)setDefaultHandlerID:(NSString*)appStoreID {
DCHECK([appStoreID length]);
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
if ([appStoreID
isEqualToString:[defaults objectForKey:kMailtoDefaultHandlerKey]])
return;
[defaults setObject:appStoreID forKey:kMailtoDefaultHandlerKey];
[_observer rewriterDidChange:self];
}
- (NSString*)defaultHandlerName {
NSString* handlerID = [self defaultHandlerID];
MailtoHandler* handler = _handlers[handlerID];
return [handler appName];
}
- (NSString*)rewriteMailtoURL:(const GURL&)gURL {
NSString* value = [self defaultHandlerID];
if ([value length]) {
MailtoHandler* handler = _handlers[value];
if ([handler isAvailable]) {
return [handler rewriteMailtoURL:gURL];
}
}
return nil;
}
#pragma mark - Private
+ (void)resetDefaultHandlerIDForTesting {
[[NSUserDefaults standardUserDefaults]
removeObjectForKey:kMailtoDefaultHandlerKey];
}
- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps {
for (MailtoHandler* app in handlerApps) {
[_handlers setObject:app forKey:[app appStoreID]];
}
[self migrateLegacyOptions];
[self autoDefaultToGmailIfInstalled];
}
//
// Implements the migration logic for users of previous versions of Google
// Chrome which supports Google Native App Launcher. The goal is to preserve
// the previous behavior and support user choice of non-system provided Mail
// client apps. System-provided Mail client app will be referred to as
// "System Mail". The migration goals are:
// - If a user has not made a choice for preferred mailto: handler in the past,
// the behavior after upgrading to this version should not change.
// - If a user had disabled Gmail app as the preferred mailto: handler in the
// past, System Mail should be selected as the default mailto: handler.
// - If a user installs Gmail app after the installation of Chrome, Gmail app
// will be chosen as the default mailto: handler, preserving the previous
// behavior.
// - If a user installs another 3rd party mail client app, assuming that the
// 3rd party mail client app is explicitly supported in Chrome, the new 3rd
// party mail client app can be set as the default mailto: handler through
// Tools > Settings > Content Settings.
//
// Two NSUserDefaults keys are interpreted with the following meanings:
// - kLegacyShouldAutoOpenKey: The existence of this key implies that the user
// has used Chrome in the past and had Gmail app installed. Gmail app may or
// may not be installed currently.
// - kMailtoDefaultHandlerKey: If this key is not set, System Mail app is used
// to handle mailto: URLs. If this key is set, the value is a string that is
// the key to |_handlers| which maps to a MailtoHandler object.
//
- (void)migrateLegacyOptions {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
// User previously had a selection made for opening mailto: links with Gmail,
// upgrade will set Gmail app to be the default mailto: handler. If user had
// opted out to using Gmail app (even when it was installed), migrate user
// to use system-provided Mail client.
// The key used in NSUserDefaults is from legacy code when Native App
// Launcher was still in use. The general format is a string prefix,
// underscore, then followed by the App Store ID of the application.
NSString* const kGmailAppStoreID = @"422689480";
NSString* const kLegacyShouldAutoOpenKey =
[NSString stringWithFormat:@"ShouldAutoOpenLinks_%@", kGmailAppStoreID];
NSNumber* legacyValue = [defaults objectForKey:kLegacyShouldAutoOpenKey];
if (legacyValue) {
switch ([legacyValue intValue]) {
case 0:
case 2: {
// If legacy user was using default (kAutoOpenLinksNotSet) or had
// explicitly chosen to use Gmail (kAutoOpenLinksYes), migrate to use
// Gmail app.
MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
if ([gmailHandler isAvailable])
[defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
else
[defaults removeObjectForKey:kMailtoDefaultHandlerKey];
break;
}
case 1:
// If legacy user was not using Gmail to handle mailto: links
// (kAutoOpenLinksNo), consider this an explicit user choice and
// migrate to use system-provided Mail app.
[defaults setObject:[[self class] systemMailApp]
forKey:kMailtoDefaultHandlerKey];
break;
default:
NOTREACHED();
break;
}
[defaults removeObjectForKey:kLegacyShouldAutoOpenKey];
}
}
- (void)autoDefaultToGmailIfInstalled {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
// If a default handler for mailto: has already been set, user had made an
// explicit choice and no further changes should be done.
if ([defaults objectForKey:kMailtoDefaultHandlerKey])
return;
NSString* const kGmailAppStoreID = @"422689480";
MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
if ([gmailHandler isAvailable])
[defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
}
@end