Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: chrome/browser/resources/print_preview/print_preview_cloud.js

Issue 10108001: Refactor print preview web ui (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('cloudprint', function() { 5 cr.define('cloudprint', function() {
6 6 'use strict';
7 // The URL to use to access the cloud print servers. 7
8 // Set by a call to setBaseURL. 8 /**
9 var cloudPrintBaseURL = ''; 9 * API to the Google Cloud Print service.
10 10 *
11 // Headers to set for most cloud print API calls. 11 * @param {string} baseUrl Base part of the Google Cloud Print service URL
12 var xCloudPrintURLHeader = {'Content-Type': 12 * with no trailing slash. For example,
13 'application/x-www-form-urlencoded', 13 * 'https://www.google.com/cloudprint'.
14 'X-CloudPrint-Proxy': 'ChromePrintPreview'}; 14 * @constructor
15 15 * @extends {cr.EventTarget}
16 // Headers to set when sending multipart data to cloud print APIs. 16 */
17 // Currently only used when submitting a job. 17 function CloudPrintInterface(baseUrl) {
18 var xCloudPrintFormHeader = {'Content-Type': 18 /**
19 'multipart/form-data; boundary=----CloudPrintFormBoundaryjc9wuprokl8i', 19 * The base URL of the Google Cloud Print API.
20 'X-CloudPrint-Proxy': 'ChromePrintPreview'}; 20 * @type {string}
21 21 * @private
22 // The last received XSRF token. This should be sent with each request 22 */
23 // to prevent XSRF. 23 this.baseURL_ = baseUrl;
24 var lastXSRFToken = ''; 24
25 25 /**
26 /** 26 * Last received XSRF token. Sent as a parameter in every request.
27 * Sets the base URL to be used for communicating with cloud print 27 * @type {string}
28 * servers. 28 * @private
29 * @param {string} cloudPrintURL The URL to use. 29 */
30 */ 30 this.xsrfToken_ = '';
31 function setBaseURL(cloudPrintURL) { 31 };
32 cloudPrintBaseURL = cloudPrintURL; 32
33 } 33 /**
34 34 * Enumeration of events dispatched.
35 /** 35 * @enum {string}
36 * Gets the base URL to be used for communicating with cloud print 36 */
37 * servers. 37 CloudPrintInterface.Event = {
38 * @return {string} The URL. 38 ERROR: 'cloudprint.CloudPrintInterface.ERROR',
39 */ 39 PRINTER_DONE: 'cloudprint.CloudPrintInterface.PRINTER_DONE',
40 function getBaseURL() { 40 SEARCH_DONE: 'cloudprint.CloudPrintInterface.SEARCH_DONE',
41 return cloudPrintBaseURL; 41 SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE'
42 } 42 };
43 43
44 /** 44 /**
45 * Extracts the XSRF token from each response to be used in the next 45 * Content type header value for a URL encoded HTTP request.
46 * request. 46 * @type {string}
47 * @param {XMLHttpRequest} xhr The object used to make the request. 47 * @private
48 * @return {string} The extracted XSRF token. 48 */
49 */ 49 CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_ =
50 function extractXSRFtoken(xhr) { 50 'application/x-www-form-urlencoded';
51 if (xhr.status == 200) { 51
52 var result = JSON.parse(xhr.responseText); 52 /**
53 return result['xsrf_token']; 53 * Content type header value for a multipart HTTP request.
54 } else { 54 * @type {string}
55 return null; 55 * @private
56 */
57 CloudPrintInterface.MULTIPART_CONTENT_TYPE_ =
58 'multipart/form-data; boundary=----CloudPrintFormBoundaryjc9wuprokl8i';
59
60 CloudPrintInterface.prototype = {
61 __proto__: cr.EventTarget.prototype,
62
63 /**
64 * Sends a Google Cloud Print search API request.
65 * @param {boolean} isRecent Whether to search for only recently used
66 * printers.
67 */
68 search: function(isRecent) {
69 var params = {};
70 if (isRecent) {
71 params['q'] = '^recent';
72 }
73 this.sendRequest_('GET', 'search', params, null, this.onSearchDone_);
74 },
75
76 /**
77 * Sends a Google Cloud Print submit API request.
78 * @param {string} body Body of the HTTP post request to send.
79 */
80 submit: function(body) {
81 this.sendRequest_('POST', 'submit', null, body, this.onSubmitDone_);
82 },
83
84 /**
85 * Sends a Google Cloud Print printer API request.
86 * @param {string} printerId ID of the printer to lookup.
87 */
88 printer: function(printerId) {
89 var params = {'printerid': printerId};
90 this.sendRequest_('GET', 'printer', params, null, this.onPrinterDone_);
91 },
92
93 /**
94 * Sends a request to the Google Cloud Print API.
95 * @param {string} method HTTP method of the request.
96 * @param {string} action Google Cloud Print action to perform.
97 * @param {!Object} params HTTP parameters to include in the request.
98 * @param {string} body HTTP multi-part encoded body.
99 * @param {function(Object)} successCallback Callback to invoke when request
100 * completes successfully.
101 */
102 sendRequest_: function(method, action, params, body, successCallback) {
103 if (!this.xsrfToken_) {
104 // TODO Should throw an error if not a read-only action or issue an
105 // xsrf token request.
106 }
107 var url = this.baseURL_ + '/' + action + '?xsrf=' + this.xsrfToken_;
108
109 if (params) {
110 for (var paramName in params) {
111 url += '&' + paramName + '=' + encodeURIComponent(params[paramName]);
112 }
113 }
114
115 var headers = {};
116 headers['X-CloudPrint-Proxy'] = 'ChromePrintPreview';
117 if (method == 'GET') {
118 headers['Content-Type'] = CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_;
119 } else if (method == 'POST') {
120 headers['Content-Type'] = CloudPrintInterface.MULTIPART_CONTENT_TYPE_;
121 }
122
123 var xhr = new XMLHttpRequest();
124 xhr.onreadystatechange = this.onReadyStateChange_.bind(
125 this, xhr, successCallback.bind(this));
126 xhr.open(method, url, true);
127 xhr.withCredentials = true;
128 for (var header in headers) {
129 xhr.setRequestHeader(header, headers[header]);
130 }
131 xhr.send(body);
132 },
133
134 /**
135 * Dispatches an ERROR event with the given error message.
136 * @param {string} message Error message to include in the ERROR event.
137 * @private
138 */
139 dispatchErrorEvent_: function(message) {
140 var errorEvt = new cr.Event(CloudPrintInterface.Event.ERROR);
141 errorEvt.message = message;
142 this.dispatchEvent(errorEvt);
143 },
144
145 /**
146 * Called when the ready-state of a XML http request changes.
147 * Calls the successCallback with the result or dispatches an ERROR event.
148 * @param {XMLHttpRequest} xhr XML http request that changed.
149 * @param {function(Object)} successCallback Callback to call if the request
150 * was successful.
151 * @private
152 */
153 onReadyStateChange_: function(xhr, successCallback) {
154 if (xhr.readyState == 4) {
155 if (xhr.status == 200) {
156 var result = JSON.parse(xhr.responseText);
157 if (result['success']) {
158 this.xsrfToken_ = result['xsrf_token'];
159 successCallback(result);
160 } else {
161 this.dispatchErrorEvent_(result['message']);
162 }
163 } else {
164 this.dispatchErrorEvent_(xhr.status + '');
165 }
166 }
167 },
168
169 /**
170 * Called when the search request completes successfully.
171 * @param {Object} result JSON response.
172 * @private
173 */
174 onSearchDone_: function(result) {
175 var printerListJson = result['printers'] || [];
176 var printerList = [];
177 for (var printerJson, i = 0; printerJson = printerListJson[i]; i++) {
178 try {
179 printerList.push(cloudprint.CloudDestinationParser.parse(
180 printerJson));
181 } catch (err) {
182 // TODO Handle error case
183 }
184 }
185 var isRecent = result['request']['params']['q'] == '^recent';
186 var searchDoneEvt = new cr.Event(CloudPrintInterface.Event.SEARCH_DONE);
187 searchDoneEvt.printers = printerList;
188 searchDoneEvt.isRecent = isRecent;
189 searchDoneEvt.email = result['request']['user'];
190 this.dispatchEvent(searchDoneEvt);
191 },
192
193 /**
194 * Called when the submit request completes successfully.
195 * @param {Object} result JSON response.
196 * @private
197 */
198 onSubmitDone_: function(result) {
199 this.dispatchEvent(new cr.Event(CloudPrintInterface.Event.SUBMIT_DONE));
200 },
201
202 /**
203 * Called when the printer request completes successfully.
204 * @param {Object} result JSON response.
205 * @private
206 */
207 onPrinterDone_: function(result) {
208 // TODO Better error handling here.
209 var printerJson = result['printers'][0];
210 var printer;
211 try {
212 printer = cloudprint.CloudDestinationParser.parse(printerJson);
213 printer.capabilities = cloudprint.CloudCapabilitiesParser.parse(
214 printerJson['capabilities']);
215 } catch (err) {
216 // TODO Handle error case
217 return;
218 }
219 var printerDoneEvt = new cr.Event(CloudPrintInterface.Event.PRINTER_DONE);
220 printerDoneEvt.printer = printer;
221 this.dispatchEvent(printerDoneEvt);
56 } 222 }
57 } 223 };
58
59 /**
60 * Makes a request to cloud print servers.
61 * @param {string} method The HTTP method to be used.
62 * @param {string} action The cloud print API to call.
63 * @param {Array} headers Headers to send with the request.
64 * @param {string} body Body to be sent with POST requests.
65 * @param {function} callback Function to be called to process response.
66 * @param {boolean} async True if we want the request made asyncronously.
67 */
68 function sendCloudPrintRequest(method,
69 action,
70 headers,
71 params,
72 body,
73 callback) {
74 var xhr = new XMLHttpRequest();
75 if (callback != null) {
76 xhr.onreadystatechange = function() {
77 if (xhr.readyState == 4) {
78 var updatedXSRFToken = extractXSRFtoken(xhr);
79 if (updatedXSRFToken != null) {
80 lastXSRFToken = updatedXSRFToken;
81 }
82 callback.call(this, xhr);
83 }
84 };
85 }
86 var url = cloudPrintBaseURL + '/' + action;
87 if (params == null) {
88 params = new Array;
89 }
90 if (lastXSRFToken.length != 0) {
91 params.push('xsrf=' + lastXSRFToken);
92 }
93 if (params.length != 0) {
94 url = url + '?';
95 for (param in params) {
96 url = url + params[param] + '&';
97 }
98 }
99 xhr.open(method, url, true);
100 xhr.withCredentials = true;
101 if (headers) {
102 for (var header in headers) {
103 if (headers.hasOwnProperty(header)) {
104 xhr.setRequestHeader(header, headers[header]);
105 }
106 }
107 }
108 xhr.send(body);
109 return xhr;
110 }
111
112 /**
113 * Parse the response from the fetchPrinters call.
114 * @param {function} callback Function to be called to process response.
115 * @param {XMLHttpRequest} xhr The object used to make the request.
116 */
117 function fetchPrintersResponse(callback, xhr) {
118 if (xhr.status == 200) {
119 var searchResult = JSON.parse(xhr.responseText);
120 if (searchResult['success']) {
121 var printerList = searchResult['printers'];
122 addCloudPrinters(printerList, callback);
123 return;
124 }
125 }
126 addCloudPrinters(null, callback);
127 }
128
129 /**
130 * Retrieve the list of printers available via cloud print.
131 * @param {function} callback Function to be called to process response.
132 */
133 function fetchPrinters(callback, all) {
134 var query = 'q=^recent';
135 if (all) {
136 query = '';
137 }
138 sendCloudPrintRequest('GET',
139 'search',
140 xCloudPrintURLHeader,
141 [query],
142 null,
143 fetchPrintersResponse.bind(this, callback));
144 }
145
146 /**
147 * Handle the response from printing to cloud print.
148 * @param {function} callback Function to be called to process response.
149 * @param {XMLHttpRequest} xhr The object used to make the request.
150 */
151 function printToCloudResponse(callback, xhr) {
152 if (xhr.status == 200) {
153 var printResult = JSON.parse(xhr.responseText);
154 if (printResult['success']) {
155 callback.call();
156 }
157 }
158 // TODO(abodenha@chromium.org) Catch and handle failures.
159 }
160
161 /**
162 * Send the current document to cloud print.
163 * @param {string} data The document to be printed.
164 * @param {function} callback Function to be called to process response.
165 */
166 function printToCloud(data, callback) {
167 // TODO(abodenha@chromium.org) Make sure we have an XSRF token before
168 // sending a submit. Right now if the user clicks print before we
169 // complete any request we wont have an XSRF and the submit will fail.
170 sendCloudPrintRequest('POST',
171 'submit',
172 xCloudPrintFormHeader,
173 null,
174 data,
175 printToCloudResponse.bind(this, callback));
176 }
177
178 /**
179 * Gets the JSON string used to control the behavior of the current
180 * print job.
181 * @param {Object} printer The printer object to get the ticket for.
182 * @return {string} The print ticket or null if not a cloud printer.
183 */
184 function getPrintTicketJSON(printer) {
185 if (isCloudPrint(printer)) {
186 return JSON.stringify({'capabilities':
187 [printer.cloudPrintOptions.colorOption]});
188 } else {
189 return null;
190 }
191 }
192 224
193 /** 225 /**
194 * Process the response from cloud print containing the capabilities 226 * Process the response from cloud print containing the capabilities
195 * for the printer. 227 * for the printer.
196 * @param {function} callback Function to be called to process response. 228 * @param {function} callback Function to be called to process response.
197 * @param {Object} printer The printer object to get the capabilites for. 229 * @param {Object} printer The printer object to get the capabilities for.
198 * @param {XMLHttpRequest} xhr The object used to make the request. 230 * @param {XMLHttpRequest} xhr The object used to make the request.
199 */ 231 */
200 function updatePrinterCapsResponse(callback, printer, xhr) { 232 function updatePrinterCapsResponse(callback, printer, xhr) {
201 if (xhr.status == 200) { 233 if (xhr.status == 200) {
202 var printResult = JSON.parse(xhr.responseText); 234 var printResult = JSON.parse(xhr.responseText);
203 if (printResult['success']) { 235 if (printResult['success']) {
204 if (!printer.cloudPrintOptions) 236 if (!printer.cloudPrintOptions)
205 printer.cloudPrintOptions = new Object; 237 printer.cloudPrintOptions = new Object;
206 printer.cloudPrintOptions.capsDownloaded = true; 238 printer.cloudPrintOptions.capsDownloaded = true;
207 printer.cloudPrintOptions.colorOption = null; 239 printer.cloudPrintOptions.colorOption = null;
(...skipping 17 matching lines...) Expand all
225 printer.cloudPrintOptions.colorOption.options = [opt]; 257 printer.cloudPrintOptions.colorOption.options = [opt];
226 printer.cloudPrintOptions.colorIsDefault = 258 printer.cloudPrintOptions.colorIsDefault =
227 opt.name == printer.cloudPrintOptions.colorOnOption.name; 259 opt.name == printer.cloudPrintOptions.colorOnOption.name;
228 } 260 }
229 } 261 }
230 } 262 }
231 } 263 }
232 callback.call(this, printer); 264 callback.call(this, printer);
233 } 265 }
234 } 266 }
235 } 267 };
236 268
237 /** 269 // Export
238 * Retrieve capabilities for a printer.
239 * @param {Object} printer The printer object to get the capabilities for.
240 * @param {function} callback Function to be called to process response.
241 */
242 function updatePrinterCaps(printer, callback) {
243 if (isCloudPrint(printer) && !printer.cloudPrintOptions.capsDownloaded) {
244 sendCloudPrintRequest('GET',
245 'printer?printerid=' +
246 printer.value +
247 '&output=json',
248 xCloudPrintURLHeader,
249 null,
250 null,
251 updatePrinterCapsResponse.bind(this,
252 callback,
253 printer));
254 } else {
255 callback.call(this, printer);
256 }
257 }
258
259 /**
260 * @param {Object} printer The printer object to get the data for.
261 * @return {boolean} true if the printer supports color.
262 */
263 function supportsColor(printer) {
264 return isCloudPrint(printer) &&
265 printer.cloudPrintOptions.colorOption != null;
266 }
267
268 /**
269 * @param {Object} printer The printer object to get the data for.
270 * @return {boolean} true if the printer defaults to color.
271 */
272 function colorIsDefault(printer) {
273 // For now assume that unsupported color means we just don't know
274 // and assume color.
275 return !supportsColor(printer) ||
276 (isCloudPrint(printer) && printer.cloudPrintOptions.colorIsDefault);
277 }
278
279 /**
280 * Turn color on or off for the specified printer.
281 * @param {Object} printer The printer object to turn color on/off for.
282 * @param {boolean} color True to turn color on.
283 */
284 function setColor(printer, color) {
285 if (isCloudPrint(printer) && supportsColor(printer)) {
286 if (color) {
287 printer.cloudPrintOptions.colorOption.options =
288 [printer.cloudPrintOptions.colorOnOption];
289 } else {
290 printer.cloudPrintOptions.colorOption.options =
291 [printer.cloudPrintOptions.colorOffOption];
292 }
293 }
294 }
295
296 /**
297 * Creates a cloud print printer and sets it as the default printer.
298 * @param {string} printer_name The name of the printer to create.
299 * @param {Object} cloud_print_data Data to be stored in cloudPrintOptions.
300 * @param {function} add_callback The callback to be called to add the new
301 * printer to the print preview UI.
302 * @param {function} update_caps_callback The callback to be called to update
303 * capabilities on the new printer.
304 */
305 function setDefaultPrinter(printer_name,
306 cloud_print_data,
307 add_callback,
308 update_caps_callback) {
309 var printer = addCloudPrinters([JSON.parse(cloud_print_data)],
310 add_callback);
311 if (printer)
312 update_caps_callback(printer);
313 }
314
315 /**
316 * Returns the data necessary to serialize a cloud print printer.
317 * @param {Object} printer The printer object to get data for.
318 * @return {string} A JSON string that can be used to recreate the
319 * cloud print portion of the printer object, or and empty string if
320 * there is no data to save.
321 */
322 function getData(printer) {
323 if (isCloudPrint(printer)) {
324 return JSON.stringify(printer.cloudPrintOptions);
325 } else {
326 return '';
327 }
328 }
329
330 /**
331 * Test if a printer is a cloud print printer.
332 * @param {Object} printer The printer to test.
333 * @return {boolean} true iff the printer is a cloud print printer.
334 */
335 function isCloudPrint(printer) {
336 return printer && printer.cloudPrintOptions != null;
337 }
338
339 /**
340 * Mark a printer as a cloud print printer and record its name and id.
341 * @param {Object} printer The printer to mark.
342 * @param {string} name The user visible name of the printer.
343 * @param {string} id The id of the printer used by cloud print to
344 * identify it.
345 */
346 function setCloudPrint(printer, name, id) {
347 if (!printer.cloudPrintOptions) {
348 printer.cloudPrintOptions = new Object;
349 }
350 printer.cloudPrintOptions.name = name;
351 printer.cloudPrintOptions.id = id;
352 }
353
354 /**
355 * Test if a particular cloud printer has already been added to the
356 * printer dropdown.
357 * @param {string} id A unique value to track this printer.
358 * @return {boolean} True if |id| has previously been passed to
359 * trackCloudPrinterAdded.
360 */
361 function cloudPrinterAlreadyAdded(id) {
362 return addedCloudPrinters[id];
363 }
364
365 /**
366 * Test if a particular printer has already been added to the printers
367 * dropdown. Records it if not.
368 * @param {string} id A unique value to track this printer.
369 * @return {boolean} False if adding this printer would exceed
370 * |maxCloudPrinters|.
371 */
372 function trackCloudPrinterAdded(id) {
373 if (Object.keys(addedCloudPrinters).length < maxCloudPrinters) {
374 addedCloudPrinters[id] = true;
375 return true;
376 } else {
377 return false;
378 }
379 }
380
381 /**
382 * Add cloud printers to the list drop down.
383 * Called from the cloudprint object on receipt of printer information from
384 * the cloud print server.
385 * @param {Array} printers Array of printer info objects.
386 * @return {Object} The currently selected printer.
387 */
388 function addCloudPrinters(printers, addDestinationListOptionAtPosition) {
389 var isFirstPass = false;
390 var printerList = $('printer-list');
391
392 if (firstCloudPrintOptionPos == lastCloudPrintOptionPos) {
393 isFirstPass = true;
394 // Remove empty entry added by setDefaultPrinter.
395 if (printerList[0] && printerList[0].textContent == '')
396 printerList.remove(0);
397 }
398 if (printers != null) {
399 for (var i = 0; i < printers.length; i++) {
400 if (!cloudPrinterAlreadyAdded(printers[i]['id'])) {
401 if (!trackCloudPrinterAdded(printers[i]['id'])) {
402 break;
403 }
404 if (printers[i]['displayName'] && printers[i]['displayName'] != '')
405 var name = printers[i]['displayName'];
406 else
407 var name = printers[i]['name'];
408
409 var option = addDestinationListOptionAtPosition(
410 lastCloudPrintOptionPos++,
411 name,
412 printers[i]['id'],
413 name == defaultOrLastUsedPrinterName,
414 false,
415 false);
416 cloudprint.setCloudPrint(option,
417 name,
418 printers[i]['id']);
419 }
420 }
421 } else {
422 if (!cloudPrinterAlreadyAdded(SIGN_IN)) {
423 addDestinationListOptionAtPosition(lastCloudPrintOptionPos++,
424 localStrings.getString('signIn'),
425 SIGN_IN,
426 false,
427 false,
428 false);
429 trackCloudPrinterAdded(SIGN_IN);
430 chrome.send('signIn');
431 }
432 }
433 var selectedPrinter = printerList.selectedIndex;
434 if (selectedPrinter < 0)
435 return null;
436 return printerList.options[selectedPrinter];
437 }
438
439 return { 270 return {
440 addCloudPrinters: addCloudPrinters, 271 CloudPrintInterface: CloudPrintInterface
441 colorIsDefault: colorIsDefault,
442 fetchPrinters: fetchPrinters,
443 getBaseURL: getBaseURL,
444 getData: getData,
445 getPrintTicketJSON: getPrintTicketJSON,
446 isCloudPrint: isCloudPrint,
447 printToCloud: printToCloud,
448 setBaseURL: setBaseURL,
449 setCloudPrint: setCloudPrint,
450 setColor: setColor,
451 setDefaultPrinter: setDefaultPrinter,
452 supportsColor: supportsColor,
453 updatePrinterCaps: updatePrinterCaps
454 }; 272 };
455 }); 273 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698