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());
}
}