2a88e4843ab0d1b0db134f8da2c4376904c91d30
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / identity / web_auth_flow.cc
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/web_auth_flow.h"
6
7 #include "apps/app_window.h"
8 #include "base/base64.h"
9 #include "base/location.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/extensions/component_loader.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/extensions/api/identity_private.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "content/public/browser/navigation_details.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/resource_request_details.h"
26 #include "content/public/browser/web_contents.h"
27 #include "crypto/random.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/extension_system.h"
30 #include "grit/browser_resources.h"
31 #include "url/gurl.h"
32
33 using apps::AppWindow;
34 using content::RenderViewHost;
35 using content::ResourceRedirectDetails;
36 using content::WebContents;
37 using content::WebContentsObserver;
38
39 namespace extensions {
40
41 namespace identity_private = api::identity_private;
42
43 WebAuthFlow::WebAuthFlow(
44     Delegate* delegate,
45     Profile* profile,
46     const GURL& provider_url,
47     Mode mode)
48     : delegate_(delegate),
49       profile_(profile),
50       provider_url_(provider_url),
51       mode_(mode),
52       embedded_window_created_(false) {
53 }
54
55 WebAuthFlow::~WebAuthFlow() {
56   DCHECK(delegate_ == NULL);
57
58   // Stop listening to notifications first since some of the code
59   // below may generate notifications.
60   registrar_.RemoveAll();
61   WebContentsObserver::Observe(NULL);
62
63   if (!app_window_key_.empty()) {
64     apps::AppWindowRegistry::Get(profile_)->RemoveObserver(this);
65
66     if (app_window_ && app_window_->web_contents())
67       app_window_->web_contents()->Close();
68   }
69 }
70
71 void WebAuthFlow::Start() {
72   apps::AppWindowRegistry::Get(profile_)->AddObserver(this);
73
74   // Attach a random ID string to the window so we can recoginize it
75   // in OnAppWindowAdded.
76   std::string random_bytes;
77   crypto::RandBytes(WriteInto(&random_bytes, 33), 32);
78   base::Base64Encode(random_bytes, &app_window_key_);
79
80   // identityPrivate.onWebFlowRequest(app_window_key, provider_url_, mode_)
81   scoped_ptr<base::ListValue> args(new base::ListValue());
82   args->AppendString(app_window_key_);
83   args->AppendString(provider_url_.spec());
84   if (mode_ == WebAuthFlow::INTERACTIVE)
85     args->AppendString("interactive");
86   else
87     args->AppendString("silent");
88
89   scoped_ptr<Event> event(
90       new Event(identity_private::OnWebFlowRequest::kEventName, args.Pass()));
91   event->restrict_to_browser_context = profile_;
92   ExtensionSystem* system = ExtensionSystem::Get(profile_);
93
94   extensions::ComponentLoader* component_loader =
95       system->extension_service()->component_loader();
96   if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) {
97     component_loader->Add(
98         IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST,
99         base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog")));
100   }
101
102   system->event_router()->DispatchEventWithLazyListener(
103       extension_misc::kIdentityApiUiAppId, event.Pass());
104 }
105
106 void WebAuthFlow::DetachDelegateAndDelete() {
107   delegate_ = NULL;
108   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
109 }
110
111 void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) {
112   if (app_window->window_key() == app_window_key_ &&
113       app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
114     app_window_ = app_window;
115     WebContentsObserver::Observe(app_window->web_contents());
116
117     registrar_.Add(
118         this,
119         content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
120         content::NotificationService::AllBrowserContextsAndSources());
121   }
122 }
123
124 void WebAuthFlow::OnAppWindowRemoved(AppWindow* app_window) {
125   if (app_window->window_key() == app_window_key_ &&
126       app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
127     app_window_ = NULL;
128     registrar_.RemoveAll();
129
130     if (delegate_)
131       delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
132   }
133 }
134
135 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
136   if (delegate_ && embedded_window_created_)
137     delegate_->OnAuthFlowURLChange(url);
138 }
139
140 void WebAuthFlow::AfterUrlLoaded() {
141   if (delegate_ && embedded_window_created_ && mode_ == WebAuthFlow::SILENT)
142     delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
143 }
144
145 void WebAuthFlow::Observe(int type,
146                           const content::NotificationSource& source,
147                           const content::NotificationDetails& details) {
148   DCHECK(app_window_);
149
150   if (!delegate_)
151     return;
152
153   if (!embedded_window_created_) {
154     DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED);
155
156     RenderViewHost* render_view(
157         content::Details<RenderViewHost>(details).ptr());
158     WebContents* web_contents = WebContents::FromRenderViewHost(render_view);
159
160     if (web_contents &&
161         (web_contents->GetEmbedderWebContents() ==
162          WebContentsObserver::web_contents())) {
163       // Switch from watching the app window to the guest inside it.
164       embedded_window_created_ = true;
165       WebContentsObserver::Observe(web_contents);
166
167       registrar_.RemoveAll();
168       registrar_.Add(this,
169                      content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
170                      content::Source<WebContents>(web_contents));
171       registrar_.Add(this,
172                      content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
173                      content::Source<WebContents>(web_contents));
174     }
175   } else {
176     // embedded_window_created_
177     switch (type) {
178       case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
179         ResourceRedirectDetails* redirect_details =
180             content::Details<ResourceRedirectDetails>(details).ptr();
181         if (redirect_details != NULL)
182           BeforeUrlLoaded(redirect_details->new_url);
183         break;
184       }
185       case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: {
186         std::pair<content::NavigationEntry*, bool>* title =
187             content::Details<std::pair<content::NavigationEntry*, bool> >(
188                 details).ptr();
189
190         if (title->first) {
191           delegate_->OnAuthFlowTitleChange(
192               base::UTF16ToUTF8(title->first->GetTitle()));
193         }
194         break;
195       }
196       default:
197         NOTREACHED()
198             << "Got a notification that we did not register for: " << type;
199         break;
200     }
201   }
202 }
203
204 void WebAuthFlow::RenderProcessGone(base::TerminationStatus status) {
205   if (delegate_)
206     delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
207 }
208
209 void WebAuthFlow::DidStartProvisionalLoadForFrame(
210     int64 frame_id,
211     int64 parent_frame_id,
212     bool is_main_frame,
213     const GURL& validated_url,
214     bool is_error_page,
215     bool is_iframe_srcdoc,
216     RenderViewHost* render_view_host) {
217   if (is_main_frame)
218     BeforeUrlLoaded(validated_url);
219 }
220
221 void WebAuthFlow::DidFailProvisionalLoad(
222     int64 frame_id,
223     const base::string16& frame_unique_name,
224     bool is_main_frame,
225     const GURL& validated_url,
226     int error_code,
227     const base::string16& error_description,
228     RenderViewHost* render_view_host) {
229   if (delegate_)
230     delegate_->OnAuthFlowFailure(LOAD_FAILED);
231 }
232
233 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) {
234   AfterUrlLoaded();
235 }
236
237 void WebAuthFlow::DidNavigateMainFrame(
238     const content::LoadCommittedDetails& details,
239     const content::FrameNavigateParams& params) {
240   if (delegate_ && details.http_status_code >= 400)
241     delegate_->OnAuthFlowFailure(LOAD_FAILED);
242 }
243
244 }  // namespace extensions