--- title: Tabbed browser slug: Code_snippets/Tabbed_browser translation_of: Archive/Add-ons/Tabbed_browser ---

您將在此尋獲協助您運用Firefox的多頁籤瀏覽器的一些程式碼片段,註解將提示您應該在什麼地方插入您自己的程式碼。

 

一般來說這些片段會包含其初始化的程式碼。基本假設是這些程式碼片段將於瀏覽器視窗內容中執行,最佳做法是以 load listener 來運行。若您要從瀏覽器視窗以外的地方來操作頁籤,您必須先取得任意一個瀏覽器視窗,詳見: Working with windows in chrome code

多世界詮釋算什麼,多 browser 詮釋才夠暈

"browser" 有很多意思。把整個 Firefox 稱為 browser 再為理所當然不過,但是 Firefox 有頁籤,每個頁籤本身也都是 browser。無論是一般所指的 web page browser,還是 XUL 裡的 {{ XULElem("browser") }} element,通通叫做 browser。在本文件和其它某些 Firefox 文件裡, browser 還有另一個意思,就是 Firefox XUL window 裡的 tabbrowser element。

如何存取 browser

從主視窗

從 global ChromeWindow 中運行的程式碼,例如套疊 (overlay) 於 browser.xul 的 extension,可以用全域變數 gBrowser 來存取 {{ XULElem("tabbrowser") }} element

// gBrowser 只存在於 browser window (browser.xul)
gBrowser.addTab(...);

如果 gBrowser 未定義,代表您的程式碼要不是太早運行,就是並非從 browser window 中運行。在 browser window 還沒完全載入之前 gBrowser 根本還不存在。若要在視窗一打開時存取 gBrowser ,請在 event listener 中使用它,並 listen 'load' 事件。

若您的程式是 sidebar 或 dialog 而無法存取主視窗,那就要先獲取主視窗才能使用 gBrowser ,詳見: Working with windows in chrome code.

從腮巴 (sidebar) 存取 browser

若您的附加元件基本上是一坨屎 (sidebar) ,那就:

var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                       .rootTreeItem
                       .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIDOMWindow);

mainWindow.gBrowser.addTab(...);

從呆鴨肉 (dialog) 存取 browser

若您的程式基本上運行於某種呆鴨肉 (dialog) ,而且是從 browser window 直接甩出來的精選優質肥美呆鴨肉,那就:

window.opener.gBrowser.addTab(...);

如果 window.opener 搞不定,那就用以下的程式碼先取得最近打開的 browser window :

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                   .getService(Components.interfaces.nsIWindowMediator);

var mainWindow = wm.getMostRecentWindow("navigator:browser");
mainWindow.gBrowser.addTab(...);

如何以 URL 開啟新 tab

// Add tab
gBrowser.addTab("http://www.google.com/");

// Add tab, then make active
gBrowser.selectedTab = gBrowser.addTab("http://www.google.com/");

操作新頁籤的內容

若要操作新開啟頁籤的內容,就要等它載入完。

// 醬子不行啊 .. 人家還沒準備好
var newTabBrowser = gBrowser.getBrowserForTab(gBrowser.addTab("http://www.google.com/"));
alert(newTabBrowser.contentDocument.body.innerHTML);

// BETTER WAY
var newTabBrowser = gBrowser.getBrowserForTab(gBrowser.addTab("http://www.google.com/"));
newTabBrowser.addEventListener("load", function () {
  newTabBrowser.contentDocument.body.innerHTML = "<div>hello world</div>";
}, true);

onLoad handler 中的 event target 是個 'tab' XUL element。 關於 getBrowserForTab(),參見: tabbrowser

如何精準地在 window 或 tab 開啟 URL

chrome://browser/content/utilityOverlay.js 中有許多方法例如 openUILinkInopenUILink, 讓 tab 開啟 URL 變簡單。

openUILinkIn( url, where, allowThirdPartyFixup, postData, referrerUrl )
where 可以是:
openUILink( url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl )
 

下列程式碼將開啟 URL 並依照什麼鬼滑鼠鍵和什麼鬼熱鍵(例:Ctrl)被按下來開新視窗、開新 tab 或是開在現有的 tab 。這是用於 {{ XULElem("menuitem") }} 的程式碼,而在其它的 XUL elements 上也沒什麼兩樣,但只限 browser.xul 的 overlay 。

XUL:

<menuitem oncommand="myExtension.foo(event)" onclick="checkForMiddleClick(this, event)" label="Click me"/>

JS:

var myExtension = {
  foo: function(event) {
    openUILink("http://www.example.com", event, false, true);
  }
}

如何優柔寡斷地開啟 URL

var gSessionStore = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);

// 生一個新 tab 但是沒載入內容
var url = "https://developer.mozilla.org";
var tab = gBrowser.addTab(null);
gSessionStore.setTabState(tab,
  '{\
    "entries":[\
      {\
        "url":"' + url + '",\
        "title":"' + url + '"\
      }\
    ],\
    "lastAccessed":0,\
    "index":1,\
    "hidden":false,\
    "attributes":{},\
    "image":null\
  }'
);

tab 再利用

與其每次需要就再開新的 tab,若已有 tab 開啟了欲前往的 URL,最佳做法就是試著重用它。遵循這個做法可使您的附加元件開啟最少的 tab。

依 URL/URI 來重用

附加元件常見的一項功能是,當 user 按下某個按鈕或連結就從browser window 中將 user 導向至某個 chrome:// 開頭的 URI (像是 help 或 about 這種) 或者一個外部的 HTML (on-line http(s)://) 。接下來的程式碼展示如何重用已顯示所欲前往 URL/URI 的頁籤,如果沒有現存的,則會開個新的。

function openAndReuseOneTabPerURL(url) {
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                     .getService(Components.interfaces.nsIWindowMediator);
  var browserEnumerator = wm.getEnumerator("navigator:browser");

  // Check each browser instance for our URL
  var found = false;
  while (!found && browserEnumerator.hasMoreElements()) {
    var browserWin = browserEnumerator.getNext();
    var tabbrowser = browserWin.gBrowser;

    // Check each tab of this browser instance
    var numTabs = tabbrowser.browsers.length;
    for (var index = 0; index < numTabs; index++) {
      var currentBrowser = tabbrowser.getBrowserAtIndex(index);
      if (url == currentBrowser.currentURI.spec) {

        // The URL is already opened. Select this tab.
        tabbrowser.selectedTab = tabbrowser.tabContainer.childNodes[index];

        // Focus *this* browser-window
        browserWin.focus();

        found = true;
        break;
      }
    }
  }

  // Our URL isn't open. Open it now.
  if (!found) {
    var recentWindow = wm.getMostRecentWindow("navigator:browser");
    if (recentWindow) {
      // Use an existing browser window
      recentWindow.delayedOpenTab(url, null, null, null, null);
    }
    else {
      // No browser windows are open, so open a new one.
      window.open(url);
    }
  }
}
依其它準則來重用

有時候您可能想重用某個先前已由您的附加元件開啟的頁籤,管它目前 URL/URI 是什麼,只要不是其他的元件開啟的。您的確可以為所欲為,只要在第一次開啟時給它一個自訂屬性。之後要重用它的時候,就從所有已開啟的頁籤找出這個有自訂屬性的頁籤,變更其 URL/URI 並 focus 或 select 該頁籤。要是連個屁都沒有 (可能被 user 關了或是根本沒開過),那就開個新的然後給它這個自訂屬性。

function openAndReuseOneTabPerAttribute(attrName, url) {
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                     .getService(Components.interfaces.nsIWindowMediator);
  for (var found = false, index = 0, tabbrowser = wm.getEnumerator('navigator:browser').getNext().gBrowser;
       index < tabbrowser.tabContainer.childNodes.length && !found;
       index++) {

    // Get the next tab
    var currentTab = tabbrowser.tabContainer.childNodes[index];

    // Does this tab contain our custom attribute?
    if (currentTab.hasAttribute(attrName)) {

      // Yes--select and focus it.
      tabbrowser.selectedTab = currentTab;

      // Focus *this* browser window in case another one is currently focused
      tabbrowser.ownerDocument.defaultView.focus();
      found = true;
    }
  }

  if (!found) {
    // Our tab isn't open. Open it now.
    var browserEnumerator = wm.getEnumerator("navigator:browser");
    var tabbrowser = browserEnumerator.getNext().gBrowser;

    // Create tab
    var newTab = tabbrowser.addTab(url);
    newTab.setAttribute(attrName, "xyz");

    // Focus tab
    tabbrowser.selectedTab = newTab;

    // Focus *this* browser window in case another one is currently focused
    tabbrowser.ownerDocument.defaultView.focus();
  }
}

該函式應如此這般調用:

openAndReuseOneTabPerAttribute("myextension-myattribute", "http://developer.mozilla.org/").

如何砍掉 tab

下列程式碼將砍掉目前選擇的 tab。

gBrowser.removeCurrentTab();

另有一個更廣用的 removeTab 方法,以 XUL {{ XULElem("tab") }} element 為唯一的參數。

如何改變 active tab

左三圈 .. ok,並沒有三圈 .. 選定向左一個 tab:

gBrowser.tabContainer.advanceSelectedTab(-1, true);

右三圈 (喔 .. 抱歉)

gBrowser.tabContainer.advanceSelectedTab(1, true);

如何偵測 page load

另見: Code snippets:On page load

function examplePageLoad(event) {
  if (event.originalTarget instanceof HTMLDocument) {
    var win = event.originalTarget.defaultView;
    if (win.frameElement) {
      // Frame within a tab was loaded. win should be the top window of
      // the frameset. If you don't want do anything when frames/iframes
      // are loaded in this web page, uncomment the following line:
      // return;
      // Find the root document:
      win = win.top;
    }
  }
}

// do not try to add a callback until the browser window has
// been initialised. We add a callback to the tabbed browser
// when the browser's window gets loaded.
window.addEventListener("load", function () {
  // Add a callback to be run every time a document loads.
  // note that this includes frames/iframes within the document
  gBrowser.addEventListener("load", examplePageLoad, true);
}, false);

...
// When no longer needed
gBrowser.removeEventListener("load", examplePageLoad, true);
...

tab 增減通知

function exampleTabAdded(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser is the XUL element of the browser that's been added
}

function exampleTabMoved(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser is the XUL element of the browser that's been moved
}

function exampleTabRemoved(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser is the XUL element of the browser that's been removed
}

// During initialization
var container = gBrowser.tabContainer;
container.addEventListener("TabOpen", exampleTabAdded, false);
container.addEventListener("TabMove", exampleTabMoved, false);
container.addEventListener("TabClose", exampleTabRemoved, false);

// When no longer needed
container.removeEventListener("TabOpen", exampleTabAdded, false);
container.removeEventListener("TabMove", exampleTabMoved, false);
container.removeEventListener("TabClose", exampleTabRemoved, false);

{{ gecko_callout_heading("1.9.1") }}

Starting in Gecko 1.9.1 {{ geckoRelease("1.9.1") }}, there's an easy way to listen on progress events on all tabs.

{{ h2_gecko_minversion("Notification when a tab's attributes change", "2.0") }}

自 Gecko 2.0 起,您可以 listen TabAttrModified event 來得知 tab 之 attribute 異動。event listener 可能收到 tab 異動的 attribute 如下:

function exampleTabAttrModified(event) {
  var tab = event.target;
  // Now you can check what's changed on the tab
}

// During initialization
var container = gBrowser.tabContainer;
container.addEventListener("TabAttrModified", exampleTabAttrModified, false);

// When no longer needed
container.removeEventListener("TabAttrModified", exampleTabAttrModified, false);

{{ h2_gecko_minversion("Notification when a tab is pinned or unpinned", "2.0") }}

自 Gecko 2.0 起,tab 可以被 "釘選";也就是說這類 tab 變成一種特殊的 application tabs ("app tabs"),可以被釘在 tab 列的開頭而且只顯示為 favicon。透過 TabPinnedTabUnpinned 事件可以得知 tab 被釘了或被拔了。

function exampleTabPinned(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser is the XUL element of the browser that's been pinned
}

function exampleTabUnpinned(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser is the XUL element of the browser that's been pinned
}

// Initialization

var container = gBrowser.tabContainer;
container.addEventListener("TabPinned", exampleTabPinned, false);
container.addEventListener("TabUnpinned", exampleTabUnpinned, false);

// When no longer needed

container.removeEventListener("TabPinned", exampleTabPinned, false);
container.removeEventListener("TabUnpinned", exampleTabUnpinned, false);

如何偵測 tab 選定

下列程式碼用來得知 browser 中的 tab 被選定:

function exampleTabSelected(event) {
  var browser = gBrowser.selectedBrowser;
  // browser is the XUL element of the browser that's just been selected
}

// During initialisation
var container = gBrowser.tabContainer;
container.addEventListener("TabSelect", exampleTabSelected, false);

// When no longer needed
container.removeEventListener("TabSelect", exampleTabSelected, false);

如何取得已選定 tab 裡的內文(document)

下列程式碼用來獲取選定 tab 中的內文(document),於 browser window scope 中運作,例如從 browser window 的 overlay 。

gBrowser.contentDocument;

content.document

若程式碼是從由 browser window 所開啟的 window 或 dialog,可用以下程式碼獲取已選定 tab 裡所顯示的內文:

window.opener.content.document

若程式從並非自 browser window 所開啟的 window 或 dialog 中運行,則可用 {{ interface("nsIWindowMediator") }} 獲取最近所使用的 browser window 中已選定 tab 裡的內文。

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                   .getService(Components.interfaces.nsIWindowMediator);
var recentWindow = wm.getMostRecentWindow("navigator:browser");
return recentWindow ? recentWindow.content.document.location : null;

另見: Working with windows in chrome code.

如何遍歷 browser

要遍歷 tabbrowser 中所有的 browser,要先取得任意一個 browser 的 window。如果程式碼是從 browser.xul overlay 執行 (譬如 toolbar button 或者 menu click handler),可以用預定義的 window 變數。但若是從獨自的 window 執行 (例如 settings/options dialog) 則要用 {{ interface("nsIWindowMediator") }} 來取得任意一個 browser 的window。

有了一個 window 後再用 win.gBrowser 取得其 <tabbrowser/> element,其中 win 就是上個步驟中所得到的 browser window 。 如果程式是從 browser.xul overlay 執行的話,可以不必寫 window.gBrowser 直接用 gBrowser 就好。很奇怪沒錯,在所有的 IDE 當中也沒有 gBrowser 這個東西,但是別忘了您是在開發 Firefox 的 extension , 這是它本身的 object model 。

最後一步,利用 gBrowser.browsers.length 得知 browser 的數量,並用 gBrowser.getBrowserAtIndex() 取得 <browser/> element。範例:

var num = gBrowser.browsers.length;
for (var i = 0; i < num; i++) {
  var b = gBrowser.getBrowserAtIndex(i);
  try {
    dump(b.currentURI.spec); // dump URLs of all open tabs to console
  } catch(e) {
    Components.utils.reportError(e);
  }
}

透過 DOM Inspector 或參見 {{ source("toolkit/content/widgets/browser.xml","browser.xml") }} 得知對應的 XBL bindings ( 不然參見 {{ XULElem("browser") }} 及 {{ XULElem("tabbrowser") }} 也行),您將可了解 <browser/><tabbrowser/> elements 有哪些方法可用。

如何取得觸發 http-on-modify-request notification 的 browser

關於http-on-* notification 請參見 Observer notifications

請注意,部份 HTTP requests 與 tab 無關;例如 RSS feed updates、extension manager requests 以及 XPCOM components 所發出的 XHR requests 等。在這些情況中,下列程式碼將返回 null 。

注意:該程式碼使用之 getInterface(Components.interfaces.nsIDOMWindow) 應更新為 {{interface("nsILoadContext")}} ,參見範例
observe: function (subject, topic, data) {
  if (topic == "http-on-modify-request") {
    subject.QueryInterface(Components.interfaces.nsIHttpChannel);
    var url = subject.URI.spec; /* url being requested. you might want this for something else */
    var browser = this.getBrowserFromChannel(subject);
    if (browser != null) {
      /* do something */
    }
  }
},

getBrowserFromChannel: function (aChannel) {
  try {
    var notificationCallbacks =
      aChannel.notificationCallbacks ? aChannel.notificationCallbacks : aChannel.loadGroup.notificationCallbacks;

    if (!notificationCallbacks)
      return null;

    var domWin = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow);
    return gBrowser.getBrowserForDocument(domWin.top.document);
  }
  catch (e) {
    dump(e + "\n");
    return null;
  }
}

{{ languages( { "fr": "fr/Extraits_de_code/Onglets_de_navigation", "ja": "ja/Code_snippets/Tabbed_browser", "pl": "pl/Fragmenty_kodu/Przegl\u0105danie_w_kartach" } ) }}