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

Side by Side Diff: chrome/browser/resources/file_manager/js/navigation_list.js

Issue 22382002: Rename VolumeList -> NavigationList in Files.app (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressed comment Created 7 years, 4 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 'use strict'; 5 'use strict';
6 6
7 /** 7 /**
8 * A volume list model. This model combines the 2 lists. 8 * A navigation list model. This model combines the 2 lists.
9 * @param {cr.ui.ArrayDataModel} volumesList The first list of the model. 9 * @param {cr.ui.ArrayDataModel} volumesList The first list of the model.
10 * @param {cr.ui.ArrayDataModel} pinnedList The second list of the model. 10 * @param {cr.ui.ArrayDataModel} pinnedList The second list of the model.
11 * @constructor 11 * @constructor
12 * @extends {cr.EventTarget} 12 * @extends {cr.EventTarget}
13 */ 13 */
14 function VolumeListModel(volumesList, pinnedList) { 14 function NavigationListModel(volumesList, pinnedList) {
15 this.volumesList_ = volumesList; 15 this.volumesList_ = volumesList;
16 this.pinnedList_ = pinnedList; 16 this.pinnedList_ = pinnedList;
17 17
18 // Generates a combined 'permuted' event from an event of either list. 18 // Generates a combined 'permuted' event from an event of either list.
19 var permutedHandler = function(listNum, e) { 19 var permutedHandler = function(listNum, e) {
20 var permutedEvent = new Event('permuted'); 20 var permutedEvent = new Event('permuted');
21 var newPermutation = []; 21 var newPermutation = [];
22 var newLength; 22 var newLength;
23 if (listNum == 1) { 23 if (listNum == 1) {
24 newLength = e.newLength + this.pinnedList_.length; 24 newLength = e.newLength + this.pinnedList_.length;
(...skipping 30 matching lines...) Expand all
55 this.dispatchEvent(changeEvent); 55 this.dispatchEvent(changeEvent);
56 }; 56 };
57 this.volumesList_.addEventListener('change', changeHandler.bind(this, 1)); 57 this.volumesList_.addEventListener('change', changeHandler.bind(this, 1));
58 this.pinnedList_.addEventListener('change', changeHandler.bind(this, 2)); 58 this.pinnedList_.addEventListener('change', changeHandler.bind(this, 2));
59 59
60 // 'splice' and 'sorted' events are not implemented, since they are not used 60 // 'splice' and 'sorted' events are not implemented, since they are not used
61 // in list.js. 61 // in list.js.
62 } 62 }
63 63
64 /** 64 /**
65 * VolumeList inherits cr.EventTarget. 65 * NavigationList inherits cr.EventTarget.
66 */ 66 */
67 VolumeListModel.prototype = { 67 NavigationListModel.prototype = {
68 __proto__: cr.EventTarget.prototype, 68 __proto__: cr.EventTarget.prototype,
69 get length() { return this.length_(); } 69 get length() { return this.length_(); }
70 }; 70 };
71 71
72 /** 72 /**
73 * Returns the item at the given index. 73 * Returns the item at the given index.
74 * @param {number} index The index of the entry to get. 74 * @param {number} index The index of the entry to get.
75 * @return {?string} The path at the given index. 75 * @return {?string} The path at the given index.
76 */ 76 */
77 VolumeListModel.prototype.item = function(index) { 77 NavigationListModel.prototype.item = function(index) {
78 var offset = this.volumesList_.length; 78 var offset = this.volumesList_.length;
79 if (index < offset) { 79 if (index < offset) {
80 var entry = this.volumesList_.item(index); 80 var entry = this.volumesList_.item(index);
81 return entry ? entry.fullPath : undefined; 81 return entry ? entry.fullPath : undefined;
82 } else { 82 } else {
83 return this.pinnedList_.item(index - offset); 83 return this.pinnedList_.item(index - offset);
84 } 84 }
85 }; 85 };
86 86
87 /** 87 /**
88 * Type of the item on the volume list. 88 * Type of the item on the navigation list.
89 * @enum {number} 89 * @enum {number}
90 */ 90 */
91 VolumeListModel.ItemType = { 91 NavigationListModel.ItemType = {
92 ROOT: 1, 92 ROOT: 1,
93 PINNED: 2 93 PINNED: 2
94 }; 94 };
95 95
96 /** 96 /**
97 * Returns the type of the item at the given index. 97 * Returns the type of the item at the given index.
98 * @param {number} index The index of the entry to get. 98 * @param {number} index The index of the entry to get.
99 * @return {VolumeListModel.ItemType} The type of the item. 99 * @return {NavigationListModel.ItemType} The type of the item.
100 */ 100 */
101 VolumeListModel.prototype.getItemType = function(index) { 101 NavigationListModel.prototype.getItemType = function(index) {
102 var offset = this.volumesList_.length; 102 var offset = this.volumesList_.length;
103 return index < offset ? 103 return index < offset ?
104 VolumeListModel.ItemType.ROOT : VolumeListModel.ItemType.PINNED; 104 NavigationListModel.ItemType.ROOT : NavigationListModel.ItemType.PINNED;
105 }; 105 };
106 106
107 /** 107 /**
108 * Returns the number of items in the model. 108 * Returns the number of items in the model.
109 * @return {number} The length of the model. 109 * @return {number} The length of the model.
110 * @private 110 * @private
111 */ 111 */
112 VolumeListModel.prototype.length_ = function() { 112 NavigationListModel.prototype.length_ = function() {
113 return this.volumesList_.length + this.pinnedList_.length; 113 return this.volumesList_.length + this.pinnedList_.length;
114 }; 114 };
115 115
116 /** 116 /**
117 * Returns the first matching item. 117 * Returns the first matching item.
118 * @param {Entry} item The entry to find. 118 * @param {Entry} item The entry to find.
119 * @param {number=} opt_fromIndex If provided, then the searching start at 119 * @param {number=} opt_fromIndex If provided, then the searching start at
120 * the {@code opt_fromIndex}. 120 * the {@code opt_fromIndex}.
121 * @return {number} The index of the first found element or -1 if not found. 121 * @return {number} The index of the first found element or -1 if not found.
122 */ 122 */
123 VolumeListModel.prototype.indexOf = function(item, opt_fromIndex) { 123 NavigationListModel.prototype.indexOf = function(item, opt_fromIndex) {
124 for (var i = opt_fromIndex || 0; i < this.length; i++) { 124 for (var i = opt_fromIndex || 0; i < this.length; i++) {
125 if (item === this.item(i)) 125 if (item === this.item(i))
126 return i; 126 return i;
127 } 127 }
128 return -1; 128 return -1;
129 }; 129 };
130 130
131 /** 131 /**
132 * A volume item. 132 * A navigation list item.
133 * @constructor 133 * @constructor
134 * @extends {HTMLLIElement} 134 * @extends {HTMLLIElement}
135 */ 135 */
136 var VolumeItem = cr.ui.define('li'); 136 var NavigationListItem = cr.ui.define('li');
137 137
138 VolumeItem.prototype = { 138 NavigationListItem.prototype = {
139 __proto__: HTMLLIElement.prototype, 139 __proto__: HTMLLIElement.prototype,
140 }; 140 };
141 141
142 /** 142 /**
143 * Decorate the item. 143 * Decorate the item.
144 */ 144 */
145 VolumeItem.prototype.decorate = function() { 145 NavigationListItem.prototype.decorate = function() {
146 // decorate() may be called twice: from the constructor and from 146 // decorate() may be called twice: from the constructor and from
147 // List.createItem(). This check prevents double-decorating. 147 // List.createItem(). This check prevents double-decorating.
148 if (this.className) 148 if (this.className)
149 return; 149 return;
150 150
151 this.className = 'root-item'; 151 this.className = 'root-item';
152 this.setAttribute('role', 'option'); 152 this.setAttribute('role', 'option');
153 153
154 this.iconDiv_ = cr.doc.createElement('div'); 154 this.iconDiv_ = cr.doc.createElement('div');
155 this.iconDiv_.className = 'volume-icon'; 155 this.iconDiv_.className = 'volume-icon';
156 this.appendChild(this.iconDiv_); 156 this.appendChild(this.iconDiv_);
157 157
158 this.label_ = cr.doc.createElement('div'); 158 this.label_ = cr.doc.createElement('div');
159 this.label_.className = 'root-label'; 159 this.label_.className = 'root-label';
160 this.appendChild(this.label_); 160 this.appendChild(this.label_);
161 161
162 cr.defineProperty(this, 'lead', cr.PropertyKind.BOOL_ATTR); 162 cr.defineProperty(this, 'lead', cr.PropertyKind.BOOL_ATTR);
163 cr.defineProperty(this, 'selected', cr.PropertyKind.BOOL_ATTR); 163 cr.defineProperty(this, 'selected', cr.PropertyKind.BOOL_ATTR);
164 }; 164 };
165 165
166 /** 166 /**
167 * Associate a path with this item. 167 * Associate a path with this item.
168 * @param {string} path Path of this item. 168 * @param {string} path Path of this item.
169 */ 169 */
170 VolumeItem.prototype.setPath = function(path) { 170 NavigationListItem.prototype.setPath = function(path) {
171 if (this.path_) 171 if (this.path_)
172 console.warn('VolumeItem.setPath should be called only once.'); 172 console.warn('NavigationListItem.setPath should be called only once.');
173 173
174 this.path_ = path; 174 this.path_ = path;
175 175
176 var rootType = PathUtil.getRootType(path); 176 var rootType = PathUtil.getRootType(path);
177 177
178 this.iconDiv_.setAttribute('volume-type-icon', rootType); 178 this.iconDiv_.setAttribute('volume-type-icon', rootType);
179 if (rootType === RootType.REMOVABLE) { 179 if (rootType === RootType.REMOVABLE) {
180 this.iconDiv_.setAttribute('volume-subtype', 180 this.iconDiv_.setAttribute('volume-subtype',
181 VolumeManager.getInstance().getDeviceType(path)); 181 VolumeManager.getInstance().getDeviceType(path));
182 } 182 }
(...skipping 15 matching lines...) Expand all
198 }.bind(this)); 198 }.bind(this));
199 199
200 this.appendChild(this.eject_); 200 this.appendChild(this.eject_);
201 } 201 }
202 }; 202 };
203 203
204 /** 204 /**
205 * Associate a context menu with this item. 205 * Associate a context menu with this item.
206 * @param {cr.ui.Menu} menu Menu this item. 206 * @param {cr.ui.Menu} menu Menu this item.
207 */ 207 */
208 VolumeItem.prototype.maybeSetContextMenu = function(menu) { 208 NavigationListItem.prototype.maybeSetContextMenu = function(menu) {
209 if (!this.path_) { 209 if (!this.path_) {
210 console.error( 210 console.error('NavigationListItem.maybeSetContextMenu must be called ' +
211 'VolumeItem.maybeSetContextMenu must be called after setPath().'); 211 'after setPath().');
212 return; 212 return;
213 } 213 }
214 214
215 var isRoot = PathUtil.isRootPath(this.path_); 215 var isRoot = PathUtil.isRootPath(this.path_);
216 var rootType = PathUtil.getRootType(this.path_); 216 var rootType = PathUtil.getRootType(this.path_);
217 // The context menu is shown on the following items: 217 // The context menu is shown on the following items:
218 // - Removable and Archive volumes 218 // - Removable and Archive volumes
219 // - Folder shortcuts 219 // - Folder shortcuts
220 if (!isRoot || 220 if (!isRoot ||
221 (rootType != RootType.DRIVE && rootType != RootType.DOWNLOADS)) 221 (rootType != RootType.DRIVE && rootType != RootType.DOWNLOADS))
222 cr.ui.contextMenuHandler.setContextMenu(this, menu); 222 cr.ui.contextMenuHandler.setContextMenu(this, menu);
223 }; 223 };
224 224
225 /** 225 /**
226 * A volume list. 226 * A navigation list.
227 * @constructor 227 * @constructor
228 * @extends {cr.ui.List} 228 * @extends {cr.ui.List}
229 */ 229 */
230 function VolumeList() { 230 function NavigationList() {
231 } 231 }
232 232
233 /** 233 /**
234 * VolumeList inherits cr.ui.List. 234 * NavigationList inherits cr.ui.List.
235 */ 235 */
236 VolumeList.prototype.__proto__ = cr.ui.List.prototype; 236 NavigationList.prototype.__proto__ = cr.ui.List.prototype;
237 237
238 /** 238 /**
239 * @param {HTMLElement} el Element to be DirectoryItem. 239 * @param {HTMLElement} el Element to be DirectoryItem.
240 * @param {DirectoryModel} directoryModel Current DirectoryModel. 240 * @param {DirectoryModel} directoryModel Current DirectoryModel.
241 * @param {cr.ui.ArrayDataModel} pinnedFolderModel Current model of the pinned 241 * @param {cr.ui.ArrayDataModel} pinnedFolderModel Current model of the pinned
242 * folders. 242 * folders.
243 */ 243 */
244 VolumeList.decorate = function(el, directoryModel, pinnedFolderModel) { 244 NavigationList.decorate = function(el, directoryModel, pinnedFolderModel) {
245 el.__proto__ = VolumeList.prototype; 245 el.__proto__ = NavigationList.prototype;
246 el.decorate(directoryModel, pinnedFolderModel); 246 el.decorate(directoryModel, pinnedFolderModel);
247 }; 247 };
248 248
249 /** 249 /**
250 * @param {DirectoryModel} directoryModel Current DirectoryModel. 250 * @param {DirectoryModel} directoryModel Current DirectoryModel.
251 * @param {cr.ui.ArrayDataModel} pinnedFolderModel Current model of the pinned 251 * @param {cr.ui.ArrayDataModel} pinnedFolderModel Current model of the pinned
252 * folders. 252 * folders.
253 */ 253 */
254 VolumeList.prototype.decorate = function(directoryModel, pinnedFolderModel) { 254 NavigationList.prototype.decorate =
255 function(directoryModel, pinnedFolderModel) {
255 cr.ui.List.decorate(this); 256 cr.ui.List.decorate(this);
256 this.__proto__ = VolumeList.prototype; 257 this.__proto__ = NavigationList.prototype;
257 258
258 this.directoryModel_ = directoryModel; 259 this.directoryModel_ = directoryModel;
259 this.volumeManager_ = VolumeManager.getInstance(); 260 this.volumeManager_ = VolumeManager.getInstance();
260 this.selectionModel = new cr.ui.ListSingleSelectionModel(); 261 this.selectionModel = new cr.ui.ListSingleSelectionModel();
261 262
262 this.directoryModel_.addEventListener('directory-changed', 263 this.directoryModel_.addEventListener('directory-changed',
263 this.onCurrentDirectoryChanged_.bind(this)); 264 this.onCurrentDirectoryChanged_.bind(this));
264 this.selectionModel.addEventListener( 265 this.selectionModel.addEventListener(
265 'change', this.onSelectionChange_.bind(this)); 266 'change', this.onSelectionChange_.bind(this));
266 this.selectionModel.addEventListener( 267 this.selectionModel.addEventListener(
267 'beforeChange', this.onBeforeSelectionChange_.bind(this)); 268 'beforeChange', this.onBeforeSelectionChange_.bind(this));
268 269
269 this.scrollBar_ = new ScrollBar(); 270 this.scrollBar_ = new ScrollBar();
270 this.scrollBar_.initialize(this.parentNode, this); 271 this.scrollBar_.initialize(this.parentNode, this);
271 272
272 // Overriding default role 'list' set by cr.ui.List.decorate() to 'listbox' 273 // Overriding default role 'list' set by cr.ui.List.decorate() to 'listbox'
273 // role for better accessibility on ChromeOS. 274 // role for better accessibility on ChromeOS.
274 this.setAttribute('role', 'listbox'); 275 this.setAttribute('role', 'listbox');
275 276
276 var self = this; 277 var self = this;
277 this.itemConstructor = function(path) { 278 this.itemConstructor = function(path) {
278 return self.renderRoot_(path); 279 return self.renderRoot_(path);
279 }; 280 };
280 281
281 this.pinnedItemList_ = pinnedFolderModel; 282 this.pinnedItemList_ = pinnedFolderModel;
282 283
283 this.dataModel = 284 this.dataModel =
284 new VolumeListModel(this.directoryModel_.getRootsList(), 285 new NavigationListModel(this.directoryModel_.getRootsList(),
285 this.pinnedItemList_); 286 this.pinnedItemList_);
286 }; 287 };
287 288
288 /** 289 /**
289 * Creates an element of a volume. This method is called from cr.ui.List 290 * Creates an element of a navigation list. This method is called from
290 * internally. 291 * cr.ui.List internally.
291 * @param {string} path Path of the directory to be rendered. 292 * @param {string} path Path of the directory to be rendered.
292 * @return {VolumeItem} Rendered element. 293 * @return {NavigationListItem} Rendered element.
293 * @private 294 * @private
294 */ 295 */
295 VolumeList.prototype.renderRoot_ = function(path) { 296 NavigationList.prototype.renderRoot_ = function(path) {
296 var item = new VolumeItem(); 297 var item = new NavigationListItem();
297 item.setPath(path); 298 item.setPath(path);
298 299
299 var handleClick = function() { 300 var handleClick = function() {
300 if (item.selected && path !== this.directoryModel_.getCurrentDirPath()) { 301 if (item.selected && path !== this.directoryModel_.getCurrentDirPath()) {
301 this.directoryModel_.changeDirectory(path); 302 this.directoryModel_.changeDirectory(path);
302 } 303 }
303 }.bind(this); 304 }.bind(this);
304 item.addEventListener('click', handleClick); 305 item.addEventListener('click', handleClick);
305 306
306 var handleEject = function() { 307 var handleEject = function() {
(...skipping 19 matching lines...) Expand all
326 } 327 }
327 return item; 328 return item;
328 }; 329 };
329 330
330 /** 331 /**
331 * Sets a context menu. Context menu is enabled only on archive and removable 332 * Sets a context menu. Context menu is enabled only on archive and removable
332 * volumes as of now. 333 * volumes as of now.
333 * 334 *
334 * @param {cr.ui.Menu} menu Context menu. 335 * @param {cr.ui.Menu} menu Context menu.
335 */ 336 */
336 VolumeList.prototype.setContextMenu = function(menu) { 337 NavigationList.prototype.setContextMenu = function(menu) {
337 this.contextMenu_ = menu; 338 this.contextMenu_ = menu;
338 339
339 for (var i = 0; i < this.dataModel.length; i++) { 340 for (var i = 0; i < this.dataModel.length; i++) {
340 this.getListItemByIndex(i).maybeSetContextMenu(this.contextMenu_); 341 this.getListItemByIndex(i).maybeSetContextMenu(this.contextMenu_);
341 } 342 }
342 }; 343 };
343 344
344 /** 345 /**
345 * Selects the n-th volume from the list. 346 * Selects the n-th item from the list.
346 * @param {number} index Volume index. 347 * @param {number} index Item index.
347 * @return {boolean} True for success, otherwise false. 348 * @return {boolean} True for success, otherwise false.
348 */ 349 */
349 VolumeList.prototype.selectByIndex = function(index) { 350 NavigationList.prototype.selectByIndex = function(index) {
350 if (index < 0 || index > this.dataModel.length - 1) 351 if (index < 0 || index > this.dataModel.length - 1)
351 return false; 352 return false;
352 353
353 var newPath = this.dataModel.item(index); 354 var newPath = this.dataModel.item(index);
354 if (!newPath) 355 if (!newPath)
355 return false; 356 return false;
356 357
357 // Prevents double-moving to the current directory. 358 // Prevents double-moving to the current directory.
358 if (this.directoryModel_.getCurrentDirEntry().fullPath == newPath) 359 if (this.directoryModel_.getCurrentDirEntry().fullPath == newPath)
359 return false; 360 return false;
360 361
361 this.directoryModel_.changeDirectory(newPath); 362 this.directoryModel_.changeDirectory(newPath);
362 return true; 363 return true;
363 }; 364 };
364 365
365 /** 366 /**
366 * Handler before root item change. 367 * Handler before root item change.
367 * @param {Event} event The event. 368 * @param {Event} event The event.
368 * @private 369 * @private
369 */ 370 */
370 VolumeList.prototype.onBeforeSelectionChange_ = function(event) { 371 NavigationList.prototype.onBeforeSelectionChange_ = function(event) {
371 if (event.changes.length == 1 && !event.changes[0].selected) 372 if (event.changes.length == 1 && !event.changes[0].selected)
372 event.preventDefault(); 373 event.preventDefault();
373 }; 374 };
374 375
375 /** 376 /**
376 * Handler for root item being clicked. 377 * Handler for root item being clicked.
377 * @param {Event} event The event. 378 * @param {Event} event The event.
378 * @private 379 * @private
379 */ 380 */
380 VolumeList.prototype.onSelectionChange_ = function(event) { 381 NavigationList.prototype.onSelectionChange_ = function(event) {
381 // This handler is invoked even when the volume list itself changes the 382 // This handler is invoked even when the navigation list itself changes the
382 // selection. In such case, we shouldn't handle the event. 383 // selection. In such case, we shouldn't handle the event.
383 if (this.dontHandleSelectionEvent_) 384 if (this.dontHandleSelectionEvent_)
384 return; 385 return;
385 386
386 this.selectByIndex(this.selectionModel.selectedIndex); 387 this.selectByIndex(this.selectionModel.selectedIndex);
387 }; 388 };
388 389
389 /** 390 /**
390 * Invoked when the current directory is changed. 391 * Invoked when the current directory is changed.
391 * @param {Event} event The event. 392 * @param {Event} event The event.
392 * @private 393 * @private
393 */ 394 */
394 VolumeList.prototype.onCurrentDirectoryChanged_ = function(event) { 395 NavigationList.prototype.onCurrentDirectoryChanged_ = function(event) {
395 var path = event.newDirEntry.fullPath || this.dataModel.getCurrentDirPath(); 396 var path = event.newDirEntry.fullPath || this.dataModel.getCurrentDirPath();
396 var newRootPath = PathUtil.getRootPath(path); 397 var newRootPath = PathUtil.getRootPath(path);
397 398
398 // Synchronizes the volume list selection with the current directory, after 399 // Synchronizes the navigation list selection with the current directory,
399 // it is changed outside of the volume list. 400 // after it is changed outside of the navigation list.
400 401
401 // (1) Select the nearest parent directory (including the pinned directories). 402 // (1) Select the nearest parent directory (including the pinned directories).
402 var bestMatchIndex = -1; 403 var bestMatchIndex = -1;
403 var bestMatchSubStringLen = 0; 404 var bestMatchSubStringLen = 0;
404 for (var i = 0; i < this.dataModel.length; i++) { 405 for (var i = 0; i < this.dataModel.length; i++) {
405 var itemPath = this.dataModel.item(i); 406 var itemPath = this.dataModel.item(i);
406 if (path.indexOf(itemPath) == 0) { 407 if (path.indexOf(itemPath) == 0) {
407 if (bestMatchSubStringLen < itemPath.length) { 408 if (bestMatchSubStringLen < itemPath.length) {
408 bestMatchIndex = i; 409 bestMatchIndex = i;
409 bestMatchSubStringLen = itemPath.length; 410 bestMatchSubStringLen = itemPath.length;
(...skipping 13 matching lines...) Expand all
423 var itemPath = this.dataModel.item(i); 424 var itemPath = this.dataModel.item(i);
424 if (PathUtil.getRootPath(itemPath) == newRootPath) { 425 if (PathUtil.getRootPath(itemPath) == newRootPath) {
425 // Not to invoke the handler of this instance, sets the guard. 426 // Not to invoke the handler of this instance, sets the guard.
426 this.dontHandleSelectionEvent_ = true; 427 this.dontHandleSelectionEvent_ = true;
427 this.selectionModel.selectedIndex = i; 428 this.selectionModel.selectedIndex = i;
428 this.dontHandleSelectionEvent_ = false; 429 this.dontHandleSelectionEvent_ = false;
429 return; 430 return;
430 } 431 }
431 } 432 }
432 }; 433 };
OLDNEW
« no previous file with comments | « chrome/browser/resources/file_manager/js/main_scripts.js ('k') | chrome/browser/resources/file_manager/js/test_util.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698