merge m-c to fx-team

This commit is contained in:
Tim Taubert 2011-12-06 08:55:18 +01:00
commit 9c6aeeac42
14 changed files with 258 additions and 38 deletions

View File

@ -180,6 +180,9 @@ function setupSearchEngine()
{
gSearchEngine = JSON.parse(localStorage["search-engine"]);
if (!gSearchEngine)
return;
// Look for extended information, like logo and links.
let searchEngineInfo = SEARCH_ENGINES[gSearchEngine.name];
if (searchEngineInfo) {

View File

@ -1963,6 +1963,8 @@
else
this.showTab(tab);
}, this);
this.tabContainer.mTabstrip.ensureElementIsVisible(this.selectedTab, false);
]]>
</body>
</method>

View File

@ -123,11 +123,12 @@
toggle('expertContent');
}
// if this is a Strict-Transport-Security host and the cert
// is bad, don't allow overrides (STS Spec section 7.3).
if (getCSSClass() == "badStsCert") {
// Disallow overrides if this is a Strict-Transport-Security
// host and the cert is bad (STS Spec section 7.3) or if the
// certerror is in a frame (bug 633691).
if (getCSSClass() == "badStsCert" || window != top) {
var ec = document.getElementById('expertContent');
document.getElementById('errorLongContent').removeChild(ec);
ec.parentNode.removeChild(ec);
}
var tech = document.getElementById("technicalContentText");

View File

@ -178,6 +178,16 @@ function SessionStoreService() {
this._prefBranch.addObserver("sessionstore.resume_from_crash", this, true);
return this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
});
XPCOMUtils.defineLazyGetter(this, "_max_tabs_undo", function () {
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
return this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
});
XPCOMUtils.defineLazyGetter(this, "_max_windows_undo", function () {
this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
return this._prefBranch.getIntPref("sessionstore.max_windows_undo");
});
}
SessionStoreService.prototype = {
@ -291,10 +301,6 @@ SessionStoreService.prototype = {
// Do pref migration before we store any values and start observing changes
this._migratePrefs();
// observe prefs changes so we can modify stored data to match
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
// this pref is only read at startup, so no need to observe it
this._sessionhistory_max_entries =
@ -642,11 +648,13 @@ SessionStoreService.prototype = {
// if the user decreases the max number of closed tabs they want
// preserved update our internal states to match that max
case "sessionstore.max_tabs_undo":
this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
for (let ix in this._windows) {
this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"), this._windows[ix]._closedTabs.length);
this._windows[ix]._closedTabs.splice(this._max_tabs_undo, this._windows[ix]._closedTabs.length);
}
break;
case "sessionstore.max_windows_undo":
this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
this._capClosedWindows();
break;
case "sessionstore.interval":
@ -1094,10 +1102,9 @@ SessionStoreService.prototype = {
var event = aWindow.document.createEvent("Events");
event.initEvent("SSTabClosing", true, false);
aTab.dispatchEvent(event);
var maxTabsUndo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
// don't update our internal state if we don't have to
if (maxTabsUndo == 0) {
if (this._max_tabs_undo == 0) {
return;
}
@ -1118,8 +1125,8 @@ SessionStoreService.prototype = {
pos: aTab._tPos
});
var length = this._windows[aWindow.__SSi]._closedTabs.length;
if (length > maxTabsUndo)
this._windows[aWindow.__SSi]._closedTabs.splice(maxTabsUndo, length - maxTabsUndo);
if (length > this._max_tabs_undo)
this._windows[aWindow.__SSi]._closedTabs.splice(this._max_tabs_undo, length - this._max_tabs_undo);
}
},
@ -2518,7 +2525,7 @@ SessionStoreService.prototype = {
// at startup we don't accidentally add them to a popup window
do {
total.unshift(lastClosedWindowsCopy.shift())
} while (total[0].isPopup)
} while (total[0].isPopup && lastClosedWindowsCopy.length > 0)
}
#endif
@ -4264,17 +4271,16 @@ SessionStoreService.prototype = {
* resize such that we have at least one non-popup window.
*/
_capClosedWindows : function sss_capClosedWindows() {
let maxWindowsUndo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
if (this._closedWindows.length <= maxWindowsUndo)
if (this._closedWindows.length <= this._max_windows_undo)
return;
let spliceTo = maxWindowsUndo;
let spliceTo = this._max_windows_undo;
#ifndef XP_MACOSX
let normalWindowIndex = 0;
// try to find a non-popup window in this._closedWindows
while (normalWindowIndex < this._closedWindows.length &&
!!this._closedWindows[normalWindowIndex].isPopup)
normalWindowIndex++;
if (normalWindowIndex >= maxWindowsUndo)
if (normalWindowIndex >= this._max_windows_undo)
spliceTo = normalWindowIndex + 1;
#endif
this._closedWindows.splice(spliceTo, this._closedWindows.length);
@ -4525,7 +4531,8 @@ let gRestoreTabsProgressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// Ignore state changes on browsers that we've already restored and state
// changes that aren't applicable.
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
if (aBrowser.__SS_restoreState &&
aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {

View File

@ -164,6 +164,7 @@ _BROWSER_FILES = \
browser_tabview_bug685476.js \
browser_tabview_bug685692.js \
browser_tabview_bug686654.js \
browser_tabview_bug696602.js \
browser_tabview_bug697390.js \
browser_tabview_bug705621.js \
browser_tabview_bug706430.js \

View File

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let win;
function test() {
waitForExplicitFinish();
newWindowWithTabView(function(newWin) {
win = newWin;
registerCleanupFunction(function() {
win.close();
});
let cw = win.TabView.getContentWindow();
let groupItemOne = cw.GroupItems.groupItems[0];
let groupItemTwo = createGroupItemWithBlankTabs(win, 300, 300, 20, 10);
is(win.gBrowser.tabContainer.getAttribute("overflow"), "true",
"The tabstrip is overflow");
is(groupItemOne.getChildren().length, 10, "Group one has 10 tabs");
is(groupItemTwo.getChildren().length, 10, "Group two has 10 tabs");
checkSelectedTabVisible("two", function() {
showTabView(function() {
checkSelectedTabVisible("one", finish);
groupItemOne.getChild(9).zoomIn();
}, win);
});
groupItemTwo.getChild(9).zoomIn();
}, function(newWin) {
for (let i = 0; i < 9; i++)
newWin.gBrowser.addTab();
});
}
function isSelectedTabVisible() {
let tabstrip = win.gBrowser.tabContainer.mTabstrip;
let scrollRect = tabstrip.scrollClientRect;
let tab = win.gBrowser.selectedTab.getBoundingClientRect();
return (scrollRect.left <= tab.left && tab.right <= scrollRect.right);
}
function checkSelectedTabVisible(groupName, callback) {
whenTabViewIsHidden(function() {
ok(isSelectedTabVisible(), "Group " + groupName + " selected tab is visible");
callback();
}, win);
}

View File

@ -152,6 +152,10 @@ let UI = {
// Used to keep track of the last opened tab.
_lastOpenedTab: null,
// Variable: _originalSmoothScroll
// Used to keep track of the tab strip smooth scroll value.
_originalSmoothScroll: null,
// ----------
// Function: toString
// Prints [UI] for debug use
@ -514,6 +518,11 @@ let UI = {
this._isChangingVisibility = true;
// store tab strip smooth scroll value and disable it.
let tabStrip = gBrowser.tabContainer.mTabstrip;
this._originalSmoothScroll = tabStrip.smoothScroll;
tabStrip.smoothScroll = false;
// initialize the direction of the page
this._initPageDirection();
@ -609,6 +618,7 @@ let UI = {
gBrowser.selectedBrowser.focus();
gBrowser.updateTitlebar();
gBrowser.tabContainer.mTabstrip.smoothScroll = this._originalSmoothScroll;
#ifdef XP_MACOSX
this.setTitlebarColors(false);
#endif

View File

@ -285,6 +285,7 @@ Highlighter.prototype = {
*/
destroy: function Highlighter_destroy()
{
this.IUI.win.clearTimeout(this.transitionDisabler);
this.browser.removeEventListener("scroll", this, true);
this.browser.removeEventListener("resize", this, true);
this.boundCloseEventHandler = null;

View File

@ -489,7 +489,8 @@
let isVertical = event.axis == event.VERTICAL_AXIS;
if (this._prevMouseScrolls.every(function(prev) prev == isVertical))
this.scrollByIndex(this._isRTLScrollbox ? -event.detail : event.detail);
this.scrollByIndex(isVertical && this._isRTLScrollbox ? -event.detail :
event.detail);
if (this._prevMouseScrolls.length > 1)
this._prevMouseScrolls.shift();

View File

@ -94,3 +94,17 @@ html|span.statActivity[seeking] > html|span.statActivitySeeking {
.controlBar[size="small"] .scrubber {
visibility: hidden;
}
/* Error description formatting */
.errorLabel {
display: none;
}
[error="errorAborted"] > [anonid="errorAborted"],
[error="errorNetwork"] > [anonid="errorNetwork"],
[error="errorDecode"] > [anonid="errorDecode"],
[error="errorSrcNotSupported"] > [anonid="errorSrcNotSupported"],
[error="errorNoSource"] > [anonid="errorNoSource"],
[error="errorGeneric"] > [anonid="errorGeneric"] {
display: inline;
}

View File

@ -198,6 +198,12 @@
<stack flex="1">
<vbox flex="1" class="statusOverlay" hidden="true">
<box class="statusIcon"/>
<label class="errorLabel" anonid="errorAborted">&error.aborted;</label>
<label class="errorLabel" anonid="errorNetwork">&error.network;</label>
<label class="errorLabel" anonid="errorDecode">&error.decode;</label>
<label class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</label>
<label class="errorLabel" anonid="errorNoSource">&error.noSource;</label>
<label class="errorLabel" anonid="errorGeneric">&error.generic;</label>
</vbox>
<vbox class="statsOverlay" hidden="true">
@ -412,8 +418,9 @@
this.bufferBar.setAttribute("value", 0);
// Set the current status icon.
if (this.video.error || this.video.networkState == this.video.NETWORK_NO_SOURCE) {
if (this.hasError()) {
this.statusIcon.setAttribute("type", "error");
this.updateErrorText();
this.setupStatusFader(true);
} else {
this.statusIcon.setAttribute("type", "throbber");
@ -452,7 +459,7 @@
// If the video hits an error, suppress controls if it
// hasn't managed to do anything else yet.
if (!this.firstFrameShown && (this.video.error || this.video.networkState == this.video.NETWORK_NO_SOURCE))
if (!this.firstFrameShown && this.hasError())
enabled = false;
return enabled;
@ -514,6 +521,8 @@
break;
case "loadstart":
this.maxCurrentTimeSeen = 0;
this.controlsSpacer.removeAttribute("aria-label");
this.statusOverlay.removeAttribute("error");
this.statusIcon.setAttribute("type", "throbber");
this.isAudioOnly = (this.video instanceof HTMLAudioElement);
this.setPlayButtonState(true);
@ -586,8 +595,9 @@
// 2. The video's networkState is NETWORK_NO_SOURCE. This means we we're
// loading from child source elements, but we were unable to select
// any of the child elements for playback during resource selection.
if (this.video.error || this.video.networkState == this.video.NETWORK_NO_SOURCE) {
if (this.hasError()) {
this.statusIcon.setAttribute("type", "error");
this.updateErrorText();
this.setupStatusFader(true);
// If video hasn't shown anything yet, disable the controls.
if (!this.firstFrameShown)
@ -604,14 +614,54 @@
clearInterval(this.statsInterval);
this.statsInterval = null;
}
for each (var event in this.videoEvents)
for each (let event in this.videoEvents)
this.video.removeEventListener(event, this, false);
this.video.removeEventListener("media-showStatistics", this._handleCustomEventsBound, false);
this.video.ownerDocument.removeEventListener("mozfullscreenchange", this.setFullscreenButtonState, false);
delete this._handleCustomEventsBound;
this.video.ownerDocument.removeEventListener("mozfullscreenchange", this._setFullscreenButtonStateBound, false);
delete this._setFullscreenButtonStateBound;
this.log("--- videocontrols terminated ---");
},
hasError : function () {
return (this.video.error != null || this.video.networkState == this.video.NETWORK_NO_SOURCE);
},
updateErrorText : function () {
let error;
let v = this.video;
// It is possible to have both v.networkState == NETWORK_NO_SOURCE
// as well as v.error being non-null. In this case, we will show
// the v.error.code instead of the v.networkState error.
if (v.error) {
switch (v.error.code) {
case v.error.MEDIA_ERR_ABORTED:
error = "errorAborted";
break;
case v.error.MEDIA_ERR_NETWORK:
error = "errorNetwork";
break;
case v.error.MEDIA_ERR_DECODE:
error = "errorDecode";
break;
case v.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
error = "errorSrcNotSupported";
break;
default:
error = "errorGeneric";
break;
}
} else if (v.networkState == v.NETWORK_NO_SOURCE) {
error = "errorNoSource";
} else {
return; // No error found.
}
let label = document.getAnonymousElementByAttribute(this.videocontrols, "anonid", error);
this.controlsSpacer.setAttribute("aria-label", label.textContent);
this.statusOverlay.setAttribute("error", error);
},
formatTime : function(aTime) {
// Format the duration as "h:mm:ss" or "m:ss"
aTime = Math.round(aTime / 1000);
@ -738,11 +788,43 @@
this.startFade(this.volumeStack, isMouseOver);
},
_hideControlsTimeout : 0,
_hideControlsFn : function () {
if (!Utils.scrubber.isDragging)
Utils.startFade(Utils.controlBar, false);
},
HIDE_CONTROLS_TIMEOUT_MS : 2000,
onMouseMove : function (event) {
// If the controls are static, don't change anything.
if (!this.dynamicControls)
return;
clearTimeout(this._hideControlsTimeout);
// Suppress fading out the controls until the video has rendered
// its first frame. But since autoplay videos start off with no
// controls, let them fade-out so the controls don't get stuck on.
if (!this.firstFrameShown &&
!(this.video.autoplay && this.video.mozAutoplayEnabled))
return;
this.startFade(this.controlBar, true);
// Only hide the controls if the mouse cursor is not left on top of
// the control bar. We only need to check the Y position of the cursor
// since the controls span the width of the video and are always located
// at the bottom of the video.
if (event.clientY < this.controlBar.getBoundingClientRect().top) {
this._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
}
},
onMouseInOut : function (event) {
// If the controls are static, don't change anything.
if (!this.dynamicControls)
return;
clearTimeout(this._hideControlsTimeout);
// Ignore events caused by transitions between child nodes.
// Note that the videocontrols element is the same
// size as the *content area* of the video element,
@ -760,10 +842,13 @@
!(this.video.autoplay && this.video.mozAutoplayEnabled))
return;
if (!isMouseOver)
if (!isMouseOver) {
this.adjustControlSize();
this.startFade(this.controlBar, isMouseOver);
// Setting a timer here to handle the case where the mouse leaves
// the video from hovering over the controls.
this._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
}
},
startFadeIn : function (element, immediate) {
@ -775,11 +860,15 @@
},
startFade : function (element, fadeIn, immediate) {
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
// so our dependent state (eg, timestamp in the thumb) will be stale.
// As a workaround, update it manually when it first becomes unhidden.
if (element.className == "controlBar" && fadeIn && element.hidden)
this.scrubber.valueChanged("curpos", this.video.currentTime * 1000, false);
if (element.className == "controlBar" && fadeIn) {
clearTimeout(this._hideControlsTimeout);
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
// so our dependent state (eg, timestamp in the thumb) will be stale.
// As a workaround, update it manually when it first becomes unhidden.
if (element.hidden)
this.scrubber.valueChanged("curpos", this.video.currentTime * 1000, false);
}
if (immediate)
element.setAttribute("immediate", true);
@ -1184,13 +1273,13 @@
// Use the handleEvent() callback for all media events.
// The "error" event listener must capture, so that it can trap error events
// from the <source> children, which don't bubble.
for each (var event in this.videoEvents)
for each (let event in this.videoEvents)
this.video.addEventListener(event, this, (event == "error") ? true : false);
var self = this;
this.muteButton.addEventListener("command", function() { self.toggleMute(); }, false);
this.playButton.addEventListener("command", function() { self.togglePause(); }, false);
this.controlsSpacer.addEventListener("click", function(e) { if (e.button == 0) { self.togglePause(); } }, false);
this.controlsSpacer.addEventListener("click", function(e) { if (e.button == 0 && !self.hasError()) { self.togglePause(); } }, false);
this.fullscreenButton.addEventListener("command", function() { self.toggleFullscreen(); }, false );
if (!this.isAudioOnly) {
this.muteButton.addEventListener("mouseover", function(e) { self.onVolumeMouseInOut(e); }, false);
@ -1200,7 +1289,8 @@
}
this.videocontrols.addEventListener("transitionend", function(e) { self.onTransitionEnd(e); }, false);
this.video.ownerDocument.addEventListener("mozfullscreenchange", this.setFullscreenButtonState, false);
this._setFullscreenButtonStateBound = this.setFullscreenButtonState.bind(this);
this.video.ownerDocument.addEventListener("mozfullscreenchange", this._setFullscreenButtonStateBound, false);
// Make the <video> element keyboard accessible.
this.video.setAttribute("tabindex", 0);
@ -1224,6 +1314,10 @@
if (!this.isTouchControl)
this.Utils.onMouseInOut(event);
</handler>
<handler event="mousemove">
if (!this.isTouchControl)
this.Utils.onMouseMove(event);
</handler>
</handlers>
</binding>

View File

@ -20,6 +20,13 @@
<!ENTITY stats.framesPresented "Frames presented">
<!ENTITY stats.framesPainted "Frames painted">
<!ENTITY error.aborted "Video loading stopped.">
<!ENTITY error.network "Video playback aborted due to a network error.">
<!ENTITY error.decode "Video can't be played because the file is corrupt.">
<!ENTITY error.srcNotSupported "Video format or MIME type is not supported.">
<!ENTITY error.noSource "Video not found.">
<!ENTITY error.generic "Video playback aborted due to an unknown error.">
<!-- LOCALIZATION NOTE (scrubberScale.nameFormat): the #1 string is the current
media position, and the #2 string is the total duration. For example, when at
the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be

View File

@ -206,7 +206,7 @@ html|td {
padding: 0 2px;
}
html|table {
font-family: Helvetica, Ariel, sans-serif;
font-family: Helvetica, Arial, sans-serif;
font-size: 11px;
color: white;
text-shadow:
@ -245,3 +245,17 @@ html|table {
.statusOverlay[fadeout] {
opacity: 0;
}
/* Error description formatting */
.errorLabel {
font-family: Helvetica, Arial, sans-serif;
font-size: 11px;
color: #bbb;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
padding: 0 10px;
text-align: center;
}

View File

@ -215,7 +215,7 @@ html|td {
padding: 0 2px;
}
html|table {
font-family: Helvetica, Ariel, sans-serif;
font-family: Helvetica, Arial, sans-serif;
font-size: 11px;
color: white;
text-shadow:
@ -254,3 +254,17 @@ html|table {
.statusOverlay[fadeout] {
opacity: 0;
}
/* Error description formatting */
.errorLabel {
font-family: Helvetica, Arial, sans-serif;
font-size: 11px;
color: #bbb;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
padding: 0 10px;
text-align: center;
}