Bug 480350 - Show currently loaded URIs in location bar autocomplete results, allow switching to the tab. r=gavin,mak sr=mconnor

This commit is contained in:
Blair McBride 2010-03-26 22:59:02 +01:00
Родитель 3ad3d7614a
Коммит 1cfd933bbd
31 изменённых файлов: 839 добавлений и 52 удалений

Просмотреть файл

@ -240,6 +240,7 @@ pref("browser.urlbar.delay", 50);
pref("browser.urlbar.restrict.history", "^");
pref("browser.urlbar.restrict.bookmark", "*");
pref("browser.urlbar.restrict.tag", "+");
pref("browser.urlbar.restrict.openpage", "%");
pref("browser.urlbar.restrict.typed", "~");
pref("browser.urlbar.match.title", "#");
pref("browser.urlbar.match.url", "@");
@ -247,7 +248,8 @@ pref("browser.urlbar.match.url", "@");
// The default behavior for the urlbar can be configured to use any combination
// of the restrict or match filters with each additional filter restricting
// more (intersection). Add the following values to set the behavior as the
// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed
// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
// 64: javascript, 128: tabs
// E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
// 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
pref("browser.urlbar.default.behavior", 0);

Просмотреть файл

@ -51,6 +51,26 @@ toolbar[mode="icons"] > #reload-button[displaystop] {
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
}
/* Some child nodes want to be ordered based on the locale's direction, while
everything else should be ltr. */
#urlbar:-moz-locale-dir(rtl) > .autocomplete-textbox-container > .textbox-input-box {
direction: rtl;
}
#urlbar html|*.autocomplete-textbox {
direction: ltr;
}
/* For results that are actions, their description text is shown instead of
the URL - this needs to follow the locale's direction, unlike URLs. */
richlistitem[type="action"]:-moz-locale-dir(rtl) > .ac-url-box {
direction: rtl;
}
#urlbar:not([actiontype]) > #urlbar-display {
display: none;
}
#wrapper-urlbar-container > #urlbar-container > #urlbar {
-moz-user-input: disabled;
cursor: -moz-grab;

Просмотреть файл

@ -3805,7 +3805,6 @@ var XULBrowserWindow = {
overLink: "",
startTime: 0,
statusText: "",
lastURI: null,
isBusy: false,
_progressCollapseTimer: 0,
@ -3869,7 +3868,6 @@ var XULBrowserWindow = {
delete this.statusTextField;
delete this.securityButton;
delete this.statusText;
delete this.lastURI;
},
setJSStatus: function (status) {
@ -4084,7 +4082,6 @@ var XULBrowserWindow = {
nBox.removeTransientNotifications();
}
}
selectedBrowser.lastURI = aLocationURI;
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
@ -7589,3 +7586,42 @@ var LightWeightThemeWebInstaller = {
node.baseURI);
}
}
function switchToTabHavingURI(aURI) {
function switchIfURIInWindow(aWindow) {
if (!("gBrowser" in aWindow))
return false;
let browsers = aWindow.gBrowser.browsers;
for (let i = 0; i < browsers.length; i++) {
let browser = browsers[i];
if (browser.currentURI.equals(aURI)) {
gURLBar.handleRevert();
aWindow.focus();
aWindow.gBrowser.tabContainer.selectedIndex = i;
return true;
}
}
return false;
}
// This can be passed either nsIURI or a string.
if (!(aURI instanceof Ci.nsIURI))
aURI = makeURI(aURI);
// Prioritise this window.
if (switchIfURIInWindow(window))
return true;
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let browserWin = winEnum.getNext();
// Skip closed (but not yet destroyed) windows,
// and the current window (which was checked earlier).
if (browserWin.closed || browserWin == window)
continue;
if (switchIfURIInWindow(browserWin))
return true;
}
// No opened tab has that url.
return false;
}

Просмотреть файл

@ -404,6 +404,7 @@
noneplaceholder="&urlbar.none.emptyText;"
type="autocomplete"
autocompletesearch="history"
autocompletesearchparam="enable-actions"
autocompletepopup="PopupAutoCompleteRichResult"
completeselectedindex="true"
tabscrolling="true"
@ -442,6 +443,7 @@
</hbox>
</hbox>
</box>
<label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
<hbox id="urlbar-icons">
<button type="menu"
style="-moz-user-focus: none"

Просмотреть файл

@ -96,6 +96,10 @@
Components.classes["@mozilla.org/browser/favicon-service;1"]
.getService(Components.interfaces.nsIFaviconService);
</field>
<field name="mBrowserHistory" readonly="true">
Components.classes["@mozilla.org/browser/nav-history-service;1"]
.getService(Components.interfaces.nsIBrowserHistory);
</field>
<field name="mTabBox" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
</field>
@ -452,15 +456,30 @@
// changing location, clear out the missing plugins list
this.mBrowser.missingPlugins = null;
if (this.mBlank)
return;
var browserHistory = this.mTabBrowser.mBrowserHistory;
if ("lastURI" in this.mBrowser && this.mBrowser.lastURI)
browserHistory.unregisterOpenPage(this.mBrowser.lastURI);
browserHistory.registerOpenPage(aLocation);
if (this.mTabBrowser.mCurrentTab == this.mTab) {
for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
let p = this.mTabBrowser.mProgressListeners[i];
if (!this.mBlank) {
if (this.mTabBrowser.mCurrentTab == this.mTab) {
for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
let p = this.mTabBrowser.mProgressListeners[i];
if (p)
try {
p.onLocationChange(aWebProgress, aRequest, aLocation);
} catch (e) {
// don't inhibit other listeners
Components.utils.reportError(e);
}
}
}
for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
let p = this.mTabBrowser.mTabsProgressListeners[i];
if (p)
try {
p.onLocationChange(aWebProgress, aRequest, aLocation);
p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
} catch (e) {
// don't inhibit other listeners
Components.utils.reportError(e);
@ -468,16 +487,8 @@
}
}
for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
let p = this.mTabBrowser.mTabsProgressListeners[i];
if (p)
try {
p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
} catch (e) {
// don't inhibit other listeners
Components.utils.reportError(e);
}
}
this.mBrowser.lastURI = aLocation;
},
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
@ -1419,6 +1430,10 @@
filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
this.mTabListeners[aTab._tPos].destroy();
let closedBrowser = this.getBrowserForTab(aTab);
if (closedBrowser.currentURI)
this.mBrowserHistory.unregisterOpenPage(closedBrowser.currentURI);
// We are no longer the primary content area.
browser.setAttribute("type", "content-targetable");
@ -2260,7 +2275,9 @@
<destructor>
<![CDATA[
for (var i = 0; i < this.mTabListeners.length; ++i) {
this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
let browser = this.getBrowserAtIndex(i);
this.mBrowserHistory.unregisterOpenPage(browser.currentURI);
browser.webProgress.removeProgressListener(this.mTabFilters[i]);
this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
this.mTabFilters[i] = null;
this.mTabListeners[i].destroy();

Просмотреть файл

@ -150,6 +150,7 @@ _BROWSER_FILES = \
alltabslistener.html \
zoom_test.html \
dummy_page.html \
browser_tabMatchesInAwesomebar.js \
$(NULL)
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))

Просмотреть файл

@ -0,0 +1,231 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:set ts=2 sw=2 sts=2 et:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places Test Code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Blair McBride <bmcbride@mozilla.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const TEST_URL_BASES = [
"http://example.org/browser/browser/base/content/test/dummy_page.html#tabmatch",
"http://example.org/browser/browser/base/content/test/moz.png#tabmatch"
];
var gPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
var gTabWaitCount = 0;
var gTabCounter = 0;
var gTestSteps = [
function() {
info("Running step 1");
for (let i = 0; i < 10; i++) {
let tab = gBrowser.addTab();
loadTab(tab, TEST_URL_BASES[0] + (++gTabCounter));
}
},
function() {
info("Running step 2");
gBrowser.selectTabAtIndex(1);
gBrowser.removeCurrentTab();
gBrowser.selectTabAtIndex(1);
gBrowser.removeCurrentTab();
for (let i = 1; i < gBrowser.tabs.length; i++)
loadTab(gBrowser.tabs[i], TEST_URL_BASES[1] + (++gTabCounter));
},
function() {
info("Running step 3");
for (let i = 1; i < gBrowser.tabs.length; i++)
loadTab(gBrowser.tabs[i], TEST_URL_BASES[0] + gTabCounter);
},
function() {
info("Running step 4");
let ps = Services.prefs;
ps.setBoolPref("browser.privatebrowsing.keep_current_session", true);
ps.setBoolPref("browser.tabs.warnOnClose", false);
gPrivateBrowsing.privateBrowsingEnabled = true;
executeSoon(function() {
ensure_opentabs_match_db();
nextStep();
});
},
function() {
info("Running step 5");
gPrivateBrowsing.privateBrowsingEnabled = false;
executeSoon(function() {
let ps = Services.prefs;
try {
ps.clearUserPref("browser.privatebrowsing.keep_current_session");
} catch (ex) {}
try {
ps.clearUserPref("browser.tabs.warnOnClose");
} catch (ex) {}
ensure_opentabs_match_db();
nextStep()
});
}
];
function test() {
waitForExplicitFinish();
nextStep();
}
function loadTab(tab, url) {
tab.linkedBrowser.addEventListener("load", function (event) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
if (--gTabWaitCount > 0)
return;
is(gTabWaitCount, 0,
"sanity check, gTabWaitCount should not be decremented below 0");
try {
ensure_opentabs_match_db();
} catch (e) {
ok(false, "exception from ensure_openpages_match_db: " + e);
}
executeSoon(nextStep);
}, true);
gTabWaitCount++;
tab.linkedBrowser.loadURI(url);
}
function nextStep() {
if (gTestSteps.length == 0) {
while (gBrowser.tabs.length > 1) {
gBrowser.selectTabAtIndex(1);
gBrowser.removeCurrentTab();
}
waitForClearHistory(finish);
return;
}
var stepFunc = gTestSteps.shift();
stepFunc();
}
function ensure_opentabs_match_db() {
var tabs = {};
var winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let browserWin = winEnum.getNext();
// skip closed-but-not-destroyed windows
if (browserWin.closed)
continue;
for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) {
let browser = browserWin.gBrowser.getBrowserAtIndex(i);
let url = browser.currentURI.spec;
if (!(url in tabs))
tabs[url] = 1;
else
tabs[url]++;
}
}
var db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.DBConnection;
try {
var stmt = db.createStatement(
"SELECT IFNULL(p_t.url, p.url) AS url, open_count, place_id " +
"FROM moz_openpages_temp " +
"LEFT JOIN moz_places p ON p.id=place_id " +
"LEFT JOIN moz_places_temp p_t ON p_t.id=place_id");
} catch (e) {
ok(false, "error creating db statement: " + e);
return;
}
var dbtabs = [];
try {
while (stmt.executeStep()) {
ok(stmt.row.url in tabs,
"url is in db, should be in tab: " + stmt.row.url);
is(tabs[stmt.row.url], stmt.row.open_count,
"db count (" + stmt.row.open_count + ") " +
"should match actual open tab count " +
"(" + tabs[stmt.row.url] + "): " + stmt.row.url);
dbtabs.push(stmt.row.url);
}
} finally {
stmt.finalize();
}
for (let url in tabs) {
// ignore URLs that should never be in the places db
if (!is_expected_in_db(url))
continue;
ok(dbtabs.indexOf(url) > -1,
"tab is open (" + tabs[url] + " times) and should recorded in db: " + url);
}
}
function is_expected_in_db(url) {
var uri = Services.io.newURI(url, null, null);
return PlacesUtils.history.canAddURI(uri);
}
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback) {
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
let observer = {
observe: function(aSubject, aTopic, aData) {
Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
aCallback();
}
};
Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
}

Просмотреть файл

@ -61,8 +61,8 @@
this.inputField.addEventListener("mousedown", this, false);
this.inputField.addEventListener("mousemove", this, false);
this.inputField.addEventListener("mouseout", this, false);
this.inputField.addEventListener("overflow", this, false);
this.inputField.addEventListener("underflow", this, false);
this.inputField.addEventListener("overflow", this, false);
this.inputField.addEventListener("underflow", this, false);
]]></constructor>
<destructor><![CDATA[
@ -72,10 +72,45 @@
this.inputField.removeEventListener("mousedown", this, false);
this.inputField.removeEventListener("mousemove", this, false);
this.inputField.removeEventListener("mouseout", this, false);
this.inputField.removeEventListener("overflow", this, false);
this.inputField.removeEventListener("underflow", this, false);
this.inputField.removeEventListener("overflow", this, false);
this.inputField.removeEventListener("underflow", this, false);
]]></destructor>
<field name="_value"></field>
<!--
onBeforeValueGet is called by the base-binding's .value getter.
It can return an object with a "value" property, to override the
return value of the getter.
-->
<method name="onBeforeValueGet">
<body><![CDATA[
if (this.hasAttribute("actiontype"))
return {value: this._value};
return null;
]]></body>
</method>
<!--
onBeforeValueSet is called by the base-binding's .value setter.
It should return the value that the setter should use.
-->
<method name="onBeforeValueSet">
<parameter name="aValue"/>
<body><![CDATA[
this._value = aValue;
var returnValue = aValue;
var action = this._parseActionUrl(aValue);
if (action) {
returnValue = action.param;
this.setAttribute("actiontype", action.type);
} else {
this.removeAttribute("actiontype");
}
return returnValue;
]]></body>
</method>
<method name="handleRevert">
<body><![CDATA[
var isScrolling = this.popupOpen;
@ -108,6 +143,13 @@
if (!url)
return;
var action = this._parseActionUrl(url);
if (action) {
if (action.type == "switchtab")
switchToTabHavingURI(action.param);
return;
}
this.value = url;
gBrowser.userTypedValue = url;
try {
@ -216,12 +258,12 @@
return [url, postData.value];
]]></body>
</method>
<field name="_contentIsCropped">false</field>
<field name="_contentIsCropped">false</field>
<method name="_initURLTooltip">
<body><![CDATA[
if (this.focused || !this._contentIsCropped)
if (this.focused || !this._contentIsCropped)
return;
if (this._tooltipTimer)
clearTimeout(this._tooltipTimer);
@ -389,13 +431,13 @@
case "mouseout":
this._hideURLTooltip();
break;
case "overflow":
this._contentIsCropped = true;
break;
case "underflow":
this._contentIsCropped = false;
this._hideURLTooltip();
break;
case "overflow":
this._contentIsCropped = true;
break;
case "underflow":
this._contentIsCropped = false;
this._hideURLTooltip();
break;
}
]]></body>
</method>
@ -442,6 +484,18 @@
this.placeholder = this.getAttribute(type + "placeholder");
]]></body>
</method>
<method name="_parseActionUrl">
<parameter name="aUrl"/>
<body><![CDATA[
if (!/^moz-action:/.test(aUrl))
return null;
// url is in the format moz-action:ACTION,PARAM
let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
return {type: action, param: param};
]]></body>
</method>
</implementation>
<handlers>
@ -613,6 +667,15 @@
this.closePopup();
controller.handleEscape();
// Check if this is meant to be an action
let action = this.mInput._parseActionUrl(url);
if (action) {
if (action.type == "switchtab")
url = action.param;
else
return;
}
// respect the usual clicking subtleties
openUILink(url, aEvent);
}
@ -640,4 +703,5 @@
</implementation>
</binding>
</bindings>

Просмотреть файл

@ -207,6 +207,7 @@
<!ENTITY urlbar.history.emptyText "Search History">
<!ENTITY urlbar.none.emptyText "Type a Web address">
<!ENTITY urlbar.accesskey "d">
<!ENTITY urlbar.switchToTab.label "Switch to tab:">
<!--
Comment duplicated from browser-sets.inc:

Двоичные данные
browser/themes/gnomestripe/browser/actionicon-tab.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 293 B

Просмотреть файл

@ -800,6 +800,17 @@ toolbar[iconsize="small"] #fullscreen-button {
display: none;
}
#urlbar-display {
margin-top: -2px;
margin-bottom: -2px;
padding-top: 3px;
padding-bottom: 2px;
-moz-padding-end: 3px;
color: GrayText;
-moz-border-end: 1px solid #AAA;
-moz-margin-end: 3px;
}
#PopupAutoComplete,
#PopupAutoCompleteRichResult {
direction: ltr !important;
@ -998,6 +1009,10 @@ toolbar[iconsize="small"] #fullscreen-button {
color: -moz-nativehyperlinktext;
}
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
}
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
color: GrayText;
}

Просмотреть файл

@ -6,6 +6,7 @@ browser.jar:
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutCertError.css (aboutCertError.css)
skin/classic/browser/actionicon-tab.png
* skin/classic/browser/browser.css (browser.css)
* skin/classic/browser/engineManager.css (engineManager.css)
skin/classic/browser/fullscreen-video.css

Двоичные данные
browser/themes/pinstripe/browser/actionicon-tab.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 293 B

Просмотреть файл

@ -868,6 +868,17 @@ toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarke
width: 10px;
}
#urlbar-display {
margin-top: -2px;
margin-bottom: -2px;
padding-top: 3px;
padding-bottom: 2px;
-moz-padding-end: 3px;
color: GrayText;
-moz-border-end: 1px solid #AAA;
-moz-margin-end: 3px;
}
#PopupAutoCompleteRichResult {
direction: ltr !important;
margin-top: 2px;
@ -929,6 +940,10 @@ richlistitem[selected="true"][current="true"] > hbox > .ac-result-type-bookmark,
font-size: 0.95em;
}
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
}
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
color: GrayText;
}

Просмотреть файл

@ -5,6 +5,7 @@ browser.jar:
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutCertError.css (aboutCertError.css)
skin/classic/browser/actionicon-tab.png
* skin/classic/browser/browser.css (browser.css)
* skin/classic/browser/engineManager.css (engineManager.css)
skin/classic/browser/feed-icons.png

Двоичные данные
browser/themes/winstripe/browser/actionicon-tab.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 293 B

Просмотреть файл

@ -610,6 +610,17 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
-moz-box-align: stretch;
}
#urlbar-display {
margin-top: -2px;
margin-bottom: -2px;
padding-top: 3px;
padding-bottom: 2px;
-moz-padding-end: 3px;
color: GrayText;
-moz-border-end: 1px solid #AAA;
-moz-margin-end: 3px;
}
/* identity box */
#identity-box {
@ -766,6 +777,10 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
color: #006600;
}
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
}
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
color: GrayText;
}

Просмотреть файл

@ -8,6 +8,7 @@ browser.jar:
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon.png)
skin/classic/browser/aboutCertError.css (aboutCertError.css)
skin/classic/browser/actionicon-tab.png
* skin/classic/browser/browser.css (browser.css)
* skin/classic/browser/engineManager.css (engineManager.css)
skin/classic/browser/fullscreen-video.css
@ -93,6 +94,7 @@ browser.jar:
* skin/classic/aero/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
skin/classic/aero/browser/aboutCertError.css (aboutCertError.css)
skin/classic/aero/browser/actionicon-tab.png (actionicon-tab.png)
* skin/classic/aero/browser/browser.css (browser-aero.css)
* skin/classic/aero/browser/engineManager.css (engineManager.css)
skin/classic/aero/browser/fullscreen-video.css

Просмотреть файл

@ -107,4 +107,13 @@ interface mozIPlacesAutoComplete : nsISupports
* Search javascript: URLs.
*/
const long BEHAVIOR_JAVASCRIPT = 1 << 6;
/**
* Search for pages that have been marked as being opened, such as a tab
* in a tabbrowser, via:
* nsIBrowserHistory.registerOpenPage(url)
*
* @see nsIBrowserHistory.idl
*/
const long BEHAVIOR_OPENPAGE = 1 << 7;
};

Просмотреть файл

@ -42,7 +42,7 @@
#include "nsISupports.idl"
#include "nsIGlobalHistory2.idl"
[scriptable, uuid(124a8db3-59da-48ac-a563-6dcf58e035b4)]
[scriptable, uuid(540aca25-1e01-467f-b24c-df89cbe40f8d)]
interface nsIBrowserHistory : nsIGlobalHistory2
{
/**
@ -156,4 +156,18 @@ interface nsIBrowserHistory : nsIGlobalHistory2
* the user (for example by clicking on it).
*/
void markPageAsFollowedLink(in nsIURI aURI);
/**
* Mark a page as being currently open.
*/
void registerOpenPage(in nsIURI aURI);
/**
* Mark a page as no longer being open (either by closing the window or tab,
* or by navigating away from that page).
*
* Note that when Private Browsing mode is entered/exited, pages need to be
* manually unregistered/registered.
*/
void unregisterOpenPage(in nsIURI aURI);
};

Просмотреть файл

@ -991,6 +991,13 @@ nsNavHistory::InitTempTables()
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
// moz_openpages_temp
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_OPENPAGES_TEMP);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(CREATE_REMOVEOPENPAGE_CLEANUP_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -1217,6 +1224,27 @@ nsNavHistory::InitStatements()
"WHERE url = ?2"),
getter_AddRefs(mDBSetPlaceTitle));
NS_ENSURE_SUCCESS(rv, rv);
// mDBRegisterOpenPage
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT OR REPLACE INTO moz_openpages_temp (place_id, open_count) "
"VALUES (?1, "
"IFNULL("
"(SELECT open_count + 1 FROM moz_openpages_temp WHERE place_id = ?1), "
"1"
")"
")"),
getter_AddRefs(mDBRegisterOpenPage));
NS_ENSURE_SUCCESS(rv, rv);
// mDBUnregisterOpenPage
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_openpages_temp "
"SET open_count = open_count - 1 "
"WHERE place_id = ?1"),
getter_AddRefs(mDBUnregisterOpenPage));
NS_ENSURE_SUCCESS(rv, rv);
// mDBVisitsForFrecency
// NOTE: This is not limited to visits with "visit_type NOT IN (0,4,7,8)"
// because otherwise mDBVisitsForFrecency would return no visits
@ -5035,6 +5063,72 @@ nsNavHistory::MarkPageAsFollowedLink(nsIURI *aURI)
}
NS_IMETHODIMP
nsNavHistory::RegisterOpenPage(nsIURI* aURI)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
NS_ENSURE_ARG(aURI);
// Don't add any pages while in Private Browsing mode, so as to avoid leaking
// information about other windows that might otherwise stay hidden
// and private.
if (InPrivateBrowsingMode())
return NS_OK;
PRBool canAdd = PR_FALSE;
nsresult rv = CanAddURI(aURI, &canAdd);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 placeId;
// Note: If the URI has never been added to history (but can be added),
// LAZY_ADD will cause this to add an orphan page, until the visit is added.
rv = GetUrlIdFor(aURI, &placeId, canAdd);
NS_ENSURE_SUCCESS(rv, rv);
if (placeId == 0)
return NS_OK;
mozStorageStatementScoper scoper(mDBRegisterOpenPage);
rv = mDBRegisterOpenPage->BindInt64Parameter(0, placeId);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBRegisterOpenPage->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsNavHistory::UnregisterOpenPage(nsIURI* aURI)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
NS_ENSURE_ARG(aURI);
// Entering Private Browsing mode will unregister all open pages, therefore
// there shouldn't be anything in the moz_openpages_temp table. So we can stop
// now without doing any unnecessary work.
if (InPrivateBrowsingMode())
return NS_OK;
PRInt64 placeId;
nsresult rv = GetUrlIdFor(aURI, &placeId, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
if (placeId == 0)
return NS_OK;
mozStorageStatementScoper scoper(mDBUnregisterOpenPage);
rv = mDBUnregisterOpenPage->BindInt64Parameter(0, placeId);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBUnregisterOpenPage->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// nsNavHistory::SetCharsetForURI
//
// Sets the character-set for a URI.
@ -8154,6 +8248,8 @@ nsNavHistory::FinalizeStatements() {
mDBUpdateFrecencyAndHidden,
mDBGetPlaceVisitStats,
mDBFullVisitCount,
mDBRegisterOpenPage,
mDBUnregisterOpenPage,
};
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {

Просмотреть файл

@ -427,6 +427,8 @@ protected:
nsCOMPtr<mozIStorageStatement> mDBGetTags; // used by GetTags
nsCOMPtr<mozIStorageStatement> mDBGetItemsWithAnno; // used by AutoComplete::StartSearch and FilterResultSet
nsCOMPtr<mozIStorageStatement> mDBSetPlaceTitle; // used by SetPageTitleInternal
nsCOMPtr<mozIStorageStatement> mDBRegisterOpenPage; // used by RegisterOpenPage
nsCOMPtr<mozIStorageStatement> mDBUnregisterOpenPage; // used by UnregisterOpenPage
// these are used by VisitIdToResultNode for making new result nodes from IDs
// Consumers need to use the getters since these statements are lazily created

Просмотреть файл

@ -94,6 +94,7 @@ const kQueryIndexVisitCount = 6;
const kQueryIndexTyped = 7;
const kQueryIndexPlaceId = 8;
const kQueryIndexQueryType = 9;
const kQueryIndexOpenPageCount = 10;
// AutoComplete query type constants. Describes the various types of queries
// that we can process.
@ -217,9 +218,11 @@ function nsPlacesAutoComplete()
// Note: h.frecency is only selected because we need it for ordering.
function sql_base_fragment(aTableName) {
return "SELECT h.url, h.title, f.url, " + kBookTagSQLFragment + ", " +
"h.visit_count, h.typed, h.id, :query_type, h.frecency " +
"h.visit_count, h.typed, h.id, :query_type, t.open_count, " +
"h.frecency " +
"FROM " + aTableName + " h " +
"LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id " +
"LEFT OUTER JOIN moz_openpages_temp t ON t.place_id = h.id " +
"WHERE h.frecency <> 0 " +
"AND AUTOCOMPLETE_MATCH(:searchString, h.url, " +
"IFNULL(bookmark, h.title), tags, " +
@ -291,6 +294,13 @@ function nsPlacesAutoComplete()
);
});
XPCOMUtils.defineLazyGetter(this, "_openPagesQuery", function() {
let replacementText = "AND t.open_count > 0";
return this._db.createStatement(
SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g")
);
});
XPCOMUtils.defineLazyGetter(this, "_typedQuery", function() {
let replacementText = "AND h.typed = 1";
return this._db.createStatement(
@ -311,7 +321,7 @@ function nsPlacesAutoComplete()
kBookTagSQLFragment + ", " +
"IFNULL(h_t.visit_count, h.visit_count) AS c_visit_count, " +
"IFNULL(h_t.typed, h.typed) AS c_typed, " +
"IFNULL(h_t.id, h.id), :query_type, rank " +
"IFNULL(h_t.id, h.id), :query_type, t.open_count, rank " +
"FROM ( " +
"SELECT ROUND(MAX(((i.input = :search_string) + " +
"(SUBSTR(i.input, 1, LENGTH(:search_string)) = :search_string)) * " +
@ -323,6 +333,7 @@ function nsPlacesAutoComplete()
"LEFT JOIN moz_places h ON h.id = i.place_id " +
"LEFT JOIN moz_places_temp h_t ON h_t.id = i.place_id " +
"LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " +
"LEFT JOIN moz_openpages_temp t ON t.place_id = i.place_id " +
"WHERE c_url NOTNULL " +
"AND AUTOCOMPLETE_MATCH(:searchString, c_url, " +
"IFNULL(bookmark, c_title), tags, " +
@ -343,12 +354,13 @@ function nsPlacesAutoComplete()
best_favicon_for_revhost("moz_places") + "), b.parent, " +
"b.title, NULL, IFNULL(h_t.visit_count, h.visit_count), " +
"IFNULL(h_t.typed, h.typed), COALESCE(h_t.id, h.id, b.fk), " +
":query_type " +
":query_type, t.open_count " +
"FROM moz_keywords k " +
"JOIN moz_bookmarks b ON b.keyword_id = k.id " +
"LEFT JOIN moz_places AS h ON h.url = search_url " +
"LEFT JOIN moz_places_temp AS h_t ON h_t.url = search_url " +
"LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " +
"LEFT JOIN moz_openpages_temp t ON t.place_id = IFNULL(h_t.id, h.id) " +
"WHERE LOWER(k.keyword) = LOWER(:keyword) " +
"ORDER BY IFNULL(h_t.frecency, h.frecency) DESC"
);
@ -387,6 +399,9 @@ nsPlacesAutoComplete.prototype = {
this._currentSearchString =
this._fixupSearchText(this._originalSearchString.toLowerCase());
var searchParamParts = aSearchParam.split(" ");
this._enableActions = searchParamParts.indexOf("enable-actions") != -1;
this._listener = aListener;
let result = Cc["@mozilla.org/autocomplete/simple-result;1"].
createInstance(Ci.nsIAutoCompleteSimpleResult);
@ -519,6 +534,7 @@ nsPlacesAutoComplete.prototype = {
"_historyQuery",
"_bookmarkQuery",
"_tagsQuery",
"_openPagesQuery",
"_typedQuery",
"_adaptiveQuery",
"_keywordQuery",
@ -598,6 +614,7 @@ nsPlacesAutoComplete.prototype = {
delete this._usedPlaceIds;
delete this._pendingQuery;
this._secondPass = false;
this._enableActions = false;
},
/**
@ -680,13 +697,14 @@ nsPlacesAutoComplete.prototype = {
this._restrictBookmarkToken = safeGetter("restrict.bookmark", "*");
this._restrictTypedToken = safeGetter("restrict.typed", "~");
this._restrictTagToken = safeGetter("restrict.tag", "+");
this._restrictOpenPageToken = safeGetter("restrict.openpage", "%");
this._matchTitleToken = safeGetter("match.title", "#");
this._matchURLToken = safeGetter("match.url", "@");
this._defaultBehavior = safeGetter("default.behavior", 0);
// Further restrictions to apply for "empty searches" (i.e. searches for "").
// By default we use (HISTORY | TYPED) = 33.
// By default we use (HISTORY | TYPED | OPENPAGE) = 161.
this._emptySearchDefaultBehavior = this._defaultBehavior |
safeGetter("default.behavior.emptyRestriction", 33);
safeGetter("default.behavior.emptyRestriction", 161);
// Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE.
if (this._matchBehavior != MATCH_ANYWHERE &&
@ -727,6 +745,11 @@ nsPlacesAutoComplete.prototype = {
case this._restrictTagToken:
this._setBehavior("tag");
break;
case this._restrictOpenPageToken:
if (!this._enableActions)
continue;
this._setBehavior("openpage");
break;
case this._matchTitleToken:
this._setBehavior("title");
break;
@ -780,6 +803,7 @@ nsPlacesAutoComplete.prototype = {
this._hasBehavior("bookmark") ? this._bookmarkQuery :
this._hasBehavior("typed") ? this._typedQuery :
this._hasBehavior("history") ? this._historyQuery :
this._hasBehavior("openpage") ? this._openPagesQuery :
this._defaultQuery;
// Bind the needed parameters to the query so consumers can use it.
@ -880,6 +904,7 @@ nsPlacesAutoComplete.prototype = {
let entryBookmarkTitle = entryParentId ?
aRow.getResultByIndex(kQueryIndexBookmarkTitle) : null;
let entryTags = aRow.getResultByIndex(kQueryIndexTags) || "";
let openPageCount = aRow.getResultByIndex(kQueryIndexOpenPageCount) || 0;
// Always prefer the bookmark title unless it is empty
let title = entryBookmarkTitle || entryTitle;
@ -926,8 +951,22 @@ nsPlacesAutoComplete.prototype = {
style = "favicon";
}
// And finally add this to our results.
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
// If actions aren't enabled, avoid doing any additional work.
if (!this._enableActions) {
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
return true;
}
// Add a special entry for an open-page match.
if ((this._hasBehavior("openpage") || this._hasBehavior("everything")) &&
openPageCount > 0)
this._addToResults(entryId, "moz-action:switchtab," + escapedEntryURL, title, entryFavicon, "action");
// If restricting to only open-page matches, there should only be the
// switch-to-tab results.
if (!this._onlyHasBehavior("openpage"))
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
return true;
},
@ -981,15 +1020,32 @@ nsPlacesAutoComplete.prototype = {
* Determines if the specified AutoComplete behavior is set.
*
* @param aType
* The behavior type to test for.
* The behavior type to test for, or "everything" to test if no
* specific behavior has been set.
* @return true if the behavior is set, false otherwise.
*/
_hasBehavior: function PAC_hasBehavior(aType)
{
if (aType == "everything")
return this._behavior == 0;
return (this._behavior &
Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]);
},
/**
* Determines if the specified AutoComplete behavior is the only behavior set.
*
* @param aType
* The behavior type to test for.
* @return true if the behavior is set and no other behaviors are set,
* false otherwise.
*/
_onlyHasBehavior: function PAC_onlyHasBehavior(aType)
{
return (this._behavior ==
Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]);
},
/**
* Enables the desired AutoComplete behavior.
*

Просмотреть файл

@ -177,4 +177,11 @@
")" \
)
#define CREATE_MOZ_OPENPAGES_TEMP NS_LITERAL_CSTRING( \
"CREATE TEMP TABLE moz_openpages_temp (" \
" place_id INTEGER PRIMARY KEY" \
", open_count INTEGER" \
")" \
)
#endif // __nsPlacesTables_h__

Просмотреть файл

@ -94,8 +94,8 @@
/**
* This trigger allows for the deletion of a record in moz_places_view. It
* removes any entry in the temporary table, and any entry in the permanent
* table as well.
* removes any entry in the temporary table, the permanent table, and any
* associated entry in moz_openpages_temp.
*/
#define CREATE_PLACES_VIEW_DELETE_TRIGGER NS_LITERAL_CSTRING( \
"CREATE TEMPORARY TRIGGER moz_places_view_delete_trigger " \
@ -106,6 +106,8 @@
"WHERE id = OLD.id; " \
"DELETE FROM moz_places " \
"WHERE id = OLD.id; " \
"DELETE FROM moz_openpages_temp " \
"WHERE place_id = OLD.id; " \
"END" \
)
@ -258,4 +260,18 @@
#define CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER \
CREATE_TEMP_SYNC_TRIGGER_BASE("moz_historyvisits", MOZ_HISTORYVISITS_COLUMNS)
/**
* This trigger removes a row from moz_openpages_temp when open_count reaches 0.
*/
#define CREATE_REMOVEOPENPAGE_CLEANUP_TRIGGER NS_LITERAL_CSTRING( \
"CREATE TEMPORARY TRIGGER moz_openpages_temp_afterupdate_trigger " \
"AFTER UPDATE OF open_count ON moz_openpages_temp FOR EACH ROW " \
"WHEN NEW.open_count = 0 " \
"BEGIN " \
"DELETE FROM moz_openpages_temp " \
"WHERE place_id = NEW.place_id;" \
"END" \
)
#endif // __nsPlacesTriggers_h__

Просмотреть файл

@ -99,6 +99,9 @@ function ensure_results(aSearch, aExpected)
controller.input = input;
if (typeof kSearchParam == "string")
input.searchParam = kSearchParam;
let numSearchesStarted = 0;
input.onSearchBegin = function() {
numSearchesStarted++;

Просмотреть файл

@ -0,0 +1,104 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:set ts=2 sw=2 sts=2 et:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places Test Code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Blair McBride <bmcbride@mozilla.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let gTabRestrictChar = prefs.getCharPref("browser.urlbar.restrict.openpage");
let kSearchParam = "enable-actions";
let kURIs = [
"http://abc.com/",
"moz-action:switchtab,http://abc.com/",
"http://xyz.net/",
"moz-action:switchtab,http://xyz.net/"
];
let kTitles = [
"ABC rocks",
"xyz.net - we're better than ABC"
];
addPageBook(0, 0);
gPages[1] = [1, 0];
addPageBook(2, 1);
gPages[3] = [3, 1];
addOpenPages(0, 1);
let gTests = [
["0: single result, that is also a tab match",
"abc.com", [0,1]],
["1: two results, one tab match",
"abc", [0,1,2]],
["2: two results, both tab matches",
"abc", [0,1,2,3],
function() {
addOpenPages(2, 1);
}],
["3: two results, both tab matches, one has multiple tabs",
"abc", [0,1,2,3],
function() {
addOpenPages(2, 5);
}],
["4: two results, no tab matches",
"abc", [0,2],
function() {
removeOpenPages(0, 1);
removeOpenPages(2, 6);
}],
["5: tab match search with restriction character",
gTabRestrictChar + " abc", [1],
function() {
addOpenPages(0, 1);
}]
];
function addOpenPages(aUri, aCount) {
let num = aCount || 1;
for (let i = 0; i < num; i++) {
bhist.registerOpenPage(toURI(kURIs[aUri]));
}
}
function removeOpenPages(aUri, aCount) {
let num = aCount || 1;
for (let i = 0; i < num; i++) {
bhist.unregisterOpenPage(toURI(kURIs[aUri]));
}
}

Просмотреть файл

@ -42,6 +42,11 @@
#
# ***** END LICENSE BLOCK *****
<!DOCTYPE bindings [
<!ENTITY % actionsDTD SYSTEM "chrome://global/locale/actions.dtd">
%actionsDTD;
]>
<bindings id="autocompleteBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
@ -253,10 +258,21 @@
<!-- =================== PUBLIC MEMBERS =================== -->
<property name="value"
onget="return this.inputField.value;">
<property name="value">
<getter><![CDATA[
if (typeof this.onBeforeValueGet == "function") {
var result = this.onBeforeValueGet();
if (result)
return result.value;
}
return this.inputField.value;
]]></getter>
<setter><![CDATA[
this.mIgnoreInput = true;
if (typeof this.onBeforeValueSet == "function")
val = this.onBeforeValueSet(val);
this.inputField.value = val;
this.mIgnoreInput = false;
var event = document.createEvent('Events');
@ -516,11 +532,23 @@
]]></body>
</method>
<method name="resetActionType">
<body><![CDATA[
if (this.mIgnoreInput)
return;
this.removeAttribute("actiontype");
]]></body>
</method>
</implementation>
<handlers>
<handler event="input"
action="if (!this.mIgnoreInput &amp;&amp; this.mController.input == this) this.mController.handleText(true);"/>
<handler event="input"><![CDATA[
if (!this.mIgnoreInput && this.mController.input == this) {
this.mController.handleText(true);
}
this.resetActionType();
]]></handler>
<handler event="keypress" phase="capturing"
action="return this.onKeyPress(event);"/>
@ -1135,7 +1163,7 @@
<binding id="autocomplete-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox align="center">
<xul:hbox align="center" class="ac-title-box">
<xul:image xbl:inherits="src=image" class="ac-site-icon"/>
<xul:hbox anonid="title-box" class="ac-title" flex="1"
onunderflow="_doUnderflow('_title');">
@ -1152,6 +1180,7 @@
</xul:hbox>
<xul:hbox align="center" class="ac-url-box">
<xul:spacer class="ac-site-icon"/>
<xul:image class="ac-action-icon"/>
<xul:hbox anonid="url-box" class="ac-url" flex="1"
onunderflow="_doUnderflow('_url');">
<xul:description anonid="url" class="ac-normal-text ac-url-text" xbl:inherits="selected"/>
@ -1325,12 +1354,19 @@
<method name="_setUpDescription">
<parameter name="aDescriptionElement"/>
<parameter name="aText"/>
<parameter name="aNoEmphasis"/>
<body>
<![CDATA[
// Get rid of all previous text
while (aDescriptionElement.hasChildNodes())
aDescriptionElement.removeChild(aDescriptionElement.firstChild);
// If aNoEmphasis is specified, don't add any emphasis
if (aNoEmphasis) {
aDescriptionElement.appendChild(document.createTextNode(aText));
return;
}
// Get the indices that separate match and non-match text
let search = this.getAttribute("text");
let tokens = this._getSearchTokens(search);
@ -1372,6 +1408,10 @@
var title = this.getAttribute("title");
var type = this.getAttribute("type");
this.removeAttribute("actiontype");
var setupUrl = true;
// If we have a tag match, show the tags and icon
if (type == "tag") {
// Configure the extra box for tags display
@ -1413,6 +1453,15 @@
// Don't emphasize keyword searches in the title or url
this.setAttribute("text", "");
} else if (type == "action") {
let [,action, param] = url.match(/^moz-action:([^,]+),(.*)$/);
this.setAttribute("actiontype", action);
url = param;
this._extraBox.hidden = true;
this._titleBox.flex = 1;
let desc = "]]>&action.switchToTab.label;<![CDATA[";
this._setUpDescription(this._url, desc, true);
setupUrl = false;
} else {
// Hide the title's extra box if we don't need extra stuff
this._extraBox.hidden = true;
@ -1429,7 +1478,8 @@
// Emphasize the matching search terms for the description
this._setUpDescription(this._title, title);
this._setUpDescription(this._url, url);
if (setupUrl)
this._setUpDescription(this._url, url);
// Set up overflow on a timeout because the contents of the box
// might not have a width yet even though we just changed them

Просмотреть файл

@ -623,6 +623,9 @@
<field name="mIconURL">null</field>
<!-- This is managed by the tabbrowser -->
<field name="lastURI">null</field>
<field name="mDestroyed">false</field>
<constructor>
@ -699,6 +702,8 @@
// The feeds cache can keep the document inside this browser alive.
this.feeds = null;
this.lastURI = null;
this.removeEventListener("pageshow", this.onPageShow, true);
this.removeEventListener("pagehide", this.onPageHide, true);
this.removeEventListener("DOMPopupBlocked", this.onPopupBlocked, true);

Просмотреть файл

@ -0,0 +1 @@
<!ENTITY action.switchToTab.label "Switch to tab">

Просмотреть файл

@ -6,6 +6,7 @@
locale/@AB_CD@/global/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
locale/@AB_CD@/global/aboutRights.dtd (%chrome/global/aboutRights.dtd)
locale/@AB_CD@/global/aboutRights.properties (%chrome/global/aboutRights.properties)
locale/@AB_CD@/global/actions.dtd (%chrome/global/actions.dtd)
locale/@AB_CD@/global/appPicker.dtd (%chrome/global/appPicker.dtd)
locale/@AB_CD@/global/brand.dtd (generic/chrome/global/brand.dtd)
+ locale/@AB_CD@/global/browser.properties (%chrome/global/browser.properties)