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

Side by Side Diff: chrome/browser/resources/options/content_settings_exceptions_area.js

Issue 7003007: Apply content-security-policy to the HTML options page. This is a (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 7 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 cr.define('options.contentSettings', function() {
6 const InlineEditableItemList = options.InlineEditableItemList;
7 const InlineEditableItem = options.InlineEditableItem;
8 const ArrayDataModel = cr.ui.ArrayDataModel;
9
10 /**
11 * Creates a new exceptions list item.
12 * @param {string} contentType The type of the list.
13 * @param {string} mode The browser mode, 'otr' or 'normal'.
14 * @param {boolean} enableAskOption Whether to show an 'ask every time'
15 * option in the select.
16 * @param {Object} exception A dictionary that contains the data of the
17 * exception.
18 * @constructor
19 * @extends {options.InlineEditableItem}
20 */
21 function ExceptionsListItem(contentType, mode, enableAskOption, exception) {
22 var el = cr.doc.createElement('div');
23 el.mode = mode;
24 el.contentType = contentType;
25 el.enableAskOption = enableAskOption;
26 el.dataItem = exception;
27 el.__proto__ = ExceptionsListItem.prototype;
28 el.decorate();
29
30 return el;
31 }
32
33 ExceptionsListItem.prototype = {
34 __proto__: InlineEditableItem.prototype,
35
36 /**
37 * Called when an element is decorated as a list item.
38 */
39 decorate: function() {
40 InlineEditableItem.prototype.decorate.call(this);
41
42 this.isPlaceholder = !this.pattern;
43 var patternCell = this.createEditableTextCell(this.pattern);
44 patternCell.className = 'exception-pattern';
45 this.contentElement.appendChild(patternCell);
46 if (this.pattern)
47 this.patternLabel = patternCell.querySelector('.static-text');
48 var input = patternCell.querySelector('input');
49
50 // TODO(stuartmorgan): Create an createEditableSelectCell abstracting
51 // this code.
52 // Setting label for display mode. |pattern| will be null for the 'add new
53 // exception' row.
54 if (this.pattern) {
55 var settingLabel = cr.doc.createElement('span');
56 settingLabel.textContent = this.settingForDisplay();
57 settingLabel.className = 'exception-setting';
58 settingLabel.setAttribute('displaymode', 'static');
59 this.contentElement.appendChild(settingLabel);
60 this.settingLabel = settingLabel;
61 }
62
63 // Setting select element for edit mode.
64 var select = cr.doc.createElement('select');
65 var optionAllow = cr.doc.createElement('option');
66 optionAllow.textContent = templateData.allowException;
67 optionAllow.value = 'allow';
68 select.appendChild(optionAllow);
69
70 if (this.enableAskOption) {
71 var optionAsk = cr.doc.createElement('option');
72 optionAsk.textContent = templateData.askException;
73 optionAsk.value = 'ask';
74 select.appendChild(optionAsk);
75 }
76
77 if (this.contentType == 'cookies') {
78 var optionSession = cr.doc.createElement('option');
79 optionSession.textContent = templateData.sessionException;
80 optionSession.value = 'session';
81 select.appendChild(optionSession);
82 }
83
84 var optionBlock = cr.doc.createElement('option');
85 optionBlock.textContent = templateData.blockException;
86 optionBlock.value = 'block';
87 select.appendChild(optionBlock);
88
89 this.contentElement.appendChild(select);
90 select.className = 'exception-setting';
91 if (this.pattern)
92 select.setAttribute('displaymode', 'edit');
93
94 // Used to track whether the URL pattern in the input is valid.
95 // This will be true if the browser process has informed us that the
96 // current text in the input is valid. Changing the text resets this to
97 // false, and getting a response from the browser sets it back to true.
98 // It starts off as false for empty string (new exceptions) or true for
99 // already-existing exceptions (which we assume are valid).
100 this.inputValidityKnown = this.pattern;
101 // This one tracks the actual validity of the pattern in the input. This
102 // starts off as true so as not to annoy the user when he adds a new and
103 // empty input.
104 this.inputIsValid = true;
105
106 this.input = input;
107 this.select = select;
108
109 this.updateEditables();
110
111 // Editing notifications and geolocation is disabled for now.
112 if (this.contentType == 'notifications' ||
113 this.contentType == 'location') {
114 this.editable = false;
115 }
116
117 var listItem = this;
118 // Handle events on the editable nodes.
119 input.oninput = function(event) {
120 listItem.inputValidityKnown = false;
121 chrome.send('checkExceptionPatternValidity',
122 [listItem.contentType, listItem.mode, input.value]);
123 };
124
125 // Listen for edit events.
126 this.addEventListener('canceledit', this.onEditCancelled_);
127 this.addEventListener('commitedit', this.onEditCommitted_);
128 },
129
130 /**
131 * The pattern (e.g., a URL) for the exception.
132 * @type {string}
133 */
134 get pattern() {
135 return this.dataItem['displayPattern'];
136 },
137 set pattern(pattern) {
138 this.dataItem['displayPattern'] = pattern;
139 },
140
141 /**
142 * The setting (allow/block) for the exception.
143 * @type {string}
144 */
145 get setting() {
146 return this.dataItem['setting'];
147 },
148 set setting(setting) {
149 this.dataItem['setting'] = setting;
150 },
151
152 /**
153 * Gets a human-readable setting string.
154 * @type {string}
155 */
156 settingForDisplay: function() {
157 var setting = this.setting;
158 if (setting == 'allow')
159 return templateData.allowException;
160 else if (setting == 'block')
161 return templateData.blockException;
162 else if (setting == 'ask')
163 return templateData.askException;
164 else if (setting == 'session')
165 return templateData.sessionException;
166 },
167
168 /**
169 * Update this list item to reflect whether the input is a valid pattern.
170 * @param {boolean} valid Whether said pattern is valid in the context of
171 * a content exception setting.
172 */
173 setPatternValid: function(valid) {
174 if (valid || !this.input.value)
175 this.input.setCustomValidity('');
176 else
177 this.input.setCustomValidity(' ');
178 this.inputIsValid = valid;
179 this.inputValidityKnown = true;
180 },
181
182 /**
183 * Set the <input> to its original contents. Used when the user quits
184 * editing.
185 */
186 resetInput: function() {
187 this.input.value = this.pattern;
188 },
189
190 /**
191 * Copy the data model values to the editable nodes.
192 */
193 updateEditables: function() {
194 this.resetInput();
195
196 var settingOption =
197 this.select.querySelector('[value=\'' + this.setting + '\']');
198 if (settingOption)
199 settingOption.selected = true;
200 },
201
202 /** @inheritDoc */
203 get currentInputIsValid() {
204 return this.inputValidityKnown && this.inputIsValid;
205 },
206
207 /** @inheritDoc */
208 get hasBeenEdited() {
209 var livePattern = this.input.value;
210 var liveSetting = this.select.value;
211 return livePattern != this.pattern || liveSetting != this.setting;
212 },
213
214 /**
215 * Called when committing an edit.
216 * @param {Event} e The end event.
217 * @private
218 */
219 onEditCommitted_: function(e) {
220 var newPattern = this.input.value;
221 var newSetting = this.select.value;
222
223 this.finishEdit(newPattern, newSetting);
224 },
225
226 /**
227 * Called when cancelling an edit; resets the control states.
228 * @param {Event} e The cancel event.
229 * @private
230 */
231 onEditCancelled_: function() {
232 this.updateEditables();
233 this.setPatternValid(true);
234 },
235
236 /**
237 * Editing is complete; update the model.
238 * @param {string} newPattern The pattern that the user entered.
239 * @param {string} newSetting The setting the user chose.
240 */
241 finishEdit: function(newPattern, newSetting) {
242 this.patternLabel.textContent = newPattern;
243 this.settingLabel.textContent = this.settingForDisplay();
244 var oldPattern = this.pattern;
245 this.pattern = newPattern;
246 this.setting = newSetting;
247
248 // TODO(estade): this will need to be updated if geolocation/notifications
249 // become editable.
250 if (oldPattern != newPattern) {
251 chrome.send('removeException',
252 [this.contentType, this.mode, oldPattern]);
253 }
254
255 chrome.send('setException',
256 [this.contentType, this.mode, newPattern, newSetting]);
257 }
258 };
259
260 /**
261 * Creates a new list item for the Add New Item row, which doesn't represent
262 * an actual entry in the exceptions list but allows the user to add new
263 * exceptions.
264 * @param {string} contentType The type of the list.
265 * @param {string} mode The browser mode, 'otr' or 'normal'.
266 * @param {boolean} enableAskOption Whether to show an 'ask every time'
267 * option in the select.
268 * @constructor
269 * @extends {cr.ui.ExceptionsListItem}
270 */
271 function ExceptionsAddRowListItem(contentType, mode, enableAskOption) {
272 var el = cr.doc.createElement('div');
273 el.mode = mode;
274 el.contentType = contentType;
275 el.enableAskOption = enableAskOption;
276 el.dataItem = [];
277 el.__proto__ = ExceptionsAddRowListItem.prototype;
278 el.decorate();
279
280 return el;
281 }
282
283 ExceptionsAddRowListItem.prototype = {
284 __proto__: ExceptionsListItem.prototype,
285
286 decorate: function() {
287 ExceptionsListItem.prototype.decorate.call(this);
288
289 this.input.placeholder = templateData.addNewExceptionInstructions;
290
291 // Do we always want a default of allow?
292 this.setting = 'allow';
293 },
294
295 /**
296 * Clear the <input> and let the placeholder text show again.
297 */
298 resetInput: function() {
299 this.input.value = '';
300 },
301
302 /** @inheritDoc */
303 get hasBeenEdited() {
304 return this.input.value != '';
305 },
306
307 /**
308 * Editing is complete; update the model. As long as the pattern isn't
309 * empty, we'll just add it.
310 * @param {string} newPattern The pattern that the user entered.
311 * @param {string} newSetting The setting the user chose.
312 */
313 finishEdit: function(newPattern, newSetting) {
314 chrome.send('setException',
315 [this.contentType, this.mode, newPattern, newSetting]);
316 },
317 };
318
319 /**
320 * Creates a new exceptions list.
321 * @constructor
322 * @extends {cr.ui.List}
323 */
324 var ExceptionsList = cr.ui.define('list');
325
326 ExceptionsList.prototype = {
327 __proto__: InlineEditableItemList.prototype,
328
329 /**
330 * Called when an element is decorated as a list.
331 */
332 decorate: function() {
333 InlineEditableItemList.prototype.decorate.call(this);
334
335 this.classList.add('settings-list');
336
337 for (var parentNode = this.parentNode; parentNode;
338 parentNode = parentNode.parentNode) {
339 if (parentNode.hasAttribute('contentType')) {
340 this.contentType = parentNode.getAttribute('contentType');
341 break;
342 }
343 }
344
345 this.mode = this.getAttribute('mode');
346
347 var exceptionList = this;
348 function handleBlur(e) {
349 // When the blur event happens we do not know who is getting focus so we
350 // delay this a bit until we know if the new focus node is outside the
351 // list.
352 var doc = e.target.ownerDocument;
353 window.setTimeout(function() {
354 var activeElement = doc.activeElement;
355 if (!exceptionList.contains(activeElement))
356 exceptionList.selectionModel.unselectAll();
357 }, 50);
358 }
359
360 this.addEventListener('blur', handleBlur, true);
361
362 // Whether the exceptions in this list allow an 'Ask every time' option.
363 this.enableAskOption = (this.contentType == 'plugins' &&
364 templateData.enable_click_to_play);
365
366 this.autoExpands = true;
367 this.reset();
368 },
369
370 /**
371 * Creates an item to go in the list.
372 * @param {Object} entry The element from the data model for this row.
373 */
374 createItem: function(entry) {
375 if (entry) {
376 return new ExceptionsListItem(this.contentType,
377 this.mode,
378 this.enableAskOption,
379 entry);
380 } else {
381 var addRowItem = new ExceptionsAddRowListItem(this.contentType,
382 this.mode,
383 this.enableAskOption);
384 addRowItem.deletable = false;
385 return addRowItem;
386 }
387 },
388
389 /**
390 * Sets the exceptions in the js model.
391 * @param {Object} entries A list of dictionaries of values, each dictionary
392 * represents an exception.
393 */
394 setExceptions: function(entries) {
395 var deleteCount = this.dataModel.length;
396
397 if (this.isEditable()) {
398 // We don't want to remove the Add New Exception row.
399 deleteCount = deleteCount - 1;
400 }
401
402 var args = [0, deleteCount];
403 args.push.apply(args, entries);
404 this.dataModel.splice.apply(this.dataModel, args);
405 },
406
407 /**
408 * The browser has finished checking a pattern for validity. Update the
409 * list item to reflect this.
410 * @param {string} pattern The pattern.
411 * @param {bool} valid Whether said pattern is valid in the context of
412 * a content exception setting.
413 */
414 patternValidityCheckComplete: function(pattern, valid) {
415 var listItems = this.items;
416 for (var i = 0; i < listItems.length; i++) {
417 var listItem = listItems[i];
418 // Don't do anything for messages for the item if it is not the intended
419 // recipient, or if the response is stale (i.e. the input value has
420 // changed since we sent the request to analyze it).
421 if (pattern == listItem.input.value)
422 listItem.setPatternValid(valid);
423 }
424 },
425
426 /**
427 * Returns whether the rows are editable in this list.
428 */
429 isEditable: function() {
430 // Editing notifications and geolocation is disabled for now.
431 return !(this.contentType == 'notifications' ||
432 this.contentType == 'location');
433 },
434
435 /**
436 * Removes all exceptions from the js model.
437 */
438 reset: function() {
439 if (this.isEditable()) {
440 // The null creates the Add New Exception row.
441 this.dataModel = new ArrayDataModel([null]);
442 } else {
443 this.dataModel = new ArrayDataModel([]);
444 }
445 },
446
447 /** @inheritDoc */
448 deleteItemAtIndex: function(index) {
449 var listItem = this.getListItemByIndex(index);
450 if (listItem.undeletable)
451 return;
452
453 var dataItem = listItem.dataItem;
454 var args = [listItem.contentType];
455 if (listItem.contentType == 'location')
456 args.push(dataItem['origin'], dataItem['embeddingOrigin']);
457 else if (listItem.contentType == 'notifications')
458 args.push(dataItem['origin'], dataItem['setting']);
459 else
460 args.push(listItem.mode, listItem.pattern);
461
462 chrome.send('removeException', args);
463 },
464 };
465
466 var OptionsPage = options.OptionsPage;
467
468 /**
469 * Encapsulated handling of content settings list subpage.
470 * @constructor
471 */
472 function ContentSettingsExceptionsArea() {
473 OptionsPage.call(this, 'contentExceptions',
474 templateData.contentSettingsPageTabTitle,
475 'content-settings-exceptions-area');
476 }
477
478 cr.addSingletonGetter(ContentSettingsExceptionsArea);
479
480 ContentSettingsExceptionsArea.prototype = {
481 __proto__: OptionsPage.prototype,
482
483 initializePage: function() {
484 OptionsPage.prototype.initializePage.call(this);
485
486 var exceptionsLists = this.pageDiv.querySelectorAll('list');
487 for (var i = 0; i < exceptionsLists.length; i++) {
488 options.contentSettings.ExceptionsList.decorate(exceptionsLists[i]);
489 }
490
491 ContentSettingsExceptionsArea.hideOTRLists();
492
493 // If the user types in the URL without a hash, show just cookies.
494 this.showList('cookies');
495 },
496
497 /**
498 * Shows one list and hides all others.
499 * @param {string} type The content type.
500 */
501 showList: function(type) {
502 var header = this.pageDiv.querySelector('h1');
503 header.textContent = templateData[type + '_header'];
504
505 var divs = this.pageDiv.querySelectorAll('div[contentType]');
506 for (var i = 0; i < divs.length; i++) {
507 if (divs[i].getAttribute('contentType') == type)
508 divs[i].classList.remove('hidden');
509 else
510 divs[i].classList.add('hidden');
511 }
512 },
513
514 /**
515 * Called after the page has been shown. Show the content type for the
516 * location's hash.
517 */
518 didShowPage: function() {
519 var hash = location.hash;
520 if (hash)
521 this.showList(hash.slice(1));
522 },
523 };
524
525 /**
526 * Called when the last incognito window is closed.
527 */
528 ContentSettingsExceptionsArea.OTRProfileDestroyed = function() {
529 this.hideOTRLists();
530 };
531
532 /**
533 * Clears and hides the incognito exceptions lists.
534 */
535 ContentSettingsExceptionsArea.hideOTRLists = function() {
536 var otrLists = document.querySelectorAll('list[mode=otr]');
537
538 for (var i = 0; i < otrLists.length; i++) {
539 otrLists[i].reset();
540 otrLists[i].parentNode.classList.add('hidden');
541 }
542 };
543
544 return {
545 ExceptionsListItem: ExceptionsListItem,
546 ExceptionsAddRowListItem: ExceptionsAddRowListItem,
547 ExceptionsList: ExceptionsList,
548 ContentSettingsExceptionsArea: ContentSettingsExceptionsArea,
549 };
550 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698