Skip to content

Instantly share code, notes, and snippets.

@jaredhirsch
Last active February 5, 2016 19:16
Show Gist options
  • Save jaredhirsch/a0dd3e200289e6020938 to your computer and use it in GitHub Desktop.
Save jaredhirsch/a0dd3e200289e6020938 to your computer and use it in GitHub Desktop.
gecko stuff

note (5 February 2016): these docs are old and out of date. UnifiedComplete.js has changed how autocomplete works in Firefox. don't rely on this information when exploring the code.

hacking on gecko

basic dev workflow

getting the code

building and seeing changes in a custom firefox build

  • TL;DR: ./mach build && ./obj-x86_64-apple-darwin14.1.0/dist/Nightly.app/Contents/MacOS/firefox-bin -P "foo" -purgecaches

  • ./mach build

    • should take 15ish minutes first time around, much less time thereafter (around 30-45 sec)
    • the build process creates an output directory called obj-<whatever>, which contains a firefox build with your changes
      • in the example above, it's obj-x86_64-apple-darwin14.1.0
    • on mac, the path to the firefox binary under that obj-foo directory is ./dist/Nightly.app/Contents/MacOS/firefox.bin
      • if you already have a copy of FF running, add -P "profileName" to open it with some other profile
    • the -purgecaches argument is needed to reload XUL, XBL, and JS/JSM
  • source: https://wiki.mozilla.org/Firefox/Dev_Cheatsheet

debugging XUL

  • The autocomplete popup is tricky to inspect because it disappears on click. In order to examine the XUL structure, shift+right click on one of the rows to open an inspector that shows the XUL DOM structure, see screenshot:

adding new XPCOM components

links

autocomplete

It looks like the autocomplete code loosely binds a dropdown box and search providers, as well as providing caching for that particular field. So, any XUL form field could provide autocomplete search of history or previously-entered strings. There is also a notion of a remote autocomplete service, so there's already some code to handle firing an XHR at a server and expecting a specific type of response. Rather than mess with that base autocomplete class (which is used everywhere), we will be better off to create our own custom autocomplete provider and our own custom autocomplete popup UI.

general XUL-related links/info

How navbar customization works (removing the separate searchbar)

  • If you customize Firefox UI with the Customize builder, the results will be stored in the profile, in a file called xulstore.json.
    • If you customize so that the searchbar is removed, then xulstore['chrome://browser/content/browser.xul']['nav-bar'].currentset becomes 'urlbar-container,bookmarks-menu-button,downloads-button,home-button,loop-button'.
    • This file is not present by default, so faking it seems difficult/error-prone. Maybe we can hook into the customization behavior (it's probably an XPCOM service?) rather than simply hiding the searchbar via some kind of CSS hack at XUL load time.

OpenSearch / adding search providers

Reading the Code / Orienteering

Guide to Mozilla source code structure https://developer.mozilla.org/en-US/docs/Mozilla_Source_Code_Directory_Structure

autocomplete-related interfaces - based on MDN, which omits some of them

Beware: MDN doesn't distinguish between obsolete xpfe interfaces (like nsIAutoCompleteItem) and toolkit interfaces used in modern Firefox. Everything we care about is under the top-level /toolkit module.

TODO: this needs another pass, the code is in a semi-refactored state, MDN is slightly inaccurate, need to reconcile everything

  • Major interfaces used by the autocomplete service:

    • nsIAutoCompleteSearch (MDN docs, C++ interface source) - object representing autocomplete search provider as an XPCOM service. (Note, the SearchSuggestionController (toolkit/components/search/SearchSuggestionController.jsm) code comments say it was extracted from the nsSearchSuggestions.jsm file to remove the dependency on the nsIAutoCompleteSearch interface. Not yet sure why.)
      • Exposes a single method, startSearch, which is passed the search query, and an nsIAutoCompleteObserver that accepts results in the form of an nsIAutoCompleteResult object. (This is a simplified summary; see the MDN page for full details and optional parameters.)
    • nsIAutoCompleteObserver (MDN docs, dxr code) - exposes onSearchResult and onUpdateSearchResult methods (updating allows for asynchronous insertion of values from a remote service, I think).
    • nsIAutoCompleteResult (MDN docs, dxr code) - represents the result of a search. The interface name is a bit ambiguous - this isn't an individual result from a search, it's an individual response from the search service for a given query. Contains result status (did search find a match, not find a match, or timeout); result count; and the actual results.
      • nsIAutoCompleteSimpleResult, an interesting subclass (C++ idl, header file, C++ impl) - used by satchel's nsFormFillController, and the places nsPlacesAutoComplete.js and UnifiedComplete.js files, TODO explore further
  • Major interfaces used by the autocomplete UI layer (TODO needs updating):

    • nsIAutoCompleteController - it's a controller but doesn't directly observe DOM; instead, observes user input / key press events fired by a nsIAutoCompleteInput, which could either be a XUL autocomplete textbox or a form fill controller that handles website form history
    • nsIAutoCompleteInput - monitors the input in a text field and displays an autocomplete panel at the appropriate time
    • nsIAutoCompletePopup (C++ interface, XUL implementation) - binds an nsIAutoCompleteInput object to an nsIDOMElement

docs generated by actually reading the code, organized by filename:

/browser/base/content/browser.js

  • assembles the modules
  • gURLBar is defined here on the global window object

Views

  • /browser/base/content/browser.xul

    • defines an autocomplete-richtextbox entity (i.e. an xml tag name)
      • this corresponds to PopupAutoCompleteRichResult
      • which is defined in components/search/content/search.xml
  • /browser/base/content/urlbarBindings.xml

    • urlbar UI handlers defined inside an XML file
    • urlbar extends content/bindings/autocomplete.xml, not sure if that really means toolkit/content/widgets/autocomplete.xml which seems to have similar stuff in it
  • /toolkit/content/widgets/autocomplete.xml

    • UI definitions for the autocomplete UI that drops down

Controllers

  • /toolkit/components/autocomplete/nsAutoCompleteController.cpp
    • HandleText method contains logic around when to search & waiting for user to stop typing
    • StartSearches method - set a timer, call StartSearch
    • StartSearch - loop over mSearches, call startSearch on (some of) them
    • SetInput - assemble mSearches by getting pointers to search services, specifically, by getting a "contract id string" from an instance of the nsIAutoCompleteInput interface.

Services

These are implementations of autocomplete interfaces I've found in the gecko codebase:

  • /toolkit/components/places

    • TODO look at other files in this directory
    • /toolkit/components/places/nsPlacesAutoComplete.js
      • This file is incredibly helpful. We can lean on this implementation when defining our own autocomplete implementation (though we need to investigate how other search providers integrate into autocomplete. I've found an OpenSearch specification, need to explore further)
      • Autocomplete strings are sent here, to be queried against the Places SQLite DB
      • see "Smart Getters" section for SQL queries corresponding to keyword, bookmark, "frecency", etc searches
      • startSearch function is very helpful reading
  • /toolkit/components/filepicker

  • toolkit/components/satchel

    • need to read this and figure out what it does

Digging into Search (search bar, not urlbar/awesomebar)

  • OpenSearch is an XML spec describing search provider endpoints

  • browser/base/content/searchSuggestionUI.js

    • creates an xhtml table, inserts into DOM after a given textbox el, styles it to look like a dropdown
    • emits GetSuggestions signal with packet format {engineName, searchString, remoteTimeout}
    • listens for ContentSearchService events on window
      • note, /browser/base/content/content.js contains a ContentSearchMediator which:
        • listens for ContentSearchClient and ContentSearch signals
        • checks them against some kind of whitelist (need to look more deeply at this)
        • if ok, republishes them as ContentSearchService events
  • netwerk/base/nsIBrowserSearchService.idl

    • C++ interface defining a remote service facade
  • toolkit/components/search/nsSearchService.js

    • basically a service facade, abstracts an OpenSearch endpoint
    • instantiates an Engine, sends requests, returns responses to caller
    • maintains a sorted array of Engines
    • also includes engineMetadataService (modifies & saves engine attributes to disk), engineUpdateService (checks for engine definition updates, modifies local Engines as needed)
  • toolkit/components/search/SearchSuggestionController.js

    • helper module, code comments say it was factored out of nsSearchSuggestions to allow multiple consumers to request & display search suggestions from a given Engine (implementation of nsISearchEngine)
    • caches form history results
    • since it provides caching & recent search history, designed to use one of these per textbox
    • searches & returns both recent history and remote search suggestions
    • interesting methods:
      • SearchSuggestionController::fetch - for each search, look it up in form history cache & fetch from remote suggestion service, if enabled
      • SearchSuggestionController::_fetchFormHistory - get an nsIAutoCompleteSearch implementation, call startSearch on it, handle response
      • SearchSuggestionController::_fetchRemote - given an engine (nsISearchEngine), get the URL via engine.getSubmission, then create & send an XHR
  • toolkit/components/search/nsSearchSuggestions.js

    • defines SuggestAutoComplete, base implementation of nsIAutoCompleteSearch
    • I think (not sure) input from the front-end is handled by nsSearchSuggestionController
    • sends output to front-end by:
      • creating & returning a FormAutoCompleteResult containing the result data
      • sending it to the caller (who must implement nsIAutoCompleteObserver)
    • fetches results from search service accessed via Services.search
    • search results are collected/aggregated by the SearchSuggestionController

Places Database

browser chrome automation / automated testing

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