For me, there is NO difference between Firefox and Emacs. They provide useful APIs, nothing more.

Three years ago, I wrote Use firefox in Emacs way to demo how to convert Firefox into Emacs by Keynsail.

A year ago I published Hello Ivy-mode, bye Helm to prove how powerful Ivy-mode is by using its API ivy-read .

Keysnail has similar javascript API prompt.selector and it's as powerful as ivy-read if not more powerful.

For example, you can insert below snippet into ~/.keysnail.js and press ",hh" or "C-c C-h" to query browse history:

function searchHistory(evt, arg) { function timeSince(now, date) { var seconds = Math.floor((now - date) / 1000); var interval = Math.floor(seconds / 31536000); if (interval > 1) { return interval + " years"; } interval = Math.floor(seconds / 2592000); if (interval > 1) { return interval + " months"; } interval = Math.floor(seconds / 86400); if (interval > 1) { return interval + " days"; } interval = Math.floor(seconds / 3600); if (interval > 1) { return interval + " hours"; } interval = Math.floor(seconds / 60); if (interval > 1) { return interval + " minutes"; } return Math.floor(seconds) + " seconds"; } function searchWithKeyword(q) { var collection = (function() { //search option var options = PlacesUtils.history.getNewQueryOptions(); options.maxResults = 4096; options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; //options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_DESCENDING; options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING; options.includeHidden = true; //search query var query = PlacesUtils.history.getNewQuery(); // read keyworld if(q && q !== '') { query.searchTerms = q; } var result = PlacesUtils.history.executeQuery(query, options); var root = result.root; var collection = []; var now = new Date().getTime(); var siteNode; root.containerOpen = true; for (var i = 0; i < root.childCount; i++) { // siteNode => nsINavHistoryResultNode siteNode = root.getChild(i); collection.push([siteNode.icon,siteNode.title,siteNode.uri, siteNode.time/1000]); } collection.sort(function(a, b) { return b[3]-a[3]; }); // reformat the time for (i = 0; i < collection.length; i++) { collection[i][3] = timeSince(now, collection[i][3]) + ' ago'; } root.containerOpen = false; return collection; })(); prompt.selector({ message : "Search history"+ (q && q !== ''? (' @'+q +':') : ':' ), collection : collection, flags : [ICON | IGNORE, 0, 0, 0], header : ["Title", "Url", "Last visited"], width : [30, 60, 10], callback: function (i) { if (i >= 0) { openUILinkIn(collection[i][2], "tab"); } }, onFinish: function() { gBrowser.focus(); _content.focus(); } }); } prompt.finish(true); prompt.read('Keyword to search history?', searchWithKeyword, null, null, null, 0, "history_search"); // searchWithKeyword('test'); } key.setViewKey([',', 'h', 'h'], searchHistory, "Search history"); key.setGlobalKey(['C-c', 'C-h'], searchHistory, "Search history");

Here is my complete .keysnail.js.