OLD | NEW |
| (Empty) |
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. | |
4 | |
5 'use strict'; | |
6 | |
7 /** | |
8 * @type {string} The app id (from the webstore) for this application. | |
9 */ | |
10 var appId = ''; | |
11 | |
12 /** | |
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. | |
16 */ | |
17 var hostId = '@pending'; | |
18 | |
19 /** | |
20 * @type {string} The network stats at the time the feedback consent dialog | |
21 * was shown. | |
22 */ | |
23 var connectionStats = ''; | |
24 | |
25 /** | |
26 * @type {string} JSON representation of recent console error messages. | |
27 */ | |
28 var consoleErrors = ''; | |
29 | |
30 /** | |
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). | |
33 */ | |
34 var sessionId = ''; | |
35 | |
36 /** | |
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. | |
39 */ | |
40 var abandonHost = 'no'; | |
41 | |
42 /** | |
43 * @type {Window} The main application window. | |
44 */ | |
45 var applicationWindow = null; | |
46 | |
47 /** | |
48 * @type {string} An unique identifier that links the feedback post with the | |
49 * logs uploaded by the host. | |
50 */ | |
51 var crashServiceReportId = ''; | |
52 | |
53 /** | |
54 * @type {string} The user-selected feedback category, represented by its | |
55 * l10n tag. | |
56 */ | |
57 var selectedCategory = ''; | |
58 | |
59 /** | |
60 * @param {string} email | |
61 * @param {string} realName | |
62 */ | |
63 function onUserInfo(email, realName) { | |
64 /** @type {number} Identifies this product to Google Feedback. **/ | |
65 var productId = 93407; | |
66 | |
67 /** @type {string} The base URL for Google Feedback. */ | |
68 var url = 'https://www.google.com/tools/feedback/survey/xhtml'; | |
69 | |
70 /** @type {string} The feedback 'bucket', used for clustering. */ | |
71 var bucket = 'feedback'; | |
72 | |
73 /** @type {string} The user's locale, used to localize the feedback page. */ | |
74 var locale = chrome.i18n.getMessage('@@ui_locale'); | |
75 | |
76 window.open(url + | |
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)); | |
87 window.close(); | |
88 | |
89 // If the VM was successfully abandoned, close the application. | |
90 if (abandonHost == 'yes') { | |
91 applicationWindow.close(); | |
92 } | |
93 }; | |
94 | |
95 /** | |
96 * @param {boolean} waiting | |
97 */ | |
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; | |
107 } | |
108 | |
109 function showError() { | |
110 setWaiting(false); | |
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); | |
122 } | |
123 | |
124 /** | |
125 * @return {string} A random string ID. | |
126 */ | |
127 function generateId() { | |
128 var idArray = new Uint8Array(20); | |
129 window.crypto.getRandomValues(idArray); | |
130 return window.btoa(String.fromCharCode.apply(null, idArray)); | |
131 } | |
132 | |
133 /** | |
134 * @param {string=} token | |
135 */ | |
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)); | |
143 }; | |
144 if (!token) { | |
145 onUserInfo('unknown', 'unknown'); | |
146 } else { | |
147 if (abandonHost == 'yes') { | |
148 var body = { | |
149 'abandonHost': 'true', | |
150 'crashServiceReportId': crashServiceReportId | |
151 }; | |
152 var uri = remoting.settings.APP_REMOTING_API_BASE_URL + | |
153 '/applications/' + appId + | |
154 '/hosts/' + hostId + | |
155 '/reportIssue'; | |
156 var onDone = function(/** !remoting.Xhr.Response */ response) { | |
157 if (response.status >= 200 && response.status < 300) { | |
158 getUserInfo(); | |
159 } else { | |
160 showError(); | |
161 } | |
162 }; | |
163 new remoting.Xhr({ | |
164 method: 'POST', | |
165 url: uri, | |
166 jsonContent: body, | |
167 oauthToken: token | |
168 }).start().then(onDone); | |
169 } else { | |
170 getUserInfo(); | |
171 } | |
172 } | |
173 } | |
174 | |
175 function onOk() { | |
176 setWaiting(true); | |
177 var abandon = /** @type {HTMLInputElement} */ | |
178 (document.getElementById('abandon-host')); | |
179 if (abandon.checked) { | |
180 abandonHost = 'yes'; | |
181 } | |
182 chrome.identity.getAuthToken({ 'interactive': false }, onToken); | |
183 } | |
184 | |
185 function onCancel() { | |
186 window.close(); | |
187 } | |
188 | |
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'); | |
198 } else { | |
199 includeLogsLabel.classList.add('disabled'); | |
200 learnMoreLink.classList.add('disabled'); | |
201 } | |
202 } | |
203 | |
204 function onToggleLogs() { | |
205 var includeLogs = document.getElementById('include-logs'); | |
206 if (includeLogs.checked) { | |
207 crashServiceReportId = generateId(); | |
208 } else { | |
209 crashServiceReportId = ''; | |
210 } | |
211 } | |
212 | |
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); | |
221 } | |
222 | |
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); | |
236 } | |
237 | |
238 function onLoad() { | |
239 window.addEventListener('message', onWindowMessage, false); | |
240 remoting.settings = new remoting.Settings(); | |
241 l10n.localize(); | |
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); | |
255 } | |
256 | |
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']); | |
264 } | |
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']); | |
269 } | |
270 }; | |
271 | |
272 window.addEventListener('load', onLoad, false); | |
OLD | NEW |