cloud_print_interface.js revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
183e168294456ca2f02db421a635f7d5f5d023966kmillikin@chromium.org// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org// Use of this source code is governed by a BSD-style license that can be 3cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org// found in the LICENSE file. 4cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 5cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.orgcr.define('cloudprint', function() { 6cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 'use strict'; 7cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 8cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org /** 9cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * API to the Google Cloud Print service. 10cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @param {string} baseUrl Base part of the Google Cloud Print service URL 11cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * with no trailing slash. For example, 12cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * 'https://www.google.com/cloudprint'. 13cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @param {!print_preview.NativeLayer} nativeLayer Native layer used to get 14cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * Auth2 tokens. 15cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @constructor 16cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @extends {cr.EventTarget} 17cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 18cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org function CloudPrintInterface(baseUrl, nativeLayer) { 19cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org /** 20cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * The base URL of the Google Cloud Print API. 21cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @type {string} 22cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @private 23cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 24cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org this.baseUrl_ = baseUrl; 25cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 26cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org /** 27cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * Used to get Auth2 tokens. 2883e168294456ca2f02db421a635f7d5f5d023966kmillikin@chromium.org * @type {!print_preview.NativeLayer} 2983e168294456ca2f02db421a635f7d5f5d023966kmillikin@chromium.org * @private 30cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 31cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org this.nativeLayer_ = nativeLayer; 32cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 33cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org /** 34cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * Last received XSRF token. Sent as a parameter in every request. 35cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @type {string} 36cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @private 37cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 385d8f0e6e7b477f422e3064bdf0dd5f2a23f75544kmillikin@chromium.org this.xsrfToken_ = ''; 395d8f0e6e7b477f422e3064bdf0dd5f2a23f75544kmillikin@chromium.org 405d8f0e6e7b477f422e3064bdf0dd5f2a23f75544kmillikin@chromium.org /** 415d8f0e6e7b477f422e3064bdf0dd5f2a23f75544kmillikin@chromium.org * Pending requests delayed until we get access token. 42cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @type {!Array.<!CloudPrintRequest>} 43cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @private 44cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 45cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org this.requestQueue_ = []; 46cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org 47cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org /** 48cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * Number of outstanding cloud destination search requests. 49cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @type {number} 50cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org * @private 51cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org */ 52cec079d8ed1f0920a0ea3dc9a3e81966013287c1whesse@chromium.org this.outstandingCloudSearchRequestCount_ = 0; 5383e168294456ca2f02db421a635f7d5f5d023966kmillikin@chromium.org 54 /** 55 * Event tracker used to keep track of native layer events. 56 * @type {!EventTracker} 57 * @private 58 */ 59 this.tracker_ = new EventTracker(); 60 61 this.addEventListeners_(); 62 }; 63 64 /** 65 * Event types dispatched by the interface. 66 * @enum {string} 67 */ 68 CloudPrintInterface.EventType = { 69 PRINTER_DONE: 'cloudprint.CloudPrintInterface.PRINTER_DONE', 70 PRINTER_FAILED: 'cloudprint.CloudPrintInterface.PRINTER_FAILED', 71 SEARCH_DONE: 'cloudprint.CloudPrintInterface.SEARCH_DONE', 72 SEARCH_FAILED: 'cloudprint.CloudPrintInterface.SEARCH_FAILED', 73 SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE', 74 SUBMIT_FAILED: 'cloudprint.CloudPrintInterface.SUBMIT_FAILED', 75 UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED: 76 'cloudprint.CloudPrintInterface.UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED' 77 }; 78 79 /** 80 * Content type header value for a URL encoded HTTP request. 81 * @type {string} 82 * @const 83 * @private 84 */ 85 CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_ = 86 'application/x-www-form-urlencoded'; 87 88 /** 89 * Multi-part POST request boundary used in communication with Google 90 * Cloud Print. 91 * @type {string} 92 * @const 93 * @private 94 */ 95 CloudPrintInterface.MULTIPART_BOUNDARY_ = 96 '----CloudPrintFormBoundaryjc9wuprokl8i'; 97 98 /** 99 * Content type header value for a multipart HTTP request. 100 * @type {string} 101 * @const 102 * @private 103 */ 104 CloudPrintInterface.MULTIPART_CONTENT_TYPE_ = 105 'multipart/form-data; boundary=' + 106 CloudPrintInterface.MULTIPART_BOUNDARY_; 107 108 /** 109 * Regex that extracts Chrome's version from the user-agent string. 110 * @type {!RegExp} 111 * @const 112 * @private 113 */ 114 CloudPrintInterface.VERSION_REGEXP_ = /.*Chrome\/([\d\.]+)/i; 115 116 /** 117 * Enumeration of JSON response fields from Google Cloud Print API. 118 * @enum {string} 119 * @private 120 */ 121 CloudPrintInterface.JsonFields_ = { 122 PRINTER: 'printer' 123 }; 124 125 /** 126 * Could Print origins used to search printers. 127 * @type {!Array.<!print_preview.Destination.Origin>} 128 * @const 129 * @private 130 */ 131 CloudPrintInterface.CLOUD_ORIGINS_ = [ 132 print_preview.Destination.Origin.COOKIES, 133 print_preview.Destination.Origin.DEVICE 134 // TODO(vitalybuka): Enable when implemented. 135 // ready print_preview.Destination.Origin.PROFILE 136 ]; 137 138 CloudPrintInterface.prototype = { 139 __proto__: cr.EventTarget.prototype, 140 141 /** @return {string} Base URL of the Google Cloud Print service. */ 142 get baseUrl() { 143 return this.baseUrl_; 144 }, 145 146 /** 147 * @return {boolean} Whether a search for cloud destinations is in progress. 148 */ 149 get isCloudDestinationSearchInProgress() { 150 return this.outstandingCloudSearchRequestCount_ > 0; 151 }, 152 153 /** 154 * Sends a Google Cloud Print search API request. 155 * @param {boolean} isRecent Whether to search for only recently used 156 * printers. 157 */ 158 search: function(isRecent) { 159 var params = [ 160 new HttpParam('connection_status', 'ALL'), 161 new HttpParam('client', 'chrome'), 162 new HttpParam('use_cdd', 'true') 163 ]; 164 if (isRecent) { 165 params.push(new HttpParam('q', '^recent')); 166 } 167 CloudPrintInterface.CLOUD_ORIGINS_.forEach(function(origin) { 168 ++this.outstandingCloudSearchRequestCount_; 169 var cpRequest = 170 this.buildRequest_('GET', 'search', params, origin, 171 this.onSearchDone_.bind(this, isRecent)); 172 this.sendOrQueueRequest_(cpRequest); 173 }, this); 174 }, 175 176 /** 177 * Sends a Google Cloud Print submit API request. 178 * @param {!print_preview.Destination} destination Cloud destination to 179 * print to. 180 * @param {!print_preview.PrintTicketStore} printTicketStore Contains the 181 * print ticket to print. 182 * @param {!print_preview.DocumentInfo} documentInfo Document data model. 183 * @param {string} data Base64 encoded data of the document. 184 */ 185 submit: function(destination, printTicketStore, documentInfo, data) { 186 var result = 187 CloudPrintInterface.VERSION_REGEXP_.exec(navigator.userAgent); 188 var chromeVersion = 'unknown'; 189 if (result && result.length == 2) { 190 chromeVersion = result[1]; 191 } 192 var params = [ 193 new HttpParam('printerid', destination.id), 194 new HttpParam('contentType', 'dataUrl'), 195 new HttpParam('title', documentInfo.title), 196 new HttpParam('ticket', 197 this.createPrintTicket_(destination, printTicketStore)), 198 new HttpParam('content', 'data:application/pdf;base64,' + data), 199 new HttpParam('tag', 200 '__google__chrome_version=' + chromeVersion), 201 new HttpParam('tag', '__google__os=' + navigator.platform) 202 ]; 203 var cpRequest = this.buildRequest_('POST', 'submit', params, 204 destination.origin, 205 this.onSubmitDone_.bind(this)); 206 this.sendOrQueueRequest_(cpRequest); 207 }, 208 209 /** 210 * Sends a Google Cloud Print printer API request. 211 * @param {string} printerId ID of the printer to lookup. 212 * @param {!print_preview.Destination.Origin} origin Origin of the printer. 213 */ 214 printer: function(printerId, origin) { 215 var params = [ 216 new HttpParam('printerid', printerId), 217 new HttpParam('use_cdd', 'true') 218 ]; 219 var cpRequest = 220 this.buildRequest_('GET', 'printer', params, origin, 221 this.onPrinterDone_.bind(this, printerId)); 222 this.sendOrQueueRequest_(cpRequest); 223 }, 224 225 /** 226 * Sends a Google Cloud Print update API request to accept (or reject) the 227 * terms-of-service of the given printer. 228 * @param {string} printerId ID of the printer to accept the 229 * terms-of-service for. 230 * @param {!print_preview.Destination.Origin} origin Origin of the printer. 231 * @param {boolean} isAccepted Whether the user accepted the 232 * terms-of-service. 233 */ 234 updatePrinterTosAcceptance: function(printerId, origin, isAccepted) { 235 var params = [ 236 new HttpParam('printerid', printerId), 237 new HttpParam('is_tos_accepted', isAccepted) 238 ]; 239 var cpRequest = 240 this.buildRequest_('POST', 'update', params, origin, 241 this.onUpdatePrinterTosAcceptanceDone_.bind(this)); 242 this.sendOrQueueRequest_(cpRequest); 243 }, 244 245 /** 246 * Adds event listeners to the relevant native layer events. 247 * @private 248 */ 249 addEventListeners_: function() { 250 this.tracker_.add( 251 this.nativeLayer_, 252 print_preview.NativeLayer.EventType.ACCESS_TOKEN_READY, 253 this.onAccessTokenReady_.bind(this)); 254 }, 255 256 /** 257 * Creates an object that represents a Google Cloud Print print ticket. 258 * @param {!print_preview.Destination} destination Destination to print to. 259 * @param {!print_preview.PrintTicketStore} printTicketStore Used to create 260 * the state of the print ticket. 261 * @return {!Object} Google Cloud Print print ticket. 262 * @private 263 */ 264 createPrintTicket_: function(destination, printTicketStore) { 265 assert(!destination.isLocal, 266 'Trying to create a Google Cloud Print print ticket for a local ' + 267 'destination'); 268 assert(destination.capabilities, 269 'Trying to create a Google Cloud Print print ticket for a ' + 270 'destination with no print capabilities'); 271 var pts = printTicketStore; // For brevity. 272 var cjt = { 273 version: '1.0', 274 print: {} 275 }; 276 if (pts.collate.isCapabilityAvailable() && pts.collate.isUserEdited()) { 277 cjt.print.collate = {collate: pts.collate.getValue() == 'true'}; 278 } 279 if (pts.color.isCapabilityAvailable() && pts.color.isUserEdited()) { 280 var colorType = pts.color.getValue() ? 281 'STANDARD_COLOR' : 'STANDARD_MONOCHROME'; 282 // Find option with this colorType to read its vendor_id. 283 var selectedOptions = destination.capabilities.printer.color.option. 284 filter(function(option) { 285 return option.type == colorType; 286 }); 287 if (selectedOptions.length == 0) { 288 console.error('Could not find correct color option'); 289 } else { 290 cjt.print.color = {type: colorType}; 291 if (selectedOptions[0].hasOwnProperty('vendor_id')) { 292 cjt.print.color.vendor_id = selectedOptions[0].vendor_id; 293 } 294 } 295 } 296 if (pts.copies.isCapabilityAvailable() && pts.copies.isUserEdited()) { 297 cjt.print.copies = {copies: pts.copies.getValueAsNumber()}; 298 } 299 if (pts.duplex.isCapabilityAvailable() && pts.duplex.isUserEdited()) { 300 cjt.print.duplex = 301 {type: pts.duplex.getValue() ? 'LONG_EDGE' : 'NO_DUPLEX'}; 302 } 303 if (pts.landscape.isCapabilityAvailable() && 304 pts.landscape.isUserEdited()) { 305 cjt.print.page_orientation = 306 {type: pts.landscape.getValue() ? 'LANDSCAPE' : 'PORTRAIT'}; 307 } 308 return JSON.stringify(cjt); 309 }, 310 311 /** 312 * Builds request to the Google Cloud Print API. 313 * @param {string} method HTTP method of the request. 314 * @param {string} action Google Cloud Print action to perform. 315 * @param {Array.<!HttpParam>} params HTTP parameters to include in the 316 * request. 317 * @param {!print_preview.Destination.Origin} origin Origin for destination. 318 * @param {function(number, Object, !print_preview.Destination.Origin)} 319 * callback Callback to invoke when request completes. 320 * @return {!CloudPrintRequest} Partially prepared request. 321 * @private 322 */ 323 buildRequest_: function(method, action, params, origin, callback) { 324 var url = this.baseUrl_ + '/' + action + '?xsrf='; 325 if (origin == print_preview.Destination.Origin.COOKIES) { 326 if (!this.xsrfToken_) { 327 // TODO(rltoscano): Should throw an error if not a read-only action or 328 // issue an xsrf token request. 329 } else { 330 url = url + this.xsrfToken_; 331 } 332 } 333 var body = null; 334 if (params) { 335 if (method == 'GET') { 336 url = params.reduce(function(partialUrl, param) { 337 return partialUrl + '&' + param.name + '=' + 338 encodeURIComponent(param.value); 339 }, url); 340 } else if (method == 'POST') { 341 body = params.reduce(function(partialBody, param) { 342 return partialBody + 'Content-Disposition: form-data; name=\"' + 343 param.name + '\"\r\n\r\n' + param.value + '\r\n--' + 344 CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n'; 345 }, '--' + CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n'); 346 } 347 } 348 349 var headers = {}; 350 headers['X-CloudPrint-Proxy'] = 'ChromePrintPreview'; 351 if (method == 'GET') { 352 headers['Content-Type'] = CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_; 353 } else if (method == 'POST') { 354 headers['Content-Type'] = CloudPrintInterface.MULTIPART_CONTENT_TYPE_; 355 } 356 357 var xhr = new XMLHttpRequest(); 358 xhr.open(method, url, true); 359 xhr.withCredentials = 360 (origin == print_preview.Destination.Origin.COOKIES); 361 for (var header in headers) { 362 xhr.setRequestHeader(header, headers[header]); 363 } 364 365 return new CloudPrintRequest(xhr, body, origin, callback); 366 }, 367 368 /** 369 * Sends a request to the Google Cloud Print API or queues if it needs to 370 * wait OAuth2 access token. 371 * @param {!CloudPrintRequest} request Request to send or queue. 372 * @private 373 */ 374 sendOrQueueRequest_: function(request) { 375 if (request.origin == print_preview.Destination.Origin.COOKIES) { 376 return this.sendRequest_(request); 377 } else { 378 this.requestQueue_.push(request); 379 this.nativeLayer_.startGetAccessToken(request.origin); 380 } 381 }, 382 383 /** 384 * Sends a request to the Google Cloud Print API. 385 * @param {!CloudPrintRequest} request Request to send. 386 * @private 387 */ 388 sendRequest_: function(request) { 389 request.xhr.onreadystatechange = 390 this.onReadyStateChange_.bind(this, request); 391 request.xhr.send(request.body); 392 }, 393 394 /** 395 * Creates a Google Cloud Print interface error that is ready to dispatch. 396 * @param {!CloudPrintInterface.EventType} type Type of the error. 397 * @param {!CloudPrintRequest} request Request that has been completed. 398 * @return {!Event} Google Cloud Print interface error event. 399 * @private 400 */ 401 createErrorEvent_: function(type, request) { 402 var errorEvent = new Event(type); 403 errorEvent.status = request.xhr.status; 404 if (request.xhr.status == 200) { 405 errorEvent.errorCode = request.result['errorCode']; 406 errorEvent.message = request.result['message']; 407 } else { 408 errorEvent.errorCode = 0; 409 errorEvent.message = ''; 410 } 411 errorEvent.origin = request.origin; 412 return errorEvent; 413 }, 414 415 /** 416 * Called when a native layer receives access token. 417 * @param {Event} evt Contains the authetication type and access token. 418 * @private 419 */ 420 onAccessTokenReady_: function(event) { 421 // TODO(vitalybuka): remove when other Origins implemented. 422 assert(event.authType == print_preview.Destination.Origin.DEVICE); 423 this.requestQueue_ = this.requestQueue_.filter(function(request) { 424 assert(request.origin == print_preview.Destination.Origin.DEVICE); 425 if (request.origin != event.authType) { 426 return true; 427 } 428 if (event.accessToken) { 429 request.xhr.setRequestHeader('Authorization', 430 'Bearer ' + event.accessToken); 431 this.sendRequest_(request); 432 } else { // No valid token. 433 // Without abort status does not exists. 434 request.xhr.abort(); 435 request.callback(request); 436 } 437 return false; 438 }, this); 439 }, 440 441 /** 442 * Called when the ready-state of a XML http request changes. 443 * Calls the successCallback with the result or dispatches an ERROR event. 444 * @param {!CloudPrintRequest} request Request that was changed. 445 * @private 446 */ 447 onReadyStateChange_: function(request) { 448 if (request.xhr.readyState == 4) { 449 if (request.xhr.status == 200) { 450 request.result = JSON.parse(request.xhr.responseText); 451 if (request.origin == print_preview.Destination.Origin.COOKIES && 452 request.result['success']) { 453 this.xsrfToken_ = request.result['xsrf_token']; 454 } 455 } 456 request.status = request.xhr.status; 457 request.callback(request); 458 } 459 }, 460 461 /** 462 * Called when the search request completes. 463 * @param {boolean} isRecent Whether the search request was for recent 464 * destinations. 465 * @param {!CloudPrintRequest} request Request that has been completed. 466 * @private 467 */ 468 onSearchDone_: function(isRecent, request) { 469 --this.outstandingCloudSearchRequestCount_; 470 if (request.xhr.status == 200 && request.result['success']) { 471 var printerListJson = request.result['printers'] || []; 472 var printerList = []; 473 printerListJson.forEach(function(printerJson) { 474 try { 475 printerList.push( 476 cloudprint.CloudDestinationParser.parse(printerJson, 477 request.origin)); 478 } catch (err) { 479 console.error('Unable to parse cloud print destination: ' + err); 480 } 481 }); 482 var searchDoneEvent = 483 new Event(CloudPrintInterface.EventType.SEARCH_DONE); 484 searchDoneEvent.printers = printerList; 485 searchDoneEvent.origin = request.origin; 486 searchDoneEvent.isRecent = isRecent; 487 searchDoneEvent.email = request.result['request']['user']; 488 this.dispatchEvent(searchDoneEvent); 489 } else { 490 var errorEvent = this.createErrorEvent_( 491 CloudPrintInterface.EventType.SEARCH_FAILED, request); 492 this.dispatchEvent(errorEvent); 493 } 494 }, 495 496 /** 497 * Called when the submit request completes. 498 * @param {!CloudPrintRequest} request Request that has been completed. 499 * @private 500 */ 501 onSubmitDone_: function(request) { 502 if (request.xhr.status == 200 && request.result['success']) { 503 var submitDoneEvent = new Event( 504 CloudPrintInterface.EventType.SUBMIT_DONE); 505 submitDoneEvent.jobId = request.result['job']['id']; 506 this.dispatchEvent(submitDoneEvent); 507 } else { 508 var errorEvent = this.createErrorEvent_( 509 CloudPrintInterface.EventType.SUBMIT_FAILED, request); 510 this.dispatchEvent(errorEvent); 511 } 512 }, 513 514 /** 515 * Called when the printer request completes. 516 * @param {string} destinationId ID of the destination that was looked up. 517 * @param {!CloudPrintRequest} request Request that has been completed. 518 * @private 519 */ 520 onPrinterDone_: function(destinationId, request) { 521 if (request.xhr.status == 200 && request.result['success']) { 522 var printerJson = request.result['printers'][0]; 523 var printer; 524 try { 525 printer = cloudprint.CloudDestinationParser.parse(printerJson, 526 request.origin); 527 } catch (err) { 528 console.error('Failed to parse cloud print destination: ' + 529 JSON.stringify(printerJson)); 530 return; 531 } 532 var printerDoneEvent = 533 new Event(CloudPrintInterface.EventType.PRINTER_DONE); 534 printerDoneEvent.printer = printer; 535 this.dispatchEvent(printerDoneEvent); 536 } else { 537 var errorEvent = this.createErrorEvent_( 538 CloudPrintInterface.EventType.PRINTER_FAILED, request); 539 errorEvent.destinationId = destinationId; 540 errorEvent.destinationOrigin = request.origin; 541 this.dispatchEvent(errorEvent, request.origin); 542 } 543 }, 544 545 /** 546 * Called when the update printer TOS acceptance request completes. 547 * @param {!CloudPrintRequest} request Request that has been completed. 548 * @private 549 */ 550 onUpdatePrinterTosAcceptanceDone_: function(request) { 551 if (request.xhr.status == 200 && request.result['success']) { 552 // Do nothing. 553 } else { 554 var errorEvent = this.createErrorEvent_( 555 CloudPrintInterface.EventType.SUBMIT_FAILED, request); 556 this.dispatchEvent(errorEvent); 557 } 558 } 559 }; 560 561 /** 562 * Data structure that holds data for Cloud Print requests. 563 * @param {!XMLHttpRequest} xhr Partially prepared http request. 564 * @param {string} body Data to send with POST requests. 565 * @param {!print_preview.Destination.Origin} origin Origin for destination. 566 * @param {function(!CloudPrintRequest)} callback Callback to invoke when 567 * request completes. 568 * @constructor 569 */ 570 function CloudPrintRequest(xhr, body, origin, callback) { 571 /** 572 * Partially prepared http request. 573 * @type {!XMLHttpRequest} 574 */ 575 this.xhr = xhr; 576 577 /** 578 * Data to send with POST requests. 579 * @type {string} 580 */ 581 this.body = body; 582 583 /** 584 * Origin for destination. 585 * @type {!print_preview.Destination.Origin} 586 */ 587 this.origin = origin; 588 589 /** 590 * Callback to invoke when request completes. 591 * @type {function(!CloudPrintRequest)} 592 */ 593 this.callback = callback; 594 595 /** 596 * Result for requests. 597 * @type {Object} JSON response. 598 */ 599 this.result = null; 600 }; 601 602 /** 603 * Data structure that represents an HTTP parameter. 604 * @param {string} name Name of the parameter. 605 * @param {string} value Value of the parameter. 606 * @constructor 607 */ 608 function HttpParam(name, value) { 609 /** 610 * Name of the parameter. 611 * @type {string} 612 */ 613 this.name = name; 614 615 /** 616 * Name of the value. 617 * @type {string} 618 */ 619 this.value = value; 620 }; 621 622 // Export 623 return { 624 CloudPrintInterface: CloudPrintInterface 625 }; 626}); 627