Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3795)

Unified Diff: chrome/browser/chromeos/first_run/drive_first_run_controller.cc

Issue 55423004: Enable Google Drive offline mode automatically on first run. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/first_run/drive_first_run_controller.cc
diff --git a/chrome/browser/chromeos/first_run/drive_first_run_controller.cc b/chrome/browser/chromeos/first_run/drive_first_run_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..73b02f7c6e8441eb7f8474f31110001093ba93d4
--- /dev/null
+++ b/chrome/browser/chromeos/first_run/drive_first_run_controller.cc
@@ -0,0 +1,335 @@
+// Copyright 2013 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/chromeos/first_run/drive_first_run_controller.h"
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/background/background_contents_service.h"
+#include "chrome/browser/background/background_contents_service_factory.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/tab_contents/background_contents.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+
+namespace {
+
+// The initial time to wait in seconds before starting the opt-in.
+const int kInitialDelaySeconds = 180;
+
+// Time to wait for Drive app background page to come up before giving up.
+const int kWebContentsTimeoutSeconds = 15;
+
+// Google Drive offline opt-in endpoint.
+const char kDriveOfflineEndpointUrl[] = "http://drive.google.com";
+
+// Google Drive app id.
+const char kDriveHostedAppId[] = "apdfllckaahabafndbhieahigkjlhalf";
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// DriveWebContentsManager
+
+// Manages web contents that does Google Drive offline opt-in. We create
+// a background WebContents that loads a Drive endpoint to initialize offline
+// mode. If successful, a background page will be opened to sync the user's
+// files for offline use.
+class DriveWebContentsManager : public content::WebContentsObserver,
+ public content::WebContentsDelegate,
+ public content::NotificationObserver {
+ public:
+ typedef base::Callback<void(bool)> CompletionCallback;
+
+ DriveWebContentsManager(Profile* profile,
+ CompletionCallback completion_callback);
achuithb 2013/11/05 01:30:47 const CompletionCallback&
Tim Song 2013/11/05 01:49:51 Done.
+ virtual ~DriveWebContentsManager();
+
+ // Start loading the WebContents for the endpoint in the context of the Drive
+ // hosted app that will initialize offline mode and open a background page.
+ void StartLoad();
+
+ // Stop loading the endpoint. The |completion_callback| will not be called.
+ void StopLoad();
+
+ private:
+ // Called when when the WebContents load succeeds or fails and schedules
+ // |RunCompletionCallback|.
+ void OnOptInDone(bool success);
achuithb 2013/11/05 01:30:47 OnWebContentsLoaded?
Tim Song 2013/11/05 01:49:51 Ah sorry, that comment is a bit inaccurate. Rename
+
+ // Runs |completion_callback|.
+ void RunCompletionCallback(bool success);
+
+ // content::WebContentsObserver overrides:
+ virtual void DidFailProvisionalLoad(
+ int64 frame_id,
+ const string16& frame_unique_name,
+ bool is_main_frame,
+ const GURL& validated_url,
+ int error_code,
+ const string16& error_description,
+ content::RenderViewHost* render_view_host) OVERRIDE;
+
+ virtual void DidFailLoad(int64 frame_id,
+ const GURL& validated_url,
+ bool is_main_frame,
+ int error_code,
+ const string16& error_description,
+ content::RenderViewHost* render_view_host) OVERRIDE;
+
+ // content::WebContentsDelegate overrides:
+ virtual bool ShouldCreateWebContents(
+ content::WebContents* web_contents,
+ int route_id,
+ WindowContainerType window_container_type,
+ const string16& frame_name,
+ const GURL& target_url,
+ const std::string& partition_id,
+ content::SessionStorageNamespace* session_storage_namespace) OVERRIDE;
+
+ // content::NotificationObserver overrides:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ Profile* profile_;
+ scoped_ptr<content::WebContents> web_contents_;
+ content::NotificationRegistrar registrar_;
+ bool started_;
+ CompletionCallback completion_callback_;
+ base::WeakPtrFactory<DriveWebContentsManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveWebContentsManager);
+};
+
+DriveWebContentsManager::DriveWebContentsManager(
+ Profile* profile,
+ CompletionCallback completion_callback)
+ : profile_(profile),
+ started_(false),
+ completion_callback_(completion_callback),
+ weak_ptr_factory_(this) {
+ DCHECK(!completion_callback_.is_null());
+ registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
+ content::Source<Profile>(profile_));
+}
+
+DriveWebContentsManager::~DriveWebContentsManager() {
+}
+
+void DriveWebContentsManager::StartLoad() {
+ started_ = true;
+ const GURL url(kDriveOfflineEndpointUrl);
+ content::WebContents::CreateParams create_params(
+ profile_, content::SiteInstance::CreateForURL(profile_, url));
+
+ web_contents_.reset(content::WebContents::Create(create_params));
+ web_contents_->SetDelegate(this);
+
+ content::NavigationController::LoadURLParams load_params(url);
+ load_params.transition_type = content::PAGE_TRANSITION_GENERATED;
+ web_contents_->GetController().LoadURLWithParams(load_params);
+
+ content::WebContentsObserver::Observe(web_contents_.get());
+}
+
+void DriveWebContentsManager::StopLoad() {
+ started_ = false;
+}
+
+void DriveWebContentsManager::OnOptInDone(bool success) {
+ if (started_) {
+ // We postpone notifying the controller as we may be in the middle
+ // of a call stack for some routine of the contained WebContents.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&DriveWebContentsManager::RunCompletionCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ success));
+ StopLoad();
+ }
+}
+
+void DriveWebContentsManager::RunCompletionCallback(bool success) {
+ completion_callback_.Run(success);
+}
+
+void DriveWebContentsManager::DidFailProvisionalLoad(
+ int64 frame_id,
+ const string16& frame_unique_name,
+ bool is_main_frame,
+ const GURL& validated_url,
+ int error_code,
+ const string16& error_description,
+ content::RenderViewHost* render_view_host) {
+ OnOptInDone(false);
+}
+
+void DriveWebContentsManager::DidFailLoad(
+ int64 frame_id,
+ const GURL& validated_url,
+ bool is_main_frame,
+ int error_code,
+ const string16& error_description,
+ content::RenderViewHost* render_view_host) {
+ OnOptInDone(false);
+}
+
+bool DriveWebContentsManager::ShouldCreateWebContents(
+ content::WebContents* web_contents,
+ int route_id,
+ WindowContainerType window_container_type,
+ const string16& frame_name,
+ const GURL& target_url,
+ const std::string& partition_id,
+ content::SessionStorageNamespace* session_storage_namespace) {
+
+ if (window_container_type == WINDOW_CONTAINER_TYPE_NORMAL)
+ return true;
+
+ // Check that the target URL is for the Drive app.
+ ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ const extensions::Extension *extension =
+ service->GetInstalledApp(target_url);
+ if (!extension || extension->id() != kDriveHostedAppId)
+ return true;
+
+ // The background contents creation is normally done in Browser, but
+ // because we're using a detached WebContents, we need to do it ourselves.
+ BackgroundContentsService* background_contents_service =
+ BackgroundContentsServiceFactory::GetForProfile(profile_);
+
+ // Prevent redirection if background contents already exists.
+ if (background_contents_service->GetAppBackgroundContents(
+ UTF8ToUTF16(kDriveHostedAppId))) {
+ return false;
+ }
+ BackgroundContents* contents = background_contents_service
+ ->CreateBackgroundContents(content::SiteInstance::Create(profile_),
+ route_id,
+ profile_,
+ frame_name,
+ ASCIIToUTF16(kDriveHostedAppId),
+ partition_id,
+ session_storage_namespace);
+
+ contents->web_contents()->GetController().LoadURL(
+ target_url,
+ content::Referrer(),
+ content::PAGE_TRANSITION_LINK,
+ std::string());
+
+ // Return false as we already created the WebContents here.
+ return false;
+}
+
+void DriveWebContentsManager::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED) {
+ const std::string app_id = UTF16ToUTF8(
+ content::Details<BackgroundContentsOpenedDetails>(details)
+ ->application_id);
+ if (app_id == kDriveHostedAppId)
+ OnOptInDone(true);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DriveFirstRunController
+
+DriveFirstRunController::DriveFirstRunController()
+ : profile_(ProfileManager::GetDefaultProfile()),
+ started_(false) {
+}
+
+DriveFirstRunController::~DriveFirstRunController() {
+}
+
+void DriveFirstRunController::EnableOfflineMode() {
+ if (!started_) {
+ started_ = true;
+ initial_delay_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(kInitialDelaySeconds),
+ this,
+ &DriveFirstRunController::EnableOfflineMode);
+ }
+
+ if (!UserManager::Get()->IsLoggedInAsRegularUser()) {
+ LOG(ERROR) << "Attempting to enable offline access "
+ "but not logged in a regular user.";
+ OnOptInDone(false);
+ return;
+ }
+
+ ExtensionService* extension_service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ if (!extension_service->GetExtensionById(kDriveHostedAppId, false)) {
+ LOG(WARNING) << "Drive app is not installed.";
+ OnOptInDone(false);
+ return;
+ }
+
+ BackgroundContentsService* background_contents_service =
+ BackgroundContentsServiceFactory::GetForProfile(profile_);
+ if (background_contents_service->GetAppBackgroundContents(
+ UTF8ToUTF16(kDriveHostedAppId))) {
+ LOG(WARNING) << "Background page for Drive app already exists";
+ OnOptInDone(false);
+ return;
+ }
+
+ web_contents_manager_.reset(new DriveWebContentsManager(
+ profile_,
+ base::Bind(&DriveFirstRunController::OnOptInDone,
+ base::Unretained(this))));
+ web_contents_manager_->StartLoad();
+ web_contents_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(kWebContentsTimeoutSeconds),
+ this,
+ &DriveFirstRunController::OnWebContentsTimedOut);
+}
+
+void DriveFirstRunController::OnWebContentsTimedOut() {
+ LOG(WARNING) << "Timed out waiting for web contents to opt-in";
+ OnOptInDone(false);
+}
+
+void DriveFirstRunController::CleanUp() {
+ if (web_contents_manager_)
+ web_contents_manager_->StopLoad();
+ web_contents_timer_.Stop();
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void DriveFirstRunController::OnOptInDone(bool success) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // TODO(tengs): Notify ash system tray to display notice that Drive
+ // offline opt-in has occurred. See crbug.com/311452.
+ CleanUp();
+}
+
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698