Skip to content

Instantly share code, notes, and snippets.

@jaredhirsch
Last active August 29, 2015 14:17
Show Gist options
  • Save jaredhirsch/48bf56e5b66e8631b522 to your computer and use it in GitHub Desktop.
Save jaredhirsch/48bf56e5b66e8631b522 to your computer and use it in GitHub Desktop.
API between autocomplete code and iframe

Deprecation notice

This API corresponds to the state of the abandoned gecko fork. The project has been ported to an addon which contains the up-to-date API docs in its repo.


browser - iframe API

For each of these events, the general packet format is { type, data }. The type is the name of the event, and the objects shown are the object under the data key.

from browser to iframe:

data events

autocomplete-search-results event

When the user types something in the urlbar, the autocomplete search service looks through history/bookmarks/other unknown local stuff, and comes up with a list of results. The current production autocomplete implementation renders these in a XUL list, but we ship them over to the iframe to be rendered.

Note: if no results are returned, the results array will be empty.

Note: the current max number of results returned is 5; I made this up, it's trivial to change it.

{ results: array of result objects }

result:
{
  url: the page URL, which also might be a non-navigable thing like "about:blank"
  title: page title
  image: favicon URL (or null, if the browser has no favicon)
  type: "bookmark" or "favicon" or "action favicon"...I don't know what these mean,
        maybe they are classes used in styling the xul list items?
  text: the search term in the urlbar
}

suggested-search-results event

When the user types something in the urlbar, we fetch search suggestions from the user's currently selected search engine, and ship them over as a list.

{
  engine: name of the search engine used. right now we just use the user's chosen defualt
  results: {
    term: the search string
    local: an array of local suggestions (I'm not sure why/when these display,
           but for my dummy profile it's always an empty list)
    remote: an array of search suggestions that come from the user's default 
            search service
  }
}

remote-tabs event

We send over a list of remote tabs when the popup is opened. This will be an empty list if the browser just opened and sync isn't initialized yet, or if the user doesn't have sync enabled.

{
  clients: array of clients
}

client:
{
  clientName: sync device name, eg "Firefox on Nexus 9"
  class: 'mobile' or 'desktop'
  tabs: array of tabs
}

tab:
{
  title: page title
  url: page url
  icon: favicon URL or null
}

complete example for one remote client:
{
  "clients": [{
    "clientName": "Firefox on Nexus 9",
    "class": "mobile",
    "tabs": [{
      "title": "Tide Predictions - MONTEREY 9413450 Tidal Data Daily View - NOAA Tides & Currents",
      "url": "http://tidesandcurrents.noaa.gov/noaatidepredictions/viewDailyPredictions.jsp?Stationid=9413450",
      "icon": null
    }, {
      "title": "New Latin - Wikipedia, the free encyclopedia",
      "url": "http://en.m.wikipedia.org/wiki/New_Latin",
      "icon": "http://bits.wikimedia.org/favicon/wikipedia.ico"
    }]
  }]
}

empty-popup event

Instead of hiding the popup when the urlbar contains no text, we're experimenting with showing some commonly-visited pages (the frequent+recent links you are shown in the new tab tiles), as well as your most recent searches: the search term plus the first non-search page you visited. We will hone this search -> content result relation over time.

{
  frecentLinks: [ // frecentLinks is a list of up to 10 items of the form:
    {
      url: page url
      title: page title
      frecency: an integer value, bigger is more "frecent"
      lastVisitDate: time the page was last visited in **microseconds** since epoch,
                     must divide by 1000 before inserting into "new Date()"
      type: 'history' (or 'enhanced' or 'sponsored', not yet clear when or
            how many of these non-history links could be shown)
    }
  ],
  searches: {
    searchEngineName: 'google' or 'yahoo' (other built-in providers might not work for right now)
    results: [ // results is a list of up to 5 items of the form
      {
        searchTerms: the search phrase, mined from the search page's title
        result: { // this should hopefully be the first page visited after a search
          url: content page url
          title: content page title
        }
      }
    ]
  }
}

Here's an example: https://6a68.pastebin.mozilla.org/8830265

UI events

navigational-key event

Sent when the user types a key in the urlbar which should adjust the fake focus in the popup.

The navigational keys we care about have event.key values of 'Tab', 'PageUp', 'PageDown', 'ArrowUp', 'ArrowDown'. Note that we are using the new event.key property, not the event.keyCode property from days of yore.

Note also that you have to separately check shiftKey to decide if a tab is really a tab, or is a shift-tab.

{
  key: one of 'Tab', 'PageUp', 'PageDown', 'ArrowUp', 'ArrowDown'
  shiftKey: true if the shift key is down. The iframe should use this to decide
            if a Tab key is a Shift-Tab or not.
}

popupopen event

Fired when the popup is opened; as the browser's width changes, so does the width of the urlbar, which controls the width of the popup that contains the iframe.

{
  width: number of pixels (eg, 586)
}

popupclose event

Note: not yet reliable / consistent.

Currently fired when the popup is closing due to the user typing any of the navigational keys that should cause the popup to close (ArrowLeft, ArrowRight, Escape, Enter). We also close if the user typed a Backspace and there's 0 or 1 chars in the urlbar.

TODO: fire this event when the popup closes due to the user clicking away or for other reasons

from iframe back to browser

WebChannel message sending looks like this (from MDN):

window.dispatchEvent(new window.CustomEvent("WebChannelMessageToChrome", {
  detail: {
    id: webChannelId,
    message: {
      something: true
    }
  }
});

The message is what's documented for each of these signals.

Currently the id is 'ohai'.

autocomplete-url-clicked event

When the user clicks on one of the autocomplete results, we send this signal to the browser, and the browser navigates the current tab to the clicked URL.

{
  type: 'autocomplete-url-clicked',
  data: {
    result: the text contents of the item clicked by the user, could be a search suggestion or a url
    resultType: 'suggestion' or 'url'
  }
}

url-selected event

When the user keys through the autocomplete list, the iframe sends up the url of the currently-selected item, so that the browser can put the item's URL in the urlbar, replicating current behavior.

Note, if the user clicks a search suggestion, we throw that in the urlbar too--so this is a misleading name, but works.

{
  type: 'url-selected',
  data: {
    result: the text contents of the item selected by the user, could be a search suggestion or a url
    resultType: 'suggestion' or 'url'
  }
}

events we might like to add sooner than later

from browser to iframe

  • 'closePopup' / 'openPopup'
    • maybe useful? we could hook into existing open/close logic to reduce duplication?

from iframe to browser

  • 'search-suggestion-clicked'
    • we could just have a "surf to this url" event, and put a data-url in the suggestion list items that takes them to the search provider's results page for the given URL
    • however, for metrics purposes, it would be good to know which set of things is getting clicked
  • keypress events for the autocomplete and suggestion items, so we can track keyboard usage vs mouse usage
@thovden
Copy link

thovden commented Apr 7, 2015

We need to collect data about outcomes (i.e., destination URLs the user actually went to) to train suggestion models. Are you thinking the iframe collects that data and considers autocomplete-url-clicked an outcome? Or do we build a more advanced heuristic in gecko that considers dwell time etc?

@jaredhirsch
Copy link
Author

@thovden My general thought here is that anything we want to iterate quickly or frequently should probably not live in gecko. However, I haven't given this a ton of thought yet, and this API is intended to be thrown away with the first throwaway prototype; would you mind sending something to the list, so that we don't forget about this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment