diff options
author | dcheng@chromium.org <dcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 17:42:51 +0000 |
---|---|---|
committer | dcheng@chromium.org <dcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-31 17:42:51 +0000 |
commit | 9faccc4fecfc865078149ae258aa53452831ec9b (patch) | |
tree | d91f46b33fe9f47bef99cbb50de6c953af7ae0cc | |
parent | b7d2489365a2d91f13a966cc47ca88751567a024 (diff) | |
download | chromium_src-9faccc4fecfc865078149ae258aa53452831ec9b.zip chromium_src-9faccc4fecfc865078149ae258aa53452831ec9b.tar.gz chromium_src-9faccc4fecfc865078149ae258aa53452831ec9b.tar.bz2 |
Revert 203417 "Identity API: switch WebAuthFlow dialog to compon..."
> Identity API: switch WebAuthFlow dialog to component app
>
> All Identity API UI is moving into a webview tag inside a component
> app, allowing Chrome Apps to be isolated from the browser.
>
> This change updates WebAuthFlow, which controls the dialogs, to invoke
> the component app instead of a browser pop-up.
>
> BUG=228908
> (This is part of step #6 in the description.)
>
> Review URL: https://chromiumcodereview.appspot.com/15897006
TBR=courage@chromium.org
Review URL: https://codereview.chromium.org/16212008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203432 0039d316-1c4b-4281-b951-d872f2087c98
10 files changed, 161 insertions, 322 deletions
diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc index cdfa8ff..d2b98eb 100644 --- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc +++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc @@ -15,10 +15,12 @@ namespace extensions { GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate, Profile* profile, + chrome::HostDesktopType host_desktop_type, const std::string& extension_id, const OAuth2Info& oauth2_info) : delegate_(delegate), - profile_(profile) { + profile_(profile), + host_desktop_type_(host_desktop_type) { const char kOAuth2RedirectPathFormat[] = "/%s#"; const char kOAuth2AuthorizeFormat[] = "%s?response_type=token&approval_prompt=force&authuser=0&" @@ -77,23 +79,9 @@ void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) { } void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { - GaiaWebAuthFlow::Failure gaia_failure; - - switch (failure) { - case WebAuthFlow::WINDOW_CLOSED: - gaia_failure = GaiaWebAuthFlow::WINDOW_CLOSED; - break; - case WebAuthFlow::LOAD_FAILED: - gaia_failure = GaiaWebAuthFlow::LOAD_FAILED; - break; - default: - NOTREACHED() << "Unexpected error from web auth flow: " << failure; - gaia_failure = GaiaWebAuthFlow::LOAD_FAILED; - break; - } - + DCHECK(failure == WebAuthFlow::WINDOW_CLOSED); delegate_->OnGaiaFlowFailure( - gaia_failure, + GaiaWebAuthFlow::WINDOW_CLOSED, GoogleServiceAuthError(GoogleServiceAuthError::NONE), std::string()); } @@ -163,10 +151,13 @@ void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) { } scoped_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) { + gfx::Rect initial_bounds; return scoped_ptr<WebAuthFlow>(new WebAuthFlow(this, profile_, url, - WebAuthFlow::INTERACTIVE)); + WebAuthFlow::INTERACTIVE, + initial_bounds, + host_desktop_type_)); } } // extensions diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h index c77ddf3..8f9dba7 100644 --- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h +++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h @@ -42,14 +42,12 @@ class GaiaWebAuthFlow : public UbertokenConsumer, public WebAuthFlow::Delegate { WINDOW_CLOSED, // Window closed by user. INVALID_REDIRECT, // Redirect parse error. SERVICE_AUTH_ERROR, // Non-OAuth related authentication error - OAUTH_ERROR, // Flow reached final redirect, which contained an error. - LOAD_FAILED // An auth flow page failed to load. + OAUTH_ERROR // Flow reached final redirect, which contained an error. }; class Delegate { public: // Called when the flow fails prior to the final OAuth redirect, - // TODO(courage): LOAD_FAILURE descriptions? virtual void OnGaiaFlowFailure(Failure failure, GoogleServiceAuthError service_error, const std::string& oauth_error) = 0; @@ -60,6 +58,7 @@ class GaiaWebAuthFlow : public UbertokenConsumer, public WebAuthFlow::Delegate { GaiaWebAuthFlow(Delegate* delegate, Profile* profile, + chrome::HostDesktopType host_desktop_type, const std::string& extension_id, const OAuth2Info& oauth2_info); virtual ~GaiaWebAuthFlow(); diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc b/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc index 01fed2d..787351fd 100644 --- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc +++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc @@ -20,7 +20,9 @@ class FakeWebAuthFlow : public WebAuthFlow { : WebAuthFlow(delegate, NULL, GURL(), - WebAuthFlow::INTERACTIVE) {} + WebAuthFlow::INTERACTIVE, + gfx::Rect(), + chrome::GetActiveDesktop()) {} virtual void Start() OVERRIDE {} }; @@ -33,6 +35,7 @@ class TestGaiaWebAuthFlow : public GaiaWebAuthFlow { GoogleServiceAuthError::State ubertoken_error_state) : GaiaWebAuthFlow(delegate, NULL, + chrome::GetActiveDesktop(), "extension_id", oauth2_info), ubertoken_error_(ubertoken_error_state) {} diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc index ce648c6..e8f0aaf 100644 --- a/chrome/browser/extensions/api/identity/identity_api.cc +++ b/chrome/browser/extensions/api/identity/identity_api.cc @@ -15,19 +15,24 @@ #include "base/values.h" #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/signin/token_service.h" #include "chrome/browser/signin/token_service_factory.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/extensions/api/identity.h" #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_manifest_constants.h" #include "chrome/common/url_constants.h" +#include "content/public/common/page_transition_types.h" #include "google_apis/gaia/gaia_constants.h" #include "googleurl/src/gurl.h" +#include "ui/base/window_open_disposition.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/user_manager.h" @@ -45,7 +50,6 @@ const char kUserNotSignedIn[] = "The user is not signed in."; const char kInteractionRequired[] = "User interaction required."; const char kInvalidRedirect[] = "Did not redirect to the right URL."; const char kOffTheRecord[] = "Identity API is disabled in incognito windows."; -const char kPageLoadFailure[] = "Authorization page could not be loaded."; const int kCachedIssueAdviceTTLSeconds = 1; } // namespace identity_constants @@ -313,12 +317,6 @@ void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( error = MapOAuth2ErrorToDescription(oauth_error); break; - // TODO(courage): load failure tests - - case GaiaWebAuthFlow::LOAD_FAILED: - error = identity_constants::kPageLoadFailure; - break; - default: NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; error = identity_constants::kInvalidRedirect; @@ -358,10 +356,14 @@ void IdentityGetAuthTokenFunction::ShowLoginPopup() { void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( const IssueAdviceInfo& issue_advice) { + Browser* current_browser = this->GetCurrentBrowser(); + chrome::HostDesktopType host_desktop_type = + current_browser ? current_browser->host_desktop_type() + : chrome::GetActiveDesktop(); const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); gaia_web_auth_flow_.reset(new GaiaWebAuthFlow( - this, profile(), GetExtension()->id(), oauth2_info)); + this, profile(), host_desktop_type, GetExtension()->id(), oauth2_info)); gaia_web_auth_flow_->Start(); } @@ -457,9 +459,16 @@ bool IdentityLaunchWebAuthFlowFunction::RunImpl() { // scheme for this version of the API.) InitFinalRedirectURLPrefix(GetExtension()->id()); + gfx::Rect initial_bounds; + AddRef(); // Balanced in OnAuthFlowSuccess/Failure. - auth_flow_.reset(new WebAuthFlow(this, profile(), auth_url, mode)); + Browser* current_browser = this->GetCurrentBrowser(); + chrome::HostDesktopType host_desktop_type = current_browser ? + current_browser->host_desktop_type() : chrome::GetActiveDesktop(); + auth_flow_.reset(new WebAuthFlow( + this, profile(), auth_url, mode, initial_bounds, + host_desktop_type)); auth_flow_->Start(); return true; } @@ -486,9 +495,6 @@ void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure( case WebAuthFlow::INTERACTION_REQUIRED: error_ = identity_constants::kInteractionRequired; break; - case WebAuthFlow::LOAD_FAILED: - error_ = identity_constants::kPageLoadFailure; - break; default: NOTREACHED() << "Unexpected error from web auth flow: " << failure; error_ = identity_constants::kInvalidRedirect; diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h index b2b85c3..dc63c42 100644 --- a/chrome/browser/extensions/api/identity/identity_api.h +++ b/chrome/browser/extensions/api/identity/identity_api.h @@ -41,7 +41,6 @@ extern const char kUserNotSignedIn[]; extern const char kInteractionRequired[]; extern const char kInvalidRedirect[]; extern const char kOffTheRecord[]; -extern const char kPageLoadFailure[]; } // namespace identity_constants // identity.getAuthToken fetches an OAuth 2 function for the diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc index 1ca9740..6514e1e 100644 --- a/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chrome/browser/extensions/api/identity/identity_apitest.cc @@ -6,7 +6,6 @@ #include "base/stringprintf.h" #include "base/values.h" #include "chrome/browser/extensions/api/identity/identity_api.h" -#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" @@ -190,37 +189,6 @@ BrowserContextKeyedService* IdentityAPITestFactory( return new IdentityAPI(static_cast<Profile*>(profile)); } -// Waits for a specific GURL to generate a NOTIFICATION_LOAD_STOP -// event, and closes the window embedding the webcontents. -class WaitForGURLAndCloseWindow : public content::WindowedNotificationObserver { - public: - explicit WaitForGURLAndCloseWindow(GURL url) - : WindowedNotificationObserver( - content::NOTIFICATION_LOAD_STOP, - content::NotificationService::AllSources()), - url_(url) {} - - // NotificationObserver: - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE { - content::NavigationController* web_auth_flow_controller = - content::Source<content::NavigationController>(source).ptr(); - content::WebContents* web_contents = - web_auth_flow_controller->GetWebContents(); - - if (web_contents->GetURL() == url_) { - web_contents->GetEmbedderWebContents()->Close(); - // Condtionally invoke parent class so that Wait will not exit - // until the target URL arrives. - content::WindowedNotificationObserver::Observe(type, source, details); - } - } - - private: - GURL url_; -}; - } // namespace class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction { @@ -577,23 +545,6 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, - InteractiveApprovalLoadFailed) { - scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); - func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); - EXPECT_CALL(*func.get(), HasLoginToken()) - .WillOnce(Return(true)); - TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( - TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); - EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); - func->set_scope_ui_failure(GaiaWebAuthFlow::LOAD_FAILED); - std::string error = utils::RunFunctionAndReturnError( - func.get(), "[{\"interactive\": true}]", browser()); - EXPECT_EQ(std::string(errors::kPageLoadFailure), error); - EXPECT_FALSE(func->login_ui_shown()); - EXPECT_TRUE(func->scope_ui_shown()); -} - -IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalInvalidRedirect) { scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); @@ -1010,13 +961,16 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) { utils::CreateEmptyExtension()); function->set_extension(empty_extension.get()); - WaitForGURLAndCloseWindow popup_observer(auth_url); + content::WindowedNotificationObserver popup_observer( + chrome::NOTIFICATION_BROWSER_WINDOW_READY, + content::NotificationService::AllSources()); std::string args = "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]"; RunFunctionAsync(function, args); popup_observer.Wait(); + content::Source<Browser>(popup_observer.source())->window()->Close(); EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function)); } @@ -1044,29 +998,6 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) { EXPECT_EQ(std::string(errors::kInteractionRequired), error); } -IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) { - net::SpawnedTestServer https_server( - net::SpawnedTestServer::TYPE_HTTPS, - net::SpawnedTestServer::kLocalhost, - base::FilePath(FILE_PATH_LITERAL( - "chrome/test/data/extensions/api_test/identity"))); - ASSERT_TRUE(https_server.Start()); - GURL auth_url(https_server.GetURL("files/five_hundred.html")); - - scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( - new IdentityLaunchWebAuthFlowFunction()); - scoped_refptr<Extension> empty_extension( - utils::CreateEmptyExtension()); - function->set_extension(empty_extension.get()); - - std::string args = "[{\"interactive\": true, \"url\": \"" + - auth_url.spec() + "\"}]"; - std::string error = utils::RunFunctionAndReturnError(function, args, - browser()); - - EXPECT_EQ(std::string(errors::kPageLoadFailure), error); -} - IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) { scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( new IdentityLaunchWebAuthFlowFunction()); diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chrome/browser/extensions/api/identity/web_auth_flow.cc index fc14858..d4347d4 100644 --- a/chrome/browser/extensions/api/identity/web_auth_flow.cc +++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc @@ -4,31 +4,26 @@ #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/extensions/shell_window.h" -#include "chrome/common/extensions/extension_constants.h" -#include "content/public/browser/navigation_details.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 "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 "crypto/random.h" +#include "content/public/common/page_transition_types.h" #include "googleurl/src/gurl.h" -#include "grit/browser_resources.h" +#include "ui/base/window_open_disposition.h" +using content::LoadNotificationDetails; +using content::NavigationController; using content::RenderViewHost; using content::ResourceRedirectDetails; using content::WebContents; @@ -40,12 +35,17 @@ WebAuthFlow::WebAuthFlow( Delegate* delegate, Profile* profile, const GURL& provider_url, - Mode mode) + Mode mode, + const gfx::Rect& initial_bounds, + chrome::HostDesktopType host_desktop_type) : delegate_(delegate), profile_(profile), provider_url_(provider_url), mode_(mode), - embedded_window_created_(false) { + initial_bounds_(initial_bounds), + host_desktop_type_(host_desktop_type), + popup_shown_(false), + contents_(NULL) { } WebAuthFlow::~WebAuthFlow() { @@ -56,53 +56,37 @@ WebAuthFlow::~WebAuthFlow() { registrar_.RemoveAll(); WebContentsObserver::Observe(NULL); - if (!shell_window_key_.empty()) { - ShellWindowRegistry::Get(profile_)->RemoveObserver(this); - - if (shell_window_ && shell_window_->web_contents()) - shell_window_->web_contents()->Close(); + if (contents_) { + // The popup owns the contents if it was displayed. + if (popup_shown_) + contents_->Close(); + else + delete contents_; } } void WebAuthFlow::Start() { - 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); + 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()); } void WebAuthFlow::DetachDelegateAndDelete() { @@ -110,135 +94,87 @@ void WebAuthFlow::DetachDelegateAndDelete() { base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); } -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()); - } +WebContents* WebAuthFlow::CreateWebContents() { + return WebContents::Create(WebContents::CreateParams(profile_)); } -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::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::BeforeUrlLoaded(const GURL& url) { - if (delegate_ && embedded_window_created_) + if (delegate_) delegate_->OnAuthFlowURLChange(url); } void WebAuthFlow::AfterUrlLoaded() { - if (delegate_ && embedded_window_created_ && mode_ == WebAuthFlow::SILENT) - delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); + // 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(); } void WebAuthFlow::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { - 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)); + 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); } - } 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; + 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; } } -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, +void WebAuthFlow::ProvisionalChangeToMainFrameUrl( + const GURL& url, RenderViewHost* render_view_host) { - 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); + BeforeUrlLoaded(url); } void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { AfterUrlLoaded(); } -void WebAuthFlow::DidNavigateMainFrame( - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params) { - if (delegate_ && details.http_status_code >= 400) - delegate_->OnAuthFlowFailure(LOAD_FAILED); +void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { + contents_ = NULL; + if (delegate_) + delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); } } // namespace extensions diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.h b/chrome/browser/extensions/api/identity/web_auth_flow.h index df6c0f9..e2e945b 100644 --- a/chrome/browser/extensions/api/identity/web_auth_flow.h +++ b/chrome/browser/extensions/api/identity/web_auth_flow.h @@ -5,9 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_WEB_AUTH_FLOW_H_ #define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_WEB_AUTH_FLOW_H_ -#include <string> - -#include "chrome/browser/extensions/shell_window_registry.h" +#include "chrome/browser/ui/host_desktop.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_observer.h" @@ -26,17 +24,13 @@ class WebContents; namespace extensions { -// Controller class for web based auth flows. The WebAuthFlow creates -// a dialog window in the scope approval component app by firing an -// event. A webview embedded in the dialog will navigate to the -// |provider_url| passed to the WebAuthFlow constructor. -// -// The WebAuthFlow monitors the WebContents of the webview, and -// notifies its delegate interface any time the WebContents navigates -// to a new URL or changes title. The delegate is expected to delete -// the flow when navigation reaches a known target location. +// Controller class for web based auth flows. The WebAuthFlow starts +// by navigating a WebContents to a URL specificed by the caller. Any +// time the WebContents navigates to a new URL, the flow's delegate is +// notified. The delegate is expected to delete the flow when +// navigation reaches a known target URL. // -// The window is not displayed until the first page load +// The WebContents is not displayed until the first page load // completes. This allows the flow to complete without flashing a // window on screen if the provider immediately redirects to the // target URL. @@ -44,8 +38,7 @@ namespace extensions { // A WebAuthFlow can be started in Mode::SILENT, which never displays // a window. If a window would be required, the flow fails. class WebAuthFlow : public content::NotificationObserver, - public content::WebContentsObserver, - public ShellWindowRegistry::Observer { + public content::WebContentsObserver { public: enum Mode { INTERACTIVE, // Show UI to the user if necessary. @@ -54,8 +47,7 @@ class WebAuthFlow : public content::NotificationObserver, enum Failure { WINDOW_CLOSED, // Window closed by user. - INTERACTION_REQUIRED, // Non-redirect page load in silent mode. - LOAD_FAILED + INTERACTION_REQUIRED // Non-redirect page load in silent mode. }; class Delegate { @@ -78,7 +70,9 @@ class WebAuthFlow : public content::NotificationObserver, WebAuthFlow(Delegate* delegate, Profile* profile, const GURL& provider_url, - Mode mode); + Mode mode, + const gfx::Rect& initial_bounds, + chrome::HostDesktopType host_desktop_type); virtual ~WebAuthFlow(); @@ -88,41 +82,27 @@ class WebAuthFlow : public content::NotificationObserver, // Prevents further calls to the delegate and deletes the flow. void DetachDelegateAndDelete(); + protected: + // Overridable for testing. + virtual content::WebContents* CreateWebContents(); + virtual void ShowAuthFlowPopup(); + private: friend class ::WebAuthFlowTest; - // ShellWindowRegistry::Observer implementation. - virtual void OnShellWindowAdded(ShellWindow* shell_window) OVERRIDE; - virtual void OnShellWindowIconChanged(ShellWindow* shell_window) OVERRIDE {} - virtual void OnShellWindowRemoved(ShellWindow* shell_window) OVERRIDE; - // NotificationObserver implementation. virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; // WebContentsObserver implementation. - virtual void DidStopLoading(content::RenderViewHost* render_view_host) - OVERRIDE; - virtual void DidNavigateMainFrame( - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params) OVERRIDE; - virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE; - virtual void DidStartProvisionalLoadForFrame( - int64 frame_id, - int64 parent_frame_id, - bool is_main_frame, - const GURL& validated_url, - bool is_error_page, - bool is_iframe_srcdoc, + virtual void ProvisionalChangeToMainFrameUrl( + const GURL& url, + content::RenderViewHost* render_view_host) OVERRIDE; + virtual void DidStopLoading( content::RenderViewHost* render_view_host) OVERRIDE; - virtual void DidFailProvisionalLoad(int64 frame_id, - bool is_main_frame, - const GURL& validated_url, - int error_code, - const string16& error_description, - content::RenderViewHost* render_view_host) - OVERRIDE; + virtual void WebContentsDestroyed( + content::WebContents* web_contents) OVERRIDE; void BeforeUrlLoaded(const GURL& url); void AfterUrlLoaded(); @@ -131,11 +111,11 @@ class WebAuthFlow : public content::NotificationObserver, Profile* profile_; GURL provider_url_; Mode mode_; + gfx::Rect initial_bounds_; + chrome::HostDesktopType host_desktop_type_; + bool popup_shown_; - ShellWindow* shell_window_; - std::string shell_window_key_; - bool embedded_window_created_; - + content::WebContents* contents_; content::NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(WebAuthFlow); diff --git a/chrome/test/data/extensions/api_test/identity/five_hundred.html b/chrome/test/data/extensions/api_test/identity/five_hundred.html deleted file mode 100644 index 70db0f2..0000000 --- a/chrome/test/data/extensions/api_test/identity/five_hundred.html +++ /dev/null @@ -1,4 +0,0 @@ -<html> -<head><title>OK</title></head> -<body></body> -</html> diff --git a/chrome/test/data/extensions/api_test/identity/five_hundred.html.mock-http-headers b/chrome/test/data/extensions/api_test/identity/five_hundred.html.mock-http-headers deleted file mode 100644 index f7c3201..0000000 --- a/chrome/test/data/extensions/api_test/identity/five_hundred.html.mock-http-headers +++ /dev/null @@ -1,2 +0,0 @@ -HTTP/1.1 500 Internal Server Error - |