2014-09-21 56 views
1

对于我的插件,我想创建一个底部停靠的“面板”,它将显示信息。类似这样的例子是Firefox自己的Web控制台面板,可以在Web开发工具下进行切换。如何在Firefox中使用XUL创建底部停靠的面板?

我试图挖掘代码,但无法弄清楚它是如何实现的。有人能给我一个关于如何用XUL创建这个基本的解释,或者指出我正确的方向吗?

回答

4

Web控制台取代var browser = aDomWindow.document.querySelector('#browser')运行不一个sidebar。在Fireox中只有一个侧边栏,它可以位于浏览器内容的左侧或右侧。即使您更改了选项卡,边栏也是UI的常用部分。它通常用于内容,历史记录,书签或其他此类信息,这些信息根据所查看的选项卡而不会发生变化。

要调查这样的东西,我会建议DOM Inspector加载项,如果你还没有安装它。

Web控制台包含在的<tabbrowser>中,位于其所在的选项卡下。

的XUL的iframe是: <iframe xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="devtools-toolbox-bottom-iframe" height="436" tooltip="aHTMLTooltip" src="chrome://browser/content/devtools/framework/toolbox.xul"/>

首先一个<splitter>则是本<hbox class="browserSidebarContainer">用于包含它的标签之后插入。

Web控制台是通过调用打开gDevToolsBrowser.selectToolCommand(gBrowser, "webconsole");

gDevToolsBrowserchrome://browser/content/browser.js定义从他们实际上在功能SH_create创建的resource:///modules/devtools/gDevTools.jsm

内容中resource:///modules/devtools/framework/toolbox-hosts.js

所有这些chrome://的和resource:///网址应该可以在Firefox中使用。 Firefox安装包含三个文件的大量文件cales omni.ja。该文件位于<安装目录> /omni.ja<安装目录> /browser/omni.ja<安装目录> /webart/omni.jaomni.ja文件只是扩展名已重命名的zip格式文件。为了能够轻松访问这些文件,我经常将它们解压缩到目录中(Firefox安装目录树之外)。我发现,当我想知道如何完成某些操作时,可以更轻松地执行搜索和操作文件。

如果您只是在寻找能够创建用于Web控制台的框的代码,其复杂程度取决于您所处的环境。下面应该从几乎任何地方工作:

/** 
* Creates an <iframe> based panel within the current tab, 
* 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. 
* @param location 
*  Placement of the panel [right|left|top|bottom|window] 
*  The default location is "right". 
* @param 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 400. 
* @param id 
*  The ID to assign to the iframe. Default is 
*  "makyen-interface-panel" 
*  The <splitter> will be assigned the 
*  ID = id + "-splitter" 
* @param chromeUrl 
*  This is the chrome:// URL to use for the contents 
*  of the iframe or the window. 
*  the default is: 
*  "chrome://browser/content/devtools/framework/toolbox.xul" 
* @param 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 by Makyen. 
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/. 
**/ 
function createInterfacePanelIframe(location,size,id,chromeUrl,features) { 
    //defaults 
    size = ((typeof size !== "number") || size<1) ? 400 : size; 
    id = typeof id !== "string" ? "makyen-interface-panel" : id; 
    chromeUrl = typeof chromeUrl !== "string" 
     ? "chrome://browser/content/devtools/framework/toolbox.xul" 
     : chromeUrl; 

    //Create some common variables if they do not exist. 
    // This should work from any Firefox context. 
    // Depending on the context in which the function is being run, 
    // this could be simplified. 
    if (typeof window === "undefined") { 
     //If there is no window defined, get the most recent. 
     var window=Components.classes["@mozilla.org/appshell/window-mediator;1"] 
          .getService(Components.interfaces.nsIWindowMediator) 
          .getMostRecentWindow("navigator:browser"); 
    } 
    if (typeof document === "undefined") { 
     //If there is no document defined, get it 
     var document = window.content.document; 
    } 
    if (typeof gBrowser === "undefined") { 
     //If there is no gBrowser defined, get it 
     var gBrowser = window.gBrowser; 
    } 

    //Get the current tab & notification box (container for tab UI). 
    let tab = gBrowser.selectedTab; 
    let browserForTab = gBrowser.getBrowserForTab(tab); 
    let notificationBox = gBrowser.getNotificationBox(browserForTab); 
    let ownerDocument = gBrowser.ownerDocument; 

    //Create the <iframe> use 
    //ownerDocument for the XUL namespace. 
    let iframeEl = ownerDocument.createElement("iframe"); 
    iframeEl.id = id; 
    iframeEl.setAttribute("src",chromeUrl); 
    iframeEl.setAttribute("height", size.toString()); 
    iframeEl.setAttribute("width", size.toString()); 

    //Call createInterfacePanel, pass the size if it is to be a window. 
    let splitterEl; 
    if(location == "window") { 
     splitterEl = createInterfacePanel(location, size, size 
             ,id + "-splitter", chromeUrl, features); 
     return [splitterEl, null]; 
    } else { 
     splitterEl = createInterfacePanel(location, iframeEl, iframeEl 
             ,id + "-splitter", chromeUrl, features); 
    } 
    return [splitterEl, iframeEl]; 
} 

/** 
* 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. 
*  If features is a string, it is assumed the width is 
*  set in that, or elsewhere (e.g. in the XUL). 
* @param sizeEl 
*  The element that contains attributes of "width" and 
*  "height". If location=="left"|"right" then the 
*  "height" attribute is removed prior to the objectEl 
*  being inserted into the DOM. 
*  A spearate reference for the size element in case the 
*  objectEl is a documentFragment containing multiple elements. 
*  However, normal usage is for objectEl === sizeEl 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). 
* @param id 
*  The ID to assign to the <splitter>. The default is: 
*  "makyen-interface-panel-splitter". 
* @param chromeUrl 
*  This is the chrome:// URL to use for the contents 
*  of the window. 
*  the default is: 
*  "chrome://browser/content/devtools/framework/toolbox.xul" 
* @param features 
*  The features string for the window. See: 
*  https://developer.mozilla.org/en-US/docs/Web/API/Window.open 
* returns 
*  if location != "window": 
*   splitterEl, The element for the <splitter>. 
*  if location == "window": 
*   The windowObjectReference returned by window.open(). 
* 
* Copyright 2014 by Makyen. 
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/. 
**/ 
function createInterfacePanel(location,objectEl,sizeEl,id,chromeUrl,features) { 
    //Set location default: 
    location = typeof location !== "string" ? "right" : location; 
    if(location == "window") { 
     if(typeof features !== "string") { 
      let width = ""; 
      let height = ""; 
      if(typeof objectEl == "number") { 
       width = "width=" + objectEl.toString() + ","; 
      } 
      if(typeof sizeEl == "number") { 
       height = "height=" + sizeEl.toString() + ","; 
      } 
      features = width + height 
         + "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://browser/content/devtools/framework/toolbox.xul" 
     : chromeUrl; 

    //Create some common variables if they do not exist. 
    // This should work from any Firefox context. 
    // Depending on the context in which the function is being run, 
    // this could be simplified. 
    if (typeof window === "undefined") { 
     //If there is no window defined, get the most recent. 
     var window=Components.classes["@mozilla.org/appshell/window-mediator;1"] 
          .getService(Components.interfaces.nsIWindowMediator) 
          .getMostRecentWindow("navigator:browser"); 
    } 
    if (typeof document === "undefined") { 
     //If there is no document defined, get it 
     var document = window.content.document; 
    } 
    if (typeof gBrowser === "undefined") { 
     //If there is no gBrowser defined, get it 
     var gBrowser = window.gBrowser; 
    } 

    //Get the current tab & notification box (container for tab UI). 
    let 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").contains("browserSidebarContainer") 
    ) { 
     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 toReturn = null; 
    switch(location) { 
     case "window" : 
      return window.open(chromeUrl,"_blank",features); 
      break; 
     case "top" : 
      if(sizeEl) { 
       sizeEl.removeAttribute("width"); 
      } 
      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. 
      toReturn = notificationBox.insertBefore(docFrag,theChild); 
      break; 
     case "bottom" : 
      if(sizeEl) { 
       sizeEl.removeAttribute("width"); 
      } 
      docFrag.appendChild(splitterEl); 
      docFrag.appendChild(objectEl); 
      //Insert the interface just after theChild. 
      toReturn = notificationBox.insertBefore(docFrag,theChild.nextSibling); 
      break; 
     case "left" : 
      if(sizeEl) { 
       sizeEl.removeAttribute("height"); 
      } 
      docFrag.appendChild(objectEl); 
      //Splitter is second in this orientaiton. 
      docFrag.appendChild(splitterEl); 
      //Insert the interface as the first child of theChild. 
      toReturn = theChild.insertBefore(docFrag,theChild.firstChild); 
      break; 
     case "right" : 
     default  : 
      //Right orientaiton, the default. 
      if(sizeEl) { 
       sizeEl.removeAttribute("height"); 
      } 
      docFrag.appendChild(splitterEl); 
      docFrag.appendChild(objectEl); 
      //Insert the interface as the last child of theChild. 
      toReturn = theChild.appendChild(docFrag); 
      break; 
    } 
    return splitterEl; 
} 

注:工具箱hosts.js代码使用方法getSidebarContainer()寻找到追加的容器。没有关于该方法的文档,所以我使用了getNotificationBox()

+0

我能够根据您的代码创建一个工作面板。感谢您提供清晰而启发性的解释。 :) – user2089518 2014-09-23 20:47:27

+0

很高兴做到这一点。我已将代码扩展为几个函数,这些函数将使用