Commit 634cde89 authored by Sylvain Defresne's avatar Sylvain Defresne Committed by Commit Bot

[ios] Initialize Firebase through the ChromeBrowserProvider

Bug: 1049042
Change-Id: Ib5932f00c2b100aec03240f994370748ff8d2a78
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2065730Reviewed-by: default avatarPeter Lee <pkl@chromium.org>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743162}
parent 7df4f93c
......@@ -11,12 +11,6 @@ import("//ios/build/config.gni")
import("//ios/chrome/features.gni")
import("//ios/public/provider/chrome/browser/build_config.gni")
import("//ios/third_party/features.gni")
import("//ios/third_party/firebase/firebase.gni")
buildflag_header("firebase_buildflags") {
header = "firebase_buildflags.h"
flags = [ "FIREBASE_ENABLED=$ios_enable_firebase_sdk" ]
}
source_set("app") {
configs += [ "//build/config/compiler:enable_arc" ]
......@@ -43,14 +37,12 @@ source_set("unit_tests") {
sources = [
"chrome_overlay_window_testing.h",
"deferred_initialization_runner_unittest.mm",
"firebase_utils_unittest.mm",
"main_application_delegate_unittest.mm",
"tab_opener_unittest.mm",
]
deps = [
":app",
":app_internal",
":firebase_buildflags",
"//base",
"//base/test:test_support",
"//components/metrics",
......@@ -121,8 +113,6 @@ source_set("app_internal") {
sources = [
"chrome_overlay_window.h",
"chrome_overlay_window.mm",
"firebase_utils.h",
"firebase_utils.mm",
"main_application_delegate.h",
"main_application_delegate.mm",
"main_application_delegate_testing.h",
......@@ -138,7 +128,6 @@ source_set("app_internal") {
deps = [
":app",
":firebase_buildflags",
":mode",
":tests_hook",
"//base",
......@@ -328,10 +317,7 @@ ios_app_bundle("chrome") {
"ios_application_icons_target must be defined.")
bundle_deps += [ ios_application_icons_target ]
if (ios_enable_firebase_sdk) {
assert(ios_firebase_resources_target != "",
"ios_firebase_resources_target must be defined if Firebase SDK " +
"is enabled.")
if (ios_firebase_resources_target != "") {
bundle_deps += [ ios_firebase_resources_target ]
}
} else {
......@@ -342,9 +328,6 @@ ios_app_bundle("chrome") {
":main",
":tests_fake_hook",
]
if (ios_enable_firebase_sdk) {
deps += [ "//ios/third_party/firebase" ]
}
if (current_toolchain == default_toolchain) {
if (ios_enable_search_widget_extension) {
......
// Copyright 2018 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.
#ifndef IOS_CHROME_APP_FIREBASE_UTILS_H_
#define IOS_CHROME_APP_FIREBASE_UTILS_H_
// Name of histogram to log whether Firebase SDK is initialized.
extern const char kFirebaseConfiguredHistogramName[];
// Number of days for Ad Conversion Attribution Window. In order to consider
// a first_open event to be associated with a marketing event (e.g. ad_click),
// the first_open event must have happened within this conversion attribution
// window.
extern const int kConversionAttributionWindowInDays;
// Firebase SDK may not be initialized, or initialized during First Run or not
// during First Run.
enum class FirebaseConfiguredState {
// Firebase is not initialized because application is not built with the
// SDK.
kDisabled = 0,
// Firebase is initialized at the app's first session (First Run).
kEnabledFirstRun,
// Firebase is initialized at the app's second or subsequent session.
kEnabledNotFirstRun,
// Firebase Analytics is for installation reporting only. Once a user has
// passed the conversion attribution window, there is nothing to report.
kDisabledConversionWindow,
// Users who installed Chrome prior to incorporating Firebase into Chrome
// should not initialize Firebase because these users could not have been
// the result of any promption campaigns that use Firebase Analytics.
kDisabledLegacyInstallation,
// Count of enum values. Must be equal to the last value above.
kMaxValue = kDisabledLegacyInstallation,
};
// Initializes Firebase SDK if configured and necessary.
void InitializeFirebase(bool is_first_run);
#endif // IOS_CHROME_APP_FIREBASE_UTILS_H_
// Copyright 2018 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/app/firebase_utils.h"
#import <Foundation/Foundation.h>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_service.h"
#import "ios/chrome/app/firebase_buildflags.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
#if BUILDFLAG(FIREBASE_ENABLED)
#import "ios/third_party/firebase/Analytics/FirebaseCore.framework/Headers/FIRApp.h"
#import "ios/third_party/firebase/Analytics/FirebaseCore.framework/Headers/FIRConfiguration.h"
#endif // BUILDFLAG(FIREBASE_ENABLED)
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const char kFirebaseConfiguredHistogramName[] =
"FirstRun.IOSFirebaseConfigured";
const int kConversionAttributionWindowInDays = 90;
void InitializeFirebase(bool is_first_run) {
#if BUILDFLAG(FIREBASE_ENABLED)
PrefService* prefs = GetApplicationContext()->GetLocalState();
const int64_t install_date = prefs->GetInt64(metrics::prefs::kInstallDate);
base::TimeDelta installed_delta =
base::TimeDelta::FromSeconds(base::Time::Now().ToTimeT() - install_date);
// Initialize Firebase SDK only if there is a possibility that user
// installed Chrome as a result of some marketing campaigns.
// 1. If user installed Chrome prior to the first version of Chrome that
// supports Firebase SDK, this is a legacy user who would not be
// attributable to any marketing event. Firebase SDK should not be
// initialized in this case.
// 2. Installation Attribution is the association of a Chrome installation
// to some marketing event. This attribution is valid only if it happens
// within a reasonable timeframe. If installation date is older than
// the acceptable Attribution Window, there is no need to initialize
// Firebase SDK since the first_open event would not be considered for
// attribution to this installation to a marketing event.
FirebaseConfiguredState enabled_state;
auto* provider =
ios::GetChromeBrowserProvider()->GetAppDistributionProvider();
if (provider->IsPreFirebaseLegacyUser(install_date)) {
enabled_state = FirebaseConfiguredState::kDisabledLegacyInstallation;
} else if (installed_delta.InDaysFloored() >=
kConversionAttributionWindowInDays) {
enabled_state = FirebaseConfiguredState::kDisabledConversionWindow;
} else {
[[FIRConfiguration sharedInstance] setLoggerLevel:FIRLoggerLevelMin];
[FIRApp configure];
enabled_state = is_first_run ? FirebaseConfiguredState::kEnabledFirstRun
: FirebaseConfiguredState::kEnabledNotFirstRun;
}
UMA_HISTOGRAM_ENUMERATION(kFirebaseConfiguredHistogramName, enabled_state);
#else
// FIRApp class should not exist if Firebase is not enabled.
DCHECK_EQ(nil, NSClassFromString(@"FIRApp"));
UMA_HISTOGRAM_ENUMERATION(kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kDisabled);
#endif // BUILDFLAG(FIREBASE_ENABLED)
}
// Copyright 2018 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/app/firebase_utils.h"
#import <Foundation/Foundation.h>
#include "base/files/file_path.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_service.h"
#import "ios/chrome/app/firebase_buildflags.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#if BUILDFLAG(FIREBASE_ENABLED)
#import "ios/third_party/firebase/Analytics/FirebaseCore.framework/Headers/FIRApp.h"
#endif // BUILDFLAG(FIREBASE_ENABLED)
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_provider.h"
#include "ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h"
#include "ios/public/provider/chrome/browser/test_chrome_browser_provider.h"
#include "ios/web/public/test/web_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#if BUILDFLAG(FIREBASE_ENABLED)
// This always returns positively that a user is a legacy user (i.e. installed
// Chrome before Chrome supports Firebase).
class LegacyUserAppDistributionProvider : public TestAppDistributionProvider {
public:
bool IsPreFirebaseLegacyUser(int64_t install_date) override { return true; }
};
// LegacyUserChromeBrowserProvider is a fake ChromeBrowserProvider that
// always returns positively that user is a legacy user. This is intended for
// testing the case of legacy users.
// NOTE: The default TestChromeBrowserProvider for unit tests always treats
// a user as a new user.
class LegacyUserChromeBrowserProvider : public ios::TestChromeBrowserProvider {
public:
LegacyUserChromeBrowserProvider()
: app_distribution_provider_(
std::make_unique<LegacyUserAppDistributionProvider>()) {}
~LegacyUserChromeBrowserProvider() override {}
AppDistributionProvider* GetAppDistributionProvider() const override {
return app_distribution_provider_.get();
}
private:
std::unique_ptr<LegacyUserAppDistributionProvider> app_distribution_provider_;
DISALLOW_COPY_AND_ASSIGN(LegacyUserChromeBrowserProvider);
};
#endif // BUILDFLAG(FIREBASE_ENABLED)
class FirebaseUtilsTest : public PlatformTest {
public:
FirebaseUtilsTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
}
void SetUp() override {
PlatformTest::SetUp();
#if BUILDFLAG(FIREBASE_ENABLED)
firapp_ = OCMClassMock([FIRApp class]);
#endif // BUILDFLAG(FIREBASE_ENABLED)
SetInstallDate(base::Time::Now().ToTimeT());
}
void TearDown() override {
[firapp_ stopMocking];
PlatformTest::TearDown();
}
void SetInstallDate(int64_t install_date) {
GetApplicationContext()->GetLocalState()->SetInt64(
metrics::prefs::kInstallDate, install_date);
}
protected:
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
web::WebTaskEnvironment task_environment_;
id firapp_;
base::HistogramTester histogram_tester_;
};
#if BUILDFLAG(FIREBASE_ENABLED)
TEST_F(FirebaseUtilsTest, EnabledInitializedHistogramFirstRun) {
// Expects Firebase SDK initialization to be called.
[[firapp_ expect] configure];
InitializeFirebase(/*is_first_run=*/true);
histogram_tester_.ExpectUniqueSample(
kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kEnabledFirstRun, 1);
EXPECT_OCMOCK_VERIFY(firapp_);
}
TEST_F(FirebaseUtilsTest, EnabledInitializedHistogramNotFirstRun) {
// Expects Firebase SDK initialization to be called.
[[firapp_ expect] configure];
InitializeFirebase(/*is_first_run=*/false);
histogram_tester_.ExpectUniqueSample(
kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kEnabledNotFirstRun, 1);
EXPECT_OCMOCK_VERIFY(firapp_);
}
TEST_F(FirebaseUtilsTest, DisabledInitializedOutsideWindow) {
// Set an app install date that is older than the conversion attribution
// window.
int64_t before_attribution =
base::TimeDelta::FromDays(kConversionAttributionWindowInDays + 1)
.InSeconds();
SetInstallDate(base::Time::Now().ToTimeT() - before_attribution);
// Expects Firebase SDK initialization to be not called.
[[firapp_ reject] configure];
InitializeFirebase(/*is_first_run=*/false);
histogram_tester_.ExpectUniqueSample(
kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kDisabledConversionWindow, 1);
EXPECT_OCMOCK_VERIFY(firapp_);
}
TEST_F(FirebaseUtilsTest, DisabledLegacyInstallation) {
// Sets up the ChromeProviderEnvironment with a fake provider that responds
// positively that user is a legacy user predating Firebase integration.
// Installation date is irrelevant in this case.
IOSChromeScopedTestingChromeBrowserProvider provider(
std::make_unique<LegacyUserChromeBrowserProvider>());
// Expects Firebase SDK initialization to be not called.
[[firapp_ reject] configure];
InitializeFirebase(/*is_first_run=*/true);
histogram_tester_.ExpectUniqueSample(
kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kDisabledLegacyInstallation, 1);
EXPECT_OCMOCK_VERIFY(firapp_);
}
#else
TEST_F(FirebaseUtilsTest, DisabledInitializedHistogram) {
// FIRApp class should not exist if Firebase is not enabled.
ASSERT_EQ(nil, NSClassFromString(@"FIRApp"));
InitializeFirebase(/*is_first_run=*/false);
histogram_tester_.ExpectUniqueSample(kFirebaseConfiguredHistogramName,
FirebaseConfiguredState::kDisabled, 1);
}
#endif // BUILDFLAG(FIREBASE_ENABLED)
......@@ -46,7 +46,6 @@
#import "ios/chrome/app/application_delegate/url_opener.h"
#include "ios/chrome/app/application_mode.h"
#import "ios/chrome/app/deferred_initialization_runner.h"
#import "ios/chrome/app/firebase_utils.h"
#import "ios/chrome/app/main_controller_private.h"
#import "ios/chrome/app/memory_monitor.h"
#import "ios/chrome/app/spotlight/spotlight_manager.h"
......@@ -922,12 +921,18 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
block:^{
auto URLLoaderFactory =
self.mainBrowserState->GetSharedURLLoaderFactory();
bool is_first_run = FirstRun::IsChromeFirstRun();
const bool is_first_run = FirstRun::IsChromeFirstRun();
ios::GetChromeBrowserProvider()
->GetAppDistributionProvider()
->ScheduleDistributionNotifications(URLLoaderFactory,
is_first_run);
InitializeFirebase(is_first_run);
const int64_t install_date =
GetApplicationContext()->GetLocalState()->GetInt64(
metrics::prefs::kInstallDate);
ios::GetChromeBrowserProvider()
->GetAppDistributionProvider()
->InitializeFirebase(install_date, is_first_run);
}];
}
......
......@@ -6,7 +6,6 @@ import("//ios/build/config.gni")
import("//ios/chrome/features.gni")
import("//ios/public/provider/chrome/browser/build_config.gni")
import("//ios/third_party/features.gni")
import("//ios/third_party/firebase/firebase.gni")
import("//testing/test.gni")
# All tests needs to be listed in that target to be built as part of
......@@ -314,10 +313,6 @@ test("ios_chrome_unittests") {
"//ios/chrome/search_widget_extension:unit_tests",
"//ios/testing:http_server_bundle_data",
]
if (ios_enable_firebase_sdk) {
# This is required here so Firebase SDK can supply ldflags to app bundle.
deps += [ "//ios/third_party/firebase" ]
}
assert_no_deps = ios_assert_no_deps
}
......@@ -7,7 +7,6 @@ import("//build/mac/tweak_info_plist.gni")
import("//ios/build/chrome_build.gni")
import("//ios/public/provider/chrome/browser/build_config.gni")
import("//ios/third_party/earl_grey/ios_eg_test.gni")
import("//ios/third_party/firebase/firebase.gni")
# Template wrapping ios_eg_test, setting default values for EarlGrey test
# based on //ios/chrome/app:chrome.
......@@ -126,10 +125,6 @@ template("chrome_ios_eg_test") {
"//ios/chrome/app:main",
"//ios/testing:http_server_bundle_data",
]
if (ios_enable_firebase_sdk) {
# This is required here so Firebase SDK can supply ldflags to app bundle.
deps += [ "//ios/third_party/firebase" ]
}
if (defined(invoker.hooks_target)) {
assert(invoker.hooks_target != "",
"hooks_target must be non-empty if defined")
......
......@@ -26,4 +26,15 @@ declare_args() {
# Label of the target providing implementation for ChromeBrowserProvider.
# Overridden when using the Google-internal repository to build Chrome on iOS.
ios_provider_target = "//ios/chrome/browser/providers:provider_factory"
# This enables the linking of Firebase SDK to Chrome for iOS. If Firebase SDK
# is enabled, ios_firebase_plist_path must be defined as a non-empty string.
# This variable is obsolete and ignored. It will be removed once it is no
# longer overriden downstream.
ios_enable_firebase_sdk = false
# This defines the build target to include a valid GoogleService-Info.plist
# file which is copied to the application bundle. This is used by Firebase
# SDK (which comes from the internal framework).
ios_firebase_resources_target = ""
}
......@@ -7,7 +7,6 @@ import("//ios/build/chrome_build.gni")
import("//ios/build/config.gni")
import("//ios/third_party/earl_grey/ios_eg_test.gni")
import("//ios/third_party/earl_grey2/ios_eg2_test.gni")
import("//ios/third_party/firebase/firebase.gni")
ios_app_bundle("showcase") {
info_plist = "core/Info.plist"
......@@ -62,13 +61,6 @@ ios_eg2_test_app_host("ios_showcase_eg2tests") {
"//ios/third_party/gtx:gtx+bundle",
]
# TODO(crbug.com/870935): This dependency of ios_showcase_egtests on
# Firebase SDK seems unnecessary. There must be some transitive
# dependencies that eventually led to ios/chrome/app:app_internal.
if (ios_enable_firebase_sdk) {
deps += [ "//ios/third_party/firebase" ]
}
assert_no_deps = ios_assert_no_deps
}
......@@ -104,12 +96,6 @@ ios_eg_test("ios_showcase_egtests") {
"//ios/showcase/text_badge_view:eg_tests",
]
# TODO(crbug.com/870935): This dependency of ios_showcase_egtests on
# Firebase SDK seems unnecessary. There must be some transitive
# dependencies that eventually led to ios/chrome/app:app_internal.
if (ios_enable_firebase_sdk) {
deps += [ "//ios/third_party/firebase" ]
}
bundle_deps = [ "//ios/showcase/core/resources" ]
assert_no_deps = ios_assert_no_deps
}
......@@ -3,7 +3,8 @@
# found in the LICENSE file.
import("//build/config/ios/rules.gni")
import("//ios/third_party/firebase/firebase.gni")
import("//ios/build/chrome_build.gni")
import("//ios/public/provider/chrome/browser/build_config.gni")
# EarlGrey tests are just XCTests that also depends on EarlGrey.
template("ios_eg_test") {
......@@ -17,10 +18,7 @@ template("ios_eg_test") {
"//ios/third_party/gtx:gtx+bundle",
"//ios/third_party/ochamcrest:ochamcrest+bundle",
]
if (ios_enable_firebase_sdk) {
assert(ios_firebase_resources_target != "",
"ios_firebase_resources_target must be defined if Firebase SDK " +
"is enabled.")
if (ios_firebase_resources_target != "") {
bundle_deps += [ ios_firebase_resources_target ]
}
if (!defined(deps)) {
......
......@@ -3,7 +3,8 @@
# found in the LICENSE file.
import("//build/config/ios/rules.gni")
import("//ios/third_party/firebase/firebase.gni")
import("//ios/build/chrome_build.gni")
import("//ios/public/provider/chrome/browser/build_config.gni")
template("ios_eg2_test_app_host") {
ios_app_bundle(target_name) {
......@@ -16,10 +17,7 @@ template("ios_eg2_test_app_host") {
bundle_deps = []
}
bundle_deps += [ "//ios/third_party/earl_grey2:app_framework+bundle" ]
if (ios_enable_firebase_sdk) {
assert(ios_firebase_resources_target != "",
"ios_firebase_resources_target must be defined if Firebase SDK " +
"is enabled.")
if (ios_firebase_resources_target != "") {
bundle_deps += [ ios_firebase_resources_target ]
}
......@@ -27,9 +25,6 @@ template("ios_eg2_test_app_host") {
deps = []
}
deps += [ "//ios/third_party/earl_grey2:app_framework+link" ]
if (ios_enable_firebase_sdk) {
deps += [ "//ios/third_party/firebase" ]
}
# Xcode needs the following frameworks installed in the application (and
# signed) for the XCTest to run, so install them using
......
# Copyright 2018 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.
declare_args() {
# This enables the linking of Firebase SDK to Chrome for iOS. If Firebase SDK
# is enabled, ios_firebase_plist_path must be defined as a non-empty string.
ios_enable_firebase_sdk = false
# This defines the build target to include a valid GoogleService-Info.plist
# file which is copied to the application bundle. This must not be an empty
# string if ios_enable_firebase_sdk is true.
ios_firebase_resources_target = ""
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment