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 d4347d445f2a7cce584c07b5b8ee571676888c9c..fc14858411e6d71a626286714bcf62bce4c23442 100644 |
--- a/chrome/browser/extensions/api/identity/web_auth_flow.cc |
+++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc |
@@ -4,26 +4,31 @@ |
#include "chrome/browser/extensions/api/identity/web_auth_flow.h" |
+#include "base/base64.h" |
#include "base/location.h" |
#include "base/message_loop.h" |
+#include "base/string_util.h" |
#include "base/utf_string_conversions.h" |
+#include "chrome/browser/extensions/component_loader.h" |
+#include "chrome/browser/extensions/event_router.h" |
+#include "chrome/browser/extensions/extension_service.h" |
+#include "chrome/browser/extensions/extension_system.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 "content/public/browser/load_notification_details.h" |
-#include "content/public/browser/navigation_controller.h" |
+#include "chrome/browser/ui/extensions/shell_window.h" |
+#include "chrome/common/extensions/extension_constants.h" |
+#include "content/public/browser/navigation_details.h" |
#include "content/public/browser/navigation_entry.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_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" |
+#include "grit/browser_resources.h" |
-using content::LoadNotificationDetails; |
-using content::NavigationController; |
using content::RenderViewHost; |
using content::ResourceRedirectDetails; |
using content::WebContents; |
@@ -35,17 +40,12 @@ 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), |
profile_(profile), |
provider_url_(provider_url), |
mode_(mode), |
- initial_bounds_(initial_bounds), |
- host_desktop_type_(host_desktop_type), |
- popup_shown_(false), |
- contents_(NULL) { |
+ embedded_window_created_(false) { |
} |
WebAuthFlow::~WebAuthFlow() { |
@@ -56,37 +56,53 @@ WebAuthFlow::~WebAuthFlow() { |
registrar_.RemoveAll(); |
WebContentsObserver::Observe(NULL); |
- if (contents_) { |
- // The popup owns the contents if it was displayed. |
- if (popup_shown_) |
- contents_->Close(); |
- else |
- delete contents_; |
+ if (!shell_window_key_.empty()) { |
+ ShellWindowRegistry::Get(profile_)->RemoveObserver(this); |
+ |
+ 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_)); |
- registrar_.Add( |
- this, |
- content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED, |
- content::Source<WebContents>(contents_)); |
- |
- controller->LoadURL( |
- provider_url_, |
- content::Referrer(), |
- content::PAGE_TRANSITION_AUTO_TOPLEVEL, |
- std::string()); |
+ ShellWindowRegistry::Get(profile_)->AddObserver(this); |
+ |
+ // Attach a random ID string to the window so we can recoginize it |
+ // in OnShellWindowAdded. |
+ 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); |
+ |
+ // identityPrivate.onWebFlowRequest(shell_window_key, provider_url_, mode_) |
+ 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_); |
+ |
+ extensions::ComponentLoader* component_loader = |
+ system->extension_service()->component_loader(); |
+ if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) { |
+ component_loader->Add( |
+ IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST, |
+ base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog"))); |
+ } |
+ |
+ 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::DetachDelegateAndDelete() { |
@@ -94,87 +110,135 @@ void WebAuthFlow::DetachDelegateAndDelete() { |
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
} |
-WebContents* WebAuthFlow::CreateWebContents() { |
- return WebContents::Create(WebContents::CreateParams(profile_)); |
+void WebAuthFlow::OnShellWindowAdded(ShellWindow* shell_window) { |
+ 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 (shell_window->window_key() == shell_window_key_ && |
+ shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) { |
+ shell_window_ = NULL; |
+ registrar_.RemoveAll(); |
+ |
+ if (delegate_) |
+ delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); |
+ } |
} |
void WebAuthFlow::BeforeUrlLoaded(const GURL& url) { |
- if (delegate_) |
+ if (delegate_ && embedded_window_created_) |
delegate_->OnAuthFlowURLChange(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 (delegate_) |
- delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); |
- return; |
- } |
- |
- // We are in interactive mode and window is not shown yet; show the window. |
- ShowAuthFlowPopup(); |
+ if (delegate_ && embedded_window_created_ && mode_ == WebAuthFlow::SILENT) |
+ delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); |
} |
void WebAuthFlow::Observe(int type, |
const content::NotificationSource& source, |
const content::NotificationDetails& details) { |
- switch (type) { |
- case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { |
- ResourceRedirectDetails* redirect_details = |
- content::Details<ResourceRedirectDetails>(details).ptr(); |
- if (redirect_details != NULL) |
- BeforeUrlLoaded(redirect_details->new_url); |
+ DCHECK(shell_window_); |
+ |
+ if (!delegate_) |
+ return; |
+ |
+ if (!embedded_window_created_) { |
+ DCHECK(type == 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. |
+ embedded_window_created_ = true; |
+ WebContentsObserver::Observe(web_contents); |
+ |
+ registrar_.RemoveAll(); |
+ registrar_.Add(this, |
+ content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, |
+ content::Source<WebContents>(web_contents)); |
+ registrar_.Add(this, |
+ content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED, |
+ content::Source<WebContents>(web_contents)); |
} |
- break; |
- case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: { |
- std::pair<content::NavigationEntry*, bool>* title = |
- content::Details< |
- std::pair<content::NavigationEntry*, bool> >(details).ptr(); |
- |
- if (title->first) |
- delegate_->OnAuthFlowTitleChange(UTF16ToUTF8(title->first->GetTitle())); |
+ } else { |
+ // embedded_window_created_ |
+ switch (type) { |
+ case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { |
+ ResourceRedirectDetails* redirect_details = |
+ content::Details<ResourceRedirectDetails>(details).ptr(); |
+ if (redirect_details != NULL) |
+ BeforeUrlLoaded(redirect_details->new_url); |
+ break; |
+ } |
+ case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: { |
+ std::pair<content::NavigationEntry*, bool>* title = |
+ content::Details<std::pair<content::NavigationEntry*, bool> >( |
+ details).ptr(); |
+ |
+ if (title->first) { |
+ delegate_->OnAuthFlowTitleChange( |
+ UTF16ToUTF8(title->first->GetTitle())); |
+ } |
+ break; |
+ } |
+ default: |
+ NOTREACHED() |
+ << "Got a notification that we did not register for: " << type; |
+ break; |
} |
- break; |
- default: |
- NOTREACHED() << "Got a notification that we did not register for: " |
- << type; |
- break; |
} |
} |
-void WebAuthFlow::ProvisionalChangeToMainFrameUrl( |
- const GURL& url, |
+void WebAuthFlow::RenderViewGone(base::TerminationStatus status) { |
+ if (delegate_) |
+ 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 (is_main_frame) |
+ BeforeUrlLoaded(validated_url); |
+} |
+ |
+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 (delegate_) |
+ delegate_->OnAuthFlowFailure(LOAD_FAILED); |
} |
void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { |
AfterUrlLoaded(); |
} |
-void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { |
- contents_ = NULL; |
- if (delegate_) |
- delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); |
+void WebAuthFlow::DidNavigateMainFrame( |
+ const content::LoadCommittedDetails& details, |
+ const content::FrameNavigateParams& params) { |
+ if (delegate_ && details.http_status_code >= 400) |
+ delegate_->OnAuthFlowFailure(LOAD_FAILED); |
} |
} // namespace extensions |