1// Copyright 2013 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/** @type {string} 6 * @const 7 */ 8var FEEDBACK_LANDING_PAGE = 9 'https://www.google.com/support/chrome/go/feedback_confirmation'; 10/** @type {number} 11 * @const 12 */ 13var MAX_ATTACH_FILE_SIZE = 3 * 1024 * 1024; 14 15/** 16 * @type {number} 17 * @const 18 */ 19var FEEDBACK_MIN_WIDTH = 500; 20/** 21 * @type {number} 22 * @const 23 */ 24var FEEDBACK_MIN_HEIGHT = 585; 25 26/** @type {number} 27 * @const 28 */ 29var CONTENT_MARGIN_HEIGHT = 40; 30 31/** @type {number} 32 * @const 33 */ 34var MAX_SCREENSHOT_WIDTH = 100; 35 36/** @type {string} 37 * @const 38 */ 39var SYSINFO_WINDOW_ID = 'sysinfo_window'; 40 41/** @type {string} 42 * @const 43 */ 44var STATS_WINDOW_ID = 'stats_window'; 45 46var attachedFileBlob = null; 47var lastReader = null; 48 49var feedbackInfo = null; 50var systemInfo = null; 51 52/** 53 * Reads the selected file when the user selects a file. 54 * @param {Event} fileSelectedEvent The onChanged event for the file input box. 55 */ 56function onFileSelected(fileSelectedEvent) { 57 $('attach-error').hidden = true; 58 var file = fileSelectedEvent.target.files[0]; 59 if (!file) { 60 // User canceled file selection. 61 attachedFileBlob = null; 62 return; 63 } 64 65 if (file.size > MAX_ATTACH_FILE_SIZE) { 66 $('attach-error').hidden = false; 67 68 // Clear our selected file. 69 $('attach-file').value = ''; 70 attachedFileBlob = null; 71 return; 72 } 73 74 attachedFileBlob = file.slice(); 75} 76 77/** 78 * Clears the file that was attached to the report with the initial request. 79 * Instead we will now show the attach file button in case the user wants to 80 * attach another file. 81 */ 82function clearAttachedFile() { 83 $('custom-file-container').hidden = true; 84 attachedFileBlob = null; 85 feedbackInfo.attachedFile = null; 86 $('attach-file').hidden = false; 87} 88 89/** 90 * Creates a closure that creates or shows a window with the given url. 91 * @param {string} windowId A string with the ID of the window we are opening. 92 * @param {string} url The destination URL of the new window. 93 * @return {function()} A function to be called to open the window. 94 */ 95function windowOpener(windowId, url) { 96 return function(e) { 97 e.preventDefault(); 98 chrome.app.window.create(url, {id: windowId}); 99 }; 100} 101 102/** 103 * Opens a new window with chrome://slow_trace, downloading performance data. 104 */ 105function openSlowTraceWindow() { 106 chrome.app.window.create( 107 'chrome://slow_trace/tracing.zip#' + feedbackInfo.traceId); 108} 109 110/** 111 * Sends the report; after the report is sent, we need to be redirected to 112 * the landing page, but we shouldn't be able to navigate back, hence 113 * we open the landing page in a new tab and sendReport closes this tab. 114 * @return {boolean} True if the report was sent. 115 */ 116function sendReport() { 117 if ($('description-text').value.length == 0) { 118 var description = $('description-text'); 119 description.placeholder = loadTimeData.getString('no-description'); 120 description.focus(); 121 return false; 122 } 123 124 // Prevent double clicking from sending additional reports. 125 $('send-report-button').disabled = true; 126 console.log('Feedback: Sending report'); 127 if (!feedbackInfo.attachedFile && attachedFileBlob) { 128 feedbackInfo.attachedFile = { name: $('attach-file').value, 129 data: attachedFileBlob }; 130 } 131 132 feedbackInfo.description = $('description-text').value; 133 feedbackInfo.pageUrl = $('page-url-text').value; 134 feedbackInfo.email = $('user-email-text').value; 135 136 var useSystemInfo = false; 137 var useHistograms = false; 138 if ($('sys-info-checkbox') != null && 139 $('sys-info-checkbox').checked && 140 systemInfo != null) { 141 // Send histograms along with system info. 142 useSystemInfo = useHistograms = true; 143 } 144<if expr="chromeos"> 145 if ($('performance-info-checkbox') == null || 146 !($('performance-info-checkbox').checked)) { 147 feedbackInfo.traceId = null; 148 } 149</if> 150 151 if (useSystemInfo) { 152 if (feedbackInfo.systemInformation != null) { 153 // Concatenate sysinfo if we had any initial system information 154 // sent with the feedback request event. 155 feedbackInfo.systemInformation = 156 feedbackInfo.systemInformation.concat(systemInfo); 157 } else { 158 feedbackInfo.systemInformation = systemInfo; 159 } 160 } 161 162 feedbackInfo.sendHistograms = useHistograms; 163 164 // If the user doesn't want to send the screenshot. 165 if (!$('screenshot-checkbox').checked) 166 feedbackInfo.screenshot = null; 167 168 chrome.feedbackPrivate.sendFeedback(feedbackInfo, function(result) { 169 window.open(FEEDBACK_LANDING_PAGE, '_blank'); 170 window.close(); 171 }); 172 173 return true; 174} 175 176/** 177 * Click listener for the cancel button. 178 * @param {Event} e The click event being handled. 179 */ 180function cancel(e) { 181 e.preventDefault(); 182 window.close(); 183} 184 185/** 186 * Converts a blob data URL to a blob object. 187 * @param {string} url The data URL to convert. 188 * @return {Blob} Blob object containing the data. 189 */ 190function dataUrlToBlob(url) { 191 var mimeString = url.split(',')[0].split(':')[1].split(';')[0]; 192 var data = atob(url.split(',')[1]); 193 var dataArray = []; 194 for (var i = 0; i < data.length; ++i) 195 dataArray.push(data.charCodeAt(i)); 196 197 return new Blob([new Uint8Array(dataArray)], {type: mimeString}); 198} 199 200<if expr="chromeos"> 201/** 202 * Update the page when performance feedback state is changed. 203 */ 204function performanceFeedbackChanged() { 205 if ($('performance-info-checkbox').checked) { 206 $('attach-file').disabled = true; 207 $('attach-file').checked = false; 208 209 $('screenshot-checkbox').disabled = true; 210 $('screenshot-checkbox').checked = false; 211 } else { 212 $('attach-file').disabled = false; 213 $('screenshot-checkbox').disabled = false; 214 } 215} 216</if> 217 218function resizeAppWindow() { 219 // We pick the width from the titlebar, which has no margins. 220 var width = $('title-bar').scrollWidth; 221 if (width < FEEDBACK_MIN_WIDTH) 222 width = FEEDBACK_MIN_WIDTH; 223 224 // We get the height by adding the titlebar height and the content height + 225 // margins. We can't get the margins for the content-pane here by using 226 // style.margin - the variable seems to not exist. 227 var height = $('title-bar').scrollHeight + 228 $('content-pane').scrollHeight + CONTENT_MARGIN_HEIGHT; 229 if (height < FEEDBACK_MIN_HEIGHT) 230 height = FEEDBACK_MIN_HEIGHT; 231 232 chrome.app.window.current().resizeTo(width, height); 233} 234 235/** 236 * Initializes our page. 237 * Flow: 238 * .) DOMContent Loaded -> . Request feedbackInfo object 239 * . Setup page event handlers 240 * .) Feedback Object Received -> . take screenshot 241 * . request email 242 * . request System info 243 * . request i18n strings 244 * .) Screenshot taken -> . Show Feedback window. 245 */ 246function initialize() { 247 // TODO(rkc): Remove logging once crbug.com/284662 is closed. 248 console.log('FEEDBACK_DEBUG: feedback.js: initialize()'); 249 250 // Add listener to receive the feedback info object. 251 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 252 if (request.sentFromEventPage) { 253 // TODO(rkc): Remove logging once crbug.com/284662 is closed. 254 console.log('FEEDBACK_DEBUG: Received feedbackInfo.'); 255 feedbackInfo = request.data; 256 $('description-text').textContent = feedbackInfo.description; 257 if (feedbackInfo.pageUrl) 258 $('page-url-text').value = feedbackInfo.pageUrl; 259 260 takeScreenshot(function(screenshotCanvas) { 261 // TODO(rkc): Remove logging once crbug.com/284662 is closed. 262 console.log('FEEDBACK_DEBUG: Taken screenshot. Showing window.'); 263 264 // We've taken our screenshot, show the feedback page without any 265 // further delay. 266 window.webkitRequestAnimationFrame(function() { 267 resizeAppWindow(); 268 }); 269 chrome.app.window.current().show(); 270 271 var screenshotDataUrl = screenshotCanvas.toDataURL('image/png'); 272 $('screenshot-image').src = screenshotDataUrl; 273 $('screenshot-image').classList.toggle('wide-screen', 274 $('screenshot-image').width > MAX_SCREENSHOT_WIDTH); 275 feedbackInfo.screenshot = dataUrlToBlob(screenshotDataUrl); 276 }); 277 278 chrome.feedbackPrivate.getUserEmail(function(email) { 279 $('user-email-text').value = email; 280 }); 281 282 chrome.feedbackPrivate.getSystemInformation(function(sysInfo) { 283 systemInfo = sysInfo; 284 }); 285 286 // An extension called us with an attached file. 287 if (feedbackInfo.attachedFile) { 288 $('attached-filename-text').textContent = 289 feedbackInfo.attachedFile.name; 290 attachedFileBlob = feedbackInfo.attachedFile.data; 291 $('custom-file-container').hidden = false; 292 $('attach-file').hidden = true; 293 } 294 295<if expr="chromeos"> 296 if (feedbackInfo.traceId && ($('performance-info-area'))) { 297 $('performance-info-area').hidden = false; 298 $('performance-info-checkbox').checked = true; 299 performanceFeedbackChanged(); 300 $('performance-info-link').onclick = openSlowTraceWindow; 301 } 302</if> 303 304 chrome.feedbackPrivate.getStrings(function(strings) { 305 loadTimeData.data = strings; 306 i18nTemplate.process(document, loadTimeData); 307 308 if ($('sys-info-url')) { 309 // Opens a new window showing the current system info. 310 $('sys-info-url').onclick = 311 windowOpener(SYSINFO_WINDOW_ID, 'chrome://system'); 312 } 313 if ($('histograms-url')) { 314 // Opens a new window showing the histogram metrics. 315 $('histograms-url').onclick = 316 windowOpener(STATS_WINDOW_ID, 'chrome://histograms'); 317 } 318 }); 319 } 320 }); 321 322 window.addEventListener('DOMContentLoaded', function() { 323 // TODO(rkc): Remove logging once crbug.com/284662 is closed. 324 console.log('FEEDBACK_DEBUG: feedback.js: DOMContentLoaded'); 325 // Ready to receive the feedback object. 326 chrome.runtime.sendMessage({ready: true}); 327 328 // Setup our event handlers. 329 $('attach-file').addEventListener('change', onFileSelected); 330 $('send-report-button').onclick = sendReport; 331 $('cancel-button').onclick = cancel; 332 $('remove-attached-file').onclick = clearAttachedFile; 333<if expr="chromeos"> 334 $('performance-info-checkbox').addEventListener( 335 'change', performanceFeedbackChanged); 336</if> 337 }); 338} 339 340initialize(); 341