| Index: chrome/browser/extensions/api/identity/web_auth_flow.cc
|
| diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chrome/browser/extensions/api/identity/web_auth_flow.cc
|
| index dd76b47244194855d01f6c31b50b4eb0c8e9122e..f0553d24d9ab587a012be2cc4594a718dfa92626 100644
|
| --- a/chrome/browser/extensions/api/identity/web_auth_flow.cc
|
| +++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc
|
| @@ -4,18 +4,27 @@
|
|
|
| #include "chrome/browser/extensions/api/identity/web_auth_flow.h"
|
|
|
| +#include "base/base64.h"
|
| #include "base/location.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome/browser/extensions/event_router.h"
|
| +#include "chrome/browser/extensions/extension_system.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/ui/browser.h"
|
| #include "chrome/browser/ui/browser_navigator.h"
|
| +#include "chrome/browser/ui/extensions/shell_window.h"
|
| +#include "chrome/common/extensions/extension_constants.h"
|
| #include "content/public/browser/load_notification_details.h"
|
| #include "content/public/browser/navigation_controller.h"
|
| #include "content/public/browser/notification_details.h"
|
| #include "content/public/browser/notification_source.h"
|
| #include "content/public/browser/notification_types.h"
|
| +#include "content/public/browser/render_process_host.h"
|
| +#include "content/public/browser/render_view_host.h"
|
| #include "content/public/browser/resource_request_details.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/common/page_transition_types.h"
|
| +#include "crypto/random.h"
|
| #include "googleurl/src/gurl.h"
|
| #include "ui/base/window_open_disposition.h"
|
|
|
| @@ -32,71 +41,100 @@ WebAuthFlow::WebAuthFlow(
|
| Delegate* delegate,
|
| Profile* profile,
|
| const GURL& provider_url,
|
| - Mode mode,
|
| - const gfx::Rect& initial_bounds,
|
| - chrome::HostDesktopType host_desktop_type)
|
| + Mode mode)
|
| : delegate_(delegate),
|
| + closed_(false),
|
| profile_(profile),
|
| provider_url_(provider_url),
|
| mode_(mode),
|
| - initial_bounds_(initial_bounds),
|
| - host_desktop_type_(host_desktop_type),
|
| - popup_shown_(false),
|
| - contents_(NULL) {
|
| + shell_window_loaded_(false) {
|
| }
|
|
|
| WebAuthFlow::~WebAuthFlow() {
|
| + DCHECK(closed_);
|
| +}
|
| +
|
| +void WebAuthFlow::Start() {
|
| + ShellWindowRegistry::Get(profile_)->AddObserver(this);
|
| +
|
| + std::string random_bytes;
|
| + crypto::RandBytes(WriteInto(&random_bytes, 33), 32);
|
| + std::string key;
|
| + bool success = base::Base64Encode(random_bytes, &shell_window_key_);
|
| + DCHECK(success);
|
| +
|
| + scoped_ptr<ListValue> args(new ListValue());
|
| + args->AppendString(shell_window_key_);
|
| + args->AppendString(provider_url_.spec());
|
| + if (mode_ == WebAuthFlow::INTERACTIVE)
|
| + args->AppendString("interactive");
|
| + else
|
| + args->AppendString("silent");
|
| +
|
| + scoped_ptr<Event> event(new Event("identityPrivate.onWebFlowRequest",
|
| + args.Pass()));
|
| + event->restrict_to_profile = profile_;
|
| + ExtensionSystem* system = ExtensionSystem::Get(profile_);
|
| + system->event_router()->AddLazyEventListener(
|
| + "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId);
|
| + system->event_router()->DispatchEventToExtension(
|
| + extension_misc::kIdentityApiUiAppId, event.Pass());
|
| + system->event_router()->RemoveLazyEventListener(
|
| + "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId);
|
| +}
|
| +
|
| +void WebAuthFlow::Close() {
|
| + CHECK(!closed_);
|
| + closed_ = true;
|
| +
|
| + // Chances are good that we are currently inside an observer callback, so
|
| + // detaching listeners now is dangerous. Use a task to unwind the stack
|
| + // before cleaning up.
|
| + MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
|
| + &WebAuthFlow::DeferredClose, base::Unretained(this)));
|
| +}
|
| +
|
| +void WebAuthFlow::DeferredClose() {
|
| + DCHECK(closed_);
|
| +
|
| // Stop listening to notifications first since some of the code
|
| // below may generate notifications.
|
| - registrar_.RemoveAll();
|
| WebContentsObserver::Observe(NULL);
|
| + registrar_.RemoveAll();
|
| + ShellWindowRegistry::Get(profile_)->RemoveObserver(this);
|
|
|
| - if (contents_) {
|
| - // The popup owns the contents if it was displayed.
|
| - if (popup_shown_)
|
| - contents_->Close();
|
| - else
|
| - delete contents_;
|
| - }
|
| -}
|
| + if (shell_window_ && shell_window_->web_contents())
|
| + shell_window_->web_contents()->Close();
|
|
|
| -void WebAuthFlow::Start() {
|
| - contents_ = CreateWebContents();
|
| - WebContentsObserver::Observe(contents_);
|
| -
|
| - NavigationController* controller = &(contents_->GetController());
|
| -
|
| - // Register for appropriate notifications to intercept navigation to the
|
| - // redirect URLs.
|
| - registrar_.Add(
|
| - this,
|
| - content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
|
| - content::Source<WebContents>(contents_));
|
| -
|
| - controller->LoadURL(
|
| - provider_url_,
|
| - content::Referrer(),
|
| - content::PAGE_TRANSITION_AUTO_TOPLEVEL,
|
| - std::string());
|
| + delegate_->OnAuthFlowClosed();
|
| }
|
|
|
| -WebContents* WebAuthFlow::CreateWebContents() {
|
| - return WebContents::Create(WebContents::CreateParams(profile_));
|
| +void WebAuthFlow::OnShellWindowAdded(ShellWindow* shell_window) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + if (shell_window->window_key() == shell_window_key_ &&
|
| + shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) {
|
| + shell_window_ = shell_window;
|
| + WebContentsObserver::Observe(shell_window->web_contents());
|
| +
|
| + registrar_.Add(
|
| + this,
|
| + content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
|
| + content::NotificationService::AllBrowserContextsAndSources());
|
| + }
|
| }
|
|
|
| -void WebAuthFlow::ShowAuthFlowPopup() {
|
| - Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile_,
|
| - host_desktop_type_);
|
| - browser_params.initial_bounds = initial_bounds_;
|
| - Browser* browser = new Browser(browser_params);
|
| - chrome::NavigateParams params(browser, contents_);
|
| - params.disposition = CURRENT_TAB;
|
| - params.window_action = chrome::NavigateParams::SHOW_WINDOW;
|
| - chrome::Navigate(¶ms);
|
| - // Observe method and WebContentsObserver::* methods will be called
|
| - // for varous navigation events. That is where we check for redirect
|
| - // to the right URL.
|
| - popup_shown_ = true;
|
| +void WebAuthFlow::OnShellWindowRemoved(ShellWindow* shell_window) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + if (shell_window->window_key() == shell_window_key_ &&
|
| + shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) {
|
| + shell_window_ = NULL;
|
| + if (!closed_)
|
| + delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
|
| + }
|
| }
|
|
|
| void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
|
| @@ -104,24 +142,39 @@ void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
|
| }
|
|
|
| void WebAuthFlow::AfterUrlLoaded() {
|
| - // Do nothing if a popup is already created.
|
| - if (popup_shown_)
|
| - return;
|
| -
|
| - // Report results directly if not in interactive mode.
|
| - if (mode_ != WebAuthFlow::INTERACTIVE) {
|
| + if (shell_window_loaded_ && mode_ == WebAuthFlow::SILENT) {
|
| delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
|
| return;
|
| }
|
| -
|
| - // We are in interactive mode and window is not shown yet; show the window.
|
| - ShowAuthFlowPopup();
|
| }
|
|
|
| void WebAuthFlow::Observe(int type,
|
| const content::NotificationSource& source,
|
| const content::NotificationDetails& details) {
|
| + if (closed_)
|
| + return;
|
| +
|
| switch (type) {
|
| + case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
|
| + RenderViewHost* render_view(
|
| + content::Details<RenderViewHost>(details).ptr());
|
| + WebContents* web_contents = WebContents::FromRenderViewHost(render_view);
|
| +
|
| + if (web_contents &&
|
| + (web_contents->GetEmbedderWebContents() ==
|
| + WebContentsObserver::web_contents())) {
|
| + // Switch from watching the shell window to the guest inside it.
|
| + shell_window_loaded_ = true;
|
| + registrar_.RemoveAll();
|
| + WebContentsObserver::Observe(web_contents);
|
| +
|
| + registrar_.Add(
|
| + this,
|
| + content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
|
| + content::Source<WebContents>(web_contents));
|
| + }
|
| + }
|
| + break;
|
| case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
|
| ResourceRedirectDetails* redirect_details =
|
| content::Details<ResourceRedirectDetails>(details).ptr();
|
| @@ -136,19 +189,57 @@ void WebAuthFlow::Observe(int type,
|
| }
|
| }
|
|
|
| -void WebAuthFlow::ProvisionalChangeToMainFrameUrl(
|
| - const GURL& url,
|
| +void WebAuthFlow::RenderViewGone(base::TerminationStatus status) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
|
| +}
|
| +
|
| +void WebAuthFlow::DidStartProvisionalLoadForFrame(
|
| + int64 frame_id,
|
| + int64 parent_frame_id,
|
| + bool is_main_frame,
|
| + const GURL& validated_url,
|
| + bool is_error_page,
|
| + bool is_iframe_srcdoc,
|
| RenderViewHost* render_view_host) {
|
| - BeforeUrlLoaded(url);
|
| + if (closed_)
|
| + return;
|
| +
|
| + if (is_main_frame)
|
| + BeforeUrlLoaded(validated_url);
|
| }
|
|
|
| -void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) {
|
| - AfterUrlLoaded();
|
| +void WebAuthFlow::DidFailProvisionalLoad(int64 frame_id,
|
| + bool is_main_frame,
|
| + const GURL& validated_url,
|
| + int error_code,
|
| + const string16& error_description,
|
| + RenderViewHost* render_view_host) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + delegate_->OnAuthFlowFailure(LOAD_FAILED);
|
| }
|
|
|
| -void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) {
|
| - contents_ = NULL;
|
| - delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
|
| +void WebAuthFlow::DidFailLoad(int64 frame_id,
|
| + const GURL& validated_url,
|
| + bool is_main_frame,
|
| + int error_code,
|
| + const string16& error_description,
|
| + RenderViewHost* render_view_host) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + delegate_->OnAuthFlowFailure(LOAD_FAILED);
|
| +}
|
| +
|
| +void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) {
|
| + if (closed_)
|
| + return;
|
| +
|
| + AfterUrlLoaded();
|
| }
|
|
|
| } // namespace extensions
|
|
|