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.
5 cr
.define('cloudprint', function() {
9 * API to the Google Cloud Print service.
10 * @param {string} baseUrl Base part of the Google Cloud Print service URL
11 * with no trailing slash. For example,
12 * 'https://www.google.com/cloudprint'.
13 * @param {!print_preview.NativeLayer} nativeLayer Native layer used to get
15 * @param {!print_preview.UserInfo} userInfo User information repository.
16 * @param {boolean} isInAppKioskMode Whether the print preview is in App
19 * @extends {cr.EventTarget}
21 function CloudPrintInterface(
22 baseUrl
, nativeLayer
, userInfo
, isInAppKioskMode
) {
24 * The base URL of the Google Cloud Print API.
28 this.baseUrl_
= baseUrl
;
31 * Used to get Auth2 tokens.
32 * @type {!print_preview.NativeLayer}
35 this.nativeLayer_
= nativeLayer
;
38 * User information repository.
39 * @type {!print_preview.UserInfo}
42 this.userInfo_
= userInfo
;
45 * Whether Print Preview is in App Kiosk mode, basically, use only printers
46 * available for the device.
50 this.isInAppKioskMode_
= isInAppKioskMode
;
53 * Currently logged in users (identified by email) mapped to the Google
55 * @type {!Object<number>}
58 this.userSessionIndex_
= {};
61 * Stores last received XSRF tokens for each user account. Sent as
62 * a parameter with every request.
63 * @type {!Object<string>}
66 this.xsrfTokens_
= {};
69 * Pending requests delayed until we get access token.
70 * @type {!Array<!CloudPrintRequest>}
73 this.requestQueue_
= [];
76 * Outstanding cloud destination search requests.
77 * @type {!Array<!CloudPrintRequest>}
80 this.outstandingCloudSearchRequests_
= [];
83 * Event tracker used to keep track of native layer events.
84 * @type {!EventTracker}
87 this.tracker_
= new EventTracker();
89 this.addEventListeners_();
93 * Event types dispatched by the interface.
96 CloudPrintInterface
.EventType
= {
97 INVITES_DONE
: 'cloudprint.CloudPrintInterface.INVITES_DONE',
98 INVITES_FAILED
: 'cloudprint.CloudPrintInterface.INVITES_FAILED',
99 PRINTER_DONE
: 'cloudprint.CloudPrintInterface.PRINTER_DONE',
100 PRINTER_FAILED
: 'cloudprint.CloudPrintInterface.PRINTER_FAILED',
101 PROCESS_INVITE_DONE
: 'cloudprint.CloudPrintInterface.PROCESS_INVITE_DONE',
102 PROCESS_INVITE_FAILED
:
103 'cloudprint.CloudPrintInterface.PROCESS_INVITE_FAILED',
104 SEARCH_DONE
: 'cloudprint.CloudPrintInterface.SEARCH_DONE',
105 SEARCH_FAILED
: 'cloudprint.CloudPrintInterface.SEARCH_FAILED',
106 SUBMIT_DONE
: 'cloudprint.CloudPrintInterface.SUBMIT_DONE',
107 SUBMIT_FAILED
: 'cloudprint.CloudPrintInterface.SUBMIT_FAILED',
108 UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED
:
109 'cloudprint.CloudPrintInterface.UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED'
113 * Content type header value for a URL encoded HTTP request.
118 CloudPrintInterface
.URL_ENCODED_CONTENT_TYPE_
=
119 'application/x-www-form-urlencoded';
122 * Multi-part POST request boundary used in communication with Google
128 CloudPrintInterface
.MULTIPART_BOUNDARY_
=
129 '----CloudPrintFormBoundaryjc9wuprokl8i';
132 * Content type header value for a multipart HTTP request.
137 CloudPrintInterface
.MULTIPART_CONTENT_TYPE_
=
138 'multipart/form-data; boundary=' +
139 CloudPrintInterface
.MULTIPART_BOUNDARY_
;
142 * Regex that extracts Chrome's version from the user-agent string.
147 CloudPrintInterface
.VERSION_REGEXP_
= /.*Chrome\/([\d\.]+)/i;
150 * Enumeration of JSON response fields from Google Cloud Print API.
154 CloudPrintInterface
.JsonFields_
= {
159 * Could Print origins used to search printers.
160 * @type {!Array<!print_preview.Destination.Origin>}
164 CloudPrintInterface
.CLOUD_ORIGINS_
= [
165 print_preview
.Destination
.Origin
.COOKIES
,
166 print_preview
.Destination
.Origin
.DEVICE
167 // TODO(vitalybuka): Enable when implemented.
168 // ready print_preview.Destination.Origin.PROFILE
171 CloudPrintInterface
.prototype = {
172 __proto__
: cr
.EventTarget
.prototype,
174 /** @return {string} Base URL of the Google Cloud Print service. */
176 return this.baseUrl_
;
180 * @return {boolean} Whether a search for cloud destinations is in progress.
182 get isCloudDestinationSearchInProgress() {
183 return this.outstandingCloudSearchRequests_
.length
> 0;
187 * Sends Google Cloud Print search API request.
188 * @param {string=} opt_account Account the search is sent for. When
189 * omitted, the search is done on behalf of the primary user.
190 * @param {print_preview.Destination.Origin=} opt_origin When specified,
191 * searches destinations for {@code opt_origin} only, otherwise starts
192 * searches for all origins.
194 search: function(opt_account
, opt_origin
) {
195 var account
= opt_account
|| '';
197 opt_origin
&& [opt_origin
] || CloudPrintInterface
.CLOUD_ORIGINS_
;
198 if (this.isInAppKioskMode_
) {
199 origins
= origins
.filter(function(origin
) {
200 return origin
!= print_preview
.Destination
.Origin
.COOKIES
;
203 this.abortSearchRequests_(origins
);
204 this.search_(true, account
, origins
);
205 this.search_(false, account
, origins
);
209 * Sends Google Cloud Print search API requests.
210 * @param {boolean} isRecent Whether to search for only recently used
212 * @param {string} account Account the search is sent for. It matters for
213 * COOKIES origin only, and can be empty (sent on behalf of the primary
214 * user in this case).
215 * @param {!Array<!print_preview.Destination.Origin>} origins Origins to
216 * search printers for.
219 search_: function(isRecent
, account
, origins
) {
221 new HttpParam('connection_status', 'ALL'),
222 new HttpParam('client', 'chrome'),
223 new HttpParam('use_cdd', 'true')
226 params
.push(new HttpParam('q', '^recent'));
228 origins
.forEach(function(origin
) {
229 var cpRequest
= this.buildRequest_(
235 this.onSearchDone_
.bind(this, isRecent
));
236 this.outstandingCloudSearchRequests_
.push(cpRequest
);
237 this.sendOrQueueRequest_(cpRequest
);
242 * Sends Google Cloud Print printer sharing invitations API requests.
243 * @param {string} account Account the request is sent for.
245 invites: function(account
) {
247 new HttpParam('client', 'chrome'),
249 this.sendOrQueueRequest_(this.buildRequest_(
253 print_preview
.Destination
.Origin
.COOKIES
,
255 this.onInvitesDone_
.bind(this)));
259 * Accepts or rejects printer sharing invitation.
260 * @param {!print_preview.Invitation} invitation Invitation to process.
261 * @param {boolean} accept Whether to accept this invitation.
263 processInvite: function(invitation
, accept
) {
265 new HttpParam('printerid', invitation
.destination
.id
),
266 new HttpParam('email', invitation
.scopeId
),
267 new HttpParam('accept', accept
),
268 new HttpParam('use_cdd', true),
270 this.sendOrQueueRequest_(this.buildRequest_(
274 invitation
.destination
.origin
,
275 invitation
.destination
.account
,
276 this.onProcessInviteDone_
.bind(this, invitation
, accept
)));
280 * Sends a Google Cloud Print submit API request.
281 * @param {!print_preview.Destination} destination Cloud destination to
283 * @param {!print_preview.PrintTicketStore} printTicketStore Contains the
284 * print ticket to print.
285 * @param {!print_preview.DocumentInfo} documentInfo Document data model.
286 * @param {string} data Base64 encoded data of the document.
288 submit: function(destination
, printTicketStore
, documentInfo
, data
) {
290 CloudPrintInterface
.VERSION_REGEXP_
.exec(navigator
.userAgent
);
291 var chromeVersion
= 'unknown';
292 if (result
&& result
.length
== 2) {
293 chromeVersion
= result
[1];
296 new HttpParam('printerid', destination
.id
),
297 new HttpParam('contentType', 'dataUrl'),
298 new HttpParam('title', documentInfo
.title
),
299 new HttpParam('ticket',
300 printTicketStore
.createPrintTicket(destination
)),
301 new HttpParam('content', 'data:application/pdf;base64,' + data
),
303 '__google__chrome_version=' + chromeVersion
),
304 new HttpParam('tag', '__google__os=' + navigator
.platform
)
306 var cpRequest
= this.buildRequest_(
312 this.onSubmitDone_
.bind(this));
313 this.sendOrQueueRequest_(cpRequest
);
317 * Sends a Google Cloud Print printer API request.
318 * @param {string} printerId ID of the printer to lookup.
319 * @param {!print_preview.Destination.Origin} origin Origin of the printer.
320 * @param {string=} account Account this printer is registered for. When
321 * provided for COOKIES {@code origin}, and users sessions are still not
322 * known, will be checked against the response (both success and failure
323 * to get printer) and, if the active user account is not the one
324 * requested, {@code account} is activated and printer request reissued.
326 printer: function(printerId
, origin
, account
) {
328 new HttpParam('printerid', printerId
),
329 new HttpParam('use_cdd', 'true'),
330 new HttpParam('printer_connection_status', 'true')
332 this.sendOrQueueRequest_(this.buildRequest_(
338 this.onPrinterDone_
.bind(this, printerId
)));
342 * Sends a Google Cloud Print update API request to accept (or reject) the
343 * terms-of-service of the given printer.
344 * @param {!print_preview.Destination} destination Destination to accept ToS
346 * @param {boolean} isAccepted Whether the user accepted ToS or not.
348 updatePrinterTosAcceptance: function(destination
, isAccepted
) {
350 new HttpParam('printerid', destination
.id
),
351 new HttpParam('is_tos_accepted', isAccepted
)
353 this.sendOrQueueRequest_(this.buildRequest_(
359 this.onUpdatePrinterTosAcceptanceDone_
.bind(this)));
363 * Adds event listeners to relevant events.
366 addEventListeners_: function() {
369 print_preview
.NativeLayer
.EventType
.ACCESS_TOKEN_READY
,
370 this.onAccessTokenReady_
.bind(this));
374 * Builds request to the Google Cloud Print API.
375 * @param {string} method HTTP method of the request.
376 * @param {string} action Google Cloud Print action to perform.
377 * @param {Array<!HttpParam>} params HTTP parameters to include in the
379 * @param {!print_preview.Destination.Origin} origin Origin for destination.
380 * @param {?string} account Account the request is sent for. Can be
381 * {@code null} or empty string if the request is not cookie bound or
382 * is sent on behalf of the primary user.
383 * @param {function(number, Object, !print_preview.Destination.Origin)}
384 * callback Callback to invoke when request completes.
385 * @return {!CloudPrintRequest} Partially prepared request.
388 buildRequest_: function(method
, action
, params
, origin
, account
, callback
) {
389 var url
= this.baseUrl_
+ '/' + action
+ '?xsrf=';
390 if (origin
== print_preview
.Destination
.Origin
.COOKIES
) {
391 var xsrfToken
= this.xsrfTokens_
[account
];
393 // TODO(rltoscano): Should throw an error if not a read-only action or
394 // issue an xsrf token request.
396 url
= url
+ xsrfToken
;
399 var index
= this.userSessionIndex_
[account
] || 0;
401 url
+= '&user=' + index
;
407 if (method
== 'GET') {
408 url
= params
.reduce(function(partialUrl
, param
) {
409 return partialUrl
+ '&' + param
.name
+ '=' +
410 encodeURIComponent(param
.value
);
412 } else if (method
== 'POST') {
413 body
= params
.reduce(function(partialBody
, param
) {
414 return partialBody
+ 'Content-Disposition: form-data; name=\"' +
415 param
.name
+ '\"\r\n\r\n' + param
.value
+ '\r\n--' +
416 CloudPrintInterface
.MULTIPART_BOUNDARY_
+ '\r\n';
417 }, '--' + CloudPrintInterface
.MULTIPART_BOUNDARY_
+ '\r\n');
422 headers
['X-CloudPrint-Proxy'] = 'ChromePrintPreview';
423 if (method
== 'GET') {
424 headers
['Content-Type'] = CloudPrintInterface
.URL_ENCODED_CONTENT_TYPE_
;
425 } else if (method
== 'POST') {
426 headers
['Content-Type'] = CloudPrintInterface
.MULTIPART_CONTENT_TYPE_
;
429 var xhr
= new XMLHttpRequest();
430 xhr
.open(method
, url
, true);
431 xhr
.withCredentials
=
432 (origin
== print_preview
.Destination
.Origin
.COOKIES
);
433 for (var header
in headers
) {
434 xhr
.setRequestHeader(header
, headers
[header
]);
437 return new CloudPrintRequest(xhr
, body
, origin
, account
, callback
);
441 * Sends a request to the Google Cloud Print API or queues if it needs to
442 * wait OAuth2 access token.
443 * @param {!CloudPrintRequest} request Request to send or queue.
446 sendOrQueueRequest_: function(request
) {
447 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
) {
448 return this.sendRequest_(request
);
450 this.requestQueue_
.push(request
);
451 this.nativeLayer_
.startGetAccessToken(request
.origin
);
456 * Sends a request to the Google Cloud Print API.
457 * @param {!CloudPrintRequest} request Request to send.
460 sendRequest_: function(request
) {
461 request
.xhr
.onreadystatechange
=
462 this.onReadyStateChange_
.bind(this, request
);
463 request
.xhr
.send(request
.body
);
467 * Creates a Google Cloud Print interface error that is ready to dispatch.
468 * @param {!CloudPrintInterface.EventType} type Type of the error.
469 * @param {!CloudPrintRequest} request Request that has been completed.
470 * @return {!Event} Google Cloud Print interface error event.
473 createErrorEvent_: function(type
, request
) {
474 var errorEvent
= new Event(type
);
475 errorEvent
.status
= request
.xhr
.status
;
476 if (request
.xhr
.status
== 200) {
477 errorEvent
.errorCode
= request
.result
['errorCode'];
478 errorEvent
.message
= request
.result
['message'];
480 errorEvent
.errorCode
= 0;
481 errorEvent
.message
= '';
483 errorEvent
.origin
= request
.origin
;
488 * Updates user info and session index from the {@code request} response.
489 * @param {!CloudPrintRequest} request Request to extract user info from.
492 setUsers_: function(request
) {
493 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
) {
494 var users
= request
.result
['request']['users'] || [];
495 this.userSessionIndex_
= {};
496 for (var i
= 0; i
< users
.length
; i
++) {
497 this.userSessionIndex_
[users
[i
]] = i
;
499 this.userInfo_
.setUsers(request
.result
['request']['user'], users
);
504 * Terminates search requests for requested {@code origins}.
505 * @param {!Array<print_preview.Destination.Origin>} origins Origins
506 * to terminate search requests for.
509 abortSearchRequests_: function(origins
) {
510 this.outstandingCloudSearchRequests_
=
511 this.outstandingCloudSearchRequests_
.filter(function(request
) {
512 if (origins
.indexOf(request
.origin
) >= 0) {
521 * Called when a native layer receives access token.
522 * @param {Event} event Contains the authentication type and access token.
525 onAccessTokenReady_: function(event
) {
526 // TODO(vitalybuka): remove when other Origins implemented.
527 assert(event
.authType
== print_preview
.Destination
.Origin
.DEVICE
);
528 this.requestQueue_
= this.requestQueue_
.filter(function(request
) {
529 assert(request
.origin
== print_preview
.Destination
.Origin
.DEVICE
);
530 if (request
.origin
!= event
.authType
) {
533 if (event
.accessToken
) {
534 request
.xhr
.setRequestHeader('Authorization',
535 'Bearer ' + event
.accessToken
);
536 this.sendRequest_(request
);
537 } else { // No valid token.
538 // Without abort status does not exist.
540 request
.callback(request
);
547 * Called when the ready-state of a XML http request changes.
548 * Calls the successCallback with the result or dispatches an ERROR event.
549 * @param {!CloudPrintRequest} request Request that was changed.
552 onReadyStateChange_: function(request
) {
553 if (request
.xhr
.readyState
== 4) {
554 if (request
.xhr
.status
== 200) {
555 request
.result
= JSON
.parse(request
.xhr
.responseText
);
556 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
&&
557 request
.result
['success']) {
558 this.xsrfTokens_
[request
.result
['request']['user']] =
559 request
.result
['xsrf_token'];
562 request
.status
= request
.xhr
.status
;
563 request
.callback(request
);
568 * Called when the search request completes.
569 * @param {boolean} isRecent Whether the search request was for recent
571 * @param {!CloudPrintRequest} request Request that has been completed.
574 onSearchDone_: function(isRecent
, request
) {
575 var lastRequestForThisOrigin
= true;
576 this.outstandingCloudSearchRequests_
=
577 this.outstandingCloudSearchRequests_
.filter(function(item
) {
578 if (item
!= request
&& item
.origin
== request
.origin
) {
579 lastRequestForThisOrigin
= false;
581 return item
!= request
;
584 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
) {
587 request
.result
['request'] &&
588 request
.result
['request']['user'];
591 if (request
.xhr
.status
== 200 && request
.result
['success']) {
593 var printerListJson
= request
.result
['printers'] || [];
594 var printerList
= [];
595 printerListJson
.forEach(function(printerJson
) {
597 printerList
.push(cloudprint
.CloudDestinationParser
.parse(
598 printerJson
, request
.origin
, activeUser
));
600 console
.error('Unable to parse cloud print destination: ' + err
);
603 // Extract and store users.
604 this.setUsers_(request
);
605 // Dispatch SEARCH_DONE event.
606 event
= new Event(CloudPrintInterface
.EventType
.SEARCH_DONE
);
607 event
.origin
= request
.origin
;
608 event
.printers
= printerList
;
609 event
.isRecent
= isRecent
;
611 event
= this.createErrorEvent_(
612 CloudPrintInterface
.EventType
.SEARCH_FAILED
,
615 event
.user
= activeUser
;
616 event
.searchDone
= lastRequestForThisOrigin
;
617 this.dispatchEvent(event
);
621 * Called when invitations search request completes.
622 * @param {!CloudPrintRequest} request Request that has been completed.
625 onInvitesDone_: function(request
) {
629 request
.result
['request'] &&
630 request
.result
['request']['user']) || '';
631 if (request
.xhr
.status
== 200 && request
.result
['success']) {
632 // Extract invitations.
633 var invitationListJson
= request
.result
['invites'] || [];
634 var invitationList
= [];
635 invitationListJson
.forEach(function(invitationJson
) {
637 invitationList
.push(cloudprint
.InvitationParser
.parse(
638 invitationJson
, activeUser
));
640 console
.error('Unable to parse invitation: ' + e
);
643 // Dispatch INVITES_DONE event.
644 event
= new Event(CloudPrintInterface
.EventType
.INVITES_DONE
);
645 event
.invitations
= invitationList
;
647 event
= this.createErrorEvent_(
648 CloudPrintInterface
.EventType
.INVITES_FAILED
, request
);
650 event
.user
= activeUser
;
651 this.dispatchEvent(event
);
655 * Called when invitation processing request completes.
656 * @param {!print_preview.Invitation} invitation Processed invitation.
657 * @param {boolean} accept Whether this invitation was accepted or rejected.
658 * @param {!CloudPrintRequest} request Request that has been completed.
661 onProcessInviteDone_: function(invitation
, accept
, request
) {
665 request
.result
['request'] &&
666 request
.result
['request']['user']) || '';
667 if (request
.xhr
.status
== 200 && request
.result
['success']) {
668 event
= new Event(CloudPrintInterface
.EventType
.PROCESS_INVITE_DONE
);
671 event
.printer
= cloudprint
.CloudDestinationParser
.parse(
672 request
.result
['printer'], request
.origin
, activeUser
);
674 console
.error('Failed to parse cloud print destination: ' + e
);
678 event
= this.createErrorEvent_(
679 CloudPrintInterface
.EventType
.PROCESS_INVITE_FAILED
, request
);
681 event
.invitation
= invitation
;
682 event
.accept
= accept
;
683 event
.user
= activeUser
;
684 this.dispatchEvent(event
);
688 * Called when the submit request completes.
689 * @param {!CloudPrintRequest} request Request that has been completed.
692 onSubmitDone_: function(request
) {
693 if (request
.xhr
.status
== 200 && request
.result
['success']) {
694 var submitDoneEvent
= new Event(
695 CloudPrintInterface
.EventType
.SUBMIT_DONE
);
696 submitDoneEvent
.jobId
= request
.result
['job']['id'];
697 this.dispatchEvent(submitDoneEvent
);
699 var errorEvent
= this.createErrorEvent_(
700 CloudPrintInterface
.EventType
.SUBMIT_FAILED
, request
);
701 this.dispatchEvent(errorEvent
);
706 * Called when the printer request completes.
707 * @param {string} destinationId ID of the destination that was looked up.
708 * @param {!CloudPrintRequest} request Request that has been completed.
711 onPrinterDone_: function(destinationId
, request
) {
712 // Special handling of the first printer request. It does not matter at
713 // this point, whether printer was found or not.
714 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
&&
717 request
.result
['request']['user'] &&
718 request
.result
['request']['users'] &&
719 request
.account
!= request
.result
['request']['user']) {
720 this.setUsers_(request
);
721 // In case the user account is known, but not the primary one,
723 if (this.userSessionIndex_
[request
.account
] > 0) {
724 this.userInfo_
.activeUser
= request
.account
;
725 // Repeat the request for the newly activated account.
727 request
.result
['request']['params']['printerid'],
730 // Stop processing this request, wait for the new response.
735 if (request
.xhr
.status
== 200 && request
.result
['success']) {
737 if (request
.origin
== print_preview
.Destination
.Origin
.COOKIES
) {
738 activeUser
= request
.result
['request']['user'];
740 var printerJson
= request
.result
['printers'][0];
743 printer
= cloudprint
.CloudDestinationParser
.parse(
744 printerJson
, request
.origin
, activeUser
);
746 console
.error('Failed to parse cloud print destination: ' +
747 JSON
.stringify(printerJson
));
750 var printerDoneEvent
=
751 new Event(CloudPrintInterface
.EventType
.PRINTER_DONE
);
752 printerDoneEvent
.printer
= printer
;
753 this.dispatchEvent(printerDoneEvent
);
755 var errorEvent
= this.createErrorEvent_(
756 CloudPrintInterface
.EventType
.PRINTER_FAILED
, request
);
757 errorEvent
.destinationId
= destinationId
;
758 errorEvent
.destinationOrigin
= request
.origin
;
759 this.dispatchEvent(errorEvent
);
764 * Called when the update printer TOS acceptance request completes.
765 * @param {!CloudPrintRequest} request Request that has been completed.
768 onUpdatePrinterTosAcceptanceDone_: function(request
) {
769 if (request
.xhr
.status
== 200 && request
.result
['success']) {
772 var errorEvent
= this.createErrorEvent_(
773 CloudPrintInterface
.EventType
.SUBMIT_FAILED
, request
);
774 this.dispatchEvent(errorEvent
);
780 * Data structure that holds data for Cloud Print requests.
781 * @param {!XMLHttpRequest} xhr Partially prepared http request.
782 * @param {string} body Data to send with POST requests.
783 * @param {!print_preview.Destination.Origin} origin Origin for destination.
784 * @param {?string} account Account the request is sent for. Can be
785 * {@code null} or empty string if the request is not cookie bound or
786 * is sent on behalf of the primary user.
787 * @param {function(!CloudPrintRequest)} callback Callback to invoke when
791 function CloudPrintRequest(xhr
, body
, origin
, account
, callback
) {
793 * Partially prepared http request.
794 * @type {!XMLHttpRequest}
799 * Data to send with POST requests.
805 * Origin for destination.
806 * @type {!print_preview.Destination.Origin}
808 this.origin
= origin
;
811 * User account this request is expected to be executed for.
814 this.account
= account
;
817 * Callback to invoke when request completes.
818 * @type {function(!CloudPrintRequest)}
820 this.callback
= callback
;
823 * Result for requests.
824 * @type {Object} JSON response.
830 * Data structure that represents an HTTP parameter.
831 * @param {string} name Name of the parameter.
832 * @param {string} value Value of the parameter.
835 function HttpParam(name
, value
) {
837 * Name of the parameter.
851 CloudPrintInterface
: CloudPrintInterface