1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/identity/experimental_web_auth_flow.h" 6 7 #include "base/location.h" 8 #include "base/message_loop/message_loop.h" 9 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/ui/browser.h" 11 #include "chrome/browser/ui/browser_navigator.h" 12 #include "content/public/browser/load_notification_details.h" 13 #include "content/public/browser/navigation_controller.h" 14 #include "content/public/browser/notification_details.h" 15 #include "content/public/browser/notification_source.h" 16 #include "content/public/browser/notification_types.h" 17 #include "content/public/browser/resource_request_details.h" 18 #include "content/public/browser/web_contents.h" 19 #include "content/public/common/page_transition_types.h" 20 #include "ui/base/window_open_disposition.h" 21 #include "url/gurl.h" 22 23 using content::LoadNotificationDetails; 24 using content::NavigationController; 25 using content::RenderViewHost; 26 using content::ResourceRedirectDetails; 27 using content::WebContents; 28 using content::WebContentsObserver; 29 30 namespace extensions { 31 32 ExperimentalWebAuthFlow::ExperimentalWebAuthFlow( 33 Delegate* delegate, 34 Profile* profile, 35 const GURL& provider_url, 36 Mode mode, 37 const gfx::Rect& initial_bounds, 38 chrome::HostDesktopType host_desktop_type) 39 : delegate_(delegate), 40 profile_(profile), 41 provider_url_(provider_url), 42 mode_(mode), 43 initial_bounds_(initial_bounds), 44 host_desktop_type_(host_desktop_type), 45 popup_shown_(false), 46 contents_(NULL) { 47 } 48 49 ExperimentalWebAuthFlow::~ExperimentalWebAuthFlow() { 50 DCHECK(delegate_ == NULL); 51 52 // Stop listening to notifications first since some of the code 53 // below may generate notifications. 54 registrar_.RemoveAll(); 55 WebContentsObserver::Observe(NULL); 56 57 if (contents_) { 58 // The popup owns the contents if it was displayed. 59 if (popup_shown_) 60 contents_->Close(); 61 else 62 delete contents_; 63 } 64 } 65 66 void ExperimentalWebAuthFlow::Start() { 67 contents_ = CreateWebContents(); 68 WebContentsObserver::Observe(contents_); 69 70 NavigationController* controller = &(contents_->GetController()); 71 72 // Register for appropriate notifications to intercept navigation to the 73 // redirect URLs. 74 registrar_.Add( 75 this, 76 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 77 content::Source<WebContents>(contents_)); 78 79 controller->LoadURL( 80 provider_url_, 81 content::Referrer(), 82 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 83 std::string()); 84 } 85 86 void ExperimentalWebAuthFlow::DetachDelegateAndDelete() { 87 delegate_ = NULL; 88 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 89 } 90 91 WebContents* ExperimentalWebAuthFlow::CreateWebContents() { 92 return WebContents::Create(WebContents::CreateParams(profile_)); 93 } 94 95 void ExperimentalWebAuthFlow::ShowAuthFlowPopup() { 96 Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile_, 97 host_desktop_type_); 98 browser_params.initial_bounds = initial_bounds_; 99 Browser* browser = new Browser(browser_params); 100 chrome::NavigateParams params(browser, contents_); 101 params.disposition = CURRENT_TAB; 102 params.window_action = chrome::NavigateParams::SHOW_WINDOW; 103 chrome::Navigate(¶ms); 104 // Observe method and WebContentsObserver::* methods will be called 105 // for varous navigation events. That is where we check for redirect 106 // to the right URL. 107 popup_shown_ = true; 108 } 109 110 void ExperimentalWebAuthFlow::BeforeUrlLoaded(const GURL& url) { 111 if (delegate_) 112 delegate_->OnAuthFlowURLChange(url); 113 } 114 115 void ExperimentalWebAuthFlow::AfterUrlLoaded() { 116 // Do nothing if a popup is already created. 117 if (popup_shown_) 118 return; 119 120 // Report results directly if not in interactive mode. 121 if (mode_ != ExperimentalWebAuthFlow::INTERACTIVE) { 122 if (delegate_) { 123 delegate_->OnAuthFlowFailure( 124 ExperimentalWebAuthFlow::INTERACTION_REQUIRED); 125 } 126 return; 127 } 128 129 // We are in interactive mode and window is not shown yet; show the window. 130 ShowAuthFlowPopup(); 131 } 132 133 void ExperimentalWebAuthFlow::Observe(int type, 134 const content::NotificationSource& source, 135 const content::NotificationDetails& details) { 136 switch (type) { 137 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { 138 ResourceRedirectDetails* redirect_details = 139 content::Details<ResourceRedirectDetails>(details).ptr(); 140 if (redirect_details != NULL) 141 BeforeUrlLoaded(redirect_details->new_url); 142 } 143 break; 144 default: 145 NOTREACHED() << "Got a notification that we did not register for: " 146 << type; 147 break; 148 } 149 } 150 151 void ExperimentalWebAuthFlow::ProvisionalChangeToMainFrameUrl( 152 const GURL& url, 153 RenderViewHost* render_view_host) { 154 BeforeUrlLoaded(url); 155 } 156 157 void ExperimentalWebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { 158 AfterUrlLoaded(); 159 } 160 161 void ExperimentalWebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { 162 contents_ = NULL; 163 if (delegate_) 164 delegate_->OnAuthFlowFailure(ExperimentalWebAuthFlow::WINDOW_CLOSED); 165 } 166 167 } // namespace extensions 168