Skip to content

Instantly share code, notes, and snippets.

@nolanlum
Last active December 13, 2019 08:38
Show Gist options
  • Select an option

  • Save nolanlum/8acf20f06864f3bc8f6af6c2689cdcdb to your computer and use it in GitHub Desktop.

Select an option

Save nolanlum/8acf20f06864f3bc8f6af6c2689cdcdb to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name MultiRowTabLiteforFx.uc.js
// @namespace http://space.geocities.yahoo.co.jp/gl/alice0775
// @description Multi-row tabs draggability fix, Experimental CSS version
// @include main
// @compatibility Firefox 69
// @author Alice0775, Endor8, TroudhuK, Izheil
// @version 06/09/2019 23:37 Fixed issue with tabs when moving to another window
// @version 05/09/2019 03:24 Fixed tab draggability to work with FF69
// @version 22/07/2019 19:21 Compatibility fix with Windows 7
// @version 23/03/2019 22:25 Comments on tab width
// @version 09/03/2019 15:38 Fixed compatibility issue with Tab Session Manager addon
// @version 18/02/2019 20:46 Tab line not being fully shown on maximized or fullscreen
// @version 03/02/2019 15:15 Firefox 67
// @version 01/02/2019 23:48 Fixed empty pixel line below tabs
// @version 31/01/2019 10:32 Fixed issue with fullscreen
// @version 30/01/2019 02:05 Fixed issue with a pixel being above the tab bar
// @version 23/11/2018 00:41 Firefox 65
// @version 19/10/2018 07:34 Firefox 62
// @version 11/05/2018 15:05 Firefox 60
// @version 08/05/2017 00:00 Firefox 48
// @version 05/01/2017 00:01 hide favicon if busy
// @version 02/09/2016 00:01 Bug 1222490 - Actually remove panorama for Fx45+
// @version 02/09/2016 00:01 workaround css for lwt
// ==/UserScript==
zzzz_MultiRowTabLite();
function zzzz_MultiRowTabLite() {
var css =`
/* MULTIROW TABS CSS */
/*
- For tab minimum width, you have to go to about:config and modify [browser.tabs.tabMinWidth]
to the value you want.
- For tab growth v
Value of 1 -> Tab grows. Fixed max width of 226px.
Value of 0 -> Tab doesn't grow. Uses tab min width as fixed width. */
:root {
--tab-growth: 1}
.tabbrowser-tab:not([pinned]) {
flex-grow: var(--tab-growth)}
.tabbrowser-tab::after {border: none !important}
:root:-moz-lwtheme[lwtheme-image] {background-repeat: repeat-y !important}
#tabbrowser-tabs .tab-background{
max-height: var(--tab-min-height) !important;
min-height: var(--tab-min-height) !important}
@media (-moz-os-version: windows-win10) {
#tabbrowser-tabs .tab-background, #tabbrowser-tabs .tabbrowser-tab {
min-height: calc(var(--tab-min-height) + 1px) !important}
}
/* This fix is intended for some updates when the tab line gets chopped on top of screen
#main-window[sizemode="normal"] .tabbrowser-tab .tab-line,
#main-window[sizemode="maximized"] .tabbrowser-tab .tab-line,
#main-window[sizemode="fullscreen"] .tabbrowser-tab .tab-line {transform: translate(0,1px) !important}
*/
#tabbrowser-tabs {margin-top: 0px !important}
.tab-stack {width: 100%}
#tabbrowser-tabs .arrowscrollbox-scrollbox {
display: flex;
flex-wrap: wrap;}
@-moz-document url(chrome://browser/content/browser.xhtml){
.scrollbutton-up[orient="horizontal"][part]~spacer,
.scrollbutton-up[orient="horizontal"][part],
.scrollbutton-down[orient="horizontal"][part]{ display: none }
scrollbox[part][orient="horizontal"]{
display: flex;
flex-wrap: wrap;
overflow-y: auto;
max-height: calc(var(--tab-min-height) * var(--multirow-n-rows));
scrollbar-color: currentColor transparent;
scrollbar-width: thin;
}
}
#tabbrowser-tabs .tabbrowser-arrowscrollbox {
overflow: visible;
display: block}
:root[uidensity="touch"] .tabbrowser-tab,
:root[uidensity="touch"] .tab-stack {
min-height: calc(var(--tab-min-height) + 3px) !important;
max-height: calc(var(--tab-min-height) + 3px) !important;
margin-bottom: 0 !important}
:root[uidensity="touch"] #tabbrowser-tabs .arrowscrollbox-scrollbox {
min-height: var(--tab-min-height) !important;}
.arrowscrollbox-overflow-start-indicator,
.arrowscrollbox-overflow-end-indicator {position: fixed !important}
@media (-moz-os-version: windows-win10) {
.titlebar-buttonbox, #titlebar-buttonbox {display: block !important; height:var(--tab-min-height) !important}}
#tabbrowser-tabs .scrollbutton-up, #tabbrowser-tabs .scrollbutton-down, #alltabs-button,
:root:not([customizing]) #TabsToolbar #new-tab-button, .tabbrowser-tab::after
{display: none}
`;
var sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService);
var uri = makeURI('data:text/css;charset=UTF=8,' + encodeURIComponent(css));
sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET);
gBrowser.tabContainer._getDropIndex = function(event, isLink) {
var tabs = document.getElementsByClassName("tabbrowser-tab")
var tab = this._getDragTargetTab(event, isLink);
if (window.getComputedStyle(this).direction == "ltr") {
for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) {
let rect = tabs[i].getBoundingClientRect();
if (event.clientX < rect.x + rect.width / 2
&& event.clientY < rect.y + rect.height) // multirow fix
return i;
}
} else {
for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) {
let rect = tabs[i].getBoundingClientRect();
if (event.clientX > rect.x + rect.width / 2
&& event.clientY < rect.y + rect.height) // multirow fix
return i;
}
}
return tabs.length;
};
// This scrolls down to the current tab when you open a new one, or restore a session.
function scrollToView() {
var selTab = document.querySelectorAll(".tabbrowser-tab[selected='true']")[0];
selTab.scrollIntoView({behavior: "smooth", block: "nearest", inline: "nearest"});
}
gBrowser.tabContainer.addEventListener('TabOpen', scrollToView, false);
gBrowser.tabContainer.addEventListener("TabSelect", scrollToView, false);
document.addEventListener("SSTabRestoring", scrollToView, false);
// This sets when to apply the fix (by default a new row starts after the 23th open tab, unless you changed the min-size of tabs)
gBrowser.tabContainer.ondragstart = function(){if(gBrowser.tabContainer.clientHeight > document.getElementsByClassName("tabbrowser-tab")[0].clientHeight) {
gBrowser.tabContainer._getDropEffectForTabDrag = function(event){return "";}; // multirow fix: to make the default "dragover" handler do nothing
gBrowser.tabContainer._onDragOver = function(event) {
event.preventDefault();
event.stopPropagation();
var ind = this._tabDropIndicator;
var effects = orig_getDropEffectForTabDrag(event);
if (effects == "link") {
let tab = this._getDragTargetTab(event, true);
if (tab) {
if (!this._dragTime)
this._dragTime = Date.now();
if (!tab.hasAttribute("pendingicon") && // annoying fix
Date.now() >= this._dragTime + this._dragOverDelay)
this.selectedItem = tab;
ind.hidden = true;
return;
}
}
var newIndex = this._getDropIndex(event, effects == "link");
if (newIndex == null)
return
var tabs = document.getElementsByClassName("tabbrowser-tab");
var ltr = (window.getComputedStyle(this).direction == "ltr");
var rect = this.arrowScrollbox.getBoundingClientRect();
var newMarginX, newMarginY;
if (newIndex == tabs.length) {
let tabRect = tabs[newIndex - 1].getBoundingClientRect();
if (ltr)
newMarginX = tabRect.right - rect.left;
else
newMarginX = rect.right - tabRect.left;
newMarginY = tabRect.top + tabRect.height - rect.top - rect.height; // multirow fix
} else {
let tabRect = tabs[newIndex].getBoundingClientRect();
if (ltr)
newMarginX = tabRect.left - rect.left;
else
newMarginX = rect.right - tabRect.right;
newMarginY = tabRect.top + tabRect.height - rect.top - rect.height; // multirow fix
}
ind.hidden = false;
newMarginX += ind.clientWidth / 2;
if (!ltr)
newMarginX *= -1;
ind.style.transform = "translate(" + Math.round(newMarginX) + "px," + Math.round(newMarginY) + "px)"; // multirow fix
ind.style.marginInlineStart = (-ind.clientWidth) + "px";
}
}
gBrowser.tabContainer.addEventListener("dragover", gBrowser.tabContainer._onDragOver, true);
gBrowser.tabContainer.onDrop = function(event) {
var newIndex;
var dt = event.dataTransfer;
var dropEffect = dt.dropEffect;
var draggedTab;
let movingTabs;
if (dt.mozTypesAt(0)[0] == TAB_DROP_TYPE) {
draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (!draggedTab) {
return;
}
movingTabs = draggedTab._dragData.movingTabs;
draggedTab.container._finishGroupSelectedTabs(draggedTab);
}
if (draggedTab && dropEffect == "copy") {}
else if (draggedTab && draggedTab.container == this) {
newIndex = this._getDropIndex(event, false);
if (newIndex > draggedTab._tPos)
newIndex--;
gBrowser.moveTabTo(draggedTab, newIndex);
}
};
gBrowser.tabContainer.addEventListener("drop", function(event){this.onDrop(event);}, true);
}};
// copy of the original and overrided _getDropEffectForTabDrag method
function orig_getDropEffectForTabDrag(event) {
var dt = event.dataTransfer;
let isMovingTabs = dt.mozItemCount > 0;
for (let i = 0; i < dt.mozItemCount; i++) {
// tabs are always added as the first type
let types = dt.mozTypesAt(0);
if (types[0] != TAB_DROP_TYPE) {
isMovingTabs = false;
break;
}}
if (isMovingTabs) {
let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (
sourceNode instanceof XULElement &&
sourceNode.localName == "tab" &&
sourceNode.ownerGlobal.isChromeWindow &&
sourceNode.ownerDocument.documentElement.getAttribute("windowtype") ==
"navigator:browser" &&
sourceNode.ownerGlobal.gBrowser.tabContainer == sourceNode.container
) {
// Do not allow transfering a private tab to a non-private window
// and vice versa.
if (
PrivateBrowsingUtils.isWindowPrivate(window) !=
PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerGlobal)
) {
return "none";}
if (
window.gMultiProcessBrowser !=
sourceNode.ownerGlobal.gMultiProcessBrowser
) {
return "none";}
return dt.dropEffect == "copy" ? "copy" : "move";
}}
if (browserDragAndDrop.canDropLink(event)) {
return "link";}
return "none";}
/* DO NOT DELETE THIS LINE */
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
/* This enables the use of JS external files */
toolbarbutton#alltabs-button {
-moz-binding: url("userChrome.xml#js")
}
/* Hide Tab Close buttons on unselected tabs unless hovered */
#tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button {
visibility: collapse !important;
opacity: 0 !important;
}
#tabbrowser-tabs .tabbrowser-tab[selected="true"]:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button {
visibility: visible !important;
}
#tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]):hover > .tab-stack > .tab-content > .tab-close-button {
visibility: visible !important;
opacity: 1 !important;
transition: all 250ms ease-in-out !important;
}
#tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button {
display: -moz-box !important;
}
/* Style pending tabs with a background and italic text */
#tabbrowser-tabs .tabbrowser-tab[pending="true"] > .tab-stack > .tab-background {
background-attachment: none !important;
background-color: #ECE9D8 !important;
background-image: none !important;
}
#tabbrowser-tabs .tabbrowser-tab[pending="true"] > .tab-stack > .tab-content > .tab-label-container > .tab-label {
color: #C00 !important;
font-style: italic !important;
}
/* Style unread tabs with reddish italic text */
/*#tabbrowser-tabs .tabbrowser-tab[unread="true"] > .tab-stack > .tab-content > .tab-label-container > .tab-label {
color: #F66 !important;
font-style: italic !important;
}*/
<?xml version="1.0"?>
<!-- Copyright (c) 2017 Haggai Nuchi
Available for use under the MIT License:
https://opensource.org/licenses/MIT
-->
<!-- Run userChrome.js/userChrome.xul and .uc.js/.uc.xul/.css files -->
<bindings id="generalBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="js">
<implementation>
<constructor><![CDATA[
if(window.userChromeJsMod) return;
window.userChromeJsMod = true;
var chromeFiles = FileUtils.getDir("UChrm", []).directoryEntries;
var xulFiles = [];
var sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService);
while(chromeFiles.hasMoreElements()) {
var file = chromeFiles.getNext().QueryInterface(Ci.nsIFile);
var fileURI = Services.io.newFileURI(file);
if(file.isFile()) {
if(/(^userChrome|\.uc)\.js$/i.test(file.leafName)) {
Services.scriptloader.loadSubScriptWithOptions(fileURI.spec, {target: window, ignoreCache: true});
}
else if(/(^userChrome|\.uc)\.xul$/i.test(file.leafName)) {
xulFiles.push(fileURI.spec);
}
else if(/\.as\.css$/i.test(file.leafName)) {
if(!sss.sheetRegistered(fileURI, sss.AGENT_SHEET))
sss.loadAndRegisterSheet(fileURI, sss.AGENT_SHEET);
}
else if(/^(?!(userChrome|userContent)\.css$).+\.css$/i.test(file.leafName)) {
if(!sss.sheetRegistered(fileURI, sss.USER_SHEET))
sss.loadAndRegisterSheet(fileURI, sss.USER_SHEET);
}
}
}
setTimeout(function loadXUL() {
if(xulFiles.length > 0) {
document.loadOverlay(xulFiles.shift(), null);
setTimeout(loadXUL, 5);
}
}, 0);
]]></constructor>
</implementation>
</binding>
</bindings>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment