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);