2018-09-13 19:38:07 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var EXPORTED_SYMBOLS = ["UrlbarView"];
|
|
|
|
|
2018-10-02 17:22:29 +03:00
|
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
|
|
UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
|
|
|
|
});
|
|
|
|
|
2018-09-13 19:38:07 +03:00
|
|
|
/**
|
|
|
|
* Receives and displays address bar autocomplete results.
|
|
|
|
*/
|
|
|
|
class UrlbarView {
|
|
|
|
/**
|
|
|
|
* @param {UrlbarInput} urlbar
|
|
|
|
* The UrlbarInput instance belonging to this UrlbarView instance.
|
|
|
|
*/
|
|
|
|
constructor(urlbar) {
|
|
|
|
this.urlbar = urlbar;
|
|
|
|
this.panel = urlbar.panel;
|
2018-09-20 16:07:18 +03:00
|
|
|
this.controller = urlbar.controller;
|
2018-09-13 19:38:07 +03:00
|
|
|
this.document = urlbar.panel.ownerDocument;
|
|
|
|
this.window = this.document.defaultView;
|
|
|
|
|
|
|
|
this._mainContainer = this.panel.querySelector(".urlbarView-body-inner");
|
|
|
|
this._rows = this.panel.querySelector(".urlbarView-results");
|
|
|
|
|
|
|
|
// For the horizontal fade-out effect, set the overflow attribute on result
|
|
|
|
// rows when they overflow.
|
|
|
|
this._rows.addEventListener("overflow", event => {
|
|
|
|
if (event.target.classList.contains("urlbarView-row-inner")) {
|
|
|
|
event.target.toggleAttribute("overflow", true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this._rows.addEventListener("underflow", event => {
|
|
|
|
if (event.target.classList.contains("urlbarView-row-inner")) {
|
|
|
|
event.target.toggleAttribute("overflow", false);
|
|
|
|
}
|
|
|
|
});
|
2018-09-20 16:07:18 +03:00
|
|
|
|
|
|
|
this.controller.addQueryListener(this);
|
2018-09-13 19:38:07 +03:00
|
|
|
}
|
|
|
|
|
2018-11-17 11:46:35 +03:00
|
|
|
get oneOffSearchButtons() {
|
|
|
|
return this._oneOffSearchButtons ||
|
|
|
|
(this._oneOffSearchButtons =
|
|
|
|
new this.window.SearchOneOffs(this.panel.querySelector(".search-one-offs")));
|
|
|
|
}
|
|
|
|
|
2018-09-13 19:38:07 +03:00
|
|
|
/**
|
|
|
|
* Opens the autocomplete results popup.
|
|
|
|
*/
|
|
|
|
open() {
|
|
|
|
this.panel.removeAttribute("hidden");
|
|
|
|
|
|
|
|
let panelDirection = this.panel.style.direction;
|
|
|
|
if (!panelDirection) {
|
|
|
|
panelDirection = this.panel.style.direction =
|
|
|
|
this.window.getComputedStyle(this.urlbar.textbox).direction;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the panel span the width of the window.
|
|
|
|
let documentRect =
|
|
|
|
this._getBoundsWithoutFlushing(this.document.documentElement);
|
|
|
|
let width = documentRect.right - documentRect.left;
|
|
|
|
this.panel.setAttribute("width", width);
|
|
|
|
|
|
|
|
// Subtract two pixels for left and right borders on the panel.
|
|
|
|
this._mainContainer.style.maxWidth = (width - 2) + "px";
|
|
|
|
|
2018-11-17 11:46:35 +03:00
|
|
|
// TODO: Search one off buttons are a stub right now.
|
|
|
|
// We'll need to set them up properly.
|
|
|
|
this.oneOffSearchButtons;
|
|
|
|
|
2018-09-13 19:38:07 +03:00
|
|
|
this.panel.openPopup(this.urlbar.textbox.closest("toolbar"), "after_end", 0, -1);
|
|
|
|
|
|
|
|
this._rows.firstElementChild.toggleAttribute("selected", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the autocomplete results popup.
|
|
|
|
*/
|
|
|
|
close() {
|
2018-10-12 19:13:42 +03:00
|
|
|
this.panel.hidePopup();
|
2018-09-13 19:38:07 +03:00
|
|
|
}
|
|
|
|
|
2018-09-20 16:07:18 +03:00
|
|
|
// UrlbarController listener methods.
|
|
|
|
onQueryStarted(queryContext) {
|
|
|
|
this._rows.textContent = "";
|
|
|
|
}
|
|
|
|
|
2018-11-06 00:54:09 +03:00
|
|
|
onQueryCancelled(queryContext) {
|
|
|
|
// Nothing.
|
|
|
|
}
|
|
|
|
|
|
|
|
onQueryFinished(queryContext) {
|
|
|
|
// Nothing.
|
|
|
|
}
|
|
|
|
|
2018-09-20 16:07:18 +03:00
|
|
|
onQueryResults(queryContext) {
|
2018-10-02 17:22:29 +03:00
|
|
|
// XXX For now, clear the results for each set received. We should really
|
|
|
|
// be updating the existing list.
|
|
|
|
this._rows.textContent = "";
|
2018-10-12 19:13:42 +03:00
|
|
|
this._queryContext = queryContext;
|
|
|
|
for (let resultIndex in queryContext.results) {
|
|
|
|
this._addRow(resultIndex);
|
2018-09-20 16:07:18 +03:00
|
|
|
}
|
|
|
|
this.open();
|
|
|
|
}
|
|
|
|
|
2018-09-13 19:38:07 +03:00
|
|
|
// Private methods below.
|
|
|
|
|
|
|
|
_getBoundsWithoutFlushing(element) {
|
|
|
|
return this.window.windowUtils.getBoundsWithoutFlushing(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
_createElement(name) {
|
|
|
|
return this.document.createElementNS("http://www.w3.org/1999/xhtml", name);
|
|
|
|
}
|
|
|
|
|
2018-10-12 19:13:42 +03:00
|
|
|
_addRow(resultIndex) {
|
|
|
|
let result = this._queryContext.results[resultIndex];
|
2018-09-13 19:38:07 +03:00
|
|
|
let item = this._createElement("div");
|
|
|
|
item.className = "urlbarView-row";
|
2018-10-12 19:13:42 +03:00
|
|
|
item.addEventListener("click", this);
|
|
|
|
item.setAttribute("resultIndex", resultIndex);
|
2018-10-02 17:22:29 +03:00
|
|
|
if (result.type == UrlbarUtils.MATCH_TYPE.TAB_SWITCH) {
|
2018-09-13 19:38:07 +03:00
|
|
|
item.setAttribute("action", "switch-to-tab");
|
|
|
|
}
|
|
|
|
|
|
|
|
let content = this._createElement("span");
|
|
|
|
content.className = "urlbarView-row-inner";
|
|
|
|
item.appendChild(content);
|
|
|
|
|
|
|
|
let actionIcon = this._createElement("span");
|
|
|
|
actionIcon.className = "urlbarView-action-icon";
|
|
|
|
content.appendChild(actionIcon);
|
|
|
|
|
|
|
|
let favicon = this._createElement("span");
|
|
|
|
favicon.className = "urlbarView-favicon";
|
|
|
|
content.appendChild(favicon);
|
|
|
|
|
|
|
|
let title = this._createElement("span");
|
|
|
|
title.className = "urlbarView-title";
|
2018-11-21 18:29:44 +03:00
|
|
|
title.textContent = result.title || result.payload.url;
|
2018-09-13 19:38:07 +03:00
|
|
|
content.appendChild(title);
|
|
|
|
|
|
|
|
let secondary = this._createElement("span");
|
|
|
|
secondary.className = "urlbarView-secondary";
|
2018-10-02 17:22:29 +03:00
|
|
|
if (result.type == UrlbarUtils.MATCH_TYPE.TAB_SWITCH) {
|
2018-09-13 19:38:07 +03:00
|
|
|
secondary.classList.add("urlbarView-action");
|
|
|
|
secondary.textContent = "Switch to Tab";
|
|
|
|
} else {
|
|
|
|
secondary.classList.add("urlbarView-url");
|
2018-11-21 18:29:44 +03:00
|
|
|
secondary.textContent = result.payload.url;
|
2018-09-13 19:38:07 +03:00
|
|
|
}
|
|
|
|
content.appendChild(secondary);
|
|
|
|
|
|
|
|
this._rows.appendChild(item);
|
|
|
|
}
|
2018-10-12 19:13:42 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Passes DOM events for the view to the _on_<event type> methods.
|
|
|
|
* @param {Event} event
|
|
|
|
* DOM event from the <view>.
|
|
|
|
*/
|
|
|
|
handleEvent(event) {
|
|
|
|
let methodName = "_on_" + event.type;
|
|
|
|
if (methodName in this) {
|
|
|
|
this[methodName](event);
|
|
|
|
} else {
|
|
|
|
throw "Unrecognized urlbar event: " + event.type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_on_click(event) {
|
|
|
|
let row = event.target;
|
|
|
|
while (!row.classList.contains("urlbarView-row")) {
|
|
|
|
row = row.parentNode;
|
|
|
|
}
|
|
|
|
let resultIndex = row.getAttribute("resultIndex");
|
|
|
|
let result = this._queryContext.results[resultIndex];
|
|
|
|
if (result) {
|
|
|
|
this.urlbar.resultSelected(event, result);
|
|
|
|
}
|
|
|
|
this.close();
|
|
|
|
}
|
2018-09-13 19:38:07 +03:00
|
|
|
}
|