2014-09-21 3 views
1

Pour mon addon, je voudrais créer un "panneau" en bas de la page qui affichera des informations. Un exemple de quelque chose comme ceci serait le panneau de la console Web de Firefox qui peut être basculé sous Outils de développement Web.Comment créer un panneau avec XUL dans Firefox?

J'ai essayé de fouiller dans le code, mais je n'ai pas compris comment cela a été implémenté. Quelqu'un pourrait-il me donner une explication de base sur la façon de créer ceci avec XUL, ou me diriger dans la bonne direction?

Répondre

4

La console Web n'est pas un sidebar. Dans Fireox, il n'y a qu'une seule barre latérale, elle peut être à gauche ou à droite du contenu du navigateur. La barre latérale est une partie constante de l'interface utilisateur, même si vous modifiez les onglets. Il est généralement utilisé pour le contenu, l'historique, les signets ou d'autres informations qui ne changent pas en fonction de l'onglet que vous consultez.

Pour étudier ce genre de choses, je suggérerais le module complémentaire DOM Inspector, si vous ne l'avez pas déjà installé.

La console Web est contenue dans un dans le <tabbrowser> sous l'onglet dans lequel elle se trouve.

Le XUL pour l'iframe est: <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"/>

D'abord un <splitter> alors le sont insérés après la <hbox class="browserSidebarContainer"> pour l'onglet dans lequel il est contenu.

La console Web est ouverte par un appel à gDevToolsBrowser.selectToolCommand(gBrowser, "webconsole");

gDevToolsBrowser est défini dans chrome://browser/content/browser.js du contenu de resource:///modules/devtools/gDevTools.jsm

Ils sont effectivement créés dans la fonction SH_create au sein resource:///modules/devtools/framework/toolbox-hosts.js

Tous ces chrome:// et resource:/// Les URL doivent fonctionner depuis Firefox. Les installations de Firefox ont un grand nombre de fichiers emballés dans trois fichiers cales omni.ja. Les fichiers sont situés dans < répertoire d'installation > /omni.ja, < répertoire d'installation > /browser/omni.ja et < répertoire d'installation > /webart/omni.ja. Les fichiers omni.ja ne sont que des fichiers au format zip avec l'extension renommée. Afin d'avoir un accès facile à ces fichiers, je les décompose systématiquement dans des répertoires (en dehors de l'arborescence du répertoire d'installation de Firefox). Je trouve que cela rend plus facile de faire des recherches et de manipuler les fichiers lorsque je veux comprendre comment quelque chose a été fait.

Si vous étiez à la recherche de code qui créerait une boîte comme celle utilisée pour la console Web, la complexité dépend du contexte dans lequel vous travaillez. Ce qui suit devrait fonctionner à peu près partout:

/** 
* 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; 
} 

Note: Le code dans la boîte à outils-hosts.jsutilise la méthode getSidebarContainer() pour trouver le récipient auquel ajouter. Il n'y a pas de documentation sur cette méthode, j'ai donc utilisé getNotificationBox().

+0

J'ai été capable de créer un panneau de travail basé sur votre code. Merci pour l'explication claire et éclairante. :) – user2089518

+0

Heureux de le faire. J'ai développé le code à quelques fonctions qui vont créer un panneau en utilisant un