Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / share_dialog.js
1 // Copyright 2013 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 'use strict';
6
7 /**
8  * @param {HTMLElement} parentNode Node to be parent for this dialog.
9  * @constructor
10  * @extends {FileManagerDialogBase}
11  * @implements {ShareClient.Observer}
12  */
13 function ShareDialog(parentNode) {
14   this.queue_ = new AsyncUtil.Queue();
15   this.onQueueTaskFinished_ = null;
16   this.shareClient_ = null;
17   this.webViewWrapper_ = null;
18   this.webView_ = null;
19   this.failureTimeout_ = null;
20   this.callback_ = null;
21
22   FileManagerDialogBase.call(this, parentNode);
23 }
24
25 /**
26  * Timeout for loading the share dialog before giving up.
27  * @type {number}
28  * @const
29  */
30 ShareDialog.FAILURE_TIMEOUT = 10000;
31
32 /**
33  * The result of opening the dialog.
34  * @enum {string}
35  * @const
36  */
37 ShareDialog.Result = Object.freeze({
38   // The dialog is closed normally. This includes user cancel.
39   SUCCESS: 'success',
40   // The dialog is closed by network error.
41   NETWORK_ERROR: 'networkError',
42   // The dialog is not opened because it is already showing.
43   ALREADY_SHOWING: 'alreadyShowing'
44 });
45
46 /**
47  * Wraps a Web View element and adds authorization headers to it.
48  * @param {string} urlPattern Pattern of urls to be authorized.
49  * @param {WebView} webView Web View element to be wrapped.
50  * @constructor
51  */
52 ShareDialog.WebViewAuthorizer = function(urlPattern, webView) {
53   this.urlPattern_ = urlPattern;
54   this.webView_ = webView;
55   this.initialized_ = false;
56   this.accessToken_ = null;
57 };
58
59 /**
60  * Initializes the web view by installing hooks injecting the authorization
61  * headers.
62  * @param {function()} callback Completion callback.
63  */
64 ShareDialog.WebViewAuthorizer.prototype.initialize = function(callback) {
65   if (this.initialized_) {
66     callback();
67     return;
68   }
69
70   var registerInjectionHooks = function() {
71     this.webView_.removeEventListener('loadstop', registerInjectionHooks);
72     this.webView_.request.onBeforeSendHeaders.addListener(
73         this.authorizeRequest_.bind(this),
74         {urls: [this.urlPattern_]},
75         ['blocking', 'requestHeaders']);
76     this.initialized_ = true;
77     callback();
78   }.bind(this);
79
80   this.webView_.addEventListener('loadstop', registerInjectionHooks);
81   this.webView_.setAttribute('src', 'data:text/html,');
82 };
83
84 /**
85  * Authorizes the web view by fetching the freshest access tokens.
86  * @param {function()} callback Completion callback.
87  */
88 ShareDialog.WebViewAuthorizer.prototype.authorize = function(callback) {
89   // Fetch or update the access token.
90   chrome.fileManagerPrivate.requestAccessToken(false,  // force_refresh
91       function(inAccessToken) {
92         this.accessToken_ = inAccessToken;
93         callback();
94       }.bind(this));
95 };
96
97 /**
98  * Injects headers into the passed request.
99  * @param {Event} e Request event.
100  * @return {{requestHeaders: HttpHeaders}} Modified headers.
101  * @private
102  */
103 ShareDialog.WebViewAuthorizer.prototype.authorizeRequest_ = function(e) {
104   e.requestHeaders.push({
105     name: 'Authorization',
106     value: 'Bearer ' + this.accessToken_
107   });
108   return {requestHeaders: e.requestHeaders};
109 };
110
111 ShareDialog.prototype = {
112   __proto__: FileManagerDialogBase.prototype
113 };
114
115 /**
116  * One-time initialization of DOM.
117  * @private
118  */
119 ShareDialog.prototype.initDom_ = function() {
120   FileManagerDialogBase.prototype.initDom_.call(this);
121   this.frame_.classList.add('share-dialog-frame');
122
123   this.webViewWrapper_ = this.document_.createElement('div');
124   this.webViewWrapper_.className = 'share-dialog-webview-wrapper';
125   this.cancelButton_.hidden = true;
126   this.okButton_.hidden = true;
127   this.frame_.insertBefore(this.webViewWrapper_,
128                            this.frame_.querySelector('.cr-dialog-buttons'));
129 };
130
131 /**
132  * @override
133  */
134 ShareDialog.prototype.onResized = function(width, height, callback) {
135   if (width && height) {
136     this.webViewWrapper_.style.width = width + 'px';
137     this.webViewWrapper_.style.height = height + 'px';
138     this.webView_.style.width = width + 'px';
139     this.webView_.style.height = height + 'px';
140   }
141   setTimeout(callback, 0);
142 };
143
144 /**
145  * @override
146  */
147 ShareDialog.prototype.onClosed = function() {
148   this.hide();
149 };
150
151 /**
152  * @override
153  */
154 ShareDialog.prototype.onLoaded = function() {
155   if (this.failureTimeout_) {
156     clearTimeout(this.failureTimeout_);
157     this.failureTimeout_ = null;
158   }
159
160   // Logs added temporarily to track crbug.com/288783.
161   console.debug('Loaded.');
162
163   this.okButton_.hidden = false;
164   this.webViewWrapper_.classList.add('loaded');
165   this.webView_.focus();
166 };
167
168 /**
169  * @override
170  */
171 ShareDialog.prototype.onLoadFailed = function() {
172   this.hideWithResult(ShareDialog.Result.NETWORK_ERROR);
173 };
174
175 /**
176  * @override
177  */
178 ShareDialog.prototype.hide = function(opt_onHide) {
179   this.hideWithResult(ShareDialog.Result.SUCCESS, opt_onHide);
180 };
181
182 /**
183  * Hide the dialog with the result and the callback.
184  * @param {ShareDialog.Result} result Result passed to the closing callback.
185  * @param {function()=} opt_onHide Callback called at the end of hiding.
186  */
187 ShareDialog.prototype.hideWithResult = function(result, opt_onHide) {
188   if (!this.isShowing())
189     return;
190
191   if (this.shareClient_) {
192     this.shareClient_.dispose();
193     this.shareClient_ = null;
194   }
195
196   this.webViewWrapper_.textContent = '';
197   if (this.failureTimeout_) {
198     clearTimeout(this.failureTimeout_);
199     this.failureTimeout_ = null;
200   }
201
202   FileManagerDialogBase.prototype.hide.call(
203       this,
204       function() {
205         if (opt_onHide)
206           opt_onHide();
207         this.callback_(result);
208         this.callback_ = null;
209       }.bind(this));
210 };
211
212 /**
213  * Shows the dialog.
214  * @param {FileEntry} entry Entry to share.
215  * @param {function(boolean)} callback Callback to be called when the showing
216  *     task is completed. The argument is whether to succeed or not. Note that
217  *     cancel is regarded as success.
218  */
219 ShareDialog.prototype.show = function(entry, callback) {
220   // If the dialog is already showing, return the error.
221   if (this.isShowing()) {
222     callback(ShareDialog.Result.ALREADY_SHOWING);
223     return;
224   }
225
226   // Initialize the variables.
227   this.callback_ = callback;
228   this.webViewWrapper_.style.width = '';
229   this.webViewWrapper_.style.height = '';
230   this.webViewWrapper_.classList.remove('loaded');
231
232   // If the embedded share dialog is not started within some time, then
233   // give up and show an error message.
234   this.failureTimeout_ = setTimeout(function() {
235     this.hideWithResult(ShareDialog.Result.NETWORK_ERROR);
236
237     // Logs added temporarily to track crbug.com/288783.
238     console.debug('Timeout. Web View points at: ' + this.webView_.src);
239   }.bind(this), ShareDialog.FAILURE_TIMEOUT);
240
241   // TODO(mtomasz): Move to initDom_() once and reuse <webview> once it gets
242   // fixed. See: crbug.com/260622.
243   this.webView_ = util.createChild(
244       this.webViewWrapper_, 'share-dialog-webview', 'webview');
245   this.webView_.setAttribute('tabIndex', '-1');
246   this.webViewAuthorizer_ = new ShareDialog.WebViewAuthorizer(
247       !window.IN_TEST ? (ShareClient.SHARE_TARGET + '/*') : '<all_urls>',
248       this.webView_);
249   this.webView_.addEventListener('newwindow', function(e) {
250     // Discard the window object and reopen in an external window.
251     e.window.discard();
252     util.visitURL(e.targetUrl);
253     e.preventDefault();
254   });
255   var show = FileManagerDialogBase.prototype.showBlankDialog.call(this);
256   if (!show) {
257     // The code shoundn't get here, since already-showing was handled before.
258     console.error('ShareDialog can\'t be shown.');
259     return;
260   }
261
262   // Initialize and authorize the Web View tag asynchronously.
263   var group = new AsyncUtil.Group();
264
265   // Fetches an url to the sharing dialog.
266   var shareUrl;
267   group.add(function(inCallback) {
268     chrome.fileManagerPrivate.getShareUrl(
269         entry.toURL(),
270         function(inShareUrl) {
271           if (!chrome.runtime.lastError)
272             shareUrl = inShareUrl;
273           else
274             console.error(chrome.runtime.lastError.message);
275           inCallback();
276         });
277   });
278   group.add(this.webViewAuthorizer_.initialize.bind(this.webViewAuthorizer_));
279   group.add(this.webViewAuthorizer_.authorize.bind(this.webViewAuthorizer_));
280
281   // Loads the share widget once all the previous async calls are finished.
282   group.run(function() {
283     // If the url is not obtained, return the network error.
284     if (!shareUrl) {
285       // Logs added temporarily to track crbug.com/288783.
286       console.debug('The share URL is not available.');
287
288       this.hideWithResult(ShareDialog.Result.NETWORK_ERROR);
289       return;
290     }
291     // Already inactive, therefore ignore.
292     if (!this.isShowing())
293       return;
294     this.shareClient_ = new ShareClient(this.webView_,
295                                         shareUrl,
296                                         this);
297     this.shareClient_.load();
298   }.bind(this));
299 };
300
301 /**
302  * Tells whether the share dialog is showing or not.
303  *
304  * @return {boolean} True since the show method is called and until the closing
305  *     callback is invoked.
306  */
307 ShareDialog.prototype.isShowing = function() {
308   return !!this.callback_;
309 };