[Offline Pages] Add an observer for requests in the CCT namespace.

This will allow us to track when requests finish.  Adds a hook so that
internal code can have different behavior.  See bug for details.

BUG=720782

Review-Url: https://codereview.chromium.org/2888273003
Cr-Commit-Position: refs/heads/master@{#473994}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index b14babe..b17d2a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -9,6 +9,7 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
@@ -28,6 +29,7 @@
 import org.chromium.chrome.browser.metrics.VariationsSession;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.net.qualityprovider.ExternalEstimateProviderAndroid;
+import org.chromium.chrome.browser.offlinepages.CCTRequestStatus;
 import org.chromium.chrome.browser.omaha.RequestGenerator;
 import org.chromium.chrome.browser.physicalweb.PhysicalWebBleClient;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
@@ -291,4 +293,13 @@
     public boolean shouldDetectVideoFullscreen() {
         return false;
     }
+
+    /**
+     * @return A callback that will be run each time an offline page is saved in the custom tabs
+     * namespace.
+     */
+    @CalledByNative
+    public Callback<CCTRequestStatus> getOfflinePagesCCTRequestDoneCallback() {
+        return null;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java
new file mode 100644
index 0000000..766cf40
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java
@@ -0,0 +1,44 @@
+// 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.
+
+package org.chromium.chrome.browser.offlinepages;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * Class used to propagate the final status of CCT offline pages requests.
+ */
+public class CCTRequestStatus {
+    private CCTRequestStatus(int savePageResult, String savedPageId) {
+        mSavePageResult = savePageResult;
+        mSavedPageId = savedPageId;
+    }
+
+    private int mSavePageResult;
+    private String mSavedPageId;
+
+    /**
+     * @return An org.chromium.components.offlinepages.BackgroundSavePageResult for this request.
+     */
+    public int getSavePageResult() {
+        return mSavePageResult;
+    }
+
+    /**
+     * @return The ID string extracted from the ClientId of the page.
+     */
+    public String getSavedPageId() {
+        return mSavedPageId;
+    }
+
+    /**
+     * Creates a request status object.
+     * @param result an offlinepages.BackgroundSavePageResult
+     * @param savedPageId the ID of the page that was completed.
+     */
+    @CalledByNative
+    public static CCTRequestStatus create(int result, String savedPageId) {
+        return new CCTRequestStatus(result, savedPageId);
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 4f54e6b..ad727240 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -669,6 +669,7 @@
   "java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsConfig.java",
   "java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java",
   "java/src/org/chromium/chrome/browser/ntp/snippets/SuggestionsSource.java",
+  "java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java",
   "java/src/org/chromium/chrome/browser/offlinepages/DeviceConditions.java",
   "java/src/org/chromium/chrome/browser/offlinepages/BackgroundOfflinerTask.java",
   "java/src/org/chromium/chrome/browser/offlinepages/BackgroundGcmScheduler.java",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index cbd8c2d9..0458f18 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2137,6 +2137,8 @@
     sources += [
       "android/offline_pages/background_scheduler_bridge.cc",
       "android/offline_pages/background_scheduler_bridge.h",
+      "android/offline_pages/cct_request_observer.cc",
+      "android/offline_pages/cct_request_observer.h",
       "android/offline_pages/downloads/offline_page_download_bridge.cc",
       "android/offline_pages/downloads/offline_page_download_bridge.h",
       "android/offline_pages/downloads/offline_page_infobar_delegate.cc",
@@ -4234,6 +4236,7 @@
       "../android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java",
       "../android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java",
       "../android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java",
+      "../android/java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java",
       "../android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
       "../android/java/src/org/chromium/chrome/browser/offlinepages/SavePageRequest.java",
       "../android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java",
diff --git a/chrome/browser/android/app_hooks.cc b/chrome/browser/android/app_hooks.cc
index e11f428..75c4742d 100644
--- a/chrome/browser/android/app_hooks.cc
+++ b/chrome/browser/android/app_hooks.cc
@@ -23,5 +23,13 @@
   return Java_AppHooks_shouldDetectVideoFullscreen(env, app_hooks_obj);
 }
 
+ScopedJavaLocalRef<jobject> AppHooks::GetOfflinePagesCCTRequestDoneCallback() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> app_hooks_obj = Java_AppHooks_get(env);
+
+  return Java_AppHooks_getOfflinePagesCCTRequestDoneCallback(env,
+                                                             app_hooks_obj);
+}
+
 }  // namespace android
 }  // namespace chrome
diff --git a/chrome/browser/android/app_hooks.h b/chrome/browser/android/app_hooks.h
index 9cf0adf..bbb2e8e 100644
--- a/chrome/browser/android/app_hooks.h
+++ b/chrome/browser/android/app_hooks.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ANDROID_APP_HOOKS_H_
 
 #include <jni.h>
+#include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 
 namespace chrome {
@@ -14,6 +15,8 @@
 class AppHooks {
  public:
   static bool ShouldDetectVideoFullscreen();
+  static base::android::ScopedJavaLocalRef<jobject>
+  GetOfflinePagesCCTRequestDoneCallback();
 
  private:
   AppHooks();
diff --git a/chrome/browser/android/offline_pages/cct_request_observer.cc b/chrome/browser/android/offline_pages/cct_request_observer.cc
new file mode 100644
index 0000000..e1f088da
--- /dev/null
+++ b/chrome/browser/android/offline_pages/cct_request_observer.cc
@@ -0,0 +1,70 @@
+// 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.
+
+#include "chrome/browser/android/offline_pages/cct_request_observer.h"
+
+#include "base/android/callback_android.h"
+#include "base/android/jni_int_wrapper.h"
+#include "base/android/jni_string.h"
+#include "chrome/browser/android/app_hooks.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "jni/CCTRequestStatus_jni.h"
+
+namespace offline_pages {
+namespace {
+int kCCTRequestObserverUserDataKey;
+}  // namespace
+
+using chrome::android::AppHooks;
+
+// static
+void CCTRequestObserver::AttachToRequestCoordinator(
+    RequestCoordinator* coordinator) {
+  if (!IsOfflinePagesCTEnabled())
+    return;
+
+  base::android::ScopedJavaLocalRef<jobject> callback =
+      AppHooks::GetOfflinePagesCCTRequestDoneCallback();
+  if (!callback.obj())
+    return;
+
+  auto request_observer = base::WrapUnique(new CCTRequestObserver(callback));
+  coordinator->AddObserver(request_observer.get());
+  coordinator->SetUserData(&kCCTRequestObserverUserDataKey,
+                           std::move(request_observer));
+}
+
+CCTRequestObserver::~CCTRequestObserver() = default;
+
+CCTRequestObserver::CCTRequestObserver(
+    base::android::ScopedJavaLocalRef<jobject> callback) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  j_callback_.Reset(env, callback.obj());
+}
+
+void CCTRequestObserver::OnAdded(const SavePageRequest& request) {}
+
+void CCTRequestObserver::OnCompleted(
+    const SavePageRequest& request,
+    RequestNotifier::BackgroundSavePageResult status) {
+  if (request.client_id().name_space != kCCTNamespace) {
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jobject> callback_info =
+      Java_CCTRequestStatus_create(
+          env, as_jint(static_cast<int>(status)),
+          base::android::ConvertUTF8ToJavaString(env, request.client_id().id));
+
+  base::android::RunCallbackAndroid(j_callback_, callback_info);
+}
+
+void CCTRequestObserver::OnChanged(const SavePageRequest& request) {}
+
+void CCTRequestObserver::OnNetworkProgress(const SavePageRequest& request,
+                                           int64_t received_bytes) {}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/android/offline_pages/cct_request_observer.h b/chrome/browser/android/offline_pages/cct_request_observer.h
new file mode 100644
index 0000000..dbe22bb
--- /dev/null
+++ b/chrome/browser/android/offline_pages/cct_request_observer.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CHROME_BROWSER_ANDROID_OFFLINE_PAGES_CCT_REQUEST_OBSERVER_H_
+#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_CCT_REQUEST_OBSERVER_H_
+
+#include <stdint.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_weak_ref.h"
+#include "base/macros.h"
+#include "base/supports_user_data.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
+
+namespace offline_pages {
+
+/**
+ * Bridge between C++ and Java for exposing when CCT offlining requests
+ * complete.
+ */
+class CCTRequestObserver : public RequestCoordinator::Observer,
+                           public base::SupportsUserData::Data {
+ public:
+  static void AttachToRequestCoordinator(RequestCoordinator* coordinator);
+  ~CCTRequestObserver() override;
+
+  // RequestCoordinator::Observer implementation.
+  void OnAdded(const SavePageRequest& request) override;
+  void OnCompleted(const SavePageRequest& request,
+                   RequestNotifier::BackgroundSavePageResult status) override;
+  void OnChanged(const SavePageRequest& request) override;
+  void OnNetworkProgress(const SavePageRequest& request,
+                         int64_t received_bytes) override;
+
+ private:
+  explicit CCTRequestObserver(
+      base::android::ScopedJavaLocalRef<jobject> callback);
+
+  base::android::ScopedJavaGlobalRef<jobject> j_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CCTRequestObserver);
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_CCT_REQUEST_OBSERVER_H_
diff --git a/chrome/browser/android/offline_pages/request_coordinator_factory.cc b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
index 2fd870b..53bd4ca 100644
--- a/chrome/browser/android/offline_pages/request_coordinator_factory.cc
+++ b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
@@ -11,6 +11,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
+#include "chrome/browser/android/offline_pages/cct_request_observer.h"
 #include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/prerendering_offliner.h"
@@ -89,6 +90,8 @@
       request_coordinator,
       base::MakeUnique<android::OfflinePageNotificationBridge>());
 
+  CCTRequestObserver::AttachToRequestCoordinator(request_coordinator);
+
   return request_coordinator;
 }