1 // Copyright 2014 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.
8 * @type {string} The app id (from the webstore) for this application.
13 * @type {string} The host id corresponding to the user's VM. The @pending
14 * place-holder instructs the Orchestrator to abandon any pending host,
15 * and is used if no host id is provided by the main window.
17 var hostId
= '@pending';
20 * @type {string} The network stats at the time the feedback consent dialog
23 var connectionStats
= '';
26 * @type {string} JSON representation of recent console error messages.
28 var consoleErrors
= '';
31 * @type {string} The most recent id for the session (unless the session is
32 * longer than 24hrs, this will be the only session id).
37 * @type {string} "no" => user did not request a VM reset; "yes" => VM was
38 * successfully reset; "failed" => user requested a reset, but it failed.
40 var abandonHost
= 'no';
43 * @type {Window} The main application window.
45 var applicationWindow
= null;
48 * @type {string} An unique identifier that links the feedback post with the
49 * logs uploaded by the host.
51 var crashServiceReportId
= '';
54 * @type {string} The user-selected feedback category, represented by its
57 var selectedCategory
= '';
60 * @param {string} email
61 * @param {string} realName
63 function onUserInfo(email
, realName
) {
64 /** @type {number} Identifies this product to Google Feedback. **/
65 var productId
= 93407;
67 /** @type {string} The base URL for Google Feedback. */
68 var url
= 'https://www.google.com/tools/feedback/survey/xhtml';
70 /** @type {string} The feedback 'bucket', used for clustering. */
71 var bucket
= 'feedback';
73 /** @type {string} The user's locale, used to localize the feedback page. */
74 var locale
= chrome
.i18n
.getMessage('@@ui_locale');
77 '?productId=' + productId
+
78 '&bucket=' + escape(bucket
) +
79 '&hl=' + escape(locale
) +
80 '&psd_email=' + escape(email
) +
81 '&psd_hostId=' + escape(hostId
) +
82 '&psd_abandonHost=' + escape(abandonHost
) +
83 '&psd_crashServiceReportId=' + escape(crashServiceReportId
) +
84 '&psd_connectionStats=' + escape(connectionStats
) +
85 '&psd_category=' + escape(selectedCategory
) +
86 '&psd_sessionId=' + escape(sessionId
));
89 // If the VM was successfully abandoned, close the application.
90 if (abandonHost
== 'yes') {
91 applicationWindow
.close();
96 * @param {boolean} waiting
98 function setWaiting(waiting
) {
99 var ok
= document
.getElementById('feedback-consent-ok');
100 var cancel
= document
.getElementById('feedback-consent-cancel');
101 var abandon
= document
.getElementById('abandon-host');
102 var working
= document
.getElementById('working');
103 ok
.disabled
= waiting
;
104 cancel
.disabled
= waiting
;
105 abandon
.disabled
= waiting
;
106 working
.hidden
= !waiting
;
109 function showError() {
111 var error
= document
.getElementById('abandon-failed');
112 var abandon
= document
.getElementById('abandon-host');
113 var logs
= document
.getElementById('include-logs');
114 var formBody
= document
.getElementById('form-body');
115 error
.hidden
= false;
116 abandon
.checked
= false;
117 logs
.checked
= false;
118 abandonHost
= 'failed';
119 crashServiceReportId
= '';
120 formBody
.hidden
= true;
121 base
.resizeWindowToContent(true);
125 * @return {string} A random string ID.
127 function generateId() {
128 var idArray
= new Uint8Array(20);
129 window
.crypto
.getRandomValues(idArray
);
130 return window
.btoa(String
.fromCharCode
.apply(null, idArray
));
134 * @param {string=} token
136 function onToken(token
) {
137 var getUserInfo = function() {
138 console
.assert(Boolean(token
), 'token is undefined.');
139 var oauth2Api
= new remoting
.OAuth2ApiImpl();
140 oauth2Api
.getUserInfo(
141 onUserInfo
, onUserInfo
.bind(null, 'unknown', 'unknown'),
142 /** @type {string} */(token
));
145 onUserInfo('unknown', 'unknown');
147 if (abandonHost
== 'yes') {
149 'abandonHost': 'true',
150 'crashServiceReportId': crashServiceReportId
152 var uri
= remoting
.settings
.APP_REMOTING_API_BASE_URL
+
153 '/applications/' + appId
+
156 var onDone = function(/** !remoting.Xhr.Response */ response
) {
157 if (response
.status
>= 200 && response
.status
< 300) {
168 }).start().then(onDone
);
177 var abandon
= /** @type {HTMLInputElement} */
178 (document
.getElementById('abandon-host'));
179 if (abandon
.checked
) {
182 chrome
.identity
.getAuthToken({ 'interactive': false }, onToken
);
185 function onCancel() {
189 function onToggleAbandon() {
190 var abandon
= document
.getElementById('abandon-host');
191 var includeLogs
= document
.getElementById('include-logs');
192 var includeLogsLabel
= document
.getElementById('include-logs-label');
193 var learnMoreLink
= document
.getElementById('learn-more');
194 includeLogs
.disabled
= !abandon
.checked
;
195 if (abandon
.checked
) {
196 includeLogsLabel
.classList
.remove('disabled');
197 learnMoreLink
.classList
.remove('disabled');
199 includeLogsLabel
.classList
.add('disabled');
200 learnMoreLink
.classList
.add('disabled');
204 function onToggleLogs() {
205 var includeLogs
= document
.getElementById('include-logs');
206 if (includeLogs
.checked
) {
207 crashServiceReportId
= generateId();
209 crashServiceReportId
= '';
213 /** @param {Event} event */
214 function onLearnMore(event
) {
215 event
.preventDefault(); // Clicking the link should not tick the checkbox.
216 var learnMoreLink
= document
.getElementById('learn-more');
217 var learnMoreInfobox
= document
.getElementById('privacy-info');
218 learnMoreLink
.hidden
= true;
219 learnMoreInfobox
.hidden
= false;
220 base
.resizeWindowToContent(true);
223 function onCategorySelect() {
224 var feedbackCategory
= /** @type {HTMLSelectElement} */
225 (document
.getElementById('feedback-category'));
226 console
.assert(feedbackCategory
.selectedOptions
.length
== 1,
227 'Expected exactly one selection; got ' +
228 feedbackCategory
.selectedOptions
.length
+ '.');
229 var selectedOption
= /** @type {HTMLElement} */
230 (feedbackCategory
.selectedOptions
[0]);
231 selectedCategory
= selectedOption
.getAttribute('i18n-content');
232 var selected
= selectedCategory
!= 'FEEDBACK_CATEGORY_SELECT';
233 document
.getElementById('feedback-consent-ok').disabled
= !selected
;
234 document
.getElementById('form-body').hidden
= !selected
;
235 base
.resizeWindowToContent(false);
239 window
.addEventListener('message', onWindowMessage
, false);
240 remoting
.settings
= new remoting
.Settings();
242 var ok
= document
.getElementById('feedback-consent-ok');
243 var cancel
= document
.getElementById('feedback-consent-cancel');
244 var abandon
= document
.getElementById('abandon-host-label');
245 var includeLogs
= document
.getElementById('include-logs-label');
246 var learnMoreLink
= document
.getElementById('learn-more');
247 var feedbackCategory
= document
.getElementById('feedback-category');
248 ok
.addEventListener('click', onOk
, false);
249 cancel
.addEventListener('click', onCancel
, false);
250 abandon
.addEventListener('click', onToggleAbandon
, false);
251 includeLogs
.addEventListener('click', onToggleLogs
, false);
252 learnMoreLink
.addEventListener('click', onLearnMore
, false);
253 feedbackCategory
.addEventListener('change', onCategorySelect
, false);
254 base
.resizeWindowToContent(true);
257 /** @param {Event} event */
258 function onWindowMessage(event
) {
259 applicationWindow
= event
.source
;
260 var method
= /** @type {string} */ (event
.data
['method']);
261 if (method
== 'init') {
262 if (event
.data
['hostId']) {
263 hostId
= /** @type {string} */ (event
.data
['hostId']);
265 appId
= /** @type {string} */ (event
.data
['appId']);
266 connectionStats
= /** @type {string} */ (event
.data
['connectionStats']);
267 consoleErrors
= /** @type {string} */ (event
.data
['consoleErrors']);
268 sessionId
= /** @type {string} */ (event
.data
['sessionId']);
272 window
.addEventListener('load', onLoad
, false);