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

Side by Side Diff: chrome/browser/resources/history.html

Issue 28308: Make history title inclusion safer (createTextNode changes).... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/dom_ui/history_ui.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 <!DOCTYPE HTML> 1 <!DOCTYPE HTML>
2 <html id="t"> 2 <html id="t">
3 <head> 3 <head>
4 <meta charset="utf-8"> 4 <meta charset="utf-8">
5 <title jscontent="title"></title> 5 <title jscontent="title"></title>
6 <script type="text/javascript"> 6 <script type="text/javascript">
7 /////////////////////////////////////////////////////////////////////////////// 7 ///////////////////////////////////////////////////////////////////////////////
8 // Globals: 8 // Globals:
9 var RESULTS_PER_PAGE = 150; 9 var RESULTS_PER_PAGE = 150;
10 var MAX_SEARCH_DEPTH_MONTHS = 18; 10 var MAX_SEARCH_DEPTH_MONTHS = 18;
11 11
12 // Amount of time between pageviews that we consider a 'break' in browsing, 12 // Amount of time between pageviews that we consider a 'break' in browsing,
13 // measured in milliseconds. 13 // measured in milliseconds.
14 var BROWSING_GAP_TIME = 15 * 60 * 1000; 14 var BROWSING_GAP_TIME = 15 * 60 * 1000;
15 15
16 function $(o) {return document.getElementById(o);} 16 function $(o) {return document.getElementById(o);}
17 17
18 function createElementWithClassName(type, className) {
19 var elm = document.createElement(type);
20 elm.className = className;
21 return elm;
22 }
23
18 // TODO(glen): Get rid of these global references, replace with a controller 24 // TODO(glen): Get rid of these global references, replace with a controller
19 // or just make the classes own more of the page. 25 // or just make the classes own more of the page.
20 var historyModel; 26 var historyModel;
21 var historyView; 27 var historyView;
22 var localStrings; 28 var localStrings;
23 var pageState; 29 var pageState;
30 var deleteQueue = [];
31 var deleteInFlight = false;
24 32
25 /////////////////////////////////////////////////////////////////////////////// 33 ///////////////////////////////////////////////////////////////////////////////
26 // localStrings: 34 // localStrings:
27 /** 35 /**
28 * We get strings into the page by using JSTemplate to populate some elements 36 * We get strings into the page by using JSTemplate to populate some elements
29 * with localized content, then reading the content of those elements into 37 * with localized content, then reading the content of those elements into
30 * this global strings object. 38 * this global strings object.
31 * @param {Node} node The DOM node containing all our strings. 39 * @param {Node} node The DOM node containing all our strings.
32 */ 40 */
33 function LocalStrings(node) { 41 function LocalStrings(node) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 /** 75 /**
68 * Class to hold all the information about an entry in our model. 76 * Class to hold all the information about an entry in our model.
69 * @param {Object} result An object containing the page's data. 77 * @param {Object} result An object containing the page's data.
70 * @param {boolean} continued Whether this page is on the same day as the 78 * @param {boolean} continued Whether this page is on the same day as the
71 * page before it 79 * page before it
72 */ 80 */
73 function Page(result, continued, model) { 81 function Page(result, continued, model) {
74 this.model_ = model; 82 this.model_ = model;
75 this.title_ = result.title; 83 this.title_ = result.title;
76 this.url_ = result.url; 84 this.url_ = result.url;
85 this.starred_ = result.starred;
77 this.snippet_ = result.snippet || ""; 86 this.snippet_ = result.snippet || "";
78 87
79 this.changed = false; 88 this.changed = false;
80 89
81 // All the date information is public so that owners can compare properties of 90 // All the date information is public so that owners can compare properties of
82 // two items easily. 91 // two items easily.
83 92
84 // We get the time in seconds, but we want it in milliseconds. 93 // We get the time in seconds, but we want it in milliseconds.
85 this.time = new Date(result.time * 1000); 94 this.time = new Date(result.time * 1000);
86 95
87 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always 96 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always
88 // get all of these. 97 // get all of these.
89 this.dateRelativeDay = result.dateRelativeDay || ""; 98 this.dateRelativeDay = result.dateRelativeDay || "";
90 this.dateTimeOfDay = result.dateTimeOfDay || ""; 99 this.dateTimeOfDay = result.dateTimeOfDay || "";
91 this.dateShort = result.dateShort || ""; 100 this.dateShort = result.dateShort || "";
92 101
93 // Whether this is the continuation of a previous day. 102 // Whether this is the continuation of a previous day.
94 this.continued = continued; 103 this.continued = continued;
95 } 104 }
96 105
97 // Page, Public: -------------------------------------------------------------- 106 // Page, Public: --------------------------------------------------------------
98 /** 107 /**
99 * @return {string} Gets the HTML representation of the page 108 * @return {DOMObject} Gets the DOM representation of the page
100 * for use in browse results. 109 * for use in browse results.
101 */ 110 */
102 Page.prototype.getBrowseResultHTML = function() { 111 Page.prototype.getBrowseResultDOM = function() {
103 return '<div class="entry">' + 112 var node = createElementWithClassName('div', 'entry');
104 '<div class="time">' + 113 var time = createElementWithClassName('div', 'time');
105 this.dateTimeOfDay + 114 time.appendChild(document.createTextNode(this.dateTimeOfDay));
106 '</div>' + 115 node.appendChild(time);
107 this.getTitleHTML_() + 116 node.appendChild(this.getTitleDOM_());
108 '</div>'; 117 return node;
109 } 118 }
110 119
111 /** 120 /**
112 * @return {string} Gets the HTML representation of the page for 121 * @return {DOMObject} Gets the DOM representation of the page for
113 * use in search results. 122 * use in search results.
114 */ 123 */
115 Page.prototype.getSearchResultHTML = function() { 124 Page.prototype.getSearchResultDOM = function() {
116 return '<tr class="entry"><td valign="top">' + 125 var row = createElementWithClassName('tr', 'entry');
117 '<div class="time">' + 126 var datecell = createElementWithClassName('td', 'time');
118 this.dateShort + 127 datecell.appendChild(document.createTextNode(this.dateShort));
119 '</div>' + 128 row.appendChild(datecell);
120 '</td><td valign="top">' + 129
121 this.getTitleHTML_() + 130 var titleCell = document.createElement('td');
122 '<div class="snippet">' + 131 titleCell.valign = 'top';
123 this.getHighlightedSnippet_() + 132 titleCell.appendChild(this.getTitleDOM_());
124 '</div>' + 133 var snippet = createElementWithClassName('div', 'snippet');
125 '</td></tr>'; 134 snippet.appendChild(document.createTextNode(this.snippet_));
135 this.highlightNodeContent_(snippet);
136 titleCell.appendChild(snippet);
137 row.appendChild(titleCell);
138
139 return row;
126 } 140 }
127 141
128 // Page, private: ------------------------------------------------------------- 142 // Page, private: -------------------------------------------------------------
129 /** 143 /**
130 * @return {string} The page's snippet highlighted with the model's 144 * Highlights the content of a node with the current search text.
131 * current search term. 145 * @param {DOMObject} node to highlight
132 */ 146 */
133 Page.prototype.getHighlightedSnippet_ = function() { 147 Page.prototype.highlightNodeContent_ = function(node) {
134 return Page.getHighlightedText_(this.snippet_, this.model_.getSearchText()); 148 node.innerHTML = Page.getHighlightedText_(node.innerHTML,
149 this.model_.getSearchText());
135 } 150 }
136 151
137 /** 152 /**
138 * @return {string} Gets the page's title highlighted with the 153 * @return {DOMObject} DOM representation for the title block.
139 * model's current search term.
140 */ 154 */
141 Page.prototype.getHighlightedTitle_ = function() { 155 Page.prototype.getTitleDOM_ = function() {
142 return Page.getHighlightedText_(this.title_, this.model_.getSearchText()); 156 var node = document.createElement('div');
143 } 157 node.className = 'title';
158 var link = document.createElement('a');
159 link.href = this.url_;
160 link.style.backgroundImage = 'url(chrome-ui://favicon/' + this.url_ + ')';
161 link.appendChild(document.createTextNode(this.title_));
162 this.highlightNodeContent_(link);
144 163
145 /** 164 node.appendChild(link);
146 * @return {string} HTML for the title block. 165
147 */ 166 if (this.starred_) {
148 Page.prototype.getTitleHTML_ = function() { 167 node.appendChild(createElementWithClassName('div', 'starred'));
149 return '<div class="title">' + 168 }
150 '<a ' + 169
151 'href="' + this.url_ + '" ' + 170 return node;
152 'style="background-image:url(chrome-ui://favicon/' +
153 this.url_ + ')" ' +
154 '>' +
155 this.getHighlightedTitle_() +
156 '</a>' +
157 '</div>';
158 } 171 }
159 172
160 // Page, private, static: ----------------------------------------------------- 173 // Page, private, static: -----------------------------------------------------
161 /** 174 /**
162 * Case-insensitively highlights a string. 175 * Case-insensitively highlights a string.
163 * @param {string} str The source string 176 * @param {string} str The source string
164 * @param {string} opt_highlight The string to highlight with 177 * @param {string} opt_highlight The string to highlight with
165 * @return {string} The highlighted string 178 * @return {string} The highlighted string
166 */ 179 */
167 Page.getHighlightedText_ = function(str, opt_highlight ) { 180 Page.getHighlightedText_ = function(str, opt_highlight ) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 return this.searchText_; 251 return this.searchText_;
239 } 252 }
240 253
241 /** 254 /**
242 * Tell the model that the view will want to see the current page. When 255 * Tell the model that the view will want to see the current page. When
243 * the data becomes available, the model will call the view back. 256 * the data becomes available, the model will call the view back.
244 * @page {Number} page The page we want to view. 257 * @page {Number} page The page we want to view.
245 */ 258 */
246 HistoryModel.prototype.requestPage = function(page) { 259 HistoryModel.prototype.requestPage = function(page) {
247 this.requestedPage_ = page; 260 this.requestedPage_ = page;
261 this.changed = true;
248 this.updateSearch_(); 262 this.updateSearch_();
249 } 263 }
250 264
251 /** 265 /**
252 * Receiver for history query. 266 * Receiver for history query.
253 * @param {String} term The search term that the results are for. 267 * @param {String} term The search term that the results are for.
254 * @param {Array} results A list of results 268 * @param {Array} results A list of results
255 */ 269 */
256 HistoryModel.prototype.addResults = function(term, results) { 270 HistoryModel.prototype.addResults = function(term, results) {
257 this.inFlight_ = false; 271 this.inFlight_ = false;
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 */ 483 */
470 HistoryView.prototype.onModelReady = function() { 484 HistoryView.prototype.onModelReady = function() {
471 this.displayResults_(); 485 this.displayResults_();
472 } 486 }
473 487
474 // HistoryView, private: ------------------------------------------------------ 488 // HistoryView, private: ------------------------------------------------------
475 /** 489 /**
476 * Update the page with results. 490 * Update the page with results.
477 */ 491 */
478 HistoryView.prototype.displayResults_ = function() { 492 HistoryView.prototype.displayResults_ = function() {
479 var output = []; 493 this.resultDiv_.innerHTML = '';
494
480 var results = this.model_.getNumberedRange( 495 var results = this.model_.getNumberedRange(
481 this.pageIndex_ * RESULTS_PER_PAGE, 496 this.pageIndex_ * RESULTS_PER_PAGE,
482 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); 497 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE);
483 498
484 if (this.model_.getSearchText()) { 499 if (this.model_.getSearchText()) {
485 output.push('<table class="results" cellspacing="0" ', 500 var resultTable = createElementWithClassName('table', 'results');
486 'cellpadding="0" border="0">'); 501 resultTable.cellSpacing = 0;
502 resultTable.cellPadding = 0;
503 resultTable.border = 0;
504
487 for (var i = 0, page; page = results[i]; i++) { 505 for (var i = 0, page; page = results[i]; i++) {
488 output.push(page.getSearchResultHTML()); 506 resultTable.appendChild(page.getSearchResultDOM());
489 } 507 }
490 output.push('</table>'); 508 this.resultDiv_.appendChild(resultTable);
491 } else { 509 } else {
492 var lastTime = Math.infinity; 510 var lastTime = Math.infinity;
493 for (var i = 0, page; page = results[i]; i++) { 511 for (var i = 0, page; page = results[i]; i++) {
494 // Break across day boundaries and insert gaps for browsing pauses. 512 // Break across day boundaries and insert gaps for browsing pauses.
495 var thisTime = page.time.getTime(); 513 var thisTime = page.time.getTime();
496 514
497 if ((i == 0 && page.continued) || !page.continued) { 515 if ((i == 0 && page.continued) || !page.continued) {
498 output.push('<div class="day">' + page.dateRelativeDay); 516 var day = createElementWithClassName('div', 'day');
517 day.appendChild(document.createTextNode(page.dateRelativeDay));
499 518
500 if (i == 0 && page.continued) 519 if (i == 0 && page.continued) {
501 output.push(' ' + localStrings.getString('cont')); 520 day.appendChild(document.createTextNode(' ' +
521 localStrings.getString('cont')));
522 }
502 523
503 output.push('<a href="#" class="delete-day" ' + 524 var link = createElementWithClassName('a', 'delete-day');
504 'onclick="return deleteDay(\'' + 525 link.href = '#';
505 page.time.toString() + '\');">' + 526 link.time = page.time.toString();
506 localStrings.getString("deleteday") + '</a>'); 527 link.onclick = deleteDay;
507 output.push('</div>'); 528 link.appendChild(
529 document.createTextNode(localStrings.getString("deleteday")));
530
531 day.appendChild(link);
532 this.resultDiv_.appendChild(day);
508 } else if (lastTime - thisTime > BROWSING_GAP_TIME) { 533 } else if (lastTime - thisTime > BROWSING_GAP_TIME) {
509 output.push('<div class="gap"></div>'); 534 this.resultDiv_.appendChild(createElementWithClassName('div', 'gap'));
510 } 535 }
511 lastTime = thisTime; 536 lastTime = thisTime;
512 537
513 // Draw entry. 538 // Add entry.
514 output.push(page.getBrowseResultHTML()); 539 this.resultDiv_.appendChild(page.getBrowseResultDOM());
515 } 540 }
516 } 541 }
517 this.resultDiv_.innerHTML = output.join(""); 542
518 this.displaySummaryBar_(); 543 this.displaySummaryBar_();
519 this.displayNavBar_(); 544 this.displayNavBar_();
520 } 545 }
521 546
522 /** 547 /**
523 * Update the summary bar with descriptive text. 548 * Update the summary bar with descriptive text.
524 */ 549 */
525 HistoryView.prototype.displaySummaryBar_ = function() { 550 HistoryView.prototype.displaySummaryBar_ = function() {
526 var searchText = this.model_.getSearchText(); 551 var searchText = this.model_.getSearchText();
527 if (searchText != '') { 552 if (searchText != '') {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 } 682 }
658 683
659 return newHash.join("&"); 684 return newHash.join("&");
660 } 685 }
661 686
662 /////////////////////////////////////////////////////////////////////////////// 687 ///////////////////////////////////////////////////////////////////////////////
663 // Document Functions: 688 // Document Functions:
664 /** 689 /**
665 * Window onload handler, sets up the page. 690 * Window onload handler, sets up the page.
666 */ 691 */
667 function load() { 692 function load() {
693 $('term').focus();
694
668 localStrings = new LocalStrings($('l10n')); 695 localStrings = new LocalStrings($('l10n'));
669 historyModel = new HistoryModel(); 696 historyModel = new HistoryModel();
670 historyView = new HistoryView(historyModel); 697 historyView = new HistoryView(historyModel);
671 pageState = new PageState(historyModel, historyView); 698 pageState = new PageState(historyModel, historyView);
672 699
673 // Create default view. 700 // Create default view.
674 var hashData = pageState.getHashData(); 701 var hashData = pageState.getHashData();
675 historyView.setSearch(hashData.q, hashData.p); 702 historyView.setSearch(hashData.q, hashData.p);
676 } 703 }
677 704
(...skipping 14 matching lines...) Expand all
692 * @param {number} page The page to set the view to. 719 * @param {number} page The page to set the view to.
693 */ 720 */
694 function setPage(page) { 721 function setPage(page) {
695 if (historyView) { 722 if (historyView) {
696 historyView.setPage(page); 723 historyView.setPage(page);
697 } 724 }
698 } 725 }
699 726
700 /** 727 /**
701 * Delete a day from history. 728 * Delete a day from history.
729 * TODO: Add UI to indicate that something is happening.
702 * @param {number} time A time from the day we wish to delete. 730 * @param {number} time A time from the day we wish to delete.
703 */ 731 */
704 function deleteDay(time) { 732 function deleteDay() {
705 if (confirm(localStrings.getString("deletedaywarning"))) 733 var time = this.time;
706 chrome.send("deleteDay", [time.toString()]); 734
735 // Check to see if item is already being deleted.
736 for (var i = 0, deleting; deleting = deleteQueue[i]; i++) {
737 if (deleting == time)
738 return false;
739 }
740
741 if (confirm(localStrings.getString("deletedaywarning"))) {
742 deleteQueue.push(time);
743 deleteNextInQueue();
744 }
745
707 return false; 746 return false;
708 } 747 }
709 748
749 /**
750 * Delete the next item in our deletion queue.
751 */
752 function deleteNextInQueue() {
753 if (!deleteInFlight && deleteQueue.length) {
754 deleteInFlight = true;
755 chrome.send("deleteDay", [deleteQueue[0]]);
756 }
757 }
758
710 /////////////////////////////////////////////////////////////////////////////// 759 ///////////////////////////////////////////////////////////////////////////////
711 // Chrome callbacks: 760 // Chrome callbacks:
712 /** 761 /**
713 * Our history system calls this function with results from searches. 762 * Our history system calls this function with results from searches.
714 */ 763 */
715 function historyResult(term, results) { 764 function historyResult(term, results) {
716 historyModel.addResults(term, results); 765 historyModel.addResults(term, results);
717 } 766 }
718 767
719 /** 768 /**
720 * Our history system calls this function when a deletion has finished. 769 * Our history system calls this function when a deletion has finished.
721 */ 770 */
722 function deleteComplete() { 771 function deleteComplete() {
723 historyView.reload(); 772 historyView.reload();
773 deleteInFlight = false;
774 if (deleteQueue.length > 1) {
775 deleteQueue = deleteQueue.slice(1, deleteQueue.length);
776 deleteNextInQueue();
777 }
778 }
779
780 /**
781 * Our history system calls this function if a delete is not ready (e.g.
782 * another delete is in-progress).
783 */
784 function deleteFailed() {
785 // The deletion failed - try again later.
786 deleteInFlight = false;
787 setTimeout(deleteNextInQueue, 500);
724 } 788 }
725 </script> 789 </script>
726 <style type="text/css"> 790 <style type="text/css">
727 body { 791 body {
728 font-family:arial; 792 font-family:arial;
729 background-color:white; 793 background-color:white;
730 color:black; 794 color:black;
731 font-size:84%; 795 font-size:84%;
732 margin:10px; 796 margin:10px;
733 } 797 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 } 850 }
787 .entry .title { 851 .entry .title {
788 max-width:600px; 852 max-width:600px;
789 overflow: hidden; 853 overflow: hidden;
790 white-space: nowrap; 854 white-space: nowrap;
791 text-overflow: ellipsis; 855 text-overflow: ellipsis;
792 } 856 }
793 .results .time, .results .title { 857 .results .time, .results .title {
794 margin-top:18px; 858 margin-top:18px;
795 } 859 }
860 .starred {
861 background:url('../../app/theme/star_small.png');
862 background-repeat:no-repeat;
863 display:inline-block;
864 margin-left:12px;
865 width:11px;
866 height:11px;
867 }
796 .entry .title a { 868 .entry .title a {
797 background-repeat:no-repeat; 869 background-repeat:no-repeat;
798 background-size:16px; 870 background-size:16px;
799 background-position:0px 1px; 871 background-position:0px 1px;
800 padding:1px 0px 4px 22px; 872 padding:1px 0px 4px 22px;
801 } 873 }
802 html[dir='rtl'] .entry .title a { 874 html[dir='rtl'] .entry .title a {
803 background-position:right; 875 background-position:right;
804 padding-left:0px; 876 padding-left:0px;
805 padding-right:22px; 877 padding-right:22px;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
845 <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '% s'</span> 917 <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '% s'</span>
846 <span id="history" jscontent="history">History</span> 918 <span id="history" jscontent="history">History</span>
847 <span id="cont" jscontent="cont">(cont.)</span> 919 <span id="cont" jscontent="cont">(cont.)</span>
848 <span id="noresults" jscontent="noresults">No results</span> 920 <span id="noresults" jscontent="noresults">No results</span>
849 <span id="noitems" jscontent="noitems">No items</span> 921 <span id="noitems" jscontent="noitems">No items</span>
850 <span id="deleteday" jscontent="deleteday">Delete history for this day</span> 922 <span id="deleteday" jscontent="deleteday">Delete history for this day</span>
851 <span id="deletedaywarning" jscontent="deletedaywarning">Are you sure you want to delete this day?</span> 923 <span id="deletedaywarning" jscontent="deletedaywarning">Are you sure you want to delete this day?</span>
852 </div> 924 </div>
853 </body> 925 </body>
854 </html> 926 </html>
OLDNEW
« no previous file with comments | « chrome/browser/dom_ui/history_ui.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698