This source file includes following definitions.
- Display
- Error
- Close
- Click
- ButtonClick
- weak_ptr_factory_
- StartLoad
- StopLoad
- OnOfflineInit
- RunCompletionCallback
- DidFailProvisionalLoad
- DidFailLoad
- ShouldCreateWebContents
- Observe
- drive_hosted_app_id_
- EnableOfflineMode
- AddObserver
- RemoveObserver
- SetDelaysForTest
- SetAppInfoForTest
- OnWebContentsTimedOut
- CleanUp
- OnOfflineInit
- ShowNotification
#include "chrome/browser/chromeos/first_run/drive_first_run_controller.h"
#include "ash/shell.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.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/chrome_extension_web_contents_observer.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/tab_contents/background_contents.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "chrome/browser/ui/singleton_tabs.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 "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_delegate.h"
#include "url/gurl.h"
namespace chromeos {
namespace {
int kInitialDelaySeconds = 180;
int kWebContentsTimeoutSeconds = 15;
const char kDriveOfflineEndpointUrl[] =
"https://docs.google.com/offline/autoenable";
const char kDriveHostedAppId[] = "apdfllckaahabafndbhieahigkjlhalf";
const char kDriveOfflineNotificationId[] = "chrome://drive/enable-offline";
const char kDriveOfflineSupportUrl[] =
"https://support.google.com/drive/answer/1628467";
}
class DriveOfflineNotificationDelegate
: public message_center::NotificationDelegate {
public:
explicit DriveOfflineNotificationDelegate(Profile* profile)
: profile_(profile) {}
virtual void Display() OVERRIDE {}
virtual void Error() OVERRIDE {}
virtual void Close(bool by_user) OVERRIDE {}
virtual void Click() OVERRIDE {}
virtual void ButtonClick(int button_index) OVERRIDE;
protected:
virtual ~DriveOfflineNotificationDelegate() {}
private:
Profile* profile_;
DISALLOW_COPY_AND_ASSIGN(DriveOfflineNotificationDelegate);
};
void DriveOfflineNotificationDelegate::ButtonClick(int button_index) {
DCHECK_EQ(0, button_index);
const GURL url = GURL(kDriveOfflineSupportUrl);
chrome::ScopedTabbedBrowserDisplayer displayer(
profile_,
chrome::HOST_DESKTOP_TYPE_ASH);
chrome::ShowSingletonTabOverwritingNTP(
displayer.browser(),
chrome::GetSingletonTabNavigateParams(displayer.browser(), url));
}
class DriveWebContentsManager : public content::WebContentsObserver,
public content::WebContentsDelegate,
public content::NotificationObserver {
public:
typedef base::Callback<
void(bool, DriveFirstRunController::UMAOutcome)> CompletionCallback;
DriveWebContentsManager(Profile* profile,
const std::string& app_id,
const std::string& endpoint_url,
const CompletionCallback& completion_callback);
virtual ~DriveWebContentsManager();
void StartLoad();
void StopLoad();
private:
void OnOfflineInit(bool success,
DriveFirstRunController::UMAOutcome outcome);
void RunCompletionCallback(bool success,
DriveFirstRunController::UMAOutcome outcome);
virtual void DidFailProvisionalLoad(
int64 frame_id,
const base::string16& frame_unique_name,
bool is_main_frame,
const GURL& validated_url,
int error_code,
const base::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 base::string16& error_description,
content::RenderViewHost* render_view_host) OVERRIDE;
virtual bool ShouldCreateWebContents(
content::WebContents* web_contents,
int route_id,
WindowContainerType window_container_type,
const base::string16& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) OVERRIDE;
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
Profile* profile_;
const std::string app_id_;
const std::string endpoint_url_;
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,
const std::string& app_id,
const std::string& endpoint_url,
const CompletionCallback& completion_callback)
: profile_(profile),
app_id_(app_id),
endpoint_url_(endpoint_url),
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(endpoint_url_);
content::WebContents::CreateParams create_params(
profile_, content::SiteInstance::CreateForURL(profile_, url));
web_contents_.reset(content::WebContents::Create(create_params));
web_contents_->SetDelegate(this);
extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
web_contents_.get());
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::OnOfflineInit(
bool success,
DriveFirstRunController::UMAOutcome outcome) {
if (started_) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&DriveWebContentsManager::RunCompletionCallback,
weak_ptr_factory_.GetWeakPtr(),
success,
outcome));
StopLoad();
}
}
void DriveWebContentsManager::RunCompletionCallback(
bool success,
DriveFirstRunController::UMAOutcome outcome) {
completion_callback_.Run(success, outcome);
}
void DriveWebContentsManager::DidFailProvisionalLoad(
int64 frame_id,
const base::string16& frame_unique_name,
bool is_main_frame,
const GURL& validated_url,
int error_code,
const base::string16& error_description,
content::RenderViewHost* render_view_host) {
if (is_main_frame) {
LOG(WARNING) << "Failed to load WebContents to enable offline mode.";
OnOfflineInit(false,
DriveFirstRunController::OUTCOME_WEB_CONTENTS_LOAD_FAILED);
}
}
void DriveWebContentsManager::DidFailLoad(
int64 frame_id,
const GURL& validated_url,
bool is_main_frame,
int error_code,
const base::string16& error_description,
content::RenderViewHost* render_view_host) {
if (is_main_frame) {
LOG(WARNING) << "Failed to load WebContents to enable offline mode.";
OnOfflineInit(false,
DriveFirstRunController::OUTCOME_WEB_CONTENTS_LOAD_FAILED);
}
}
bool DriveWebContentsManager::ShouldCreateWebContents(
content::WebContents* web_contents,
int route_id,
WindowContainerType window_container_type,
const base::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;
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
const extensions::Extension *extension =
service->GetInstalledApp(target_url);
if (!extension || extension->id() != app_id_)
return true;
BackgroundContentsService* background_contents_service =
BackgroundContentsServiceFactory::GetForProfile(profile_);
if (background_contents_service->GetAppBackgroundContents(
base::UTF8ToUTF16(app_id_))) {
return false;
}
BackgroundContents* contents = background_contents_service
->CreateBackgroundContents(content::SiteInstance::Create(profile_),
route_id,
profile_,
frame_name,
base::ASCIIToUTF16(app_id_),
partition_id,
session_storage_namespace);
contents->web_contents()->GetController().LoadURL(
target_url,
content::Referrer(),
content::PAGE_TRANSITION_LINK,
std::string());
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 = base::UTF16ToUTF8(
content::Details<BackgroundContentsOpenedDetails>(details)
->application_id);
if (app_id == app_id_)
OnOfflineInit(true, DriveFirstRunController::OUTCOME_OFFLINE_ENABLED);
}
}
DriveFirstRunController::DriveFirstRunController(Profile* profile)
: profile_(profile),
started_(false),
initial_delay_secs_(kInitialDelaySeconds),
web_contents_timeout_secs_(kWebContentsTimeoutSeconds),
drive_offline_endpoint_url_(kDriveOfflineEndpointUrl),
drive_hosted_app_id_(kDriveHostedAppId) {
}
DriveFirstRunController::~DriveFirstRunController() {
}
void DriveFirstRunController::EnableOfflineMode() {
if (!started_) {
started_ = true;
initial_delay_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(initial_delay_secs_),
this,
&DriveFirstRunController::EnableOfflineMode);
return;
}
if (!UserManager::Get()->IsLoggedInAsRegularUser()) {
LOG(ERROR) << "Attempting to enable offline access "
"but not logged in a regular user.";
OnOfflineInit(false, OUTCOME_WRONG_USER_TYPE);
return;
}
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
if (!extension_service->GetExtensionById(drive_hosted_app_id_, false)) {
LOG(WARNING) << "Drive app is not installed.";
OnOfflineInit(false, OUTCOME_APP_NOT_INSTALLED);
return;
}
BackgroundContentsService* background_contents_service =
BackgroundContentsServiceFactory::GetForProfile(profile_);
if (background_contents_service->GetAppBackgroundContents(
base::UTF8ToUTF16(drive_hosted_app_id_))) {
LOG(WARNING) << "Background page for Drive app already exists";
OnOfflineInit(false, OUTCOME_BACKGROUND_PAGE_EXISTS);
return;
}
web_contents_manager_.reset(new DriveWebContentsManager(
profile_,
drive_hosted_app_id_,
drive_offline_endpoint_url_,
base::Bind(&DriveFirstRunController::OnOfflineInit,
base::Unretained(this))));
web_contents_manager_->StartLoad();
web_contents_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(web_contents_timeout_secs_),
this,
&DriveFirstRunController::OnWebContentsTimedOut);
}
void DriveFirstRunController::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void DriveFirstRunController::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void DriveFirstRunController::SetDelaysForTest(int initial_delay_secs,
int timeout_secs) {
DCHECK(!started_);
initial_delay_secs_ = initial_delay_secs;
web_contents_timeout_secs_ = timeout_secs;
}
void DriveFirstRunController::SetAppInfoForTest(
const std::string& app_id,
const std::string& endpoint_url) {
DCHECK(!started_);
drive_hosted_app_id_ = app_id;
drive_offline_endpoint_url_ = endpoint_url;
}
void DriveFirstRunController::OnWebContentsTimedOut() {
LOG(WARNING) << "Timed out waiting for web contents.";
FOR_EACH_OBSERVER(Observer, observer_list_, OnTimedOut());
OnOfflineInit(false, OUTCOME_WEB_CONTENTS_TIMED_OUT);
}
void DriveFirstRunController::CleanUp() {
if (web_contents_manager_)
web_contents_manager_->StopLoad();
web_contents_timer_.Stop();
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
void DriveFirstRunController::OnOfflineInit(bool success, UMAOutcome outcome) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (success)
ShowNotification();
UMA_HISTOGRAM_ENUMERATION("DriveOffline.CrosAutoEnableOutcome",
outcome, OUTCOME_MAX);
FOR_EACH_OBSERVER(Observer, observer_list_, OnCompletion(success));
CleanUp();
}
void DriveFirstRunController::ShowNotification() {
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
DCHECK(service);
const extensions::Extension* extension =
service->GetExtensionById(drive_hosted_app_id_, false);
DCHECK(extension);
message_center::RichNotificationData data;
data.buttons.push_back(message_center::ButtonInfo(
l10n_util::GetStringUTF16(IDS_DRIVE_OFFLINE_NOTIFICATION_BUTTON)));
ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
scoped_ptr<message_center::Notification> notification(
new message_center::Notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
kDriveOfflineNotificationId,
base::string16(),
l10n_util::GetStringUTF16(IDS_DRIVE_OFFLINE_NOTIFICATION_MESSAGE),
resource_bundle.GetImageNamed(IDR_NOTIFICATION_DRIVE),
base::UTF8ToUTF16(extension->name()),
message_center::NotifierId(message_center::NotifierId::APPLICATION,
kDriveHostedAppId),
data,
new DriveOfflineNotificationDelegate(profile_)));
notification->set_priority(message_center::LOW_PRIORITY);
message_center::MessageCenter::Get()->AddNotification(notification.Pass());
}
}