Merge from m-c.
|
@ -895,8 +895,16 @@ nsHTMLTableAccessible::GetCellAt(PRInt32 aRow, PRInt32 aColumn,
|
|||
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
|
||||
nsAccessible *cell =
|
||||
GetAccService()->GetAccessibleInWeakShell(cellContent, mWeakShell);
|
||||
if (cell)
|
||||
CallQueryInterface(cell, aTableCellAccessible);
|
||||
|
||||
if (!cell) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (cell != this) {
|
||||
// XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may
|
||||
// return itself as a cell what makes Orca hang.
|
||||
NS_ADDREF(*aTableCellAccessible = cell);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,11 @@ function doTest()
|
|||
accNotCreated = (!isAccessible("t5_cell"));
|
||||
ok(accNotCreated, "wrongly created table cell accessible");
|
||||
|
||||
// test crazy table
|
||||
var table6 = getAccessible("table6", [nsIAccessibleTable]);
|
||||
ok(!table6.getCellAt(0, 0),
|
||||
"We don't expect cell accessible for crazy table 6!");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -138,6 +143,11 @@ addA11yLoadEvent(doTest);
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="display:table;" id="table6">
|
||||
<input type="checkbox">
|
||||
<a href="bar">Bad checkbox</a>
|
||||
</div>
|
||||
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
function doTest()
|
||||
{
|
||||
// Actually, just disable this test everywhere -- bug 586818.
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
||||
if (LINUX) {
|
||||
todo(false, "Enable test on Linux - see bug 525175.");
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -4,6 +4,8 @@ skin testpilot skin skin/all/
|
|||
skin testpilot-os skin skin/linux/ os=Linux
|
||||
skin testpilot-os skin skin/mac/ os=Darwin
|
||||
skin testpilot-os skin skin/win/ os=WINNT
|
||||
overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/content/tp-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion<4.0b1pre
|
||||
overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/content/feedback-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion>=4.0b1pre
|
||||
overlay chrome://browser/content/browser.xul chrome://testpilot/content/tp-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion<=3.6.*
|
||||
overlay chrome://browser/content/browser.xul chrome://testpilot/content/feedback-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion>3.7a1pre
|
||||
# For the menubar on Mac
|
||||
|
|
|
@ -286,11 +286,9 @@ var TestPilotXulWindow = {
|
|||
let openInTab = (task.taskType == TaskConstants.TYPE_LEGACY);
|
||||
|
||||
this.addDescription(textVbox, task.title, task.summary);
|
||||
if (task.showMoreInfoLink) {
|
||||
this.addXulLink(
|
||||
textVbox, this._stringBundle.getString("testpilot.moreInfo"),
|
||||
task.defaultUrl, openInTab);
|
||||
}
|
||||
this.addXulLink(
|
||||
textVbox, this._stringBundle.getString("testpilot.moreInfo"),
|
||||
task.defaultUrl, openInTab);
|
||||
|
||||
// Create the rightmost status area, depending on status:
|
||||
let statusVbox = document.createElement("vbox");
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
#feedback-menu-button .toolbarbutton-text {
|
||||
display: -moz-box;
|
||||
margin: 0;
|
||||
color: -moz-dialogtext;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
#feedback-menu-button .toolbarbutton-icon {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -166,10 +166,6 @@ var TestPilotTask = {
|
|||
return url + this._id;
|
||||
},
|
||||
|
||||
get showMoreInfoLink() {
|
||||
return true;
|
||||
},
|
||||
|
||||
// event handlers:
|
||||
|
||||
onExperimentStartup: function TestPilotTask_onExperimentStartup() {
|
||||
|
@ -1021,10 +1017,6 @@ TestPilotWebSurvey.prototype = {
|
|||
return this.infoPageUrl;
|
||||
},
|
||||
|
||||
get showMoreInfoLink() {
|
||||
return false;
|
||||
},
|
||||
|
||||
onDetailPageOpened: function TPWS_onDetailPageOpened() {
|
||||
/* Once you view the URL of the survey, we'll assume you've taken it.
|
||||
* There's no reliable way to tell whether you have or not, so let's
|
||||
|
|
|
@ -83,3 +83,6 @@ endif
|
|||
ifneq (,$(filter windows, $(MOZ_WIDGET_TOOLKIT)))
|
||||
DEFINES += -DMENUBAR_CAN_AUTOHIDE=1
|
||||
endif
|
||||
|
||||
libs::
|
||||
$(NSINSTALL) $(srcdir)/content/tabview/modules/* $(FINAL_TARGET)/modules/tabview
|
||||
|
|
|
@ -208,6 +208,10 @@
|
|||
<menu id="view-menu" label="&viewMenu.label;"
|
||||
accesskey="&viewMenu.accesskey;">
|
||||
<menupopup id="menu_viewPopup">
|
||||
<menuitem id="menu_tabview"
|
||||
label="&showTabView.label;"
|
||||
accesskey="&showTabView.accesskey;"
|
||||
command="Browser:ToggleTabView"/>
|
||||
<menu id="viewToolbarsMenu"
|
||||
label="&viewToolbarsMenu.label;"
|
||||
accesskey="&viewToolbarsMenu.accesskey;">
|
||||
|
@ -590,7 +594,11 @@
|
|||
<menu id="tools-menu"
|
||||
label="&toolsMenu.label;"
|
||||
accesskey="&toolsMenu.accesskey;">
|
||||
<menupopup id="menu_ToolsPopup">
|
||||
<menupopup id="menu_ToolsPopup"
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
onpopupshowing="gSyncUI.updateUI();"
|
||||
#endif
|
||||
>
|
||||
<menuitem id="menu_search"
|
||||
label="&search.label;"
|
||||
accesskey="&search.accesskey;"
|
||||
|
|
|
@ -480,9 +480,9 @@ var PlacesCommandHook = {
|
|||
var tabList = [];
|
||||
var seenURIs = {};
|
||||
|
||||
var browsers = gBrowser.browsers;
|
||||
for (var i = 0; i < browsers.length; ++i) {
|
||||
let uri = browsers[i].currentURI;
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
for (let i = 0; i < tabs.length; ++i) {
|
||||
let uri = tabs[i].linkedBrowser.currentURI;
|
||||
|
||||
// skip redundant entries
|
||||
if (uri.spec in seenURIs)
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
<command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);"/>
|
||||
<command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
|
||||
<command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
|
||||
<command id="Browser:ToggleTabView" oncommand="TabView.toggle();"/>
|
||||
<command id="cmd_fullZoomReduce" oncommand="FullZoom.reduce()"/>
|
||||
<command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
|
||||
<command id="cmd_fullZoomReset" oncommand="FullZoom.reset()"/>
|
||||
|
|
|
@ -40,37 +40,31 @@
|
|||
// gSyncUI handles updating the tools menu
|
||||
let gSyncUI = {
|
||||
init: function SUI_init() {
|
||||
let obs = [["weave:service:sync:start", "onActivityStart"],
|
||||
["weave:service:sync:finish", "onSyncFinish"],
|
||||
["weave:service:sync:error", "onSyncError"],
|
||||
["weave:service:sync:delayed", "onSyncDelay"],
|
||||
["weave:service:setup-complete", "onLoginFinish"],
|
||||
["weave:service:login:start", "onActivityStart"],
|
||||
["weave:service:login:finish", "onLoginFinish"],
|
||||
["weave:service:login:error", "onLoginError"],
|
||||
["weave:service:logout:finish", "onLogout"],
|
||||
["weave:service:start-over", "onStartOver"]];
|
||||
// this will be the first notification fired during init
|
||||
// we can set up everything else later
|
||||
Services.obs.addObserver(this, "weave:service:ready", true);
|
||||
},
|
||||
initUI: function SUI_initUI() {
|
||||
let obs = ["weave:service:sync:start",
|
||||
"weave:service:sync:finish",
|
||||
"weave:service:sync:error",
|
||||
"weave:service:sync:delayed",
|
||||
"weave:service:setup-complete",
|
||||
"weave:service:login:start",
|
||||
"weave:service:login:finish",
|
||||
"weave:service:login:error",
|
||||
"weave:service:logout:finish",
|
||||
"weave:service:start-over"];
|
||||
|
||||
// If this is a browser window?
|
||||
if (gBrowser) {
|
||||
obs.push(["weave:notification:added", "onNotificationAdded"],
|
||||
["weave:notification:removed", "onNotificationRemoved"]);
|
||||
obs.push("weave:notification:added", "weave:notification:removed");
|
||||
}
|
||||
|
||||
// Add the observers now and remove them on unload
|
||||
let self = this;
|
||||
let addRem = function(add) {
|
||||
obs.forEach(function([topic, func]) {
|
||||
//XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
|
||||
// of `this`. Fix in a followup. (bug 583347)
|
||||
if (add)
|
||||
Weave.Svc.Obs.add(topic, self[func], self);
|
||||
else
|
||||
Weave.Svc.Obs.remove(topic, self[func], self);
|
||||
});
|
||||
};
|
||||
addRem(true);
|
||||
window.addEventListener("unload", function() addRem(false), false);
|
||||
obs.forEach(function(topic) {
|
||||
Services.obs.addObserver(self, topic, true);
|
||||
});
|
||||
|
||||
// Find the alltabs-popup, only if there is a gBrowser
|
||||
if (gBrowser) {
|
||||
|
@ -80,7 +74,6 @@ let gSyncUI = {
|
|||
self.alltabsPopupShowing();
|
||||
}, true);
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
|
@ -242,7 +235,6 @@ let gSyncUI = {
|
|||
}
|
||||
},
|
||||
|
||||
|
||||
// Commands
|
||||
doUpdateMenu: function SUI_doUpdateMenu(event) {
|
||||
this._updateLastSyncItem();
|
||||
|
@ -369,7 +361,50 @@ let gSyncUI = {
|
|||
|
||||
this.updateUI();
|
||||
this._updateLastSyncItem();
|
||||
}
|
||||
},
|
||||
|
||||
observe: function SUI_observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "weave:service:sync:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
this.onSyncFinish();
|
||||
break;
|
||||
case "weave:service:sync:error":
|
||||
this.onSyncError();
|
||||
break;
|
||||
case "weave:service:sync:delayed":
|
||||
this.onSyncDelay();
|
||||
break;
|
||||
case "weave:service:setup-complete":
|
||||
this.onLoginFinish();
|
||||
break;
|
||||
case "weave:service:login:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:login:finish":
|
||||
this.onLoginFinish();
|
||||
break;
|
||||
case "weave:service:login:error":
|
||||
this.onLoginError();
|
||||
break;
|
||||
case "weave:service:logout:finish":
|
||||
this.onLogout();
|
||||
break;
|
||||
case "weave:service:start-over":
|
||||
this.onStartOver();
|
||||
break;
|
||||
case "weave:service:ready":
|
||||
this.initUI();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference
|
||||
])
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
|
||||
|
|
|
@ -215,12 +215,13 @@ var ctrlTab = {
|
|||
if (this._tabList)
|
||||
return this._tabList;
|
||||
|
||||
var list = Array.slice(gBrowser.tabs);
|
||||
let list = gBrowser.visibleTabs;
|
||||
|
||||
if (this._closing)
|
||||
this.detachTab(this._closing, list);
|
||||
|
||||
for (let i = 0; i < gBrowser.tabContainer.selectedIndex; i++)
|
||||
// Rotate the list until the selected tab is first
|
||||
while (!list[0].selected)
|
||||
list.push(list.shift());
|
||||
|
||||
if (this.recentlyUsedLimit != 0) {
|
||||
|
@ -462,11 +463,12 @@ var ctrlTab = {
|
|||
} else if (!event.shiftKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (gBrowser.tabs.length > 2) {
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
if (tabs.length > 2) {
|
||||
this.open();
|
||||
} else if (gBrowser.tabs.length == 2) {
|
||||
gBrowser.selectedTab = gBrowser.selectedTab.nextSibling ||
|
||||
gBrowser.selectedTab.previousSibling;
|
||||
} else if (tabs.length == 2) {
|
||||
let index = gBrowser.selectedTab == tabs[0] ? 1 : 0;
|
||||
gBrowser.selectedTab = tabs[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -664,7 +666,7 @@ var allTabs = {
|
|||
Array.forEach(this.previews, function (preview) {
|
||||
var tab = preview._tab;
|
||||
var matches = 0;
|
||||
if (filter.length) {
|
||||
if (filter.length && !tab.hidden) {
|
||||
let tabstring = tab.linkedBrowser.currentURI.spec;
|
||||
try {
|
||||
tabstring = decodeURI(tabstring);
|
||||
|
@ -673,7 +675,7 @@ var allTabs = {
|
|||
for (let i = 0; i < filter.length; i++)
|
||||
matches += tabstring.indexOf(filter[i]) > -1;
|
||||
}
|
||||
if (matches < filter.length) {
|
||||
if (matches < filter.length || tab.hidden) {
|
||||
preview.hidden = true;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is the Tab View
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2010
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Raymond Lee <raymond@appcoast.com>
|
||||
# Ian Gilman <ian@iangilman.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
let TabView = {
|
||||
_deck: null,
|
||||
_window: null,
|
||||
_sessionstore: null,
|
||||
_visibilityID: "tabview-visibility",
|
||||
|
||||
// ----------
|
||||
get windowTitle() {
|
||||
delete this.windowTitle;
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
let title = gNavigatorBundle.getFormattedString("tabView.title", [brandShortName]);
|
||||
return this.windowTitle = title;
|
||||
},
|
||||
|
||||
// ----------
|
||||
init: function TabView_init() {
|
||||
// ___ keys
|
||||
this._setBrowserKeyHandlers();
|
||||
|
||||
// ___ visibility
|
||||
this._sessionstore =
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
|
||||
let data = this._sessionstore.getWindowValue(window, this._visibilityID);
|
||||
if (data && data == "true")
|
||||
this.show();
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Creates the frame and calls the callback once it's loaded.
|
||||
// If the frame already exists, calls the callback immediately.
|
||||
_initFrame: function TabView__initFrame(callback) {
|
||||
if (this._window) {
|
||||
if (typeof callback == "function")
|
||||
callback();
|
||||
} else {
|
||||
// ___ find the deck
|
||||
this._deck = document.getElementById("tab-view-deck");
|
||||
|
||||
// ___ create the frame
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.id = "tab-view";
|
||||
iframe.setAttribute("transparent", "true");
|
||||
iframe.flex = 1;
|
||||
|
||||
if (typeof callback == "function")
|
||||
iframe.addEventListener("DOMContentLoaded", callback, false);
|
||||
|
||||
iframe.setAttribute("src", "chrome://browser/content/tabview.html");
|
||||
this._deck.appendChild(iframe);
|
||||
this._window = iframe.contentWindow;
|
||||
|
||||
// ___ visibility storage handler
|
||||
let self = this;
|
||||
function observer(subject, topic, data) {
|
||||
if (topic == "quit-application-requested") {
|
||||
let data = (self.isVisible() ? "true" : "false");
|
||||
self._sessionstore.setWindowValue(window, self._visibilityID, data);
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, "quit-application-requested", false);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
isVisible: function() {
|
||||
return (this._deck ? this._deck.selectedIndex == 1 : false);
|
||||
},
|
||||
|
||||
// ----------
|
||||
show: function() {
|
||||
if (this.isVisible())
|
||||
return;
|
||||
|
||||
this._initFrame(function() {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("tabviewshow", false, false);
|
||||
dispatchEvent(event);
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
hide: function() {
|
||||
if (!this.isVisible())
|
||||
return;
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("tabviewhide", false, false);
|
||||
dispatchEvent(event);
|
||||
},
|
||||
|
||||
// ----------
|
||||
toggle: function() {
|
||||
if (this.isVisible())
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
},
|
||||
|
||||
// ----------
|
||||
updateContextMenu: function(tab, popup) {
|
||||
let isEmpty = true;
|
||||
|
||||
while(popup.lastChild && popup.lastChild.id != "context_namedGroups")
|
||||
popup.removeChild(popup.lastChild);
|
||||
|
||||
let self = this;
|
||||
this._initFrame(function() {
|
||||
let activeGroup = tab.tabItem.parent;
|
||||
let groupItems = self._window.GroupItems.groupItems;
|
||||
|
||||
groupItems.forEach(function(groupItem) {
|
||||
if (groupItem.getTitle().length > 0 &&
|
||||
(!activeGroup || activeGroup.id != groupItem.id)) {
|
||||
let menuItem = self._createGroupMenuItem(groupItem);
|
||||
popup.appendChild(menuItem);
|
||||
isEmpty = false;
|
||||
}
|
||||
});
|
||||
document.getElementById("context_namedGroups").hidden = isEmpty;
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
_createGroupMenuItem : function(groupItem) {
|
||||
let menuItem = document.createElement("menuitem")
|
||||
menuItem.setAttribute("class", "group");
|
||||
menuItem.setAttribute("label", groupItem.getTitle());
|
||||
menuItem.setAttribute(
|
||||
"oncommand",
|
||||
"TabView.moveTabTo(TabContextMenu.contextTab,'" + groupItem.id + "')");
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
// ----------
|
||||
moveTabTo: function(tab, groupItemId) {
|
||||
if (this._window)
|
||||
this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Adds new key commands to the browser, for invoking the Tab Candy UI
|
||||
// and for switching between groups of tabs when outside of the Tab Candy UI.
|
||||
_setBrowserKeyHandlers : function() {
|
||||
let self = this;
|
||||
|
||||
window.addEventListener("keypress", function(event) {
|
||||
if (self.isVisible())
|
||||
return;
|
||||
|
||||
let charCode = event.charCode;
|
||||
#ifdef XP_MACOSX
|
||||
// if a text box in a webpage has the focus, the event.altKey would
|
||||
// return false so we are depending on the charCode here.
|
||||
if (!event.ctrlKey && !event.metaKey && !event.shiftKey &&
|
||||
charCode == 160) { // alt + space
|
||||
#else
|
||||
if (event.ctrlKey && !event.metaKey && !event.shiftKey &&
|
||||
event.altKey && charCode == 32) { // ctrl + alt + space
|
||||
#endif
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
self.show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Control (+ Shift) + `
|
||||
if (event.ctrlKey && !event.metaKey && !event.altKey &&
|
||||
(charCode == 96 || charCode == 126)) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
self._initFrame(function() {
|
||||
let tabItem = self._window.GroupItems.getNextGroupItemTab(event.shiftKey);
|
||||
if (tabItem)
|
||||
window.gBrowser.selectedTab = tabItem.tab;
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
};
|
|
@ -178,6 +178,7 @@ let gInitialPages = [
|
|||
#include inspector.js
|
||||
#include browser-places.js
|
||||
#include browser-tabPreviews.js
|
||||
#include browser-tabview.js
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include browser-syncui.js
|
||||
|
@ -1524,6 +1525,8 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
gSyncUI.init();
|
||||
#endif
|
||||
|
||||
TabView.init();
|
||||
|
||||
Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
|
||||
}
|
||||
|
||||
|
@ -6769,17 +6772,17 @@ var gBookmarkAllTabsHandler = {
|
|||
this._command = document.getElementById("Browser:BookmarkAllTabs");
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", this, true);
|
||||
gBrowser.tabContainer.addEventListener("TabClose", this, true);
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", this, true);
|
||||
gBrowser.tabContainer.addEventListener("TabMove", this, true);
|
||||
this._updateCommandState();
|
||||
},
|
||||
|
||||
_updateCommandState: function BATH__updateCommandState(aTabClose) {
|
||||
var numTabs = gBrowser.tabs.length;
|
||||
_updateCommandState: function BATH__updateCommandState() {
|
||||
let remainingTabs = gBrowser.visibleTabs.filter(function(tab) {
|
||||
return gBrowser._removingTabs.indexOf(tab) == -1;
|
||||
});
|
||||
|
||||
// The TabClose event is fired before the tab is removed from the DOM
|
||||
if (aTabClose)
|
||||
numTabs--;
|
||||
|
||||
if (numTabs > 1)
|
||||
if (remainingTabs.length > 1)
|
||||
this._command.removeAttribute("disabled");
|
||||
else
|
||||
this._command.setAttribute("disabled", "true");
|
||||
|
@ -6791,7 +6794,7 @@ var gBookmarkAllTabsHandler = {
|
|||
|
||||
// nsIDOMEventListener
|
||||
handleEvent: function(aEvent) {
|
||||
this._updateCommandState(aEvent.type == "TabClose");
|
||||
this._updateCommandState();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7799,7 +7802,7 @@ var TabContextMenu = {
|
|||
updateContextMenu: function updateContextMenu(aPopupMenu) {
|
||||
this.contextTab = document.popupNode.localName == "tab" ?
|
||||
document.popupNode : gBrowser.selectedTab;
|
||||
var disabled = gBrowser.tabs.length == 1;
|
||||
let disabled = gBrowser.visibleTabs.length == 1;
|
||||
|
||||
// Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
|
||||
document.getElementById("context_closeTab").disabled =
|
||||
|
@ -7821,7 +7824,7 @@ var TabContextMenu = {
|
|||
|
||||
// Disable "Close other Tabs" if there is only one unpinned tab and
|
||||
// hide it when the user rightclicked on a pinned tab.
|
||||
var unpinnedTabs = gBrowser.tabs.length - gBrowser._numPinnedTabs;
|
||||
let unpinnedTabs = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs;
|
||||
document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
|
||||
document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
|
||||
}
|
||||
|
|
|
@ -102,6 +102,9 @@
|
|||
|
||||
<script type="application/javascript" src="chrome://browser/content/places/editBookmarkOverlay.js"/>
|
||||
|
||||
<deck flex="1" id="tab-view-deck">
|
||||
<vbox flex="1">
|
||||
|
||||
# All sets except for popupsets (commands, keys, stringbundles and broadcasters) *must* go into the
|
||||
# browser-sets.inc file for sharing with hiddenWindow.xul.
|
||||
#include browser-sets.inc
|
||||
|
@ -110,6 +113,18 @@
|
|||
<menupopup id="tabContextMenu"
|
||||
onpopupshowing="if (event.target == this) TabContextMenu.updateContextMenu(this);"
|
||||
onpopuphidden="if (event.target == this) TabContextMenu.contextTab = null;">
|
||||
<menu id="context_tabViewMenu" class="menu-iconic" label="&moveTabTo.label;"
|
||||
accesskey="&moveTabTo.accesskey;">
|
||||
<menupopup id="context_tabViewMenuPopup"
|
||||
onpopupshowing="if (event.target == this) TabView.updateContextMenu(TabContextMenu.contextTab, this);">
|
||||
<menuitem label="&createNewGroup.label;"
|
||||
accesskey="&createNewGroup.accesskey;"
|
||||
oncommand="TabView.moveTabTo(TabContextMenu.contextTab, null);" />
|
||||
<menuitem id="context_namedGroups" label="&namedGroups.label;"
|
||||
disabled="true" />
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
|
||||
oncommand="gBrowser.reloadTab(TabContextMenu.contextTab);"/>
|
||||
<menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
|
||||
|
@ -893,7 +908,7 @@
|
|||
iconsize="small" defaulticonsize="small" lockiconsize="true"
|
||||
aria-label="&tabsToolbar.label;"
|
||||
context="toolbar-context-menu"
|
||||
defaultset="tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
|
||||
defaultset="tabbrowser-tabs,new-tab-button,tabview-button,alltabs-button,tabs-closebutton"
|
||||
collapsed="true">
|
||||
|
||||
<tabs id="tabbrowser-tabs"
|
||||
|
@ -927,6 +942,12 @@
|
|||
position="after_end"/>
|
||||
</toolbarbutton>
|
||||
|
||||
<toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&tabViewButton.label;"
|
||||
command="Browser:ToggleTabView"
|
||||
tooltiptext="&tabViewButton.tooltip;"
|
||||
removable="true"/>
|
||||
|
||||
<toolbarbutton id="tabs-closebutton"
|
||||
class="close-button tabs-closebutton"
|
||||
command="cmd_close"
|
||||
|
@ -1040,7 +1061,7 @@
|
|||
onclick="if (event.button == 0 && event.detail == 1) displaySecurityInfo();"/>
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<statusbarpanel id="sync-status-button"
|
||||
class="statusbarpanel-iconic-text"
|
||||
class="statusbarpanel-iconic"
|
||||
image="chrome://browser/skin/sync-16.png"
|
||||
label="&syncLogInItem.label;"
|
||||
oncommand="gSyncUI.handleStatusbarButton();"
|
||||
|
@ -1081,4 +1102,11 @@
|
|||
</svg:mask>
|
||||
</svg:svg>
|
||||
#endif
|
||||
|
||||
</vbox>
|
||||
# <iframe id="tab-view"> is dynamically appended as the 2nd child of #tab-view-deck.
|
||||
# Introducing the iframe dynamically, as needed, was found to be better than
|
||||
# starting with an empty iframe here in browser.xul from a Ts standpoint.
|
||||
</deck>
|
||||
|
||||
</window>
|
||||
|
|
|
@ -88,6 +88,8 @@
|
|||
<field name="tabs" readonly="true">
|
||||
this.tabContainer.childNodes;
|
||||
</field>
|
||||
<property name="visibleTabs" readonly="true"
|
||||
onget="return Array.filter(this.tabs, function(tab) !tab.hidden);"/>
|
||||
<field name="mURIFixup" readonly="true">
|
||||
Components.classes["@mozilla.org/docshell/urifixup;1"]
|
||||
.getService(Components.interfaces.nsIURIFixup);
|
||||
|
@ -721,7 +723,13 @@
|
|||
<method name="updateTitlebar">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
|
||||
if (TabView && TabView.isVisible()) {
|
||||
// ToDo: this will be removed when we gain ability to draw to the menu bar.
|
||||
// Bug 586175
|
||||
this.ownerDocument.title = TabView.windowTitle;
|
||||
} else {
|
||||
this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -758,6 +766,7 @@
|
|||
newBrowser.docShell.isActive = true;
|
||||
this.mCurrentBrowser = newBrowser;
|
||||
this.mCurrentTab = this.selectedTab;
|
||||
this.mCurrentTab.hidden = false;
|
||||
|
||||
if (updatePageReport)
|
||||
this.mCurrentBrowser.updatePageReport();
|
||||
|
@ -1016,7 +1025,8 @@
|
|||
// == 1 false YES
|
||||
// == 1 true NO
|
||||
// > 1 false/true NO
|
||||
var owner = (aURIs.length > 1) || aLoadInBackground ? null : this.selectedTab;
|
||||
var multiple = aURIs.length > 1;
|
||||
var owner = multiple || aLoadInBackground ? null : this.selectedTab;
|
||||
var firstTabAdded = null;
|
||||
|
||||
if (aReplace) {
|
||||
|
@ -1028,7 +1038,7 @@
|
|||
}
|
||||
}
|
||||
else
|
||||
firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: true});
|
||||
firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple});
|
||||
|
||||
var tabNum = this.tabContainer.selectedIndex;
|
||||
for (let i = 1; i < aURIs.length; ++i) {
|
||||
|
@ -1243,7 +1253,7 @@
|
|||
var tabsToClose = this.tabs.length;
|
||||
|
||||
if (!aAll)
|
||||
tabsToClose -= 1 + gBrowser._numPinnedTabs;
|
||||
tabsToClose = this.visibleTabs.length - (1 + this._numPinnedTabs);
|
||||
if (tabsToClose <= 1)
|
||||
return true;
|
||||
|
||||
|
@ -1294,11 +1304,12 @@
|
|||
return;
|
||||
|
||||
if (this.warnAboutClosingTabs(false)) {
|
||||
let tabs = this.visibleTabs;
|
||||
this.selectedTab = aTab;
|
||||
|
||||
for (let i = this.tabs.length - 1; i >= 0; --i) {
|
||||
if (this.tabs[i] != aTab && !this.tabs[i].pinned)
|
||||
this.removeTab(this.tabs[i]);
|
||||
for (let i = tabs.length - 1; i >= 0; --i) {
|
||||
if (tabs[i] != aTab && !tabs[i].pinned)
|
||||
this.removeTab(tabs[i]);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
|
@ -1323,10 +1334,19 @@
|
|||
<parameter name="aParams"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
|
||||
if (aParams)
|
||||
var animate = aParams.animate;
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate &&
|
||||
this._removingTabs.indexOf(aTab) > -1) {
|
||||
this._endRemoveTab(aTab);
|
||||
return;
|
||||
}
|
||||
|
||||
var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
|
||||
|
||||
if (!this._beginRemoveTab(aTab, false, null, true))
|
||||
return;
|
||||
|
||||
|
@ -1380,7 +1400,8 @@
|
|||
|
||||
var closeWindow = false;
|
||||
var newTab = false;
|
||||
if (this.tabs.length - this._removingTabs.length == 1) {
|
||||
if (this.tabs.length - this._removingTabs.length == 1 &&
|
||||
(!TabView || !TabView.isVisible())) {
|
||||
closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
|
||||
!window.toolbar.visible ||
|
||||
this.tabContainer._closeWindowWithLastTab;
|
||||
|
@ -1564,18 +1585,29 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var tab = aTab;
|
||||
let removing = this._removingTabs;
|
||||
function keepRemaining(tab) {
|
||||
// A tab remains only if it's not being removed nor blurred
|
||||
return removing.indexOf(tab) == -1 && tab != aTab;
|
||||
}
|
||||
|
||||
// Switch to a visible tab unless there aren't any remaining
|
||||
let remainingTabs = this.visibleTabs.filter(keepRemaining);
|
||||
if (remainingTabs.length == 0)
|
||||
remainingTabs = Array.filter(this.tabs, keepRemaining);
|
||||
|
||||
// Try to find a remaining tab that comes after the given tab
|
||||
var tab = aTab;
|
||||
do {
|
||||
tab = tab.nextSibling;
|
||||
} while (tab && this._removingTabs.indexOf(tab) != -1);
|
||||
} while (tab && remainingTabs.indexOf(tab) == -1);
|
||||
|
||||
if (!tab) {
|
||||
tab = aTab;
|
||||
|
||||
do {
|
||||
tab = tab.previousSibling;
|
||||
} while (tab && this._removingTabs.indexOf(tab) != -1);
|
||||
} while (tab && remainingTabs.indexOf(tab) == -1);
|
||||
}
|
||||
|
||||
this.selectedTab = tab;
|
||||
|
@ -1651,10 +1683,11 @@
|
|||
<method name="reloadAllTabs">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var l = this.mPanelContainer.childNodes.length;
|
||||
let tabs = this.visibleTabs;
|
||||
let l = tabs.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
try {
|
||||
this.getBrowserAtIndex(i).reload();
|
||||
this.getBrowserForTab(tabs[i]).reload();
|
||||
} catch (e) {
|
||||
// ignore failure to reload so others will be reloaded
|
||||
}
|
||||
|
@ -1751,19 +1784,30 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="showOnlyTheseTabs">
|
||||
<parameter name="aTabs"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
Array.forEach(this.tabs, function(tab) {
|
||||
tab.hidden = aTabs.indexOf(tab) == -1 && !tab.pinned && !tab.selected;
|
||||
});
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="selectTabAtIndex">
|
||||
<parameter name="aIndex"/>
|
||||
<parameter name="aEvent"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let tabs = this.visibleTabs;
|
||||
|
||||
// count backwards for aIndex < 0
|
||||
if (aIndex < 0)
|
||||
aIndex += this.tabs.length;
|
||||
aIndex += tabs.length;
|
||||
|
||||
if (aIndex >= 0 &&
|
||||
aIndex < this.tabs.length &&
|
||||
aIndex != this.tabContainer.selectedIndex)
|
||||
this.selectedTab = this.tabs[aIndex];
|
||||
if (aIndex >= 0 && aIndex < tabs.length)
|
||||
this.selectedTab = tabs[aIndex];
|
||||
|
||||
if (aEvent) {
|
||||
aEvent.preventDefault();
|
||||
|
@ -1805,7 +1849,7 @@
|
|||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.tabs.length == 1)
|
||||
if (this.visibleTabs.length == 1)
|
||||
return null;
|
||||
|
||||
// tell a new window to take the "dropped" tab
|
||||
|
@ -2390,9 +2434,9 @@
|
|||
</method>
|
||||
<method name="_canScrollToElement">
|
||||
<parameter name="tab"/>
|
||||
<body>
|
||||
return !tab.pinned;
|
||||
</body>
|
||||
<body><![CDATA[
|
||||
return !tab.pinned && !tab.hidden;
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
|
@ -2404,7 +2448,7 @@
|
|||
var tabs = document.getBindingParent(this);
|
||||
tabs.removeAttribute("overflow");
|
||||
|
||||
tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser._endRemoveTab,
|
||||
tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
|
||||
tabs.tabbrowser);
|
||||
|
||||
tabs._positionPinnedTabs();
|
||||
|
@ -2569,7 +2613,8 @@
|
|||
if (this.childNodes.length == 1 && this._closeWindowWithLastTab)
|
||||
this.setAttribute("closebuttons", "noclose");
|
||||
else {
|
||||
let tab = this.childNodes.item(this.tabbrowser._numPinnedTabs);
|
||||
// Grab the last tab for size comparison
|
||||
let tab = this.tabbrowser.visibleTabs.pop();
|
||||
if (tab && tab.getBoundingClientRect().width > this.mTabClipWidth)
|
||||
this.setAttribute("closebuttons", "alltabs");
|
||||
else
|
||||
|
@ -3366,7 +3411,7 @@
|
|||
<![CDATA[
|
||||
// set up the menu popup
|
||||
var tabcontainer = gBrowser.tabContainer;
|
||||
var tabs = tabcontainer.childNodes;
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
|
||||
// Listen for changes in the tab bar.
|
||||
tabcontainer.addEventListener("TabOpen", this, false);
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is drag.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: drag.js
|
||||
|
||||
// ----------
|
||||
// Variable: drag
|
||||
// The Drag that's currently in process.
|
||||
var drag = {
|
||||
info: null,
|
||||
zIndex: 100
|
||||
};
|
||||
|
||||
|
||||
// ##########
|
||||
// Class: Drag (formerly DragInfo)
|
||||
// Helper class for dragging <Item>s
|
||||
//
|
||||
// ----------
|
||||
// Constructor: Drag
|
||||
// Called to create a Drag in response to an <Item> draggable "start" event.
|
||||
// Note that it is also used partially during <Item>'s resizable method as well.
|
||||
//
|
||||
// Parameters:
|
||||
// item - The <Item> being dragged
|
||||
// event - The DOM event that kicks off the drag
|
||||
// isResizing - (boolean) is this a resizing instance? or (if false) dragging?
|
||||
// isFauxDrag - (boolean) true if a faux drag, which is used when simply snapping.
|
||||
var Drag = function(item, event, isResizing, isFauxDrag) {
|
||||
Utils.assert(item && (item.isAnItem || item.isAFauxItem),
|
||||
'must be an item, or at least a faux item');
|
||||
|
||||
this.isResizing = isResizing || false;
|
||||
this.item = item;
|
||||
this.el = item.container;
|
||||
this.$el = iQ(this.el);
|
||||
this.parent = this.item.parent;
|
||||
this.startPosition = new Point(event.clientX, event.clientY);
|
||||
this.startTime = Date.now();
|
||||
|
||||
this.item.isDragging = true;
|
||||
this.item.setZ(999999);
|
||||
|
||||
this.safeWindowBounds = Items.getSafeWindowBounds();
|
||||
|
||||
Trenches.activateOthersTrenches(this.el);
|
||||
|
||||
if (!isFauxDrag) {
|
||||
// When a tab drag starts, make it the focused tab.
|
||||
if (this.item.isAGroupItem) {
|
||||
var tab = UI.getActiveTab();
|
||||
if (!tab || tab.parent != this.item) {
|
||||
if (this.item._children.length)
|
||||
UI.setActiveTab(this.item._children[0]);
|
||||
}
|
||||
} else if (this.item.isATabItem) {
|
||||
UI.setActiveTab(this.item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Drag.prototype = {
|
||||
// ----------
|
||||
// Function: snapBounds
|
||||
// Adjusts the given bounds according to the currently active trenches. Used by <Drag.snap>
|
||||
//
|
||||
// Parameters:
|
||||
// bounds - (<Rect>) bounds
|
||||
// stationaryCorner - which corner is stationary? by default, the top left.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
|
||||
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
|
||||
// proportionally or not
|
||||
// checkItemStatus - (boolean) make sure this is a valid item which should be snapped
|
||||
snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
|
||||
if (!stationaryCorner)
|
||||
stationaryCorner || 'topleft';
|
||||
var update = false; // need to update
|
||||
var updateX = false;
|
||||
var updateY = false;
|
||||
var newRect;
|
||||
var snappedTrenches = {};
|
||||
|
||||
// OH SNAP!
|
||||
|
||||
// if we aren't holding down the meta key...
|
||||
if (!Keys.meta) {
|
||||
// snappable = true if we aren't a tab on top of something else, and
|
||||
// there's no active drop site...
|
||||
let snappable = !(this.item.isATabItem &&
|
||||
this.item.overlapsWithOtherItems()) &&
|
||||
!iQ(".acceptsDrop").length;
|
||||
if (!checkItemStatus || snappable) {
|
||||
newRect = Trenches.snap(bounds, stationaryCorner, assumeConstantSize,
|
||||
keepProportional);
|
||||
if (newRect) { // might be false if no changes were made
|
||||
update = true;
|
||||
snappedTrenches = newRect.snappedTrenches || {};
|
||||
bounds = newRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the bounds are in the window.
|
||||
newRect = this.snapToEdge(bounds, stationaryCorner, assumeConstantSize,
|
||||
keepProportional);
|
||||
if (newRect) {
|
||||
update = true;
|
||||
bounds = newRect;
|
||||
Utils.extend(snappedTrenches, newRect.snappedTrenches);
|
||||
}
|
||||
|
||||
Trenches.hideGuides();
|
||||
for (var edge in snappedTrenches) {
|
||||
var trench = snappedTrenches[edge];
|
||||
if (typeof trench == 'object') {
|
||||
trench.showGuide = true;
|
||||
trench.show();
|
||||
}
|
||||
}
|
||||
|
||||
return update ? bounds : false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: snap
|
||||
// Called when a drag or mousemove occurs. Set the bounds based on the mouse move first, then
|
||||
// call snap and it will adjust the item's bounds if appropriate. Also triggers the display of
|
||||
// trenches that it snapped to.
|
||||
//
|
||||
// Parameters:
|
||||
// stationaryCorner - which corner is stationary? by default, the top left.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
|
||||
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
|
||||
// proportionally or not
|
||||
snap: function Drag_snap(stationaryCorner, assumeConstantSize, keepProportional) {
|
||||
var bounds = this.item.getBounds();
|
||||
bounds = this.snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, true);
|
||||
if (bounds) {
|
||||
this.item.setBounds(bounds, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// --------
|
||||
// Function: snapToEdge
|
||||
// Returns a version of the bounds snapped to the edge if it is close enough. If not,
|
||||
// returns false. If <Keys.meta> is true, this function will simply enforce the
|
||||
// window edges.
|
||||
//
|
||||
// Parameters:
|
||||
// rect - (<Rect>) current bounds of the object
|
||||
// stationaryCorner - which corner is stationary? by default, the top left.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
|
||||
// keepProportional - (boolean) if we are allowed to change the rect's size, whether the
|
||||
// dimensions should scaled proportionally or not.
|
||||
snapToEdge: function Drag_snapToEdge(rect, stationaryCorner, assumeConstantSize, keepProportional) {
|
||||
|
||||
var swb = this.safeWindowBounds;
|
||||
var update = false;
|
||||
var updateX = false;
|
||||
var updateY = false;
|
||||
var snappedTrenches = {};
|
||||
|
||||
var snapRadius = (Keys.meta ? 0 : Trenches.defaultRadius);
|
||||
if (rect.left < swb.left + snapRadius ) {
|
||||
if (stationaryCorner.indexOf('right') > -1)
|
||||
rect.width = rect.right - swb.left;
|
||||
rect.left = swb.left;
|
||||
update = true;
|
||||
updateX = true;
|
||||
snappedTrenches.left = 'edge';
|
||||
}
|
||||
|
||||
if (rect.right > swb.right - snapRadius) {
|
||||
if (updateX || !assumeConstantSize) {
|
||||
var newWidth = swb.right - rect.left;
|
||||
if (keepProportional)
|
||||
rect.height = rect.height * newWidth / rect.width;
|
||||
rect.width = newWidth;
|
||||
update = true;
|
||||
} else if (!updateX || !Trenches.preferLeft) {
|
||||
rect.left = swb.right - rect.width;
|
||||
update = true;
|
||||
}
|
||||
snappedTrenches.right = 'edge';
|
||||
delete snappedTrenches.left;
|
||||
}
|
||||
if (rect.top < swb.top + snapRadius) {
|
||||
if (stationaryCorner.indexOf('bottom') > -1)
|
||||
rect.height = rect.bottom - swb.top;
|
||||
rect.top = swb.top;
|
||||
update = true;
|
||||
updateY = true;
|
||||
snappedTrenches.top = 'edge';
|
||||
}
|
||||
if (rect.bottom > swb.bottom - snapRadius) {
|
||||
if (updateY || !assumeConstantSize) {
|
||||
var newHeight = swb.bottom - rect.top;
|
||||
if (keepProportional)
|
||||
rect.width = rect.width * newHeight / rect.height;
|
||||
rect.height = newHeight;
|
||||
update = true;
|
||||
} else if (!updateY || !Trenches.preferTop) {
|
||||
rect.top = swb.bottom - rect.height;
|
||||
update = true;
|
||||
}
|
||||
snappedTrenches.top = 'edge';
|
||||
delete snappedTrenches.bottom;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rect.snappedTrenches = snappedTrenches;
|
||||
return rect;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: drag
|
||||
// Called in response to an <Item> draggable "drag" event.
|
||||
drag: function(event) {
|
||||
this.snap('topleft', true);
|
||||
|
||||
if (this.parent && this.parent.expanded) {
|
||||
var distance = this.startPosition.distance(new Point(event.clientX, event.clientY));
|
||||
if (distance > 100) {
|
||||
this.parent.remove(this.item);
|
||||
this.parent.collapse();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: stop
|
||||
// Called in response to an <Item> draggable "stop" event.
|
||||
stop: function() {
|
||||
Trenches.hideGuides();
|
||||
this.item.isDragging = false;
|
||||
|
||||
if (this.parent && !this.parent.locked.close && this.parent != this.item.parent &&
|
||||
this.parent.isEmpty()) {
|
||||
this.parent.close();
|
||||
}
|
||||
|
||||
if (this.parent && this.parent.expanded)
|
||||
this.parent.arrange();
|
||||
|
||||
if (this.item && !this.item.parent) {
|
||||
this.item.setZ(drag.zIndex);
|
||||
drag.zIndex++;
|
||||
|
||||
this.item.pushAway();
|
||||
}
|
||||
|
||||
Trenches.disactivate();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,258 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is infoitems.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
* Aza Raskin <aza@mozilla.com>
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Ehsan Akhgari <ehsan@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: infoitems.js
|
||||
|
||||
(function() {
|
||||
|
||||
// ##########
|
||||
// Class: InfoItem
|
||||
// An <Item> in TabView used for displaying information, such as the welcome video.
|
||||
// Note that it implements the <Subscribable> interface.
|
||||
//
|
||||
// ----------
|
||||
// Constructor: InfoItem
|
||||
//
|
||||
// Parameters:
|
||||
// bounds - a <Rect> for where the item should be located
|
||||
// options - various options for this infoItem (see below)
|
||||
//
|
||||
// Possible options:
|
||||
// locked - see <Item.locked>; default is {}
|
||||
// dontPush - true if this infoItem shouldn't push away on creation; default is false
|
||||
window.InfoItem = function(bounds, options) {
|
||||
try {
|
||||
Utils.assertThrow(Utils.isRect(bounds), 'bounds');
|
||||
|
||||
if (typeof options == 'undefined')
|
||||
options = {};
|
||||
|
||||
this._inited = false;
|
||||
this.isAnInfoItem = true;
|
||||
this.defaultSize = bounds.size();
|
||||
this.locked = (options.locked ? Utils.copy(options.locked) : {});
|
||||
this.bounds = new Rect(bounds);
|
||||
this.isDragging = false;
|
||||
|
||||
var self = this;
|
||||
|
||||
var $container = iQ('<div>')
|
||||
.addClass('info-item')
|
||||
.css(this.bounds)
|
||||
.appendTo('body');
|
||||
|
||||
this.$contents = iQ('<div>')
|
||||
.appendTo($container);
|
||||
|
||||
var $close = iQ('<div>')
|
||||
.addClass('close')
|
||||
.click(function() {
|
||||
self.close();
|
||||
})
|
||||
.appendTo($container);
|
||||
|
||||
// ___ locking
|
||||
if (this.locked.bounds)
|
||||
$container.css({cursor: 'default'});
|
||||
|
||||
if (this.locked.close)
|
||||
$close.hide();
|
||||
|
||||
// ___ Superclass initialization
|
||||
this._init($container[0]);
|
||||
|
||||
if (this.$debug)
|
||||
this.$debug.css({zIndex: -1000});
|
||||
|
||||
// ___ Finish Up
|
||||
if (!this.locked.bounds)
|
||||
this.draggable();
|
||||
|
||||
// ___ Position
|
||||
this.snap();
|
||||
|
||||
// ___ Push other objects away
|
||||
if (!options.dontPush)
|
||||
this.pushAway();
|
||||
|
||||
this._inited = true;
|
||||
this.save();
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
// ----------
|
||||
window.InfoItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
|
||||
// ----------
|
||||
// Function: getStorageData
|
||||
// Returns all of the info worth storing about this item.
|
||||
getStorageData: function() {
|
||||
var data = null;
|
||||
|
||||
try {
|
||||
data = {
|
||||
bounds: this.getBounds(),
|
||||
locked: Utils.copy(this.locked)
|
||||
};
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: save
|
||||
// Saves this item to persistent storage.
|
||||
save: function() {
|
||||
try {
|
||||
if (!this._inited) // too soon to save now
|
||||
return;
|
||||
|
||||
var data = this.getStorageData();
|
||||
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: setBounds
|
||||
// Sets the bounds with the given <Rect>, animating unless "immediately" is false.
|
||||
setBounds: function(rect, immediately) {
|
||||
try {
|
||||
Utils.assertThrow(Utils.isRect(rect), 'InfoItem.setBounds: rect must be a real rectangle!');
|
||||
|
||||
// ___ Determine what has changed
|
||||
var css = {};
|
||||
|
||||
if (rect.left != this.bounds.left)
|
||||
css.left = rect.left;
|
||||
|
||||
if (rect.top != this.bounds.top)
|
||||
css.top = rect.top;
|
||||
|
||||
if (rect.width != this.bounds.width)
|
||||
css.width = rect.width;
|
||||
|
||||
if (rect.height != this.bounds.height)
|
||||
css.height = rect.height;
|
||||
|
||||
if (Utils.isEmptyObject(css))
|
||||
return;
|
||||
|
||||
this.bounds = new Rect(rect);
|
||||
Utils.assertThrow(Utils.isRect(this.bounds),
|
||||
'InfoItem.setBounds: this.bounds must be a real rectangle!');
|
||||
|
||||
// ___ Update our representation
|
||||
if (immediately) {
|
||||
iQ(this.container).css(css);
|
||||
} else {
|
||||
TabItems.pausePainting();
|
||||
iQ(this.container).animate(css, {
|
||||
duration: 350,
|
||||
easing: "tabviewBounce",
|
||||
complete: function() {
|
||||
TabItems.resumePainting();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._updateDebugBounds();
|
||||
this.setTrenches(rect);
|
||||
this.save();
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: setZ
|
||||
// Set the Z order for the item's container.
|
||||
setZ: function(value) {
|
||||
try {
|
||||
Utils.assertThrow(typeof value == 'number', 'value must be a number');
|
||||
|
||||
this.zIndex = value;
|
||||
|
||||
iQ(this.container).css({zIndex: value});
|
||||
|
||||
if (this.$debug)
|
||||
this.$debug.css({zIndex: value + 1});
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: close
|
||||
// Closes the item.
|
||||
close: function() {
|
||||
try {
|
||||
this._sendToSubscribers("close");
|
||||
this.removeTrenches();
|
||||
iQ(this.container).fadeOut(function() {
|
||||
iQ(this).remove();
|
||||
Items.unsquish();
|
||||
});
|
||||
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: html
|
||||
// Sets the item's container's html to the specified value.
|
||||
html: function(value) {
|
||||
try {
|
||||
Utils.assertThrow(typeof value == 'string', 'value must be a string');
|
||||
this.$contents.html(value);
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,720 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is iq.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
* Aza Raskin <aza@mozilla.com>
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
*
|
||||
* This file incorporates work from:
|
||||
* jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js
|
||||
* This incorporated work is covered by the following copyright and
|
||||
* permission notice:
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: iq.js
|
||||
// Various helper functions, in the vein of jQuery.
|
||||
|
||||
// ----------
|
||||
// Function: iQ
|
||||
// Returns an iQClass object which represents an individual element or a group
|
||||
// of elements. It works pretty much like jQuery(), with a few exceptions,
|
||||
// most notably that you can't use strings with complex html,
|
||||
// just simple tags like '<div>'.
|
||||
function iQ(selector, context) {
|
||||
// The iQ object is actually just the init constructor 'enhanced'
|
||||
return new iQClass(selector, context);
|
||||
};
|
||||
|
||||
// A simple way to check for HTML strings or ID strings
|
||||
// (both of which we optimize for)
|
||||
let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
|
||||
|
||||
// Match a standalone tag
|
||||
let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
|
||||
|
||||
// ##########
|
||||
// Class: iQClass
|
||||
// The actual class of iQ result objects, representing an individual element
|
||||
// or a group of elements.
|
||||
//
|
||||
// ----------
|
||||
// Function: iQClass
|
||||
// You don't call this directly; this is what's called by iQ().
|
||||
let iQClass = function(selector, context) {
|
||||
|
||||
// Handle $(""), $(null), or $(undefined)
|
||||
if (!selector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Handle $(DOMElement)
|
||||
if (selector.nodeType) {
|
||||
this.context = selector;
|
||||
this[0] = selector;
|
||||
this.length = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// The body element only exists once, optimize finding it
|
||||
if (selector === "body" && !context) {
|
||||
this.context = document;
|
||||
this[0] = document.body;
|
||||
this.selector = "body";
|
||||
this.length = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Handle HTML strings
|
||||
if (typeof selector === "string") {
|
||||
// Are we dealing with HTML string or an ID?
|
||||
|
||||
let match = quickExpr.exec(selector);
|
||||
|
||||
// Verify a match, and that no context was specified for #id
|
||||
if (match && (match[1] || !context)) {
|
||||
|
||||
// HANDLE $(html) -> $(array)
|
||||
if (match[1]) {
|
||||
let doc = (context ? context.ownerDocument || context : document);
|
||||
|
||||
// If a single string is passed in and it's a single tag
|
||||
// just do a createElement and skip the rest
|
||||
let ret = rsingleTag.exec(selector);
|
||||
|
||||
if (ret) {
|
||||
if (Utils.isPlainObject(context)) {
|
||||
Utils.assert(false, 'does not support HTML creation with context');
|
||||
} else {
|
||||
selector = [doc.createElement(ret[1])];
|
||||
}
|
||||
|
||||
} else {
|
||||
Utils.assert(false, 'does not support complex HTML creation');
|
||||
}
|
||||
|
||||
return Utils.merge(this, selector);
|
||||
|
||||
// HANDLE $("#id")
|
||||
} else {
|
||||
let elem = document.getElementById(match[2]);
|
||||
|
||||
if (elem) {
|
||||
this.length = 1;
|
||||
this[0] = elem;
|
||||
}
|
||||
|
||||
this.context = document;
|
||||
this.selector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
// HANDLE $("TAG")
|
||||
} else if (!context && /^\w+$/.test(selector)) {
|
||||
this.selector = selector;
|
||||
this.context = document;
|
||||
selector = document.getElementsByTagName(selector);
|
||||
return Utils.merge(this, selector);
|
||||
|
||||
// HANDLE $(expr, $(...))
|
||||
} else if (!context || context.iq) {
|
||||
return (context || iQ(document)).find(selector);
|
||||
|
||||
// HANDLE $(expr, context)
|
||||
// (which is just equivalent to: $(context).find(expr)
|
||||
} else {
|
||||
return iQ(context).find(selector);
|
||||
}
|
||||
|
||||
// HANDLE $(function)
|
||||
// Shortcut for document ready
|
||||
} else if (typeof selector == "function") {
|
||||
Utils.log('iQ does not support ready functions');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof selector.selector !== "undefined") {
|
||||
this.selector = selector.selector;
|
||||
this.context = selector.context;
|
||||
}
|
||||
|
||||
let ret = this || [];
|
||||
if (selector != null) {
|
||||
// The window, strings (and functions) also have 'length'
|
||||
if (selector.length == null || typeof selector == "string" || selector.setInterval) {
|
||||
Array.push(ret, selector);
|
||||
} else {
|
||||
Utils.merge(ret, selector);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
iQClass.prototype = {
|
||||
|
||||
// Start with an empty selector
|
||||
selector: "",
|
||||
|
||||
// The default length of a iQ object is 0
|
||||
length: 0,
|
||||
|
||||
// ----------
|
||||
// Function: each
|
||||
// Execute a callback for every element in the matched set.
|
||||
each: function(callback) {
|
||||
if (typeof callback != "function") {
|
||||
Utils.assert(false, "each's argument must be a function");
|
||||
return null;
|
||||
}
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
callback(this[i]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: addClass
|
||||
// Adds the given class(es) to the receiver.
|
||||
addClass: function(value) {
|
||||
Utils.assertThrow(typeof value == "string" && value,
|
||||
'requires a valid string argument');
|
||||
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.nodeType === 1) {
|
||||
value.split(/\s+/).forEach(function(className) {
|
||||
elem.classList.add(className);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: removeClass
|
||||
// Removes the given class(es) from the receiver.
|
||||
removeClass: function(value) {
|
||||
if (typeof value != "string" || !value) {
|
||||
Utils.assert(false, 'does not support function argument');
|
||||
return null;
|
||||
}
|
||||
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.nodeType === 1 && elem.className) {
|
||||
value.split(/\s+/).forEach(function(className) {
|
||||
elem.classList.remove(className);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: hasClass
|
||||
// Returns true is the receiver has the given css class.
|
||||
hasClass: function(singleClassName) {
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (this[i].classList.contains(singleClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: find
|
||||
// Searches the receiver and its children, returning a new iQ object with
|
||||
// elements that match the given selector.
|
||||
find: function(selector) {
|
||||
let ret = [];
|
||||
let length = 0;
|
||||
|
||||
let l = this.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
length = ret.length;
|
||||
try {
|
||||
Utils.merge(ret, this[i].querySelectorAll(selector));
|
||||
} catch(e) {
|
||||
Utils.log('iQ.find error (bad selector)', e);
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
// Make sure that the results are unique
|
||||
for (let n = length; n < ret.length; n++) {
|
||||
for (let r = 0; r < length; r++) {
|
||||
if (ret[r] === ret[n]) {
|
||||
ret.splice(n--, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iQ(ret);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: remove
|
||||
// Removes the receiver from the DOM.
|
||||
remove: function() {
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.parentNode) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: empty
|
||||
// Removes all of the reciever's children and HTML content from the DOM.
|
||||
empty: function() {
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
while (elem.firstChild) {
|
||||
elem.removeChild(elem.firstChild);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: width
|
||||
// Returns the width of the receiver.
|
||||
width: function() {
|
||||
let bounds = this.bounds();
|
||||
return bounds.width;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: height
|
||||
// Returns the height of the receiver.
|
||||
height: function() {
|
||||
let bounds = this.bounds();
|
||||
return bounds.height;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: position
|
||||
// Returns an object with the receiver's position in left and top
|
||||
// properties.
|
||||
position: function() {
|
||||
let bounds = this.bounds();
|
||||
return new Point(bounds.left, bounds.top);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: bounds
|
||||
// Returns a <Rect> with the receiver's bounds.
|
||||
bounds: function() {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
let rect = this[0].getBoundingClientRect();
|
||||
return new Rect(Math.floor(rect.left), Math.floor(rect.top),
|
||||
Math.floor(rect.width), Math.floor(rect.height));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: data
|
||||
// Pass in both key and value to attach some data to the receiver;
|
||||
// pass in just key to retrieve it.
|
||||
data: function(key, value) {
|
||||
let data = null;
|
||||
if (typeof value === "undefined") {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
data = this[0].iQData;
|
||||
if (data)
|
||||
return data[key];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
data = elem.iQData;
|
||||
|
||||
if (!data)
|
||||
data = elem.iQData = {};
|
||||
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: html
|
||||
// Given a value, sets the receiver's innerHTML to it; otherwise returns
|
||||
// what's already there.
|
||||
html: function(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (typeof value === "undefined")
|
||||
return this[0].innerHTML;
|
||||
|
||||
this[0].innerHTML = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: text
|
||||
// Given a value, sets the receiver's textContent to it; otherwise returns
|
||||
// what's already there.
|
||||
text: function(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (typeof value === "undefined") {
|
||||
return this[0].textContent;
|
||||
}
|
||||
|
||||
return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: val
|
||||
// Given a value, sets the receiver's value to it; otherwise returns what's already there.
|
||||
val: function(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (typeof value === "undefined") {
|
||||
return this[0].value;
|
||||
}
|
||||
|
||||
this[0].value = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: appendTo
|
||||
// Appends the receiver to the result of iQ(selector).
|
||||
appendTo: function(selector) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
iQ(selector).append(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: append
|
||||
// Appends the result of iQ(selector) to the receiver.
|
||||
append: function(selector) {
|
||||
let object = iQ(selector);
|
||||
Utils.assert(object.length == 1 && this.length == 1,
|
||||
'does not yet support multi-objects (or null objects)');
|
||||
this[0].appendChild(object[0]);
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: attr
|
||||
// Sets or gets an attribute on the element(s).
|
||||
attr: function(key, value) {
|
||||
Utils.assert(typeof key === 'string', 'string key');
|
||||
if (typeof value === "undefined") {
|
||||
Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
|
||||
return this[0].getAttribute(key);
|
||||
}
|
||||
|
||||
for (let i = 0; this[i] != null; i++)
|
||||
this[i].setAttribute(key, value);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: css
|
||||
// Sets or gets CSS properties on the receiver. When setting certain numerical properties,
|
||||
// will automatically add "px". A property can be removed by setting it to null.
|
||||
//
|
||||
// Possible call patterns:
|
||||
// a: object, b: undefined - sets with properties from a
|
||||
// a: string, b: undefined - gets property specified by a
|
||||
// a: string, b: string/number - sets property specified by a to b
|
||||
css: function(a, b) {
|
||||
let properties = null;
|
||||
|
||||
if (typeof a === 'string') {
|
||||
let key = a;
|
||||
if (typeof b === "undefined") {
|
||||
Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
|
||||
|
||||
return window.getComputedStyle(this[0], null).getPropertyValue(key);
|
||||
}
|
||||
properties = {};
|
||||
properties[key] = b;
|
||||
} else {
|
||||
properties = a;
|
||||
}
|
||||
|
||||
let pixels = {
|
||||
'left': true,
|
||||
'top': true,
|
||||
'right': true,
|
||||
'bottom': true,
|
||||
'width': true,
|
||||
'height': true
|
||||
};
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
for (let key in properties) {
|
||||
let value = properties[key];
|
||||
if (pixels[key] && typeof value != 'string')
|
||||
value += 'px';
|
||||
|
||||
if (value == null) {
|
||||
elem.style.removeProperty(key);
|
||||
} else if (key.indexOf('-') != -1)
|
||||
elem.style.setProperty(key, value, '');
|
||||
else
|
||||
elem.style[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: animate
|
||||
// Uses CSS transitions to animate the element.
|
||||
//
|
||||
// Parameters:
|
||||
// css - an object map of the CSS properties to change
|
||||
// options - an object with various properites (see below)
|
||||
//
|
||||
// Possible "options" properties:
|
||||
// duration - how long to animate, in milliseconds
|
||||
// easing - easing function to use. Possibilities include
|
||||
// "tabviewBounce", "easeInQuad". Default is "ease".
|
||||
// complete - function to call once the animation is done, takes nothing
|
||||
// in, but "this" is set to the element that was animated.
|
||||
animate: function(css, options) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
let easings = {
|
||||
tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)",
|
||||
// TODO: change 1.0 above to 1.29 after bug 575672 is fixed
|
||||
|
||||
easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
|
||||
fast: 'cubic-bezier(0.7,0,1,1)'
|
||||
};
|
||||
|
||||
let duration = (options.duration || 400);
|
||||
let easing = (easings[options.easing] || 'ease');
|
||||
|
||||
// The latest versions of Firefox do not animate from a non-explicitly
|
||||
// set css properties. So for each element to be animated, go through
|
||||
// and explicitly define 'em.
|
||||
let rupper = /([A-Z])/g;
|
||||
this.each(function(elem) {
|
||||
let cStyle = window.getComputedStyle(elem, null);
|
||||
for (let prop in css) {
|
||||
prop = prop.replace(rupper, "-$1").toLowerCase();
|
||||
iQ(elem).css(prop, cStyle.getPropertyValue(prop));
|
||||
}
|
||||
});
|
||||
|
||||
this.css({
|
||||
'-moz-transition-property': 'all', // TODO: just animate the properties we're changing
|
||||
'-moz-transition-duration': (duration / 1000) + 's',
|
||||
'-moz-transition-timing-function': easing
|
||||
});
|
||||
|
||||
this.css(css);
|
||||
|
||||
let self = this;
|
||||
setTimeout(function() {
|
||||
self.css({
|
||||
'-moz-transition-property': 'none',
|
||||
'-moz-transition-duration': '',
|
||||
'-moz-transition-timing-function': ''
|
||||
});
|
||||
|
||||
if (typeof options.complete == "function")
|
||||
options.complete.apply(self);
|
||||
}, duration);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: fadeOut
|
||||
// Animates the receiver to full transparency. Calls callback on completion.
|
||||
fadeOut: function(callback) {
|
||||
Utils.assert(typeof callback == "function" || typeof callback === "undefined",
|
||||
'does not yet support duration');
|
||||
|
||||
this.animate({
|
||||
opacity: 0
|
||||
}, {
|
||||
duration: 400,
|
||||
complete: function() {
|
||||
iQ(this).css({display: 'none'});
|
||||
if (typeof callback == "function")
|
||||
callback.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: fadeIn
|
||||
// Animates the receiver to full opacity.
|
||||
fadeIn: function() {
|
||||
this.css({display: ''});
|
||||
this.animate({
|
||||
opacity: 1
|
||||
}, {
|
||||
duration: 400
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: hide
|
||||
// Hides the receiver.
|
||||
hide: function() {
|
||||
this.css({display: 'none', opacity: 0});
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: show
|
||||
// Shows the receiver.
|
||||
show: function() {
|
||||
this.css({display: '', opacity: 1});
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: bind
|
||||
// Binds the given function to the given event type. Also wraps the function
|
||||
// in a try/catch block that does a Utils.log on any errors.
|
||||
bind: function(type, func) {
|
||||
let handler = function(event) func.apply(this, [event]);
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
if (!elem.iQEventData)
|
||||
elem.iQEventData = {};
|
||||
|
||||
if (!elem.iQEventData[type])
|
||||
elem.iQEventData[type] = [];
|
||||
|
||||
elem.iQEventData[type].push({
|
||||
original: func,
|
||||
modified: handler
|
||||
});
|
||||
|
||||
elem.addEventListener(type, handler, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: one
|
||||
// Binds the given function to the given event type, but only for one call;
|
||||
// automatically unbinds after the event fires once.
|
||||
one: function(type, func) {
|
||||
Utils.assert(typeof func == "function", 'does not support eventData argument');
|
||||
|
||||
let handler = function(e) {
|
||||
iQ(this).unbind(type, handler);
|
||||
return func.apply(this, [e]);
|
||||
};
|
||||
|
||||
return this.bind(type, handler);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: unbind
|
||||
// Unbinds the given function from the given event type.
|
||||
unbind: function(type, func) {
|
||||
Utils.assert(typeof func == "function", 'Must provide a function');
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
let handler = func;
|
||||
if (elem.iQEventData && elem.iQEventData[type]) {
|
||||
let count = elem.iQEventData[type].length;
|
||||
for (let a = 0; a < count; a++) {
|
||||
let pair = elem.iQEventData[type][a];
|
||||
if (pair.original == func) {
|
||||
handler = pair.modified;
|
||||
elem.iQEventData[type].splice(a, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elem.removeEventListener(type, handler, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------
|
||||
// Create various event aliases
|
||||
let events = [
|
||||
'keyup',
|
||||
'keydown',
|
||||
'mouseup',
|
||||
'mousedown',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mousemove',
|
||||
'click',
|
||||
'resize',
|
||||
'change',
|
||||
'blur',
|
||||
'focus'
|
||||
];
|
||||
|
||||
events.forEach(function(event) {
|
||||
iQClass.prototype[event] = function(func) {
|
||||
return this.bind(event, func);
|
||||
};
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is TabView Groups.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Edward Lee <edilee@mozilla.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let EXPORTED_SYMBOLS = ["Groups"];
|
||||
|
||||
let Groups = let (T = {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Public
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Private
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
init: function init() {
|
||||
// Only allow calling init once
|
||||
T.init = function() T;
|
||||
|
||||
// load all groups data
|
||||
// presumably we can load from app global, not a window
|
||||
// how do we know which window has which group?
|
||||
// load tab data to figure out which go into which group
|
||||
// set up interface for subscribing to our data
|
||||
|
||||
return T;
|
||||
}
|
||||
}) T.init();
|
|
@ -0,0 +1,728 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is utils.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Aza Raskin <aza@mozilla.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
*
|
||||
* This file incorporates work from:
|
||||
* jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js
|
||||
* This incorporated work is covered by the following copyright and
|
||||
* permission notice:
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: utils.js
|
||||
|
||||
let EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils"];
|
||||
|
||||
// #########
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// ##########
|
||||
// Class: Point
|
||||
// A simple point.
|
||||
//
|
||||
// Constructor: Point
|
||||
// If a is a Point, creates a copy of it. Otherwise, expects a to be x,
|
||||
// and creates a Point with it along with y. If either a or y are omitted,
|
||||
// 0 is used in their place.
|
||||
function Point(a, y) {
|
||||
if (Utils.isPoint(a)) {
|
||||
this.x = a.x;
|
||||
this.y = a.y;
|
||||
} else {
|
||||
this.x = (Utils.isNumber(a) ? a : 0);
|
||||
this.y = (Utils.isNumber(y) ? y : 0);
|
||||
}
|
||||
};
|
||||
|
||||
Point.prototype = {
|
||||
// ----------
|
||||
// Function: distance
|
||||
// Returns the distance from this point to the given <Point>.
|
||||
distance: function(point) {
|
||||
var ax = this.x - point.x;
|
||||
var ay = this.y - point.y;
|
||||
return Math.sqrt((ax * ax) + (ay * ay));
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Rect
|
||||
// A simple rectangle. Note that in addition to the left and width, it also has
|
||||
// a right property; changing one affects the others appropriately. Same for the
|
||||
// vertical properties.
|
||||
//
|
||||
// Constructor: Rect
|
||||
// If a is a Rect, creates a copy of it. Otherwise, expects a to be left,
|
||||
// and creates a Rect with it along with top, width, and height.
|
||||
function Rect(a, top, width, height) {
|
||||
// Note: perhaps 'a' should really be called 'rectOrLeft'
|
||||
if (Utils.isRect(a)) {
|
||||
this.left = a.left;
|
||||
this.top = a.top;
|
||||
this.width = a.width;
|
||||
this.height = a.height;
|
||||
} else {
|
||||
this.left = a;
|
||||
this.top = top;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
};
|
||||
|
||||
Rect.prototype = {
|
||||
|
||||
get right() this.left + this.width,
|
||||
set right(value) {
|
||||
this.width = value - this.left;
|
||||
},
|
||||
|
||||
get bottom() this.top + this.height,
|
||||
set bottom(value) {
|
||||
this.height = value - this.top;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Variable: xRange
|
||||
// Gives you a new <Range> for the horizontal dimension.
|
||||
get xRange() new Range(this.left, this.right),
|
||||
|
||||
// ----------
|
||||
// Variable: yRange
|
||||
// Gives you a new <Range> for the vertical dimension.
|
||||
get yRange() new Range(this.top, this.bottom),
|
||||
|
||||
// ----------
|
||||
// Function: intersects
|
||||
// Returns true if this rectangle intersects the given <Rect>.
|
||||
intersects: function(rect) {
|
||||
return (rect.right > this.left &&
|
||||
rect.left < this.right &&
|
||||
rect.bottom > this.top &&
|
||||
rect.top < this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: intersection
|
||||
// Returns a new <Rect> with the intersection of this rectangle and the give <Rect>,
|
||||
// or null if they don't intersect.
|
||||
intersection: function(rect) {
|
||||
var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0);
|
||||
box.right = Math.min(rect.right, this.right);
|
||||
box.bottom = Math.min(rect.bottom, this.bottom);
|
||||
if (box.width > 0 && box.height > 0)
|
||||
return box;
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Returns a boolean denoting if the <Rect> is contained inside
|
||||
// of the bounding rect.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Rect>
|
||||
contains: function(rect) {
|
||||
return (rect.left > this.left &&
|
||||
rect.right < this.right &&
|
||||
rect.top > this.top &&
|
||||
rect.bottom < this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: center
|
||||
// Returns a new <Point> with the center location of this rectangle.
|
||||
center: function() {
|
||||
return new Point(this.left + (this.width / 2), this.top + (this.height / 2));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: size
|
||||
// Returns a new <Point> with the dimensions of this rectangle.
|
||||
size: function() {
|
||||
return new Point(this.width, this.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: position
|
||||
// Returns a new <Point> with the top left of this rectangle.
|
||||
position: function() {
|
||||
return new Point(this.left, this.top);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: area
|
||||
// Returns the area of this rectangle.
|
||||
area: function() {
|
||||
return this.width * this.height;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: inset
|
||||
// Makes the rect smaller (if the arguments are positive) as if a margin is added all around
|
||||
// the initial rect, with the margin widths (symmetric) being specified by the arguments.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Point> or two arguments: x and y
|
||||
inset: function(a, b) {
|
||||
if (Utils.isPoint(a)) {
|
||||
b = a.y;
|
||||
a = a.x;
|
||||
}
|
||||
|
||||
this.left += a;
|
||||
this.width -= a * 2;
|
||||
this.top += b;
|
||||
this.height -= b * 2;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: offset
|
||||
// Moves (translates) the rect by the given vector.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Point> or two arguments: x and y
|
||||
offset: function(a, b) {
|
||||
if (Utils.isPoint(a)) {
|
||||
this.left += a.x;
|
||||
this.top += a.y;
|
||||
} else {
|
||||
this.left += a;
|
||||
this.top += b;
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: equals
|
||||
// Returns true if this rectangle is identical to the given <Rect>.
|
||||
equals: function(rect) {
|
||||
return (rect.left == this.left &&
|
||||
rect.top == this.top &&
|
||||
rect.width == this.width &&
|
||||
rect.height == this.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: union
|
||||
// Returns a new <Rect> with the union of this rectangle and the given <Rect>.
|
||||
union: function(a) {
|
||||
var newLeft = Math.min(a.left, this.left);
|
||||
var newTop = Math.min(a.top, this.top);
|
||||
var newWidth = Math.max(a.right, this.right) - newLeft;
|
||||
var newHeight = Math.max(a.bottom, this.bottom) - newTop;
|
||||
var newRect = new Rect(newLeft, newTop, newWidth, newHeight);
|
||||
|
||||
return newRect;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: copy
|
||||
// Copies the values of the given <Rect> into this rectangle.
|
||||
copy: function(a) {
|
||||
this.left = a.left;
|
||||
this.top = a.top;
|
||||
this.width = a.width;
|
||||
this.height = a.height;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: css
|
||||
// Returns an object with the dimensions of this rectangle, suitable for
|
||||
// passing into iQ's css method. You could of course just pass the rectangle
|
||||
// straight in, but this is cleaner, as it removes all the extraneous
|
||||
// properties. If you give a <Rect> to <iQClass.css> without this, it will
|
||||
// ignore the extraneous properties, but result in CSS warnings.
|
||||
css: function() {
|
||||
return {
|
||||
left: this.left,
|
||||
top: this.top,
|
||||
width: this.width,
|
||||
height: this.height
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Range
|
||||
// A physical interval, with a min and max.
|
||||
//
|
||||
// Constructor: Range
|
||||
// Creates a Range with the given min and max
|
||||
function Range(min, max) {
|
||||
if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it.
|
||||
this.min = min.min;
|
||||
this.max = min.max;
|
||||
} else {
|
||||
this.min = min || 0;
|
||||
this.max = max || 0;
|
||||
}
|
||||
};
|
||||
|
||||
Range.prototype = {
|
||||
// Variable: extent
|
||||
// Equivalent to max-min
|
||||
get extent() {
|
||||
return (this.max - this.min);
|
||||
},
|
||||
|
||||
set extent(extent) {
|
||||
this.max = extent - this.min;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Whether the <Range> contains the given <Range> or value or not.
|
||||
//
|
||||
// Paramaters
|
||||
// - a number or <Range>
|
||||
contains: function(value) {
|
||||
if (Utils.isNumber(value))
|
||||
return value >= this.min && value <= this.max;
|
||||
if (Utils.isRange(value))
|
||||
return value.min >= this.min && value.max <= this.max;
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: proportion
|
||||
// Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min,
|
||||
// returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max).
|
||||
//
|
||||
// Paramaters
|
||||
// - a number
|
||||
// - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear.
|
||||
proportion: function(value, smooth) {
|
||||
if (value <= this.min)
|
||||
return 0;
|
||||
if (this.max <= value)
|
||||
return 1;
|
||||
|
||||
var proportion = (value - this.min) / this.extent;
|
||||
|
||||
if (smooth) {
|
||||
// The ease function ".5+.5*Math.tanh(4*x-2)" is a pretty
|
||||
// little graph. It goes from near 0 at x=0 to near 1 at x=1
|
||||
// smoothly and beautifully.
|
||||
// http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29
|
||||
function tanh(x) {
|
||||
var e = Math.exp(x);
|
||||
return (e - 1/e) / (e + 1/e);
|
||||
}
|
||||
return .5 - .5 * tanh(2 - 4 * proportion);
|
||||
}
|
||||
|
||||
return proportion;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: scale
|
||||
// Takes the given value in [0,1] and maps it to the associated value on the Range.
|
||||
//
|
||||
// Paramaters
|
||||
// - a number in [0,1]
|
||||
scale: function(value) {
|
||||
if (value > 1)
|
||||
value = 1;
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
return this.min + this.extent * value;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Subscribable
|
||||
// A mix-in for allowing objects to collect subscribers for custom events.
|
||||
function Subscribable() {
|
||||
this.subscribers = null;
|
||||
};
|
||||
|
||||
Subscribable.prototype = {
|
||||
// ----------
|
||||
// Function: addSubscriber
|
||||
// The given callback will be called when the Subscribable fires the given event.
|
||||
// The refObject is used to facilitate removal if necessary.
|
||||
addSubscriber: function(refObject, eventName, callback) {
|
||||
try {
|
||||
Utils.assertThrow(refObject, "refObject");
|
||||
Utils.assertThrow(typeof callback == "function", "callback must be a function");
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers)
|
||||
this.subscribers = {};
|
||||
|
||||
if (!this.subscribers[eventName])
|
||||
this.subscribers[eventName] = [];
|
||||
|
||||
var subs = this.subscribers[eventName];
|
||||
var existing = subs.filter(function(element) {
|
||||
return element.refObject == refObject;
|
||||
});
|
||||
|
||||
if (existing.length) {
|
||||
Utils.assert(existing.length == 1, 'should only ever be one');
|
||||
existing[0].callback = callback;
|
||||
} else {
|
||||
subs.push({
|
||||
refObject: refObject,
|
||||
callback: callback
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: removeSubscriber
|
||||
// Removes the callback associated with refObject for the given event.
|
||||
removeSubscriber: function(refObject, eventName) {
|
||||
try {
|
||||
Utils.assertThrow(refObject, "refObject");
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers || !this.subscribers[eventName])
|
||||
return;
|
||||
|
||||
this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) {
|
||||
return element.refObject != refObject;
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _sendToSubscribers
|
||||
// Internal routine. Used by the Subscribable to fire events.
|
||||
_sendToSubscribers: function(eventName, eventInfo) {
|
||||
try {
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers || !this.subscribers[eventName])
|
||||
return;
|
||||
|
||||
var subsCopy = this.subscribers[eventName].concat();
|
||||
subsCopy.forEach(function(object) {
|
||||
try {
|
||||
object.callback(this, eventInfo);
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Utils
|
||||
// Singelton with common utility functions.
|
||||
let Utils = {
|
||||
// ___ Logging
|
||||
|
||||
// ----------
|
||||
// Function: log
|
||||
// Prints the given arguments to the JavaScript error console as a message.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
log: function() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
Services.console.logStringMessage(text);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: error
|
||||
// Prints the given arguments to the JavaScript error console as an error.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
error: function() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
Cu.reportError("tabview error: " + text);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: trace
|
||||
// Prints the given arguments to the JavaScript error console as a message,
|
||||
// along with a full stack trace.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
trace: function() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
// cut off the first two lines of the stack trace, because they're just this function.
|
||||
let stack = Error().stack.replace(/^.*?\n.*?\n/, "");
|
||||
// if the caller was assert, cut out the line for the assert function as well.
|
||||
if (this.trace.caller.name == 'Utils_assert')
|
||||
stack = stack.replace(/^.*?\n/, "");
|
||||
this.log('trace: ' + text + '\n' + stack);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: assert
|
||||
// Prints a stack trace along with label (as a console message) if condition is false.
|
||||
assert: function Utils_assert(condition, label) {
|
||||
if (!condition) {
|
||||
let text;
|
||||
if (typeof label != 'string')
|
||||
text = 'badly formed assert';
|
||||
else
|
||||
text = "tabview assert: " + label;
|
||||
|
||||
this.trace(text);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: assertThrow
|
||||
// Throws label as an exception if condition is false.
|
||||
assertThrow: function(condition, label) {
|
||||
if (!condition) {
|
||||
let text;
|
||||
if (typeof label != 'string')
|
||||
text = 'badly formed assert';
|
||||
else
|
||||
text = "tabview assert: " + label;
|
||||
|
||||
// cut off the first two lines of the stack trace, because they're just this function.
|
||||
text += Error().stack.replace(/^.*?\n.*?\n/, "");
|
||||
|
||||
throw text;
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: expandObject
|
||||
// Prints the given object to a string, including all of its properties.
|
||||
expandObject: function(obj) {
|
||||
var s = obj + ' = {';
|
||||
for (let prop in obj) {
|
||||
let value;
|
||||
try {
|
||||
value = obj[prop];
|
||||
} catch(e) {
|
||||
value = '[!!error retrieving property]';
|
||||
}
|
||||
|
||||
s += prop + ': ';
|
||||
if (typeof value == 'string')
|
||||
s += '\'' + value + '\'';
|
||||
else if (typeof value == 'function')
|
||||
s += 'function';
|
||||
else
|
||||
s += value;
|
||||
|
||||
s += ', ';
|
||||
}
|
||||
return s + '}';
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: expandArgumentsForLog
|
||||
// Expands all of the given args (an array) into a single string.
|
||||
expandArgumentsForLog: function(args) {
|
||||
var that = this;
|
||||
return Array.map(args, function(arg) {
|
||||
return typeof arg == 'object' ? that.expandObject(arg) : arg;
|
||||
}).join('; ');
|
||||
},
|
||||
|
||||
// ___ Misc
|
||||
|
||||
// ----------
|
||||
// Function: isRightClick
|
||||
// Given a DOM mouse event, returns true if it was for the right mouse button.
|
||||
isRightClick: function(event) {
|
||||
return event.button == 2;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isDOMElement
|
||||
// Returns true if the given object is a DOM element.
|
||||
isDOMElement: function(object) {
|
||||
return object instanceof Ci.nsIDOMElement;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isNumber
|
||||
// Returns true if the argument is a valid number.
|
||||
isNumber: function(n) {
|
||||
return typeof n == 'number' && !isNaN(n);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isRect
|
||||
// Returns true if the given object (r) looks like a <Rect>.
|
||||
isRect: function(r) {
|
||||
return (r &&
|
||||
this.isNumber(r.left) &&
|
||||
this.isNumber(r.top) &&
|
||||
this.isNumber(r.width) &&
|
||||
this.isNumber(r.height));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isRange
|
||||
// Returns true if the given object (r) looks like a <Range>.
|
||||
isRange: function(r) {
|
||||
return (r &&
|
||||
this.isNumber(r.min) &&
|
||||
this.isNumber(r.max));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isPoint
|
||||
// Returns true if the given object (p) looks like a <Point>.
|
||||
isPoint: function(p) {
|
||||
return (p && this.isNumber(p.x) && this.isNumber(p.y));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isPlainObject
|
||||
// Check to see if an object is a plain object (created using "{}" or "new Object").
|
||||
isPlainObject: function(obj) {
|
||||
// Must be an Object.
|
||||
// Make sure that DOM nodes and window objects don't pass through, as well
|
||||
if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" ||
|
||||
obj.nodeType || obj.setInterval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not own constructor property must be Object
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
if (obj.constructor &&
|
||||
!hasOwnProperty.call(obj, "constructor") &&
|
||||
!hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
|
||||
var key;
|
||||
for (key in obj) {}
|
||||
|
||||
return key === undefined || hasOwnProperty.call(obj, key);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isEmptyObject
|
||||
// Returns true if the given object has no members.
|
||||
isEmptyObject: function(obj) {
|
||||
for (let name in obj)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: copy
|
||||
// Returns a copy of the argument. Note that this is a shallow copy; if the argument
|
||||
// has properties that are themselves objects, those properties will be copied by reference.
|
||||
copy: function(value) {
|
||||
if (value && typeof value == 'object') {
|
||||
if (Array.isArray(value))
|
||||
return this.extend([], value);
|
||||
return this.extend({}, value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: merge
|
||||
// Merge two array-like objects into the first and return it.
|
||||
merge: function(first, second) {
|
||||
Array.forEach(second, function(el) Array.push(first, el));
|
||||
return first;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: extend
|
||||
// Pass several objects in and it will combine them all into the first object and return it.
|
||||
extend: function() {
|
||||
|
||||
// copy reference to target object
|
||||
let target = arguments[0] || {};
|
||||
// Deep copy is not supported
|
||||
if (typeof target === "boolean") {
|
||||
this.assert(false, "The first argument of extend cannot be a boolean." +
|
||||
"Deep copy is not supported.");
|
||||
return target;
|
||||
}
|
||||
|
||||
// Back when this was in iQ + iQ.fn, so you could extend iQ objects with it.
|
||||
// This is no longer supported.
|
||||
let length = arguments.length;
|
||||
if (length === 1) {
|
||||
this.assert(false, "Extending the iQ prototype using extend is not supported.");
|
||||
return target;
|
||||
}
|
||||
|
||||
// Handle case when target is a string or something
|
||||
if (typeof target != "object" && typeof target != "function") {
|
||||
target = {};
|
||||
}
|
||||
|
||||
for (let i = 1; i < length; i++) {
|
||||
// Only deal with non-null/undefined values
|
||||
let options = arguments[i];
|
||||
if (options != null) {
|
||||
// Extend the base object
|
||||
for (let name in options) {
|
||||
let copy = options[name];
|
||||
|
||||
// Prevent never-ending loop
|
||||
if (target === copy)
|
||||
continue;
|
||||
|
||||
if (copy !== undefined)
|
||||
target[name] = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,213 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is storage.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ehsan Akhgari <ehsan@mozilla.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: storage.js
|
||||
|
||||
// ##########
|
||||
// Class: Storage
|
||||
// Singleton for permanent storage of TabView data.
|
||||
let Storage = {
|
||||
GROUP_DATA_IDENTIFIER: "tabview-group",
|
||||
GROUPS_DATA_IDENTIFIER: "tabview-groups",
|
||||
TAB_DATA_IDENTIFIER: "tabview-tab",
|
||||
UI_DATA_IDENTIFIER: "tabview-ui",
|
||||
|
||||
// ----------
|
||||
// Function: init
|
||||
// Sets up the object.
|
||||
init: function() {
|
||||
this._sessionStore =
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: uninit
|
||||
uninit : function() {
|
||||
this._sessionStore = null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: wipe
|
||||
// Cleans out all the stored data, leaving empty objects.
|
||||
wipe: function() {
|
||||
try {
|
||||
var self = this;
|
||||
|
||||
// ___ Tabs
|
||||
AllTabs.tabs.forEach(function(tab) {
|
||||
if (tab.ownerDocument.defaultView != gWindow)
|
||||
return;
|
||||
|
||||
self.saveTab(tab, null);
|
||||
});
|
||||
|
||||
// ___ Other
|
||||
this.saveGroupItemsData(gWindow, {});
|
||||
this.saveUIData(gWindow, {});
|
||||
|
||||
this._sessionStore.setWindowValue(gWindow, this.GROUP_DATA_IDENTIFIER,
|
||||
JSON.stringify({}));
|
||||
} catch (e) {
|
||||
Utils.log("Error in wipe: "+e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveTab
|
||||
// Saves the data for a single tab.
|
||||
saveTab: function(tab, data) {
|
||||
Utils.assert(tab, "tab");
|
||||
|
||||
this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
|
||||
JSON.stringify(data));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: getTabData
|
||||
// Returns the data object associated with a single tab.
|
||||
getTabData: function(tab) {
|
||||
Utils.assert(tab, "tab");
|
||||
|
||||
var existingData = null;
|
||||
try {
|
||||
var tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
|
||||
if (tabData != "") {
|
||||
existingData = JSON.parse(tabData);
|
||||
}
|
||||
} catch (e) {
|
||||
// getWindowValue will fail if the property doesn't exist
|
||||
Utils.log(e);
|
||||
}
|
||||
|
||||
return existingData;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveGroupItem
|
||||
// Saves the data for a single groupItem, associated with a specific window.
|
||||
saveGroupItem: function(win, data) {
|
||||
var id = data.id;
|
||||
var existingData = this.readGroupItemData(win);
|
||||
existingData[id] = data;
|
||||
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
|
||||
JSON.stringify(existingData));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: deleteGroupItem
|
||||
// Deletes the data for a single groupItem from the given window.
|
||||
deleteGroupItem: function(win, id) {
|
||||
var existingData = this.readGroupItemData(win);
|
||||
delete existingData[id];
|
||||
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
|
||||
JSON.stringify(existingData));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readGroupItemData
|
||||
// Returns the data for all groupItems associated with the given window.
|
||||
readGroupItemData: function(win) {
|
||||
var existingData = {};
|
||||
try {
|
||||
existingData = JSON.parse(
|
||||
this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER)
|
||||
);
|
||||
} catch (e) {
|
||||
// getWindowValue will fail if the property doesn't exist
|
||||
Utils.log("Error in readGroupItemData: "+e);
|
||||
}
|
||||
return existingData;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveGroupItemsData
|
||||
// Saves the global data for the <GroupItems> singleton for the given window.
|
||||
saveGroupItemsData: function(win, data) {
|
||||
this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readGroupItemsData
|
||||
// Reads the global data for the <GroupItems> singleton for the given window.
|
||||
readGroupItemsData: function(win) {
|
||||
return this.readData(win, this.GROUPS_DATA_IDENTIFIER);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveUIData
|
||||
// Saves the global data for the <UIManager> singleton for the given window.
|
||||
saveUIData: function(win, data) {
|
||||
this.saveData(win, this.UI_DATA_IDENTIFIER, data);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readUIData
|
||||
// Reads the global data for the <UIManager> singleton for the given window.
|
||||
readUIData: function(win) {
|
||||
return this.readData(win, this.UI_DATA_IDENTIFIER);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveData
|
||||
// Generic routine for saving data to a window.
|
||||
saveData: function(win, id, data) {
|
||||
try {
|
||||
this._sessionStore.setWindowValue(win, id, JSON.stringify(data));
|
||||
} catch (e) {
|
||||
Utils.log("Error in saveData: "+e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readData
|
||||
// Generic routine for reading data from a window.
|
||||
readData: function(win, id) {
|
||||
var existingData = {};
|
||||
try {
|
||||
var data = this._sessionStore.getWindowValue(win, id);
|
||||
if (data)
|
||||
existingData = JSON.parse(data);
|
||||
} catch (e) {
|
||||
Utils.log("Error in readData: "+e);
|
||||
}
|
||||
|
||||
return existingData;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,479 @@
|
|||
html {
|
||||
overflow: hidden;
|
||||
/* image-rendering: -moz-crisp-edges; */
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: transparent;
|
||||
font-family: Tahoma, sans-serif !important;
|
||||
padding: 0px;
|
||||
color: rgba(0,0,0,0.4);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#bg {
|
||||
background: -moz-linear-gradient(top,#C4C4C4,#9E9E9E);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999999;
|
||||
}
|
||||
|
||||
/* Tab Styling
|
||||
----------------------------------*/
|
||||
|
||||
|
||||
.tab {
|
||||
position: absolute;
|
||||
padding: 4px 6px 6px 4px;
|
||||
border: 1px solid rgba(230,230,230,1);
|
||||
background-color: rgba(245,245,245,1);
|
||||
overflow: visible !important;
|
||||
-moz-border-radius: 0.4em;
|
||||
-moz-box-shadow: inset rgba(255, 255, 255, 0.6) 0 0 0 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tab canvas,
|
||||
.cached-thumb {
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.thumb-shadow {
|
||||
position: absolute;
|
||||
border-bottom: 5px solid rgba(0,0,0,0.05);
|
||||
margin-right: -12px;
|
||||
bottom: 2px;
|
||||
width: 94.5%;
|
||||
}
|
||||
|
||||
.favicon {
|
||||
position: absolute;
|
||||
background-color: rgba(245,245,245,1);
|
||||
-moz-border-radius-bottomright: 0.4em;
|
||||
-moz-box-shadow:
|
||||
inset rgba(255, 255, 255, 0.6) 0 -2px 0px,
|
||||
inset rgba(255, 255, 255, 0.6) -2px 0px 0px;
|
||||
padding: 4px 6px 6px 4px;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
border-right: 1px solid rgba(0,0,0,0.2);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.2);
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
.favicon img {
|
||||
border: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
/* background is set in platform.css */
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.expander {
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 6px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(chrome://global/skin/icons/resizer.png) no-repeat;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.expander:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.favicon img:hover,
|
||||
.close img:hover,
|
||||
.expander img:hover {
|
||||
opacity: 1;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
text-align: center;
|
||||
width: 94.5%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stacked {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.stacked .tab-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stacked .thumb-shadow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stacked .thumb {
|
||||
-moz-box-shadow: rgba(0,0,0,.2) 1px 1px 6px;
|
||||
}
|
||||
|
||||
.stack-trayed .tab-title {
|
||||
display: block !important;
|
||||
text-shadow: rgba(0,0,0,1) 1px 1px 2px;
|
||||
color: #EEE;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.stack-trayed .thumb {
|
||||
-moz-box-shadow: none !important;
|
||||
}
|
||||
|
||||
.focus {
|
||||
-moz-box-shadow: rgba(54,79,225,1) 0px 0px 5px -1px !important;
|
||||
}
|
||||
|
||||
.front .tab-title,
|
||||
.front .close,
|
||||
.front .favicon,
|
||||
.front .expander,
|
||||
.front .thumb-shadow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.front .focus {
|
||||
-moz-box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Tab GroupItem
|
||||
----------------------------------*/
|
||||
|
||||
.tabInGroupItem {
|
||||
border: none;
|
||||
-moz-box-shadow: none !important;
|
||||
}
|
||||
|
||||
|
||||
.groupItem {
|
||||
position: absolute;
|
||||
/* float: left; */
|
||||
cursor: move;
|
||||
border: 1px solid rgba(230,230,230,1);
|
||||
background-color: rgba(248,248,248,1);
|
||||
-moz-border-radius: 0.4em;
|
||||
-moz-box-shadow:
|
||||
inset rgba(255, 255, 255, 0.6) 0 0 0 2px,
|
||||
rgba(0,0,0,0.2) 1px 1px 4px;
|
||||
}
|
||||
|
||||
.groupItem.activeGroupItem {
|
||||
-moz-box-shadow:
|
||||
rgba(0,0,0,0.6) 1px 1px 8px;
|
||||
}
|
||||
|
||||
.phantom {
|
||||
border: 1px solid rgba(190,190,190,1);
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-color: rgba(0,0,0,.7) !important;
|
||||
-moz-box-shadow: 3px 3px 8px rgba(0,0,0,.5);
|
||||
-moz-border-radius: 0.4em;
|
||||
/*
|
||||
border: 1px solid rgba(230,230,230,1);
|
||||
background-color: rgba(248,248,248,1);
|
||||
-moz-box-shadow:
|
||||
rgba(0,0,0, .3) 2px 2px 8px,
|
||||
inset rgba(255, 255, 255, 0.6) 0 0 0 2px; */
|
||||
}
|
||||
|
||||
/* InfoItems
|
||||
----------------------------------*/
|
||||
|
||||
.info-item {
|
||||
position: absolute;
|
||||
cursor: move;
|
||||
border: 1px solid rgba(230,230,230,1);
|
||||
background-color: rgba(248,248,248,1);
|
||||
-moz-border-radius: 0.4em;
|
||||
-moz-box-shadow:
|
||||
inset rgba(255, 255, 255, 0.6) 0 0 0 2px,
|
||||
rgba(0,0,0, .2) 1px 1px 4px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Trenches
|
||||
----------------------------------*/
|
||||
|
||||
.guideTrench,
|
||||
.visibleTrench,
|
||||
.activeVisibleTrench {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.guideTrench {
|
||||
z-index: -101;
|
||||
opacity: 0.9;
|
||||
border: 1px dashed rgba(0,0,0,.12);
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
-moz-box-shadow: 1px 1px 0 rgba(255,255,255,.15);
|
||||
}
|
||||
|
||||
.visibleTrench {
|
||||
z-index: -103;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
.activeVisibleTrench {
|
||||
z-index: -102;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.activeVisibleTrench.activeTrench {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.visibleTrench.border,
|
||||
.activeVisibleTrench.border {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.visibleTrench.guide,
|
||||
.activeVisibleTrench.guide {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
/* Other
|
||||
----------------------------------*/
|
||||
|
||||
.newTabButton {
|
||||
width: 16px;
|
||||
height: 15px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
position: absolute !important;
|
||||
cursor: pointer;
|
||||
opacity: .3;
|
||||
background-image: url(chrome://browser/skin/tabview/new-tab.png);
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.newTabButton:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.newTabButtonAlt {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
z-index: 99999;
|
||||
border: none;
|
||||
-moz-border-radius: 4px;
|
||||
font-size: 50px;
|
||||
line-height: 50px;
|
||||
height: 57px !important;
|
||||
width: 70px !important;
|
||||
margin-top: 4px !important;
|
||||
text-align: center;
|
||||
background-color: #888888;
|
||||
-moz-box-shadow: inset 0px 0px 5px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.3);
|
||||
}
|
||||
|
||||
.newTabButtonAlt > span {
|
||||
color: #909090;
|
||||
text-shadow: 0px 0px 7px rgba(0,0,0,.4), 0 -1px 0 rgba(255,255,255,.6);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.active {
|
||||
-moz-box-shadow: 5px 5px 4px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
.acceptsDrop {
|
||||
-moz-box-shadow: 2px 2px 10px -1px rgba(0,0,0,.6);
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
input.name {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: #999;
|
||||
margin: 3px 0px 0px 3px;
|
||||
padding: 1px;
|
||||
background-image: url(chrome://browser/skin/tabview/edit-light.png);
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
input.name:hover {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
input.name-locked:hover {
|
||||
border: 1px solid transparent !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
input.name:focus {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
input.defaultName {
|
||||
font-style: italic !important;
|
||||
background-image-opacity: .1;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
input.defaultName:hover {
|
||||
color: #CCC;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.title-shield {
|
||||
position: absolute;
|
||||
margin: 3px 0px 0px 3px;
|
||||
padding: 1px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.transparentBorder {
|
||||
border: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.stackExpander {
|
||||
position: absolute;
|
||||
opacity: .4;
|
||||
cursor: pointer;
|
||||
background-image: url(chrome://browser/skin/tabview/stack-expander.png);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.stackExpander:hover {
|
||||
opacity: .7 !important;
|
||||
}
|
||||
|
||||
.shield {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* Resizable
|
||||
----------------------------------*/
|
||||
.resizer {
|
||||
background-image: url(chrome://global/skin/icons/resizer.png);
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
opacity: .2;
|
||||
}
|
||||
|
||||
.iq-resizable { }
|
||||
|
||||
.iq-resizable-handle {
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
z-index: 99999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.iq-resizable-disabled .iq-resizable-handle,
|
||||
.iq-resizable-autohide .iq-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.iq-resizable-se {
|
||||
cursor: se-resize;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
}
|
||||
|
||||
/* Utils
|
||||
----------------------------------*/
|
||||
|
||||
.front {
|
||||
z-index: 999999 !important;
|
||||
-moz-border-radius: 0 !important;
|
||||
-moz-box-shadow: none !important;
|
||||
-moz-transform: none !important;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
|
||||
/* Feedback
|
||||
----------------------------------*/
|
||||
|
||||
.bottomButton {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
z-index: 99999 !important;
|
||||
background-color: blue;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: #9E9E9E;
|
||||
-moz-box-shadow: 0px 0px 4px rgba(0,0,0,.3), inset 0px 1px 0px rgba(255,255,255,.4);
|
||||
}
|
||||
|
||||
.bottomButton:hover {
|
||||
cursor: pointer;
|
||||
background-color: #A5A5A5;
|
||||
-moz-box-shadow: 0px 0px 5px rgba(0,0,0,.6), inset 0px 1px 0px rgba(255,255,255,.4);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
|
||||
<head>
|
||||
<title> </title>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||
<link rel="stylesheet" href="tabview.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/tabview/tabview.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body transparent="true">
|
||||
<div id="content">
|
||||
<div id="bg" />
|
||||
</div>
|
||||
|
||||
<script type="text/javascript;version=1.8" src="tabview.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,38 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/tabview/AllTabs.jsm");
|
||||
Cu.import("resource://gre/modules/tabview/groups.jsm");
|
||||
Cu.import("resource://gre/modules/tabview/utils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gWindow", function() {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShell).
|
||||
chromeEventHandler.ownerDocument.defaultView;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowser", function() gWindow.gBrowser);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTabViewDeck", function() {
|
||||
return gWindow.document.getElementById("tab-view-deck");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() {
|
||||
return gWindow.document.getElementById("tab-view");
|
||||
});
|
||||
|
||||
# NB: Certain files need to evaluate before others
|
||||
|
||||
#include iq.js
|
||||
#include storage.js
|
||||
#include items.js
|
||||
#include groupitems.js
|
||||
#include tabitems.js
|
||||
#include drag.js
|
||||
#include trench.js
|
||||
#include infoitems.js
|
||||
#include ui.js
|
|
@ -0,0 +1,677 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is trench.js.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
* Aza Raskin <aza@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// **********
|
||||
// Title: trench.js
|
||||
|
||||
// ##########
|
||||
// Class: Trench
|
||||
//
|
||||
// Class for drag-snapping regions; called "trenches" as they are long and narrow.
|
||||
|
||||
// Constructor: Trench
|
||||
//
|
||||
// Parameters:
|
||||
// element - the DOM element for Item (GroupItem or TabItem) from which the trench is projected
|
||||
// xory - either "x" or "y": whether the trench's <position> is along the x- or y-axis.
|
||||
// In other words, if "x", the trench is vertical; if "y", the trench is horizontal.
|
||||
// type - either "border" or "guide". Border trenches mark the border of an Item.
|
||||
// Guide trenches extend out (unless they are intercepted) and act as "guides".
|
||||
// edge - which edge of the Item that this trench corresponds to.
|
||||
// Either "top", "left", "bottom", or "right".
|
||||
var Trench = function(element, xory, type, edge) {
|
||||
//----------
|
||||
// Variable: id
|
||||
// (integer) The id for the Trench. Set sequentially via <Trenches.nextId>
|
||||
this.id = Trenches.nextId++;
|
||||
|
||||
// ---------
|
||||
// Variables: Initial parameters
|
||||
// element - (DOMElement)
|
||||
// parentItem - <Item> which projects this trench; to be set with setParentItem
|
||||
// xory - (string) "x" or "y"
|
||||
// type - (string) "border" or "guide"
|
||||
// edge - (string) "top", "left", "bottom", or "right"
|
||||
this.el = element;
|
||||
this.parentItem = null;
|
||||
this.xory = xory; // either "x" or "y"
|
||||
this.type = type; // "border" or "guide"
|
||||
this.edge = edge; // "top", "left", "bottom", or "right"
|
||||
|
||||
this.$el = iQ(this.el);
|
||||
|
||||
//----------
|
||||
// Variable: dom
|
||||
// (array) DOM elements for visible reflexes of the Trench
|
||||
this.dom = [];
|
||||
|
||||
//----------
|
||||
// Variable: showGuide
|
||||
// (boolean) Whether this trench will project a visible guide (dotted line) or not.
|
||||
this.showGuide = false;
|
||||
|
||||
//----------
|
||||
// Variable: active
|
||||
// (boolean) Whether this trench is currently active or not.
|
||||
// Basically every trench aside for those projected by the Item currently being dragged
|
||||
// all become active.
|
||||
this.active = false;
|
||||
this.gutter = Items.defaultGutter;
|
||||
|
||||
//----------
|
||||
// Variable: position
|
||||
// (integer) position is the position that we should snap to.
|
||||
this.position = 0;
|
||||
|
||||
//----------
|
||||
// Variables: some Ranges
|
||||
// range - (<Range>) explicit range; this is along the transverse axis
|
||||
// minRange - (<Range>) the minimum active range
|
||||
// activeRange - (<Range>) the currently active range
|
||||
this.range = new Range(0,10000);
|
||||
this.minRange = new Range(0,0);
|
||||
this.activeRange = new Range(0,10000);
|
||||
};
|
||||
|
||||
Trench.prototype = {
|
||||
//----------
|
||||
// Variable: radius
|
||||
// (integer) radius is how far away we should snap from
|
||||
get radius() this.customRadius || Trenches.defaultRadius,
|
||||
|
||||
setParentItem: function Trench_setParentItem(item) {
|
||||
if (!item.isAnItem) {
|
||||
Utils.assert(false, "parentItem must be an Item");
|
||||
return false;
|
||||
}
|
||||
this.parentItem = item;
|
||||
return true;
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: setPosition
|
||||
// set the trench's position.
|
||||
//
|
||||
// Parameters:
|
||||
// position - (integer) px center position of the trench
|
||||
// range - (<Range>) the explicit active range of the trench
|
||||
// minRange - (<Range>) the minimum range of the trench
|
||||
setPosition: function Trench_setPos(position, range, minRange) {
|
||||
this.position = position;
|
||||
|
||||
var page = Items.getPageBounds(true);
|
||||
|
||||
// optionally, set the range.
|
||||
if (Utils.isRange(range)) {
|
||||
this.range = range;
|
||||
} else {
|
||||
this.range = new Range(0, (this.xory == 'x' ? page.height : page.width));
|
||||
}
|
||||
|
||||
// if there's a minRange, set that too.
|
||||
if (Utils.isRange(minRange))
|
||||
this.minRange = minRange;
|
||||
|
||||
// set the appropriate bounds as a rect.
|
||||
if (this.xory == "x") // vertical
|
||||
this.rect = new Rect(this.position - this.radius, this.range.min, 2 * this.radius, this.range.extent);
|
||||
else // horizontal
|
||||
this.rect = new Rect(this.range.min, this.position - this.radius, this.range.extent, 2 * this.radius);
|
||||
|
||||
this.show(); // DEBUG
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: setActiveRange
|
||||
// set the trench's currently active range.
|
||||
//
|
||||
// Parameters:
|
||||
// activeRange - (<Range>)
|
||||
setActiveRange: function Trench_setActiveRect(activeRange) {
|
||||
if (!Utils.isRange(activeRange))
|
||||
return false;
|
||||
this.activeRange = activeRange;
|
||||
if (this.xory == "x") { // horizontal
|
||||
this.activeRect = new Rect(this.position - this.radius, this.activeRange.min, 2 * this.radius, this.activeRange.extent);
|
||||
this.guideRect = new Rect(this.position, this.activeRange.min, 0, this.activeRange.extent);
|
||||
} else { // vertical
|
||||
this.activeRect = new Rect(this.activeRange.min, this.position - this.radius, this.activeRange.extent, 2 * this.radius);
|
||||
this.guideRect = new Rect(this.activeRange.min, this.position, this.activeRange.extent, 0);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: setWithRect
|
||||
// Set the trench's position using the given rect. We know which side of the rect we should match
|
||||
// because we've already recorded this information in <edge>.
|
||||
//
|
||||
// Parameters:
|
||||
// rect - (<Rect>)
|
||||
setWithRect: function Trench_setWithRect(rect) {
|
||||
|
||||
if (!Utils.isRect(rect))
|
||||
Utils.error('argument must be Rect');
|
||||
|
||||
// First, calculate the range for this trench.
|
||||
// Border trenches are always only active for the length of this range.
|
||||
// Guide trenches, however, still use this value as its minRange.
|
||||
if (this.xory == "x")
|
||||
var range = new Range(rect.top - this.gutter, rect.bottom + this.gutter);
|
||||
else
|
||||
var range = new Range(rect.left - this.gutter, rect.right + this.gutter);
|
||||
|
||||
if (this.type == "border") {
|
||||
// border trenches have a range, so set that too.
|
||||
if (this.edge == "left")
|
||||
this.setPosition(rect.left - this.gutter, range);
|
||||
else if (this.edge == "right")
|
||||
this.setPosition(rect.right + this.gutter, range);
|
||||
else if (this.edge == "top")
|
||||
this.setPosition(rect.top - this.gutter, range);
|
||||
else if (this.edge == "bottom")
|
||||
this.setPosition(rect.bottom + this.gutter, range);
|
||||
} else if (this.type == "guide") {
|
||||
// guide trenches have no range, but do have a minRange.
|
||||
if (this.edge == "left")
|
||||
this.setPosition(rect.left, false, range);
|
||||
else if (this.edge == "right")
|
||||
this.setPosition(rect.right, false, range);
|
||||
else if (this.edge == "top")
|
||||
this.setPosition(rect.top, false, range);
|
||||
else if (this.edge == "bottom")
|
||||
this.setPosition(rect.bottom, false, range);
|
||||
}
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: show
|
||||
//
|
||||
// Show guide (dotted line), if <showGuide> is true.
|
||||
//
|
||||
// If <Trenches.showDebug> is true, we will draw the trench. Active portions are drawn with 0.5
|
||||
// opacity. If <active> is false, the entire trench will be
|
||||
// very translucent.
|
||||
show: function Trench_show() { // DEBUG
|
||||
if (this.active && this.showGuide) {
|
||||
if (!this.dom.guideTrench)
|
||||
this.dom.guideTrench = iQ("<div/>").addClass('guideTrench').css({id: 'guideTrench'+this.id});
|
||||
var guideTrench = this.dom.guideTrench;
|
||||
guideTrench.css(this.guideRect.css());
|
||||
iQ("body").append(guideTrench);
|
||||
} else {
|
||||
if (this.dom.guideTrench) {
|
||||
this.dom.guideTrench.remove();
|
||||
delete this.dom.guideTrench;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Trenches.showDebug) {
|
||||
this.hide(true); // true for dontHideGuides
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.dom.visibleTrench)
|
||||
this.dom.visibleTrench = iQ("<div/>")
|
||||
.addClass('visibleTrench')
|
||||
.addClass(this.type) // border or guide
|
||||
.css({id: 'visibleTrench'+this.id});
|
||||
var visibleTrench = this.dom.visibleTrench;
|
||||
|
||||
if (!this.dom.activeVisibleTrench)
|
||||
this.dom.activeVisibleTrench = iQ("<div/>")
|
||||
.addClass('activeVisibleTrench')
|
||||
.addClass(this.type) // border or guide
|
||||
.css({id: 'activeVisibleTrench'+this.id});
|
||||
var activeVisibleTrench = this.dom.activeVisibleTrench;
|
||||
|
||||
if (this.active)
|
||||
activeVisibleTrench.addClass('activeTrench');
|
||||
else
|
||||
activeVisibleTrench.removeClass('activeTrench');
|
||||
|
||||
visibleTrench.css(this.rect.css());
|
||||
activeVisibleTrench.css((this.activeRect || this.rect).css());
|
||||
iQ("body").append(visibleTrench);
|
||||
iQ("body").append(activeVisibleTrench);
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: hide
|
||||
// Hide the trench.
|
||||
hide: function Trench_hide(dontHideGuides) {
|
||||
if (this.dom.visibleTrench)
|
||||
this.dom.visibleTrench.remove();
|
||||
if (this.dom.activeVisibleTrench)
|
||||
this.dom.activeVisibleTrench.remove();
|
||||
if (!dontHideGuides && this.dom.guideTrench)
|
||||
this.dom.guideTrench.remove();
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: rectOverlaps
|
||||
// Given a <Rect>, compute whether it overlaps with this trench. If it does, return an
|
||||
// adjusted ("snapped") <Rect>; if it does not overlap, simply return false.
|
||||
//
|
||||
// Note that simply overlapping is not all that is required to be affected by this function.
|
||||
// Trenches can only affect certain edges of rectangles... for example, a "left"-edge guide
|
||||
// trench should only affect left edges of rectangles. We don't snap right edges to left-edged
|
||||
// guide trenches. For border trenches, the logic is a bit different, so left snaps to right and
|
||||
// top snaps to bottom.
|
||||
//
|
||||
// Parameters:
|
||||
// rect - (<Rect>) the rectangle in question
|
||||
// stationaryCorner - which corner is stationary? by default, the top left.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
|
||||
// keepProportional - (boolean) if we are allowed to change the rect's size, whether the
|
||||
// dimensions should scaled proportionally or not.
|
||||
//
|
||||
// Returns:
|
||||
// false - if rect does not overlap with this trench
|
||||
// newRect - (<Rect>) an adjusted version of rect, if it is affected by this trench
|
||||
rectOverlaps: function Trench_rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional) {
|
||||
var edgeToCheck;
|
||||
if (this.type == "border") {
|
||||
if (this.edge == "left")
|
||||
edgeToCheck = "right";
|
||||
else if (this.edge == "right")
|
||||
edgeToCheck = "left";
|
||||
else if (this.edge == "top")
|
||||
edgeToCheck = "bottom";
|
||||
else if (this.edge == "bottom")
|
||||
edgeToCheck = "top";
|
||||
} else { // if trench type is guide or barrier...
|
||||
edgeToCheck = this.edge;
|
||||
}
|
||||
|
||||
rect.adjustedEdge = edgeToCheck;
|
||||
|
||||
switch (edgeToCheck) {
|
||||
case "left":
|
||||
if (this.ruleOverlaps(rect.left, rect.yRange)) {
|
||||
if (stationaryCorner.indexOf('right') > -1)
|
||||
rect.width = rect.right - this.position;
|
||||
rect.left = this.position;
|
||||
return rect;
|
||||
}
|
||||
break;
|
||||
case "right":
|
||||
if (this.ruleOverlaps(rect.right, rect.yRange)) {
|
||||
if (assumeConstantSize) {
|
||||
rect.left = this.position - rect.width;
|
||||
} else {
|
||||
var newWidth = this.position - rect.left;
|
||||
if (keepProportional)
|
||||
rect.height = rect.height * newWidth / rect.width;
|
||||
rect.width = newWidth;
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
break;
|
||||
case "top":
|
||||
if (this.ruleOverlaps(rect.top, rect.xRange)) {
|
||||
if (stationaryCorner.indexOf('bottom') > -1)
|
||||
rect.height = rect.bottom - this.position;
|
||||
rect.top = this.position;
|
||||
return rect;
|
||||
}
|
||||
break;
|
||||
case "bottom":
|
||||
if (this.ruleOverlaps(rect.bottom, rect.xRange)) {
|
||||
if (assumeConstantSize) {
|
||||
rect.top = this.position - rect.height;
|
||||
} else {
|
||||
var newHeight = this.position - rect.top;
|
||||
if (keepProportional)
|
||||
rect.width = rect.width * newHeight / rect.height;
|
||||
rect.height = newHeight;
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: ruleOverlaps
|
||||
// Computes whether the given "rule" (a line segment, essentially), given by the position and
|
||||
// range arguments, overlaps with the current trench. Note that this function assumes that
|
||||
// the rule and the trench are in the same direction: both horizontal, or both vertical.
|
||||
//
|
||||
// Parameters:
|
||||
// position - (integer) a position in px
|
||||
// range - (<Range>) the rule's range
|
||||
ruleOverlaps: function Trench_ruleOverlaps(position, range) {
|
||||
return (this.position - this.radius < position &&
|
||||
position < this.position + this.radius &&
|
||||
this.activeRange.contains(range));
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: adjustRangeIfIntercept
|
||||
// Computes whether the given boundary (given as a position and its active range), perpendicular
|
||||
// to the trench, intercepts the trench or not. If it does, it returns an adjusted <Range> for
|
||||
// the trench. If not, it returns false.
|
||||
//
|
||||
// Parameters:
|
||||
// position - (integer) the position of the boundary
|
||||
// range - (<Range>) the target's range, on the trench's transverse axis
|
||||
adjustRangeIfIntercept: function Trench_adjustRangeIfIntercept(position, range) {
|
||||
if (this.position - this.radius > range.min && this.position + this.radius < range.max) {
|
||||
var activeRange = new Range(this.activeRange);
|
||||
|
||||
// there are three ways this can go:
|
||||
// 1. position < minRange.min
|
||||
// 2. position > minRange.max
|
||||
// 3. position >= minRange.min && position <= minRange.max
|
||||
|
||||
if (position < this.minRange.min) {
|
||||
activeRange.min = Math.min(this.minRange.min,position);
|
||||
} else if (position > this.minRange.max) {
|
||||
activeRange.max = Math.max(this.minRange.max,position);
|
||||
} else {
|
||||
// this should be impossible because items can't overlap and we've already checked
|
||||
// that the range intercepts.
|
||||
}
|
||||
return activeRange;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
//----------
|
||||
// Function: calculateActiveRange
|
||||
// Computes and sets the <activeRange> for the trench, based on the <GroupItems> around.
|
||||
// This makes it so trenches' active ranges don't extend through other groupItems.
|
||||
calculateActiveRange: function Trench_calculateActiveRange() {
|
||||
|
||||
// set it to the default: just the range itself.
|
||||
this.setActiveRange(this.range);
|
||||
|
||||
// only guide-type trenches need to set a separate active range
|
||||
if (this.type != 'guide')
|
||||
return;
|
||||
|
||||
var groupItems = GroupItems.groupItems;
|
||||
var trench = this;
|
||||
groupItems.forEach(function(groupItem) {
|
||||
if (groupItem.isDragging) // floating groupItems don't block trenches
|
||||
return;
|
||||
if (trench.el == groupItem.container) // groupItems don't block their own trenches
|
||||
return;
|
||||
var bounds = groupItem.getBounds();
|
||||
var activeRange = new Range();
|
||||
if (trench.xory == 'y') { // if this trench is horizontal...
|
||||
activeRange = trench.adjustRangeIfIntercept(bounds.left, bounds.yRange);
|
||||
if (activeRange)
|
||||
trench.setActiveRange(activeRange);
|
||||
activeRange = trench.adjustRangeIfIntercept(bounds.right, bounds.yRange);
|
||||
if (activeRange)
|
||||
trench.setActiveRange(activeRange);
|
||||
} else { // if this trench is vertical...
|
||||
activeRange = trench.adjustRangeIfIntercept(bounds.top, bounds.xRange);
|
||||
if (activeRange)
|
||||
trench.setActiveRange(activeRange);
|
||||
activeRange = trench.adjustRangeIfIntercept(bounds.bottom, bounds.xRange);
|
||||
if (activeRange)
|
||||
trench.setActiveRange(activeRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Trenches
|
||||
// Singelton for managing all <Trench>es.
|
||||
var Trenches = {
|
||||
// ---------
|
||||
// Variables:
|
||||
// nextId - (integer) a counter for the next <Trench>'s <Trench.id> value.
|
||||
// showDebug - (boolean) whether to draw the <Trench>es or not.
|
||||
// defaultRadius - (integer) the default radius for new <Trench>es.
|
||||
nextId: 0,
|
||||
showDebug: false,
|
||||
defaultRadius: 10,
|
||||
|
||||
// ---------
|
||||
// Variables: snapping preferences; used to break ties in snapping.
|
||||
// preferTop - (boolean) prefer snapping to the top to the bottom
|
||||
// preferLeft - (boolean) prefer snapping to the left to the right
|
||||
preferTop: true,
|
||||
preferLeft: true,
|
||||
|
||||
trenches: [],
|
||||
|
||||
// ---------
|
||||
// Function: getById
|
||||
// Return the specified <Trench>.
|
||||
//
|
||||
// Parameters:
|
||||
// id - (integer)
|
||||
getById: function Trenches_getById(id) {
|
||||
return this.trenches[id];
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: register
|
||||
// Register a new <Trench> and returns the resulting <Trench> ID.
|
||||
//
|
||||
// Parameters:
|
||||
// See the constructor <Trench.Trench>'s parameters.
|
||||
//
|
||||
// Returns:
|
||||
// id - (int) the new <Trench>'s ID.
|
||||
register: function Trenches_register(element, xory, type, edge) {
|
||||
var trench = new Trench(element, xory, type, edge);
|
||||
this.trenches[trench.id] = trench;
|
||||
return trench.id;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: registerWithItem
|
||||
// Register a whole set of <Trench>es using an <Item> and returns the resulting <Trench> IDs.
|
||||
//
|
||||
// Parameters:
|
||||
// item - the <Item> to project trenches
|
||||
// type - either "border" or "guide"
|
||||
//
|
||||
// Returns:
|
||||
// ids - array of the new <Trench>es' IDs.
|
||||
registerWithItem: function Trenches_registerWithItem(item, type) {
|
||||
var container = item.container;
|
||||
var ids = {};
|
||||
ids.left = Trenches.register(container,"x",type,"left");
|
||||
ids.right = Trenches.register(container,"x",type,"right");
|
||||
ids.top = Trenches.register(container,"y",type,"top");
|
||||
ids.bottom = Trenches.register(container,"y",type,"bottom");
|
||||
|
||||
this.getById(ids.left).setParentItem(item);
|
||||
this.getById(ids.right).setParentItem(item);
|
||||
this.getById(ids.top).setParentItem(item);
|
||||
this.getById(ids.bottom).setParentItem(item);
|
||||
|
||||
return ids;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: unregister
|
||||
// Unregister one or more <Trench>es.
|
||||
//
|
||||
// Parameters:
|
||||
// ids - (integer) a single <Trench> ID or (array) a list of <Trench> IDs.
|
||||
unregister: function Trenches_unregister(ids) {
|
||||
if (!Array.isArray(ids))
|
||||
ids = [ids];
|
||||
var self = this;
|
||||
ids.forEach(function(id) {
|
||||
self.trenches[id].hide();
|
||||
delete self.trenches[id];
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: activateOthersTrenches
|
||||
// Activate all <Trench>es other than those projected by the current element.
|
||||
//
|
||||
// Parameters:
|
||||
// element - (DOMElement) the DOM element of the Item being dragged or resized.
|
||||
activateOthersTrenches: function Trenches_activateOthersTrenches(element) {
|
||||
this.trenches.forEach(function(t) {
|
||||
if (t.el === element)
|
||||
return;
|
||||
if (t.parentItem && (t.parentItem.isAFauxItem ||
|
||||
t.parentItem.isDragging ||
|
||||
t.parentItem.isDropTarget))
|
||||
return;
|
||||
t.active = true;
|
||||
t.calculateActiveRange();
|
||||
t.show(); // debug
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: disactivate
|
||||
// After <activateOthersTrenches>, disactivates all the <Trench>es again.
|
||||
disactivate: function Trenches_disactivate() {
|
||||
this.trenches.forEach(function(t) {
|
||||
t.active = false;
|
||||
t.showGuide = false;
|
||||
t.show();
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: hideGuides
|
||||
// Hide all guides (dotted lines) en masse.
|
||||
hideGuides: function Trenches_hideGuides() {
|
||||
this.trenches.forEach(function(t) {
|
||||
t.showGuide = false;
|
||||
t.show();
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: snap
|
||||
// Used to "snap" an object's bounds to active trenches and to the edge of the window.
|
||||
// If the meta key is down (<Key.meta>), it will not snap but will still enforce the rect
|
||||
// not leaving the safe bounds of the window.
|
||||
//
|
||||
// Parameters:
|
||||
// rect - (<Rect>) the object's current bounds
|
||||
// stationaryCorner - which corner is stationary? by default, the top left.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
|
||||
// keepProportional - (boolean) if we are allowed to change the rect's size, whether the
|
||||
// dimensions should scaled proportionally or not.
|
||||
//
|
||||
// Returns:
|
||||
// (<Rect>) - the updated bounds, if they were updated
|
||||
// false - if the bounds were not updated
|
||||
snap: function Trenches_snap(rect,stationaryCorner,assumeConstantSize,keepProportional) {
|
||||
// hide all the guide trenches, because the correct ones will be turned on later.
|
||||
Trenches.hideGuides();
|
||||
|
||||
var updated = false;
|
||||
var updatedX = false;
|
||||
var updatedY = false;
|
||||
|
||||
var snappedTrenches = {};
|
||||
|
||||
for (var i in this.trenches) {
|
||||
var t = this.trenches[i];
|
||||
if (!t.active || t.parentItem.isDropTarget)
|
||||
continue;
|
||||
// newRect will be a new rect, or false
|
||||
var newRect = t.rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional);
|
||||
|
||||
if (newRect) { // if rectOverlaps returned an updated rect...
|
||||
|
||||
if (assumeConstantSize && updatedX && updatedY)
|
||||
break;
|
||||
if (assumeConstantSize && updatedX && (newRect.adjustedEdge == "left"||newRect.adjustedEdge == "right"))
|
||||
continue;
|
||||
if (assumeConstantSize && updatedY && (newRect.adjustedEdge == "top"||newRect.adjustedEdge == "bottom"))
|
||||
continue;
|
||||
|
||||
rect = newRect;
|
||||
updated = true;
|
||||
|
||||
// register this trench as the "snapped trench" for the appropriate edge.
|
||||
snappedTrenches[newRect.adjustedEdge] = t;
|
||||
|
||||
// if updatedX, we don't need to update x any more.
|
||||
if (newRect.adjustedEdge == "left" && this.preferLeft)
|
||||
updatedX = true;
|
||||
if (newRect.adjustedEdge == "right" && !this.preferLeft)
|
||||
updatedX = true;
|
||||
|
||||
// if updatedY, we don't need to update x any more.
|
||||
if (newRect.adjustedEdge == "top" && this.preferTop)
|
||||
updatedY = true;
|
||||
if (newRect.adjustedEdge == "bottom" && !this.preferTop)
|
||||
updatedY = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
rect.snappedTrenches = snappedTrenches;
|
||||
return rect;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: show
|
||||
// <Trench.show> all <Trench>es.
|
||||
show: function Trenches_show() {
|
||||
this.trenches.forEach(function(t) {
|
||||
t.show();
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: toggleShown
|
||||
// Toggle <Trenches.showDebug> and trigger <Trenches.show>
|
||||
toggleShown: function Trenches_toggleShown() {
|
||||
this.showDebug = !this.showDebug;
|
||||
this.show();
|
||||
}
|
||||
};
|
|
@ -40,6 +40,8 @@ srcdir = @srcdir@
|
|||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/base/content/test
|
||||
|
||||
DIRS += tabview
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
@ -167,6 +169,10 @@ _BROWSER_FILES = \
|
|||
browser_selectTabAtIndex.js \
|
||||
browser_tabfocus.js \
|
||||
browser_tabs_owner.js \
|
||||
browser_visibleTabs.js \
|
||||
browser_visibleTabs_contextMenu.js \
|
||||
browser_visibleTabs_bookmarkAllPages.js \
|
||||
browser_visibleTabs_tabPreview.js \
|
||||
discovery.html \
|
||||
moz.png \
|
||||
test_bug435035.html \
|
||||
|
|
|
@ -5,6 +5,11 @@ function test() {
|
|||
gBrowser.removeTab(tab);
|
||||
is(tab.parentNode, null, "tab removed immediately");
|
||||
|
||||
tab = gBrowser.addTab("about:blank", { skipAnimation: true });
|
||||
gBrowser.removeTab(tab, { animate: true });
|
||||
gBrowser.removeTab(tab);
|
||||
is(tab.parentNode, null, "tab removed immediately when calling removeTab again after the animation was kicked off");
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref("browser.tabs.animate", true);
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabbrowser visibleTabs test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Edward Lee <edilee@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
|
||||
// Add a tab that will get pinned
|
||||
let pinned = gBrowser.addTab();
|
||||
gBrowser.pinTab(pinned);
|
||||
|
||||
let testTab = gBrowser.addTab();
|
||||
|
||||
let visible = gBrowser.visibleTabs;
|
||||
is(visible.length, 3, "3 tabs should be open");
|
||||
is(visible[0], pinned, "the pinned tab is first");
|
||||
is(visible[1], origTab, "original tab is next");
|
||||
is(visible[2], testTab, "last created tab is last");
|
||||
|
||||
// Only show the test tab (but also get pinned and selected)
|
||||
is(gBrowser.selectedTab, origTab, "sanity check that we're on the original tab");
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
is(gBrowser.visibleTabs.length, 3, "all 3 tabs are still visible");
|
||||
|
||||
// Select the test tab and only show that (and pinned)
|
||||
gBrowser.selectedTab = testTab;
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
|
||||
visible = gBrowser.visibleTabs;
|
||||
is(visible.length, 2, "2 tabs should be visible including the pinned");
|
||||
is(visible[0], pinned, "first is pinned");
|
||||
is(visible[1], testTab, "next is the test tab");
|
||||
is(gBrowser.tabs.length, 3, "3 tabs should still be open");
|
||||
|
||||
gBrowser.selectTabAtIndex(0);
|
||||
is(gBrowser.selectedTab, pinned, "first tab is pinned");
|
||||
gBrowser.selectTabAtIndex(1);
|
||||
is(gBrowser.selectedTab, testTab, "second tab is the test tab");
|
||||
gBrowser.selectTabAtIndex(2);
|
||||
is(gBrowser.selectedTab, testTab, "no third tab, so no change");
|
||||
gBrowser.selectTabAtIndex(0);
|
||||
is(gBrowser.selectedTab, pinned, "switch back to the pinned");
|
||||
gBrowser.selectTabAtIndex(2);
|
||||
is(gBrowser.selectedTab, pinned, "no third tab, so no change");
|
||||
gBrowser.selectTabAtIndex(-1);
|
||||
is(gBrowser.selectedTab, testTab, "last tab is the test tab");
|
||||
|
||||
gBrowser.tabContainer.advanceSelectedTab(1, true);
|
||||
is(gBrowser.selectedTab, pinned, "wrapped around the end to pinned");
|
||||
gBrowser.tabContainer.advanceSelectedTab(1, true);
|
||||
is(gBrowser.selectedTab, testTab, "next to test tab");
|
||||
gBrowser.tabContainer.advanceSelectedTab(1, true);
|
||||
is(gBrowser.selectedTab, pinned, "next to pinned again");
|
||||
|
||||
gBrowser.tabContainer.advanceSelectedTab(-1, true);
|
||||
is(gBrowser.selectedTab, testTab, "going backwards to last tab");
|
||||
gBrowser.tabContainer.advanceSelectedTab(-1, true);
|
||||
is(gBrowser.selectedTab, pinned, "next to pinned");
|
||||
gBrowser.tabContainer.advanceSelectedTab(-1, true);
|
||||
is(gBrowser.selectedTab, testTab, "next to test tab again");
|
||||
|
||||
// Try showing all tabs
|
||||
gBrowser.showOnlyTheseTabs(Array.slice(gBrowser.tabs));
|
||||
is(gBrowser.visibleTabs.length, 3, "all 3 tabs are visible again");
|
||||
|
||||
// Select the pinned tab and show the testTab to make sure selection updates
|
||||
gBrowser.selectedTab = pinned;
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
is(gBrowser.tabs[1], origTab, "make sure origTab is in the middle");
|
||||
is(origTab.hidden, true, "make sure it's hidden");
|
||||
gBrowser.removeTab(pinned);
|
||||
is(gBrowser.selectedTab, testTab, "making sure origTab was skipped");
|
||||
is(gBrowser.visibleTabs.length, 1, "only testTab is there");
|
||||
|
||||
// Only show one of the non-pinned tabs (but testTab is selected)
|
||||
gBrowser.showOnlyTheseTabs([origTab]);
|
||||
is(gBrowser.visibleTabs.length, 2, "got 2 tabs");
|
||||
|
||||
// Now really only show one of the tabs
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
visible = gBrowser.visibleTabs;
|
||||
is(visible.length, 1, "only the original tab is visible");
|
||||
is(visible[0], testTab, "it's the original tab");
|
||||
is(gBrowser.tabs.length, 2, "still have 2 open tabs");
|
||||
|
||||
// Close the last visible tab and make sure we still get a visible tab
|
||||
gBrowser.removeTab(testTab);
|
||||
is(gBrowser.visibleTabs.length, 1, "only orig is left and visible");
|
||||
is(gBrowser.tabs.length, 1, "sanity check that it matches");
|
||||
is(gBrowser.selectedTab, origTab, "got the orig tab");
|
||||
is(origTab.hidden, false, "and it's not hidden -- visible!");
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is bookmark all pages test with tab view.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tabOne = gBrowser.addTab("about:blank");
|
||||
let tabTwo = gBrowser.addTab("http://mochi.test:8888/");
|
||||
gBrowser.selectedTab = tabTwo;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tabTwo);
|
||||
let onLoad = function() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
|
||||
gBrowser.showOnlyTheseTabs([tabTwo]);
|
||||
|
||||
is(gBrowser.visibleTabs.length, 1, "Only one tab is visible");
|
||||
|
||||
let uris = PlacesCommandHook._getUniqueTabInfo();
|
||||
is(uris.length, 1, "Only one uri is returned");
|
||||
|
||||
is(uris[0].spec, tabTwo.linkedBrowser.currentURI.spec, "It's the correct URI");
|
||||
|
||||
gBrowser.removeTab(tabOne);
|
||||
gBrowser.removeTab(tabTwo);
|
||||
Array.forEach(gBrowser.tabs, function(tab) {
|
||||
tab.hidden = false;
|
||||
});
|
||||
|
||||
finish();
|
||||
}
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabbrowser visibleTabs Bookmark All Tabs test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
is(gBrowser.visibleTabs.length, 1, "1 tab should be open");
|
||||
is(Disabled(), true, "Bookmark All Tabs should be hidden");
|
||||
|
||||
// Add a tab
|
||||
let testTab = gBrowser.addTab();
|
||||
is(gBrowser.visibleTabs.length, 2, "2 tabs should be open");
|
||||
is(Disabled(), false, "Bookmark All Tabs should be available");
|
||||
|
||||
// Hide the original tab
|
||||
gBrowser.selectedTab = testTab;
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
is(gBrowser.visibleTabs.length, 1, "1 tab should be visible");
|
||||
is(Disabled(), true, "Bookmark All Tabs should be hidden as there is only one visible tab");
|
||||
|
||||
// Add a tab that will get pinned
|
||||
let pinned = gBrowser.addTab();
|
||||
gBrowser.pinTab(pinned);
|
||||
is(gBrowser.visibleTabs.length, 2, "2 tabs should be visible now");
|
||||
is(Disabled(), false, "Bookmark All Tabs should be available as there are two visible tabs");
|
||||
|
||||
// Show all tabs
|
||||
let allTabs = [tab for each (tab in gBrowser.tabs)];
|
||||
gBrowser.showOnlyTheseTabs(allTabs);
|
||||
|
||||
// reset the environment
|
||||
gBrowser.removeTab(testTab);
|
||||
gBrowser.removeTab(pinned);
|
||||
is(gBrowser.visibleTabs.length, 1, "only orig is left and visible");
|
||||
is(gBrowser.tabs.length, 1, "sanity check that it matches");
|
||||
is(Disabled(), true, "Bookmark All Tabs should be hidden");
|
||||
is(gBrowser.selectedTab, origTab, "got the orig tab");
|
||||
is(origTab.hidden, false, "and it's not hidden -- visible!");
|
||||
}
|
||||
|
||||
function Disabled() {
|
||||
let command = document.getElementById("Browser:BookmarkAllTabs");
|
||||
return command.hasAttribute("disabled") && command.getAttribute("disabled") === "true";
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabbrowser visibleTabs context menu test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
is(gBrowser.visibleTabs.length, 1, "there is one visible tab");
|
||||
let testTab = gBrowser.addTab();
|
||||
is(gBrowser.visibleTabs.length, 2, "there are now two visible tabs");
|
||||
|
||||
// Check the context menu with two tabs
|
||||
popup(origTab);
|
||||
is(TabContextMenu.contextTab, origTab, "TabContextMenu context is the original tab");
|
||||
is(document.getElementById("context_closeTab").disabled, false, "Close Tab is enabled");
|
||||
is(document.getElementById("context_reloadAllTabs").disabled, false, "Reload All Tabs is enabled");
|
||||
|
||||
// Hide the original tab.
|
||||
gBrowser.selectedTab = testTab;
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
is(gBrowser.visibleTabs.length, 1, "now there is only one visible tab");
|
||||
|
||||
// Check the context menu with one tab.
|
||||
popup(testTab);
|
||||
is(TabContextMenu.contextTab, testTab, "TabContextMenu context is the test tab");
|
||||
is(document.getElementById("context_closeTab").disabled, true, "Close Tab is disabled");
|
||||
is(document.getElementById("context_reloadAllTabs").disabled, true, "Reload All Tabs is disabled");
|
||||
|
||||
// Add a tab that will get pinned
|
||||
// So now there's one pinned tab, one visible unpinned tab, and one hidden tab
|
||||
let pinned = gBrowser.addTab();
|
||||
gBrowser.pinTab(pinned);
|
||||
is(gBrowser.visibleTabs.length, 2, "now there are two visible tabs");
|
||||
|
||||
// Check the context menu on the unpinned visible tab
|
||||
popup(testTab);
|
||||
is(TabContextMenu.contextTab, testTab, "TabContextMenu context is again the test tab");
|
||||
is(document.getElementById("context_closeOtherTabs").disabled, true, "Close Other Tabs is disabled");
|
||||
|
||||
// Show all tabs
|
||||
let allTabs = [tab for each (tab in gBrowser.tabs)];
|
||||
gBrowser.showOnlyTheseTabs(allTabs);
|
||||
|
||||
// Check the context menu now
|
||||
popup(testTab);
|
||||
is(TabContextMenu.contextTab, testTab, "TabContextMenu context is yet again the test tab");
|
||||
is(document.getElementById("context_closeOtherTabs").disabled, false, "Close Other Tabs is enabled");
|
||||
|
||||
gBrowser.removeTab(testTab);
|
||||
gBrowser.removeTab(pinned);
|
||||
}
|
||||
|
||||
function popup(tab) {
|
||||
document.popupNode = tab;
|
||||
TabContextMenu.updateContextMenu(document.getElementById("tabContextMenu"));
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tab preview with tab view.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
gPrefService.setBoolPref("browser.ctrlTab.previews", true);
|
||||
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
let tabOne = gBrowser.addTab();
|
||||
let tabTwo = gBrowser.addTab();
|
||||
|
||||
// test the ctrlTab.tabList
|
||||
pressCtrlTab();
|
||||
ok(ctrlTab.tabList.length, 3, "Show 3 tabs in tab preview");
|
||||
releaseCtrl();
|
||||
|
||||
gBrowser.showOnlyTheseTabs([origTab]);
|
||||
pressCtrlTab();
|
||||
ok(ctrlTab.tabList.length, 1, "Show 1 tab in tab preview");
|
||||
ok(!ctrlTab.isOpen, "With 1 tab open, Ctrl+Tab doesn't open the preview panel");
|
||||
|
||||
gBrowser.showOnlyTheseTabs([origTab, tabOne, tabTwo]);
|
||||
pressCtrlTab();
|
||||
ok(ctrlTab.isOpen, "With 3 tabs open, Ctrl+Tab does open the preview panel");
|
||||
releaseCtrl();
|
||||
|
||||
// cleanup
|
||||
gBrowser.removeTab(tabOne);
|
||||
gBrowser.removeTab(tabTwo);
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.ctrlTab.previews"))
|
||||
gPrefService.clearUserPref("browser.ctrlTab.previews");
|
||||
}
|
||||
|
||||
function pressCtrlTab(aShiftKey) {
|
||||
EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true, shiftKey: !!aShiftKey });
|
||||
}
|
||||
|
||||
function releaseCtrl() {
|
||||
EventUtils.synthesizeKey("VK_CONTROL", { type: "keyup" });
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/base/content/test/tabview
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
$(warning All TabView tests are disabled for now while we iterate on the UI)
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_tabview_launch.js \
|
||||
$(NULL)
|
||||
# browser_tabview_dragdrop.js \
|
||||
# browser_tabview_group.js \
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
|
@ -0,0 +1,159 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabview drag and drop test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
setTimeout(function() { TabView.toggle(); }, 0);
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
// create group one and two
|
||||
let padding = 10;
|
||||
let pageBounds = contentWindow.Items.getPageBounds();
|
||||
pageBounds.inset(padding, padding);
|
||||
|
||||
let box = new contentWindow.Rect(pageBounds);
|
||||
box.width = 300;
|
||||
box.height = 300;
|
||||
|
||||
let groupOne = new contentWindow.GroupItem([], { bounds: box });
|
||||
ok(groupOne.isEmpty(), "This group is empty");
|
||||
|
||||
let groupTwo = new contentWindow.GroupItem([], { bounds: box });
|
||||
|
||||
groupOne.addSubscriber(groupOne, "tabAdded", function() {
|
||||
groupOne.removeSubscriber(groupOne, "tabAdded");
|
||||
groupTwo.newTab("");
|
||||
});
|
||||
groupTwo.addSubscriber(groupTwo, "tabAdded", function() {
|
||||
groupTwo.removeSubscriber(groupTwo, "tabAdded");
|
||||
// carry on testing
|
||||
addTest(contentWindow, groupOne.id, groupTwo.id);
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
let onTabViewHidden = function() {
|
||||
// show the tab view.
|
||||
TabView.toggle();
|
||||
if (++count == 2) {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
}
|
||||
};
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
// open tab in group
|
||||
groupOne.newTab("");
|
||||
}
|
||||
|
||||
function addTest(contentWindow, groupOneId, groupTwoId) {
|
||||
let groupOne = contentWindow.GroupItems.groupItem(groupOneId);
|
||||
let groupTwo = contentWindow.GroupItems.groupItem(groupTwoId);
|
||||
let groupOneTabItemCount = groupOne.getChildren().length;
|
||||
let groupTwoTabItemCount = groupTwo.getChildren().length;
|
||||
is(groupOneTabItemCount, 1, "GroupItem one has a tab");
|
||||
is(groupTwoTabItemCount, 1, "GroupItem two has two tabs");
|
||||
|
||||
let srcElement = groupOne.getChild(0).container;
|
||||
ok(srcElement, "The source element exists");
|
||||
|
||||
// calculate the offsets
|
||||
let groupTwoRect = groupTwo.container.getBoundingClientRect();
|
||||
let srcElementRect = srcElement.getBoundingClientRect();
|
||||
let offsetX =
|
||||
Math.round(groupTwoRect.left + groupTwoRect.width/5) - srcElementRect.left;
|
||||
let offsetY =
|
||||
Math.round(groupTwoRect.top + groupTwoRect.height/5) - srcElementRect.top;
|
||||
|
||||
simulateDragDrop(srcElement, offsetX, offsetY, contentWindow);
|
||||
|
||||
is(groupOne.getChildren().length, --groupOneTabItemCount,
|
||||
"The number of children in group one is decreased by 1");
|
||||
is(groupTwo.getChildren().length, ++groupTwoTabItemCount,
|
||||
"The number of children in group two is increased by 1");
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
groupTwo.addSubscriber(groupTwo, "close", function() {
|
||||
groupTwo.removeSubscriber(groupTwo, "close");
|
||||
contentWindow.UI.hideTabView();
|
||||
});
|
||||
groupTwo.closeAll();
|
||||
}
|
||||
|
||||
function simulateDragDrop(srcElement, offsetX, offsetY, contentWindow) {
|
||||
// enter drag mode
|
||||
let dataTransfer;
|
||||
|
||||
EventUtils.synthesizeMouse(
|
||||
srcElement, 1, 1, { type: "mousedown" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"dragenter", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 1, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
|
||||
// drag over
|
||||
for (let i = 4; i >= 0; i--)
|
||||
EventUtils.synthesizeMouse(
|
||||
srcElement, Math.round(offsetX/5), Math.round(offsetY/4),
|
||||
{ type: "mousemove" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
|
||||
// drop
|
||||
EventUtils.synthesizeMouse(srcElement, 0, 0, { type: "mouseup" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"drop", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabview group test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
testEmptyGroupItem(contentWindow);
|
||||
}
|
||||
|
||||
function testEmptyGroupItem(contentWindow) {
|
||||
let groupItemCount = contentWindow.GroupItems.groupItems.length;
|
||||
|
||||
// create empty group item
|
||||
let emptyGroupItem = createEmptyGroupItem(contentWindow, 100);
|
||||
ok(emptyGroupItem.isEmpty(), "This group is empty");
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, ++groupItemCount,
|
||||
"The number of groups is increased by 1");
|
||||
|
||||
emptyGroupItem.addSubscriber(emptyGroupItem, "close", function() {
|
||||
emptyGroupItem.removeSubscriber(emptyGroupItem, "close");
|
||||
|
||||
// check the number of groups.
|
||||
is(contentWindow.GroupItems.groupItems.length, --groupItemCount,
|
||||
"The number of groups is decreased by 1");
|
||||
|
||||
testGroupItemWithTabItem(contentWindow);
|
||||
});
|
||||
|
||||
let closeButton = emptyGroupItem.container.getElementsByClassName("close");
|
||||
ok(closeButton[0], "Group close button exists");
|
||||
|
||||
// click the close button
|
||||
EventUtils.synthesizeMouse(closeButton[0], 1, 1, {}, contentWindow);
|
||||
}
|
||||
|
||||
function testGroupItemWithTabItem(contentWindow) {
|
||||
let groupItem = createEmptyGroupItem(contentWindow, 200);
|
||||
let tabItemCount = 0;
|
||||
|
||||
groupItem.addSubscriber(groupItem, "tabAdded", function() {
|
||||
groupItem.removeSubscriber(groupItem, "tabAdded");
|
||||
TabView.toggle();
|
||||
});
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
is(groupItem.getChildren().length, ++tabItemCount,
|
||||
"The number of children in new tab group is increased by 1");
|
||||
|
||||
let tabItem = groupItem.getChild(groupItem.getChildren().length - 1);
|
||||
ok(tabItem, "Tab item exists");
|
||||
|
||||
let tabRemoved = false;
|
||||
tabItem.addSubscriber(tabItem, "close", function() {
|
||||
tabItem.removeSubscriber(tabItem, "close");
|
||||
|
||||
ok(tabRemoved, "Tab is removed");
|
||||
// tabItem would get destroyed after the close event is sent so we have a 0 delay here.
|
||||
is(groupItem.getChildren().length, --tabItemCount,
|
||||
"The number of children in new tab group is decreased by 1");
|
||||
finish();
|
||||
});
|
||||
tabItem.addSubscriber(tabItem, "tabRemoved", function() {
|
||||
tabItem.removeSubscriber(tabItem, "tabRemoved");
|
||||
tabRemoved = true;
|
||||
});
|
||||
|
||||
// remove the tab item. The code detects mousedown and mouseup so we stimulate here
|
||||
let closeButton = tabItem.container.getElementsByClassName("close");
|
||||
ok(closeButton, "Tab item close button exists");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, closeButton[0], contentWindow);
|
||||
EventUtils.sendMouseEvent({ type: "mouseup" }, closeButton[0], contentWindow);
|
||||
};
|
||||
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
// click on the + button
|
||||
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
|
||||
ok(newTabButton[0], "New tab button exists");
|
||||
|
||||
EventUtils.synthesizeMouse(newTabButton[0], 1, 1, {}, contentWindow);
|
||||
}
|
||||
|
||||
function createEmptyGroupItem(contentWindow, padding) {
|
||||
let pageBounds = contentWindow.Items.getPageBounds();
|
||||
pageBounds.inset(padding, padding);
|
||||
|
||||
let box = new contentWindow.Rect(pageBounds);
|
||||
box.width = 300;
|
||||
box.height = 300;
|
||||
|
||||
let emptyGroupItem = new contentWindow.GroupItem([], { bounds: box });
|
||||
|
||||
return emptyGroupItem;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabview launch test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tabViewShownCount = 0;
|
||||
let onTabViewHidden = function() {
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
if (tabViewShownCount == 1) {
|
||||
document.getElementById("menu_tabview").doCommand();
|
||||
} else if (tabViewShownCount == 2) {
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (utils) {
|
||||
var keyCode = 0;
|
||||
var charCode;
|
||||
var eventObject;
|
||||
if (navigator.platform.indexOf("Mac") != -1) {
|
||||
charCode = 160;
|
||||
eventObject = { altKey: true };
|
||||
} else {
|
||||
charCode = 32;
|
||||
eventObject = { altKey: true, ctrlKey: true };
|
||||
}
|
||||
var modifiers = EventUtils._parseModifiers(eventObject);
|
||||
var keyDownDefaultHappened =
|
||||
utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
|
||||
utils.sendKeyEvent("keypress", keyCode, charCode, modifiers,
|
||||
!keyDownDefaultHappened);
|
||||
utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
|
||||
}
|
||||
} else if (tabViewShownCount == 3) {
|
||||
window.removeEventListener("tabviewshown", onTabViewShown, false);
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
let onTabViewShown = function() {
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
tabViewShownCount++
|
||||
TabView.toggle();
|
||||
}
|
||||
window.addEventListener("tabviewshown", onTabViewShown, false);
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
let button = document.getElementById("tabview-button");
|
||||
ok(button, "Tab View button exists");
|
||||
EventUtils.synthesizeMouse(button, 1, 1, {});
|
||||
}
|
|
@ -46,6 +46,9 @@ browser.jar:
|
|||
content/browser/sanitizeDialog.css (content/sanitizeDialog.css)
|
||||
* content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
* content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
content/browser/tabview.css (content/tabview/tabview.css)
|
||||
* content/browser/tabview.js (content/tabview/tabview.js)
|
||||
content/browser/tabview.html (content/tabview/tabview.html)
|
||||
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
* content/browser/utilityOverlay.js (content/utilityOverlay.js)
|
||||
* content/browser/web-panels.js (content/web-panels.js)
|
||||
|
|
|
@ -128,6 +128,31 @@ BrowserGlue.prototype = {
|
|||
Services.prefs.savePrefFile(null);
|
||||
},
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
_setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
|
||||
// Assume that a non-zero value for services.sync.autoconnectDelay should override
|
||||
if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
|
||||
let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay");
|
||||
|
||||
if (prefDelay > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// delays are in seconds
|
||||
const MAX_DELAY = 300;
|
||||
let delay = 3;
|
||||
let enum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (enum.hasMoreElements()) {
|
||||
delay += enum.getNext().gBrowser.tabs.length;
|
||||
}
|
||||
delay = delay <= MAX_DELAY ? delay : MAX_DELAY;
|
||||
|
||||
let syncTemp = {};
|
||||
Cu.import("resource://services-sync/service.js", syncTemp);
|
||||
syncTemp.Weave.Service.delayedAutoConnect(delay);
|
||||
},
|
||||
#endif
|
||||
|
||||
// nsIObserver implementation
|
||||
observe: function BG_observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
|
@ -165,6 +190,11 @@ BrowserGlue.prototype = {
|
|||
case "browser-lastwindow-close-granted":
|
||||
this._setPrefToSaveSession();
|
||||
break;
|
||||
#endif
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
case "weave:service:ready":
|
||||
this._setSyncAutoconnectDelay();
|
||||
break;
|
||||
#endif
|
||||
case "session-save":
|
||||
this._setPrefToSaveSession(true);
|
||||
|
@ -238,6 +268,9 @@ BrowserGlue.prototype = {
|
|||
#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
|
||||
os.addObserver(this, "browser-lastwindow-close-requested", false);
|
||||
os.addObserver(this, "browser-lastwindow-close-granted", false);
|
||||
#endif
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
os.addObserver(this, "weave:service:ready", false);
|
||||
#endif
|
||||
os.addObserver(this, "session-save", false);
|
||||
os.addObserver(this, "places-init-complete", false);
|
||||
|
@ -262,6 +295,9 @@ BrowserGlue.prototype = {
|
|||
#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
|
||||
os.removeObserver(this, "browser-lastwindow-close-requested");
|
||||
os.removeObserver(this, "browser-lastwindow-close-granted");
|
||||
#endif
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
os.removeObserver(this, "weave:service:ready", false);
|
||||
#endif
|
||||
os.removeObserver(this, "session-save");
|
||||
if (this._isIdleObserver)
|
||||
|
@ -385,29 +421,6 @@ BrowserGlue.prototype = {
|
|||
temp.WinTaskbarJumpList.startup();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
// Assume that a non-zero value for services.sync.autoconnectDelay should override
|
||||
if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
|
||||
let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay");
|
||||
|
||||
if (prefDelay > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// delays are in seconds
|
||||
const MAX_DELAY = 300;
|
||||
let delay = 3;
|
||||
let enum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (enum.hasMoreElements()) {
|
||||
delay += enum.getNext().gBrowser.tabs.length;
|
||||
}
|
||||
delay = delay <= MAX_DELAY ? delay : MAX_DELAY;
|
||||
|
||||
let syncTemp = {};
|
||||
Cu.import("resource://services-sync/service.js", syncTemp);
|
||||
syncTemp.Weave.Service.delayedAutoConnect(delay);
|
||||
#endif
|
||||
},
|
||||
|
||||
|
@ -1387,7 +1400,7 @@ GeolocationPrompt.prototype = {
|
|||
|
||||
chromeWin.PopupNotifications.show(browser, "geolocation", message, "geo-notification-icon",
|
||||
mainAction, secondaryActions);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var components = [BrowserGlue, GeolocationPrompt];
|
||||
|
|
|
@ -50,7 +50,7 @@ const RELOAD_ACTION_NOTHING = 0;
|
|||
const RELOAD_ACTION_INSERT = 1;
|
||||
// Removing items from the view, select the first item after the last selected
|
||||
const RELOAD_ACTION_REMOVE = 2;
|
||||
// Moving items within a view, don't treat the dropped items as additional
|
||||
// Moving items within a view, don't treat the dropped items as additional
|
||||
// rows.
|
||||
const RELOAD_ACTION_MOVE = 3;
|
||||
|
||||
|
@ -67,7 +67,7 @@ const REMOVE_PAGES_MAX_SINGLEREMOVES = 10;
|
|||
|
||||
/**
|
||||
* Represents an insertion point within a container where we can insert
|
||||
* items.
|
||||
* items.
|
||||
* @param aItemId
|
||||
* The identifier of the parent container
|
||||
* @param aIndex
|
||||
|
@ -139,7 +139,7 @@ PlacesController.prototype = {
|
|||
return true;
|
||||
}
|
||||
|
||||
// All other Places Commands are prefixed with "placesCmd_" ... this
|
||||
// All other Places Commands are prefixed with "placesCmd_" ... this
|
||||
// filters out other commands that we do _not_ support (see 329587).
|
||||
const CMD_PREFIX = "placesCmd_";
|
||||
return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX);
|
||||
|
@ -178,7 +178,7 @@ PlacesController.prototype = {
|
|||
if (this._view.selType != "single") {
|
||||
let rootNode = this._view.result.root;
|
||||
if (rootNode.containerOpen && rootNode.childCount > 0)
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case "placesCmd_open":
|
||||
|
@ -308,9 +308,9 @@ PlacesController.prototype = {
|
|||
|
||||
onEvent: function PC_onEvent(eventName) { },
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether or not the selection can be removed, either by the
|
||||
* Determine whether or not the selection can be removed, either by the
|
||||
* delete or cut operations based on whether or not any of its contents
|
||||
* are non-removable. We don't need to worry about recursion here since it
|
||||
* is a policy decision that a removable item not be placed inside a non-
|
||||
|
@ -373,14 +373,14 @@ PlacesController.prototype = {
|
|||
var root = this._view.result.root;
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
if (nodes[i] == root)
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Looks at the data on the clipboard to see if it is paste-able.
|
||||
* Looks at the data on the clipboard to see if it is paste-able.
|
||||
* Paste-able data is:
|
||||
* - in a format that the view can receive
|
||||
* @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
|
||||
|
@ -427,7 +427,7 @@ PlacesController.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* Gathers information about the selected nodes according to the following
|
||||
* rules:
|
||||
* "link" node is a URI
|
||||
|
@ -442,7 +442,7 @@ PlacesController.prototype = {
|
|||
*
|
||||
* @returns an array of objects corresponding the selected nodes. Each
|
||||
* object has each of the properties above set if its corresponding
|
||||
* node matches the rule. In addition, the annotations names for each
|
||||
* node matches the rule. In addition, the annotations names for each
|
||||
* node are set on its corresponding object as properties.
|
||||
* Notes:
|
||||
* 1) This can be slow, so don't call it anywhere performance critical!
|
||||
|
@ -456,7 +456,7 @@ PlacesController.prototype = {
|
|||
if (nodes.length == 0)
|
||||
nodes.push(root); // See the second note above
|
||||
|
||||
for (var i=0; i < nodes.length; i++) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var nodeData = {};
|
||||
var node = nodes[i];
|
||||
var nodeType = node.type;
|
||||
|
@ -514,16 +514,16 @@ PlacesController.prototype = {
|
|||
|
||||
// annotations
|
||||
if (uri) {
|
||||
var names = PlacesUtils.annotations.getPageAnnotationNames(uri);
|
||||
for (var j = 0; j < names.length; ++j)
|
||||
let names = PlacesUtils.annotations.getPageAnnotationNames(uri);
|
||||
for (let j = 0; j < names.length; ++j)
|
||||
nodeData[names[j]] = true;
|
||||
}
|
||||
|
||||
// For items also include the item-specific annotations
|
||||
if (node.itemId != -1) {
|
||||
names = PlacesUtils.annotations
|
||||
.getItemAnnotationNames(node.itemId);
|
||||
for (j = 0; j < names.length; ++j)
|
||||
let names = PlacesUtils.annotations
|
||||
.getItemAnnotationNames(node.itemId);
|
||||
for (let j = 0; j < names.length; ++j)
|
||||
nodeData[names[j]] = true;
|
||||
}
|
||||
metadata.push(nodeData);
|
||||
|
@ -532,14 +532,14 @@ PlacesController.prototype = {
|
|||
return metadata;
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* Determines if a context-menu item should be shown
|
||||
* @param aMenuItem
|
||||
* the context menu item
|
||||
* the context menu item
|
||||
* @param aMetaData
|
||||
* meta data about the selection
|
||||
* @returns true if the conditions (see buildContextMenu) are satisfied
|
||||
* and the item can be displayed, false otherwise.
|
||||
* and the item can be displayed, false otherwise.
|
||||
*/
|
||||
_shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
|
||||
var selectiontype = aMenuItem.getAttribute("selectiontype");
|
||||
|
@ -551,8 +551,8 @@ PlacesController.prototype = {
|
|||
var forceHideAttr = aMenuItem.getAttribute("forcehideselection");
|
||||
if (forceHideAttr) {
|
||||
var forceHideRules = forceHideAttr.split("|");
|
||||
for (var i = 0; i < aMetaData.length; ++i) {
|
||||
for (var j=0; j < forceHideRules.length; ++j) {
|
||||
for (let i = 0; i < aMetaData.length; ++i) {
|
||||
for (let j = 0; j < forceHideRules.length; ++j) {
|
||||
if (forceHideRules[j] in aMetaData[i])
|
||||
return false;
|
||||
}
|
||||
|
@ -682,7 +682,7 @@ PlacesController.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Select all links in the current view.
|
||||
* Select all links in the current view.
|
||||
*/
|
||||
selectAll: function PC_selectAll() {
|
||||
this._view.selectAll();
|
||||
|
@ -691,7 +691,7 @@ PlacesController.prototype = {
|
|||
/**
|
||||
* Opens the bookmark properties for the selected URI Node.
|
||||
*/
|
||||
showBookmarkPropertiesForSelection:
|
||||
showBookmarkPropertiesForSelection:
|
||||
function PC_showBookmarkPropertiesForSelection() {
|
||||
var node = this._view.selectedNode;
|
||||
if (!node)
|
||||
|
@ -717,7 +717,7 @@ PlacesController.prototype = {
|
|||
* receive a string instead of an nsIURI object.
|
||||
*/
|
||||
_assertURINotString: function PC__assertURINotString(value) {
|
||||
NS_ASSERT((typeof(value) == "object") && !(value instanceof String),
|
||||
NS_ASSERT((typeof(value) == "object") && !(value instanceof String),
|
||||
"This method should be passed a URI as a nsIURI object, not as a string.");
|
||||
},
|
||||
|
||||
|
@ -759,16 +759,15 @@ PlacesController.prototype = {
|
|||
|
||||
var messageKey = "tabs.openWarningMultipleBranded";
|
||||
var openKey = "tabs.openButtonMultiple";
|
||||
var strings = document.getElementById("placeBundle");
|
||||
const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
|
||||
var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService).
|
||||
createBundle(BRANDING_BUNDLE_URI).
|
||||
GetStringFromName("brandShortName");
|
||||
|
||||
|
||||
var buttonPressed = promptService.confirmEx(window,
|
||||
PlacesUIUtils.getString("tabs.openWarningTitle"),
|
||||
PlacesUIUtils.getFormattedString(messageKey,
|
||||
PlacesUIUtils.getFormattedString(messageKey,
|
||||
[numTabsToOpen, brandShortName]),
|
||||
(promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
|
||||
+ (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
|
||||
|
@ -788,7 +787,7 @@ PlacesController.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Opens the links in the selected folder, or the selected links in new tabs.
|
||||
* Opens the links in the selected folder, or the selected links in new tabs.
|
||||
*/
|
||||
openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
|
||||
var node = this._view.selectedNode;
|
||||
|
@ -828,7 +827,7 @@ PlacesController.prototype = {
|
|||
|
||||
/**
|
||||
* Create a new Bookmark folder somewhere. Prompts the user for the name
|
||||
* of the folder.
|
||||
* of the folder.
|
||||
*/
|
||||
newFolder: function PC_newFolder() {
|
||||
var ip = this._view.insertionPoint;
|
||||
|
@ -880,17 +879,17 @@ PlacesController.prototype = {
|
|||
|
||||
/**
|
||||
* Walk the list of folders we're removing in this delete operation, and
|
||||
* see if the selected node specified is already implicitly being removed
|
||||
* because it is a child of that folder.
|
||||
* see if the selected node specified is already implicitly being removed
|
||||
* because it is a child of that folder.
|
||||
* @param node
|
||||
* Node to check for containment.
|
||||
* Node to check for containment.
|
||||
* @param pastFolders
|
||||
* List of folders the calling function has already traversed
|
||||
* @returns true if the node should be skipped, false otherwise.
|
||||
* @returns true if the node should be skipped, false otherwise.
|
||||
*/
|
||||
_shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
|
||||
/**
|
||||
* Determines if a node is contained by another node within a resultset.
|
||||
* Determines if a node is contained by another node within a resultset.
|
||||
* @param node
|
||||
* The node to check for containment for
|
||||
* @param parent
|
||||
|
@ -906,7 +905,7 @@ PlacesController.prototype = {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (var j = 0; j < pastFolders.length; ++j) {
|
||||
if (isContainedBy(node, pastFolders[j]))
|
||||
return true;
|
||||
|
@ -915,10 +914,10 @@ PlacesController.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Creates a set of transactions for the removal of a range of items.
|
||||
* Creates a set of transactions for the removal of a range of items.
|
||||
* A range is an array of adjacent nodes in a view.
|
||||
* @param [in] range
|
||||
* An array of nodes to remove. Should all be adjacent.
|
||||
* An array of nodes to remove. Should all be adjacent.
|
||||
* @param [out] transactions
|
||||
* An array of transactions.
|
||||
* @param [optional] removedFolders
|
||||
|
@ -1090,7 +1089,7 @@ PlacesController.prototype = {
|
|||
|
||||
var root = this._view.result.root;
|
||||
|
||||
if (PlacesUtils.nodeIsFolder(root))
|
||||
if (PlacesUtils.nodeIsFolder(root))
|
||||
this._removeRowsFromBookmarks(aTxnName);
|
||||
else if (PlacesUtils.nodeIsQuery(root)) {
|
||||
var queryType = PlacesUtils.asQuery(root).queryOptions.queryType;
|
||||
|
@ -1136,7 +1135,7 @@ PlacesController.prototype = {
|
|||
addData(PlacesUtils.TYPE_HTML, index, overrideURI);
|
||||
}
|
||||
|
||||
// This order is _important_! It controls how this and other
|
||||
// This order is _important_! It controls how this and other
|
||||
// applications select data to be inserted based on type.
|
||||
addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);
|
||||
|
||||
|
@ -1166,8 +1165,8 @@ PlacesController.prototype = {
|
|||
try {
|
||||
let nodes = this._view.selectedNodes;
|
||||
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
let foundFolder = false, foundLink = false;
|
||||
let copiedFolders = [];
|
||||
let placeString, mozURLString, htmlString, unicodeString;
|
||||
|
@ -1179,11 +1178,11 @@ PlacesController.prototype = {
|
|||
continue;
|
||||
if (PlacesUtils.nodeIsFolder(node))
|
||||
copiedFolders.push(node);
|
||||
|
||||
|
||||
function generateChunk(type, overrideURI) {
|
||||
let suffix = i < (nodes.length - 1) ? PlacesUtils.endl : "";
|
||||
let uri = overrideURI;
|
||||
|
||||
|
||||
if (PlacesUtils.nodeIsLivemarkContainer(node))
|
||||
uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec
|
||||
|
||||
|
@ -1194,8 +1193,8 @@ PlacesController.prototype = {
|
|||
htmlString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_HTML,
|
||||
uri) + suffix);
|
||||
|
||||
var placeSuffix = i < (nodes.length - 1) ? "," : "";
|
||||
var resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
|
||||
let placeSuffix = i < (nodes.length - 1) ? "," : "";
|
||||
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
|
||||
return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix;
|
||||
}
|
||||
|
||||
|
@ -1207,7 +1206,7 @@ PlacesController.prototype = {
|
|||
xferable.addDataFlavor(type);
|
||||
xferable.setTransferData(type, PlacesUIUtils._wrapString(data), data.length * 2);
|
||||
}
|
||||
// This order is _important_! It controls how this and other applications
|
||||
// This order is _important_! It controls how this and other applications
|
||||
// select data to be inserted based on type.
|
||||
if (placeString)
|
||||
addData(PlacesUtils.TYPE_X_MOZ_PLACE, placeString);
|
||||
|
@ -1241,11 +1240,11 @@ PlacesController.prototype = {
|
|||
*/
|
||||
paste: function PC_paste() {
|
||||
// Strategy:
|
||||
//
|
||||
// There can be data of various types (folder, separator, link) on the
|
||||
//
|
||||
// There can be data of various types (folder, separator, link) on the
|
||||
// clipboard. We need to get all of that data and build edit transactions
|
||||
// for them. This means asking the clipboard once for each type and
|
||||
// aggregating the results.
|
||||
// for them. This means asking the clipboard once for each type and
|
||||
// aggregating the results.
|
||||
|
||||
/**
|
||||
* Constructs a transferable that can receive data of specific types.
|
||||
|
@ -1255,10 +1254,9 @@ PlacesController.prototype = {
|
|||
* @returns The transferable.
|
||||
*/
|
||||
function makeXferable(types) {
|
||||
var xferable =
|
||||
Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
for (var i = 0; i < types.length; ++i)
|
||||
var xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
for (var i = 0; i < types.length; ++i)
|
||||
xferable.addDataFlavor(types[i]);
|
||||
return xferable;
|
||||
}
|
||||
|
@ -1291,10 +1289,10 @@ PlacesController.prototype = {
|
|||
if (ip.isTag) {
|
||||
var uri = PlacesUtils._uri(items[i].uri);
|
||||
txn = PlacesUIUtils.ptm.tagURI(uri, [ip.itemId]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// adjusted to make sure that items are given the correct index
|
||||
// transactions insert differently if index == -1
|
||||
// transactions insert differently if index == -1
|
||||
// transaction will enqueue the item.
|
||||
if (ip.index > -1)
|
||||
index = ip.index + i;
|
||||
|
@ -1307,18 +1305,18 @@ PlacesController.prototype = {
|
|||
}
|
||||
catch (e) {
|
||||
// getAnyTransferData will throw if there is no data of the specified
|
||||
// type on the clipboard.
|
||||
// type on the clipboard.
|
||||
// unwrapNodes will throw if the data that is present is malformed in
|
||||
// some way.
|
||||
// some way.
|
||||
// In either case, don't fail horribly, just return no data.
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get transactions to paste any folders, separators or links that might
|
||||
// be on the clipboard, aggregate them and execute them.
|
||||
// be on the clipboard, aggregate them and execute them.
|
||||
var transactions = getTransactions([PlacesUtils.TYPE_X_MOZ_PLACE,
|
||||
PlacesUtils.TYPE_X_MOZ_URL,
|
||||
PlacesUtils.TYPE_X_MOZ_URL,
|
||||
PlacesUtils.TYPE_UNICODE]);
|
||||
var txn = PlacesUIUtils.ptm.aggregateTransactions("Paste", transactions);
|
||||
PlacesUIUtils.ptm.doTransaction(txn);
|
||||
|
@ -1336,8 +1334,8 @@ PlacesController.prototype = {
|
|||
/**
|
||||
* Handles drag and drop operations for views. Note that this is view agnostic!
|
||||
* You should not use PlacesController._view within these methods, since
|
||||
* the view that the item(s) have been dropped on was not necessarily active.
|
||||
* Drop functions are passed the view that is being dropped on.
|
||||
* the view that the item(s) have been dropped on was not necessarily active.
|
||||
* Drop functions are passed the view that is being dropped on.
|
||||
*/
|
||||
let PlacesControllerDragHelper = {
|
||||
/**
|
||||
|
@ -1416,7 +1414,8 @@ let PlacesControllerDragHelper = {
|
|||
let dragged;
|
||||
try {
|
||||
dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
|
||||
} catch (e) {
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1442,10 +1441,10 @@ let PlacesControllerDragHelper = {
|
|||
return true;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a node can be moved.
|
||||
*
|
||||
*
|
||||
* @param aNode
|
||||
* A nsINavHistoryResultNode node.
|
||||
* @returns True if the node can be moved, false otherwise.
|
||||
|
@ -1477,7 +1476,7 @@ let PlacesControllerDragHelper = {
|
|||
|
||||
/**
|
||||
* Determines if a container node can be moved.
|
||||
*
|
||||
*
|
||||
* @param aId
|
||||
* A bookmark folder id.
|
||||
* @param [optional] aParentId
|
||||
|
|
|
@ -132,6 +132,6 @@ var SidebarUtils = {
|
|||
},
|
||||
|
||||
clearURLFromStatusBar: function SU_clearURLFromStatusBar() {
|
||||
window.top.XULBrowserWindow.setOverLink("", null);
|
||||
window.top.XULBrowserWindow.setOverLink("", null);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -780,4 +780,3 @@
|
|||
</binding>
|
||||
|
||||
</bindings>
|
||||
|
||||
|
|
|
@ -37,11 +37,21 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function PlacesTreeView(aFlatList, aOnOpenFlatContainer) {
|
||||
this._tree = null;
|
||||
this._result = null;
|
||||
this._selection = null;
|
||||
this._rootNode = null;
|
||||
this._rows = [];
|
||||
this._flatList = aFlatList;
|
||||
this._openContainerCallback = aOnOpenFlatContainer;
|
||||
}
|
||||
|
||||
PlacesTreeView.prototype = {
|
||||
_makeAtom: function PTV__makeAtom(aString) {
|
||||
return Cc["@mozilla.org/atom-service;1"].
|
||||
getService(Ci.nsIAtomService).
|
||||
getAtom(aString);
|
||||
return Cc["@mozilla.org/atom-service;1"].
|
||||
getService(Ci.nsIAtomService).
|
||||
getAtom(aString);
|
||||
},
|
||||
|
||||
_atoms: [],
|
||||
|
@ -122,8 +132,7 @@ PlacesTreeView.prototype = {
|
|||
if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
|
||||
return aContainer._plainContainer = false;
|
||||
|
||||
let resultType = aContainer.queryOptions.resultType;
|
||||
switch (resultType) {
|
||||
switch (aContainer.queryOptions.resultType) {
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
|
||||
|
@ -602,8 +611,7 @@ PlacesTreeView.prototype = {
|
|||
return;
|
||||
|
||||
// Bail out for hidden separators.
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) &&
|
||||
this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
|
||||
return;
|
||||
|
||||
let parentRow;
|
||||
|
@ -632,7 +640,7 @@ PlacesTreeView.prototype = {
|
|||
// children themselves that would be in the way.
|
||||
let cc = aParentNode.childCount;
|
||||
let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
|
||||
this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
|
||||
this.isSorted();
|
||||
for (let i = aNewIndex + 1; i < cc; i++) {
|
||||
let node = aParentNode.getChild(i);
|
||||
if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
|
||||
|
@ -679,8 +687,7 @@ PlacesTreeView.prototype = {
|
|||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
// Bail out for hidden separators.
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) &&
|
||||
this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
|
||||
return;
|
||||
|
||||
let parentRow = aParentNode == this._rootNode ?
|
||||
|
@ -728,8 +735,7 @@ PlacesTreeView.prototype = {
|
|||
return;
|
||||
|
||||
// Bail out for hidden separators.
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) &&
|
||||
this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
|
||||
if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
|
||||
return;
|
||||
|
||||
let oldRow = this._getRowForNode(aNode, true);
|
||||
|
@ -1204,7 +1210,7 @@ PlacesTreeView.prototype = {
|
|||
else {
|
||||
// Use the last-selected node's container.
|
||||
container = lastSelected.parent;
|
||||
|
||||
|
||||
// During its Drag & Drop operation, the tree code closes-and-opens
|
||||
// containers very often (part of the XUL "spring-loaded folders"
|
||||
// implementation). And in certain cases, we may reach a closed
|
||||
|
@ -1309,8 +1315,7 @@ PlacesTreeView.prototype = {
|
|||
|
||||
getCellText: function PTV_getCellText(aRow, aColumn) {
|
||||
let node = this._getNodeForRow(aRow);
|
||||
let columnType = this._getColumnType(aColumn);
|
||||
switch (columnType) {
|
||||
switch (this._getColumnType(aColumn)) {
|
||||
case this.COLUMN_TYPE_TITLE:
|
||||
// normally, this is just the title, but we don't want empty items in
|
||||
// the tree view so return a special string if the title is empty.
|
||||
|
@ -1428,8 +1433,7 @@ PlacesTreeView.prototype = {
|
|||
let newSort;
|
||||
let newSortingAnnotation = "";
|
||||
const NHQO = Ci.nsINavHistoryQueryOptions;
|
||||
let columnType = this._getColumnType(aColumn);
|
||||
switch (columnType) {
|
||||
switch (this._getColumnType(aColumn)) {
|
||||
case this.COLUMN_TYPE_TITLE:
|
||||
if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING)
|
||||
newSort = NHQO.SORT_BY_TITLE_DESCENDING;
|
||||
|
@ -1576,13 +1580,3 @@ PlacesTreeView.prototype = {
|
|||
performActionOnRow: function(aAction, aRow) { },
|
||||
performActionOnCell: function(aAction, aRow, aColumn) { }
|
||||
};
|
||||
|
||||
function PlacesTreeView(aFlatList, aOnOpenFlatContainer) {
|
||||
this._tree = null;
|
||||
this._result = null;
|
||||
this._selection = null;
|
||||
this._rootNode = null;
|
||||
this._rows = [];
|
||||
this._flatList = aFlatList;
|
||||
this._openContainerCallback = aOnOpenFlatContainer;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ var PlacesUIUtils = {
|
|||
* @returns A URI object for the spec.
|
||||
*/
|
||||
createFixedURI: function PUIU_createFixedURI(aSpec) {
|
||||
return URIFixup.createFixupURI(aSpec, 0);
|
||||
return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -128,7 +128,7 @@ var PlacesUIUtils = {
|
|||
*/
|
||||
_getBookmarkItemCopyTransaction:
|
||||
function PUIU__getBookmarkItemCopyTransaction(aData, aContainer, aIndex,
|
||||
aExcludeAnnotations) {
|
||||
aExcludeAnnotations) {
|
||||
var itemURL = PlacesUtils._uri(aData.uri);
|
||||
var itemTitle = aData.title;
|
||||
var keyword = aData.keyword || null;
|
||||
|
@ -278,7 +278,7 @@ var PlacesUIUtils = {
|
|||
* the move/insert.
|
||||
*/
|
||||
makeTransaction: function PUIU_makeTransaction(data, type, container,
|
||||
index, copy) {
|
||||
index, copy) {
|
||||
switch (data.type) {
|
||||
case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
|
||||
if (copy)
|
||||
|
@ -289,7 +289,7 @@ var PlacesUIUtils = {
|
|||
case PlacesUtils.TYPE_X_MOZ_PLACE:
|
||||
if (data.id == -1) // Not bookmarked.
|
||||
return this._getURIItemCopyTransaction(data, container, index);
|
||||
|
||||
|
||||
if (copy)
|
||||
return this._getBookmarkItemCopyTransaction(data, container, index);
|
||||
// Otherwise move the item.
|
||||
|
@ -361,14 +361,14 @@ var PlacesUIUtils = {
|
|||
* bookmarks root folder.
|
||||
*/
|
||||
showAddBookmarkUI: function PUIU_showAddBookmarkUI(aURI,
|
||||
aTitle,
|
||||
aDescription,
|
||||
aDefaultInsertionPoint,
|
||||
aShowPicker,
|
||||
aLoadInSidebar,
|
||||
aKeyword,
|
||||
aPostData,
|
||||
aCharSet) {
|
||||
aTitle,
|
||||
aDescription,
|
||||
aDefaultInsertionPoint,
|
||||
aShowPicker,
|
||||
aLoadInSidebar,
|
||||
aKeyword,
|
||||
aPostData,
|
||||
aCharSet) {
|
||||
var info = {
|
||||
action: "add",
|
||||
type: "bookmark"
|
||||
|
@ -417,9 +417,9 @@ var PlacesUIUtils = {
|
|||
*/
|
||||
showMinimalAddBookmarkUI:
|
||||
function PUIU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
|
||||
aDefaultInsertionPoint, aShowPicker,
|
||||
aLoadInSidebar, aKeyword, aPostData,
|
||||
aCharSet) {
|
||||
aDefaultInsertionPoint, aShowPicker,
|
||||
aLoadInSidebar, aKeyword, aPostData,
|
||||
aCharSet) {
|
||||
var info = {
|
||||
action: "add",
|
||||
type: "bookmark",
|
||||
|
@ -485,11 +485,11 @@ var PlacesUIUtils = {
|
|||
* bookmarks root folder.
|
||||
*/
|
||||
showAddLivemarkUI: function PUIU_showAddLivemarkURI(aFeedURI,
|
||||
aSiteURI,
|
||||
aTitle,
|
||||
aDescription,
|
||||
aDefaultInsertionPoint,
|
||||
aShowPicker) {
|
||||
aSiteURI,
|
||||
aTitle,
|
||||
aDescription,
|
||||
aDefaultInsertionPoint,
|
||||
aShowPicker) {
|
||||
var info = {
|
||||
action: "add",
|
||||
type: "livemark"
|
||||
|
@ -525,8 +525,8 @@ var PlacesUIUtils = {
|
|||
*/
|
||||
showMinimalAddLivemarkUI:
|
||||
function PUIU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle,
|
||||
aDescription, aDefaultInsertionPoint,
|
||||
aShowPicker) {
|
||||
aDescription, aDefaultInsertionPoint,
|
||||
aShowPicker) {
|
||||
var info = {
|
||||
action: "add",
|
||||
type: "livemark",
|
||||
|
@ -706,7 +706,7 @@ var PlacesUIUtils = {
|
|||
/**
|
||||
* By calling this before visiting an URL, the visit will be associated to a
|
||||
* TRANSITION_BOOKMARK transition.
|
||||
* This is used when visiting pages from the bookmarks menu,
|
||||
* This is used when visiting pages from the bookmarks menu,
|
||||
* personal toolbar, and bookmarks from within the places organizer.
|
||||
* If this is not called visits will be marked as TRANSITION_LINK.
|
||||
*/
|
||||
|
@ -730,6 +730,8 @@ var PlacesUIUtils = {
|
|||
* bookmarked (see bug 224521).
|
||||
* @param aURINode
|
||||
* a URI node
|
||||
* @param aWindow
|
||||
* a window on which a potential error alert is shown on.
|
||||
* @return true if it's safe to open the node in the browser, false otherwise.
|
||||
*
|
||||
*/
|
||||
|
@ -890,7 +892,7 @@ var PlacesUIUtils = {
|
|||
openNodeWithEvent: function PUIU_openNodeWithEvent(aNode, aEvent) {
|
||||
this.openNodeIn(aNode, this._getCurrentActiveWin().whereToOpenLink(aEvent));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Loads the node's URL in the appropriate tab or window or as a
|
||||
* web panel.
|
||||
|
@ -927,12 +929,12 @@ var PlacesUIUtils = {
|
|||
* Used to avoid nsIURI overhead in frequently called UI functions.
|
||||
*
|
||||
* @param aUrlString the url to guess the scheme from.
|
||||
*
|
||||
*
|
||||
* @return guessed scheme for this url string.
|
||||
*
|
||||
* @note this is not supposed be perfect, so use it only for UI purposes.
|
||||
*/
|
||||
guessUrlSchemeForUI: function PUU_guessUrlSchemeForUI(aUrlString) {
|
||||
guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
|
||||
return aUrlString.substr(0, aUrlString.indexOf(":"));
|
||||
},
|
||||
|
||||
|
@ -961,7 +963,7 @@ var PlacesUIUtils = {
|
|||
return title || this.getString("noTitle");
|
||||
},
|
||||
|
||||
get leftPaneQueries() {
|
||||
get leftPaneQueries() {
|
||||
// build the map
|
||||
this.leftPaneFolderId;
|
||||
return this.leftPaneQueries;
|
||||
|
@ -1239,7 +1241,7 @@ var PlacesUIUtils = {
|
|||
queryName = name;
|
||||
}
|
||||
}
|
||||
return queryName;
|
||||
return queryName;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1380,7 +1382,7 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() {
|
|||
|
||||
/**
|
||||
* Transaction for editing a the description of a bookmark or a folder.
|
||||
*
|
||||
*
|
||||
* @param aItemId
|
||||
* id of the item to edit.
|
||||
* @param aDescription
|
||||
|
|
|
@ -46,11 +46,8 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_TEST_FILES = \
|
||||
browser_console_clear.js \
|
||||
browser_privatebrowsing_beforeunload_enter.js \
|
||||
browser_privatebrowsing_beforeunload_exit.js \
|
||||
browser_privatebrowsing_certexceptionsui.js \
|
||||
browser_privatebrowsing_commandline_toggle.js \
|
||||
browser_privatebrowsing_cookieacceptdialog.js \
|
||||
browser_privatebrowsing_crh.js \
|
||||
browser_privatebrowsing_downloadmonitor.js \
|
||||
browser_privatebrowsing_fastswitch.js \
|
||||
|
@ -87,5 +84,14 @@ _BROWSER_TEST_FILES = \
|
|||
title.sjs \
|
||||
$(NULL)
|
||||
|
||||
# Turn off private browsing tests that perma-timeout on Linux.
|
||||
ifneq (Linux,$(OS_ARCH))
|
||||
_BROWSER_TEST_FILES += \
|
||||
browser_privatebrowsing_beforeunload_enter.js \
|
||||
browser_privatebrowsing_beforeunload_exit.js \
|
||||
browser_privatebrowsing_cookieacceptdialog.js \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
||||
|
|
|
@ -1212,6 +1212,7 @@ SessionStoreService.prototype = {
|
|||
|
||||
if (aTab.pinned)
|
||||
tabData.pinned = true;
|
||||
tabData.hidden = aTab.hidden;
|
||||
|
||||
var disallow = [];
|
||||
for (var i = 0; i < CAPABILITIES.length; i++)
|
||||
|
@ -1947,6 +1948,7 @@ SessionStoreService.prototype = {
|
|||
tabbrowser.pinTab(tabs[t]);
|
||||
else
|
||||
tabbrowser.unpinTab(tabs[t]);
|
||||
tabs[t].hidden = winData.tabs[t].hidden;
|
||||
}
|
||||
|
||||
// when overwriting tabs, remove all superflous ones
|
||||
|
@ -2031,6 +2033,7 @@ SessionStoreService.prototype = {
|
|||
tabbrowser.pinTab(tab);
|
||||
else
|
||||
tabbrowser.unpinTab(tab);
|
||||
tab.hidden = tabData.hidden;
|
||||
|
||||
tabData._tabStillLoading = true;
|
||||
if (!tabData.entries || tabData.entries.length == 0) {
|
||||
|
|
|
@ -115,6 +115,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_524745.js \
|
||||
browser_528776.js \
|
||||
browser_579879.js \
|
||||
browser_586147.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is sessionstore test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Edward Lee <edilee@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function observeOneRestore(callback) {
|
||||
let topic = "sessionstore-browser-state-restored";
|
||||
Services.obs.addObserver(function() {
|
||||
Services.obs.removeObserver(arguments.callee, topic, false);
|
||||
callback();
|
||||
}, topic, false);
|
||||
};
|
||||
|
||||
function test() {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
waitForExplicitFinish();
|
||||
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
let hiddenTab = gBrowser.addTab();
|
||||
|
||||
is(gBrowser.visibleTabs.length, 2, "should have 2 tabs before hiding");
|
||||
gBrowser.showOnlyTheseTabs([origTab]);
|
||||
is(gBrowser.visibleTabs.length, 1, "only 1 after hiding");
|
||||
ok(hiddenTab.hidden, "sanity check that it's hidden");
|
||||
|
||||
let extraTab = gBrowser.addTab();
|
||||
let state = ss.getBrowserState();
|
||||
let stateObj = JSON.parse(state);
|
||||
let tabs = stateObj.windows[0].tabs;
|
||||
is(tabs.length, 3, "just checking that browser state is correct");
|
||||
ok(!tabs[0].hidden, "first tab is visible");
|
||||
ok(tabs[1].hidden, "second is hidden");
|
||||
ok(!tabs[2].hidden, "third is visible");
|
||||
|
||||
// Make the third tab hidden and then restore the modified state object
|
||||
tabs[2].hidden = true;
|
||||
|
||||
observeOneRestore(function() {
|
||||
let testWindow = Services.wm.getEnumerator("navigator:browser").getNext();
|
||||
is(testWindow.gBrowser.visibleTabs.length, 1, "only restored 1 visible tab");
|
||||
let tabs = testWindow.gBrowser.tabs;
|
||||
ok(!tabs[0].hidden, "first is still visible");
|
||||
ok(tabs[1].hidden, "second tab is still hidden");
|
||||
ok(tabs[2].hidden, "third tab is now hidden");
|
||||
|
||||
// Restore the original state and clean up now that we're done
|
||||
gBrowser.removeTab(hiddenTab);
|
||||
gBrowser.removeTab(extraTab);
|
||||
finish();
|
||||
});
|
||||
ss.setBrowserState(JSON.stringify(stateObj));
|
||||
}
|
|
@ -45,7 +45,7 @@ MOZ_STATIC_BUILD_UNSUPPORTED=1
|
|||
# always enabled for form history
|
||||
MOZ_MORKREADER=1
|
||||
MOZ_SAFE_BROWSING=1
|
||||
MOZ_SERVICES_SYNC=
|
||||
MOZ_SERVICES_SYNC=1
|
||||
MOZ_APP_VERSION=$FIREFOX_VERSION
|
||||
MOZ_EXTENSIONS_DEFAULT=" gnomevfs reporter"
|
||||
# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
|
||||
|
|
|
@ -82,6 +82,12 @@ ifdef _MSC_VER
|
|||
DEFINES += -D_MSC_VER=$(_MSC_VER)
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_CHROME_FILE_FORMAT),jar)
|
||||
DEFINES += -DJAREXT=.jar
|
||||
else
|
||||
DEFINES += -DJAREXT=
|
||||
endif
|
||||
|
||||
# Don't ifdef MOZ_IPC this because mac ppc needs it too.
|
||||
include $(topsrcdir)/ipc/app/defs.mk
|
||||
DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#endif
|
||||
|
||||
[@AB_CD@]
|
||||
@BINPATH@/chrome/@AB_CD@.jar
|
||||
@BINPATH@/chrome/@AB_CD@@JAREXT@
|
||||
@BINPATH@/chrome/@AB_CD@.manifest
|
||||
@BINPATH@/@PREF_DIR@/firefox-l10n.js
|
||||
@BINPATH@/searchplugins/*
|
||||
|
@ -401,7 +401,7 @@
|
|||
#endif
|
||||
|
||||
; [Browser Chrome Files]
|
||||
@BINPATH@/chrome/browser.jar
|
||||
@BINPATH@/chrome/browser@JAREXT@
|
||||
@BINPATH@/chrome/browser.manifest
|
||||
@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
|
||||
@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
|
||||
|
@ -409,7 +409,7 @@
|
|||
#if MOZ_UPDATE_CHANNEL == beta
|
||||
@BINPATH@/extensions/testpilot@labs.mozilla.com/*
|
||||
#endif
|
||||
@BINPATH@/chrome/toolkit.jar
|
||||
@BINPATH@/chrome/toolkit@JAREXT@
|
||||
@BINPATH@/chrome/toolkit.manifest
|
||||
@BINPATH@/@PREF_DIR@/reporter.js
|
||||
#ifdef XP_UNIX
|
||||
|
@ -508,7 +508,7 @@
|
|||
@BINPATH@/@DLL_PREFIX@nssdbm3.chk
|
||||
#endif
|
||||
#endif
|
||||
@BINPATH@/chrome/pippki.jar
|
||||
@BINPATH@/chrome/pippki@JAREXT@
|
||||
@BINPATH@/chrome/pippki.manifest
|
||||
|
||||
; for Solaris SPARC
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
@DLL_PREFIX@zlib@DLL_SUFFIX@
|
||||
LICENSE
|
||||
browserconfig.properties
|
||||
chrome.manifest
|
||||
chrome/US.jar
|
||||
chrome/app-chrome.manifest
|
||||
chrome/browser.manifest
|
||||
chrome/chrome.rdf
|
||||
chrome/chromelist.txt
|
||||
chrome/classic.jar
|
||||
|
@ -21,14 +21,18 @@ chrome/classic.manifest
|
|||
chrome/comm.jar
|
||||
chrome/comm.manifest
|
||||
chrome/en-win.jar
|
||||
chrome/@AB_CD@.manifest
|
||||
chrome/help.jar
|
||||
chrome/installed-chrome.txt
|
||||
chrome/m3ffxtbr.jar
|
||||
chrome/m3ffxtbr.manifest
|
||||
chrome/overlayinfo/
|
||||
chrome/pippki.manifest
|
||||
chrome/reporter.jar
|
||||
chrome/reporter.manifest
|
||||
chrome/toolkit.manifest
|
||||
component.reg
|
||||
components/browser.manifest
|
||||
components/components.list
|
||||
components/@DLL_PREFIX@browserdirprovider@DLL_SUFFIX@
|
||||
components/@DLL_PREFIX@brwsrdir@DLL_SUFFIX@
|
||||
|
@ -541,6 +545,181 @@ uninstall/UninstallFirefox.exe
|
|||
uninstall/uninst.exe
|
||||
uninstall/uninstall.exe
|
||||
xpicleanup@BIN_SUFFIX@
|
||||
#ifdef MOZ_OMNIJAR
|
||||
chrome/@AB_CD@.jar
|
||||
chrome/browser.jar
|
||||
chrome/localized.manifest
|
||||
chrome/nonlocalized.manifest
|
||||
chrome/pippki.jar
|
||||
chrome/toolkit.jar
|
||||
components/addonManager.js
|
||||
components/amContentHandler.js
|
||||
components/amWebInstallListener.js
|
||||
components/browser.xpt
|
||||
components/components.manifest
|
||||
components/contentAreaDropListener.js
|
||||
components/contentSecurityPolicy.js
|
||||
components/crypto-SDR.js
|
||||
components/FeedConverter.js
|
||||
components/FeedProcessor.js
|
||||
components/FeedWriter.js
|
||||
components/fuelApplication.js
|
||||
components/GPSDGeolocationProvider.js
|
||||
components/interfaces.manifest
|
||||
components/jsconsole-clhandler.js
|
||||
components/NetworkGeolocationProvider.js
|
||||
components/nsBadCertHandler.js
|
||||
components/nsBlocklistService.js
|
||||
components/nsBrowserContentHandler.js
|
||||
components/nsBrowserGlue.js
|
||||
components/nsContentDispatchChooser.js
|
||||
components/nsContentPrefService.js
|
||||
components/nsDefaultCLH.js
|
||||
components/nsDownloadManagerUI.js
|
||||
#ifdef XP_UNIX
|
||||
#ifndef XP_MACOSX
|
||||
components/nsFilePicker.js
|
||||
#endif
|
||||
#endif
|
||||
components/nsFormAutoComplete.js
|
||||
components/nsFormHistory.js
|
||||
components/nsHandlerService.js
|
||||
components/nsHelperAppDlg.js
|
||||
components/nsINIProcessor.js
|
||||
components/nsLivemarkService.js
|
||||
components/nsLoginInfo.js
|
||||
components/nsLoginManager.js
|
||||
components/nsLoginManagerPrompter.js
|
||||
components/nsMicrosummaryService.js
|
||||
components/nsPlacesAutoComplete.js
|
||||
components/nsPlacesDBFlush.js
|
||||
components/nsPlacesExpiration.js
|
||||
components/nsPrivateBrowsingService.js
|
||||
components/nsPrompter.js
|
||||
components/nsProxyAutoConfig.js
|
||||
components/nsSafebrowsingApplication.js
|
||||
components/nsSearchService.js
|
||||
components/nsSearchSuggestions.js
|
||||
components/nsSessionStartup.js
|
||||
components/nsSessionStore.js
|
||||
components/nsSetDefaultBrowser.js
|
||||
components/nsSidebar.js
|
||||
components/nsTaggingService.js
|
||||
components/nsTryToClose.js
|
||||
components/nsUpdateService.js
|
||||
components/nsUpdateServiceStub.js
|
||||
components/nsUpdateTimerManager.js
|
||||
components/nsUrlClassifierLib.js
|
||||
components/nsUrlClassifierListManager.js
|
||||
components/nsURLFormatter.js
|
||||
components/nsWebHandlerApp.js
|
||||
components/PlacesProtocolHandler.js
|
||||
components/storage-Legacy.js
|
||||
components/storage-mozStorage.js
|
||||
components/txEXSLTRegExFunctions.js
|
||||
components/WebContentConverter.js
|
||||
defaults/autoconfig/platform.js
|
||||
defaults/autoconfig/prefcalls.js
|
||||
defaults/pref/channel-prefs.js
|
||||
defaults/pref/firefox-branding.js
|
||||
defaults/pref/firefox.js
|
||||
defaults/pref/firefox-l10n.js
|
||||
defaults/pref/reporter.js
|
||||
defaults/profile/bookmarks.html
|
||||
defaults/profile/chrome
|
||||
defaults/profile/chrome/userChrome-example.css
|
||||
defaults/profile/chrome/userContent-example.css
|
||||
defaults/profile/localstore.rdf
|
||||
defaults/profile/mimeTypes.rdf
|
||||
defaults/profile/prefs.js
|
||||
greprefs.js
|
||||
modules/AddonLogging.jsm
|
||||
modules/AddonManager.jsm
|
||||
modules/AddonRepository.jsm
|
||||
modules/AddonUpdateChecker.jsm
|
||||
modules/CertUtils.jsm
|
||||
modules/CrashSubmit.jsm
|
||||
modules/CSPUtils.jsm
|
||||
modules/ctypes.jsm
|
||||
modules/debug.js
|
||||
modules/distribution.js
|
||||
modules/DownloadLastDir.jsm
|
||||
modules/DownloadPaths.jsm
|
||||
modules/DownloadUtils.jsm
|
||||
modules/FileUtils.jsm
|
||||
modules/Geometry.jsm
|
||||
modules/HUDService.jsm
|
||||
modules/InlineSpellChecker.jsm
|
||||
modules/ISO8601DateUtils.jsm
|
||||
modules/LightweightThemeConsumer.jsm
|
||||
modules/LightweightThemeManager.jsm
|
||||
modules/Microformats.js
|
||||
modules/NetUtil.jsm
|
||||
modules/NetworkPrioritizer.jsm
|
||||
modules/openLocationLastURL.jsm
|
||||
modules/PerfMeasurement.jsm
|
||||
modules/PlacesDBUtils.jsm
|
||||
modules/PlacesUIUtils.jsm
|
||||
modules/PlacesUtils.jsm
|
||||
modules/PluginProvider.jsm
|
||||
modules/PluralForm.jsm
|
||||
modules/PopupNotifications.jsm
|
||||
modules/Services.jsm
|
||||
modules/SpatialNavigation.js
|
||||
modules/stylePanel.jsm
|
||||
modules/utils.js
|
||||
modules/WindowDraggingUtils.jsm
|
||||
#ifdef XP_WIN
|
||||
modules/WindowsJumpLists.jsm
|
||||
modules/WindowsPreviewPerTab.jsm
|
||||
#endif
|
||||
modules/XPCOMUtils.jsm
|
||||
modules/XPIProvider.jsm
|
||||
res/contenteditable.css
|
||||
res/designmode.css
|
||||
res/dtd/mathml.dtd
|
||||
res/dtd/xhtml11.dtd
|
||||
res/EditorOverride.css
|
||||
res/entityTables
|
||||
res/entityTables/html40Latin1.properties
|
||||
res/entityTables/html40Special.properties
|
||||
res/entityTables/html40Symbols.properties
|
||||
res/entityTables/htmlEntityVersions.properties
|
||||
res/entityTables/mathml20.properties
|
||||
res/entityTables/transliterate.properties
|
||||
res/fonts/mathfont.properties
|
||||
res/fonts/mathfontStandardSymbolsL.properties
|
||||
res/fonts/mathfontSTIXNonUnicode.properties
|
||||
res/fonts/mathfontSTIXSize1.properties
|
||||
res/fonts/mathfontSTIXSizeOneSym.properties
|
||||
res/fonts/mathfontUnicode.properties
|
||||
res/grabber.gif
|
||||
res/html/folder.png
|
||||
res/langGroups.properties
|
||||
res/language.properties
|
||||
res/svg.css
|
||||
res/table-add-column-after-active.gif
|
||||
res/table-add-column-after.gif
|
||||
res/table-add-column-after-hover.gif
|
||||
res/table-add-column-before-active.gif
|
||||
res/table-add-column-before.gif
|
||||
res/table-add-column-before-hover.gif
|
||||
res/table-add-row-after-active.gif
|
||||
res/table-add-row-after.gif
|
||||
res/table-add-row-after-hover.gif
|
||||
res/table-add-row-before-active.gif
|
||||
res/table-add-row-before.gif
|
||||
res/table-add-row-before-hover.gif
|
||||
res/table-remove-column-active.gif
|
||||
res/table-remove-column.gif
|
||||
res/table-remove-column-hover.gif
|
||||
res/table-remove-row-active.gif
|
||||
res/table-remove-row.gif
|
||||
res/table-remove-row-hover.gif
|
||||
#else
|
||||
components/binary.manifest
|
||||
omni.jar
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
../Plug-Ins/PrintPDE.plugin/Contents/Info.plist
|
||||
../Plug-Ins/PrintPDE.plugin/Contents/MacOS/PrintPDE
|
||||
|
|
|
@ -22,6 +22,14 @@
|
|||
!define PreReleaseSuffix "@PRE_RELEASE_SUFFIX@"
|
||||
!define BrandFullName "${BrandFullNameInternal}${PreReleaseSuffix}"
|
||||
|
||||
# LSP_CATEGORIES is the permitted LSP categories for the application. Each LSP
|
||||
# category value is ANDed together to set multiple permitted categories.
|
||||
# See http://msdn.microsoft.com/en-us/library/ms742253%28VS.85%29.aspx
|
||||
# The value below permits the LSP_INSPECTOR, LSP_REDIRECTOR, LSP_PROXY,
|
||||
# LSP_FIREWALL, LSP_INBOUND_MODIFY, LSP_OUTBOUND_MODIFY, LSP_CRYPTO_COMPRESS,
|
||||
# and LSP_LOCAL_CACHE LSP categories.
|
||||
!define LSP_CATEGORIES "0x000000ff"
|
||||
|
||||
# NO_INSTDIR_FROM_REG is defined for pre-releases which have a PreReleaseSuffix
|
||||
# (e.g. Alpha X, Beta X, etc.) to prevent finding a non-default installation
|
||||
# directory in the registry and using that as the default. This prevents
|
||||
|
|
|
@ -113,6 +113,7 @@ VIAddVersionKey "OriginalFilename" "setup.exe"
|
|||
!insertmacro RegCleanAppHandler
|
||||
!insertmacro RegCleanMain
|
||||
!insertmacro RegCleanUninstall
|
||||
!insertmacro SetAppLSPCategories
|
||||
!insertmacro SetBrandNameVars
|
||||
!insertmacro UpdateShortcutAppModelIDs
|
||||
!insertmacro UnloadUAC
|
||||
|
@ -236,7 +237,7 @@ Section "-Application" APP_IDX
|
|||
SetDetailsPrint none
|
||||
|
||||
${LogHeader} "Installing Main Files"
|
||||
${CopyFilesFromDir} "$EXEDIR\nonlocalized" "$INSTDIR" \
|
||||
${CopyFilesFromDir} "$EXEDIR\core" "$INSTDIR" \
|
||||
"$(ERROR_CREATE_DIRECTORY_PREFIX)" \
|
||||
"$(ERROR_CREATE_DIRECTORY_SUFFIX)"
|
||||
|
||||
|
@ -267,15 +268,6 @@ Section "-Application" APP_IDX
|
|||
${LogUninstall} "File: \install_wizard.log"
|
||||
${LogUninstall} "File: \updates.xml"
|
||||
|
||||
SetDetailsPrint both
|
||||
DetailPrint $(STATUS_INSTALL_LANG)
|
||||
SetDetailsPrint none
|
||||
|
||||
${LogHeader} "Installing Localized Files"
|
||||
${CopyFilesFromDir} "$EXEDIR\localized" "$INSTDIR" \
|
||||
"$(ERROR_CREATE_DIRECTORY_PREFIX)" \
|
||||
"$(ERROR_CREATE_DIRECTORY_SUFFIX)"
|
||||
|
||||
; Check if QuickTime is installed and copy the nsIQTScriptablePlugin.xpt from
|
||||
; its plugins directory into the app's components directory.
|
||||
ClearErrors
|
||||
|
@ -374,7 +366,6 @@ Section "-Application" APP_IDX
|
|||
${If} $TmpVal == "HKLM"
|
||||
; Set the Start Menu Internet and Vista Registered App HKLM registry keys.
|
||||
${SetStartMenuInternet}
|
||||
|
||||
${FixShellIconHandler}
|
||||
|
||||
; If we are writing to HKLM and create the quick launch and the desktop
|
||||
|
@ -398,6 +389,11 @@ Section "-Application" APP_IDX
|
|||
StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\$R9"
|
||||
${CreateRegKey} "$TmpVal" "$0" 0
|
||||
|
||||
${If} $TmpVal == "HKLM"
|
||||
; Set the permitted LSP Categories for WinVista and above
|
||||
${SetAppLSPCategories} ${LSP_CATEGORIES}
|
||||
${EndIf}
|
||||
|
||||
; Create shortcuts
|
||||
${LogHeader} "Adding Shortcuts"
|
||||
|
||||
|
@ -524,8 +520,8 @@ SectionEnd
|
|||
Function CustomAbort
|
||||
${If} "${AB_CD}" == "en-US"
|
||||
${AndIf} "$PageName" != ""
|
||||
${AndIf} ${FileExists} "$EXEDIR\nonlocalized\distribution\distribution.ini"
|
||||
ReadINIStr $0 "$EXEDIR\nonlocalized\distribution\distribution.ini" "Global" "about"
|
||||
${AndIf} ${FileExists} "$EXEDIR\core\distribution\distribution.ini"
|
||||
ReadINIStr $0 "$EXEDIR\core\distribution\distribution.ini" "Global" "about"
|
||||
ClearErrors
|
||||
${WordFind} "$0" "Funnelcake" "E#" $1
|
||||
${Unless} ${Errors}
|
||||
|
@ -684,18 +680,18 @@ BrandingText " "
|
|||
|
||||
Function preWelcome
|
||||
StrCpy $PageName "Welcome"
|
||||
${If} ${FileExists} "$EXEDIR\localized\distribution\modern-wizard.bmp"
|
||||
${If} ${FileExists} "$EXEDIR\core\distribution\modern-wizard.bmp"
|
||||
Delete "$PLUGINSDIR\modern-wizard.bmp"
|
||||
CopyFiles /SILENT "$EXEDIR\localized\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
|
||||
CopyFiles /SILENT "$EXEDIR\core\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function preOptions
|
||||
StrCpy $PageName "Options"
|
||||
${If} ${FileExists} "$EXEDIR\localized\distribution\modern-header.bmp"
|
||||
${If} ${FileExists} "$EXEDIR\core\distribution\modern-header.bmp"
|
||||
${AndIf} $hHeaderBitmap == ""
|
||||
Delete "$PLUGINSDIR\modern-header.bmp"
|
||||
CopyFiles /SILENT "$EXEDIR\localized\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
|
||||
CopyFiles /SILENT "$EXEDIR\core\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
|
||||
${ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
|
||||
${EndIf}
|
||||
!insertmacro MUI_HEADER_TEXT "$(OPTIONS_PAGE_TITLE)" "$(OPTIONS_PAGE_SUBTITLE)"
|
||||
|
@ -921,7 +917,7 @@ FunctionEnd
|
|||
Function .onInit
|
||||
StrCpy $PageName ""
|
||||
StrCpy $LANGUAGE 0
|
||||
${SetBrandNameVars} "$EXEDIR\localized\distribution\setup.ini"
|
||||
${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
|
||||
|
||||
${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OS_MSG)"
|
||||
|
||||
|
@ -1004,9 +1000,8 @@ Function .onInit
|
|||
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Bottom "70"
|
||||
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" State "1"
|
||||
|
||||
; There must always be nonlocalized and localized directories.
|
||||
${GetSize} "$EXEDIR\nonlocalized\" "/S=0K" $R5 $R7 $R8
|
||||
${GetSize} "$EXEDIR\localized\" "/S=0K" $R6 $R7 $R8
|
||||
; There must always be a core directory.
|
||||
${GetSize} "$EXEDIR\core\" "/S=0K" $R5 $R7 $R8
|
||||
IntOp $R8 $R5 + $R6
|
||||
SectionSetSize ${APP_IDX} $R8
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
${RegCleanUninstall}
|
||||
${UpdateProtocolHandlers}
|
||||
${FixShellIconHandler}
|
||||
${SetAppLSPCategories} ${LSP_CATEGORIES}
|
||||
|
||||
; Only update the Clients\StartMenuInternet registry key values if they
|
||||
; don't exist or this installation is the same as the one set in those keys.
|
||||
|
|
|
@ -100,6 +100,7 @@ VIAddVersionKey "OriginalFilename" "helper.exe"
|
|||
!insertmacro RegCleanAppHandler
|
||||
!insertmacro RegCleanMain
|
||||
!insertmacro RegCleanUninstall
|
||||
!insertmacro SetAppLSPCategories
|
||||
!insertmacro SetBrandNameVars
|
||||
!insertmacro UpdateShortcutAppModelIDs
|
||||
!insertmacro UnloadUAC
|
||||
|
@ -122,6 +123,7 @@ VIAddVersionKey "OriginalFilename" "helper.exe"
|
|||
!insertmacro un.RegCleanUninstall
|
||||
!insertmacro un.RegCleanProtocolHandler
|
||||
!insertmacro un.RemoveQuotesFromPath
|
||||
!insertmacro un.SetAppLSPCategories
|
||||
!insertmacro un.SetBrandNameVars
|
||||
|
||||
!include shared.nsh
|
||||
|
@ -240,6 +242,7 @@ Section "Uninstall"
|
|||
${un.RegCleanMain} "Software\Mozilla"
|
||||
${un.RegCleanUninstall}
|
||||
${un.DeleteShortcuts}
|
||||
${un.SetAppLSPCategories}
|
||||
${EndIf}
|
||||
|
||||
${un.RegCleanAppHandler} "FirefoxURL"
|
||||
|
|
|
@ -100,6 +100,8 @@ ifeq (WINNT,$(OS_ARCH))
|
|||
UNINSTALLER_PACKAGE_HOOK = $(RM) -r $(STAGEDIST)/uninstall; \
|
||||
$(NSINSTALL) -D $(STAGEDIST)/uninstall; \
|
||||
cp ../installer/windows/l10ngen/helper.exe $(STAGEDIST)/uninstall; \
|
||||
$(RM) $(_ABS_DIST)/l10n-stage/setup.exe; \
|
||||
cp ../installer/windows/l10ngen/setup.exe $(_ABS_DIST)/l10n-stage; \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -195,30 +197,14 @@ repackage-win32-installer: WIN32_INSTALLER_OUT="$(_ABS_DIST)/$(PKG_INST_PATH)$(P
|
|||
repackage-win32-installer: $(WIN32_INSTALLER_IN) $(SUBMAKEFILES)
|
||||
@echo "Repackaging $(WIN32_INSTALLER_IN) into $(WIN32_INSTALLER_OUT)."
|
||||
$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY) export
|
||||
if test ! -d $(_ABS_DIST)/$(PKG_INST_PATH); then \
|
||||
$(NSINSTALL) -D $(_ABS_DIST)/$(PKG_INST_PATH); \
|
||||
fi
|
||||
$(RM) -rf l10n-stage
|
||||
$(NSINSTALL) -D l10n-stage
|
||||
$(CYGWIN_WRAPPER) 7z x -ol10n-stage $(WIN32_INSTALLER_IN)
|
||||
$(RM) -r l10n-stage/localized
|
||||
$(RM) l10n-stage/setup.exe
|
||||
# copy xpi-stage over, but not install.rdf and chrome.manifest,
|
||||
# those are just for language packs
|
||||
cp -r $(DIST)/xpi-stage/locale-$(AB_CD) l10n-stage/localized
|
||||
$(RM) l10n-stage/localized/install.rdf l10n-stage/localized/chrome.manifest
|
||||
mv l10n-stage/localized/chrome/$(AB_CD).manifest l10n-stage/localized/chrome/localized.manifest
|
||||
$(MAKE) -C ../installer/windows CONFIG_DIR=l10ngen l10ngen/setup.exe l10ngen/7zSD.sfx
|
||||
cp ../installer/windows/l10ngen/setup.exe l10n-stage
|
||||
$(NSINSTALL) -D l10n-stage/localized/uninstall
|
||||
cp ../installer/windows/l10ngen/helper.exe l10n-stage/localized/uninstall
|
||||
rm -f app.7z
|
||||
cd l10n-stage && \
|
||||
$(CYGWIN_WRAPPER) 7z a -r -t7z ../app.7z -mx -m0=BCJ2 -m1=LZMA:d24 -m2=LZMA:d19 -m3=LZMA:d19 -mb0:1 -mb0s1:2 -mb0s2:3
|
||||
cat ../installer/windows/l10ngen/7zSD.sfx \
|
||||
$(topsrcdir)/browser/installer/windows/app.tag \
|
||||
app.7z > $(WIN32_INSTALLER_OUT)
|
||||
chmod 0755 $(WIN32_INSTALLER_OUT)
|
||||
$(MAKE) repackage-zip \
|
||||
AB_CD=$(AB_CD) \
|
||||
MOZ_PKG_FORMAT=SFX7Z \
|
||||
ZIP_IN=$(WIN32_INSTALLER_IN) \
|
||||
ZIP_OUT=$(WIN32_INSTALLER_OUT) \
|
||||
SFX_HEADER="$(PWD)/../installer/windows/l10ngen/7zSD.sfx \
|
||||
$(topsrcdir)/browser/installer/windows/app.tag"
|
||||
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
repackage-win32-installer-%: $(WIN32_INSTALLER_IN) libs-%
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
<!ENTITY mainWindow.titlePrivateBrowsingSuffix "(Private Browsing)">
|
||||
|
||||
<!-- Tab context menu -->
|
||||
<!ENTITY moveTabTo.label "Move This Tab To…">
|
||||
<!ENTITY moveTabTo.accesskey "M">
|
||||
<!ENTITY createNewGroup.label "Create New Group">
|
||||
<!ENTITY createNewGroup.accesskey "C">
|
||||
<!ENTITY namedGroups.label "Named Groups">
|
||||
<!ENTITY reloadTab.label "Reload Tab">
|
||||
<!ENTITY reloadTab.accesskey "R">
|
||||
<!ENTITY reloadAllTabs.label "Reload All Tabs">
|
||||
|
@ -127,6 +132,8 @@
|
|||
|
||||
<!-- Toolbar items -->
|
||||
<!ENTITY homeButton.label "Home">
|
||||
<!ENTITY tabViewButton.label "Tab Sets">
|
||||
<!ENTITY tabViewButton.tooltip "Open a visual tab interface">
|
||||
|
||||
<!ENTITY bookmarksButton.label "Bookmarks">
|
||||
<!ENTITY bookmarksButton.tooltip "Display your bookmarks">
|
||||
|
@ -233,6 +240,8 @@
|
|||
|
||||
<!ENTITY viewMenu.label "View">
|
||||
<!ENTITY viewMenu.accesskey "V">
|
||||
<!ENTITY showTabView.label "TabView">
|
||||
<!ENTITY showTabView.accesskey "V">
|
||||
<!ENTITY viewToolbarsMenu.label "Toolbars">
|
||||
<!ENTITY viewToolbarsMenu.accesskey "T">
|
||||
<!ENTITY viewSidebarMenu.label "Sidebar">
|
||||
|
|
|
@ -275,5 +275,8 @@ ctrlTab.showAll.label=;Show all #1 tabs
|
|||
# Used as the bookmark name when saving a keyword for a search field.
|
||||
addKeywordTitleAutoFill=Search %S
|
||||
|
||||
# TabView
|
||||
tabView.title=%S Tab Sets
|
||||
|
||||
extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name=Default
|
||||
extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description=The default theme.
|
||||
|
|
|
@ -563,6 +563,10 @@ toolbar[mode="full"] .toolbarbutton-menubutton-button {
|
|||
list-style-image: url("moz-icon://stock/gtk-home?size=toolbar&state=disabled");
|
||||
}
|
||||
|
||||
#tabview-button {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview.png);
|
||||
}
|
||||
|
||||
#downloads-button {
|
||||
-moz-image-region: rect(0px 24px 24px 0px);
|
||||
}
|
||||
|
@ -878,7 +882,7 @@ toolbar[iconsize="small"] #fullscreen-button {
|
|||
-moz-border-start: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
#identity-box:focus {
|
||||
#identity-box:-moz-focusring {
|
||||
outline: 1px dotted -moz-DialogText;
|
||||
}
|
||||
|
||||
|
@ -1523,3 +1527,12 @@ panel[dimmed="true"] {
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Tab Context Menu */
|
||||
#context_tabViewMenu {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview-16.png);
|
||||
}
|
||||
|
||||
#context_tabViewMenuPopup > menuitem.group {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,13 @@ browser.jar:
|
|||
skin/classic/browser/tabbrowser/progress.png (tabbrowser/progress.png)
|
||||
skin/classic/browser/tabbrowser/progress-pulsing.png (tabbrowser/progress-pulsing.png)
|
||||
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
|
||||
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
|
||||
skin/classic/browser/tabview/edit.png (tabview/edit.png)
|
||||
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
|
||||
skin/classic/browser/tabview/tabview.css (tabview/tabview.css)
|
||||
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
|
||||
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)
|
||||
skin/classic/browser/tabview/tabview-16.png (tabview/tabview-16.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-16-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
|
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 466 B |
После Ширина: | Высота: | Размер: 49 KiB |
После Ширина: | Высота: | Размер: 710 B |
|
@ -0,0 +1,7 @@
|
|||
/* This file is for platform-specific CSS for TabView, and is loaded after the
|
||||
platform-independent tabview.css, to allow overwriting.
|
||||
*/
|
||||
|
||||
.close {
|
||||
background: url("moz-icon://stock/gtk-close?size=menu") no-repeat;
|
||||
}
|
После Ширина: | Высота: | Размер: 397 B |
|
@ -544,6 +544,12 @@ toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl) {
|
|||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
/* tabview button */
|
||||
|
||||
#tabview-button {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview.png);
|
||||
}
|
||||
|
||||
/* download manager button */
|
||||
|
||||
#downloads-button {
|
||||
|
@ -699,7 +705,7 @@ toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl) {
|
|||
}
|
||||
|
||||
|
||||
#identity-box:focus:not(:active):not([open="true"]) {
|
||||
#identity-box:-moz-focusring {
|
||||
-moz-box-shadow: 0 0 3px 1px -moz-mac-focusring inset,
|
||||
0 0 3px 2px -moz-mac-focusring;
|
||||
}
|
||||
|
@ -2108,3 +2114,12 @@ panel[dimmed="true"] {
|
|||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/* Tab Context Menu */
|
||||
#context_tabViewMenu {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview-16.png);
|
||||
}
|
||||
|
||||
#context_tabViewMenuPopup > menuitem.group {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,13 @@ browser.jar:
|
|||
skin/classic/browser/tabbrowser/tabbrowser-tabs-bkgnd.png (tabbrowser/tabbrowser-tabs-bkgnd.png)
|
||||
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
|
||||
skin/classic/browser/tabbrowser/tab-bkgnd.png (tabbrowser/tab-bkgnd.png)
|
||||
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
|
||||
skin/classic/browser/tabview/edit.png (tabview/edit.png)
|
||||
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
|
||||
skin/classic/browser/tabview/tabview.css (tabview/tabview.css)
|
||||
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
|
||||
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)
|
||||
skin/classic/browser/tabview/tabview-16.png (tabview/tabview-16.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-16-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
|
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 466 B |
После Ширина: | Высота: | Размер: 49 KiB |
После Ширина: | Высота: | Размер: 710 B |
|
@ -0,0 +1,7 @@
|
|||
/* This file is for platform-specific CSS for TabView, and is loaded after the
|
||||
platform-independent tabview.css, to allow overwriting.
|
||||
*/
|
||||
|
||||
.close {
|
||||
background: url(chrome://global/skin/icons/closetab.png) no-repeat;
|
||||
}
|
После Ширина: | Высота: | Размер: 397 B |
|
@ -60,11 +60,8 @@
|
|||
|
||||
#navigator-toolbox {
|
||||
-moz-appearance: none;
|
||||
border-top: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#navigator-toolbox[tabsontop="true"] {
|
||||
border-top: none;
|
||||
border-bottom: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
|
@ -81,7 +78,11 @@
|
|||
}
|
||||
|
||||
#nav-bar:not(:-moz-lwtheme),
|
||||
#navigator-toolbox[tabsontop="true"] > #nav-bar {
|
||||
#nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme),
|
||||
#nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme),
|
||||
#navigator-toolbox[tabsontop="true"] > #nav-bar,
|
||||
#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar,
|
||||
#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar {
|
||||
background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
|
||||
}
|
||||
|
||||
|
@ -606,6 +607,12 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
|
|||
-moz-image-region: rect(0, 90px, 18px, 72px);
|
||||
}
|
||||
|
||||
/* tabview button */
|
||||
|
||||
#tabview-button {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview.png);
|
||||
}
|
||||
|
||||
/* download manager button */
|
||||
|
||||
#downloads-button {
|
||||
|
@ -844,13 +851,13 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
|
|||
background-color: hsl(92,45%,52%);
|
||||
}
|
||||
|
||||
#identity-box:focus {
|
||||
#identity-box:-moz-focusring {
|
||||
outline: 1px dotted -moz-DialogText;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
#identity-box.verifiedDomain:focus ,
|
||||
#identity-box.verifiedIdentity:focus {
|
||||
#identity-box.verifiedDomain:-moz-focusring,
|
||||
#identity-box.verifiedIdentity:-moz-focusring {
|
||||
outline-color: white;
|
||||
}
|
||||
|
||||
|
@ -1115,10 +1122,6 @@ richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-i
|
|||
-moz-box-shadow: 0 -1px ThreeDShadow inset;
|
||||
}
|
||||
|
||||
#TabsToolbar[tabsontop="false"] {
|
||||
border-bottom: 1px solid ThreeDShadow !important;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs:-moz-system-metric(touch-enabled) {
|
||||
min-height: .73cm;
|
||||
}
|
||||
|
@ -1812,3 +1815,12 @@ panel[dimmed="true"] {
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Tab Context Menu */
|
||||
#context_tabViewMenu {
|
||||
list-style-image: url(chrome://browser/skin/tabview/tabview-16.png);
|
||||
}
|
||||
|
||||
#context_tabViewMenuPopup > menuitem.group {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,13 @@ browser.jar:
|
|||
skin/classic/browser/tabbrowser/tab.png (tabbrowser/tab.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)
|
||||
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
|
||||
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
|
||||
skin/classic/browser/tabview/edit.png (tabview/edit.png)
|
||||
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
|
||||
skin/classic/browser/tabview/tabview.css (tabview/tabview.css)
|
||||
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
|
||||
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)
|
||||
skin/classic/browser/tabview/tabview-16.png (tabview/tabview-16.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-16-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
@ -198,6 +205,13 @@ browser.jar:
|
|||
skin/classic/aero/browser/tabbrowser/tab.png (tabbrowser/tab.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)
|
||||
skin/classic/aero/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
|
||||
skin/classic/aero/browser/tabview/edit-light.png (tabview/edit-light.png)
|
||||
skin/classic/aero/browser/tabview/edit.png (tabview/edit.png)
|
||||
skin/classic/aero/browser/tabview/new-tab.png (tabview/new-tab.png)
|
||||
skin/classic/aero/browser/tabview/tabview.css (tabview/tabview.css)
|
||||
skin/classic/aero/browser/tabview/stack-expander.png (tabview/stack-expander.png)
|
||||
skin/classic/aero/browser/tabview/tabview.png (tabview/tabview.png)
|
||||
skin/classic/aero/browser/tabview/tabview-16.png (tabview/tabview-16.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/aero/browser/sync-16-throbber.png
|
||||
skin/classic/aero/browser/sync-16.png
|
||||
|
|
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 466 B |
После Ширина: | Высота: | Размер: 49 KiB |
После Ширина: | Высота: | Размер: 710 B |
|
@ -0,0 +1,53 @@
|
|||
/* This file is for platform-specific CSS for TabView, and is loaded after the
|
||||
platform-independent tabview.css, to allow overwriting.
|
||||
*/
|
||||
|
||||
.close {
|
||||
background: url(chrome://global/skin/icons/Close.gif) no-repeat;
|
||||
}
|
||||
|
||||
#bg {
|
||||
background: -moz-linear-gradient(top,#F1F5FB,#EDF4FB);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999999;
|
||||
}
|
||||
|
||||
.newTabButtonAlt {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
z-index: 99999;
|
||||
border: none;
|
||||
-moz-border-radius: 4px;
|
||||
font-size: 50px;
|
||||
line-height: 50px;
|
||||
height: 57px !important;
|
||||
width: 70px !important;
|
||||
margin-top: 4px !important;
|
||||
text-align: center;
|
||||
background-color: #B9CDEB;
|
||||
-moz-box-shadow: inset 0px 0px 5px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.3);
|
||||
}
|
||||
|
||||
.newTabButtonAlt > span {
|
||||
color: #D9E7FC;
|
||||
text-shadow: 0px 0px 7px rgba(0,0,0,.4), 0 -1px 0 rgba(255,255,255,.6);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.newTabGroup {
|
||||
background-color: #D9E7FC;
|
||||
border: none;
|
||||
border-top: 1px solid rgba(127,127,127,.5);
|
||||
-moz-border-radius: 0.1em;
|
||||
-moz-box-shadow: inset 0px 5px 10px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.inNewTabGroup .favicon {
|
||||
background-color: #D9E7FC;
|
||||
-moz-box-shadow: none;
|
||||
}
|
||||
|
После Ширина: | Высота: | Размер: 397 B |
|
@ -321,6 +321,7 @@ user_pref("security.default_personal_cert", "Select Automatically"); // Need to
|
|||
user_pref("network.http.prompt-temp-redirect", false);
|
||||
user_pref("media.cache_size", 100);
|
||||
user_pref("security.warn_viewing_mixed", false);
|
||||
user_pref("app.update.enabled", false);
|
||||
|
||||
// Only load extensions from the application and user profile
|
||||
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
|
||||
|
|
|
@ -42,46 +42,68 @@ use Archive::Zip(':ERROR_CODES');
|
|||
|
||||
my ($BUILDCONFIG);
|
||||
|
||||
sub fixBuildconfig($$);
|
||||
sub fixBuildconfig($$$);
|
||||
|
||||
$BUILDCONFIG = 'content/global/buildconfig.html';
|
||||
|
||||
if (scalar(@ARGV) != 2) {
|
||||
print STDERR ("usage: fix-buildconfig <zipfile1> <zipfile2>\n");
|
||||
if (scalar(@ARGV) != 3) {
|
||||
print STDERR ("usage: fix-buildconfig <jar|file> <file1> <file2>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!fixBuildconfig($ARGV[0], $ARGV[1])) {
|
||||
if (!fixBuildconfig($ARGV[0], $ARGV[1], $ARGV[2])) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
sub fixBuildconfig($$) {
|
||||
my ($zipPath1, $zipPath2);
|
||||
($zipPath1, $zipPath2) = @_;
|
||||
sub fixBuildconfig($$$) {
|
||||
my ($mode, $path1, $path2);
|
||||
($mode, $path1, $path2) = @_;
|
||||
|
||||
my ($ze, $zip1, $zip2);
|
||||
|
||||
$zip1 = Archive::Zip->new();
|
||||
if (($ze = $zip1->read($zipPath1)) != AZ_OK) {
|
||||
print STDERR ($0.': could not read "'.$zipPath1.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
$zip2 = Archive::Zip->new();
|
||||
if (($ze = $zip2->read($zipPath2)) != AZ_OK) {
|
||||
print STDERR ($0.': could not read "'.$zipPath2.'": error '.$ze."\n");
|
||||
if ($mode ne 'jar' && $mode ne 'file') {
|
||||
print STDERR ($0.': must specify jar or file\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
my ($contents1, $contents2);
|
||||
if (!defined($contents1 = $zip1->contents($BUILDCONFIG))) {
|
||||
print STDERR ($0.': could not get "'.$BUILDCONFIG.'" from "'.$zipPath1.'"'.
|
||||
my ($ze, $zip1, $zip2);
|
||||
|
||||
if ($mode eq 'jar') {
|
||||
$zip1 = Archive::Zip->new();
|
||||
if (($ze = $zip1->read($path1)) != AZ_OK) {
|
||||
print STDERR ($0.': could not read "'.$path1.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
$zip2 = Archive::Zip->new();
|
||||
if (($ze = $zip2->read($path2)) != AZ_OK) {
|
||||
print STDERR ($0.': could not read "'.$path2.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
$contents1 = $zip1->contents($BUILDCONFIG);
|
||||
$contents2 = $zip2->contents($BUILDCONFIG);
|
||||
} elsif ($mode eq 'file') {
|
||||
local($/);
|
||||
my ($file1, $file2);
|
||||
|
||||
open($file1, '<'.$path1.$BUILDCONFIG) or return 0;
|
||||
open($file2, '<'.$path2.$BUILDCONFIG) or return 0;
|
||||
|
||||
$contents1 = <$file1>;
|
||||
$contents2 = <$file2>;
|
||||
|
||||
close($file1);
|
||||
close($file2);
|
||||
}
|
||||
|
||||
if (!defined($contents1)) {
|
||||
print STDERR ($0.': could not get "'.$BUILDCONFIG.'" from "'.$path1.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
if (!defined($contents2 = $zip2->contents($BUILDCONFIG))) {
|
||||
print STDERR ($0.': could not get "'.$BUILDCONFIG.'" from "'.$zipPath2.'"'.
|
||||
if (!defined($contents2)) {
|
||||
print STDERR ($0.': could not get "'.$BUILDCONFIG.'" from "'.$path2.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -117,25 +139,37 @@ sub fixBuildconfig($$) {
|
|||
my ($contentsNew);
|
||||
$contentsNew = join("\n", @linesNew);
|
||||
|
||||
if (!defined($zip1->contents($BUILDCONFIG, $contentsNew))) {
|
||||
print STDERR ($0.': could not set "'.$BUILDCONFIG.'" to "'.$zipPath1.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
if (!defined($zip2->contents($BUILDCONFIG, $contentsNew))) {
|
||||
print STDERR ($0.': could not set "'.$BUILDCONFIG.'" to "'.$zipPath2.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
if ($mode eq 'jar') {
|
||||
if (!defined($zip1->contents($BUILDCONFIG, $contentsNew))) {
|
||||
print STDERR ($0.': could not set "'.$BUILDCONFIG.'" to "'.$path1.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
if (!defined($zip2->contents($BUILDCONFIG, $contentsNew))) {
|
||||
print STDERR ($0.': could not set "'.$BUILDCONFIG.'" to "'.$path2.'"'.
|
||||
"\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (($ze = $zip1->overwrite()) != AZ_OK) {
|
||||
print STDERR ($0.': could not write "'.$path1.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
if (($ze = $zip2->overwrite()) != AZ_OK) {
|
||||
print STDERR ($0.': could not write "'.$path2.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
} elsif ($mode eq 'file') {
|
||||
my ($file1, $file2);
|
||||
|
||||
if (($ze = $zip1->overwrite()) != AZ_OK) {
|
||||
print STDERR ($0.': could not write "'.$zipPath1.'": error '.$ze."\n");
|
||||
return 0;
|
||||
}
|
||||
if (($ze = $zip2->overwrite()) != AZ_OK) {
|
||||
print STDERR ($0.': could not write "'.$zipPath2.'": error '.$ze."\n");
|
||||
return 0;
|
||||
open($file1, '>'.$path1.$BUILDCONFIG) or return 0;
|
||||
open($file2, '>'.$path2.$BUILDCONFIG) or return 0;
|
||||
|
||||
print $file1 ($contentsNew);
|
||||
print $file2 ($contentsNew);
|
||||
|
||||
close($file1);
|
||||
close($file2);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -67,7 +67,7 @@ ifeq ($(MOZ_BUILD_APP),camino) # {
|
|||
INSTALLER_DIR = camino/installer
|
||||
MOZ_PKG_APPNAME = camino
|
||||
APPNAME = Camino.app
|
||||
BUILDCONFIG_JAR = Contents/MacOS/chrome/toolkit.jar
|
||||
BUILDCONFIG_BASE = Contents/MacOS/chrome
|
||||
else # } {
|
||||
MOZ_PKG_APPNAME = $(MOZ_APP_NAME)
|
||||
APPNAME = $(MOZ_APP_DISPLAYNAME)$(DBGTAG).app
|
||||
|
@ -77,9 +77,17 @@ INSTALLER_DIR = xulrunner/installer/mac
|
|||
APPNAME = XUL.framework
|
||||
APP_CONTENTS = Versions/Current
|
||||
endif # } xulrunner
|
||||
BUILDCONFIG_JAR = $(APP_CONTENTS)/chrome/toolkit.jar
|
||||
BUILDCONFIG_BASE = $(APP_CONTENTS)/chrome
|
||||
endif # } !camino
|
||||
|
||||
ifeq ($(MOZ_CHROME_FILE_FORMAT),jar)
|
||||
BUILDCONFIG = $(BUILDCONFIG_BASE)/toolkit.jar
|
||||
FIX_MODE = jar
|
||||
else
|
||||
BUILDCONFIG = $(BUILDCONFIG_BASE)/toolkit/
|
||||
FIX_MODE = file
|
||||
endif
|
||||
|
||||
postflight_all:
|
||||
# Build the universal package out of only the bits that would be released.
|
||||
# Call the packager to set this up. Set UNIVERSAL_BINARY= to avoid producing
|
||||
|
@ -97,9 +105,9 @@ postflight_all:
|
|||
$(DIST_ARCH_2)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(APP_CONTENTS)/*.chk
|
||||
# The only difference betewen the two trees now should be the
|
||||
# about:buildconfig page. Fix it up.
|
||||
$(TOPSRCDIR)/build/macosx/universal/fix-buildconfig \
|
||||
$(DIST_ARCH_1)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(BUILDCONFIG_JAR) \
|
||||
$(DIST_ARCH_2)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(BUILDCONFIG_JAR)
|
||||
$(TOPSRCDIR)/build/macosx/universal/fix-buildconfig $(FIX_MODE) \
|
||||
$(DIST_ARCH_1)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(BUILDCONFIG) \
|
||||
$(DIST_ARCH_2)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(BUILDCONFIG)
|
||||
mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
|
||||
rm -f $(DIST_ARCH_2)/universal
|
||||
ln -s $(DIST_UNI) $(DIST_ARCH_2)/universal
|
||||
|
|
|
@ -437,7 +437,7 @@ public:
|
|||
nsIViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult) = 0;
|
||||
void DeleteShell() { mPresShell = nsnull; }
|
||||
virtual void DeleteShell() = 0;
|
||||
|
||||
nsIPresShell* GetShell() const
|
||||
{
|
||||
|
@ -1408,6 +1408,12 @@ public:
|
|||
*/
|
||||
virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId) = 0;
|
||||
|
||||
void ScheduleBeforePaintEvent();
|
||||
void BeforePaintEventFiring()
|
||||
{
|
||||
mHavePendingPaint = PR_FALSE;
|
||||
}
|
||||
|
||||
protected:
|
||||
~nsIDocument()
|
||||
{
|
||||
|
@ -1547,6 +1553,9 @@ protected:
|
|||
// True if document has ever had script handling object.
|
||||
PRPackedBool mHasHadScriptHandlingObject;
|
||||
|
||||
// True if we're waiting for a before-paint event.
|
||||
PRPackedBool mHavePendingPaint;
|
||||
|
||||
// The document's script global object, the object from which the
|
||||
// document can get its script context and scope. This is the
|
||||
// *inner* window object.
|
||||
|
|
|
@ -80,10 +80,10 @@ nsAttrValue::nsAttrValue(const nsAString& aValue)
|
|||
SetTo(aValue);
|
||||
}
|
||||
|
||||
nsAttrValue::nsAttrValue(nsICSSStyleRule* aValue)
|
||||
nsAttrValue::nsAttrValue(nsICSSStyleRule* aValue, const nsAString* aSerialized)
|
||||
: mBits(0)
|
||||
{
|
||||
SetTo(aValue);
|
||||
SetTo(aValue, aSerialized);
|
||||
}
|
||||
|
||||
#ifdef MOZ_SVG
|
||||
|
@ -249,7 +249,7 @@ nsAttrValue::SetTo(const nsAttrValue& aOther)
|
|||
case eAtomArray:
|
||||
{
|
||||
if (!EnsureEmptyAtomArray() ||
|
||||
!GetAtomArrayValue()->AppendObjects(*otherCont->mAtomArray)) {
|
||||
!GetAtomArrayValue()->AppendElements(*otherCont->mAtomArray)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
@ -313,12 +313,13 @@ nsAttrValue::SetTo(PRInt16 aInt)
|
|||
}
|
||||
|
||||
void
|
||||
nsAttrValue::SetTo(nsICSSStyleRule* aValue)
|
||||
nsAttrValue::SetTo(nsICSSStyleRule* aValue, const nsAString* aSerialized)
|
||||
{
|
||||
if (EnsureEmptyMiscContainer()) {
|
||||
MiscContainer* cont = GetMiscContainer();
|
||||
NS_ADDREF(cont->mCSSStyleRule = aValue);
|
||||
cont->mType = eCSSStyleRule;
|
||||
SetMiscAtomOrString(aSerialized);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,6 +432,7 @@ nsAttrValue::ToString(nsAString& aResult) const
|
|||
if (decl) {
|
||||
decl->ToString(aResult);
|
||||
}
|
||||
const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -503,7 +505,7 @@ nsAttrValue::GetEnumString(nsAString& aResult, PRBool aRealTag) const
|
|||
NS_NOTREACHED("couldn't find value in EnumTable");
|
||||
}
|
||||
|
||||
PRInt32
|
||||
PRUint32
|
||||
nsAttrValue::GetAtomCount() const
|
||||
{
|
||||
ValueType type = Type();
|
||||
|
@ -513,7 +515,7 @@ nsAttrValue::GetAtomCount() const
|
|||
}
|
||||
|
||||
if (type == eAtomArray) {
|
||||
return GetAtomArrayValue()->Count();
|
||||
return GetAtomArrayValue()->Length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -531,7 +533,7 @@ nsAttrValue::AtomAt(PRInt32 aIndex) const
|
|||
|
||||
NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
|
||||
|
||||
return GetAtomArrayValue()->ObjectAt(aIndex);
|
||||
return GetAtomArrayValue()->ElementAt(aIndex);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
|
@ -592,9 +594,11 @@ nsAttrValue::HashValue() const
|
|||
case eAtomArray:
|
||||
{
|
||||
PRUint32 retval = 0;
|
||||
PRInt32 i, count = cont->mAtomArray->Count();
|
||||
for (i = 0; i < count; ++i) {
|
||||
retval ^= NS_PTR_TO_INT32(cont->mAtomArray->ObjectAt(i));
|
||||
PRUint32 count = cont->mAtomArray->Length();
|
||||
for (nsCOMPtr<nsIAtom> *cur = cont->mAtomArray->Elements(),
|
||||
*end = cur + count;
|
||||
cur != end; ++cur) {
|
||||
retval ^= NS_PTR_TO_INT32(cur->get());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
@ -690,18 +694,10 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const
|
|||
// For classlists we could be insensitive to order, however
|
||||
// classlists are never mapped attributes so they are never compared.
|
||||
|
||||
PRInt32 count = thisCont->mAtomArray->Count();
|
||||
if (count != otherCont->mAtomArray->Count()) {
|
||||
if (!(*thisCont->mAtomArray == *otherCont->mAtomArray)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRInt32 i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (thisCont->mAtomArray->ObjectAt(i) !=
|
||||
otherCont->mAtomArray->ObjectAt(i)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
needsStringComparison = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
|
@ -827,19 +823,21 @@ nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
|
|||
default:
|
||||
{
|
||||
if (Type() == eAtomArray) {
|
||||
nsCOMArray<nsIAtom>* array = GetAtomArrayValue();
|
||||
AtomArray* array = GetAtomArrayValue();
|
||||
if (aCaseSensitive == eCaseMatters) {
|
||||
return array->IndexOf(aValue) >= 0;
|
||||
return array->IndexOf(aValue) != AtomArray::NoIndex;
|
||||
}
|
||||
|
||||
nsDependentAtomString val1(aValue);
|
||||
|
||||
for (PRInt32 i = 0, count = array->Count(); i < count; ++i) {
|
||||
for (nsCOMPtr<nsIAtom> *cur = array->Elements(),
|
||||
*end = cur + array->Length();
|
||||
cur != end; ++cur) {
|
||||
// For performance reasons, don't do a full on unicode case
|
||||
// insensitive string comparison. This is only used for quirks mode
|
||||
// anyway.
|
||||
if (nsContentUtils::EqualsIgnoreASCIICase(val1,
|
||||
nsDependentAtomString(array->ObjectAt(i)))) {
|
||||
nsDependentAtomString(*cur))) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -913,9 +911,9 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue)
|
|||
return;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIAtom>* array = GetAtomArrayValue();
|
||||
AtomArray* array = GetAtomArrayValue();
|
||||
|
||||
if (!array->AppendObject(classAtom)) {
|
||||
if (!array->AppendElement(classAtom)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
@ -930,7 +928,7 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue)
|
|||
|
||||
classAtom = do_GetAtom(Substring(start, iter));
|
||||
|
||||
if (!array->AppendObject(classAtom)) {
|
||||
if (!array->AppendElement(classAtom)) {
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
@ -1264,7 +1262,10 @@ nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
|
|||
"Trying to re-set atom or string!");
|
||||
if (aValue) {
|
||||
PRUint32 len = aValue->Length();
|
||||
NS_ASSERTION(len, "Empty string?");
|
||||
// We're allowing eCSSStyleRule attributes to store empty strings as it
|
||||
// can be beneficial to store an empty style attribute as a parsed rule.
|
||||
// Add other types as needed.
|
||||
NS_ASSERTION(len || Type() == eCSSStyleRule, "Empty string?");
|
||||
MiscContainer* cont = GetMiscContainer();
|
||||
if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
|
||||
nsIAtom* atom = NS_NewAtom(*aValue);
|
||||
|
@ -1362,7 +1363,7 @@ nsAttrValue::EnsureEmptyAtomArray()
|
|||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIAtom>* array = new nsCOMArray<nsIAtom>;
|
||||
AtomArray* array = new AtomArray;
|
||||
if (!array) {
|
||||
Reset();
|
||||
return PR_FALSE;
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "nsColor.h"
|
||||
#include "nsCaseTreatment.h"
|
||||
#include "nsMargin.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
typedef PRUptrdiff PtrBits;
|
||||
class nsAString;
|
||||
|
@ -57,7 +58,7 @@ class nsIAtom;
|
|||
class nsICSSStyleRule;
|
||||
class nsISVGValue;
|
||||
class nsIDocument;
|
||||
template<class E> class nsCOMArray;
|
||||
template<class E> class nsTArray;
|
||||
template<class E> class nsTPtrArray;
|
||||
|
||||
#define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
|
||||
|
@ -92,10 +93,12 @@ public:
|
|||
|
||||
class nsAttrValue {
|
||||
public:
|
||||
typedef nsTArray< nsCOMPtr<nsIAtom> > AtomArray;
|
||||
|
||||
nsAttrValue();
|
||||
nsAttrValue(const nsAttrValue& aOther);
|
||||
explicit nsAttrValue(const nsAString& aValue);
|
||||
explicit nsAttrValue(nsICSSStyleRule* aValue);
|
||||
nsAttrValue(nsICSSStyleRule* aValue, const nsAString* aSerialized);
|
||||
#ifdef MOZ_SVG
|
||||
explicit nsAttrValue(nsISVGValue* aValue);
|
||||
#endif
|
||||
|
@ -132,7 +135,7 @@ public:
|
|||
void SetTo(const nsAttrValue& aOther);
|
||||
void SetTo(const nsAString& aValue);
|
||||
void SetTo(PRInt16 aInt);
|
||||
void SetTo(nsICSSStyleRule* aValue);
|
||||
void SetTo(nsICSSStyleRule* aValue, const nsAString* aSerialized);
|
||||
#ifdef MOZ_SVG
|
||||
void SetTo(nsISVGValue* aValue);
|
||||
#endif
|
||||
|
@ -151,7 +154,7 @@ public:
|
|||
PRBool GetColorValue(nscolor& aColor) const;
|
||||
inline PRInt16 GetEnumValue() const;
|
||||
inline float GetPercentValue() const;
|
||||
inline nsCOMArray<nsIAtom>* GetAtomArrayValue() const;
|
||||
inline AtomArray* GetAtomArrayValue() const;
|
||||
inline nsICSSStyleRule* GetCSSStyleRuleValue() const;
|
||||
#ifdef MOZ_SVG
|
||||
inline nsISVGValue* GetSVGValue() const;
|
||||
|
@ -170,7 +173,7 @@ public:
|
|||
// Methods to get access to atoms we may have
|
||||
// Returns the number of atoms we have; 0 if we have none. It's OK
|
||||
// to call this without checking the type first; it handles that.
|
||||
PRInt32 GetAtomCount() const;
|
||||
PRUint32 GetAtomCount() const;
|
||||
// Returns the atom at aIndex (0-based). Do not call this with
|
||||
// aIndex >= GetAtomCount().
|
||||
nsIAtom* AtomAt(PRInt32 aIndex) const;
|
||||
|
@ -333,7 +336,7 @@ private:
|
|||
PRUint32 mEnumValue;
|
||||
PRInt32 mPercent;
|
||||
nsICSSStyleRule* mCSSStyleRule;
|
||||
nsCOMArray<nsIAtom>* mAtomArray;
|
||||
AtomArray* mAtomArray;
|
||||
#ifdef MOZ_SVG
|
||||
nsISVGValue* mSVGValue;
|
||||
#endif
|
||||
|
@ -426,7 +429,7 @@ nsAttrValue::GetPercentValue() const
|
|||
/ 100.0f;
|
||||
}
|
||||
|
||||
inline nsCOMArray<nsIAtom>*
|
||||
inline nsAttrValue::AtomArray*
|
||||
nsAttrValue::GetAtomArrayValue() const
|
||||
{
|
||||
NS_PRECONDITION(Type() == eAtomArray, "wrong type");
|
||||
|
|
|
@ -665,6 +665,7 @@ nsContentUtils::InitializeEventTable() {
|
|||
{ nsGkAtoms::onvolumechange, NS_VOLUMECHANGE, EventNameType_HTML, NS_EVENT_NULL },
|
||||
#endif // MOZ_MEDIA
|
||||
{ nsGkAtoms::onMozAfterPaint, NS_AFTERPAINT, EventNameType_None, NS_EVENT },
|
||||
{ nsGkAtoms::onMozBeforePaint, NS_BEFOREPAINT, EventNameType_None, NS_EVENT_NULL },
|
||||
|
||||
{ nsGkAtoms::onMozScrolledAreaChanged, NS_SCROLLEDAREACHANGED, EventNameType_None, NS_SCROLLAREA_EVENT },
|
||||
|
||||
|
@ -6004,7 +6005,7 @@ nsContentUtils::ReparentClonedObjectToScope(JSContext* cx,
|
|||
}
|
||||
|
||||
struct ClassMatchingInfo {
|
||||
nsCOMArray<nsIAtom> mClasses;
|
||||
nsAttrValue::AtomArray mClasses;
|
||||
nsCaseTreatment mCaseTreatment;
|
||||
};
|
||||
|
||||
|
@ -6020,14 +6021,14 @@ MatchClassNames(nsIContent* aContent, PRInt32 aNamespaceID, nsIAtom* aAtom,
|
|||
|
||||
// need to match *all* of the classes
|
||||
ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
|
||||
PRInt32 length = info->mClasses.Count();
|
||||
PRUint32 length = info->mClasses.Length();
|
||||
if (!length) {
|
||||
// If we actually had no classes, don't match.
|
||||
return PR_FALSE;
|
||||
}
|
||||
PRInt32 i;
|
||||
PRUint32 i;
|
||||
for (i = 0; i < length; ++i) {
|
||||
if (!classAttr->Contains(info->mClasses.ObjectAt(i),
|
||||
if (!classAttr->Contains(info->mClasses[i],
|
||||
info->mCaseTreatment)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
@ -6054,9 +6055,9 @@ AllocClassMatchingInfo(nsINode* aRootNode,
|
|||
NS_ENSURE_TRUE(info, nsnull);
|
||||
|
||||
if (attrValue.Type() == nsAttrValue::eAtomArray) {
|
||||
info->mClasses.AppendObjects(*(attrValue.GetAtomArrayValue()));
|
||||
info->mClasses.SwapElements(*(attrValue.GetAtomArrayValue()));
|
||||
} else if (attrValue.Type() == nsAttrValue::eAtom) {
|
||||
info->mClasses.AppendObject(attrValue.GetAtomValue());
|
||||
info->mClasses.AppendElement(attrValue.GetAtomValue());
|
||||
}
|
||||
|
||||
info->mCaseTreatment =
|
||||
|
|
|
@ -783,6 +783,36 @@ nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) c
|
|||
mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ExternalResourceHider(nsIURI* aKey,
|
||||
nsExternalResourceMap::ExternalResource* aData,
|
||||
void* aClosure)
|
||||
{
|
||||
aData->mViewer->Hide();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsExternalResourceMap::HideViewers()
|
||||
{
|
||||
mMap.EnumerateRead(ExternalResourceHider, nsnull);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ExternalResourceShower(nsIURI* aKey,
|
||||
nsExternalResourceMap::ExternalResource* aData,
|
||||
void* aClosure)
|
||||
{
|
||||
aData->mViewer->Show();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsExternalResourceMap::ShowViewers()
|
||||
{
|
||||
mMap.EnumerateRead(ExternalResourceShower, nsnull);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
|
||||
nsIDocumentViewer* aViewer,
|
||||
|
@ -811,6 +841,9 @@ nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
|
|||
} else {
|
||||
doc->SetDisplayDocument(aDisplayDocument);
|
||||
|
||||
// Make sure that hiding our viewer will tear down its presentation.
|
||||
aViewer->SetSticky(PR_FALSE);
|
||||
|
||||
rv = aViewer->Init(nsnull, nsIntRect(0, 0, 0, 0));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aViewer->Open(nsnull, nsnull);
|
||||
|
@ -3039,11 +3072,28 @@ nsDocument::doCreateShell(nsPresContext* aContext,
|
|||
// Note: we don't hold a ref to the shell (it holds a ref to us)
|
||||
mPresShell = shell;
|
||||
|
||||
mExternalResourceMap.ShowViewers();
|
||||
|
||||
if (mHavePendingPaint) {
|
||||
mPresShell->GetPresContext()->RefreshDriver()->
|
||||
ScheduleBeforePaintEvent(this);
|
||||
}
|
||||
|
||||
shell.swap(*aInstancePtrResult);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::DeleteShell()
|
||||
{
|
||||
mExternalResourceMap.HideViewers();
|
||||
if (mHavePendingPaint) {
|
||||
mPresShell->GetPresContext()->RefreshDriver()->RevokeBeforePaintEvent(this);
|
||||
}
|
||||
mPresShell = nsnull;
|
||||
}
|
||||
|
||||
static void
|
||||
SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
||||
{
|
||||
|
@ -7816,3 +7866,16 @@ nsIDocument::CreateStaticClone(nsISupports* aCloneContainer)
|
|||
return clonedDoc.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::ScheduleBeforePaintEvent()
|
||||
{
|
||||
if (!mHavePendingPaint) {
|
||||
// We don't want to use GetShell() here, because we want to schedule the
|
||||
// paint even if we're frozen. Either we'll get unfrozen and then the
|
||||
// event will fire, or we'll quietly go away at some point.
|
||||
mHavePendingPaint =
|
||||
!mPresShell ||
|
||||
mPresShell->GetPresContext()->RefreshDriver()->
|
||||
ScheduleBeforePaintEvent(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,6 +375,12 @@ public:
|
|||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
};
|
||||
|
||||
// Hide all our viewers
|
||||
void HideViewers();
|
||||
|
||||
// Show all our viewers
|
||||
void ShowViewers();
|
||||
|
||||
protected:
|
||||
class PendingLoad : public ExternalResourceLoad,
|
||||
public nsIStreamListener
|
||||
|
@ -588,6 +594,7 @@ public:
|
|||
nsIViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult);
|
||||
virtual void DeleteShell();
|
||||
|
||||
virtual nsresult SetSubDocumentFor(nsIContent *aContent,
|
||||
nsIDocument* aSubDoc);
|
||||
|
|