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.
6 * @param {HTMLElement} parentNode Node to be parent for this dialog.
8 * @extends {FileManagerDialogBase}
9 * @implements {ShareClient.Observer}
11 function ShareDialog(parentNode) {
12 this.queue_ = new AsyncUtil.Queue();
13 this.onQueueTaskFinished_ = null;
14 this.shareClient_ = null;
15 this.webViewWrapper_ = null;
17 this.failureTimeout_ = null;
18 this.callback_ = null;
20 FileManagerDialogBase.call(this, parentNode);
24 * Timeout for loading the share dialog before giving up.
28 ShareDialog.FAILURE_TIMEOUT = 10000;
31 * The result of opening the dialog.
35 ShareDialog.Result = {
36 // The dialog is closed normally. This includes user cancel.
38 // The dialog is closed by network error.
39 NETWORK_ERROR: 'networkError',
40 // The dialog is not opened because it is already showing.
41 ALREADY_SHOWING: 'alreadyShowing'
43 Object.freeze(ShareDialog.Result);
46 * Wraps a Web View element and adds authorization headers to it.
47 * @param {string} urlPattern Pattern of urls to be authorized.
48 * @param {Element} webView Web View element to be wrapped.
51 ShareDialog.WebViewAuthorizer = function(urlPattern, webView) {
52 this.urlPattern_ = urlPattern;
53 this.webView_ = webView;
54 this.initialized_ = false;
55 this.accessToken_ = null;
59 * Initializes the web view by installing hooks injecting the authorization
61 * @param {function()} callback Completion callback.
63 ShareDialog.WebViewAuthorizer.prototype.initialize = function(callback) {
64 if (this.initialized_) {
69 var registerInjectionHooks = function() {
70 this.webView_.removeEventListener('loadstop', registerInjectionHooks);
71 this.webView_.request.onBeforeSendHeaders.addListener(
72 this.authorizeRequest_.bind(this),
73 {urls: [this.urlPattern_]},
74 ['blocking', 'requestHeaders']);
75 this.initialized_ = true;
79 this.webView_.addEventListener('loadstop', registerInjectionHooks);
80 this.webView_.setAttribute('src', 'data:text/html,');
84 * Authorizes the web view by fetching the freshest access tokens.
85 * @param {function()} callback Completion callback.
87 ShareDialog.WebViewAuthorizer.prototype.authorize = function(callback) {
88 // Fetch or update the access token.
89 chrome.fileManagerPrivate.requestAccessToken(false, // force_refresh
90 function(inAccessToken) {
91 this.accessToken_ = inAccessToken;
97 * Injects headers into the passed request.
98 * @param {!Object} e Request event.
99 * @return {!{requestHeaders: Array.<!HttpHeader>}} Modified headers.
102 ShareDialog.WebViewAuthorizer.prototype.authorizeRequest_ = function(e) {
103 e.requestHeaders.push({
104 name: 'Authorization',
105 value: 'Bearer ' + this.accessToken_
107 return {requestHeaders: e.requestHeaders};
110 ShareDialog.prototype = {
111 __proto__: FileManagerDialogBase.prototype
115 * One-time initialization of DOM.
118 ShareDialog.prototype.initDom_ = function() {
119 FileManagerDialogBase.prototype.initDom_.call(this);
120 this.frame_.classList.add('share-dialog-frame');
122 this.webViewWrapper_ = this.document_.createElement('div');
123 this.webViewWrapper_.className = 'share-dialog-webview-wrapper';
124 this.cancelButton_.hidden = true;
125 this.okButton_.hidden = true;
126 this.frame_.insertBefore(this.webViewWrapper_,
127 this.frame_.querySelector('.cr-dialog-buttons'));
133 ShareDialog.prototype.onResized = function(width, height, callback) {
134 if (width && height) {
135 this.webViewWrapper_.style.width = width + 'px';
136 this.webViewWrapper_.style.height = height + 'px';
137 this.webView_.style.width = width + 'px';
138 this.webView_.style.height = height + 'px';
140 setTimeout(callback, 0);
146 ShareDialog.prototype.onClosed = function() {
153 ShareDialog.prototype.onLoaded = function() {
154 if (this.failureTimeout_) {
155 clearTimeout(this.failureTimeout_);
156 this.failureTimeout_ = null;
159 // Logs added temporarily to track crbug.com/288783.
160 console.debug('Loaded.');
162 this.okButton_.hidden = false;
163 this.webViewWrapper_.classList.add('loaded');
164 this.webView_.focus();
170 ShareDialog.prototype.onLoadFailed = function() {
171 this.hideWithResult(ShareDialog.Result.NETWORK_ERROR);
175 * @param {Function=} opt_onHide Called when the dialog is hidden.
178 ShareDialog.prototype.hide = function(opt_onHide) {
179 this.hideWithResult(ShareDialog.Result.SUCCESS, opt_onHide);
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.
187 ShareDialog.prototype.hideWithResult = function(result, opt_onHide) {
188 if (!this.isShowing())
191 if (this.shareClient_) {
192 this.shareClient_.dispose();
193 this.shareClient_ = null;
196 this.webViewWrapper_.textContent = '';
197 if (this.failureTimeout_) {
198 clearTimeout(this.failureTimeout_);
199 this.failureTimeout_ = null;
202 FileManagerDialogBase.prototype.hide.call(
207 this.callback_(result);
208 this.callback_ = null;
214 * @param {FileEntry} entry Entry to share.
215 * @param {function(ShareDialog.Result)} callback Callback to be called when the
216 * showing task is completed. The argument is whether to succeed or not.
217 * Note that cancel is regarded as success.
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);
226 // Initialize the variables.
227 this.callback_ = callback;
228 this.webViewWrapper_.style.width = '';
229 this.webViewWrapper_.style.height = '';
230 this.webViewWrapper_.classList.remove('loaded');
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);
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);
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>',
249 this.webView_.addEventListener('newwindow', function(e) {
250 // Discard the window object and reopen in an external window.
252 util.visitURL(e.targetUrl);
255 var show = FileManagerDialogBase.prototype.showBlankDialog.call(this);
257 // The code shoundn't get here, since already-showing was handled before.
258 console.error('ShareDialog can\'t be shown.');
262 // Initialize and authorize the Web View tag asynchronously.
263 var group = new AsyncUtil.Group();
265 // Fetches an url to the sharing dialog.
267 group.add(function(inCallback) {
268 chrome.fileManagerPrivate.getShareUrl(
270 function(inShareUrl) {
271 if (!chrome.runtime.lastError)
272 shareUrl = inShareUrl;
274 console.error(chrome.runtime.lastError.message);
278 group.add(this.webViewAuthorizer_.initialize.bind(this.webViewAuthorizer_));
279 group.add(this.webViewAuthorizer_.authorize.bind(this.webViewAuthorizer_));
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.
285 // Logs added temporarily to track crbug.com/288783.
286 console.debug('The share URL is not available.');
288 this.hideWithResult(ShareDialog.Result.NETWORK_ERROR);
291 // Already inactive, therefore ignore.
292 if (!this.isShowing())
294 this.shareClient_ = new ShareClient(this.webView_,
297 this.shareClient_.load();
302 * Tells whether the share dialog is showing or not.
304 * @return {boolean} True since the show method is called and until the closing
305 * callback is invoked.
307 ShareDialog.prototype.isShowing = function() {
308 return !!this.callback_;