Bug 808235 - give chatbox focus when restored. r=jaws
authorMark Hammond <mhammond@skippinet.com.au>
Tue, 20 Nov 2012 12:54:50 +1100
changeset 119310 5c2a58449a3d04ac1adade9a93f82f7a9a61d33d
parent 119309 14f42821a132fff2ca6b702f2132532240ea0fc7
child 119311 1aab9de3c55472c04651164b445357cb1bf5b87c
push idunknown
push userunknown
push dateunknown
reviewersjaws
bugs808235
milestone20.0a1
Bug 808235 - give chatbox focus when restored. r=jaws
browser/base/content/browser-social.js
browser/base/content/socialchat.xml
browser/base/content/test/browser_social_chatwindow.js
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -249,20 +249,17 @@ let SocialChatBar = {
   update: function() {
     if (!this.isAvailable)
       this.chatbar.removeAll();
     else {
       this.chatbar.hidden = document.mozFullScreen;
     }
   },
   focus: function SocialChatBar_focus() {
-    if (!this.chatbar.selectedChat)
-      return;
-    let commandDispatcher = gBrowser.ownerDocument.commandDispatcher;
-    commandDispatcher.advanceFocusIntoSubtree(this.chatbar.selectedChat);
+    this.chatbar.focus();
   }
 }
 
 function sizeSocialPanelToContent(panel, iframe) {
   // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
   let doc = iframe.contentDocument;
   if (!doc || !doc.body) {
     return;
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -22,23 +22,30 @@
       <field name="iframe" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "iframe");
       </field>
 
       <property name="minimized">
         <getter>
           return this.getAttribute("minimized") == "true";
         </getter>
-        <setter>
+        <setter><![CDATA[
           this.isActive = !val;
-          if (val)
+          let parent = this.parentNode;
+          if (val) {
             this.setAttribute("minimized", "true");
-          else
+            // If this chat is the selected one a new one needs to be selected.
+            if (parent.selectedChat == this)
+              parent._selectAnotherChat();
+          } else {
             this.removeAttribute("minimized");
-        </setter>
+            // this chat gets selected.
+            parent.selectedChat = this;
+          }
+        ]]></setter>
       </property>
 
       <property name="isActive">
         <getter>
           return this.iframe.docShell.isActive;
         </getter>
         <setter>
           this.iframe.docShell.isActive = !!val;
@@ -118,16 +125,25 @@
       <field name="menupopup" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "nubMenu");
       </field>
 
       <field name="nub" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "nub");
       </field>
 
+      <method name="focus">
+        <body><![CDATA[
+          if (!this.selectedChat)
+            return;
+          let commandDispatcher = gBrowser.ownerDocument.commandDispatcher;
+          commandDispatcher.advanceFocusIntoSubtree(this.selectedChat);
+        ]]></body>
+      </method>
+
       <property name="selectedChat">
         <getter><![CDATA[
           return this._selectedChat;
         ]]></getter>
         <setter><![CDATA[
           // this is pretty horrible, but we:
           // * want to avoid doing touching 'selected' attribute when the
           //   specified chat is already selected.
@@ -136,16 +152,17 @@
           // * need to handle either current or new being null.
           if (this._selectedChat != val) {
             if (this._selectedChat) {
               this._selectedChat.removeAttribute("selected");
             }
             this._selectedChat = val;
             if (val) {
               this._selectedChat.setAttribute("selected", "true");
+              this.focus();
             }
           }
           if (val) {
             this._selectedChat.removeAttribute("activity");
           }
         ]]></setter>
       </property>
 
@@ -189,16 +206,36 @@
           // in the order in which they should be collapsed.
           // (currently this is all visible ones other than the selected one.)
           for (let child of this.visibleChildren)
             if (child != this.selectedChat)
               yield child;
         ]]></getter>
       </property>
 
+      <method name="_selectAnotherChat">
+        <body><![CDATA[
+          // Select a different chat (as the currently selected one is no
+          // longer suitable as the selection - maybe it is being minimized or
+          // closed.)  We only select non-minimized and non-collapsed chats,
+          // and if none are found, set the selectedChat to null.
+          // It's possible in the future we will track most-recently-selected
+          // chats or similar to find the "best" candidate - for now though
+          // the choice is somewhat arbitrary.
+          for (let other of this.children) {
+            if (other != this.selectedChat && !other.minimized && !other.collapsed) {
+              this.selectedChat = other;
+              return;
+            }
+          }
+          // can't find another - so set no chat as selected.
+          this.selectedChat = null;
+        ]]></body>
+      </method>
+
       <method name="updateTitlebar">
         <parameter name="aChatbox"/>
         <body><![CDATA[
           if (aChatbox.collapsed) {
             let menuitem = this.menuitemMap.get(aChatbox);
             if (aChatbox.getAttribute("activity")) {
               menuitem.setAttribute("activity", true);
               this.nub.setAttribute("activity", true);
@@ -301,31 +338,32 @@
           this.resize();
         ]]></body>
       </method>
 
       <method name="_remove">
         <parameter name="aChatbox"/>
         <body><![CDATA[
           if (this.selectedChat == aChatbox) {
-            this.selectedChat = aChatbox.previousSibling ? aChatbox.previousSibling : aChatbox.nextSibling
+            this._selectAnotherChat();
           }
           this.removeChild(aChatbox);
           // child might have been collapsed.
           let menuitem = this.menuitemMap.get(aChatbox);
           if (menuitem) {
             this.menuitemMap.delete(aChatbox);
             this.menupopup.removeChild(menuitem);
           }
           this.chatboxForURL.delete(aChatbox.getAttribute('src'));
         ]]></body>
       </method>
 
       <method name="removeAll">
         <body><![CDATA[
+          this.selectedChat = null;
           while (this.firstChild) {
             this._remove(this.firstChild);
           }
           // and the nub/popup must also die.
           this.nub.collapsed = true;
         ]]></body>
       </method>
 
@@ -391,18 +429,18 @@
               }
               return;
             }
             this.chatboxForURL.delete(aURL);
           }
           cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
           if (aMode == "minimized")
             cb.setAttribute("minimized", "true");
+          this.insertBefore(cb, this.firstChild);
           this.selectedChat = cb;
-          this.insertBefore(cb, this.firstChild);
           this.initChatBox(cb, aProvider, aURL, aCallback);
           this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
           this.resize();
         ]]></body>
       </method>
 
       <method name="resize">
         <body><![CDATA[
--- a/browser/base/content/test/browser_social_chatwindow.js
+++ b/browser/base/content/test/browser_social_chatwindow.js
@@ -584,12 +584,10 @@ function getPopupWidth() {
   ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
   let cs = document.defaultView.getComputedStyle(popup.parentNode);
   let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
   return popup.parentNode.getBoundingClientRect().width + margins;
 }
 
 function closeAllChats() {
   let chatbar = window.SocialChatBar.chatbar;
-  while (chatbar.selectedChat) {
-    chatbar.selectedChat.close();
-  }
+  chatbar.removeAll();
 }