Bug 496647 Set content tab titles from the actual content not pre-stored values. r=philringnalda,ui-review=clarkbw
authorMark Banner <bugzilla@standard8.plus.com>
Tue, 09 Jun 2009 10:46:30 +0100
changeset 2808 61d5f8fd0fe2d3bca890b4b7e69af89c270df8f0
parent 2807 119303f10845456473ab867388b1f0415bcf3a6d
child 2809 af67b109386e987f723d6c1660ea803f34115dc7
child 2894 5ce9e897332fe5f297ca9cbcd0e7d0c7d7a062c5
child 2943 dda72d51a524b15b12a7d07ad97b80e2b5f8ef69
push id2276
push userbugzilla@standard8.plus.com
push dateTue, 09 Jun 2009 09:47:35 +0000
treeherdercomm-central@61d5f8fd0fe2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilringnalda
bugs496647
Bug 496647 Set content tab titles from the actual content not pre-stored values. r=philringnalda,ui-review=clarkbw
mail/base/content/specialTabs.js
mail/base/content/tabmail.xml
mail/locales/en-US/chrome/messenger/aboutRights.properties
mail/locales/en-US/chrome/messenger/messenger.properties
--- a/mail/base/content/specialTabs.js
+++ b/mail/base/content/specialTabs.js
@@ -59,16 +59,22 @@ var specialTabs = {
 
   /**
    * A tab to show content pages.
    */
   contentTabType: {
     name: "contentTab",
     perTabPanel: "vbox",
     lastBrowserId: 0,
+    get loadingTabString() {
+      delete this.loadingTabString;
+      return this.loadingTabString = document.getElementById("bundle_messenger")
+                                             .getString("loadingTab");
+    },
+
     modes: {
       contentTab: {
         type: "contentTab",
         maxTabs: 10
       }
     },
     shouldSwitchTo: function onSwitchTo(aContentPage, aTitle) {
       let tabmail = document.getElementById("tabmail");
@@ -89,48 +95,58 @@ var specialTabs = {
           // Ensure we go to the correct location on the page.
           tabInfo[selectedIndex].panel.firstChild
                                 .setAttribute("src", aContentPage);
           return selectedIndex;
         }
       }
       return -1;
     },
-    // XXX it would be nice to have an onload listener and set the title
-    // after the load, however tabmail doesn't support that at the moment.
-    openTab: function onTabOpened(aTab, aContentPage, aTitle) {
+    openTab: function onTabOpened(aTab, aContentPage) {
       // You can't dynamically change an iframe from a non-content to a content
       // type, therefore we dynamically create the element instead.
       let iframe = document.createElement("browser");
       iframe.setAttribute("type", "content-primary");
       iframe.setAttribute("flex", "1");
       iframe.setAttribute("autocompleteenabled", false);
       iframe.setAttribute("disablehistory", true);
       iframe.setAttribute("id", "contentTabType" + this.lastBrowserId);
 
+      function onDOMTitleChanged(aEvent) {
+        document.getElementById("tabmail").setTabTitle(aTab);
+      }
+      // Save the function we'll use as listener so we can remove it later.
+      aTab.contentTabType = { titleListener: onDOMTitleChanged };
+      // Add the listener.
+      iframe.addEventListener("DOMTitleChanged",
+                              aTab.contentTabType.titleListener, true);
+
+      aTab.title = this.loadingTabString;
+
       aTab.panel.appendChild(iframe);
 
       iframe.setAttribute("src", aContentPage);
 
-      aTab.title = aTitle;
-
       let findbar = document.createElement("findbar");
       findbar.setAttribute("browserid", "contentTabType" + this.lastBrowserId);
       aTab.panel.appendChild(findbar);
       this.lastBrowserId++;
     },
     closeTab: function onTabClosed(aTab) {
+      aTab.panel.firstChild.removeEventListener("DOMTitleChanged",
+                                                aTab.contentTabType.titleListener, true);
     },
     saveTabState: function onSaveTabState(aTab) {
       aTab.panel.firstChild.setAttribute("type", "content-targetable");
     },
     showTab: function onShowTab(aTab) {
       aTab.panel.firstChild.setAttribute("type", "content-primary");
     },
     onTitleChanged: function onTitleChanged(aTab) {
+      aTab.title = aTab.panel.firstChild.contentDocument.title;
     },
     supportsCommand: function supportsCommand(aTab, aCommand) {
       switch (aCommand) {
         case "cmd_fullZoomReduce":
         case "cmd_fullZoomEnlarge":
         case "cmd_fullZoomReset":
         case "cmd_fullZoomToggle":
         case "cmd_find":
@@ -216,20 +232,17 @@ var specialTabs = {
    * Shows the what's new page in a content tab.
    */
   showWhatsNewPage: function onShowWhatsNewPage() {
     let startpage =
         Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                   .getService(Components.interfaces.nsIURLFormatter)
                   .formatURLPref("mailnews.start_page.override_url");
 
-    let msgBundle = document.getElementById("bundle_messenger");
-
-    document.getElementById('tabmail').openTab("contentTab", startpage,
-                                               msgBundle.getString("whatsNew"));
+    document.getElementById('tabmail').openTab("contentTab", startpage);
   },
 
   /**
    * Looks at the existing prefs and determines if we should show about:rights
    * or not.
    *
    * This is controlled by two prefs:
    *
@@ -261,28 +274,26 @@ var specialTabs = {
     var brandBundle =
       stringBundle.createBundle("chrome://branding/locale/brand.properties");
     var rightsBundle =
       stringBundle.createBundle("chrome://messenger/locale/aboutRights.properties");
 
     var productName = brandBundle.GetStringFromName("brandFullName");
     var notifyRightsText = rightsBundle.formatStringFromName("notifyRightsText",
                                                              [productName], 1);
-    var rightsTitle = rightsBundle.GetStringFromName("aboutRightsTitle");
 
     var buttons = [
       {
         label: rightsBundle.GetStringFromName("buttonLabel"),
         accessKey: rightsBundle.GetStringFromName("buttonAccessKey"),
         popup: null,
         callback: function(aNotificationBar, aButton) {
           // Show the about:rights tab
           document.getElementById('tabmail').openTab("contentTab",
-                                                     "about:rights",
-                                                     rightsTitle);
+                                                     "about:rights");
         }
       }
     ];
 
     var box = notifyBox.appendNotification(notifyRightsText, "about-rights",
                                            null, notifyBox.PRIORITY_INFO_LOW,
                                            buttons);
     // arbitrary number, just so bar sticks around for a bit
--- a/mail/base/content/tabmail.xml
+++ b/mail/base/content/tabmail.xml
@@ -71,17 +71,17 @@
     - 1) Code that wants to open new tabs.
     - 2) Code that wants to contribute one or more varieties of tabs.
     - 3) Code that wants to monitor to know when the active tab changes.
     -
     - Consumer code should use the following methods:
     - * openTab(aTabModeName, arguments...): Open a tab of the given "mode",
     -   passing the provided arguments.  The tab type author should tell you
     -   the modes they implement and the required/optional arguments.
-    - * setTabTitle([aOptionalTabNode]): Tells us that the title of the current
+    - * setTabTitle([aOptionalTabInfo]): Tells us that the title of the current
     -   tab (if no argument is provided) or provided tab needs to be updated.
     -   This will result in a call to the tab mode's logic to update the title.
     -   In the event this is not for the current tab, the caller is responsible
     -   for ensuring that the underlying tab mode is capable of providing a tab
     -   title when it is in the background.  (The is currently not the case for
     -   "folder" and "mail" modes because of their implementation.)
     - * removeCurrentTab(): Close the current tab.
     - * removeTabByNode(aTabElement): Close the tab whose tabmail-tab bound
@@ -298,21 +298,20 @@
         <body><![CDATA[
           // From the moment of creation, our XBL binding already has a visible
           //  tab.  We need to create a tab information structure for this tab.
           //  In the process we also generate a synthetic tab title changed
           //  event to ensure we have an accurate title.  We assume the tab
           //  contents will set themselves up correctly.
           if (this.tabInfo.length == 0) {
             let firstTab = {mode: this.defaultTabMode, canClose: false};
-            let firstTabNode = this.tabContainer.firstChild;
             firstTab.mode.tabs.push(firstTab);
            
             this.tabInfo[0] = this.currentTabInfo = firstTab;
-            this.setTabTitle(firstTabNode);
+            this.setTabTitle(firstTab);
 
             if (this.tabMonitors.length) {
               for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
                 tabMonitor.onTabSwitched(tab, null);
             }
           }
         ]]></body>
       </method>
@@ -572,44 +571,60 @@
             if (event.button != 1 || event.target.localName != 'tab')
               return;
             this.removeTabByNode(event.target);
             event.stopPropagation();
           ]]>
         </body>
       </method>
       <method name="setTabTitle">
-        <parameter name="aTabNode"/>
+        <parameter name="aTab"/>
         <body>
           <![CDATA[
-            if (!aTabNode)
-              aTabNode =
-                this.tabContainer.childNodes[this.tabContainer.selectedIndex];
-                
-            // get the owner for the tab...
-            var i;
-            var numTabs = this.tabContainer.childNodes.length;
-            for (i = 0; i < numTabs; i++)
-              if (this.tabContainer.childNodes[i] == aTabNode)
-                break;
+            // First find the tab and its index.
+            let tab;
+            let index;
+            if (aTab) {
+              tab = aTab;
+              for (index = 0; index < this.tabInfo.length; ++index) {
+                if (tab == this.tabInfo[index])
+                  break;
+              }
+            }
+            else {
+              index = this.tabContainer.selectedIndex;
+              tab = this.tabInfo[index];
+            }
+
             // on startup, we may not have a tab...
-            let tab = this.tabInfo[i];
             if (tab)
             {
+              let tabNode =
+                this.tabContainer.childNodes[index];
+
               let titleChangeFunc = tab.mode.onTitleChanged ||
                                     tab.mode.tabType.onTitleChanged;
               if (titleChangeFunc)
-                titleChangeFunc.call(tab.mode.tabType, tab, aTabNode);
+                titleChangeFunc.call(tab.mode.tabType, tab, tabNode);
 
               if (this.tabMonitors.length) {
-                for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
+                for each (let [index, tabMonitor] in Iterator(this.tabMonitors))
                   tabMonitor.onTabTitleChanged(tab);
               }
-                                    
-              aTabNode.setAttribute("label", tab.title);
+
+              tabNode.setAttribute("label", tab.title);
+
+              // Update the window title if we're the displayed tab.
+              if (index == this.tabContainer.selectedIndex) {
+                let docTitle = tab.title;
+#ifndef XP_MACOSX
+                docTitle += " - " + gBrandBundle.getString("brandFullName");
+#endif
+                document.title = docTitle;
+              }
             }
           ]]>
         </body>
       </method>
       <method name="onTabContextMenuShowing">
         <parameter name="aTabNode"/>
         <body>
           <![CDATA[
--- a/mail/locales/en-US/chrome/messenger/aboutRights.properties
+++ b/mail/locales/en-US/chrome/messenger/aboutRights.properties
@@ -1,5 +1,4 @@
-aboutRightsTitle=About Your Rights
 buttonLabel=Know your rights…
 buttonAccessKey=K
 # LOCALIZATION NOTE (notifyText): %S will be replaced by brandFullName from brand.properties
 notifyRightsText=%S is free and open source software from the non-profit Mozilla Foundation.
--- a/mail/locales/en-US/chrome/messenger/messenger.properties
+++ b/mail/locales/en-US/chrome/messenger/messenger.properties
@@ -475,18 +475,18 @@ processingJunkMessages=Processing Junk M
 fileNotFoundTitle = File Not Found
 #LOCALIZATION NOTE(fileNotFoundMsg): %S is the filename
 fileNotFoundMsg = The file %S does not exist.
 
 # second person direct object pronoun; used in the collapsed header view if
 # the user is in the To or Cc field of a message
 headerFieldYou=You
 
-# The what's new tab title, shown on version update
-whatsNew=What's New
+# Shown when content tabs are being loaded.
+loadingTab=Loading…
 
 applyToCollapsedMsgsTitle=Confirm Delete of Messages in Collapsed Thread(s)
 applyToCollapsedMsgs=Warning - this will delete messages in collapsed thread(s)
 applyToCollapsedAlwaysAskCheckbox=Always ask me before deleting messages in collapsed threads
 applyNowButton=Apply
 
 #LOCALIZATION NOTE (glodaSearch_results_why_*):  These strings populate the
 # glodaWhyColumn when performing a gloda-backed search, explaining why a