我正在编写基于Firefox Add-on SDK的扩展程序。我需要同时使用 左侧和右侧边栏 。默认情况下,我可以在左侧显示一个。我已经阅读过使用CSS在左侧和右侧ui/sidebar之间进行更改:
@namespace url(http://www.mozilla.org/keymaster/gat...re.is.only.xul);
hbox#browser {
direction: rtl !important;
}
hbox#browser > vbox {
direction: ltr !important;
}
但是,这似乎是旧学校的一部分,因为我没有定义.xul文件。
你知道在浏览器的左右两侧都有侧栏吗?
答案 0 :(得分:2)
您的问题不明确完全您想要什么。因此,我做了一些假设。
最终,没有官方支持的方式来拥有多个"侧边栏"一次显示。默认情况下,只有一个"侧边栏"存在于Firefox浏览器窗口中。虽然没有支持的API为您提供多个侧边栏,但可以通过修改Firefox浏览器窗口的XUL DOM来创建多个侧边栏(界面面板)。
下面的代码创建了浏览器窗口顶部,底部,左侧,右侧或单独窗口中的用户界面面板(侧栏,顶栏,底栏和窗口)。可以创建界面面板,使它们相对于窗口(当您切换选项卡时它们保持可见;如书签/历史侧边栏),或者相对于选项卡(它们仅在选项卡所在的选项卡上可见)显示已创建;与开发人员工具一样。
createInterfacePanelIframe()
方法创建一个包含<iframe>
(或<browser>
)的界面面板,您可以为其提供网址和所有属性。 <iframe>
和<splitter>
的元素将返回给调用者,以便您可以对其执行其他操作,或者从Firefox窗口的DOM中删除它们以删除/隐藏它们。此方法创建<iframe>
并调用createInterfacePanel()
将其插入到指定位置的Firefox浏览器中。
createInterfacePanel()
方法会将传递给它的XUL元素(使用Document Fragment传递多个元素)放入Firefox浏览器窗口的DOM中,同时加上{{1在指定的位置(左侧,右侧,顶部,底部,窗口以及相对于窗口或选项卡)。您可以指定希望界面面板位于的窗口和/或选项卡,或者,默认情况下,界面面板将插入当前窗口/选项卡中。如果已指定要插入的接口面板,则将在现有接口面板旁边创建另一个面板。您可以创建的数字没有固有的限制(例如,如果您愿意,您可以在右侧有10个面板。)
此外,下面还有一个示例Firefox附加组件SDK扩展,它添加了6个不同的按钮。前5个按钮创建或销毁界面面板。徽章指示按钮将影响哪个面板(左,右,顶部,底部和窗口)。绿色徽章表示将创建界面面板。红色徽章表示单击将破坏现有面板。第六个按钮切换Tab相对界面面板和Window相对界面面板之间的其他按钮。如果您创建所有面板,您将有8个面板(4个用于窗口,4个用于当前选项卡)以及两个单独的窗口(每个浏览器窗口)。
以下是演示插件的外观。下图中显示的面板很窄和/或很短,以便在此页面上显示。下面的代码允许您根据需要制作任何尺寸的面板,并允许用户调整尺寸
这是创建侧边栏(界面面板)的代码:
sidebars.js :
<splitter>
演示版Firefox附加组件SDK扩展名为:
的package.json :
/**
* createInterfacePanelIframe(location,options)
* Creates an <iframe> based panel within the current tab, within the
* current window, or opens a window, for use as an user interface
* box. If it is not a window, it is associated with the current
* browser tab, or the current browser window depending on the
* byWindow option.
* @param location
* Placement of the panel [right|left|top|bottom|window]
* The default location is 'right'.
* @param options
* An Object containing optional parameters.
* height
* The height of a top or bottom sidebar
* Default is 200.
* width
* The width of a left or right sidebar
* Default is 200.
* size
* Width if on left or right. Height if top or bottom.
* Both width and height if location='window' unless
* features is a string.
* Default is 200.
* id
* The ID to assign to the iframe. Default is
* 'makyen-interface-panel'
* The <splitter> will be assigned the
* ID = id + '-splitter'
* url
* This is the chrome:// URL to use for the contents
* of the iframe or the window.
* the default is:
* 'chrome://devtools/content/framework/toolbox.xul'
* iframeAttributes
* An Object
* Contains key/value pairs which are applies as attributes
* of the iframe. These will override any defaults or other
* attributes derived from other options (e.g. id, height,
* width, type, etc.). If the value of the property is null
* then that attribute will be removed.
* useBrowser
* If true, a <browser> element is used instead of an <iframe>.
* byWindow
* If true then the created sidebar is for the window
* not the tab.
* tab
* The tab for which to create the sidebar. If not
* specified, the current tab is used.
* window
* The window for which to create the sidebar. If not
* specified, the current window is used.
* features
* The features string for the window. See:
* https://developer.mozilla.org/en-US/docs/Web/API/Window.open
*
* returns [splitterEl, iframeEl]
* The elements for the <splitter> and <iframe>.
*
* Copyright 2014-2016 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
**/
function createInterfacePanelIframe(location,options){
//options
let size,width,height,id,chromeUrl;
if(typeof options === 'object'){
size = options.size;
width = options.width;
height = options.height;
id = options.id;
chromeUrl = options.url;
}
if(!width && !height && size){
width = size;
height = size;
}
[width,height] = getSizeWithDefaults(location,width,height);
//defaults
id = typeof id !== 'string' ? 'makyen-interface-panel' : id;
chromeUrl = typeof chromeUrl !== 'string'
? 'chrome://devtools/content/framework/toolbox.xul'
: chromeUrl;
//Create some common variables if they do not exist.
//This gets the currently active Firefox XUL window.
// Add/remove a '/' to comment/un-comment the code appropriate for your add-on type.
//* Add-on SDK:
let activeWindow = options.window ?
options.window : require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = options.window ?
options.window : Services.wm.getMostRecentWindow('navigator:browser');
//*/
let mainDocument = activeWindow.document;
//Create the <iframe> use
//mainDocument for the XUL namespace.
let iframeEl;
if(options.useBrowser){
iframeEl = mainDocument.createElement('browser');
} else {
iframeEl = mainDocument.createElement('iframe');
}
iframeEl.id = id;
iframeEl.setAttribute('src',chromeUrl);
iframeEl.setAttribute("tooltip", "aHTMLTooltip");
iframeEl.setAttribute("autocompleteenabled", true);
iframeEl.setAttribute("autocompletepopup", "PopupAutoComplete");
iframeEl.setAttribute("disablehistory",true);
iframeEl.setAttribute('type', 'content');
if(typeof height === 'number'){
iframeEl.setAttribute('height', height.toString());
}
if(typeof width === 'number'){
iframeEl.setAttribute('width', width.toString());
}
if(typeof options.iframeAttributes === 'object'){
let attrs = options.iframeAttributes;
for(let attr in attrs){
if(attrs.hasOwnProperty(attr)) {
if(attrs[attr]===null){
iframeEl.removeAttribute(attr);
}else{
iframeEl.setAttribute(attr, attrs[attr]);
}
}
}
}
//Call createInterfacePanel
let splitterEl;
let newOptions = {};
if(height) {
newOptions.height = height;
}
if(width) {
newOptions.width = width;
}
newOptions.url = chromeUrl;
if(options.tab){
newOptions.tab = options.tab;
}
if(options.window){
newOptions.window = options.window;
}
if(options.features){
newOptions.features = options.features;
}
if(options.byWindow){
newOptions.byWindow = options.byWindow;
}
newOptions.id = id + '-splitter';
splitterEl = createInterfacePanel(location, iframeEl, newOptions)
return [splitterEl, iframeEl];
}
/**
* createInterfacePanel(location,objectEl,options)
* Creates a panel within the current tab, or opens a window, for use as a
* user interface box. If not a window, it is associated with the current
* browser tab.
* @param location
* Placement of the panel [right|left|top|bottom|window]
* The default location is 'right'.
* @param objectEl
* The element of an XUL object that will be inserted into
* the DOM such that it is within the current tab.
* Some examples of possible objects are <iframe>,
* <browser>, <box>, <hbox>, <vbox>, etc.
* If the location='window' and features is not a string
* and this is a number then it is used as the width of the
* window.
* @param options
* An Object containing optional parameters.
* height
* The height of a top or bottom sidebar
* Default is 200.
* width
* The width of a left or right sidebar
* Default is 200.
* size
* Width if on left or right. Height if top or bottom.
* Both width and height if location='window' unless
* features is a string.
* Default is 200.
* If none of height, width or size is specified, then the
* size of the sidebar should be specified within the XUL
* elements referenced by objectEl.
* sizeEl
* The element that is to contain attributes of 'width' and
* 'height'. If location=='left'|'right' then the
* 'height' attribute is removed prior to the objectEl
* being inserted into the DOM.
* This is an optional spearate reference for the size element
* in case the objectEl is a documentFragment containing
* multiple elements. However, normal usage is for
* objectEl === sizeEl (which is default if unspecified)
* when location != 'window'.
* When location == 'window' and features is not a string,
* and sizeEl is a number then it is used as the height
* of the window.
* If features is a string, it is assumed the height is
* set in that, or elsewhere (e.g. in the XUL).
* id
* The ID to assign to the <splitter>. The default is:
* 'makyen-interface-panel-splitter'.
* url
* This is the chrome:// URL to use for the contents
* of the window.
* the default is:
* 'chrome://devtools/content/framework/toolbox.xul'
* byWindow
* If true then the created sidebar is for the window
* not the tab.
* tab
* The tab for which to create the sidebar. If not
* specified, the current tab is used.
* window
* The window for which to create the sidebar. If not
* specified, the current window is used.
* features
* The features string for the window. See:
* https://developer.mozilla.org/en-US/docs/Web/API/Window.open
* If features is a string, it is assumed the width is
* set in that, or elsewhere (e.g. in the XUL).
*
* returns
* if location != 'window':
* splitterEl, The element for the <splitter>.
* if location == 'window':
* The windowObjectReference returned by window.open().
*
* Copyright 2014-2016 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
**/
function createInterfacePanel(location,objectEl,options) {
//function createInterfacePanel(location,objectEl,sizeEl,id,chromeUrl,features) {
//options
let size,width,height,sizeEl,id,chromeUrl,byWindow,features;
if(typeof options === 'object'){
size = options.size;
width = options.width;
height = options.height;
//If a separate sizeEl is not specified, then use the ObjectEl for sizeEl.
// This is so we could pass a document fragment with multiple elements,
// But only one which should have a specified size.
sizeEl = options.sizeEl? options.sizeEl:objectEl;
id = options.id;
chromeUrl = options.url;
byWindow = options.byWindow;
features = options.features;
}
if(!width && !height && size){
width = size;
height = size;
}
[width,height] = getSizeWithDefaults(location,width,height);
//Set location default:
location = typeof location !== 'string' ? 'right' : location;
if(location == 'window') {
if(typeof features !== 'string') {
let widthText = 'width=' + width.toString() + ',';
let heightText = 'height=' + height.toString() + ',';
features = widthText + heightText
+ 'menubar=no,toolbar=no,location=no,personalbar=no'
+ ',status=no,chrome=yes,resizable,centerscreen';
}
}
id = typeof id !== 'string' ? 'makyen-interface-panel-splitter' : id;
chromeUrl = typeof chromeUrl !== 'string'
? 'chrome://devtools/content/framework/toolbox.xul'
: chromeUrl;
//Create some common variables if they do not exist.
//This gets the currently active Firefox XUL window.
// Add/remove a '/' to comment/un-comment the code appropriate for your add-on type.
//* Add-on SDK:
let activeWindow = options.window ?
options.window : require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = options.window ?
options.window : Services.wm.getMostRecentWindow('navigator:browser');
//*/
if (typeof gBrowser === 'undefined') {
//If there is no gBrowser defined, get it
var gBrowser = activeWindow.gBrowser;
}
//Get the tab & notification box (container for tab UI).
let tab = options.tab?options.tab:gBrowser.selectedTab;
let browserForTab = gBrowser.getBrowserForTab( tab );
let notificationBox = gBrowser.getNotificationBox( browserForTab );
let ownerDocument = gBrowser.ownerDocument;
//Create a Document Fragment.
//If doing multiple DOM additions, we should be in the habit
// of doing things in a way which causes the least number of reflows.
// We know that we are going to add more than one thing, so use a
// document fragment.
let docFrag = ownerDocument.createDocumentFragment();
//ownerDocument must be used here in order to have the XUL namespace
// or the splitter causes problems.
// createElementNS() does not work here.
let splitterEl = ownerDocument.createElement('splitter');
splitterEl.id = id ;
//Look for the child element with class='browserSidebarContainer'.
//It is the element in procimity to which the <splitter>
//and objectEl will be placed.
let theChild = notificationBox.firstChild;
while (!theChild.hasAttribute('class')
|| (theChild.getAttribute('class').indexOf('browserSidebarContainer') === -1)
) {
theChild = theChild.nextSibling;
if(!theChild) {
//We failed to find the correct node.
//This implies that the structure Firefox
// uses has changed significantly and it should
// be assumed that the extension is no longer compatible.
return null;
}
}
let tabBrowser = ownerDocument.getElementById('content');
let heightAttr='height';
let widthAttr='width';
if(byWindow) {
notificationBox = ownerDocument.getElementById('browser');
theChild = ownerDocument.getElementById('appcontent');
//When Window referenced, where we need to put the elements is
// slightly different, but works out to just being a swapping
// of 'location' values.
//Swap the width and height attributes and options.
heightAttr='width';
widthAttr='height';
let foo = width;
width = height;
height = foo;
foo = options.width;
options.width = options.height;
options.height = foo;
switch(location) {
case 'window' :
//no change
break;
case 'top' :
location = 'left'
break;
case 'bottom' :
location = 'right'
break;
case 'left' :
location = 'top'
break;
case 'right' :
default :
location = 'bottom'
break;
}
}
switch(location) {
case 'window' :
return activeWindow.open(chromeUrl,'_blank',features);
break;
case 'top' :
if(options.size || options.height) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(widthAttr);
sizeEl.setAttribute(heightAttr,height);
}
docFrag.appendChild(objectEl);
docFrag.appendChild(splitterEl);
//Inserting the document fragment results in the same
// DOM structure as if you Inserted each child of the
// fragment separately. (i.e. the document fragment
// is just a temporary container).
//Insert the interface prior to theChild.
notificationBox.insertBefore(docFrag,theChild);
break;
case 'bottom' :
if(options.size || options.height) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(widthAttr);
sizeEl.setAttribute(heightAttr,height);
}
docFrag.appendChild(splitterEl);
docFrag.appendChild(objectEl);
//Insert the interface just after theChild.
notificationBox.insertBefore(docFrag,theChild.nextSibling);
break;
case 'left' :
if(options.size || options.width) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(heightAttr);
sizeEl.setAttribute(widthAttr,width);
}
docFrag.appendChild(objectEl);
//Splitter is second in this orientaiton.
docFrag.appendChild(splitterEl);
if(byWindow) {
//Insert the interface prior to the tabbrowser to put
// global notifications above the top sidebar.
theChild.insertBefore(docFrag,tabBrowser);
}else{
//Insert the interface as the first child of theChild.
theChild.insertBefore(docFrag,theChild.firstChild);
}
break;
case 'right' :
default :
//Right orientaiton, the default.
if(options.size || options.width) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(heightAttr);
sizeEl.setAttribute(widthAttr,width);
}
docFrag.appendChild(splitterEl);
docFrag.appendChild(objectEl);
//Insert the interface as the last child of theChild.
theChild.appendChild(docFrag);
break;
}
return splitterEl;
}
function getSizeWithDefaults(location,width,height){
let defaultSize = 200;
switch(location) {
case 'window' :
width = ( (typeof width !== 'number') || width<1) ? defaultSize : width;
height = ( (typeof height !== 'number') || height<1) ? defaultSize : height;
break;
case 'top' :
case 'bottom' :
width = null;
height = ( (typeof height !== 'number') || height<1) ? defaultSize : height;
break;
case 'left' :
case 'right' :
default :
width = ( (typeof width !== 'number') || width<1) ? defaultSize : width;
height = null;
break;
}
return [width,height];
}
exports.createInterfacePanel=createInterfacePanel;
exports.createInterfacePanelIframe=createInterfacePanelIframe;
数据/ sidebar.html :
{
"title": "Demo Sidebars",
"name": "demo-sidebars",
"version": "0.0.1",
"description": "Demo creating Window related sidebars",
"main": "index.js",
"author": "Makyen",
"permissions": {"private-browsing": true},
"engines": {
"firefox": ">=38.0a1",
"fennec": ">=38.0a1"
},
"license": "MIT",
"keywords": [
"jetpack"
]
}
index.js :
<html>
<head>
<meta charset="utf-8">
</head>
<body style="background-color:white;">
This is a Window.
</body>
</html>
此答案中的代码经过调整,并从我对#34; Firefox Extension, Window related sidebar&#34;的回答中大大扩展。这个答案有很多额外的信息,比如在Firefox浏览器中如何构建侧边栏(界面面板)。
答案 1 :(得分:-2)
firefox中有一个侧边栏API,这个插件可以很好地利用它 - https://addons.mozilla.org/en-US/firefox/addon/whatsapp-web-messenger/
每个镀铬窗口中似乎都有一个window.SidebarUI
对象。