| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ios/chrome/browser/ios_chrome_main_parts.h" |
| |
| #include "base/base_switches.h" |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/ios/ios_util.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/path_service.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/default_tick_clock.h" |
| #include "components/content_settings/core/browser/cookie_settings.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "components/crash/core/common/reporter_running_ios.h" |
| #include "components/flags_ui/pref_service_flags_storage.h" |
| #include "components/heap_profiling/in_process/heap_profiler_controller.h" |
| #include "components/language/core/browser/language_usage_metrics.h" |
| #include "components/language/core/browser/pref_names.h" |
| #include "components/metrics/call_stack_profile_builder.h" |
| #include "components/metrics/call_stack_profile_metrics_provider.h" |
| #include "components/metrics/expired_histogram_util.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/metrics_services_manager/metrics_services_manager.h" |
| #include "components/open_from_clipboard/clipboard_recent_content.h" |
| #include "components/prefs/json_pref_store.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/translate/core/browser/translate_download_manager.h" |
| #include "components/ukm/ios/features.h" |
| #include "components/variations/field_trial_config/field_trial_util.h" |
| #include "components/variations/service/variations_service.h" |
| #include "components/variations/synthetic_trials_active_group_id_provider.h" |
| #include "components/variations/variations_crash_keys.h" |
| #include "components/variations/variations_ids_provider.h" |
| #include "ios/chrome/browser/application_context_impl.h" |
| #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h" |
| #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h" |
| #include "ios/chrome/browser/chrome_paths.h" |
| #include "ios/chrome/browser/crash_report/crash_helper.h" |
| #import "ios/chrome/browser/first_run/first_run.h" |
| #include "ios/chrome/browser/flags/about_flags.h" |
| #include "ios/chrome/browser/install_time_util.h" |
| #include "ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h" |
| #include "ios/chrome/browser/metrics/ios_expired_histograms_array.h" |
| #include "ios/chrome/browser/open_from_clipboard/create_clipboard_recent_content.h" |
| #include "ios/chrome/browser/policy/browser_policy_connector_ios.h" |
| #include "ios/chrome/browser/pref_names.h" |
| #include "ios/chrome/browser/safe_browsing/safe_browsing_service.h" |
| #include "ios/chrome/browser/translate/translate_service_ios.h" |
| #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| #include "ios/web/public/thread/web_task_traits.h" |
| #include "ios/web/public/thread/web_thread.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_stream_factory.h" |
| #include "net/url_request/url_request.h" |
| #include "rlz/buildflags/buildflags.h" |
| #include "ui/base/l10n/l10n_util_mac.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| #if BUILDFLAG(ENABLE_RLZ) |
| #include "components/rlz/rlz_tracker.h" // nogncheck |
| #include "ios/chrome/browser/rlz/rlz_tracker_delegate_impl.h" // nogncheck |
| #endif |
| |
| #if BUILDFLAG(USE_ALLOCATOR_SHIM) |
| #include "base/allocator/allocator_shim.h" |
| #endif |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| |
| // Sets |level| value for NSURLFileProtectionKey key for the URL with given |
| // |local_state_path|. |
| void SetProtectionLevel(const base::FilePath& file_path, id level) { |
| NSString* file_path_string = base::SysUTF8ToNSString(file_path.value()); |
| NSURL* file_path_url = [NSURL fileURLWithPath:file_path_string |
| isDirectory:NO]; |
| NSError* error = nil; |
| BOOL protection_set = [file_path_url setResourceValue:level |
| forKey:NSURLFileProtectionKey |
| error:&error]; |
| DCHECK(protection_set) << base::SysNSStringToUTF8(error.localizedDescription); |
| } |
| |
| #if BUILDFLAG(USE_ALLOCATOR_SHIM) |
| // Do not install allocator shim on iOS 13.4 due to high crash volume on this |
| // particular version of OS. TODO(crbug.com/1108219): Remove this workaround |
| // when/if the bug gets fixed. |
| // |
| // Do not install allocator shim for now, until it's clear why Chrome crashes |
| // on iOS 14.3+ on startup. |
| // TODO(crbug.com/1150599): Remove this workaround when/if the bug gets fixed. |
| bool ShouldInstallAllocatorShim() { |
| return false; |
| } |
| #endif |
| |
| // If enabled, always pass |true| to MetricsServicesManager |
| // UpdateUploadPermissions. Once impact of the UmaCellular logic to check |
| // cellular is determined, this flag and the incorrect logic can be removed. |
| const base::Feature kFixUmaCellularMetricsRecording{ |
| "FixUmaCellularMetricsRecording", base::FEATURE_DISABLED_BY_DEFAULT}; |
| |
| } // namespace |
| |
| IOSChromeMainParts::IOSChromeMainParts( |
| const base::CommandLine& parsed_command_line) |
| : parsed_command_line_(parsed_command_line), local_state_(nullptr) { |
| // Chrome disallows cookies by default. All code paths that want to use |
| // cookies need to go through one of Chrome's URLRequestContexts which have |
| // a ChromeNetworkDelegate attached that selectively allows cookies again. |
| net::URLRequest::SetDefaultCookiePolicyToBlock(); |
| } |
| |
| IOSChromeMainParts::~IOSChromeMainParts() {} |
| |
| void IOSChromeMainParts::PreEarlyInitialization() { |
| #if BUILDFLAG(USE_ALLOCATOR_SHIM) |
| if (ShouldInstallAllocatorShim()) { |
| base::allocator::InitializeAllocatorShim(); |
| } |
| #endif |
| } |
| |
| void IOSChromeMainParts::PreMainMessageLoopStart() { |
| l10n_util::OverrideLocaleWithCocoaLocale(); |
| const std::string loaded_locale = |
| ui::ResourceBundle::InitSharedInstanceWithLocale( |
| std::string(), nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES); |
| CHECK(!loaded_locale.empty()); |
| |
| base::FilePath resources_pack_path; |
| base::PathService::Get(ios::FILE_RESOURCES_PACK, &resources_pack_path); |
| ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( |
| resources_pack_path, ui::SCALE_FACTOR_100P); |
| } |
| |
| void IOSChromeMainParts::PreCreateThreads() { |
| // IMPORTANT |
| // Calls in this function should not post tasks or create threads as |
| // components used to handle those tasks are not yet available. This work |
| // should be deferred to PreMainMessageLoopRunImpl. |
| |
| // The initial read is done synchronously, the TaskPriority is thus only used |
| // for flushes to disks and BACKGROUND is therefore appropriate. Priority of |
| // remaining BACKGROUND+BLOCK_SHUTDOWN tasks is bumped by the ThreadPool on |
| // shutdown. |
| scoped_refptr<base::SequencedTaskRunner> local_state_task_runner = |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); |
| |
| base::FilePath local_state_path; |
| CHECK(base::PathService::Get(ios::FILE_LOCAL_STATE, &local_state_path)); |
| application_context_.reset(new ApplicationContextImpl( |
| local_state_task_runner.get(), parsed_command_line_, |
| l10n_util::GetLocaleOverride())); |
| DCHECK_EQ(application_context_.get(), GetApplicationContext()); |
| |
| // Check the first run state early; this must be done before IO is disallowed |
| // so that later calls can use the cached value. (The return value is ignored |
| // because this is only to trigger the internal lookup and caching for later |
| // use.) |
| FirstRun::IsChromeFirstRun(); |
| |
| // Convert freeform experimental settings into switches before initializing |
| // local state, in case any of the settings affect policy. |
| AppendSwitchesFromExperimentalSettings( |
| base::CommandLine::ForCurrentProcess()); |
| |
| // Initialize local state. |
| local_state_ = application_context_->GetLocalState(); |
| DCHECK(local_state_); |
| |
| flags_ui::PrefServiceFlagsStorage flags_storage( |
| application_context_->GetLocalState()); |
| ConvertFlagsToSwitches(&flags_storage, |
| base::CommandLine::ForCurrentProcess()); |
| |
| // Now that the command line has been mutated based on about:flags, we can |
| // initialize field trials. The field trials are needed by IOThread's |
| // initialization which happens in BrowserProcess:PreCreateThreads. Metrics |
| // initialization is handled in PreMainMessageLoopRun since it posts tasks. |
| SetupFieldTrials(); |
| |
| // Sync the crashpad field tral state to NSUserDefaults. Called immediately |
| // after setting up field trials. |
| crash_helper::SyncCrashpadEnabledOnNextRun(); |
| |
| #if BUILDFLAG(USE_ALLOCATOR_SHIM) |
| // Do not install allocator shim on iOS 13.4 due to high crash volume on this |
| // particular version of OS. TODO(crbug.com/1108219): Remove this workaround |
| // when/if the bug gets fixed. |
| if (ShouldInstallAllocatorShim()) { |
| // Start heap profiling as early as possible so it can start recording |
| // memory allocations. Requires the allocator shim to be enabled. |
| heap_profiler_controller_ = std::make_unique<HeapProfilerController>(); |
| metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback( |
| base::BindRepeating( |
| &metrics::CallStackProfileMetricsProvider::ReceiveProfile)); |
| heap_profiler_controller_->Start(); |
| } |
| #endif |
| |
| variations::InitCrashKeys(); |
| |
| metrics::EnableExpiryChecker(::kExpiredHistogramsHashes, |
| ::kNumExpiredHistograms); |
| |
| // TODO(crbug.com/1164533): Remove code below some time after February 2021. |
| NSString* const kRemoveProtectionFromPrefFileKey = |
| @"RemoveProtectionFromPrefKey"; |
| if ([NSUserDefaults.standardUserDefaults |
| boolForKey:kRemoveProtectionFromPrefFileKey]) { |
| // Restore default protection level when user is no longer in the |
| // experimental group. |
| SetProtectionLevel(local_state_path, |
| NSFileProtectionCompleteUntilFirstUserAuthentication); |
| [NSUserDefaults.standardUserDefaults |
| removeObjectForKey:kRemoveProtectionFromPrefFileKey]; |
| } |
| |
| application_context_->PreCreateThreads(); |
| } |
| |
| void IOSChromeMainParts::PreMainMessageLoopRun() { |
| application_context_->PreMainMessageLoopRun(); |
| |
| // ContentSettingsPattern need to be initialized before creating the |
| // ChromeBrowserState. |
| ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(nullptr, 0); |
| |
| // Ensure ClipboadRecentContentIOS is created. |
| ClipboardRecentContent::SetInstance(CreateClipboardRecentContentIOS()); |
| |
| // Ensure that the browser state is initialized. |
| EnsureBrowserStateKeyedServiceFactoriesBuilt(); |
| ios::ChromeBrowserStateManager* browser_state_manager = |
| application_context_->GetChromeBrowserStateManager(); |
| ChromeBrowserState* last_used_browser_state = |
| browser_state_manager->GetLastUsedBrowserState(); |
| |
| // This must occur at PreMainMessageLoopRun because |SetupMetrics()| uses the |
| // blocking pool, which is disabled until the CreateThreads phase of startup. |
| // TODO(crbug.com/786494): Investigate whether metrics recording can be |
| // initialized consistently across iOS and non-iOS platforms |
| SetupMetrics(); |
| |
| // Now that the file thread has been started, start recording. |
| StartMetricsRecording(); |
| |
| // Because the crashpad flag takes 2 restarts to take effect, register a |
| // synthetic field try when crashpad is actually running. Called immediately |
| // after starting metrics recording. |
| IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "CrashpadIOS", |
| crash_reporter::IsCrashpadRunning() ? "Enabled" : "Disabled"); |
| |
| #if BUILDFLAG(ENABLE_RLZ) |
| // Init the RLZ library. This just schedules a task on the file thread to be |
| // run sometime later. If this is the first run we record the installation |
| // event. |
| int ping_delay = last_used_browser_state->GetPrefs()->GetInteger( |
| FirstRun::GetPingDelayPrefName()); |
| // Negative ping delay means to send ping immediately after a first search is |
| // recorded. |
| rlz::RLZTracker::SetRlzDelegate(base::WrapUnique(new RLZTrackerDelegateImpl)); |
| rlz::RLZTracker::InitRlzDelayed( |
| FirstRun::IsChromeFirstRun(), ping_delay < 0, |
| base::TimeDelta::FromMilliseconds(abs(ping_delay)), |
| RLZTrackerDelegateImpl::IsGoogleDefaultSearch(last_used_browser_state), |
| RLZTrackerDelegateImpl::IsGoogleHomepage(last_used_browser_state), |
| RLZTrackerDelegateImpl::IsGoogleInStartpages(last_used_browser_state)); |
| #endif // BUILDFLAG(ENABLE_RLZ) |
| |
| TranslateServiceIOS::Initialize(); |
| language::LanguageUsageMetrics::RecordAcceptLanguages( |
| last_used_browser_state->GetPrefs()->GetString( |
| language::prefs::kAcceptLanguages)); |
| language::LanguageUsageMetrics::RecordApplicationLanguage( |
| application_context_->GetApplicationLocale()); |
| |
| // Request new variations seed information from server. |
| variations::VariationsService* variations_service = |
| application_context_->GetVariationsService(); |
| if (variations_service) { |
| variations_service->set_policy_pref_service( |
| last_used_browser_state->GetPrefs()); |
| variations_service->PerformPreMainMessageLoopStartup(); |
| } |
| |
| // Initialize Chrome Browser Cloud Management. |
| auto* policy_connector = application_context_->GetBrowserPolicyConnector(); |
| if (policy_connector) { |
| policy_connector->chrome_browser_cloud_management_controller()->Init( |
| application_context_->GetLocalState(), |
| application_context_->GetSharedURLLoaderFactory()); |
| } |
| |
| // Ensure that Safe Browsing is initialized. |
| SafeBrowsingService* safe_browsing_service = |
| application_context_->GetSafeBrowsingService(); |
| base::FilePath user_data_path; |
| CHECK(base::PathService::Get(ios::DIR_USER_DATA, &user_data_path)); |
| safe_browsing_service->Initialize(last_used_browser_state->GetPrefs(), |
| user_data_path); |
| } |
| |
| void IOSChromeMainParts::PostMainMessageLoopRun() { |
| TranslateServiceIOS::Shutdown(); |
| #if BUILDFLAG(ENABLE_RLZ) |
| rlz::RLZTracker::CleanupRlz(); |
| #endif // BUILDFLAG(ENABLE_RLZ) |
| application_context_->StartTearDown(); |
| } |
| |
| void IOSChromeMainParts::PostDestroyThreads() { |
| application_context_->PostDestroyThreads(); |
| } |
| |
| // This will be called after the command-line has been mutated by about:flags |
| void IOSChromeMainParts::SetupFieldTrials() { |
| base::SetRecordActionTaskRunner( |
| base::CreateSingleThreadTaskRunner({web::WebThread::UI})); |
| |
| // Initialize FieldTrialList to support FieldTrials that use one-time |
| // randomization. |
| DCHECK(!field_trial_list_); |
| field_trial_list_.reset( |
| new base::FieldTrialList(application_context_->GetMetricsServicesManager() |
| ->CreateEntropyProvider())); |
| |
| std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); |
| |
| // Associate parameters chosen in about:flags and create trial/group for them. |
| flags_ui::PrefServiceFlagsStorage flags_storage( |
| application_context_->GetLocalState()); |
| std::vector<std::string> variation_ids = |
| RegisterAllFeatureVariationParameters(&flags_storage, feature_list.get()); |
| |
| // On iOS, GPU benchmarking is not supported. So, pass in a dummy value for |
| // the name of the switch that enables gpu benchmarking. |
| // TODO(crbug.com/988603): This should also set up extra switch-dependent |
| // feature overrides. |
| application_context_->GetVariationsService()->SetupFieldTrials( |
| "dummy-enable-gpu-benchmarking", switches::kEnableFeatures, |
| switches::kDisableFeatures, variation_ids, |
| std::vector<base::FeatureList::FeatureOverrideInfo>(), |
| std::move(feature_list), &ios_field_trials_); |
| } |
| |
| void IOSChromeMainParts::SetupMetrics() { |
| metrics::MetricsService* metrics = application_context_->GetMetricsService(); |
| metrics->synthetic_trial_registry()->AddSyntheticTrialObserver( |
| variations::VariationsIdsProvider::GetInstance()); |
| metrics->synthetic_trial_registry()->AddSyntheticTrialObserver( |
| variations::SyntheticTrialsActiveGroupIdProvider::GetInstance()); |
| // Now that field trials have been created, initializes metrics recording. |
| metrics->InitializeMetricsRecordingState(); |
| } |
| |
| void IOSChromeMainParts::StartMetricsRecording() { |
| bool isConnectionCellular = net::NetworkChangeNotifier::IsConnectionCellular( |
| net::NetworkChangeNotifier::GetConnectionType()); |
| bool mayUpload = false; |
| if (base::FeatureList::IsEnabled(kUmaCellular)) { |
| if (base::FeatureList::IsEnabled(kFixUmaCellularMetricsRecording)) { |
| mayUpload = true; |
| } else { |
| // This is wrong and should be removed, but the fix is gated by the |
| // feature flag above to measure impact. |
| mayUpload = !isConnectionCellular; |
| } |
| } else { |
| // TODO(crbug.com/1179809): Now that kUmaCellular is default, all references |
| // to kUmaCellular and kMetricsReportingWifiOnly should be removed, but only |
| // after a study of kFixUmaCellularMetricsRecording is completed. |
| bool wifiOnly = local_state_->GetBoolean(prefs::kMetricsReportingWifiOnly); |
| mayUpload = !wifiOnly || !isConnectionCellular; |
| } |
| |
| application_context_->GetMetricsServicesManager()->UpdateUploadPermissions( |
| mayUpload); |
| } |