| Index: chrome/browser/ui/intents/web_intent_picker_controller.cc
|
| diff --git a/chrome/browser/ui/intents/web_intent_picker_controller.cc b/chrome/browser/ui/intents/web_intent_picker_controller.cc
|
| deleted file mode 100644
|
| index 185b3ef0d9fa73aa34ab01f44512facdc2f6c760..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/ui/intents/web_intent_picker_controller.cc
|
| +++ /dev/null
|
| @@ -1,972 +0,0 @@
|
| -// Copyright (c) 2012 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/ui/intents/web_intent_picker_controller.h"
|
| -
|
| -#include <vector>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/md5.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/time.h"
|
| -#include "base/utf_string_conversions.h"
|
| -#include "chrome/browser/download/download_item_model.h"
|
| -#include "chrome/browser/extensions/extension_service.h"
|
| -#include "chrome/browser/extensions/extension_system.h"
|
| -#include "chrome/browser/extensions/platform_app_launcher.h"
|
| -#include "chrome/browser/extensions/webstore_installer.h"
|
| -#include "chrome/browser/intents/cws_intents_registry_factory.h"
|
| -#include "chrome/browser/intents/default_web_intent_service.h"
|
| -#include "chrome/browser/intents/intent_service_host.h"
|
| -#include "chrome/browser/intents/native_services.h"
|
| -#include "chrome/browser/intents/web_intents_registry_factory.h"
|
| -#include "chrome/browser/intents/web_intents_reporting.h"
|
| -#include "chrome/browser/profiles/profile.h"
|
| -#include "chrome/browser/tab_contents/tab_util.h"
|
| -#include "chrome/browser/ui/browser.h"
|
| -#include "chrome/browser/ui/browser_finder.h"
|
| -#include "chrome/browser/ui/browser_list.h"
|
| -#include "chrome/browser/ui/browser_navigator.h"
|
| -#include "chrome/browser/ui/browser_tabstrip.h"
|
| -#include "chrome/browser/ui/browser_window.h"
|
| -#include "chrome/browser/ui/intents/web_intent_icon_loader.h"
|
| -#include "chrome/browser/ui/intents/web_intent_picker.h"
|
| -#include "chrome/browser/ui/intents/web_intent_picker_model.h"
|
| -#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| -#include "chrome/browser/ui/web_contents_modal_dialog_manager.h"
|
| -#include "chrome/browser/webdata/web_data_service.h"
|
| -#include "chrome/common/url_constants.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "content/public/browser/download_manager.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "content/public/browser/web_contents_observer.h"
|
| -#include "content/public/browser/web_intents_dispatcher.h"
|
| -#include "ui/gfx/favicon_size.h"
|
| -#include "ui/gfx/image/image.h"
|
| -
|
| -using extensions::WebstoreInstaller;
|
| -
|
| -DEFINE_WEB_CONTENTS_USER_DATA_KEY(WebIntentPickerController);
|
| -
|
| -namespace {
|
| -
|
| -// Maximum amount of time to delay displaying dialog while waiting for data.
|
| -const int kMaxHiddenSetupTimeMs = 200;
|
| -
|
| -// Minimum amount of time to show waiting dialog, if it is shown.
|
| -const int kMinThrobberDisplayTimeMs = 800;
|
| -
|
| -// Gets the web intents registry for the specified profile.
|
| -WebIntentsRegistry* GetWebIntentsRegistry(Profile* profile) {
|
| - return WebIntentsRegistryFactory::GetForProfile(profile);
|
| -}
|
| -
|
| -// Gets the Chrome web store intents registry for the specified profile.
|
| -CWSIntentsRegistry* GetCWSIntentsRegistry(Profile* profile) {
|
| - return CWSIntentsRegistryFactory::GetForProfile(profile);
|
| -}
|
| -
|
| -class SourceWindowObserver : content::WebContentsObserver {
|
| - public:
|
| - SourceWindowObserver(content::WebContents* web_contents,
|
| - base::WeakPtr<WebIntentPickerController> controller)
|
| - : content::WebContentsObserver(web_contents),
|
| - controller_(controller) {}
|
| - virtual ~SourceWindowObserver() {}
|
| -
|
| - // Implement WebContentsObserver
|
| - virtual void WebContentsDestroyed(
|
| - content::WebContents* web_contents) OVERRIDE {
|
| - if (controller_)
|
| - controller_->SourceWebContentsDestroyed(web_contents);
|
| - delete this;
|
| - }
|
| -
|
| - private:
|
| - base::WeakPtr<WebIntentPickerController> controller_;
|
| -};
|
| -
|
| -// Deletes |service|, presumably called from a dispatcher callback.
|
| -void DeleteIntentService(
|
| - web_intents::IntentServiceHost* service,
|
| - webkit_glue::WebIntentReplyType type) {
|
| - delete service;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// UMAReporter handles reporting Web Intents events to UMA.
|
| -class WebIntentPickerController::UMAReporter {
|
| - public:
|
| -
|
| - // Resets the service active duration timer to "now".
|
| - void ResetServiceActiveTimer();
|
| -
|
| - // Records the duration of time spent using the service. Uses |reply_type|
|
| - // to distinguish between successful and failed service calls.
|
| - void RecordServiceActiveDuration(webkit_glue::WebIntentReplyType reply_type);
|
| -
|
| - private:
|
| -
|
| - // The time when the user began using the service.
|
| - base::TimeTicks service_start_time_;
|
| -};
|
| -
|
| -void WebIntentPickerController::UMAReporter::ResetServiceActiveTimer() {
|
| - service_start_time_ = base::TimeTicks::Now();
|
| -}
|
| -
|
| -void WebIntentPickerController::UMAReporter::RecordServiceActiveDuration(
|
| - webkit_glue::WebIntentReplyType reply_type) {
|
| - if (!service_start_time_.is_null()) {
|
| - web_intents::RecordServiceActiveDuration(reply_type,
|
| - base::TimeTicks::Now() - service_start_time_);
|
| - }
|
| -}
|
| -
|
| -WebIntentPickerController::WebIntentPickerController(
|
| - content::WebContents* web_contents)
|
| - : dialog_state_(kPickerHidden),
|
| - web_contents_(web_contents),
|
| - profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
|
| - picker_(NULL),
|
| - picker_model_(new WebIntentPickerModel()),
|
| - uma_reporter_(new UMAReporter()),
|
| - pending_async_count_(0),
|
| - pending_registry_calls_count_(0),
|
| - pending_cws_request_(false),
|
| - picker_shown_(false),
|
| - window_disposition_source_(NULL),
|
| - source_intents_dispatcher_(NULL),
|
| - intents_dispatcher_(NULL),
|
| - location_bar_button_indicated_(true),
|
| - service_tab_(NULL),
|
| - icon_loader_(NULL),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(dispatcher_factory_(this)) {
|
| - native_services_.reset(new web_intents::NativeServiceFactory());
|
| -#if defined(TOOLKIT_VIEWS)
|
| - cancelled_ = true;
|
| -#endif
|
| - icon_loader_.reset(
|
| - new web_intents::IconLoader(profile_, picker_model_.get()));
|
| -}
|
| -
|
| -WebIntentPickerController::~WebIntentPickerController() {
|
| - if (picker_)
|
| - picker_->InvalidateDelegate();
|
| - if (webstore_installer_.get())
|
| - webstore_installer_->InvalidateDelegate();
|
| -}
|
| -
|
| -// TODO(gbillock): combine this with ShowDialog.
|
| -void WebIntentPickerController::SetIntentsDispatcher(
|
| - content::WebIntentsDispatcher* intents_dispatcher) {
|
| - // TODO(gbillock): This is to account for multiple dispatches in the same tab.
|
| - // That is currently not a well-handled case, and this is a band-aid.
|
| - dispatcher_factory_.InvalidateWeakPtrs();
|
| - intents_dispatcher_ = intents_dispatcher;
|
| - intents_dispatcher_->RegisterReplyNotification(
|
| - base::Bind(&WebIntentPickerController::OnSendReturnMessage,
|
| - dispatcher_factory_.GetWeakPtr()));
|
| -
|
| - // Initialize the reporting bucket.
|
| - const webkit_glue::WebIntentData& intent = intents_dispatcher_->GetIntent();
|
| - uma_bucket_ = web_intents::ToUMABucket(intent.action, intent.type);
|
| -}
|
| -
|
| -// TODO(smckay): rename this "StartActivity".
|
| -void WebIntentPickerController::ShowDialog(const string16& action,
|
| - const string16& type) {
|
| - ShowDialog(kEnableDefaults);
|
| -}
|
| -
|
| -void WebIntentPickerController::ReshowDialog() {
|
| - ShowDialog(kSuppressDefaults);
|
| -}
|
| -
|
| -void WebIntentPickerController::ShowDialog(DefaultsUsage suppress_defaults) {
|
| - web_intents::RecordIntentDispatched(uma_bucket_);
|
| -
|
| - DCHECK(intents_dispatcher_);
|
| -
|
| -#if defined(TOOLKIT_VIEWS)
|
| - cancelled_ = true;
|
| -#endif
|
| -
|
| - // Only show a picker once.
|
| - // TODO(gbillock): There's a hole potentially admitting multiple
|
| - // in-flight dispatches since we don't create the picker
|
| - // in this method, but only after calling the registry.
|
| - if (picker_shown_) {
|
| - intents_dispatcher_->SendReply(webkit_glue::WebIntentReply(
|
| - webkit_glue::WEB_INTENT_REPLY_FAILURE,
|
| - ASCIIToUTF16("Simultaneous intent invocation.")));
|
| - return;
|
| - }
|
| -
|
| - // TODO(binji): Figure out what to do when intents are invoked from incognito
|
| - // mode.
|
| - if (profile_->IsOffTheRecord()) {
|
| - intents_dispatcher_->SendReply(webkit_glue::WebIntentReply(
|
| - webkit_glue::WEB_INTENT_REPLY_FAILURE, string16()));
|
| - return;
|
| - }
|
| -
|
| - picker_model_->Clear();
|
| - picker_model_->set_action(intents_dispatcher_->GetIntent().action);
|
| - picker_model_->set_type(intents_dispatcher_->GetIntent().type);
|
| -
|
| - // If the intent is explicit, skip showing the picker.
|
| - const GURL& service = intents_dispatcher_->GetIntent().service;
|
| - // TODO(gbillock): Decide whether to honor the default suppression flag
|
| - // here or suppress the control for explicit intents.
|
| - if (service.is_valid() && !suppress_defaults) {
|
| - // Get services from the registry to verify a registered extension
|
| - // page for this action/type if it is permitted to be dispatched. (Also
|
| - // required to find disposition set by service.)
|
| - pending_async_count_++;
|
| - GetWebIntentsRegistry(profile_)->GetIntentServices(
|
| - picker_model_->action(), picker_model_->type(),
|
| - base::Bind(
|
| - &WebIntentPickerController::
|
| - OnWebIntentServicesAvailableForExplicitIntent,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - return;
|
| - }
|
| -
|
| - // As soon as the dialog is requested, block all input events
|
| - // on the original tab.
|
| - WebContentsModalDialogManager* web_contents_modal_dialog_manager =
|
| - WebContentsModalDialogManager::FromWebContents(web_contents_);
|
| - web_contents_modal_dialog_manager->BlockWebContentsInteraction(true);
|
| - SetDialogState(kPickerSetup);
|
| -
|
| - pending_async_count_++;
|
| - pending_registry_calls_count_++;
|
| - GetWebIntentsRegistry(profile_)->GetIntentServices(
|
| - picker_model_->action(), picker_model_->type(),
|
| - base::Bind(&WebIntentPickerController::OnWebIntentServicesAvailable,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -
|
| - GURL invoking_url = web_contents_->GetURL();
|
| - if (invoking_url.is_valid() && !suppress_defaults) {
|
| - pending_async_count_++;
|
| - pending_registry_calls_count_++;
|
| - GetWebIntentsRegistry(profile_)->GetDefaultIntentService(
|
| - picker_model_->action(), picker_model_->type(), invoking_url,
|
| - base::Bind(&WebIntentPickerController::OnWebIntentDefaultsAvailable,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - }
|
| -
|
| - pending_cws_request_ = true;
|
| - pending_async_count_++;
|
| - GetCWSIntentsRegistry(profile_)->GetIntentServices(
|
| - picker_model_->action(), picker_model_->type(),
|
| - base::Bind(&WebIntentPickerController::OnCWSIntentServicesAvailable,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void WebIntentPickerController::OnServiceChosen(
|
| - const GURL& url,
|
| - webkit_glue::WebIntentServiceData::Disposition disposition,
|
| - DefaultsUsage suppress_defaults) {
|
| - web_intents::RecordServiceInvoke(uma_bucket_);
|
| - uma_reporter_->ResetServiceActiveTimer();
|
| - ExtensionService* extension_service =
|
| - extensions::ExtensionSystem::Get(profile_)->extension_service();
|
| - DCHECK(extension_service);
|
| -
|
| -#if defined(TOOLKIT_VIEWS)
|
| - cancelled_ = false;
|
| -#endif
|
| -
|
| - // Set the default here. Activating the intent resets the picker model.
|
| - // TODO(gbillock): we should perhaps couple the model to the dispatcher so
|
| - // we can re-activate the model on use-another-service.
|
| - if (!suppress_defaults)
|
| - SetDefaultServiceForSelection(url);
|
| -
|
| -
|
| - // TODO(smckay): this basically smells like another disposition.
|
| - const extensions::Extension* extension =
|
| - extension_service->GetInstalledApp(url);
|
| - if (extension && extension->is_platform_app()) {
|
| - extensions::LaunchPlatformAppWithWebIntent(profile_,
|
| - extension, intents_dispatcher_, web_contents_);
|
| - ClosePicker();
|
| - return;
|
| - }
|
| -
|
| - // TODO(smckay): This entire method shold basically be pulled out
|
| - // into a separate class dedicated to the execution of intents.
|
| - // The tricky part is with the "INLINE" disposition where we
|
| - // want to (re)use the picker to handle the intent. A bit of
|
| - // artful composition + lazy instantiation should make that possible.
|
| - switch (disposition) {
|
| - case webkit_glue::WebIntentServiceData::DISPOSITION_NATIVE: {
|
| - web_intents::IntentServiceHost* service =
|
| - native_services_->CreateServiceInstance(
|
| - url, intents_dispatcher_->GetIntent(), web_contents_);
|
| - DCHECK(service);
|
| -
|
| - intents_dispatcher_->RegisterReplyNotification(
|
| - base::Bind(&DeleteIntentService, base::Unretained(service)));
|
| -
|
| - service->HandleIntent(intents_dispatcher_);
|
| - break;
|
| - }
|
| -
|
| - case webkit_glue::WebIntentServiceData::DISPOSITION_INLINE: {
|
| - // Set the model to inline disposition. It will notify the picker which
|
| - // will respond (via OnInlineDispositionWebContentsCreated) with the
|
| - // WebContents to dispatch the intent to.
|
| - picker_model_->SetInlineDisposition(url);
|
| - break;
|
| - }
|
| -
|
| - case webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW: {
|
| - content::WebContents* contents = content::WebContents::Create(
|
| - content::WebContents::CreateParams(
|
| - profile_, tab_util::GetSiteInstanceForNewTab(profile_, url)));
|
| - WebIntentPickerController::CreateForWebContents(contents);
|
| -
|
| - // Let the controller for the target WebContents know that it is hosting a
|
| - // web intents service. Suppress if we're not showing the
|
| - // use-another-service button.
|
| - if (picker_model_->show_use_another_service()) {
|
| - WebIntentPickerController::FromWebContents(contents)->
|
| - SetWindowDispositionSource(web_contents_, intents_dispatcher_);
|
| - }
|
| -
|
| - intents_dispatcher_->DispatchIntent(contents);
|
| - service_tab_ = contents;
|
| -
|
| - // This call performs all the tab strip manipulation, notifications, etc.
|
| - // Since we're passing in a target_contents, it assumes that we will
|
| - // navigate the page ourselves, though.
|
| - chrome::NavigateParams params(profile_, url,
|
| - content::PAGE_TRANSITION_LINK);
|
| - params.target_contents = contents;
|
| - params.disposition = NEW_FOREGROUND_TAB;
|
| - params.tabstrip_add_types = TabStripModel::ADD_INHERIT_GROUP;
|
| - chrome::Navigate(¶ms);
|
| -
|
| - service_tab_->GetController().LoadURL(
|
| - url, content::Referrer(),
|
| - content::PAGE_TRANSITION_AUTO_BOOKMARK, std::string());
|
| -
|
| - ClosePicker();
|
| - break;
|
| - }
|
| -
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -}
|
| -
|
| -content::WebContents*
|
| -WebIntentPickerController::CreateWebContentsForInlineDisposition(
|
| - Profile* profile, const GURL& url) {
|
| - content::WebContents::CreateParams create_params(
|
| - profile, tab_util::GetSiteInstanceForNewTab(profile, url));
|
| - content::WebContents* web_contents = content::WebContents::Create(
|
| - create_params);
|
| - intents_dispatcher_->DispatchIntent(web_contents);
|
| - return web_contents;
|
| -}
|
| -
|
| -void WebIntentPickerController::SetDefaultServiceForSelection(const GURL& url) {
|
| - DCHECK(picker_model_.get());
|
| - if (url == picker_model_->default_service_url())
|
| - return;
|
| -
|
| - DefaultWebIntentService record;
|
| - record.action = picker_model_->action();
|
| - record.type = picker_model_->type();
|
| - record.service_url = url.spec();
|
| - record.user_date = static_cast<int>(floor(base::Time::Now().ToDoubleT()));
|
| - GetWebIntentsRegistry(profile_)->RegisterDefaultIntentService(record);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionInstallRequested(
|
| - const std::string& id) {
|
| - // Create a local copy of |id| since it is a reference to a member on a UI
|
| - // object, and SetPendingExtensionInstallId triggers an OnModelChanged and
|
| - // a subsequent rebuild of UI objects.
|
| - std::string extension_id(id);
|
| -
|
| - picker_model_->SetPendingExtensionInstallId(extension_id);
|
| -
|
| - scoped_ptr<WebstoreInstaller::Approval> approval(
|
| - WebstoreInstaller::Approval::CreateWithInstallPrompt(profile_));
|
| - // Don't show a bubble pointing to the extension or any other post
|
| - // installation UI.
|
| - approval->skip_post_install_ui = true;
|
| - approval->show_dialog_callback = base::Bind(
|
| - &WebIntentPickerController::OnShowExtensionInstallDialog,
|
| - weak_ptr_factory_.GetWeakPtr());
|
| -
|
| - webstore_installer_ = new WebstoreInstaller(profile_, this,
|
| - &web_contents_->GetController(), extension_id,
|
| - approval.Pass(), WebstoreInstaller::FLAG_INLINE_INSTALL);
|
| -
|
| - pending_async_count_++;
|
| - webstore_installer_->Start();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionLinkClicked(
|
| - const std::string& id,
|
| - WindowOpenDisposition disposition) {
|
| - // Navigate from source tab.
|
| - GURL extension_url(extension_urls::GetWebstoreItemDetailURLPrefix() + id);
|
| - chrome::NavigateParams params(profile_, extension_url,
|
| - content::PAGE_TRANSITION_LINK);
|
| - params.disposition =
|
| - (disposition == CURRENT_TAB) ? NEW_FOREGROUND_TAB : disposition;
|
| - chrome::Navigate(¶ms);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnSuggestionsLinkClicked(
|
| - WindowOpenDisposition disposition) {
|
| - // Navigate from source tab.
|
| - GURL query_url = extension_urls::GetWebstoreIntentQueryURL(
|
| - UTF16ToUTF8(picker_model_->action()),
|
| - UTF16ToUTF8(picker_model_->type()));
|
| - chrome::NavigateParams params(profile_, query_url,
|
| - content::PAGE_TRANSITION_LINK);
|
| - params.disposition =
|
| - (disposition == CURRENT_TAB) ? NEW_FOREGROUND_TAB : disposition;
|
| - chrome::Navigate(¶ms);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnUserCancelledPickerDialog() {
|
| - if (!intents_dispatcher_)
|
| - return;
|
| -
|
| - intents_dispatcher_->SendReply(webkit_glue::WebIntentReply(
|
| - webkit_glue::WEB_INTENT_PICKER_CANCELLED, string16()));
|
| - web_intents::RecordPickerCancel(uma_bucket_);
|
| -
|
| - ClosePicker();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnChooseAnotherService() {
|
| - DCHECK(intents_dispatcher_);
|
| - web_intents::RecordChooseAnotherService(uma_bucket_);
|
| - intents_dispatcher_->ResetDispatch();
|
| - picker_model_->SetInlineDisposition(GURL::EmptyGURL());
|
| -}
|
| -
|
| -void WebIntentPickerController::OnClosing() {
|
| - SetDialogState(kPickerHidden);
|
| - picker_ = NULL;
|
| - picker_model_->ClearPendingExtensionInstall();
|
| - CancelDownload();
|
| -#if defined(TOOLKIT_VIEWS)
|
| - if (cancelled_)
|
| - OnUserCancelledPickerDialog();
|
| -#endif
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionDownloadStarted(
|
| - const std::string& id,
|
| - content::DownloadItem* item) {
|
| - DownloadItemModel(item).SetShouldShowInShelf(false);
|
| - download_id_ = item->GetGlobalId();
|
| - picker_model_->UpdateExtensionDownloadState(item);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionDownloadProgress(
|
| - const std::string& id,
|
| - content::DownloadItem* item) {
|
| - picker_model_->UpdateExtensionDownloadState(item);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionInstallSuccess(
|
| - const std::string& extension_id) {
|
| - webstore_installer_ = NULL; // Release reference.
|
| -
|
| - // OnExtensionInstallSuccess is called via NotificationService::Notify before
|
| - // the extension is added to the ExtensionService. Dispatch via PostTask to
|
| - // allow ExtensionService to update.
|
| - MessageLoop::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(
|
| - &WebIntentPickerController::DispatchToInstalledExtension,
|
| - base::Unretained(this),
|
| - extension_id));
|
| -}
|
| -
|
| -void WebIntentPickerController::DispatchToInstalledExtension(
|
| - const std::string& extension_id) {
|
| - web_intents::RecordCWSExtensionInstalled(uma_bucket_);
|
| -
|
| - download_id_ = content::DownloadId();
|
| - picker_model_->ClearPendingExtensionInstall();
|
| - if (picker_)
|
| - picker_->OnExtensionInstallSuccess(extension_id);
|
| -
|
| - WebIntentsRegistry::IntentServiceList services;
|
| - GetWebIntentsRegistry(profile_)->GetIntentServicesForExtensionFilter(
|
| - picker_model_->action(), picker_model_->type(),
|
| - extension_id,
|
| - &services);
|
| -
|
| - // Extension must be registered with registry by now.
|
| - DCHECK(services.size() > 0);
|
| -
|
| - // TODO(binji): We're going to need to disambiguate if there are multiple
|
| - // services. For now, just choose the first.
|
| - const webkit_glue::WebIntentServiceData& service_data = services[0];
|
| -
|
| - picker_model_->RemoveSuggestedExtension(extension_id);
|
| - AddServiceToModel(service_data);
|
| - OnServiceChosen(service_data.service_url, service_data.disposition,
|
| - kEnableDefaults);
|
| - AsyncOperationFinished();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnExtensionInstallFailure(
|
| - const std::string& id,
|
| - const std::string& error,
|
| - WebstoreInstaller::FailureReason reason) {
|
| - webstore_installer_ = NULL; // Release reference.
|
| -
|
| - // If the user cancelled the install then don't show an error message.
|
| - if (reason == WebstoreInstaller::FAILURE_REASON_CANCELLED)
|
| - picker_model_->ClearPendingExtensionInstall();
|
| - else
|
| - picker_model_->SetPendingExtensionInstallStatusString(UTF8ToUTF16(error));
|
| -
|
| - if (picker_)
|
| - picker_->OnExtensionInstallFailure(id);
|
| - AsyncOperationFinished();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnSendReturnMessage(
|
| - webkit_glue::WebIntentReplyType reply_type) {
|
| - ClosePicker();
|
| - uma_reporter_->RecordServiceActiveDuration(reply_type);
|
| -
|
| - if (service_tab_ &&
|
| - reply_type != webkit_glue::WEB_INTENT_SERVICE_CONTENTS_CLOSED) {
|
| - Browser* browser = chrome::FindBrowserWithWebContents(service_tab_);
|
| - if (browser) {
|
| - int index = browser->tab_strip_model()->GetIndexOfWebContents(
|
| - service_tab_);
|
| - browser->tab_strip_model()->CloseWebContentsAt(
|
| - index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
|
| -
|
| - // Activate source tab.
|
| - Browser* source_browser =
|
| - chrome::FindBrowserWithWebContents(web_contents_);
|
| - if (source_browser) {
|
| - int source_index = source_browser->tab_strip_model()->
|
| - GetIndexOfWebContents(web_contents_);
|
| - source_browser->tab_strip_model()->ActivateTabAt(source_index, false);
|
| - }
|
| - }
|
| - service_tab_ = NULL;
|
| - }
|
| -
|
| - intents_dispatcher_ = NULL;
|
| -}
|
| -
|
| -void WebIntentPickerController::AddServiceToModel(
|
| - const webkit_glue::WebIntentServiceData& service) {
|
| -
|
| - picker_model_->AddInstalledService(
|
| - service.title,
|
| - service.service_url,
|
| - service.disposition);
|
| -
|
| - icon_loader_->LoadFavicon(service.service_url);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnWebIntentServicesAvailable(
|
| - const std::vector<webkit_glue::WebIntentServiceData>& services) {
|
| - for (size_t i = 0; i < services.size(); ++i)
|
| - AddServiceToModel(services[i]);
|
| -
|
| - RegistryCallsCompleted();
|
| - AsyncOperationFinished();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnWebIntentServicesAvailableForExplicitIntent(
|
| - const std::vector<webkit_glue::WebIntentServiceData>& services) {
|
| - DCHECK(intents_dispatcher_);
|
| - DCHECK(intents_dispatcher_->GetIntent().service.is_valid());
|
| - for (size_t i = 0; i < services.size(); ++i) {
|
| - if (services[i].service_url != intents_dispatcher_->GetIntent().service)
|
| - continue;
|
| -
|
| - InvokeServiceWithSelection(services[i]);
|
| - AsyncOperationFinished();
|
| - return;
|
| - }
|
| -
|
| - // No acceptable extension. The intent cannot be dispatched.
|
| - intents_dispatcher_->SendReply(webkit_glue::WebIntentReply(
|
| - webkit_glue::WEB_INTENT_REPLY_FAILURE, ASCIIToUTF16(
|
| - "Explicit extension URL is not available.")));
|
| -
|
| - AsyncOperationFinished();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnWebIntentDefaultsAvailable(
|
| - const DefaultWebIntentService& default_service) {
|
| - if (!default_service.service_url.empty()) {
|
| - // TODO(gbillock): this doesn't belong in the model, but keep it there
|
| - // for now.
|
| - picker_model_->set_default_service_url(GURL(default_service.service_url));
|
| - }
|
| -
|
| - RegistryCallsCompleted();
|
| - AsyncOperationFinished();
|
| -}
|
| -
|
| -void WebIntentPickerController::RegistryCallsCompleted() {
|
| - pending_registry_calls_count_--;
|
| - if (pending_registry_calls_count_ != 0) return;
|
| -
|
| - if (picker_model_->default_service_url().is_valid()) {
|
| - // If there's a default service, dispatch to it immediately
|
| - // without showing the picker.
|
| - const WebIntentPickerModel::InstalledService* default_service =
|
| - picker_model_->GetInstalledServiceWithURL(
|
| - GURL(picker_model_->default_service_url()));
|
| - if (default_service != NULL) {
|
| - InvokeService(*default_service);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - OnPickerEvent(kPickerEventRegistryDataComplete);
|
| - OnIntentDataArrived();
|
| -}
|
| -
|
| -void WebIntentPickerController::OnCWSIntentServicesAvailable(
|
| - const CWSIntentsRegistry::IntentExtensionList& extensions) {
|
| - ExtensionServiceInterface* extension_service =
|
| - extensions::ExtensionSystem::Get(profile_)->extension_service();
|
| -
|
| - std::vector<WebIntentPickerModel::SuggestedExtension> suggestions;
|
| - for (size_t i = 0; i < extensions.size(); ++i) {
|
| - const CWSIntentsRegistry::IntentExtensionInfo& info = extensions[i];
|
| -
|
| - // Do not include suggestions for already installed extensions.
|
| - if (extension_service->GetExtensionById(info.id, true))
|
| - continue;
|
| -
|
| - suggestions.push_back(WebIntentPickerModel::SuggestedExtension(
|
| - info.name, info.id, info.average_rating));
|
| -
|
| - icon_loader_->LoadExtensionIcon(info.icon_url, info.id);
|
| - }
|
| -
|
| - picker_model_->AddSuggestedExtensions(suggestions);
|
| -
|
| - AsyncOperationFinished();
|
| - pending_cws_request_ = false;
|
| - OnIntentDataArrived();
|
| -}
|
| -
|
| -
|
| -void WebIntentPickerController::OnIntentDataArrived() {
|
| - DCHECK(picker_model_.get());
|
| -
|
| - if (!pending_cws_request_ &&
|
| - pending_registry_calls_count_ == 0)
|
| - OnPickerEvent(kPickerEventAsyncDataComplete);
|
| -}
|
| -
|
| -void WebIntentPickerController::Reset() {
|
| - // Abandon all callbacks.
|
| - weak_ptr_factory_.InvalidateWeakPtrs();
|
| - timer_factory_.InvalidateWeakPtrs();
|
| -
|
| - // Reset state associated with callbacks.
|
| - pending_async_count_ = 0;
|
| - pending_registry_calls_count_ = 0;
|
| - pending_cws_request_ = false;
|
| -
|
| - // Reset picker.
|
| - icon_loader_.reset();
|
| - picker_model_.reset(new WebIntentPickerModel());
|
| - icon_loader_.reset(
|
| - new web_intents::IconLoader(profile_, picker_model_.get()));
|
| -
|
| - picker_shown_ = false;
|
| -
|
| - DCHECK(web_contents_);
|
| - WebContentsModalDialogManager* web_contents_modal_dialog_manager =
|
| - WebContentsModalDialogManager::FromWebContents(web_contents_);
|
| - web_contents_modal_dialog_manager->BlockWebContentsInteraction(false);
|
| -}
|
| -
|
| -void WebIntentPickerController::OnShowExtensionInstallDialog(
|
| - const ExtensionInstallPrompt::ShowParams& show_params,
|
| - ExtensionInstallPrompt::Delegate* delegate,
|
| - const ExtensionInstallPrompt::Prompt& prompt) {
|
| - picker_model_->SetPendingExtensionInstallDelegate(delegate);
|
| - picker_model_->SetPendingExtensionInstallPrompt(prompt);
|
| - if (picker_)
|
| - picker_->OnShowExtensionInstallDialog(show_params, delegate, prompt);
|
| -}
|
| -
|
| -void WebIntentPickerController::SetWindowDispositionSource(
|
| - content::WebContents* source,
|
| - content::WebIntentsDispatcher* dispatcher) {
|
| - DCHECK(source);
|
| - DCHECK(dispatcher);
|
| - location_bar_button_indicated_ = false;
|
| - window_disposition_source_ = source;
|
| - if (window_disposition_source_) {
|
| - // This object is self-deleting when the source WebContents is destroyed.
|
| - new SourceWindowObserver(window_disposition_source_,
|
| - weak_ptr_factory_.GetWeakPtr());
|
| - }
|
| -
|
| - if (dispatcher) {
|
| - dispatcher->RegisterReplyNotification(
|
| - base::Bind(&WebIntentPickerController::SourceDispatcherReplied,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - }
|
| - source_intents_dispatcher_ = dispatcher;
|
| -}
|
| -
|
| -void WebIntentPickerController::SourceWebContentsDestroyed(
|
| - content::WebContents* source) {
|
| - window_disposition_source_ = NULL;
|
| - // TODO(gbillock): redraw location bar to kill button
|
| -}
|
| -
|
| -void WebIntentPickerController::SourceDispatcherReplied(
|
| - webkit_glue::WebIntentReplyType reply_type) {
|
| - source_intents_dispatcher_ = NULL;
|
| - // TODO(gbillock): redraw location bar to kill button
|
| -}
|
| -
|
| -bool WebIntentPickerController::ShowLocationBarPickerButton() {
|
| - return window_disposition_source_ || source_intents_dispatcher_;
|
| -}
|
| -
|
| -void WebIntentPickerController::OnPickerEvent(WebIntentPickerEvent event) {
|
| - switch (event) {
|
| - case kPickerEventHiddenSetupTimeout:
|
| - DCHECK(dialog_state_ == kPickerSetup);
|
| - SetDialogState(kPickerWaiting);
|
| - break;
|
| -
|
| - case kPickerEventMaxWaitTimeExceeded:
|
| - DCHECK(dialog_state_ == kPickerWaiting);
|
| -
|
| - // If registry data is complete, go to main dialog. Otherwise, wait.
|
| - if (pending_registry_calls_count_ == 0)
|
| - SetDialogState(kPickerMain);
|
| - else
|
| - SetDialogState(kPickerWaitLong);
|
| - break;
|
| -
|
| - case kPickerEventRegistryDataComplete:
|
| - DCHECK(dialog_state_ == kPickerSetup ||
|
| - dialog_state_ == kPickerWaiting ||
|
| - dialog_state_ == kPickerWaitLong);
|
| -
|
| - // If minimum wait dialog time is exceeded, display main dialog.
|
| - // Either way, we don't do a thing.
|
| - break;
|
| -
|
| - case kPickerEventAsyncDataComplete:
|
| - DCHECK(dialog_state_ == kPickerSetup ||
|
| - dialog_state_ == kPickerWaiting ||
|
| - dialog_state_ == kPickerWaitLong ||
|
| - dialog_state_ == kPickerInline);
|
| -
|
| - // In setup state, transition to main dialog. In waiting state, let
|
| - // timer expire.
|
| - if (dialog_state_ == kPickerSetup)
|
| - SetDialogState(kPickerMain);
|
| - break;
|
| -
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -}
|
| -
|
| -void WebIntentPickerController::LocationBarPickerButtonClicked() {
|
| - DCHECK(web_contents_);
|
| - if (window_disposition_source_ && source_intents_dispatcher_) {
|
| - Browser* service_browser =
|
| - chrome::FindBrowserWithWebContents(web_contents_);
|
| - if (!service_browser) return;
|
| -
|
| - Browser* client_browser =
|
| - chrome::FindBrowserWithWebContents(window_disposition_source_);
|
| - if (!client_browser)
|
| - return;
|
| - int client_index = client_browser->tab_strip_model()->GetIndexOfWebContents(
|
| - window_disposition_source_);
|
| - DCHECK(client_index != TabStripModel::kNoTab);
|
| -
|
| - source_intents_dispatcher_->ResetDispatch();
|
| -
|
| - WebIntentPickerController* client_controller =
|
| - WebIntentPickerController::FromWebContents(window_disposition_source_);
|
| - DCHECK(client_controller);
|
| -
|
| - // This call deletes this object, so anything below here needs to
|
| - // use stack variables.
|
| - chrome::CloseWebContents(service_browser, web_contents_, true);
|
| -
|
| - // Re-open the other tab and activate the picker.
|
| - client_browser->window()->Activate();
|
| - client_browser->tab_strip_model()->ActivateTabAt(client_index, true);
|
| - // The picker has been Reset() when the new tab is created; need to fully
|
| - // reload.
|
| - client_controller->ReshowDialog();
|
| - }
|
| - // TODO(gbillock): figure out what we ought to do in this case. Probably
|
| - // nothing? Refresh the location bar?
|
| -}
|
| -
|
| -void WebIntentPickerController::AsyncOperationFinished() {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| - if (--pending_async_count_ == 0) {
|
| - if (picker_)
|
| - picker_->OnPendingAsyncCompleted();
|
| - }
|
| -}
|
| -
|
| -void WebIntentPickerController::InvokeServiceWithSelection(
|
| - const webkit_glue::WebIntentServiceData& service) {
|
| - if (picker_shown_) {
|
| - intents_dispatcher_->SendReply(webkit_glue::WebIntentReply(
|
| - webkit_glue::WEB_INTENT_REPLY_FAILURE,
|
| - ASCIIToUTF16("Simultaneous intent invocation.")));
|
| - return;
|
| - }
|
| -
|
| - // TODO(gbillock): this is a bit hacky and exists because in the inline case,
|
| - // the picker currently assumes it exists.
|
| - AddServiceToModel(service);
|
| - picker_model_->set_show_use_another_service(false);
|
| -
|
| - if (service.disposition ==
|
| - webkit_glue::WebIntentServiceData::DISPOSITION_INLINE) {
|
| - picker_model_->SetInlineDisposition(service.service_url);
|
| - SetDialogState(kPickerInline);
|
| - return;
|
| - }
|
| -
|
| - OnServiceChosen(service.service_url, service.disposition, kSuppressDefaults);
|
| -}
|
| -
|
| -void WebIntentPickerController::InvokeService(
|
| - const WebIntentPickerModel::InstalledService& service) {
|
| - if (service.disposition ==
|
| - webkit_glue::WebIntentServiceData::DISPOSITION_INLINE) {
|
| - // This call will ensure the picker dialog is created and initialized.
|
| - picker_model_->SetInlineDisposition(service.url);
|
| - SetDialogState(kPickerInline);
|
| - return;
|
| - }
|
| - OnServiceChosen(service.url, service.disposition, kEnableDefaults);
|
| -}
|
| -
|
| -void WebIntentPickerController::SetDialogState(WebIntentPickerState state) {
|
| - // Ignore events that don't change state.
|
| - if (state == dialog_state_)
|
| - return;
|
| -
|
| - // Any pending timers are abandoned on state changes.
|
| - timer_factory_.InvalidateWeakPtrs();
|
| -
|
| - switch (state) {
|
| - case kPickerSetup:
|
| - DCHECK_EQ(dialog_state_, kPickerHidden);
|
| - // Post timer CWS pending
|
| - MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| - base::Bind(&WebIntentPickerController::OnPickerEvent,
|
| - timer_factory_.GetWeakPtr(),
|
| - kPickerEventHiddenSetupTimeout),
|
| - base::TimeDelta::FromMilliseconds(kMaxHiddenSetupTimeMs));
|
| - break;
|
| -
|
| - case kPickerWaiting:
|
| - DCHECK_EQ(dialog_state_, kPickerSetup);
|
| - // Waiting dialog can be dismissed after minimum wait time.
|
| - MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| - base::Bind(&WebIntentPickerController::OnPickerEvent,
|
| - timer_factory_.GetWeakPtr(),
|
| - kPickerEventMaxWaitTimeExceeded),
|
| - base::TimeDelta::FromMilliseconds(kMinThrobberDisplayTimeMs));
|
| - break;
|
| -
|
| - case kPickerWaitLong:
|
| - DCHECK_EQ(dialog_state_, kPickerWaiting);
|
| - break;
|
| -
|
| - case kPickerInline:
|
| - // Intentional fall-through.
|
| - case kPickerMain:
|
| - // No DCHECK - main state can be reached from any state.
|
| - // Ready to display data.
|
| - picker_model_->SetWaitingForSuggestions(false);
|
| - break;
|
| -
|
| - case kPickerHidden:
|
| - Reset();
|
| - break;
|
| -
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| -
|
| - }
|
| -
|
| - dialog_state_ = state;
|
| -
|
| - // Create picker dialog when changing away from hidden state.
|
| - if (dialog_state_ != kPickerHidden && dialog_state_ != kPickerSetup)
|
| - CreatePicker();
|
| -}
|
| -
|
| -void WebIntentPickerController::CreatePicker() {
|
| - // If picker is non-NULL, it was set by a test.
|
| - if (picker_ == NULL)
|
| - picker_ = WebIntentPicker::Create(web_contents_, this, picker_model_.get());
|
| - picker_->SetActionString(WebIntentPicker::GetDisplayStringForIntentAction(
|
| - picker_model_->action()));
|
| - web_intents::RecordPickerShow(
|
| - uma_bucket_, picker_model_->GetInstalledServiceCount());
|
| - picker_shown_ = true;
|
| -}
|
| -
|
| -void WebIntentPickerController::ClosePicker() {
|
| - SetDialogState(kPickerHidden);
|
| - if (picker_)
|
| - picker_->Close();
|
| -}
|
| -
|
| -void WebIntentPickerController::CancelDownload() {
|
| - if (!download_id_.IsValid())
|
| - return;
|
| - Profile* profile =
|
| - Profile::FromBrowserContext(web_contents_->GetBrowserContext());
|
| - content::DownloadManager* download_manager =
|
| - content::BrowserContext::GetDownloadManager(profile);
|
| - if (!download_manager)
|
| - return;
|
| - content::DownloadItem* item =
|
| - download_manager->GetDownload(download_id_.local());
|
| - if (item)
|
| - item->Cancel(true);
|
| - download_id_ = content::DownloadId();
|
| -}
|
|
|