diff --git a/browser/base/content/bindings.xml b/browser/base/content/bindings.xml new file mode 100644 index 000000000000..c91675d61ad5 --- /dev/null +++ b/browser/base/content/bindings.xml @@ -0,0 +1,80 @@ + + +# -*- Mode: HTML -*- +# ***** 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 mozilla.org browser. +# +# The Initial Developer of the Original Code is +# Simon Bünzli +# Portions created by the Initial Developer are Copyright (C) 2007 - 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# 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 ***** + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 35249c2abd3c..d01c9af88936 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -23,6 +23,17 @@ toolbar[printpreview="true"] { -moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup"); } +/* ::::: Unified Back-/Forward Button ::::: */ +#unified-back-forward-button { + -moz-binding: url("chrome://browser/content/bindings.xml#unified-back-forward-button-wrapper"); +} +#unified-back-forward-button > toolbarbutton > dropmarker { + display: none; /* we provide our own */ +} +.unified-nav-current { + font-weight: bold; +} + menuitem.spell-suggestion { font-weight: bold; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 567908e59e3f..331f12075e41 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -181,6 +181,45 @@ function UpdateBackForwardCommands(aWebNavigation) } } +var UnifiedBackForwardButton = { + unify: function() { + var backButton = document.getElementById("back-button"); + if (!backButton || !backButton.nextSibling || backButton.nextSibling.id != "forward-button") + return; // back and forward buttons aren't adjacent + + var wrapper = document.createElement("toolbaritem"); + wrapper.id = "unified-back-forward-button"; + wrapper.className = "chromeclass-toolbar-additional"; + wrapper.setAttribute("context", "backMenu"); + + var toolbar = backButton.parentNode; + toolbar.insertBefore(wrapper, backButton); + + var forwardButton = backButton.nextSibling; + wrapper.appendChild(backButton); + wrapper.appendChild(forwardButton); + + var popup = backButton.getElementsByTagName("menupopup")[0].cloneNode(true); + wrapper.appendChild(popup); + + this._unified = true; + }, + + separate: function() { + if (!this._unified) + return; + + var wrapper = document.getElementById("unified-back-forward-button"); + var toolbar = wrapper.parentNode; + + toolbar.insertBefore(wrapper.firstChild, wrapper); // Back button + toolbar.insertBefore(wrapper.firstChild, wrapper); // Forward button + toolbar.removeChild(wrapper); + + this._unified = false; + } +}; + #ifdef XP_MACOSX /** * Click-and-Hold implementation for the Back and Forward buttons @@ -920,6 +959,7 @@ function delayedStartup() sidebar.setAttribute("src", sidebarBox.getAttribute("src")); } + UnifiedBackForwardButton.unify(); UpdateUrlbarSearchSplitterState(); try { @@ -1444,12 +1484,14 @@ function BrowserHandleShiftBackspace() function BrowserBackMenu(event) { - return FillHistoryMenu(event.target, "back"); + var menuType = UnifiedBackForwardButton._unified ? "unified" : "back"; + return FillHistoryMenu(event.target, menuType); } function BrowserForwardMenu(event) { - return FillHistoryMenu(event.target, "forward"); + var menuType = UnifiedBackForwardButton._unified ? "unified" : "forward"; + return FillHistoryMenu(event.target, menuType); } function BrowserStop() @@ -2934,6 +2976,7 @@ function FillHistoryMenu(aParent, aMenu) var webNav = getWebNavigation(); var sessionHistory = webNav.sessionHistory; + var bundle_browser = document.getElementById("bundle_browser"); var count = sessionHistory.count; var index = sessionHistory.index; @@ -2950,7 +2993,8 @@ function FillHistoryMenu(aParent, aMenu) { entry = sessionHistory.getEntryAtIndex(j, false); if (entry) - createMenuItem(aParent, j, entry.title); + createMenuItem(aParent, j, entry.title || entry.URI.spec, + bundle_browser.getString("tabHistory.goBack")); } break; case "forward": @@ -2960,9 +3004,39 @@ function FillHistoryMenu(aParent, aMenu) { entry = sessionHistory.getEntryAtIndex(j, false); if (entry) - createMenuItem(aParent, j, entry.title); + createMenuItem(aParent, j, entry.title || entry.URI.spec, + bundle_browser.getString("tabHistory.goForward")); } break; + case "unified": + if (count <= 1) // don't display the popup for a single item + return false; + + var half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2); + var start = Math.max(index - half_length, 0); + end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count); + if (end == count) + start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0); + + var tooltips = [ + bundle_browser.getString("tabHistory.goBack"), + bundle_browser.getString("tabHistory.current"), + bundle_browser.getString("tabHistory.goForward") + ]; + var classNames = ["unified-nav-back", "unified-nav-current", "unified-nav-forward"]; + + for (var j = end - 1; j >= start; j--) { + entry = sessionHistory.getEntryAtIndex(j, false); + var tooltip = tooltips[j < index ? 0 : j == index ? 1 : 2]; + var className = classNames[j < index ? 0 : j == index ? 1 : 2]; + var item = createMenuItem(aParent, j, entry.title || entry.URI.spec, tooltip, className); + + if (j == index) { // mark the current history item + item.setAttribute("type", "radio"); + item.setAttribute("checked", "true"); + } + } + break; } return true; @@ -2984,12 +3058,16 @@ function addToUrlbarHistory(aUrlToAdd) } } -function createMenuItem( aParent, aIndex, aLabel) +function createMenuItem(aParent, aIndex, aLabel, aTooltipText, aClassName) { var menuitem = document.createElement( "menuitem" ); menuitem.setAttribute( "label", aLabel ); menuitem.setAttribute( "index", aIndex ); - aParent.appendChild( menuitem ); + if (aTooltipText) + menuitem.setAttribute("tooltiptext", aTooltipText); + if (aClassName) + menuitem.className = aClassName; + return aParent.appendChild(menuitem); } function deleteHistoryItems(aParent) @@ -3088,6 +3166,8 @@ function BrowserCustomizeToolbar() var cmd = document.getElementById("cmd_CustomizeToolbars"); cmd.setAttribute("disabled", "true"); + UnifiedBackForwardButton.separate(); + var splitter = document.getElementById("urlbar-search-splitter"); if (splitter) splitter.parentNode.removeChild(splitter); @@ -3130,6 +3210,7 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) #endif } + UnifiedBackForwardButton.unify(); UpdateUrlbarSearchSplitterState(); // Update the urlbar diff --git a/browser/base/jar.mn b/browser/base/jar.mn index f5fd20c80720..c617a6b7f0b5 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -13,6 +13,7 @@ browser.jar: * content/browser/aboutDialog.xul (content/aboutDialog.xul) * content/browser/aboutDialog.js (content/aboutDialog.js) content/browser/aboutDialog.css (content/aboutDialog.css) +* content/browser/bindings.xml (content/bindings.xml) * content/browser/browser.css (content/browser.css) * content/browser/browser.js (content/browser.js) * content/browser/browser.xul (content/browser.xul) diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 14a0838ed7f7..ab48be5e997f 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -81,6 +81,11 @@ feedHasFeedsNew=Subscribe to this page… menuOpenAllInTabs.label=Open All in Tabs menuOpenAllInTabs.accesskey=o +# Unified Back-/Forward Popup +tabHistory.current=Stay on this page +tabHistory.goBack=Go back to this page +tabHistory.goForward=Go forward to this page + # Block autorefresh refreshBlocked.goButton=Allow refreshBlocked.goButton.accesskey=A