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