Bug 953867 - Add support for tabs with arbitrary content in the conversation window, r=Mic,florian.

This commit is contained in:
Nihanth Subramanya 2013-06-21 16:36:07 +05:30
Родитель c0b4936627
Коммит 8c1d9e9e8b
12 изменённых файлов: 424 добавлений и 334 удалений

Просмотреть файл

@ -405,10 +405,9 @@
let localName = elt.localName;
if (localName == "tab") {
let conv = elt.linkedConversation.conv;
if (conv)
return updateTooltipFromConversation(conv, elt);
return false;
if (!elt.linkedConversation || !elt.linkedConversation.conv)
return false;
return updateTooltipFromConversation(elt.linkedConversation.conv, elt);
}
if (localName == "conv")

Просмотреть файл

@ -81,6 +81,9 @@ var FullZoom = {
* checking ourselves if it differs costs more.
**/
applyPrefValue: function FullZoom_applyPrefValue() {
// If there's no browser (non-conversation tabs), don't do anything.
if (!getBrowser())
return;
let value = parseFloat(Services.prefs.getCharPref(FullZoom.prefName));
if (isNaN(value))
value = 1;

Просмотреть файл

@ -147,6 +147,10 @@
<parameter name="aConversation"/>
<body>
<![CDATA[
// Swap the docshells.
this.browser.swapDocShells(aConversation.browser);
// Ensure observers are removed.
aConversation.conv = null;
this.editor.value = aConversation.editor.value;
this.browser.browserResize();
this.updateTyping();
@ -918,7 +922,7 @@
</body>
</method>
<method name="onConvResize">
<method name="onResize">
<parameter name="event"/>
<body>
<![CDATA[
@ -1477,15 +1481,85 @@
</body>
</method>
<method name="switchingToPanel">
<body>
<![CDATA[
if (this._visibleTimer)
return;
// Start a timer to detect if the tab has been visible to the
// user for long enough to actually be seen (as opposed to the
// tab only being visible "accidentally in passing").
delete this._wasVisible;
this._visibleTimer = setTimeout(function() {
this._wasVisible = true;
delete this._visibleTimer;
}.bind(this), 1000);
]]>
</body>
</method>
<method name="focus">
<body>
<![CDATA[
this.editor.focus();
this.onSelect();
]]>
</body>
</method>
<method name="switchingAwayFromPanel">
<body>
<![CDATA[
if (this._visibleTimer) {
clearTimeout(this._visibleTimer);
delete this._visibleTimer;
}
// Remove the unread ruler if the tab has been visible without
// interruptions for sufficiently long.
if (this._wasVisible)
this.browser.removeUnreadRuler();
]]>
</body>
</method>
<method name="getPanelSpecificMenuItems">
<body>
<![CDATA[
let items = [];
const XUL_NS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let bundle =
Services.strings.createBundle("chrome://instantbird/locale/conversation.properties");
let conv = this;
function createMenuItem(aId, aCommandHandler) {
let item = document.createElementNS(XUL_NS, "menuitem");
item.setAttribute("label", bundle.GetStringFromName(aId + ".label"));
item.setAttribute("accesskey", bundle.GetStringFromName(aId + ".accesskey"));
item.addEventListener("command", aCommandHandler);
return item;
}
let showLogsItem = createMenuItem("contextShowLogs", function() conv.showLogs());
showLogsItem.disabled = !this.hasLogs();
items.push(showLogsItem);
let hideConvItem = createMenuItem("contextHideConv", function() {
conv.hide();
document.getBindingParent(conv).removeTab(conv.tab);
});
items.push(hideConvItem);
let closeConvItem = createMenuItem("contextCloseConv", function() {
conv.close();
document.getBindingParent(conv).removeTab(conv.tab);
});
items.push(closeConvItem);
return items;
]]>
</body>
</method>
<method name="hasLogs">
<body>
<![CDATA[
@ -1663,8 +1737,10 @@
else if (this._conv.contact && this._conv.contact.getBuddies().length > 1)
this.getElt("conv-top-info").allowTargetChange();
if (this.tab)
if (this.tab) {
this.tab.setAttribute("label", this._conv.title);
this.tab.setAttribute("tooltip", "buddyTooltip");
}
if (!("Status" in window))
Components.utils.import("resource:///modules/imStatusUtils.jsm");

Просмотреть файл

@ -15,7 +15,7 @@ var convWindow = {
// swap the given tab with the default dummy conversation tab
// and then close the original tab in the other window.
let tab = window.arguments.shift();
document.getElementById("conversations").importConversation(tab);
getTabBrowser().importPanel(tab);
}
}
@ -36,10 +36,13 @@ var convWindow = {
Conversations.onWindowFocus(window);
setTimeout(function () {
// setting the focus to the textbox just after the window is
// activated puts the textbox in an unconsistant state, some
// activated puts the textbox in an inconsistent state, some
// special characters like ^ don't work, so delay the focus
// operation...
getBrowser().selectedConversation.focus();
let panel = getTabBrowser().selectedPanel;
panel.focus();
if ("onSelect" in panel)
panel.onSelect();
}, 0);
},
onresize: function mo_onresize(aEvent) {
@ -47,18 +50,19 @@ var convWindow = {
return;
// Resize each textbox (if the splitter has not been used).
let convs = getBrowser().conversations;
for each (let conv in convs)
conv.onConvResize(aEvent);
let panels = getTabBrowser().tabPanels;
for (let panel of panels) {
if ("onResize" in panel)
panel.onResize(aEvent);
}
}
};
function getConvWindowURL() "chrome://instantbird/content/instantbird.xul"
function getBrowser()
{
return document.getElementById("conversations");
}
function getTabBrowser() document.getElementById("conversations")
function getBrowser() getTabBrowser().selectedBrowser
// Copied from mozilla/browser/base/content/browser.js (and simplified)
var XULBrowserWindow = {

Просмотреть файл

@ -32,7 +32,7 @@
titlemodifier="&convWindow.titlemodifier;"
width = "500"
height = "600"
onclose= "return getBrowser().warnAboutClosingTabs(true);"
onclose= "return getTabBrowser().warnAboutClosingTabs(true);"
persist= "width height screenX screenY"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
@ -52,20 +52,27 @@
#endif
<commandset id="conversationsCommands">
<command id="cmd_close" oncommand="document.getElementById('conversations').removeCurrentTab()"/>
<command id="cmd_putOnHold" oncommand="var tabbrowser = document.getElementById('conversations');
tabbrowser.mCurrentTab.linkedConversation.hide();
tabbrowser.removeTab(tabbrowser.mCurrentTab);"/>
<command id="cmd_showLogs" oncommand="document.getElementById('conversations').mCurrentTab.linkedConversation.showLogs();"/>
<command id="cmd_textZoomReduce" oncommand="FullZoom.reduce();"/>
<command id="cmd_textZoomEnlarge" oncommand="FullZoom.enlarge();"/>
<command id="cmd_textZoomReset" oncommand="FullZoom.reset();"/>
<command id="cmd_close" oncommand="getTabBrowser().removeCurrentTab()"/>
<command id="cmd_putOnHold"
oncommand="var tabbrowser = getTabBrowser();
if (!tabbrowser.selectedConversation) return;
tabbrowser.selectedConversation.hide();
tabbrowser.removeCurrentTab();"/>
<command id="cmd_showLogs"
oncommand="var conv = getTabBrowser().selectedConversation;
if (conv) conv.showLogs();"/>
<command id="cmd_textZoomReduce" oncommand="if (getBrowser()) FullZoom.reduce();"/>
<command id="cmd_textZoomEnlarge" oncommand="if (getBrowser()) FullZoom.enlarge();"/>
<command id="cmd_textZoomReset" oncommand="if (getBrowser()) FullZoom.reset();"/>
<command id="cmd_find"
oncommand="document.getElementById('conversations').findbar.onFindCommand();"/>
oncommand="var conv = getTabBrowser().selectedConversation;
if (conv) conv.findbar.onFindCommand();"/>
<command id="cmd_findAgain"
oncommand="document.getElementById('conversations').findbar.onFindAgainCommand(false);"/>
oncommand="var conv = getTabBrowser().selectedConversation;
if (conv) conv.findbar.onFindAgainCommand(false);"/>
<command id="cmd_findPrevious"
oncommand="document.getElementById('conversations').findbar.onFindAgainCommand(true);"/>
oncommand="var conv = getTabBrowser().selectedConversation;
if (conv) conv.findbar.onFindAgainCommand(true);"/>
<commandset id="editMenuCommands"/>
</commandset>
@ -96,15 +103,15 @@
#else
#define NUM_SELECT_TAB_MODIFIER accel
#endif
#expand <key id="key_selectTab1" oncommand="getBrowser().selectTabAtIndex(0, event);" key="1" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab2" oncommand="getBrowser().selectTabAtIndex(1, event);" key="2" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab3" oncommand="getBrowser().selectTabAtIndex(2, event);" key="3" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab4" oncommand="getBrowser().selectTabAtIndex(3, event);" key="4" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab5" oncommand="getBrowser().selectTabAtIndex(4, event);" key="5" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab6" oncommand="getBrowser().selectTabAtIndex(5, event);" key="6" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab7" oncommand="getBrowser().selectTabAtIndex(6, event);" key="7" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab8" oncommand="getBrowser().selectTabAtIndex(7, event);" key="8" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectLastTab" oncommand="getBrowser().selectTabAtIndex(-1, event);" key="9" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab1" oncommand="getTabBrowser().selectTabAtIndex(0, event);" key="1" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab2" oncommand="getTabBrowser().selectTabAtIndex(1, event);" key="2" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab3" oncommand="getTabBrowser().selectTabAtIndex(2, event);" key="3" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab4" oncommand="getTabBrowser().selectTabAtIndex(3, event);" key="4" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab5" oncommand="getTabBrowser().selectTabAtIndex(4, event);" key="5" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab6" oncommand="getTabBrowser().selectTabAtIndex(5, event);" key="6" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab7" oncommand="getTabBrowser().selectTabAtIndex(6, event);" key="7" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab8" oncommand="getTabBrowser().selectTabAtIndex(7, event);" key="8" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectLastTab" oncommand="getTabBrowser().selectTabAtIndex(-1, event);" key="9" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
</keyset>
<stringbundleset id="stringbundleset">
@ -113,11 +120,11 @@
<popupset id="mainPopupSet">
<tooltip id="aHTMLTooltip"
onpopupshowing="return getBrowser().selectedBrowser.FillInHTMLTooltip(document.tooltipNode);"/>
onpopupshowing="return getBrowser().FillInHTMLTooltip(document.tooltipNode);"/>
<tooltip id="buddyTooltip" type="buddy"/>
<menupopup id="contentAreaContextMenu"
onpopupshowing="if (event.target != this) return true; gContextMenu = new nsContextMenu(this, window.getBrowser()); return gContextMenu.shouldDisplay;"
onpopupshowing="if (event.target != this) return true; gContextMenu = new nsContextMenu(this, window.getTabBrowser()); return gContextMenu.shouldDisplay;"
onpopuphiding="if (event.target == this &amp;&amp; gContextMenu) { gContextMenu.cleanup(); gContextMenu = null; }">
<menuitem id="context-openlink"
label="&openLinkCmd.label;"

Просмотреть файл

@ -148,14 +148,26 @@ let gGestureSupport = {
this._tabs.selectedIndex++;
break;
case "swipe-down":
// This gesture isn't available if there's no browser.
if (!getBrowser())
break;
if (aEvent.originalTarget.ownerDocument == getBrowser().contentDocument)
getBrowser().contentWindow.focus();
getBrowser().selectedBrowser.scrollToNextSection();
if (getTabBrowser().selectedConversation)
getBrowser().scrollToNextSection();
else
goDoCommand("cmd_scrollBottom");
break;
case "swipe-up":
// This gesture isn't available if there's no browser.
if (!getBrowser())
break;
if (aEvent.originalTarget.ownerDocument == getBrowser().contentDocument)
getBrowser().contentWindow.focus();
getBrowser().selectedBrowser.scrollToPreviousSection();
if (getTabBrowser().selectedConversation)
getBrowser().scrollToPreviousSection();
else
goDoCommand("cmd_scrollTop");
break;
case "swipe-left":
case "swipe-right":

Просмотреть файл

@ -53,3 +53,8 @@ tabconversation {
#context_newTabSeparator {
display: none;
}
/* Ensure two menuseparators aren't shown together when no tab-specific menuitems exist */
#context_tabSpecificStartSeparator + #context_tabSpecificEndSeparator {
display: none;
}

Просмотреть файл

@ -25,64 +25,51 @@
<content>
<xul:stringbundle anonid="tbstringbundle" src="chrome://instantbird/locale/tabbrowser.properties"/>
<xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
onselect="if (event.target.localName != 'tabpanels') return;
document.getBindingParent(this).updateCurrentTab();">
<xul:hbox class="tab-drop-indicator-bar" collapsed="true" chromedir="&locale.dir;"
ondragover="this.parentNode.parentNode._onDragOver(event);"
ondragleave="this.parentNode.parentNode._onDragLeave(event);"
ondrop="this.parentNode.parentNode._onDrop(event);">
ondragover="document.getBindingParent(this)._onDragOver(event);"
ondragleave="document.getBindingParent(this)._onDragLeave(event);"
ondrop="document.getBindingParent(this)._onDrop(event);">
<xul:hbox class="tab-drop-indicator" mousethrough="always"/>
</xul:hbox>
<xul:hbox class="tabbrowser-strip" collapsed="true"
tooltip="buddyTooltip" context="_child"
context="_child"
anonid="strip"
ondragstart="this.parentNode.parentNode._onDragStart(event);"
ondragover="this.parentNode.parentNode._onDragOver(event);"
ondrop="this.parentNode.parentNode._onDrop(event);"
ondragend="this.parentNode.parentNode._onDragEnd(event);"
ondragleave="this.parentNode.parentNode._onDragLeave(event);">
<xul:menupopup id="tabContextMenu" onpopupshowing="return this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
ondragstart="document.getBindingParent(this)._onDragStart(event);"
ondragover="document.getBindingParent(this)._onDragOver(event);"
ondrop="document.getBindingParent(this)._onDrop(event);"
ondragend="document.getBindingParent(this)._onDragEnd(event);"
ondragleave="document.getBindingParent(this)._onDragLeave(event);">
<xul:menupopup id="tabContextMenu" onpopupshowing="return document.getBindingParent(this).tabContextMenuShowing(this);"
onpopuphiding="return document.getBindingParent(this).tabContextMenuHiding(this)">
<xul:menuitem id="context_newTab" label="&newTab.label;" accesskey="&newTab.accesskey;"
xbl:inherits="oncommand=onnewtab"/>
<xul:menuseparator id="context_newTabSeparator"/>
<xul:menuitem id="context_openTabInWindow" label="&openTabInNewWindow.label;"
accesskey="&openTabInNewWindow.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
oncommand="var tabbrowser = document.getBindingParent(this);
tabbrowser.replaceTabsWithWindow([tabbrowser.mContextTab]);"/>
<xul:menuseparator/>
<xul:menuitem id="context_showLogs" label="&showLogs.label;" accesskey="&showLogs.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.mContextTab.linkedConversation.showLogs();"/>
<xul:menuseparator/>
<xul:menuitem id="context_closeConv" label="&closeConv.label;" accesskey="&closeConv.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.mContextTab.linkedConversation.close();
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
<xul:menuitem id="context_hideConv" label="&hideConv.label;" accesskey="&hideConv.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.mContextTab.linkedConversation.hide();
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
<xul:menuseparator/>
<xul:menuseparator id="context_tabSpecificStartSeparator"/>
<xul:menuseparator id="context_tabSpecificEndSeparator"/>
<xul:menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
oncommand="var tabbrowser = document.getBindingParent(this);
tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
<xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
oncommand="var tabbrowser = document.getBindingParent(this);
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
</xul:menupopup>
<xul:tabs class="tabbrowser-tabs" flex="1"
anonid="tabcontainer"
setfocus="false"
onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
onclick="document.getBindingParent(this).onTabClick(event);"
xbl:inherits="onnewtab"
ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
onclosetab="var node = this.parentNode;
while (node.localName != 'tabbrowser')
node = node.parentNode;
node.removeCurrentTab();"
onkeypress="this.parentNode.parentNode.parentNode.onTabKeypress(event);">
ondblclick="document.getBindingParent(this).onTabBarDblClick(event);"
onclosetab="document.getBindingParent(this).removeCurrentTab();"
onkeypress="document.getBindingParent(this).onTabKeypress(event);">
<xul:tab selected="true" validate="never"
onerror="this.removeAttribute('image');"
maxwidth="250" width="0" minwidth="100" flex="100"
@ -90,7 +77,7 @@
</xul:tabs>
</xul:hbox>
<xul:tabpanels flex="1" class="tabbrowser-tabpanels plain" selectedIndex="0" anonid="panelcontainer">
<xul:conversation selected="true"/>
<xul:tabpanel selected="true"/>
</xul:tabpanels>
</xul:tabbox>
<children/>
@ -120,9 +107,6 @@
<field name="mCurrentTab">
null
</field>
<field name="mCurrentBrowser">
null
</field>
<field name="mFirstTabIsDummy">
true
</field>
@ -136,10 +120,14 @@
false
#endif
</field>
<field name="_browsers">
<!-- _conversations and _tabPanels are used as caches
to avoid creating arrays multiple times
(See conversations and tabPanels properties) -->
<field name="_conversations">
null
</field>
<field name="_conversations">
<field name="_tabPanels">
null
</field>
@ -150,28 +138,10 @@
null
</field>
<method name="getBrowserAtIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
return this.browsers[aIndex];
]]>
</body>
</method>
<method name="getConversationAtIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
return this.conversations[aIndex];
]]>
</body>
</method>
<method name="updateTitlebar">
<body>
<![CDATA[
if (!this.mCurrentConversation) // tabbrowser not initialized yet
if (!this.mCurrentTab) // tabbrowser not initialized yet
return;
var newTitle = "";
@ -179,9 +149,7 @@
var docElement = this.ownerDocument.documentElement;
var sep = docElement.getAttribute("titlemenuseparator");
if (this.mCurrentTab)
docTitle = this.mCurrentTab.getAttribute("label");
docTitle = this.mCurrentTab.getAttribute("label");
if (!docTitle)
docTitle = docElement.getAttribute("titledefault");
@ -199,7 +167,7 @@
</body>
</method>
<method name="updatePopupMenu">
<method name="tabContextMenuShowing">
<parameter name="aPopupMenu"/>
<body>
<![CDATA[
@ -209,33 +177,66 @@
this.mContextTab = tagName == "tab" ?
document.popupNode : this.selectedTab;
var disabled = this.mTabs.length == 1;
var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
for (var i = 0; i < menuItems.length; i++)
menuItems[i].disabled = disabled;
document.getElementById("context_showLogs").disabled =
!this.mContextTab.linkedConversation.hasLogs();
var multipleTabMenuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
for (let item of multipleTabMenuItems)
item.disabled = disabled;
let tabSpecificEndSeparator = document.getElementById("context_tabSpecificEndSeparator");
if ("getPanelSpecificMenuItems" in this.mContextTab.linkedTabPanel) {
// Add in tab-specific menu items from the tab panel
let panelMenuItems = this.mContextTab.linkedTabPanel.getPanelSpecificMenuItems();
for (let item of panelMenuItems)
aPopupMenu.insertBefore(item, tabSpecificEndSeparator);
}
return true;
]]>
</body>
</method>
<method name="updateCurrentBrowser">
<method name="tabContextMenuHiding">
<parameter name="aPopupMenu"/>
<body>
<![CDATA[
// Remove tab specific menu items added onpopupshowing.
let range = document.createRange();
range.setStartAfter(document.getElementById("context_tabSpecificStartSeparator"));
range.setEndBefore(document.getElementById("context_tabSpecificEndSeparator"));
range.deleteContents();
return true;
]]>
</body>
</method>
<method name="updateCurrentTab">
<parameter name="aForceUpdate"/>
<body>
<![CDATA[
var newConversation = this.getConversationAtIndex(this.mTabContainer.selectedIndex);
if (!aForceUpdate && this.mCurrentConversation == newConversation)
/* This method handles transitioning when switching tabs.
* When a new tab is selected, mCurrentTab still refers to the
* previously selected tab until we set it in this method.
* this.selectedTab always refers to the actual selected tab
* (see the "selectedTab" property).
* Also, the selected* properties other than selectedTab use
* mCurrentTab and not selectedTab. This ensures a newly selected
* tab's properties are not accessed till this method is called.
*/
// We check that the currently selected tab is different from
// mCurrentTab (i.e. a different tab was selected) before updating.
if (!aForceUpdate && this.mCurrentTab == this.selectedTab)
return;
this.mCurrentConversation = newConversation;
this.mCurrentBrowser.docShell.isActive = false;
this.mCurrentBrowser = newConversation.browser;
this.mCurrentBrowser.docShell.isActive =
(window.windowState != window.STATE_MINIMIZED);
// Deactivate the previous browser if it existed...
if (this.selectedBrowser)
this.selectedBrowser.docShell.isActive = false;
// ... set mCurrentTab to newly selected tab...
this.mCurrentTab = this.selectedTab;
this.mCurrentTab.switchingToTab();
// ... and activate the new browser if it exists.
if (this.selectedBrowser) {
this.mCurrentTab.linkedBrowser.docShell.isActive =
(window.windowState != window.STATE_MINIMIZED);
}
if ("switchingToPanel" in this.selectedPanel)
this.selectedPanel.switchingToPanel();
// Update the window title.
this.updateTitlebar();
@ -259,14 +260,18 @@
// and "600ms - a bit too noticeable already".
if (this._tabSelectTimer)
clearTimeout(this._tabSelectTimer);
if (!("onSelect" in this.selectedPanel))
return;
this._tabSelectTimer = setTimeout(function() {
this.mCurrentConversation.onSelect();
this.selectedPanel.onSelect();
}.bind(this), 400);
return;
}
delete this._tabSelectTimer;
this.mCurrentConversation.focus();
this.selectedPanel.focus();
if ("onSelect" in this.selectedPanel)
this.selectedPanel.onSelect();
]]>
</body>
</method>
@ -283,10 +288,10 @@
if (tabKeyCodes.indexOf(event.keyCode) != -1)
return;
// Focus the editbox and pass the key to it.
// Focus the panel and pass the key to it.
event.preventDefault();
event.stopPropagation();
this.mCurrentConversation.editor.focus();
this.selectedPanel.focus();
const masks = Components.interfaces.nsIDOMNSEvent;
var modifiers = 0;
@ -302,7 +307,7 @@
modifiers |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK
: masks.CONTROL_MASK;
// Can't use dispatchEvent to the textbox as these refuse untrusted key events.
this.mCurrentConversation.ownerDocument.defaultView
this.selectedPanel.ownerDocument.defaultView
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.sendKeyEvent(event.type, event.keyCode, event.charCode, modifiers);
@ -399,66 +404,105 @@
<body>
<![CDATA[
if (!this.mFirstTabIsDummy) {
if (!Services.prefs.getBoolPref("messenger.conversations.openInTabs"))
return null;
if (Services.prefs.getBoolPref("messenger.conversations.useSeparateWindowsForMUCs") &&
aConv.isChat != this.conversations[0].hasAttribute("chat"))
this.conversations[0] && aConv.isChat != this.conversations[0].hasAttribute("chat"))
return null;
}
return this._addConversation(aConv);
]]>
let convPanel = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"conversation");
return this.addPanel(convPanel, aConv);
]]>
</body>
</method>
<method name="_addConversation">
<!-- Adds a tab panel.
Conversation specific properties and attributes are set if aConv is defined.
Note that a panel must focus a child element in its focus method to
prevent a previously focused element from retaining focus and continuing
to receive input.
Panels can implement the following methods to customize behavior in certain situations:
destroy:
Called before panel is removed.
The destructor doesn't always get called when the panel is removed,
so use this method to force any required cleanup.
finishImport:
When a tab is moved to a different window, a new instance of the panel
is created. This method is called on the new instance after adding it.
Use it to initialize the panel from the instance in the previous window,
which is passed as a parameter.
getPanelSpecificMenuItems:
Called before showing the tab's context menu.
Use it to return an array of menu items specific to this panel.
onResize:
Called when the window is resized.
Use it to perform any changes required due to the new size.
onSelect:
Called when the tab is selected and the user is not just scrolling past it.
Use it for things like marking conversations as read.
switchingToPanel:
Called when switching to the panel, even just in passing.
Use it to customize behavior when the panel is displayed.
switchingAwayFromPanel:
Called when switching away from the panel.
Use it to customize behavior when the panel is hidden. -->
<method name="addPanel">
<parameter name="aPanel"/>
<parameter name="aConv"/>
<!-- aPanel is a node containing the content of the panel
aConv is an (optional) imIConversation instance -->
<body>
<![CDATA[
// invalidate cache, because mTabContainer is about to change
this._browsers = null;
this._conversations = null;
var conv;
if (this.mFirstTabIsDummy)
conv = this.mPanelContainer.firstChild;
else {
conv = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"conversation");
this.mPanelContainer.appendChild(conv);
if (this.mStrip.collapsed)
this.setStripVisibilityTo(true);
if (!this.mFirstTabIsDummy) {
if (!Services.prefs.getBoolPref("messenger.conversations.openInTabs"))
return null;
}
conv.setAttribute("contenttooltip", this.getAttribute("contenttooltip"));
conv.setAttribute("contentcontextmenu", this.getAttribute("contentcontextmenu"));
conv.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
// invalidate cache, because mTabContainer is about to change
this._conversations = null;
this._tabPanels = null;
var t;
if (this.mFirstTabIsDummy)
t = this.mTabContainer.firstChild;
else
t = this.mTabContainer.addTab();
conv.tab = t;
conv.conv = aConv;
this.mPanelContainer.appendChild(aPanel);
if (this.mStrip.collapsed &&
!Services.prefs.getBoolPref("browser.tabs.autoHide"))
this.setStripVisibilityTo(true);
this.mFirstTabIsDummy = false;
var t = this.mTabContainer.addTab();
aPanel.tab = t;
if (aConv) {
aConv.QueryInterface(Components.interfaces.imIConversation);
// set up the shared autoscroll popup if it doesn't exist yet
if (!this._autoScrollPopup) {
this._autoScrollPopup = aPanel.browser._createAutoScrollPopup();
this._autoScrollPopup.id = "autoscroller";
this.appendChild(this._autoScrollPopup);
}
aPanel.setAttribute("contenttooltip", this.getAttribute("contenttooltip"));
aPanel.setAttribute("contentcontextmenu", this.getAttribute("contentcontextmenu"));
aPanel.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
aPanel.conv = aConv;
t.linkedConversation = aPanel;
t.linkedBrowser = aPanel.browser;
// We start our browsers out as inactive, and then maintain
// activeness in updateCurrentTab.
aPanel.browser.docShell.isActive = false;
}
this.setStripVisibilityTo(
!(this.mFirstTabIsDummy && Services.prefs.getBoolPref("browser.tabs.autoHide")));
var uniqueId = "panel" + Date.now() + t._tPos;
this.mPanelContainer.lastChild.id = uniqueId;
aPanel.id = uniqueId;
t.linkedPanel = uniqueId;
t.linkedConversation = conv;
t.linkedBrowser = conv.browser;
// We start our browsers out as inactive, and then maintain
// activeness in updateCurrentBrowser.
t.linkedBrowser.docShell.isActive = false;
this.updateCurrentBrowser(true);
t.linkedTabPanel = aPanel;
return conv;
this.updateCurrentTab(true);
if (this.mFirstTabIsDummy)
this.removeTab(this.mTabContainer.firstChild);
this.mFirstTabIsDummy = false;
return aPanel;
]]>
</body>
</method>
@ -572,9 +616,9 @@
if (this._removingTabs.indexOf(aTab) > -1 || this._windowIsClosing)
return null;
var browser = this.getBrowserForTab(aTab);
var browser = aTab.linkedBrowser;
if (!aTabWillBeMoved) {
if (!aTabWillBeMoved && browser) {
let ds = browser.docShell;
if (ds.contentViewer && !ds.contentViewer.permitUnload())
return null;
@ -640,8 +684,7 @@
setTimeout(_delayedUpdate, 0, this.tabContainer);
}
var conversation = this.getConversationForTab(aTab);
var browser = this.getBrowserForTab(aTab);
var panel = aTab.linkedTabPanel;
// Because of the way XBL works (fields just set JS
// properties on the element) and the code we have in place
@ -651,15 +694,12 @@
// cleanup ourselves.
// This has to happen before we remove the child so that the
// XBL implementation of nsIObserver still works.
conversation.destroy();
if ("destroy" in panel)
panel.destroy();
if (conversation == this.mCurrentConversation)
this.mCurrentConversation = null;
// Invalidate browsers cache, as the tab is removed from the
// tab container.
this._browsers = null;
// Invalidate caches, as the tab is removed from the tab container.
this._conversations = null;
this._tabPanels = null;
// Remove the tab ...
this.tabContainer.removeChild(aTab);
@ -677,13 +717,13 @@
// a consistent state (tab removed, tab positions updated, etc.).
// Also, it's important that another tab has been selected before
// the panel is removed; otherwise, a random sibling panel can flash.
this.mPanelContainer.removeChild(conversation);
this.mPanelContainer.removeChild(panel);
// As the panel is removed, the removal of a dependent document can
// cause the whole window to close. So at this point, it's possible
// that the binding is destructed.
if (this.mTabBox)
this.mTabBox.selectedPanel = this.getConversationForTab(this.mCurrentTab);
this.mTabBox.selectedPanel = this.selectedPanel;
if (aCloseWindow)
this._windowIsClosing = closeWindow(true);
@ -717,41 +757,37 @@
</body>
</method>
<method name="importConversation">
<method name="importPanel">
<parameter name="aOtherTab"/>
<body>
<![CDATA[
// That's gBrowser for the other window, not the tab's browser!
var remoteBrowser =
aOtherTab.ownerDocument.defaultView.getBrowser();
var remoteTabBrowser = aOtherTab.ownerDocument.defaultView.getTabBrowser();
// First, start teardown of the other browser. Make sure to not
// fire the beforeunload event in the process. Close the other
// window if this was its last tab.
var endRemoveArgs = remoteBrowser._beginRemoveTab(aOtherTab, true);
var endRemoveArgs = remoteTabBrowser._beginRemoveTab(aOtherTab, true);
var newPanel = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
aOtherTab.linkedTabPanel.nodeName);
this.addPanel(newPanel, aOtherTab.linkedTabPanel.conv || null);
var conv = this._addConversation(aOtherTab.linkedConversation.conv);
var aOurTab = newPanel.tab;
var aOurTab = conv.tab;
var ourBrowser = this.getBrowserForTab(aOurTab);
// make sure it has a docshell
ourBrowser.docShell;
// Swap the docshells
ourBrowser.swapDocShells(aOtherTab.linkedBrowser);
aOtherTab.linkedConversation.conv = null;
conv.finishImport(aOtherTab.linkedConversation);
// Tell the new panel to sync up with the other one.
if ("finishImport" in newPanel)
newPanel.finishImport(aOtherTab.linkedTabPanel);
// Finish tearing down the tab that's going away.
remoteBrowser._endRemoveTab(endRemoveArgs);
remoteTabBrowser._endRemoveTab(endRemoveArgs);
// If the tab was already selected (this happpens in the scenario
// of replaceTabsWithWindow), notify onLocationChange, etc.
if (aOurTab == this.selectedTab)
this.updateCurrentBrowser(true);
this.updateCurrentTab(true);
return conv;
return newPanel;
]]>
</body>
</method>
@ -772,24 +808,6 @@
</body>
</method>
<method name="getBrowserForTab">
<parameter name="aTab"/>
<body>
<![CDATA[
return aTab.linkedBrowser;
]]>
</body>
</method>
<method name="getConversationForTab">
<parameter name="aTab"/>
<body>
<![CDATA[
return aTab.linkedConversation;
]]>
</body>
</method>
<method name="selectTabAtIndex">
<parameter name="aIndex"/>
<parameter name="aEvent"/>
@ -832,28 +850,35 @@
</property>
<property name="selectedBrowser"
onget="return this.mCurrentBrowser;"
onget="return this.mCurrentTab.linkedBrowser || null;"
readonly="true"/>
<property name="selectedConversation"
onget="return this.mCurrentConversation;"
onget="return this.mCurrentTab.linkedConversation || null;"
readonly="true"/>
<property name="browsers" readonly="true">
<property name="selectedPanel"
onget="return this.mCurrentTab.linkedTabPanel || null;"
readonly="true"/>
<property name="tabPanels" readonly="true">
<getter>
<![CDATA[
return this._browsers ||
(this._browsers = Array.map(this.mTabs, function (tab) tab.linkedBrowser));
return this._tabPanels ||
(this._tabPanels = Array.map(this.mTabs, function (tab) tab.linkedTabPanel));
]]>
</getter>
</property>
<property name="conversations" readonly="true">
<getter>
<![CDATA[
return this._conversations ||
(this._conversations = Array.map(this.mTabs, function (tab) tab.linkedConversation));
]]>
<![CDATA[
if (!this._conversations) {
this._conversations = Array.map(this.mTabs, function(aTab) aTab.linkedConversation)
.filter(function(aConv) aConv);
}
return this._conversations;
]]>
</getter>
</property>
@ -1036,11 +1061,11 @@
// Compute the new index *before* we add a tab
newIndex = this.getNewIndex(aEvent);
var conv = this.importConversation(draggedTab);
this.moveTabTo(conv.tab, newIndex);
var panel = this.importPanel(draggedTab);
this.moveTabTo(panel.tab, newIndex);
// We need to set selectedTab after we've swapped the docShells
this.selectedTab = conv.tab;
this.selectedTab = panel.tab;
}
]]>
</body>
@ -1125,8 +1150,9 @@
<parameter name="aIndex"/>
<body>
<![CDATA[
this._browsers = null; // invalidate cache
// Invalidate caches.
this._conversations = null;
this._tabPanels = null;
var oldPosition = aTab._tPos;
@ -1136,8 +1162,8 @@
// bounds: .item() returns null (so it acts like appendChild), but [] throws
this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes.item(aIndex));
// invalidate cache, because mTabContainer is about to change
this._browsers = null;
this._conversations = null;
this._tabPanels = null;
var i;
for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
@ -1180,7 +1206,7 @@
<body>
<![CDATA[
var tabPos = this.mCurrentTab._tPos;
if (tabPos < this.browsers.length - 1) {
if (tabPos < this.mTabs.length - 1) {
this.moveTabTo(this.mCurrentTab, tabPos + 1);
this.mCurrentTab.focus();
}
@ -1220,9 +1246,8 @@
<body>
<![CDATA[
var tabPos = this.mCurrentTab._tPos;
if (tabPos < this.browsers.length - 1) {
this.moveTabTo(this.mCurrentTab,
this.browsers.length - 1);
if (tabPos < this.mTabs.length - 1) {
this.moveTabTo(this.mCurrentTab, this.mTabs.length - 1);
this.mCurrentTab.focus();
}
]]>
@ -1243,33 +1268,6 @@
</body>
</method>
<!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
MAKE SURE TO ADD IT HERE AS WELL. -->
<property name="docShell"
onget="return this.mCurrentBrowser.docShell"
readonly="true"/>
<property name="contentWindow"
readonly="true"
onget="return this.mCurrentBrowser.contentWindow"/>
<property name="markupDocumentViewer"
onget="return this.mCurrentBrowser.markupDocumentViewer;"
readonly="true"/>
<property name="contentDocument"
onget="return this.mCurrentBrowser.contentDocument;"
readonly="true"/>
<property name="contentTitle"
onget="return this.mCurrentBrowser.contentTitle;"
readonly="true"/>
<property name="findbar"
onget="return this.mCurrentConversation.findbar;"
readonly="true"/>
<method name="dragDropSecurityCheck">
<parameter name="aEvent"/>
<parameter name="aDragSession"/>
@ -1371,8 +1369,8 @@
<body><![CDATA[
switch (aEvent.type) {
case "sizemodechange":
if (aEvent.target == window) {
this.mCurrentBrowser.docShell.isActive =
if (this.selectedBrowser && aEvent.target == window) {
this.selectedBrowser.docShell.isActive =
(window.windowState != window.STATE_MINIMIZED);
}
break;
@ -1383,24 +1381,29 @@
<field name="_windowActivateHandler" readonly="true">
<![CDATA[({
tabbrowser: this,
handleEvent: function handleEvent(aEvent)
this.tabbrowser.mCurrentTab.switchingToTab()
handleEvent: function handleEvent() {
if ("switchingToPanel" in this.tabbrowser.selectedPanel)
this.tabbrowser.selectedPanel.switchingToPanel();
}
})]]>
</field>
<field name="_windowDeactivateHandler" readonly="true">
<![CDATA[({
tabbrowser: this,
handleEvent: function handleEvent(aEvent)
this.tabbrowser.mCurrentTab.switchingAwayFromTab()
handleEvent: function handleEvent() {
if ("switchingAwayFromPanel" in this.tabbrowser.selectedPanel)
this.tabbrowser.selectedPanel.switchingAwayFromPanel();
}
})]]>
</field>
<constructor>
<![CDATA[
this.mCurrentConversation = this.mPanelContainer.firstChild;
this.mCurrentBrowser = this.mCurrentConversation.browser;
this.mCurrentTab = this.mTabContainer.firstChild;
// Dummy tab is not a conversation
this.mCurrentTab.linkedTabPanel = this.mPanelContainer.firstChild;
this.mCurrentTab.linkedTabPanel.tab = this.mCurrentTab;
document.addEventListener("keypress", this._keyEventHandler);
document.addEventListener("sizemodechange", this);
@ -1408,17 +1411,10 @@
window.addEventListener("activate", this._windowActivateHandler);
var uniqueId = "panel" + Date.now();
this.mCurrentConversation.id = uniqueId;
this.mCurrentTab.linkedTabPanel.id = uniqueId;
this.mCurrentTab.linkedPanel = uniqueId;
this.mCurrentTab._tPos = 0;
this.mCurrentTab.linkedConversation = this.mCurrentConversation;
this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
// set up the shared autoscroll popup
this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
this._autoScrollPopup.id = "autoscroller";
this.appendChild(this._autoScrollPopup);
this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
Services.prefs.addObserver("browser.tabs.autoHide", this._prefObserver, false);
Services.prefs.addObserver("messenger.conversations.useSeparateWindowsForMUCs", this._prefObserver, false);
]]>
@ -2077,40 +2073,6 @@
<implementation>
<field name="mOverCloseButton">false</field>
<field name="mCorrespondingMenuitem">null</field>
<method name="switchingToTab">
<body>
<![CDATA[
if (this._visibleTimer)
return;
// Start a timer to detect if the tab has been visible to the
// user for long enough to actually be seen (as opposed to the
// tab only being visible "accidentally in passing").
delete this._wasVisible;
this._visibleTimer = setTimeout(function() {
this._wasVisible = true;
delete this._visibleTimer;
}.bind(this), 1000);
]]>
</body>
</method>
<method name="switchingAwayFromTab">
<body>
<![CDATA[
if (this._visibleTimer) {
clearTimeout(this._visibleTimer);
delete this._visibleTimer;
}
// Remove the unread ruler if the tab has been visible without
// interruptions for sufficiently long.
if (this._wasVisible)
this.linkedBrowser.removeUnreadRuler();
]]>
</body>
</method>
</implementation>
<handlers>
@ -2137,9 +2099,10 @@
// the current tab to mark the conversation as read before leaving it.
// This is necessary when Instantbird (and therefore the current tab)
// did not have focus before this click.
if (!this.linkedConversation.hasAttribute("selected")) {
let tabbrowser = document.getBindingParent(this);
tabbrowser.mCurrentConversation.onSelect();
if (!this.linkedTabPanel.hasAttribute("selected")) {
let panel = document.getBindingParent(this).selectedPanel;
if ("onSelect" in panel)
panel.onSelect();
}
this.style.MozUserFocus = 'ignore';
@ -2160,18 +2123,32 @@
</handler>
<handler event="DOMAttrModified">
<![CDATA[
if (event.attrName == "label" && ("getBrowser" in window))
getBrowser().updateTitlebar();
if (event.attrName != "selected")
return;
if (event.attrChange == event.REMOVAL) {
this.linkedConversation.removeAttribute("selected");
this.switchingAwayFromTab();
if (event.attrName == "label") {
if ("getTabBrowser" in window)
getTabBrowser().updateTitlebar();
// Update our tooltiptext, but only if a tooltip hasn't been set.
if (!this.hasAttribute("tooltip"))
this.setAttribute("tooltiptext", event.newValue);
}
else if (event.attrName == "tooltip") {
if (event.attrChange == event.ADDITION) {
// Tooltip was added. Stop using our tooltiptext attribute.
this.removeAttribute("tooltiptext");
}
else if (event.attrChange == event.REMOVAL) {
// Tooltip was removed. Switch to using our label as tooltiptext.
this.setAttribute("tooltiptext", this.getAttribute("label"));
}
}
else if (event.attrName == "selected") {
if (event.attrChange == event.REMOVAL) {
this.linkedTabPanel.removeAttribute("selected");
if ("switchingAwayFromPanel" in this.linkedTabPanel)
this.linkedTabPanel.switchingAwayFromPanel();
}
else
this.linkedTabPanel.setAttribute("selected", event.newValue);
}
else
this.linkedConversation.setAttribute("selected", event.newValue);
]]>
</handler>
</handlers>

Просмотреть файл

@ -0,0 +1,10 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
contextShowLogs.label=Show Logs
contextShowLogs.accesskey=L
contextCloseConv.label=Close Conversation
contextCloseConv.accesskey=v
contextHideConv.label=Put Conversation on Hold
contextHideConv.accesskey=h

Просмотреть файл

@ -11,11 +11,5 @@
<!ENTITY closeOtherTabs.label "Close Other Tabs">
<!ENTITY openTabInNewWindow.label "Open in a New Window">
<!ENTITY openTabInNewWindow.accesskey "W">
<!ENTITY showLogs.label "Show Logs">
<!ENTITY showLogs.accesskey "L">
<!ENTITY closeConv.label "Close Conversation">
<!ENTITY closeConv.accesskey "v">
<!ENTITY hideConv.label "Put Conversation on Hold">
<!ENTITY hideConv.accesskey "h">
<!ENTITY listAllTabs.label "List all tabs">
<!ENTITY newTabButton.tooltip "Open a new tab">

Просмотреть файл

@ -14,6 +14,7 @@
locale/@AB_CD@/instantbird/accountWizard.properties (%chrome/instantbird/accountWizard.properties)
locale/@AB_CD@/instantbird/addbuddy.dtd (%chrome/instantbird/addbuddy.dtd)
locale/@AB_CD@/instantbird/buddytooltip.properties (%chrome/instantbird/buddytooltip.properties)
locale/@AB_CD@/instantbird/conversation.properties (%chrome/instantbird/conversation.properties)
locale/@AB_CD@/instantbird/core.properties (%chrome/instantbird/core.properties)
locale/@AB_CD@/instantbird/credits.dtd (%chrome/instantbird/credits.dtd)
locale/@AB_CD@/instantbird/engineManager.dtd (%chrome/instantbird/engineManager.dtd)

Просмотреть файл

@ -77,6 +77,8 @@ var Conversations = {
let doc = conv.ownerDocument;
doc.getElementById("conversations").selectedTab = conv.tab;
conv.focus();
// Tell it to mark itself as read.
conv.onSelect();
doc.defaultView.focus();
#ifdef XP_MACOSX
Components.classes["@mozilla.org/widget/macdocksupport;1"]