Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 <include src="../uber/uber_utils.js"> | 5 <include src="../uber/uber_utils.js"> |
| 6 <include src="history_focus_manager.js"> | 6 <include src="history_focus_manager.js"> |
| 7 | 7 |
| 8 /////////////////////////////////////////////////////////////////////////////// | 8 /////////////////////////////////////////////////////////////////////////////// |
| 9 // Globals: | 9 // Globals: |
| 10 /** @const */ var RESULTS_PER_PAGE = 150; | 10 /** @const */ var RESULTS_PER_PAGE = 150; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 * chrome/browser/ui/webui/history_ui.cc: | 50 * chrome/browser/ui/webui/history_ui.cc: |
| 51 * BrowsingHistoryHandler::HistoryEntry::ToValue() | 51 * BrowsingHistoryHandler::HistoryEntry::ToValue() |
| 52 * @typedef {{allTimestamps: Array<number>, | 52 * @typedef {{allTimestamps: Array<number>, |
| 53 * blockedVisit: (boolean|undefined), | 53 * blockedVisit: (boolean|undefined), |
| 54 * dateRelativeDay: (string|undefined), | 54 * dateRelativeDay: (string|undefined), |
| 55 * dateShort: string, | 55 * dateShort: string, |
| 56 * dateTimeOfDay: (string|undefined), | 56 * dateTimeOfDay: (string|undefined), |
| 57 * deviceName: string, | 57 * deviceName: string, |
| 58 * deviceType: string, | 58 * deviceType: string, |
| 59 * domain: string, | 59 * domain: string, |
| 60 * fallbackFaviconText: string, | |
|
Dan Beam
2016/02/05 20:42:11
is this ever more than a character?
pkotwicz
2016/02/06 02:59:55
Yes, fallbackFaviconText is "IP" for IP urls. Othe
| |
| 60 * hostFilteringBehavior: (number|undefined), | 61 * hostFilteringBehavior: (number|undefined), |
| 61 * snippet: (string|undefined), | 62 * snippet: (string|undefined), |
| 62 * starred: boolean, | 63 * starred: boolean, |
| 63 * time: number, | 64 * time: number, |
| 64 * title: string, | 65 * title: string, |
| 65 * url: string}} | 66 * url: string}} |
| 66 */ | 67 */ |
| 67 var HistoryEntry; | 68 var HistoryEntry; |
| 68 | 69 |
| 69 /** | 70 /** |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 * visit before it. | 119 * visit before it. |
| 119 * @param {HistoryModel} model The model object this entry belongs to. | 120 * @param {HistoryModel} model The model object this entry belongs to. |
| 120 * @constructor | 121 * @constructor |
| 121 */ | 122 */ |
| 122 function Visit(result, continued, model) { | 123 function Visit(result, continued, model) { |
| 123 this.model_ = model; | 124 this.model_ = model; |
| 124 this.title_ = result.title; | 125 this.title_ = result.title; |
| 125 this.url_ = result.url; | 126 this.url_ = result.url; |
| 126 this.domain_ = result.domain; | 127 this.domain_ = result.domain; |
| 127 this.starred_ = result.starred; | 128 this.starred_ = result.starred; |
| 129 this.fallbackFaviconText_ = result.fallbackFaviconText; | |
| 128 | 130 |
| 129 // These identify the name and type of the device on which this visit | 131 // These identify the name and type of the device on which this visit |
| 130 // occurred. They will be empty if the visit occurred on the current device. | 132 // occurred. They will be empty if the visit occurred on the current device. |
| 131 this.deviceName = result.deviceName; | 133 this.deviceName = result.deviceName; |
| 132 this.deviceType = result.deviceType; | 134 this.deviceType = result.deviceType; |
| 133 | 135 |
| 134 // The ID will be set according to when the visit was displayed, not | 136 // The ID will be set according to when the visit was displayed, not |
| 135 // received. Set to -1 to show that it has not been set yet. | 137 // received. Set to -1 to show that it has not been set yet. |
| 136 this.id_ = -1; | 138 this.id_ = -1; |
| 137 | 139 |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 245 bookmarkSection.removeEventListener('click', f); | 247 bookmarkSection.removeEventListener('click', f); |
| 246 e.preventDefault(); | 248 e.preventDefault(); |
| 247 }.bind(this)); | 249 }.bind(this)); |
| 248 } | 250 } |
| 249 | 251 |
| 250 if (focusless) | 252 if (focusless) |
| 251 bookmarkSection.tabIndex = -1; | 253 bookmarkSection.tabIndex = -1; |
| 252 | 254 |
| 253 entryBox.appendChild(bookmarkSection); | 255 entryBox.appendChild(bookmarkSection); |
| 254 | 256 |
| 257 if (addTitleFavicon || this.blockedVisit) { | |
| 258 var faviconSection = createElementWithClassName('div', 'favicon'); | |
| 259 if (this.blockedVisit) | |
| 260 faviconSection.classList.add('blocked-icon'); | |
| 261 else | |
| 262 this.loadFavicon_(faviconSection); | |
| 263 entryBox.appendChild(faviconSection); | |
| 264 } | |
| 265 | |
| 255 var visitEntryWrapper = /** @type {HTMLElement} */( | 266 var visitEntryWrapper = /** @type {HTMLElement} */( |
| 256 entryBox.appendChild(document.createElement('div'))); | 267 entryBox.appendChild(document.createElement('div'))); |
| 257 if (addTitleFavicon || this.blockedVisit) | 268 if (addTitleFavicon || this.blockedVisit) |
| 258 visitEntryWrapper.classList.add('visit-entry'); | 269 visitEntryWrapper.classList.add('visit-entry'); |
| 259 if (this.blockedVisit) { | 270 if (this.blockedVisit) { |
| 260 visitEntryWrapper.classList.add('blocked-indicator'); | 271 visitEntryWrapper.classList.add('blocked-indicator'); |
| 261 visitEntryWrapper.appendChild(this.getVisitAttemptDOM_()); | 272 visitEntryWrapper.appendChild(this.getVisitAttemptDOM_()); |
| 262 } else { | 273 } else { |
| 263 var title = visitEntryWrapper.appendChild( | 274 var title = visitEntryWrapper.appendChild( |
| 264 this.getTitleDOM_(isSearchResult)); | 275 this.getTitleDOM_(isSearchResult)); |
| 265 | 276 |
| 266 if (addTitleFavicon) | |
| 267 this.addFaviconToElement_(visitEntryWrapper); | |
| 268 | |
| 269 if (focusless) | 277 if (focusless) |
| 270 title.querySelector('a').tabIndex = -1; | 278 title.querySelector('a').tabIndex = -1; |
| 271 | 279 |
| 272 visitEntryWrapper.appendChild(domain); | 280 visitEntryWrapper.appendChild(domain); |
| 273 } | 281 } |
| 274 | 282 |
| 275 if (isMobileVersion()) { | 283 if (isMobileVersion()) { |
| 276 if (this.model_.editingEntriesAllowed) { | 284 if (this.model_.editingEntriesAllowed) { |
| 277 var removeButton = createElementWithClassName('button', 'remove-entry'); | 285 var removeButton = createElementWithClassName('button', 'remove-entry'); |
| 278 removeButton.setAttribute('aria-label', | 286 removeButton.setAttribute('aria-label', |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 Visit.prototype.getVisitAttemptDOM_ = function() { | 474 Visit.prototype.getVisitAttemptDOM_ = function() { |
| 467 var node = createElementWithClassName('div', 'title'); | 475 var node = createElementWithClassName('div', 'title'); |
| 468 node.innerHTML = loadTimeData.getStringF('blockedVisitText', | 476 node.innerHTML = loadTimeData.getStringF('blockedVisitText', |
| 469 this.url_, | 477 this.url_, |
| 470 this.id_, | 478 this.id_, |
| 471 this.domain_); | 479 this.domain_); |
| 472 return node; | 480 return node; |
| 473 }; | 481 }; |
| 474 | 482 |
| 475 /** | 483 /** |
| 476 * Set the favicon for an element. | 484 * Load the favicon for an element. |
| 477 * @param {Element} el The DOM element to which to add the icon. | 485 * @param {Element} faviconDiv The DOM element for which to load the icon. |
| 478 * @private | 486 * @private |
| 479 */ | 487 */ |
| 480 Visit.prototype.addFaviconToElement_ = function(el) { | 488 Visit.prototype.loadFavicon_ = function(faviconDiv) { |
| 481 var url = isMobileVersion() ? | 489 if (cr.isAndroid) { |
| 482 getFaviconImageSet(this.url_, 32, 'touch-icon') : | 490 // On Android, if a large icon is unavailable, a fallback favicon is |
| 483 getFaviconImageSet(this.url_); | 491 // generated in Javascript because Android does not yet support text |
|
newt (away)
2016/01/25 19:43:35
Maybe: "an HTML/CSS fallback favicon is generated"
pkotwicz
2016/02/06 02:59:55
Done.
| |
| 484 el.style.backgroundImage = url; | 492 // drawing in native. |
|
Dan Beam
2016/02/05 20:42:12
well, that's not really true...
https://developer
pkotwicz
2016/02/06 02:59:55
I was referring to Android using canvas_notimpleme
| |
| 493 | |
| 494 // Check whether a fallback favicon needs to be generated. | |
| 495 var desiredPixelSize = 32 * window.devicePixelRatio; | |
| 496 var img = new Image(); | |
| 497 img.onload = this.onLargeFaviconLoadedAndroid_.bind(this, faviconDiv, img); | |
|
Dan Beam
2016/02/05 20:42:11
you probably don't need to bind img because you ca
Dan Beam
2016/02/05 20:42:12
can we just trigger onerror in the case we fail to
pkotwicz
2016/02/06 02:59:55
On 2016/02/05 20:42:11, Dan Beam wrote:
> you prob
pkotwicz
2016/02/06 02:59:55
Currently, when there is no large enough favicon,
| |
| 498 img.src = 'chrome://large-icon/' + desiredPixelSize + '/' + this.url_; | |
| 499 } else { | |
| 500 faviconDiv.style.backgroundImage = getFaviconImageSet(this.url_); | |
| 501 } | |
| 485 }; | 502 }; |
| 486 | 503 |
| 487 /** | 504 /** |
| 505 * Called when the chrome://large-icon image has finished loading. | |
| 506 * @param {Element} faviconDiv The DOM element to add the favicon to. | |
| 507 * @param {HTMLImageElement} loadedImg The image which finished loading. | |
| 508 * @private | |
| 509 */ | |
| 510 Visit.prototype.onLargeFaviconLoadedAndroid_ = function(faviconDiv, loadedImg) { | |
| 511 // The loaded image should either: | |
| 512 // - Have the desired size. | |
| 513 // OR | |
| 514 // - Be 1x1 px with the background color for the fallback icon. | |
| 515 if (loadedImg.width == 1 && loadedImg.height == 1) { | |
|
Dan Beam
2016/02/05 20:42:12
is it likely that width == 1 and height != 1?
pkotwicz
2016/02/06 02:59:55
It is not likely. I changed the if() to check just
| |
| 516 faviconDiv.classList.add('fallback-favicon'); | |
|
Dan Beam
2016/02/05 20:42:11
can we just create a wrapper div *if* the icon fai
pkotwicz
2016/02/06 02:59:55
Creating the wrapper div only if the icon fails to
| |
| 517 faviconDiv.innerHTML = this.fallbackFaviconText_; | |
|
Dan Beam
2016/02/05 20:42:11
can you use textContent instead? (innerHTML is ba
pkotwicz
2016/02/06 02:59:55
Done.
| |
| 518 } | |
| 519 faviconDiv.style.backgroundImage = url(loadedImg.src); | |
| 520 }; | |
| 521 | |
| 522 /** | |
| 488 * Launch a search for more history entries from the same domain. | 523 * Launch a search for more history entries from the same domain. |
| 489 * @private | 524 * @private |
| 490 */ | 525 */ |
| 491 Visit.prototype.showMoreFromSite_ = function() { | 526 Visit.prototype.showMoreFromSite_ = function() { |
| 492 recordUmaAction('HistoryPage_EntryMenuShowMoreFromSite'); | 527 recordUmaAction('HistoryPage_EntryMenuShowMoreFromSite'); |
| 493 historyView.setSearch(this.domain_); | 528 historyView.setSearch(this.domain_); |
| 494 $('search-field').focus(); | 529 $('search-field').focus(); |
| 495 }; | 530 }; |
| 496 | 531 |
| 497 /** | 532 /** |
| (...skipping 844 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1342 | 1377 |
| 1343 /** | 1378 /** |
| 1344 * Generates and adds the grouped visits DOM for a certain domain. This | 1379 * Generates and adds the grouped visits DOM for a certain domain. This |
| 1345 * includes the clickable arrow and domain name and the visit entries for | 1380 * includes the clickable arrow and domain name and the visit entries for |
| 1346 * that domain. | 1381 * that domain. |
| 1347 * @param {Element} results DOM object to which to add the elements. | 1382 * @param {Element} results DOM object to which to add the elements. |
| 1348 * @param {string} domain Current domain name. | 1383 * @param {string} domain Current domain name. |
| 1349 * @param {Array} domainVisits Array of visits for this domain. | 1384 * @param {Array} domainVisits Array of visits for this domain. |
| 1350 * @private | 1385 * @private |
| 1351 */ | 1386 */ |
| 1352 HistoryView.prototype.getGroupedVisitsDOM_ = function( | 1387 HistoryView.prototype.getGroupedVisitsDOM_ = function( |
|
Dan Beam
2016/02/05 20:42:11
ah, you did check the grouped view :) sorry for no
| |
| 1353 results, domain, domainVisits) { | 1388 results, domain, domainVisits) { |
| 1354 // Add a new domain entry. | 1389 // Add a new domain entry. |
| 1355 var siteResults = results.appendChild( | 1390 var siteResults = results.appendChild( |
| 1356 createElementWithClassName('li', 'site-entry')); | 1391 createElementWithClassName('li', 'site-entry')); |
| 1357 | 1392 |
| 1358 var siteDomainWrapper = siteResults.appendChild( | 1393 var siteDomainWrapper = siteResults.appendChild( |
| 1359 createElementWithClassName('div', 'site-domain-wrapper')); | 1394 createElementWithClassName('div', 'site-domain-wrapper')); |
| 1360 // Make a row that will contain the arrow, the favicon and the domain. | 1395 // Make a row that will contain the arrow, the favicon and the domain. |
| 1361 var siteDomainRow = siteDomainWrapper.appendChild( | 1396 var siteDomainRow = siteDomainWrapper.appendChild( |
| 1362 createElementWithClassName('div', 'site-domain-row')); | 1397 createElementWithClassName('div', 'site-domain-row')); |
| 1363 | 1398 |
| 1364 if (this.model_.editingEntriesAllowed) { | 1399 if (this.model_.editingEntriesAllowed) { |
| 1365 var siteDomainCheckbox = | 1400 var siteDomainCheckbox = |
| 1366 createElementWithClassName('input', 'domain-checkbox'); | 1401 createElementWithClassName('input', 'domain-checkbox'); |
| 1367 | 1402 |
| 1368 siteDomainCheckbox.type = 'checkbox'; | 1403 siteDomainCheckbox.type = 'checkbox'; |
| 1369 siteDomainCheckbox.addEventListener('click', domainCheckboxClicked); | 1404 siteDomainCheckbox.addEventListener('click', domainCheckboxClicked); |
| 1370 siteDomainCheckbox.domain_ = domain; | 1405 siteDomainCheckbox.domain_ = domain; |
| 1371 siteDomainCheckbox.setAttribute('aria-label', domain); | 1406 siteDomainCheckbox.setAttribute('aria-label', domain); |
| 1372 siteDomainRow.appendChild(siteDomainCheckbox); | 1407 siteDomainRow.appendChild(siteDomainCheckbox); |
| 1373 } | 1408 } |
| 1374 | 1409 |
| 1375 var siteArrow = siteDomainRow.appendChild( | 1410 var siteArrow = siteDomainRow.appendChild( |
| 1376 createElementWithClassName('div', 'site-domain-arrow')); | 1411 createElementWithClassName('div', 'site-domain-arrow')); |
| 1412 var siteFavicon = siteDomainRow.appendChild( | |
| 1413 createElementWithClassName('div', 'favicon')); | |
|
Dan Beam
2016/02/05 20:42:12
is it necessary to add this wrapper on desktop?
| |
| 1377 var siteDomain = siteDomainRow.appendChild( | 1414 var siteDomain = siteDomainRow.appendChild( |
| 1378 createElementWithClassName('div', 'site-domain')); | 1415 createElementWithClassName('div', 'site-domain')); |
| 1379 var siteDomainLink = siteDomain.appendChild(new ActionLink); | 1416 var siteDomainLink = siteDomain.appendChild(new ActionLink); |
| 1380 siteDomainLink.textContent = domain; | 1417 siteDomainLink.textContent = domain; |
| 1381 var numberOfVisits = createElementWithClassName('span', 'number-visits'); | 1418 var numberOfVisits = createElementWithClassName('span', 'number-visits'); |
| 1382 var domainElement = document.createElement('span'); | 1419 var domainElement = document.createElement('span'); |
| 1383 | 1420 |
| 1384 numberOfVisits.textContent = loadTimeData.getStringF('numberVisits', | 1421 numberOfVisits.textContent = loadTimeData.getStringF('numberVisits', |
| 1385 domainVisits.length); | 1422 domainVisits.length); |
| 1386 siteDomain.appendChild(numberOfVisits); | 1423 siteDomain.appendChild(numberOfVisits); |
| 1387 | 1424 |
| 1388 domainVisits[0].addFaviconToElement_(siteDomain); | 1425 domainVisits[0].loadFavicon_(siteFavicon); |
| 1389 | 1426 |
| 1390 siteDomainWrapper.addEventListener( | 1427 siteDomainWrapper.addEventListener( |
| 1391 'click', this.toggleGroupedVisits_.bind(this)); | 1428 'click', this.toggleGroupedVisits_.bind(this)); |
| 1392 | 1429 |
| 1393 if (this.model_.isSupervisedProfile) { | 1430 if (this.model_.isSupervisedProfile) { |
| 1394 siteDomainRow.appendChild( | 1431 siteDomainRow.appendChild( |
| 1395 getFilteringStatusDOM(domainVisits[0].hostFilteringBehavior)); | 1432 getFilteringStatusDOM(domainVisits[0].hostFilteringBehavior)); |
| 1396 } | 1433 } |
| 1397 | 1434 |
| 1398 siteResults.appendChild(siteDomainWrapper); | 1435 siteResults.appendChild(siteDomainWrapper); |
| (...skipping 980 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2379 historyView.reload(); | 2416 historyView.reload(); |
| 2380 } | 2417 } |
| 2381 | 2418 |
| 2382 // Add handlers to HTML elements. | 2419 // Add handlers to HTML elements. |
| 2383 document.addEventListener('DOMContentLoaded', load); | 2420 document.addEventListener('DOMContentLoaded', load); |
| 2384 | 2421 |
| 2385 // This event lets us enable and disable menu items before the menu is shown. | 2422 // This event lets us enable and disable menu items before the menu is shown. |
| 2386 document.addEventListener('canExecute', function(e) { | 2423 document.addEventListener('canExecute', function(e) { |
| 2387 e.canExecute = true; | 2424 e.canExecute = true; |
| 2388 }); | 2425 }); |
| OLD | NEW |