Chromium Code Reviews| Index: chrome/browser/chromeos/first_run/drive_opt_in_controller.cc |
| diff --git a/chrome/browser/chromeos/first_run/drive_opt_in_controller.cc b/chrome/browser/chromeos/first_run/drive_opt_in_controller.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..30b290692f065950a6c65ae91a48c70dc4f5a85d |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/first_run/drive_opt_in_controller.cc |
| @@ -0,0 +1,327 @@ |
| +// 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_opt_in_controller.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 kDriveOfflineOptInUrl[] = "http://drive.google.com"; |
| + |
| +// Google Drive app id. |
| +const char kDriveHostedAppId[] = "apdfllckaahabafndbhieahigkjlhalf"; |
| + |
| +} // namespace |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// DriveOptInWebContentsManager |
| + |
| +// 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 do sync the user's |
|
achuithb
2013/11/01 19:25:15
"do" unnecessary
Tim Song
2013/11/02 00:13:14
Done.
|
| +// files for offline use. |
| +class DriveOptInWebContentsManager : public content::WebContentsObserver, |
| + public content::WebContentsDelegate, |
| + public content::NotificationObserver { |
| + public: |
| + DriveOptInWebContentsManager(Profile* profile, |
| + DriveOptInController* controller); |
| + ~DriveOptInWebContentsManager(); |
|
achuithb
2013/11/01 19:25:15
virtual
Tim Song
2013/11/02 00:13:14
Done.
|
| + |
| + void StartLoad(); |
| + void StopLoad(); |
| + |
| + private: |
| + void OnOptInSuccess(); |
| + void OnOptInFailure(); |
| + |
| + // 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); |
|
achuithb
2013/11/01 19:25:15
OVERRIDE
Tim Song
2013/11/02 00:13:14
Done.
|
| + |
| + // 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_; |
| + DriveOptInController* controller_; |
| + scoped_ptr<content::WebContents> web_contents_; |
| + content::NotificationRegistrar registrar_; |
| + bool started_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DriveOptInWebContentsManager); |
| +}; |
| + |
| +DriveOptInWebContentsManager::DriveOptInWebContentsManager( |
| + Profile* profile, DriveOptInController* controller) |
| + : profile_(profile), |
| + controller_(controller), |
| + started_(false) { |
| + registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED, |
| + content::Source<Profile>(profile_)); |
| +} |
| + |
| +DriveOptInWebContentsManager::~DriveOptInWebContentsManager() { |
| +} |
| + |
| +void DriveOptInWebContentsManager::StartLoad() { |
| + started_ = true; |
| + GURL url(kDriveOfflineOptInUrl); |
|
achuithb
2013/11/01 19:25:15
const?
Tim Song
2013/11/02 00:13:14
Done.
|
| + 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 DriveOptInWebContentsManager::StopLoad() { |
| + started_ = false; |
| +} |
| + |
| +void DriveOptInWebContentsManager::OnOptInSuccess() { |
| + if (started_) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DriveOptInController::OnOptInSuccess, |
| + base::Unretained(controller_))); |
| + StopLoad(); |
| + } |
| +} |
| + |
| +void DriveOptInWebContentsManager::OnOptInFailure() { |
| + if (started_) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DriveOptInController::OnOptInFailure, |
| + base::Unretained(controller_))); |
| + StopLoad(); |
| + } |
| +} |
| + |
| +void DriveOptInWebContentsManager::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) { |
| + OnOptInFailure(); |
| +} |
| + |
| +void DriveOptInWebContentsManager::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 { |
| + OnOptInFailure(); |
| +} |
| + |
| +// content::WebContentsDelegate overrides: |
|
achuithb
2013/11/01 19:25:15
don't think you need this
Tim Song
2013/11/02 00:13:14
Done.
|
| +bool DriveOptInWebContentsManager::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 { |
|
achuithb
2013/11/01 19:25:15
don't think you need OVERRIDE here
Tim Song
2013/11/02 00:13:14
Done.
|
| + |
| + 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; |
| +} |
| + |
| +// content::NotificationObserver overrides: |
|
achuithb
2013/11/01 19:25:15
don't think you need this
Tim Song
2013/11/02 00:13:14
Done.
|
| +void DriveOptInWebContentsManager::Observe( |
| + int type, |
| + const content::NotificationSource& source, |
| + const content::NotificationDetails& details) OVERRIDE { |
| + if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED) { |
| + std::string app_id = UTF16ToUTF8( |
|
achuithb
2013/11/01 19:25:15
const?
Tim Song
2013/11/02 00:13:14
Done.
|
| + content::Details<BackgroundContentsOpenedDetails>(details) |
| + ->application_id); |
| + if (app_id == kDriveHostedAppId) { |
|
achuithb
2013/11/01 19:25:15
don't need {}
Tim Song
2013/11/02 00:13:14
Done.
|
| + OnOptInSuccess(); |
| + } |
| + } |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// DriveOptInController |
| + |
| +DriveOptInController::DriveOptInController() |
| + : profile_(ProfileManager::GetDefaultProfile()), |
| + initial_delay_done_(false) { |
| +} |
| + |
| +DriveOptInController::~DriveOptInController() { |
| +} |
| + |
| +void DriveOptInController::StartOfflineOptIn() { |
| + if (!initial_delay_done_) { |
| + initial_delay_done_ = true; |
| + initial_delay_timer_.Start( |
| + FROM_HERE, |
| + base::TimeDelta::FromSeconds(kInitialDelaySeconds), |
| + this, |
| + &DriveOptInController::StartOfflineOptIn); |
| + } |
| + |
| + if (!UserManager::Get()->IsLoggedInAsRegularUser()) { |
| + LOG(ERROR) << "Attempting to opt-in but not logged in a regular user."; |
| + OnOptInFailure(); |
| + return; |
| + } |
| + |
| + ExtensionService* extension_service = |
| + extensions::ExtensionSystem::Get(profile_)->extension_service(); |
| + if (!extension_service->GetExtensionById(kDriveHostedAppId, false)) { |
| + LOG(WARNING) << "Drive app not is not installed."; |
| + OnOptInFailure(); |
| + return; |
| + } |
| + |
| + BackgroundContentsService* background_contents_service = |
| + BackgroundContentsServiceFactory::GetForProfile(profile_); |
| + if (background_contents_service->GetAppBackgroundContents( |
| + UTF8ToUTF16(kDriveHostedAppId))) { |
| + LOG(WARNING) << "Background page for Drive app already exists"; |
| + OnOptInFailure(); |
| + return; |
| + } |
| + |
| + web_contents_manager_.reset( |
| + new DriveOptInWebContentsManager(profile_, this)); |
| + web_contents_manager_->StartLoad(); |
| + web_contents_timer_.Start( |
| + FROM_HERE, |
| + base::TimeDelta::FromSeconds(kWebContentsTimeoutSeconds), |
| + this, |
| + &DriveOptInController::OnWebContentsTimedOut); |
| +} |
| + |
| +void DriveOptInController::OnWebContentsTimedOut() { |
| + LOG(WARNING) << "Timed out waiting for web contents to opt-in"; |
| + OnOptInFailure(); |
| +} |
| + |
| +void DriveOptInController::CleanUp() { |
| + if (web_contents_manager_) |
| + web_contents_manager_->StopLoad(); |
| + web_contents_timer_.Stop(); |
| + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| +} |
| + |
| +void DriveOptInController::OnOptInSuccess() { |
| + 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(); |
| +} |
| + |
| +void DriveOptInController::OnOptInFailure() { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + CleanUp(); |
| +} |
| + |
| +} // namespace chromeos |