Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: chrome/browser/resources/feedback/js/event_handler.js

Issue 1794513002: Fix sending multiple feedback reports within short durations of each other (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @type {number} 6 * @type {number}
7 * @const 7 * @const
8 */ 8 */
9 var FEEDBACK_WIDTH = 500; 9 var FEEDBACK_WIDTH = 500;
10 /** 10 /**
11 * @type {number} 11 * @type {number}
12 * @const 12 * @const
13 */ 13 */
14 var FEEDBACK_HEIGHT = 585; 14 var FEEDBACK_HEIGHT = 585;
15 15
16 /** 16 /**
17 * @type {string} 17 * @type {string}
18 * @const 18 * @const
19 */ 19 */
20 var FEEDBACK_DEFAULT_WINDOW_ID = 'default_window'; 20 var FEEDBACK_DEFAULT_WINDOW_ID = 'default_window';
21 21
22 /**
23 * The feedback info received initially when the feedback UI was first requested
24 * to start.
25 * @type {Object}
26 */
27 var initialFeedbackInfo = null;
28
29 /**
30 * The feedbak info that is ready to be sent later when the system information
31 * becomes available.
32 * @type {Object}
33 */
34 var finalFeedbackInfo = null;
35
36 /**
37 * The system information received from the C++ side.
38 * @type {Object}
39 */
40 var systemInfo = null;
41
42 /**
43 * True if the system information has been received, false otherwise.
44 * @type {boolean}
45 */
46 var isSystemInfoReady = false;
47
48 /**
49 * A callback to be invoked when the system information is ready.
50 * @type {function(sysInfo)}
51 */
52 var onSystemInfoReadyCallback = null;
53
54 // To generate a hashed extension ID, use a sha-256 hash, all in lower case. 22 // To generate a hashed extension ID, use a sha-256 hash, all in lower case.
55 // Example: 23 // Example:
56 // echo -n 'abcdefghijklmnopqrstuvwxyzabcdef' | sha1sum | \ 24 // echo -n 'abcdefghijklmnopqrstuvwxyzabcdef' | sha1sum | \
57 // awk '{print toupper($1)}' 25 // awk '{print toupper($1)}'
58 var whitelistedExtensionIds = [ 26 var whitelistedExtensionIds = [
59 '12E618C3C6E97495AAECF2AC12DEB082353241C6', // QuickOffice 27 '12E618C3C6E97495AAECF2AC12DEB082353241C6', // QuickOffice
60 '3727DD3E564B6055387425027AD74C58784ACC15', // QuickOffice 28 '3727DD3E564B6055387425027AD74C58784ACC15', // QuickOffice
61 '2FC374607C2DF285634B67C64A2E356C607091C3', // QuickOffice 29 '2FC374607C2DF285634B67C64A2E356C607091C3', // QuickOffice
62 '2843C1E82A9B6C6FB49308FDDF4E157B6B44BC2B', // G+ Photos 30 '2843C1E82A9B6C6FB49308FDDF4E157B6B44BC2B', // G+ Photos
63 '5B5DA6D054D10DB917AF7D9EAE3C56044D1B0B03', // G+ Photos 31 '5B5DA6D054D10DB917AF7D9EAE3C56044D1B0B03', // G+ Photos
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 'A8208CCC87F8261AFAEB6B85D5E8D47372DDEA6B', // http://crbug.com/478929 72 'A8208CCC87F8261AFAEB6B85D5E8D47372DDEA6B', // http://crbug.com/478929
105 'B620CF4203315F9F2E046EDED22C7571A935958D', // http://crbug.com/510270 73 'B620CF4203315F9F2E046EDED22C7571A935958D', // http://crbug.com/510270
106 'B206D8716769728278D2D300349C6CB7D7DE2EF9', // http://crbug.com/510270 74 'B206D8716769728278D2D300349C6CB7D7DE2EF9', // http://crbug.com/510270
107 'EFCF5358672FEE04789FD2EC3638A67ADEDB6C8C', // http://crbug.com/514696 75 'EFCF5358672FEE04789FD2EC3638A67ADEDB6C8C', // http://crbug.com/514696
108 'FAD85BC419FE00995D196312F53448265EFA86F1', // http://crbug.com/516527 76 'FAD85BC419FE00995D196312F53448265EFA86F1', // http://crbug.com/516527
109 'F33B037DEDA65F226B7409C2ADB0CF3F8565AB03', // http://crbug.com/541769 77 'F33B037DEDA65F226B7409C2ADB0CF3F8565AB03', // http://crbug.com/541769
110 '969C788BCBC82FBBE04A17360CA165C23A419257', // http://crbug.com/541769 78 '969C788BCBC82FBBE04A17360CA165C23A419257', // http://crbug.com/541769
111 '3BC3740BFC58F06088B300274B4CFBEA20136342', // http://crbug.com/541769 79 '3BC3740BFC58F06088B300274B4CFBEA20136342', // http://crbug.com/541769
112 ]; 80 ];
113 81
82 /**
83 * A FeedbackRequest object represents a unique feedback report, requested by an
84 * instance of the feedback window. It contains the system information specific
85 * to this report, the full feedbackInfo, and callbacks to send the report upon
86 * request.
87 */
88 class FeedbackRequest {
xiyuan 2016/03/14 23:47:28 Are we allowed to use this ES6 feature? This seems
afakhry 2016/03/15 19:25:38 I asked sky@ who referred me to estade@ who referr
xiyuan 2016/03/15 19:54:06 Thank you for digging into this. I am fine as long
89 constructor(requestId, feedbackInfo) {
90 this.id_ = requestId;
91 this.feedbackInfo_ = feedbackInfo;
92 this.onSystemInfoReadyCallback_ = null;
93 this.isSystemInfoReady_ = false;
94 }
95
96 /**
97 * Called when the system information is sent from the C++ side.
98 * @param {Object} sysInfo The received system information.
99 */
100 getSystemInformationCallback(sysInfo) {
101 this.isSystemInfoReady_ = true;
102
103 // Combine the newly received system information with whatever system
104 // information we have in the feedback info (if any).
105 if (this.feedbackInfo_.systemInformation) {
106 this.feedbackInfo_.systemInformation =
107 this.feedbackInfo_.systemInformation.concat(sysInfo);
108 } else {
109 this.feedbackInfo_.systemInformation = sysInfo;
110 }
xiyuan 2016/03/14 23:47:28 Just to double check with you that changes here on
afakhry 2016/03/15 19:25:38 Yes I made sure they refer to the same thing, and
111
112 if (this.onSystemInfoReadyCallback_ != null) {
113 this.onSystemInfoReadyCallback_();
114 this.onSystemInfoReadyCallback_ = null;
115 }
116 }
117
118 /**
119 * Retrieves the system information for this request object.
120 * @param {function()} callback Invoked to notify the listener that the system
121 * information has been received.
122 */
123 getSystemInformation(callback) {
124 if (this.isSystemInfoReady_) {
125 callback();
126 return;
127 }
128
129 this.onSystemInfoReadyCallback_ = callback;
130 // The C++ side must reply to the callback specific to this object.
131 var boundCallback = this.getSystemInformationCallback.bind(this);
132 chrome.feedbackPrivate.getSystemInformation(boundCallback);
133 }
134
135 /**
136 * Sends the feedback report represented by the object, either now if system
137 * information is ready, or later once it is.
138 * @param {boolean} useSystemInfo True if the user would like the system
139 * information to be sent with the report.
140 */
141 sendReport(useSystemInfo) {
142 if (useSystemInfo && !this.isSystemInfoReady_) {
143 this.onSystemInfoReadyCallback_ = this.sendReportNow;
xiyuan 2016/03/14 23:47:28 Think we need to bind here, i.e. this.sendReportNo
afakhry 2016/03/15 19:25:38 It works without this bind, since it will be calle
xiyuan 2016/03/15 19:54:06 Interesting. Just tried it and it works as you des
144 return;
145 }
146
147 this.sendReportNow();
148 }
149
150 /**
151 * Sends the report immediately and removes this object once the report is
152 * sent.
153 */
154 sendReportNow() {
155 /** @const */ var ID = this.id_;
156 chrome.feedbackPrivate.sendFeedback(this.feedbackInfo_,
157 function(result) {
158 console.log('Feedback: Report sent for request with ID ' + ID);
159
160 // The following deletes this FeedbackRequest object.
161 feedbackRequestsMap.delete(ID);
162 });
163 }
164 };
165
166 /**
167 * Maps each FeedbackRequest object by its unique ID.
168 * @type {Map}
169 */
170 var feedbackRequestsMap = new Map();
171
172 /**
173 * Used to generate unique IDs for FeedbackRequest objects.
174 * @type {number}
175 */
176 var lastUsedId = 0;
114 177
115 /** 178 /**
116 * Function to determine whether or not a given extension id is whitelisted to 179 * Function to determine whether or not a given extension id is whitelisted to
117 * invoke the feedback UI. If the extension is whitelisted, the callback to 180 * invoke the feedback UI. If the extension is whitelisted, the callback to
118 * start the Feedback UI will be called. 181 * start the Feedback UI will be called.
119 * @param {string} id the id of the sender extension. 182 * @param {string} id the id of the sender extension.
120 * @param {Function} startFeedbackCallback The callback function that will 183 * @param {Function} startFeedbackCallback The callback function that will
121 * will start the feedback UI. 184 * will start the feedback UI.
122 * @param {Object} feedbackInfo The feedback info object to pass to the 185 * @param {Object} feedbackInfo The feedback info object to pass to the
123 * start feedback UI callback. 186 * start feedback UI callback.
(...skipping 14 matching lines...) Expand all
138 } 201 }
139 202
140 /** 203 /**
141 * Callback which gets notified once our feedback UI has loaded and is ready to 204 * Callback which gets notified once our feedback UI has loaded and is ready to
142 * receive its initial feedback info object. 205 * receive its initial feedback info object.
143 * @param {Object} request The message request object. 206 * @param {Object} request The message request object.
144 * @param {Object} sender The sender of the message. 207 * @param {Object} sender The sender of the message.
145 * @param {function(Object)} sendResponse Callback for sending a response. 208 * @param {function(Object)} sendResponse Callback for sending a response.
146 */ 209 */
147 function feedbackReadyHandler(request, sender, sendResponse) { 210 function feedbackReadyHandler(request, sender, sendResponse) {
148 if (request.ready) { 211 if (request.ready)
149 chrome.runtime.sendMessage( 212 chrome.runtime.sendMessage({sentFromEventPage: true});
150 {sentFromEventPage: true, data: initialFeedbackInfo});
151 }
152 } 213 }
153 214
154
155 /** 215 /**
156 * Callback which gets notified if another extension is requesting feedback. 216 * Callback which gets notified if another extension is requesting feedback.
157 * @param {Object} request The message request object. 217 * @param {Object} request The message request object.
158 * @param {Object} sender The sender of the message. 218 * @param {Object} sender The sender of the message.
159 * @param {function(Object)} sendResponse Callback for sending a response. 219 * @param {function(Object)} sendResponse Callback for sending a response.
160 */ 220 */
161 function requestFeedbackHandler(request, sender, sendResponse) { 221 function requestFeedbackHandler(request, sender, sendResponse) {
162 if (request.requestFeedback) 222 if (request.requestFeedback)
163 senderWhitelisted(sender.id, startFeedbackUI, request.feedbackInfo); 223 senderWhitelisted(sender.id, startFeedbackUI, request.feedbackInfo);
164 } 224 }
165 225
166 /** 226 /**
167 * Called when the system information is sent from the C++ side.
168 * @param {Object} sysInfo The received system information.
169 */
170
171 function getSystemInformationCallback(sysInfo) {
172 systemInfo = sysInfo;
173 isSystemInfoReady = true;
174 if (onSystemInfoReadyCallback != null)
175 onSystemInfoReadyCallback(sysInfo);
176 }
177
178 /**
179 * If the user requested to send the report before the system information was
180 * received, this callback will be invoked once the system information is ready
181 * to send the report then.
182 * @param {Object} sysInfo The received system information.
183 */
184
185 function onSysInfoReadyForSend(sysInfo) {
186 // Combine the newly received system information with whatever system
187 // information we have in the final feedback info (if any).
188 if (finalFeedbackInfo.systemInformation) {
189 finalFeedbackInfo.systemInformation =
190 finalFeedbackInfo.systemInformation.concat(sysInfo);
191 } else {
192 finalFeedbackInfo.systemInformation = sysInfo;
193 }
194
195 chrome.feedbackPrivate.sendFeedback(finalFeedbackInfo, function(result) {});
196 }
197
198 /**
199 * Callback which starts up the feedback UI. 227 * Callback which starts up the feedback UI.
200 * @param {Object} feedbackInfo Object containing any initial feedback info. 228 * @param {Object} feedbackInfo Object containing any initial feedback info.
201 */ 229 */
202 function startFeedbackUI(feedbackInfo) { 230 function startFeedbackUI(feedbackInfo) {
203 var win = chrome.app.window.get(FEEDBACK_DEFAULT_WINDOW_ID); 231 var win = chrome.app.window.get(FEEDBACK_DEFAULT_WINDOW_ID);
204 if (win) { 232 if (win) {
205 win.show(); 233 win.show();
206 return; 234 return;
207 } 235 }
208 chrome.app.window.create('html/default.html', { 236 chrome.app.window.create('html/default.html', {
209 frame: 'none', 237 frame: 'none',
210 id: FEEDBACK_DEFAULT_WINDOW_ID, 238 id: FEEDBACK_DEFAULT_WINDOW_ID,
211 width: FEEDBACK_WIDTH, 239 width: FEEDBACK_WIDTH,
212 height: FEEDBACK_HEIGHT, 240 height: FEEDBACK_HEIGHT,
213 hidden: true, 241 hidden: true,
214 resizable: false }, 242 resizable: false },
215 function(appWindow) { 243 function(appWindow) {
216 // Initialize the state of the app only once upon the creation of the 244 // Generate a unique feedback request ID for this feedback window.
217 // feedback UI window. 245 // A FeedbackRequest object is used to represent this instance of the
218 initialFeedbackInfo = feedbackInfo; 246 // feedback window.
219 finalFeedbackInfo = null; 247 /** @const */ var ID = ++lastUsedId;
220 systemInfo = null; 248 feedbackRequestsMap.set(ID, new FeedbackRequest(ID, feedbackInfo));
xiyuan 2016/03/14 23:47:28 It seems |feedbackRequestsMap| could be optimized
afakhry 2016/03/15 19:25:38 Yes, thanks for the suggestion. Done. However, we
xiyuan 2016/03/15 19:54:06 I don't think we need to manage this explicitly. I
afakhry 2016/03/16 00:45:34 Right. I removed the map, and it works. My brain n
221 isSystemInfoReady = false; 249 // The feedbackInfo member of the new window should refer to the one in
222 onSystemInfoReadyCallback = null; 250 // its corresponding FeedbackRequest object to avoid copying and
251 // duplicatations.
252 appWindow.contentWindow.feedbackInfo =
253 feedbackRequestsMap.get(ID).feedbackInfo_;
254
255 // When this instance of the feedback window is closed, the
256 // corresponding FeedbackRequest object must be deleted unless it needs
257 // to be kept alive until the report is sent later.
258 var deferRequestdeleteUntilSent = false;
223 259
224 // Define some functions for the new window so that it can call back 260 // Define some functions for the new window so that it can call back
225 // into here. 261 // into here.
226 262
227 // Define a function for the new window to get the system information. 263 // Define a function for the new window to get the system information.
228 appWindow.contentWindow.getSystemInformation = function(callback) { 264 appWindow.contentWindow.getSystemInformation = function(callback) {
229 if (!isSystemInfoReady) { 265 var request = feedbackRequestsMap.get(ID);
230 onSystemInfoReadyCallback = callback; 266 if (request == undefined) {
231 chrome.feedbackPrivate.getSystemInformation( 267 console.log('Feedback Error: Invalid request ID ' + ID);
232 getSystemInformationCallback);
233 return; 268 return;
234 } 269 }
235 270
236 callback(systemInfo); 271 request.getSystemInformation(callback);
237 }; 272 };
238 273
239 // Define a function to be called by the new window when the report is 274 // Define a function to request sending the feedback report.
240 // not ready yet, and has to be sent later when the system information 275 appWindow.contentWindow.sendFeedbackReport = function(useSystemInfo) {
241 // is received. 276 var request = feedbackRequestsMap.get(ID);
242 appWindow.contentWindow.sendReportLater = function(feedbackInfo) { 277 if (request == undefined) {
243 finalFeedbackInfo = feedbackInfo; 278 console.log('Feedback Error: Invalid request ID ' + ID);
244 if (!isSystemInfoReady) {
245 onSystemInfoReadyCallback = onSysInfoReadyForSend;
246 return; 279 return;
247 } 280 }
248 281
249 onSysInfoReadyForSend(systemInfo); 282 // The report will be sent by the request object. Keep it alive until
283 // the report is sent.
284 deferRequestdeleteUntilSent = true;
285 request.sendReport(useSystemInfo);
250 }; 286 };
251 287
252 // Returns whether the system information has been received or not. 288 // Observe when the window is closed.
253 appWindow.contentWindow.isSystemInfoReady = function() { 289 appWindow.onClosed.addListener(function() {
254 return isSystemInfoReady; 290 if (!deferRequestdeleteUntilSent)
255 }; 291 feedbackRequestsMap.delete(appWindow.contentWindow.REQUEST_ID);
292 });
256 }); 293 });
257 } 294 }
258 295
259 chrome.runtime.onMessage.addListener(feedbackReadyHandler); 296 chrome.runtime.onMessage.addListener(feedbackReadyHandler);
260 chrome.runtime.onMessageExternal.addListener(requestFeedbackHandler); 297 chrome.runtime.onMessageExternal.addListener(requestFeedbackHandler);
261 chrome.feedbackPrivate.onFeedbackRequested.addListener(startFeedbackUI); 298 chrome.feedbackPrivate.onFeedbackRequested.addListener(startFeedbackUI);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698