Home | History | Annotate | Download | only in suggestions_internals
      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
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * Javascript for suggestions_internals.html, served from
      7  * chrome://suggestions-internals/. This is used to debug suggestions ranking.
      8  * When loaded, the page will show the current set of suggestions, along with a
      9  * large set of information (e.g. all the signals that were taken into
     10  * consideration for deciding which pages were selected to be shown to the user)
     11  * that will aid in debugging and optimizing the algorithms.
     12  */
     13 cr.define('suggestionsInternals', function() {
     14   'use strict';
     15 
     16   /**
     17    * Register our event handlers.
     18    */
     19   function initialize() {
     20     $('suggestions-form').addEventListener('submit', onRefreshClicked);
     21     $('show-discarded').addEventListener('change', refresh);
     22     refresh();
     23   }
     24 
     25   /**
     26    * Called when the 'Refresh' button is clicked. Reloads the suggestions data.
     27    */
     28   function onRefreshClicked(event) {
     29     refresh();
     30     event.preventDefault();
     31   }
     32 
     33   /**
     34    * Reloads the suggestions data by sending a 'getSuggestions' message to
     35    * Chrome. The C++ code is then expected to call 'setSuggestions' when there
     36    * are suggestions ready.
     37    */
     38   function refresh() {
     39     chrome.send('getSuggestions');
     40   }
     41 
     42   /**
     43    * A list of columns that we do not want to display.
     44    * @type {Array.<string>}
     45    * @const
     46    */
     47   var IGNORED_COLUMNS = [
     48     'direction'
     49   ];
     50 
     51   /**
     52    * A list specifying the name of the first columns to be displayed. If
     53    * present, they will be displayed in this order, followed by the remaining
     54    * columns.
     55    * @type {Array.<string>}
     56    * @const
     57    */
     58   var PREFERRED_COLUMN_ORDER = [
     59     'title',
     60     'url',
     61     'score'
     62   ];
     63 
     64   function setBooleanColumn(column, value) {
     65     if (value) {
     66       column.innerText = 'Y';
     67       column.classList.add('boolean-property-true');
     68     } else {
     69       column.innerText = 'N';
     70       column.classList.add('boolean-property-false');
     71     }
     72   }
     73 
     74   /**
     75    * Called by Chrome code, with a ranked list of suggestions. The columns
     76    * to be displayed are calculated automatically from the properties of the
     77    * elements in the list, such that all properties have a column.
     78    */
     79   function setSuggestions(list) {
     80     // Build a list of all the columns that will be displayed.
     81     var columns = [];
     82     list.forEach(function(entry) {
     83       for (var column in entry) {
     84         if (typeof entry[column] == 'object') {
     85           // Expand one level deep
     86           for (var subColumn in entry[column]) {
     87             var path = column + '.' + subColumn;
     88             if (columns.indexOf(path) < 0)
     89               columns.push(path);
     90           }
     91         } else if (columns.indexOf(column) < 0) {
     92           columns.push(column);
     93         }
     94       }
     95     });
     96 
     97     // Remove columns that we don't want to display.
     98     columns = columns.filter(function(column) {
     99       return IGNORED_COLUMNS.indexOf(column) < 0;
    100     });
    101 
    102     // Move the preferred columns to the start of the column list.
    103     for (var i = PREFERRED_COLUMN_ORDER.length - 1; i >= 0; i--) {
    104       var index = columns.indexOf(PREFERRED_COLUMN_ORDER[i]);
    105       if (index >= 0)
    106         columns.unshift(columns.splice(index, 1)[0]);
    107     }
    108 
    109     // Special columns.
    110     columns.unshift('favicon');
    111     columns.unshift('screenshot');
    112     columns.unshift('rank');
    113 
    114     // Erase whatever is currently being displayed.
    115     var output = $('suggestions-debug-text');
    116     output.innerHTML = '';
    117 
    118     // Create the container table and add the header row.
    119     var table = document.createElement('table');
    120     table.className = 'suggestions-debug-table';
    121     var header = document.createElement('tr');
    122     columns.forEach(function(entry) {
    123       var column = document.createElement('th');
    124       column.innerText = entry;
    125       header.appendChild(column);
    126     });
    127     table.appendChild(header);
    128 
    129     // Add all the suggestions to the table.
    130     var rank = 1;
    131     list.forEach(function(entry) {
    132       var row = document.createElement('tr');
    133       columns.forEach(function(columnName) {
    134         var column = document.createElement('td');
    135         // Expand the path and find the data if it's there.
    136         var path = columnName.split('.');
    137         var data = entry;
    138         for (var i = 0; i < path.length; ++i) {
    139           if (data && data.hasOwnProperty(path[i]))
    140             data = data[path[i]];
    141           else
    142             data = undefined;
    143         }
    144         // Only add the column if the current suggestion has this property
    145         // (otherwise, leave the cell empty).
    146         if (typeof(data) != 'undefined') {
    147           if (typeof(data) == 'boolean') {
    148             setBooleanColumn(column, data);
    149           } else if (/^https?:\/\/.+$/.test(data)) {
    150             // If the text is a URL, make it an anchor element.
    151             var anchor = document.createElement('a');
    152             anchor.href = data;
    153             anchor.innerText = data;
    154             column.appendChild(anchor);
    155           } else {
    156             column.innerText = data;
    157           }
    158         } else if (columnName == 'rank') {
    159           column.innerText = rank++;
    160         } else if (columnName == 'screenshot') {
    161           var thumbnailUrl = 'chrome://thumb/' + entry.url;
    162           var img = document.createElement('img');
    163           img.onload = function() { setBooleanColumn(column, true); }
    164           img.onerror = function() { setBooleanColumn(column, false); }
    165           img.src = thumbnailUrl;
    166         } else if (columnName == 'favicon') {
    167           var faviconUrl = 'chrome://favicon/size/16@1x/' + entry.url;
    168           column.style.backgroundImage = url(faviconUrl);
    169           column.style.backgroundRepeat = 'no-repeat';
    170           column.style.backgroundPosition = 'center center';
    171         }
    172         row.appendChild(column);
    173       });
    174       table.appendChild(row);
    175     });
    176 
    177     output.appendChild(table);
    178   }
    179 
    180   return {
    181     initialize: initialize,
    182     setSuggestions: setSuggestions
    183   };
    184 });
    185 
    186 document.addEventListener('DOMContentLoaded', suggestionsInternals.initialize);
    187