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

Side by Side Diff: chrome/browser/resources/settings/search_settings.js

Issue 2739323005: MD Settings: Allow search within settings to track multiple requests separately. (Closed)
Patch Set: Address comments. Created 3 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 cr.exportPath('settings');
6
7 /**
8 * A data structure used by callers to combine the results of multiple search
9 * requests.
10 *
11 * @typedef {{
12 * canceled: Boolean,
13 * didFindMatches: Boolean,
14 * wasClearSearch: Boolean,
15 * }}
16 */
17 settings.SearchResult;
18
5 cr.define('settings', function() { 19 cr.define('settings', function() {
6 /** @const {string} */ 20 /** @const {string} */
7 var WRAPPER_CSS_CLASS = 'search-highlight-wrapper'; 21 var WRAPPER_CSS_CLASS = 'search-highlight-wrapper';
8 22
9 /** @const {string} */ 23 /** @const {string} */
10 var ORIGINAL_CONTENT_CSS_CLASS = 'search-highlight-original-content'; 24 var ORIGINAL_CONTENT_CSS_CLASS = 'search-highlight-original-content';
11 25
12 /** @const {string} */ 26 /** @const {string} */
13 var HIT_CSS_CLASS = 'search-highlight-hit'; 27 var HIT_CSS_CLASS = 'search-highlight-hit';
14 28
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 * 132 *
119 * @param {!settings.SearchRequest} request 133 * @param {!settings.SearchRequest} request
120 * @param {!Node} root The root of the sub-tree to be searched 134 * @param {!Node} root The root of the sub-tree to be searched
121 * @private 135 * @private
122 */ 136 */
123 function findAndHighlightMatches_(request, root) { 137 function findAndHighlightMatches_(request, root) {
124 var foundMatches = false; 138 var foundMatches = false;
125 function doSearch(node) { 139 function doSearch(node) {
126 if (node.nodeName == 'TEMPLATE' && node.hasAttribute('route-path') && 140 if (node.nodeName == 'TEMPLATE' && node.hasAttribute('route-path') &&
127 !node.if && !node.hasAttribute(SKIP_SEARCH_CSS_ATTRIBUTE)) { 141 !node.if && !node.hasAttribute(SKIP_SEARCH_CSS_ATTRIBUTE)) {
128 getSearchManager().queue_.addRenderTask( 142 request.queue_.addRenderTask(new RenderTask(request, node));
129 new RenderTask(request, node));
130 return; 143 return;
131 } 144 }
132 145
133 if (IGNORED_ELEMENTS.has(node.nodeName)) 146 if (IGNORED_ELEMENTS.has(node.nodeName))
134 return; 147 return;
135 148
136 if (node instanceof HTMLElement) { 149 if (node instanceof HTMLElement) {
137 var element = /** @type {HTMLElement} */(node); 150 var element = /** @type {HTMLElement} */(node);
138 if (element.hasAttribute(SKIP_SEARCH_CSS_ATTRIBUTE) || 151 if (element.hasAttribute(SKIP_SEARCH_CSS_ATTRIBUTE) ||
139 element.hasAttribute('hidden') || 152 element.hasAttribute('hidden') ||
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 assert(!this.node.if); 310 assert(!this.node.if);
298 this.node.if = true; 311 this.node.if = true;
299 312
300 return new Promise(function(resolve, reject) { 313 return new Promise(function(resolve, reject) {
301 var parent = this.node.parentNode; 314 var parent = this.node.parentNode;
302 parent.async(function() { 315 parent.async(function() {
303 var renderedNode = 316 var renderedNode =
304 parent.querySelector('[route-path="' + routePath + '"]'); 317 parent.querySelector('[route-path="' + routePath + '"]');
305 // Register a SearchAndHighlightTask for the part of the DOM that was 318 // Register a SearchAndHighlightTask for the part of the DOM that was
306 // just rendered. 319 // just rendered.
307 getSearchManager().queue_.addSearchAndHighlightTask( 320 this.request.queue_.addSearchAndHighlightTask(
308 new SearchAndHighlightTask(this.request, assert(renderedNode))); 321 new SearchAndHighlightTask(this.request, assert(renderedNode)));
309 resolve(); 322 resolve();
310 }.bind(this)); 323 }.bind(this));
311 }.bind(this)); 324 }.bind(this));
312 }, 325 },
313 }; 326 };
314 327
315 /** 328 /**
316 * @constructor 329 * @constructor
317 * @extends {Task} 330 * @extends {Task}
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 setSectionsVisibility_: function(visible) { 378 setSectionsVisibility_: function(visible) {
366 var sections = this.node.querySelectorAll('settings-section'); 379 var sections = this.node.querySelectorAll('settings-section');
367 380
368 for (var i = 0; i < sections.length; i++) 381 for (var i = 0; i < sections.length; i++)
369 sections[i].hiddenBySearch = !visible; 382 sections[i].hiddenBySearch = !visible;
370 }, 383 },
371 }; 384 };
372 385
373 /** 386 /**
374 * @constructor 387 * @constructor
388 * @param {!settings.SearchRequest} request
375 */ 389 */
376 function TaskQueue() { 390 function TaskQueue(request) {
391 /** @private {!settings.SearchRequest} */
392 this.request_ = request;
393
377 /** 394 /**
378 * @private {{ 395 * @private {{
379 * high: !Array<!Task>, 396 * high: !Array<!Task>,
380 * middle: !Array<!Task>, 397 * middle: !Array<!Task>,
381 * low: !Array<!Task> 398 * low: !Array<!Task>
382 * }} 399 * }}
383 */ 400 */
384 this.queues_; 401 this.queues_;
385 this.reset(); 402 this.reset();
386 403
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 var task = this.popNextTask_(); 462 var task = this.popNextTask_();
446 if (!task) { 463 if (!task) {
447 this.running_ = false; 464 this.running_ = false;
448 if (this.onEmptyCallback_) 465 if (this.onEmptyCallback_)
449 this.onEmptyCallback_(); 466 this.onEmptyCallback_();
450 return; 467 return;
451 } 468 }
452 469
453 this.running_ = true; 470 this.running_ = true;
454 window.requestIdleCallback(function() { 471 window.requestIdleCallback(function() {
455 function startNextTask() { 472 if (!this.request_.canceled) {
456 this.running_ = false; 473 task.exec().then(function() {
457 this.consumePending_(); 474 this.running_ = false;
475 this.consumePending_();
476 }.bind(this));
458 } 477 }
459 if (task.request.id == 478 // Nothing to do otherwise. Since the request corresponding to this
460 getSearchManager().activeRequest_.id) { 479 // queue was canceled, the queue is disposed along with the request.
461 task.exec().then(startNextTask.bind(this));
462 } else {
463 // Dropping this task without ever executing it, since a new search
464 // has been issued since this task was queued.
465 startNextTask.call(this);
466 }
467 }.bind(this)); 480 }.bind(this));
468 return; 481 return;
469 } 482 }
470 }, 483 },
471 }; 484 };
472 485
473 /** 486 /**
474 * @constructor 487 * @constructor
488 *
489 * @param {string} rawQuery
490 * @param {!HTMLElement} root
475 */ 491 */
476 var SearchRequest = function(rawQuery) { 492 var SearchRequest = function(rawQuery, root) {
477 /** @type {number} */
478 this.id = SearchRequest.nextId_++;
479
480 /** @private {string} */ 493 /** @private {string} */
481 this.rawQuery_ = rawQuery; 494 this.rawQuery_ = rawQuery;
482 495
496 /** @private {!HTMLElement} */
497 this.root_ = root;
498
483 /** @type {?RegExp} */ 499 /** @type {?RegExp} */
484 this.regExp = this.generateRegExp_(); 500 this.regExp = this.generateRegExp_();
485 501
486 /** 502 /**
487 * Whether this request was fully processed. 503 * Whether this request was canceled before completing.
488 * @type {boolean} 504 * @type {boolean}
489 */ 505 */
490 this.finished = false; 506 this.canceled = false;
491 507
492 /** @private {boolean} */ 508 /** @private {boolean} */
493 this.foundMatches_ = false; 509 this.foundMatches_ = false;
494 510
495 /** @type {!PromiseResolver} */ 511 /** @type {!PromiseResolver} */
496 this.resolver = new PromiseResolver(); 512 this.resolver = new PromiseResolver();
513
514 /** @private {!TaskQueue} */
515 this.queue_ = new TaskQueue(this);
516 this.queue_.onEmpty(function() {
517 this.resolver.resolve(this);
518 }.bind(this));
497 }; 519 };
498 520
499 /** @private {number} */
500 SearchRequest.nextId_ = 0;
501
502 /** @private {!RegExp} */ 521 /** @private {!RegExp} */
503 SearchRequest.SANITIZE_REGEX_ = /[-[\]{}()*+?.,\\^$|#\s]/g; 522 SearchRequest.SANITIZE_REGEX_ = /[-[\]{}()*+?.,\\^$|#\s]/g;
504 523
505 SearchRequest.prototype = { 524 SearchRequest.prototype = {
506 /** 525 /**
526 * Fires this search request.
527 */
528 start: function() {
529 this.queue_.addTopLevelSearchTask(
530 new TopLevelSearchTask(this, this.root_));
531 },
532
533 /**
507 * @return {?RegExp} 534 * @return {?RegExp}
508 * @private 535 * @private
509 */ 536 */
510 generateRegExp_: function() { 537 generateRegExp_: function() {
511 var regExp = null; 538 var regExp = null;
512 539
513 // Generate search text by escaping any characters that would be 540 // Generate search text by escaping any characters that would be
514 // problematic for regular expressions. 541 // problematic for regular expressions.
515 var searchText = this.rawQuery_.trim().replace( 542 var searchText = this.rawQuery_.trim().replace(
516 SearchRequest.SANITIZE_REGEX_, '\\$&'); 543 SearchRequest.SANITIZE_REGEX_, '\\$&');
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 * searching finished. 581 * searching finished.
555 */ 582 */
556 search: function(text, page) {} 583 search: function(text, page) {}
557 }; 584 };
558 585
559 /** 586 /**
560 * @constructor 587 * @constructor
561 * @implements {SearchManager} 588 * @implements {SearchManager}
562 */ 589 */
563 var SearchManagerImpl = function() { 590 var SearchManagerImpl = function() {
564 /** @private {?settings.SearchRequest} */ 591 /** @private {!Set<!settings.SearchRequest>} */
565 this.activeRequest_ = null; 592 this.activeRequests_ = new Set();
566 593
567 /** @private {!TaskQueue} */ 594 /** @private {?string} */
568 this.queue_ = new TaskQueue(); 595 this.lastSearchedText_ = null;
569 this.queue_.onEmpty(function() {
570 this.activeRequest_.finished = true;
571 this.activeRequest_.resolver.resolve(this.activeRequest_);
572 this.activeRequest_ = null;
573 }.bind(this));
574 }; 596 };
575 cr.addSingletonGetter(SearchManagerImpl); 597 cr.addSingletonGetter(SearchManagerImpl);
576 598
577 SearchManagerImpl.prototype = { 599 SearchManagerImpl.prototype = {
578 /** @override */ 600 /** @override */
579 search: function(text, page) { 601 search: function(text, page) {
580 // Creating a new request only if the |text| changed. 602 // Cancel any pending requests if a request with different text is
581 if (!this.activeRequest_ || !this.activeRequest_.isSame(text)) { 603 // submitted.
582 // Resolving previous search request without marking it as 604 if (text != this.lastSearchedText_) {
583 // 'finished', if any, and dropping all pending tasks. 605 this.activeRequests_.forEach(function(request) {
584 this.queue_.reset(); 606 request.canceled = true;
585 if (this.activeRequest_) 607 request.resolver.resolve(request);
586 this.activeRequest_.resolver.resolve(this.activeRequest_); 608 });
587 609 this.activeRequests_.clear();
588 this.activeRequest_ = new SearchRequest(text);
589 } 610 }
590 611
591 this.queue_.addTopLevelSearchTask( 612 this.lastSearchedText_ = text;
592 new TopLevelSearchTask(this.activeRequest_, page)); 613 var request = new SearchRequest(text, page);
593 614 this.activeRequests_.add(request);
594 return this.activeRequest_.resolver.promise; 615 request.start();
616 return request.resolver.promise.then(function() {
617 // Stop tracking requests that finished.
618 this.activeRequests_.delete(request);
619 return request;
620 }.bind(this));
595 }, 621 },
596 }; 622 };
597 623
598 /** @return {!SearchManager} */ 624 /** @return {!SearchManager} */
599 function getSearchManager() { 625 function getSearchManager() {
600 return SearchManagerImpl.getInstance(); 626 return SearchManagerImpl.getInstance();
601 } 627 }
602 628
603 /** 629 /**
604 * Sets the SearchManager singleton instance, useful for testing. 630 * Sets the SearchManager singleton instance, useful for testing.
605 * @param {!SearchManager} searchManager 631 * @param {!SearchManager} searchManager
606 */ 632 */
607 function setSearchManagerForTesting(searchManager) { 633 function setSearchManagerForTesting(searchManager) {
608 SearchManagerImpl.instance_ = searchManager; 634 SearchManagerImpl.instance_ = searchManager;
609 } 635 }
610 636
611 return { 637 return {
612 getSearchManager: getSearchManager, 638 getSearchManager: getSearchManager,
613 setSearchManagerForTesting: setSearchManagerForTesting, 639 setSearchManagerForTesting: setSearchManagerForTesting,
614 SearchRequest: SearchRequest, 640 SearchRequest: SearchRequest,
615 }; 641 };
616 }); 642 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698