зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to tracemonkey.
This commit is contained in:
Коммит
7d438a554a
|
@ -1179,6 +1179,9 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|||
if (!targetNode)
|
||||
return;
|
||||
|
||||
// Note: For universal/global ARIA states and properties we don't care if
|
||||
// there is an ARIA role present or not.
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_required) {
|
||||
nsRefPtr<nsAccEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
|
@ -1209,6 +1212,24 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|||
return;
|
||||
}
|
||||
|
||||
// For aria drag and drop changes we fire a generic attribute change event;
|
||||
// at least until native API comes up with a more meaningful event.
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_grabbed ||
|
||||
aAttribute == nsAccessibilityAtoms::aria_dropeffect) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
|
||||
targetNode);
|
||||
}
|
||||
|
||||
// We treat aria-expanded as a global ARIA state for historical reasons
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_expanded) {
|
||||
nsRefPtr<nsAccEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
nsIAccessibleStates::STATE_EXPANDED,
|
||||
PR_FALSE);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
|
||||
// We don't care about these other ARIA attribute changes unless there is
|
||||
// an ARIA role set for the element
|
||||
|
@ -1249,15 +1270,6 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_expanded) {
|
||||
nsRefPtr<nsAccEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
nsIAccessibleStates::STATE_EXPANDED,
|
||||
PR_FALSE);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_readonly) {
|
||||
nsRefPtr<nsAccEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
|
@ -1290,14 +1302,6 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|||
nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// For aria drag and drop changes we fire a generic attribute change event;
|
||||
// at least until native API comes up with a more meaningful event.
|
||||
if (aAttribute == nsAccessibilityAtoms::aria_grabbed ||
|
||||
aAttribute == nsAccessibilityAtoms::aria_dropeffect) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
|
||||
targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
||||
|
|
|
@ -48,6 +48,7 @@ include $(topsrcdir)/config/rules.mk
|
|||
_TEST_FILES =\
|
||||
focus.html \
|
||||
scroll.html \
|
||||
test_aria_statechange.html \
|
||||
test_attrs.html \
|
||||
test_caretmove.html \
|
||||
$(warning test_coalescence.html temporarily disabled) \
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>ARIA state change event testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
var gQueue = null;
|
||||
|
||||
function expandNode(aNodeOrID, bExpand)
|
||||
{
|
||||
this.DOMNode = getNode(aNodeOrID);
|
||||
|
||||
this.invoke = function expand_invoke() {
|
||||
// Note: this should fire an EVENT_STATE_CHANGE
|
||||
this.DOMNode.setAttribute("aria-expanded", bExpand);
|
||||
};
|
||||
|
||||
this.check = function expand_check() {
|
||||
testStates(aNodeOrID,
|
||||
bExpand ? STATE_EXPANDED : STATE_COLLAPSED,
|
||||
EXT_STATE_EXPANDABLE);
|
||||
};
|
||||
|
||||
this.getID = function changeValue_getID() {
|
||||
return prettyName(aNodeOrID) + " aria-expanded changed";
|
||||
};
|
||||
}
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
|
||||
|
||||
gQueue.push(new expandNode("section", true));
|
||||
gQueue.push(new expandNode("section", false));
|
||||
gQueue.push(new expandNode("div", true));
|
||||
gQueue.push(new expandNode("div", false));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=551684"
|
||||
title="No statechange event for aria-expanded on native HTML elements, is fired on ARIA widgets">
|
||||
Mozilla Bug 551684
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="eventdump"></div>
|
||||
|
||||
<!-- aria-expanded -->
|
||||
<div id="section" role="section" aria-expanded="false">expandable section</div>
|
||||
<div id="div" aria-expanded="false">expandable native div</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -844,9 +844,6 @@ pref("browser.zoom.siteSpecific", true);
|
|||
// Whether or not to update background tabs to the current zoom level.
|
||||
pref("browser.zoom.updateBackgroundTabs", true);
|
||||
|
||||
// replace newlines with spaces when pasting into <input type="text"> fields
|
||||
pref("editor.singleLine.pasteNewlines", 2);
|
||||
|
||||
// The breakpad report server to link to in about:crashes
|
||||
pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
|
||||
|
||||
|
|
|
@ -44,18 +44,26 @@
|
|||
var tabPreviews = {
|
||||
aspectRatio: 0.5625, // 16:9
|
||||
init: function tabPreviews_init() {
|
||||
if (this._selectedTab)
|
||||
return;
|
||||
this._selectedTab = gBrowser.selectedTab;
|
||||
|
||||
this.width = Math.ceil(screen.availWidth / 5.75);
|
||||
this.height = Math.round(this.width * this.aspectRatio);
|
||||
|
||||
window.addEventListener("unload", this, false);
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", this, false);
|
||||
gBrowser.tabContainer.addEventListener("SSTabRestored", this, false);
|
||||
},
|
||||
uninit: function tabPreviews_uninit() {
|
||||
window.removeEventListener("unload", this, false);
|
||||
gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
|
||||
gBrowser.tabContainer.removeEventListener("SSTabRestored", this, false);
|
||||
this._selectedTab = null;
|
||||
},
|
||||
get: function tabPreviews_get(aTab) {
|
||||
this.init();
|
||||
|
||||
if (aTab.__thumbnail_lastURI &&
|
||||
aTab.__thumbnail_lastURI != aTab.linkedBrowser.currentURI.spec) {
|
||||
aTab.__thumbnail = null;
|
||||
|
@ -64,6 +72,8 @@ var tabPreviews = {
|
|||
return aTab.__thumbnail || this.capture(aTab, !aTab.hasAttribute("busy"));
|
||||
},
|
||||
capture: function tabPreviews_capture(aTab, aStore) {
|
||||
this.init();
|
||||
|
||||
var thumbnail = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
thumbnail.mozOpaque = true;
|
||||
thumbnail.height = this.height;
|
||||
|
@ -105,6 +115,9 @@ var tabPreviews = {
|
|||
case "SSTabRestored":
|
||||
this.capture(event.target, true);
|
||||
break;
|
||||
case "unload":
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -225,6 +238,8 @@ var ctrlTab = {
|
|||
|
||||
init: function ctrlTab_init() {
|
||||
if (!this._recentlyUsedTabs) {
|
||||
tabPreviews.init();
|
||||
|
||||
this._recentlyUsedTabs = [gBrowser.selectedTab];
|
||||
this._init(true);
|
||||
}
|
||||
|
@ -577,6 +592,8 @@ var allTabs = {
|
|||
return;
|
||||
this._initiated = true;
|
||||
|
||||
tabPreviews.init();
|
||||
|
||||
Array.forEach(gBrowser.tabs, function (tab) {
|
||||
this._addPreview(tab);
|
||||
}, this);
|
||||
|
|
|
@ -1307,7 +1307,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
// themselves.
|
||||
gBrowser.addEventListener("command", BrowserOnCommand, false);
|
||||
|
||||
tabPreviews.init();
|
||||
ctrlTab.readPref();
|
||||
gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
|
||||
gPrefService.addObserver(allTabs.prefName, allTabs, false);
|
||||
|
@ -1373,7 +1372,6 @@ function BrowserShutdown()
|
|||
|
||||
gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
|
||||
gPrefService.removeObserver(allTabs.prefName, allTabs);
|
||||
tabPreviews.uninit();
|
||||
ctrlTab.uninit();
|
||||
allTabs.uninit();
|
||||
|
||||
|
@ -7341,6 +7339,10 @@ let gPrivateBrowsingUI = {
|
|||
}
|
||||
}
|
||||
|
||||
if (gURLBar) {
|
||||
gURLBar.editor.transactionManager.clear();
|
||||
}
|
||||
|
||||
document.getElementById("menu_import").removeAttribute("disabled");
|
||||
|
||||
// Re-enable the Clear Recent History... menu item on exit of PB mode
|
||||
|
|
|
@ -100,10 +100,6 @@ function nsContextMenu(aXulMenu, aBrowser) {
|
|||
|
||||
// Prototype for nsContextMenu "class."
|
||||
nsContextMenu.prototype = {
|
||||
// onDestroy is a no-op at this point.
|
||||
onDestroy: function () {
|
||||
},
|
||||
|
||||
// Initialize context menu.
|
||||
initMenu: function CM_initMenu(aPopup, aBrowser) {
|
||||
this.menu = aPopup;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
function test() {
|
||||
var contentWin = window.open("about:blank", "", "width=100,height=100");
|
||||
var enumerator = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let win = enumerator.getNext();
|
||||
|
|
|
@ -100,8 +100,6 @@ function test() {
|
|||
var gBMFolder = null;
|
||||
var gAddedEngines = [];
|
||||
function setupKeywords() {
|
||||
var searchService = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
gBMFolder = Application.bookmarks.menu.addFolder("keyword-test");
|
||||
for each (var item in testData) {
|
||||
var data = item[0];
|
||||
|
@ -113,8 +111,8 @@ function setupKeywords() {
|
|||
}
|
||||
|
||||
if (data instanceof searchKeywordData) {
|
||||
searchService.addEngineWithDetails(data.keyword, "", data.keyword, "", data.method, data.uri.spec);
|
||||
var addedEngine = searchService.getEngineByName(data.keyword);
|
||||
Services.search.addEngineWithDetails(data.keyword, "", data.keyword, "", data.method, data.uri.spec);
|
||||
var addedEngine = Services.search.getEngineByName(data.keyword);
|
||||
if (data.postData) {
|
||||
var [paramName, paramValue] = data.postData.split("=");
|
||||
addedEngine.addParam(paramName, paramValue, null);
|
||||
|
@ -126,8 +124,6 @@ function setupKeywords() {
|
|||
}
|
||||
|
||||
function cleanupKeywords() {
|
||||
var searchService = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
gBMFolder.remove();
|
||||
gAddedEngines.map(searchService.removeEngine);
|
||||
gAddedEngines.map(Services.search.removeEngine);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -8,7 +6,7 @@ function test() {
|
|||
gBrowser.selectedBrowser.addEventListener("load", function () {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
pageInfo = BrowserPageInfo();
|
||||
obs.addObserver(observer, "page-info-dialog-loaded", false);
|
||||
Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
|
||||
}, true);
|
||||
content.location =
|
||||
"https://example.com/browser/browser/base/content/test/feed_tab.html";
|
||||
|
@ -29,7 +27,7 @@ function test() {
|
|||
break;
|
||||
case 2:
|
||||
atTest++;
|
||||
obs.removeObserver(observer, "page-info-dialog-loaded");
|
||||
Services.obs.removeObserver(observer, "page-info-dialog-loaded");
|
||||
testLockDoubleClick();
|
||||
break;
|
||||
}
|
||||
|
@ -76,9 +74,7 @@ function test() {
|
|||
}
|
||||
|
||||
function testLockDoubleClick() {
|
||||
var pageInfoDialogs = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("Browser:page-info");
|
||||
var pageInfoDialogs = Services.wm.getEnumerator("Browser:page-info");
|
||||
var i = 0;
|
||||
while (pageInfoDialogs.hasMoreElements()) {
|
||||
i++;
|
||||
|
|
|
@ -4,8 +4,7 @@ var gTestBrowser = null;
|
|||
var gNextTest = null;
|
||||
|
||||
function get_test_plugin() {
|
||||
var ph = Components.classes["@mozilla.org/plugin/host;1"]
|
||||
.getService(Components.interfaces.nsIPluginHost);
|
||||
var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
var tags = ph.getPluginTags();
|
||||
|
||||
// Find the test plugin
|
||||
|
@ -23,9 +22,7 @@ function WindowOpenListener(url, opencallback, closecallback) {
|
|||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.addListener(this);
|
||||
Services.wm.addListener(this);
|
||||
}
|
||||
|
||||
WindowOpenListener.prototype = {
|
||||
|
@ -61,9 +58,7 @@ WindowOpenListener.prototype = {
|
|||
if (this.window != window)
|
||||
return;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.removeListener(this);
|
||||
Services.wm.removeListener(this);
|
||||
this.opencallback = null;
|
||||
this.window = null;
|
||||
this.domwindow = null;
|
||||
|
|
|
@ -71,13 +71,11 @@ function test()
|
|||
let history = doc.getElementById("history-checkbox");
|
||||
|
||||
// Add download to DB
|
||||
let ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
let file = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
|
||||
file.append("satitize-dm-test.file");
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
let testPath = ios.newFileURI(file).spec;
|
||||
let testPath = Services.io.newFileURI(file).spec;
|
||||
let data = {
|
||||
name: "381603.patch",
|
||||
source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
|
||||
|
@ -142,22 +140,20 @@ function test()
|
|||
db.executeSimpleSQL("DELETE FROM moz_downloads");
|
||||
|
||||
// Close the UI if necessary
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
let win = ww.getWindowByName("Sanatize", null);
|
||||
let win = Services.ww.getWindowByName("Sanatize", null);
|
||||
if (win && (win instanceof Ci.nsIDOMWindowInternal))
|
||||
win.close();
|
||||
|
||||
// Start the test when the sanitize window loads
|
||||
ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
ww.unregisterNotification(arguments.callee);
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
aSubject.QueryInterface(Ci.nsIDOMEventTarget)
|
||||
.addEventListener("DOMContentLoaded", doTest, false);
|
||||
});
|
||||
|
||||
// Let the methods that run onload finish before we test
|
||||
let doTest = function() setTimeout(function() {
|
||||
let win = ww.getWindowByName("Sanitize", null)
|
||||
let win = Services.ww.getWindowByName("Sanitize", null)
|
||||
.QueryInterface(Ci.nsIDOMWindowInternal);
|
||||
|
||||
for (let i = 0; i < tests.length; i++)
|
||||
|
@ -168,7 +164,7 @@ function test()
|
|||
}, 0);
|
||||
|
||||
// Show the UI
|
||||
ww.openWindow(window,
|
||||
Services.ww.openWindow(window,
|
||||
"chrome://browser/content/sanitize.xul",
|
||||
"Sanitize",
|
||||
"chrome,titlebar,centerscreen",
|
||||
|
|
|
@ -51,15 +51,13 @@
|
|||
*/
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Components.interfaces.mozIJSSubScriptLoader).
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://mochikit/content/MochiKit/packed.js");
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Components.interfaces.mozIJSSubScriptLoader).
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://browser/content/sanitize.js");
|
||||
|
||||
const winWatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
const bhist = Cc["@mozilla.org/browser/global-history;2"].
|
||||
|
@ -540,7 +538,7 @@ WindowHelper.prototype = {
|
|||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
winWatch.unregisterNotification(windowObserver);
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
|
||||
var loaded = false;
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
|
@ -597,8 +595,8 @@ WindowHelper.prototype = {
|
|||
});
|
||||
}, false);
|
||||
}
|
||||
winWatch.registerNotification(windowObserver);
|
||||
winWatch.openWindow(null,
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
Services.ww.openWindow(null,
|
||||
"chrome://browser/content/sanitize.xul",
|
||||
"SanitizeDialog",
|
||||
"chrome,titlebar,dialog,centerscreen,modal",
|
||||
|
|
|
@ -50,15 +50,13 @@
|
|||
*/
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Components.interfaces.mozIJSSubScriptLoader).
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://mochikit/content/MochiKit/packed.js");
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Components.interfaces.mozIJSSubScriptLoader).
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://browser/content/sanitize.js");
|
||||
|
||||
const winWatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
const bhist = Cc["@mozilla.org/browser/global-history;2"].
|
||||
|
@ -622,7 +620,7 @@ function openWindow(aOnloadCallback) {
|
|||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
winWatch.unregisterNotification(windowObserver);
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
win.addEventListener("load", function onload(event) {
|
||||
win.removeEventListener("load", onload, false);
|
||||
|
@ -641,8 +639,8 @@ function openWindow(aOnloadCallback) {
|
|||
});
|
||||
}, false);
|
||||
}
|
||||
winWatch.registerNotification(windowObserver);
|
||||
winWatch.openWindow(null,
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
Services.ww.openWindow(null,
|
||||
"chrome://browser/content/sanitize.xul",
|
||||
"Sanitize",
|
||||
"chrome,titlebar,dialog,centerscreen,modal",
|
||||
|
|
|
@ -37,48 +37,24 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
version(170);
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
|
||||
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
|
||||
const NS_APP_BOOKMARKS_50_FILE = "BMarks";
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
var Cr = Components.results;
|
||||
|
||||
function LOG(aMsg) {
|
||||
aMsg = ("*** PLACES TESTS: " + aMsg);
|
||||
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
|
||||
logStringMessage(aMsg);
|
||||
print(aMsg);
|
||||
// Import common head.
|
||||
let (commonFile = do_get_file("../../test_places/head_common.js", false)) {
|
||||
let uri = Services.io.newFileURI(commonFile);
|
||||
Services.scriptloader.loadSubScript(uri.spec, this);
|
||||
}
|
||||
|
||||
var gProfD = do_get_profile();
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
// Put any other stuff relative to this test folder below.
|
||||
|
||||
var dirProvider = {
|
||||
getFile: function(prop, persistent) {
|
||||
persistent.value = true;
|
||||
if (prop == NS_APP_BOOKMARKS_50_FILE) {
|
||||
var bmarks = gProfD.clone();
|
||||
bmarks.append("bookmarks.html");
|
||||
return bmarks;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
|
||||
iid.equals(Ci.nsISupports)) {
|
||||
return this;
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(dirProvider);
|
||||
|
||||
var XULAppInfo = {
|
||||
// Needed by some test that relies on having an app registered.
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "PlacesTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
|
||||
|
@ -91,80 +67,29 @@ var XULAppInfo = {
|
|||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: function QueryInterface(iid) {
|
||||
if (iid.equals(Ci.nsIXULAppInfo) ||
|
||||
iid.equals(Ci.nsIXULRuntime) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
var XULAppInfoFactory = {
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
|
||||
"XULAppInfo", "@mozilla.org/xre/app-info;1",
|
||||
XULAppInfoFactory);
|
||||
|
||||
var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
|
||||
function uri(spec) {
|
||||
return iosvc.newURI(spec, null, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes all bookmarks and checks for correct cleanup
|
||||
*/
|
||||
function remove_all_bookmarks() {
|
||||
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||
getService(Ci.nsINavBookmarksService);
|
||||
// Clear all bookmarks.
|
||||
bs.removeFolderChildren(bs.bookmarksMenuFolder);
|
||||
bs.removeFolderChildren(bs.toolbarFolder);
|
||||
bs.removeFolderChildren(bs.unfiledBookmarksFolder);
|
||||
|
||||
// Check for correct cleanup.
|
||||
dump_table("moz_bookmarks");
|
||||
dump_table("moz_places");
|
||||
check_no_bookmarks()
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that we don't have any bookmark
|
||||
*/
|
||||
function check_no_bookmarks() {
|
||||
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsINavHistoryService);
|
||||
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||
getService(Ci.nsINavBookmarksService);
|
||||
var query = hs.getNewQuery();
|
||||
query.setFolders([bs.toolbarFolder, bs.bookmarksMenuFolder, bs.unfiledBookmarksFolder], 3);
|
||||
var options = hs.getNewQueryOptions();
|
||||
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
|
||||
var result = hs.executeQuery(query, options);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
var cc = root.childCount;
|
||||
// Dump contents if found.
|
||||
for (var i = 0; i < cc ; i++) {
|
||||
var node = root.getChild(i);
|
||||
print("Found unexpected child at " + i + ": " + node.title);
|
||||
}
|
||||
do_check_eq(cc, 0);
|
||||
root.containerOpen = false;
|
||||
}
|
||||
|
||||
let gTestDir = do_get_cwd();
|
||||
const FILENAME_BOOKMARKS_HTML = "bookmarks.html";
|
||||
let backup_date = new Date().toLocaleFormat("%Y-%m-%d");
|
||||
let (backup_date = new Date().toLocaleFormat("%Y-%m-%d")) {
|
||||
const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json";
|
||||
}
|
||||
|
||||
// Smart bookmarks constants.
|
||||
const SMART_BOOKMARKS_VERSION = 2;
|
||||
|
@ -174,179 +99,3 @@ const SMART_BOOKMARKS_ON_MENU = 3; // Takes in count the additional separator.
|
|||
// Default bookmarks constants.
|
||||
const DEFAULT_BOOKMARKS_ON_TOOLBAR = 2;
|
||||
const DEFAULT_BOOKMARKS_ON_MENU = 3;
|
||||
|
||||
/**
|
||||
* Creates a bookmarks.html file in the profile folder from a given source file.
|
||||
*
|
||||
* @param aFilename
|
||||
* Name of the file to copy to the profile folder. This file must
|
||||
* exist in the directory that contains the test files.
|
||||
*
|
||||
* @return nsIFile object for the file.
|
||||
*/
|
||||
function create_bookmarks_html(aFilename) {
|
||||
if (!aFilename)
|
||||
do_throw("you must pass a filename to create_bookmarks_html function");
|
||||
remove_bookmarks_html();
|
||||
let bookmarksHTMLFile = gTestDir.clone();
|
||||
bookmarksHTMLFile.append(aFilename);
|
||||
do_check_true(bookmarksHTMLFile.exists());
|
||||
bookmarksHTMLFile.copyTo(gProfD, FILENAME_BOOKMARKS_HTML);
|
||||
let profileBookmarksHTMLFile = gProfD.clone();
|
||||
profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
|
||||
do_check_true(profileBookmarksHTMLFile.exists());
|
||||
return profileBookmarksHTMLFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bookmarks.html file from the profile folder.
|
||||
*/
|
||||
function remove_bookmarks_html() {
|
||||
let profileBookmarksHTMLFile = gProfD.clone();
|
||||
profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
|
||||
if (profileBookmarksHTMLFile.exists()) {
|
||||
profileBookmarksHTMLFile.remove(false);
|
||||
do_check_false(profileBookmarksHTMLFile.exists());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check bookmarks.html file exists in the profile folder.
|
||||
*
|
||||
* @return nsIFile object for the file.
|
||||
*/
|
||||
function check_bookmarks_html() {
|
||||
let profileBookmarksHTMLFile = gProfD.clone();
|
||||
profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
|
||||
do_check_true(profileBookmarksHTMLFile.exists());
|
||||
return profileBookmarksHTMLFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON backup in the profile folder folder from a given source file.
|
||||
*
|
||||
* @param aFilename
|
||||
* Name of the file to copy to the profile folder. This file must
|
||||
* exist in the directory that contains the test files.
|
||||
*
|
||||
* @return nsIFile object for the file.
|
||||
*/
|
||||
function create_JSON_backup(aFilename) {
|
||||
if (!aFilename)
|
||||
do_throw("you must pass a filename to create_JSON_backup function");
|
||||
remove_all_JSON_backups();
|
||||
let bookmarksBackupDir = gProfD.clone();
|
||||
bookmarksBackupDir.append("bookmarkbackups");
|
||||
if (!bookmarksBackupDir.exists()) {
|
||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
|
||||
do_check_true(bookmarksBackupDir.exists());
|
||||
}
|
||||
let bookmarksJSONFile = gTestDir.clone();
|
||||
bookmarksJSONFile.append(aFilename);
|
||||
do_check_true(bookmarksJSONFile.exists());
|
||||
bookmarksJSONFile.copyTo(bookmarksBackupDir, FILENAME_BOOKMARKS_JSON);
|
||||
let profileBookmarksJSONFile = bookmarksBackupDir.clone();
|
||||
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
|
||||
do_check_true(profileBookmarksJSONFile.exists());
|
||||
return profileBookmarksJSONFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bookmarksbackup dir and all backups from the profile folder.
|
||||
*/
|
||||
function remove_all_JSON_backups() {
|
||||
let bookmarksBackupDir = gProfD.clone();
|
||||
bookmarksBackupDir.append("bookmarkbackups");
|
||||
if (bookmarksBackupDir.exists()) {
|
||||
bookmarksBackupDir.remove(true);
|
||||
do_check_false(bookmarksBackupDir.exists());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a JSON backup file for today exists in the profile folder.
|
||||
*
|
||||
* @return nsIFile object for the file.
|
||||
*/
|
||||
function check_JSON_backup() {
|
||||
let profileBookmarksJSONFile = gProfD.clone();
|
||||
profileBookmarksJSONFile.append("bookmarkbackups");
|
||||
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
|
||||
do_check_true(profileBookmarksJSONFile.exists());
|
||||
return profileBookmarksJSONFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the rows of a table out to the console.
|
||||
*
|
||||
* @param aName
|
||||
* The name of the table or view to output.
|
||||
*/
|
||||
function dump_table(aName)
|
||||
{
|
||||
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsPIPlacesDatabase).
|
||||
DBConnection;
|
||||
let stmt = db.createStatement("SELECT * FROM " + aName);
|
||||
|
||||
dump("\n*** Printing data from " + aName + ":\n");
|
||||
let count = 0;
|
||||
while (stmt.executeStep()) {
|
||||
let columns = stmt.numEntries;
|
||||
|
||||
if (count == 0) {
|
||||
// print the column names
|
||||
for (let i = 0; i < columns; i++)
|
||||
dump(stmt.getColumnName(i) + "\t");
|
||||
dump("\n");
|
||||
}
|
||||
|
||||
// print the row
|
||||
for (let i = 0; i < columns; i++) {
|
||||
switch (stmt.getTypeOfIndex(i)) {
|
||||
case Ci.mozIStorageValueArray.VALUE_TYPE_NULL:
|
||||
dump("NULL\t");
|
||||
break;
|
||||
case Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER:
|
||||
dump(stmt.getInt64(i) + "\t");
|
||||
break;
|
||||
case Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT:
|
||||
dump(stmt.getDouble(i) + "\t");
|
||||
break;
|
||||
case Ci.mozIStorageValueArray.VALUE_TYPE_TEXT:
|
||||
dump(stmt.getString(i) + "\t");
|
||||
break;
|
||||
}
|
||||
}
|
||||
dump("\n");
|
||||
|
||||
count++;
|
||||
}
|
||||
dump("*** There were a total of " + count + " rows of data.\n\n");
|
||||
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
stmt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes any events in the event loop of the main thread.
|
||||
*/
|
||||
function flush_main_thread_events()
|
||||
{
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
while (tm.mainThread.hasPendingEvents())
|
||||
tm.mainThread.processNextEvent(false);
|
||||
}
|
||||
|
||||
// These tests are known to randomly fail due to bug 507790 when database
|
||||
// flushes are active, so we turn off syncing for them.
|
||||
let randomFailingSyncTests = [
|
||||
"test_browserGlue_smartBookmarks.js",
|
||||
];
|
||||
let currentTestFilename = do_get_file(_TEST_FILE[0], true).leafName;
|
||||
if (randomFailingSyncTests.indexOf(currentTestFilename) != -1) {
|
||||
print("Test " + currentTestFilename + " is known random due to bug 507790, disabling PlacesDBFlush component.");
|
||||
let sync = Cc["@mozilla.org/places/sync;1"].getService(Ci.nsIObserver);
|
||||
sync.observe(null, "places-debug-stop-sync", null);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ function run_test() {
|
|||
//var bookmarksFileOld = do_get_file("bookmarks.large.html");
|
||||
var bookmarksFileOld = do_get_file("bookmarks.preplaces.html");
|
||||
// file pointer to a new places-exported json file
|
||||
var jsonFile = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
var jsonFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
jsonFile.append("bookmarks.exported.json");
|
||||
|
||||
// create bookmarks.exported.json
|
||||
|
|
|
@ -90,7 +90,7 @@ function run_test() {
|
|||
stmt.finalize();
|
||||
|
||||
// Export bookmarks
|
||||
var bookmarksFile = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
var bookmarksFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
bookmarksFile.append("bookmarks.exported.html");
|
||||
if (bookmarksFile.exists())
|
||||
bookmarksFile.remove(false);
|
||||
|
|
|
@ -115,7 +115,7 @@ var tests = [
|
|||
data: NSIOBSERVER_DATA_JSON,
|
||||
folderId: null,
|
||||
run: function () {
|
||||
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file.append("this file doesn't exist because nobody created it");
|
||||
try {
|
||||
PlacesUtils.restoreBookmarksFromJSONFile(this.file);
|
||||
|
@ -169,7 +169,7 @@ var tests = [
|
|||
data: NSIOBSERVER_DATA_HTML,
|
||||
folderId: null,
|
||||
run: function () {
|
||||
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file.append("this file doesn't exist because nobody created it");
|
||||
try {
|
||||
importer.importHTMLFromFile(this.file, false);
|
||||
|
@ -223,7 +223,7 @@ var tests = [
|
|||
data: NSIOBSERVER_DATA_HTML_INIT,
|
||||
folderId: null,
|
||||
run: function () {
|
||||
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file.append("this file doesn't exist because nobody created it");
|
||||
try {
|
||||
importer.importHTMLFromFile(this.file, true);
|
||||
|
@ -284,7 +284,7 @@ var tests = [
|
|||
finalTopic: NSIOBSERVER_TOPIC_FAILED,
|
||||
data: NSIOBSERVER_DATA_HTML,
|
||||
run: function () {
|
||||
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this.file.append("this file doesn't exist because nobody created it");
|
||||
this.folderId = bmsvc.createFolder(bmsvc.unfiledBookmarksFolder,
|
||||
"test folder",
|
||||
|
@ -407,7 +407,7 @@ function checkBookmarksExist() {
|
|||
* @return The nsILocalFile
|
||||
*/
|
||||
function createFile(aBasename) {
|
||||
var file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
var file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
file.append(aBasename);
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
|
|
|
@ -104,7 +104,7 @@ function run_test() {
|
|||
// file pointer to legacy bookmarks file
|
||||
var bookmarksFileOld = do_get_file("bookmarks.preplaces.html");
|
||||
// file pointer to a new places-exported bookmarks file
|
||||
var bookmarksFileNew = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
var bookmarksFileNew = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
bookmarksFileNew.append("bookmarks.exported.html");
|
||||
|
||||
// create bookmarks.exported.html
|
||||
|
|
|
@ -47,14 +47,13 @@ const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed";
|
|||
const PREF_DISTRIBUTION_ID = "distribution.id";
|
||||
|
||||
const TOPIC_FINAL_UI_STARTUP = "final-ui-startup";
|
||||
const TOPIC_PLACES_INIT_COMPLETE = "places-init-complete";
|
||||
const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
// Copy distribution.ini file to our app dir.
|
||||
let distroDir = dirSvc.get("XCurProcD", Ci.nsIFile);
|
||||
let distroDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
distroDir.append("distribution");
|
||||
let iniFile = distroDir.clone();
|
||||
iniFile.append("distribution.ini");
|
||||
|
@ -97,13 +96,13 @@ function run_test() {
|
|||
TOPIC_FINAL_UI_STARTUP,
|
||||
null);
|
||||
// Test will continue on customization complete notification.
|
||||
let observer = {
|
||||
let cObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
os.removeObserver(this, TOPIC_CUSTOMIZATION_COMPLETE);
|
||||
continue_test();
|
||||
do_execute_soon(continue_test);
|
||||
}
|
||||
}
|
||||
os.addObserver(observer, TOPIC_CUSTOMIZATION_COMPLETE, false);
|
||||
os.addObserver(cObserver, TOPIC_CUSTOMIZATION_COMPLETE, false);
|
||||
}
|
||||
}
|
||||
os.addObserver(observer, TOPIC_PLACES_INIT_COMPLETE, false);
|
||||
|
@ -145,7 +144,7 @@ function continue_test() {
|
|||
do_register_cleanup(function() {
|
||||
// Remove the distribution file, even if the test failed, otherwise all
|
||||
// next tests will import it.
|
||||
let iniFile = dirSvc.get("XCurProcD", Ci.nsIFile);
|
||||
let iniFile = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
iniFile.append("distribution");
|
||||
iniFile.append("distribution.ini");
|
||||
iniFile.remove(false);
|
||||
|
|
|
@ -63,8 +63,7 @@ const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
|
|||
const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
|
||||
const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
|
||||
const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
|
||||
const TOPIC_PLACES_INIT_COMPLETE = "places-init-complete";
|
||||
const TOPIC_PLACES_DATABASE_LOCKED = "places-database-locked";
|
||||
|
||||
let tests = [];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -16,17 +16,14 @@ function test() {
|
|||
getService(Ci.nsIHandlerService);
|
||||
hserv.store(info);
|
||||
|
||||
var obs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
|
||||
function observer(win, topic, data) {
|
||||
if (topic != "app-handler-pane-loaded")
|
||||
return;
|
||||
|
||||
obs.removeObserver(observer, "app-handler-pane-loaded");
|
||||
Services.obs.removeObserver(observer, "app-handler-pane-loaded");
|
||||
runTest(win);
|
||||
}
|
||||
obs.addObserver(observer, "app-handler-pane-loaded", false);
|
||||
Services.obs.addObserver(observer, "app-handler-pane-loaded", false);
|
||||
|
||||
openDialog("chrome://browser/content/preferences/preferences.xul", "Preferences",
|
||||
"chrome,titlebar,toolbar,centerscreen,dialog=no", "paneApplications");
|
||||
|
|
|
@ -36,28 +36,26 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function runTestOnPrivacyPrefPane(testFunc) {
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
ww.unregisterNotification(this);
|
||||
Services.ww.unregisterNotification(this);
|
||||
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
win.addEventListener("load", function() {
|
||||
win.removeEventListener("load", arguments.callee, false);
|
||||
testFunc(dialog.document.defaultView);
|
||||
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
dialog.close();
|
||||
}, false);
|
||||
} else if (aTopic == "domwindowclosed") {
|
||||
ww.unregisterNotification(this);
|
||||
Services.ww.unregisterNotification(this);
|
||||
testRunner.runNext();
|
||||
}
|
||||
}
|
||||
};
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
let dialog = openDialog("chrome://browser/content/preferences/preferences.xul", "Preferences",
|
||||
"chrome,titlebar,toolbar,centerscreen,dialog=no", "panePrivacy");
|
||||
|
@ -528,10 +526,8 @@ function reset_preferences(win) {
|
|||
|
||||
let testRunner;
|
||||
function run_test_subset(subset) {
|
||||
let psvc = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
let instantApplyOrig = psvc.getBoolPref("browser.preferences.instantApply");
|
||||
psvc.setBoolPref("browser.preferences.instantApply", true);
|
||||
let instantApplyOrig = Services.prefs.getBoolPref("browser.preferences.instantApply");
|
||||
Services.prefs.setBoolPref("browser.preferences.instantApply", true);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -541,7 +537,7 @@ function run_test_subset(subset) {
|
|||
runNext: function() {
|
||||
if (this.counter == this.tests.length) {
|
||||
// cleanup
|
||||
psvc.setBoolPref("browser.preferences.instantApply", instantApplyOrig);
|
||||
Services.prefs.setBoolPref("browser.preferences.instantApply", instantApplyOrig);
|
||||
finish();
|
||||
} else {
|
||||
let self = this;
|
||||
|
|
|
@ -72,6 +72,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_privatebrowsing_transition.js \
|
||||
browser_privatebrowsing_ui.js \
|
||||
browser_privatebrowsing_urlbarfocus.js \
|
||||
browser_privatebrowsing_urlbarundo.js \
|
||||
browser_privatebrowsing_viewsource.js \
|
||||
browser_privatebrowsing_windowtitle.js \
|
||||
browser_privatebrowsing_windowtitle_page.html \
|
||||
|
|
|
@ -57,9 +57,7 @@ function test() {
|
|||
dialogWin.document.documentElement.getButton("cancel").click();
|
||||
}
|
||||
|
||||
Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService)
|
||||
.addObserver(promptObserver, "common-dialog-loaded", false);
|
||||
Services.obs.addObserver(promptObserver, "common-dialog-loaded", false);
|
||||
|
||||
waitForExplicitFinish();
|
||||
let browser1 = gBrowser.getBrowserForTab(gBrowser.addTab());
|
||||
|
@ -150,9 +148,7 @@ function test() {
|
|||
gBrowser.removeTab(gBrowser.tabContainer.lastChild);
|
||||
gBrowser.removeTab(gBrowser.tabContainer.lastChild);
|
||||
|
||||
Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService)
|
||||
.removeObserver(promptObserver, "common-dialog-loaded", false);
|
||||
Services.obs.removeObserver(promptObserver, "common-dialog-loaded", false);
|
||||
finish();
|
||||
}
|
||||
for (let i = 0; i < gBrowser.browsers.length; ++i)
|
||||
|
|
|
@ -63,10 +63,8 @@ function test() {
|
|||
prefetchCert: true,
|
||||
};
|
||||
function testCheckbox() {
|
||||
let obsSvc = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
obsSvc.addObserver(function (aSubject, aTopic, aData) {
|
||||
obsSvc.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
|
||||
ok(win.gCert, "The certificate information should be available now");
|
||||
|
||||
let checkbox = win.document.getElementById("permanent");
|
||||
|
@ -90,10 +88,8 @@ function test() {
|
|||
prefetchCert: true,
|
||||
};
|
||||
function testCheckbox() {
|
||||
let obsSvc = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
obsSvc.addObserver(function (aSubject, aTopic, aData) {
|
||||
obsSvc.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
|
||||
ok(win.gCert, "The certificate information should be available now");
|
||||
|
||||
let checkbox = win.document.getElementById("permanent");
|
||||
|
|
|
@ -42,8 +42,6 @@ function test() {
|
|||
// initialization
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -120,7 +118,7 @@ function test() {
|
|||
function observer(aSubject, aTopic, aData) {
|
||||
isnot(aTopic, "domwindowopened", "The -private-toggle argument should be silent");
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
let tab = gBrowser.selectedTab;
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
|
@ -149,7 +147,7 @@ function test() {
|
|||
|
||||
let newTab = gBrowser.addTab();
|
||||
gBrowser.removeTab(tab);
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
finish();
|
||||
}, true);
|
||||
}, true);
|
||||
|
|
|
@ -42,8 +42,6 @@ function test() {
|
|||
// initialization
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
let cp = Cc["@mozilla.org/embedcomp/cookieprompt-service;1"].
|
||||
getService(Ci.nsICookiePromptService);
|
||||
|
||||
|
@ -54,7 +52,7 @@ function test() {
|
|||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
win.addEventListener("load", function onLoad(event) {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
|
@ -76,7 +74,7 @@ function test() {
|
|||
});
|
||||
}, false);
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
let remember = {};
|
||||
const time = (new Date("Jan 1, 2030")).getTime() / 1000;
|
||||
|
|
|
@ -46,8 +46,6 @@ function test() {
|
|||
getService(Ci.nsIDownloadManager);
|
||||
if (!gDownloadMgr)
|
||||
gDownloadMgr = dm;
|
||||
let iosvc = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
let panel = document.getElementById("download-monitor");
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -159,10 +157,8 @@ function addDownload(dm, aParams)
|
|||
return dl;
|
||||
}
|
||||
|
||||
function createURI(aObj)
|
||||
{
|
||||
let ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
function createURI(aObj) {
|
||||
let ios = Services.io;
|
||||
return (aObj instanceof Ci.nsIFile) ? ios.newFileURI(aObj) :
|
||||
ios.newURI(aObj, null, null);
|
||||
}
|
||||
|
|
|
@ -42,8 +42,6 @@ function test() {
|
|||
// initialization
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Add a history entry.
|
||||
|
@ -59,7 +57,7 @@ function test() {
|
|||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
let organizer = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
organizer.addEventListener("load", function onLoad(event) {
|
||||
organizer.removeEventListener("load", onLoad, false);
|
||||
|
@ -103,8 +101,8 @@ function test() {
|
|||
}, false);
|
||||
}
|
||||
|
||||
ww.registerNotification(observer);
|
||||
ww.openWindow(null,
|
||||
Services.ww.registerNotification(observer);
|
||||
Services.ww.openWindow(null,
|
||||
"chrome://browser/content/places/places.xul",
|
||||
"",
|
||||
"chrome,toolbar=yes,dialog=no,resizable",
|
||||
|
|
|
@ -42,8 +42,6 @@ let cookieManager = Cc["@mozilla.org/cookiemanager;1"].
|
|||
getService(Ci.nsICookieManager2);
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
let _obs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
let observerNotified = 0, firstUnloadFired = 0, secondUnloadFired = 0;
|
||||
|
||||
function pbObserver(aSubject, aTopic, aData) {
|
||||
|
@ -56,7 +54,7 @@ function pbObserver(aSubject, aTopic, aData) {
|
|||
is(firstUnloadFired, 1, "The first unload event should have been processed by now");
|
||||
break;
|
||||
case "exit":
|
||||
_obs.removeObserver(pbObserver, "private-browsing");
|
||||
Services.obs.removeObserver(pbObserver, "private-browsing");
|
||||
observerNotified++;
|
||||
is(observerNotified, 2, "This should be the second notification");
|
||||
is(secondUnloadFired, 1, "The second unload event should have been processed by now");
|
||||
|
@ -66,7 +64,7 @@ function pbObserver(aSubject, aTopic, aData) {
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
_obs.addObserver(pbObserver, "private-browsing", false);
|
||||
Services.obs.addObserver(pbObserver, "private-browsing", false);
|
||||
is(gBrowser.tabs.length, 1, "There should only be one tab");
|
||||
let testTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = testTab;
|
||||
|
|
|
@ -48,9 +48,7 @@ function test() {
|
|||
if (aTopic == "private-browsing")
|
||||
observerData = aData;
|
||||
}
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.addObserver(observer, "private-browsing", false);
|
||||
Services.obs.addObserver(observer, "private-browsing", false);
|
||||
let pbMenuItem = document.getElementById("privateBrowsingItem");
|
||||
// add a new blank tab to ensure the title can be meaningfully compared later
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
@ -92,6 +90,6 @@ function test() {
|
|||
|
||||
// cleanup
|
||||
gBrowser.removeCurrentTab();
|
||||
os.removeObserver(observer, "private-browsing");
|
||||
Services.obs.removeObserver(observer, "private-browsing");
|
||||
gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* ***** 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 Private Browsing Tests.
|
||||
*
|
||||
* 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):
|
||||
* Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
// This test makes sure that the undo history of the URL bar is cleared when
|
||||
// leaving the private browsing mode.
|
||||
|
||||
function test() {
|
||||
// initialization
|
||||
Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
|
||||
// enter private browsing mode
|
||||
pb.privateBrowsingEnabled = true;
|
||||
|
||||
// fill in the URL bar with something
|
||||
gURLBar.value = "some test value";
|
||||
|
||||
ok(gURLBar.editor.transactionManager.numberOfUndoItems > 0,
|
||||
"The undo history for the URL bar should not be empty");
|
||||
|
||||
// leave private browsing mode
|
||||
pb.privateBrowsingEnabled = false;
|
||||
|
||||
is(gURLBar.editor.transactionManager.numberOfUndoItems, 0,
|
||||
"The undo history of the URL bar should be cleared after leaving the private browsing mode");
|
||||
|
||||
// cleanup
|
||||
Services.prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
|
||||
}
|
||||
|
|
@ -50,13 +50,11 @@ function test() {
|
|||
aboutBrowser.addEventListener("load", function () {
|
||||
aboutBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
function observer(aSubject, aTopic, aData) {
|
||||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
win.addEventListener("load", function () {
|
||||
|
@ -71,7 +69,7 @@ function test() {
|
|||
}, true);
|
||||
}, false);
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
openViewSource();
|
||||
|
||||
|
@ -84,14 +82,14 @@ function test() {
|
|||
function observer(aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowclosed") {
|
||||
ok(true, "Entering the private browsing mode should close the view source window");
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
|
||||
step2();
|
||||
}
|
||||
else if (aTopic == "domwindowopened")
|
||||
ok(false, "Entering the private browsing mode should not open any view source window");
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
gBrowser.addTabsProgressListener({
|
||||
onLocationChange: function() {},
|
||||
|
@ -124,7 +122,7 @@ function test() {
|
|||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
win.addEventListener("load", function () {
|
||||
|
@ -139,7 +137,7 @@ function test() {
|
|||
}, true);
|
||||
}, false);
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
openViewSource();
|
||||
}
|
||||
|
@ -151,12 +149,12 @@ function test() {
|
|||
if (aTopic == "domwindowclosed") {
|
||||
ok(true, "Leaving the private browsing mode should close the existing view source window");
|
||||
if (++events == 2)
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
}
|
||||
else if (aTopic == "domwindowopened") {
|
||||
ok(true, "Leaving the private browsing mode should restore the previous view source window");
|
||||
if (++events == 2)
|
||||
ww.unregisterNotification(observer);
|
||||
Services.ww.unregisterNotification(observer);
|
||||
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
win.addEventListener("load", function () {
|
||||
|
@ -177,7 +175,7 @@ function test() {
|
|||
}, false);
|
||||
}
|
||||
}
|
||||
ww.registerNotification(observer);
|
||||
Services.ww.registerNotification(observer);
|
||||
|
||||
// exit private browsing mode
|
||||
pb.privateBrowsingEnabled = false;
|
||||
|
|
|
@ -33,10 +33,7 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
var gSS = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
var gObs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
var gSS = Services.search;
|
||||
|
||||
function observers(aSubject, aTopic, aData) {
|
||||
switch (aData) {
|
||||
|
@ -54,7 +51,7 @@ function observers(aSubject, aTopic, aData) {
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gObs.addObserver(observers, "browser-search-engine-modified", false);
|
||||
Services.obs.addObserver(observers, "browser-search-engine-modified", false);
|
||||
|
||||
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml",
|
||||
Ci.nsISearchEngine.DATA_XML, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC",
|
||||
|
@ -83,6 +80,6 @@ function test4() {
|
|||
ok(engine, "An engine is present.");
|
||||
isnot(engine.name, "Foo", "Current engine reset after removal");
|
||||
|
||||
gObs.removeObserver(observers, "browser-search-engine-modified");
|
||||
Services.obs.removeObserver(observers, "browser-search-engine-modified");
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ function test() {
|
|||
|
||||
searchBar.value = "test";
|
||||
|
||||
var obs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
var ss = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
var ss = Services.search;
|
||||
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
|
@ -26,13 +23,13 @@ function test() {
|
|||
testReturn();
|
||||
break;
|
||||
case "engine-removed":
|
||||
obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml",
|
||||
Ci.nsISearchEngine.DATA_XML, "data:image/x-icon,%00",
|
||||
false);
|
||||
|
|
|
@ -33,8 +33,7 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
let gSS = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
let gSS = Services.search;
|
||||
let gObs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
|
||||
|
@ -51,13 +50,13 @@ function test() {
|
|||
gSS.removeEngine(engine);
|
||||
break;
|
||||
case "engine-removed":
|
||||
gObs.removeObserver(observer, "browser-search-engine-modified");
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
test2();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gObs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-1.xml",
|
||||
Ci.nsISearchEngine.DATA_XML, "data:image/x-icon;%00",
|
||||
false);
|
||||
|
@ -73,13 +72,13 @@ function test2() {
|
|||
gSS.removeEngine(engine);
|
||||
break;
|
||||
case "engine-removed":
|
||||
gObs.removeObserver(observer, "browser-search-engine-modified");
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gObs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-2.xml",
|
||||
Ci.nsISearchEngine.DATA_XML, "data:image/x-icon;%00",
|
||||
false);
|
||||
|
|
|
@ -63,13 +63,11 @@ function test() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
function waitForFileExistence(aMessage, aDoNext) {
|
||||
const TOPIC = "sessionstore-state-write-complete";
|
||||
os.addObserver(function (aSubject, aTopic, aData) {
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
// Remove the observer so we do not leak.
|
||||
os.removeObserver(arguments.callee, TOPIC);
|
||||
Services.obs.removeObserver(arguments.callee, TOPIC);
|
||||
|
||||
// Check that the file exists.
|
||||
ok(getSessionstoreFile().exists(), aMessage);
|
||||
|
|
|
@ -109,9 +109,7 @@ function browserWindowsCount(expected, msg) {
|
|||
if (typeof expected == "number")
|
||||
expected = [expected, expected];
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -172,8 +170,6 @@ function test() {
|
|||
aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
|
||||
}
|
||||
}
|
||||
let observerService = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
|
||||
/**
|
||||
* Helper: Sets prefs as the testsuite requires
|
||||
|
@ -194,7 +190,7 @@ function test() {
|
|||
function setupTestsuite(testFn) {
|
||||
// Register our observers
|
||||
for (let o in observing)
|
||||
observerService.addObserver(observer, o, false);
|
||||
Services.obs.addObserver(observer, o, false);
|
||||
|
||||
// Make the main test window not count as a browser window any longer
|
||||
oldWinType = document.documentElement.getAttribute("windowtype");
|
||||
|
@ -207,7 +203,7 @@ function test() {
|
|||
function cleanupTestsuite(callback) {
|
||||
// Finally remove observers again
|
||||
for (let o in observing)
|
||||
observerService.removeObserver(observer, o, false);
|
||||
Services.obs.removeObserver(observer, o, false);
|
||||
|
||||
// Reset the prefs we touched
|
||||
[
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -39,9 +39,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -74,10 +72,8 @@ function test() {
|
|||
// Wait for the sessionstore.js file to be written before going on.
|
||||
// Note: we don't wait for the complete event, since if asyncCopy fails we
|
||||
// would timeout.
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.addObserver(function (aSubject, aTopic, aData) {
|
||||
os.removeObserver(arguments.callee, aTopic);
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(arguments.callee, aTopic);
|
||||
info("sessionstore.js is being written");
|
||||
executeSoon(continue_test);
|
||||
}, "sessionstore-state-write", false);
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -39,7 +39,6 @@ function test() {
|
|||
|
||||
// test setup
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
waitForExplicitFinish();
|
||||
|
||||
let uniqueName = "bug 448741";
|
||||
|
@ -71,7 +70,7 @@ function test() {
|
|||
|
||||
ok(valueWasCleaned, "found and removed the specific tab value");
|
||||
aSubject.data = uneval(state);
|
||||
os.removeObserver(cleaningObserver, aTopic, false);
|
||||
Services.obs.removeObserver(cleaningObserver, aTopic, false);
|
||||
}
|
||||
|
||||
// make sure that all later observers don't see that value any longer
|
||||
|
@ -82,15 +81,15 @@ function test() {
|
|||
|
||||
// clean up
|
||||
gBrowser.removeTab(tab);
|
||||
os.removeObserver(checkingObserver, aTopic, false);
|
||||
Services.obs.removeObserver(checkingObserver, aTopic, false);
|
||||
if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
|
||||
gPrefService.clearUserPref("browser.sessionstore.interval");
|
||||
finish();
|
||||
}
|
||||
|
||||
// last added observers are invoked first
|
||||
os.addObserver(checkingObserver, "sessionstore-state-write", false);
|
||||
os.addObserver(cleaningObserver, "sessionstore-state-write", false);
|
||||
Services.obs.addObserver(checkingObserver, "sessionstore-state-write", false);
|
||||
Services.obs.addObserver(cleaningObserver, "sessionstore-state-write", false);
|
||||
|
||||
// trigger an immediate save operation
|
||||
gPrefService.setIntPref("browser.sessionstore.interval", 0);
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -53,10 +51,6 @@ function test() {
|
|||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -98,7 +92,7 @@ function test() {
|
|||
break;
|
||||
|
||||
case "domwindowclosed":
|
||||
ww.unregisterNotification(windowObserver);
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
// Use executeSoon to ensure this happens after SS observer.
|
||||
executeSoon(function () {
|
||||
is(ss.getClosedWindowCount(),
|
||||
|
@ -110,8 +104,8 @@ function test() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
ww.registerNotification(windowObserver);
|
||||
ww.openWindow(null,
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
Services.ww.openWindow(null,
|
||||
location,
|
||||
"_blank",
|
||||
"chrome,all,dialog=no",
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -52,7 +50,6 @@ function test() {
|
|||
|
||||
// test setup
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -91,6 +88,6 @@ function test() {
|
|||
}, true);
|
||||
},true);
|
||||
|
||||
let referrerURI = ioService.newURI(REFERRER1, null, null);
|
||||
let referrerURI = Services.io.newURI(REFERRER1, null, null);
|
||||
browser.loadURI("http://example.org", referrerURI, null);
|
||||
}
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -36,9 +36,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
|
||||
function browserWindowsCount() {
|
||||
let count = 0;
|
||||
let e = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -53,8 +51,6 @@ function test() {
|
|||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
let uniqKey = "bug524745";
|
||||
let uniqVal = Date.now();
|
||||
|
||||
|
|
|
@ -41,15 +41,11 @@ function test() {
|
|||
// test setup
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
waitForExplicitFinish();
|
||||
|
||||
function browserWindowsCount(expected) {
|
||||
let count = 0;
|
||||
let e = wm.getEnumerator("navigator:browser");
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
@ -87,7 +83,7 @@ function test() {
|
|||
|
||||
// let the first window be focused (see above)
|
||||
function pollMostRecentWindow() {
|
||||
if (wm.getMostRecentWindow("navigator:browser") == window) {
|
||||
if (Services.wm.getMostRecentWindow("navigator:browser") == window) {
|
||||
ss.setBrowserState(oldState);
|
||||
} else {
|
||||
info("waiting for the current window to become active");
|
||||
|
@ -100,11 +96,11 @@ function test() {
|
|||
else {
|
||||
browserWindowsCount(1);
|
||||
ok(!window.closed, "Restoring the old state should have left this window open");
|
||||
os.removeObserver(observer, "sessionstore-browser-state-restored");
|
||||
Services.obs.removeObserver(observer, "sessionstore-browser-state-restored");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
os.addObserver(observer, "sessionstore-browser-state-restored", false);
|
||||
Services.obs.addObserver(observer, "sessionstore-browser-state-restored", false);
|
||||
|
||||
// set browser to test state
|
||||
ss.setBrowserState(JSON.stringify(testState));
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
|
||||
|
||||
function browserWindowsCount(expected) {
|
||||
var count = 0;
|
||||
var e = wm.getEnumerator("navigator:browser");
|
||||
var e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
|
|
|
@ -180,6 +180,12 @@ PreviewController.prototype = {
|
|||
this.linkedBrowser.removeEventListener("pageshow", this, false);
|
||||
this.linkedBrowser.removeEventListener("DOMTitleChanged", this, false);
|
||||
this.linkedBrowser.removeEventListener("MozAfterPaint", this, false);
|
||||
|
||||
// Break cycles, otherwise we end up leaking the window with everything
|
||||
// attached to it.
|
||||
delete this.win;
|
||||
delete this.preview;
|
||||
delete this.dirtyRegion;
|
||||
},
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
|
@ -383,7 +389,6 @@ function TabWindow(win) {
|
|||
this.tabbrowser.tabContainer.addEventListener(this.events[i], this, false);
|
||||
this.tabbrowser.addTabsProgressListener(this);
|
||||
|
||||
|
||||
AeroPeek.windows.push(this);
|
||||
let tabs = this.tabbrowser.tabs;
|
||||
for (let i = 0; i < tabs.length; i++)
|
||||
|
@ -402,6 +407,8 @@ TabWindow.prototype = {
|
|||
|
||||
let tabs = this.tabbrowser.tabs;
|
||||
|
||||
this.tabbrowser.removeTabsProgressListener(this);
|
||||
|
||||
for (let i = 0; i < this.events.length; i++)
|
||||
this.tabbrowser.tabContainer.removeEventListener(this.events[i], this, false);
|
||||
|
||||
|
@ -589,6 +596,17 @@ var AeroPeek = {
|
|||
this.enabled = this._prefenabled = this.prefs.getBoolPref(TOGGLE_PREF_NAME);
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
this._enabled = false;
|
||||
|
||||
this.prefs.removeObserver(TOGGLE_PREF_NAME, this);
|
||||
this.prefs.removeObserver(DISABLE_THRESHOLD_PREF_NAME, this);
|
||||
this.prefs.removeObserver(CACHE_EXPIRATION_TIME_PREF_NAME, this);
|
||||
|
||||
if (this.cacheTimer)
|
||||
this.cacheTimer.cancel();
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
},
|
||||
|
@ -636,7 +654,10 @@ var AeroPeek = {
|
|||
return;
|
||||
|
||||
win.gTaskbarTabGroup.destroy();
|
||||
win.gTaskbarTabGroup = null;
|
||||
delete win.gTaskbarTabGroup;
|
||||
|
||||
if (this.windows.length == 0)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
resetCacheTimer: function () {
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
|
||||
// This listens for the next opened window and checks it is of the right url.
|
||||
// opencallback is called when the new window is fully loaded
|
||||
// closecallback is called when the window is closed
|
||||
|
@ -9,9 +6,7 @@ function WindowOpenListener(url, opencallback, closecallback) {
|
|||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
|
||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
wm.addListener(this);
|
||||
Services.wm.addListener(this);
|
||||
}
|
||||
|
||||
WindowOpenListener.prototype = {
|
||||
|
@ -47,9 +42,7 @@ WindowOpenListener.prototype = {
|
|||
if (this.window != window)
|
||||
return;
|
||||
|
||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
wm.removeListener(this);
|
||||
Services.wm.removeListener(this);
|
||||
this.opencallback = null;
|
||||
this.window = null;
|
||||
this.domwindow = null;
|
||||
|
|
|
@ -7,12 +7,11 @@ function test() {
|
|||
}
|
||||
|
||||
// ensure that we don't accidentally quit
|
||||
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.addObserver(quitRequestObserver, "quit-application-requested", false);
|
||||
Services.obs.addObserver(quitRequestObserver, "quit-application-requested", false);
|
||||
|
||||
ok(!Application.quit(), "Tried to quit - and didn't succeed");
|
||||
ok(!Application.restart(), "Tried to restart - and didn't succeed");
|
||||
|
||||
// clean up
|
||||
os.removeObserver(quitRequestObserver, "quit-application-requested", false);
|
||||
Services.obs.removeObserver(quitRequestObserver, "quit-application-requested", false);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
|
||||
var gLastFolderAction = "";
|
||||
var gLastBookmarkAction = "";
|
||||
|
||||
function url(spec) {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
return ios.newURI(spec, null, null);
|
||||
return Services.io.newURI(spec, null, null);
|
||||
}
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -132,7 +132,7 @@ GCONF_VERSION=1.2.1
|
|||
GIO_VERSION=2.0
|
||||
STARTUP_NOTIFICATION_VERSION=0.8
|
||||
DBUS_VERSION=0.60
|
||||
SQLITE_VERSION=3.6.23
|
||||
SQLITE_VERSION=3.6.23.1
|
||||
LIBNOTIFY_VERSION=0.4
|
||||
|
||||
MSMANIFEST_TOOL=
|
||||
|
@ -6595,6 +6595,13 @@ if test $MOZ_PLATFORM_MAEMO; then
|
|||
if test -z "$_LIB_FOUND"; then
|
||||
AC_MSG_ERROR([Hildon FM-2 is required when building for Maemo])
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES(LIBLOCATION,liblocation, _LIB_FOUND=1, _LIB_FOUND=)
|
||||
MOZ_PLATFORM_MAEMO_LIBS="$MOZ_PLATFORM_MAEMO_LIBS $LIBLOCATION_LIBS"
|
||||
MOZ_PLATFORM_MAEMO_CFLAGS="$MOZ_PLATFORM_MAEMO_CFLAGS $LIBLOCATION_CFLAGS"
|
||||
if test -z "$_LIB_FOUND"; then
|
||||
AC_MSG_ERROR([liblocation is required when building for Maemo])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_PLATFORM_MAEMO_LIBS)
|
||||
|
|
|
@ -310,12 +310,12 @@ nsICanvasRenderingContextWebGL_TexSubImage2D(JSContext *cx, uintN argc, jsval *v
|
|||
if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
|
||||
return JS_FALSE;
|
||||
|
||||
if (argc < 7 || (argc > 7 && argc < 9))
|
||||
if (argc < 7)
|
||||
return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
|
||||
|
||||
jsval *argv = JS_ARGV(cx, vp);
|
||||
|
||||
int32 intargs[8];
|
||||
int32 intargs[9];
|
||||
|
||||
// convert the first six args, they must be ints
|
||||
for (jsuint i = 0; i < 6; ++i) {
|
||||
|
|
|
@ -788,7 +788,10 @@ nsContentEventHandler::OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent)
|
|||
nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
|
||||
|
||||
nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
|
||||
if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame) {
|
||||
if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame ||
|
||||
!targetFrame->GetContent() ||
|
||||
!nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(),
|
||||
mRootContent)) {
|
||||
// there is no character at the point.
|
||||
aEvent->mReply.mOffset = nsQueryContentEvent::NOT_FOUND;
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#if !defined(nsHTMLAudioElement_h__)
|
||||
#define nsHTMLAudioElement_h__
|
||||
|
||||
#include "nsIDOMHTMLAudioElement.h"
|
||||
#include "nsIJSNativeInitializer.h"
|
||||
#include "nsHTMLMediaElement.h"
|
||||
|
@ -74,3 +77,5 @@ public:
|
|||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#if !defined(nsHTMLMediaElement_h__)
|
||||
#define nsHTMLMediaElement_h__
|
||||
|
||||
#include "nsIDOMHTMLMediaElement.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsMediaDecoder.h"
|
||||
|
@ -538,3 +541,5 @@ protected:
|
|||
|
||||
nsRefPtr<gfxASurface> mPrintSurface;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#if !defined(nsHTMLVideoElement_h__)
|
||||
#define nsHTMLVideoElement_h__
|
||||
|
||||
#include "nsIDOMHTMLVideoElement.h"
|
||||
#include "nsHTMLMediaElement.h"
|
||||
|
||||
|
@ -76,3 +79,5 @@ public:
|
|||
// If there is no video frame, returns the given default size.
|
||||
nsIntSize GetVideoSize(nsIntSize defaultSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#include "nsLayoutUtils.h"
|
||||
#include "nsVideoFrame.h"
|
||||
#include "BasicLayers.h"
|
||||
#include <limits>
|
||||
|
||||
#ifdef MOZ_OGG
|
||||
#include "nsOggDecoder.h"
|
||||
|
@ -819,7 +820,7 @@ NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(float aCurrentTime)
|
|||
/* readonly attribute float duration; */
|
||||
NS_IMETHODIMP nsHTMLMediaElement::GetDuration(float *aDuration)
|
||||
{
|
||||
*aDuration = mDecoder ? mDecoder->GetDuration() : 0.0;
|
||||
*aDuration = mDecoder ? mDecoder->GetDuration() : std::numeric_limits<float>::quiet_NaN();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1849,7 +1849,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
|
|||
if (!IsHTML() || mDisableDocWrite) {
|
||||
// No calling document.open() on XHTML
|
||||
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
PRBool loadAsHtml5 = nsHtml5Module::sEnabled;
|
||||
|
@ -2099,7 +2099,7 @@ nsHTMLDocument::Close()
|
|||
if (!IsHTML()) {
|
||||
// No calling document.close() on XHTML!
|
||||
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -2164,7 +2164,7 @@ nsHTMLDocument::WriteCommon(const nsAString& aText,
|
|||
if (!IsHTML() || mDisableDocWrite) {
|
||||
// No calling document.write*() on XHTML!
|
||||
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
|
|
@ -134,7 +134,6 @@ public:
|
|||
nsIContent* aContent);
|
||||
|
||||
nsresult AddText(const nsAString& aString);
|
||||
nsresult AddTextToContent(nsIContent* aContent, const nsAString& aText);
|
||||
nsresult FlushText();
|
||||
|
||||
void ProcessBaseTag(nsIContent* aContent);
|
||||
|
@ -763,26 +762,6 @@ nsHTMLFragmentContentSink::AddText(const nsAString& aString)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLFragmentContentSink::AddTextToContent(nsIContent* aContent, const nsAString& aText) {
|
||||
NS_ASSERTION(aContent !=nsnull, "can't add text w/o a content");
|
||||
|
||||
nsresult result=NS_OK;
|
||||
|
||||
if(aContent) {
|
||||
if (!aText.IsEmpty()) {
|
||||
nsCOMPtr<nsIContent> text;
|
||||
result = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager);
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
text->SetText(aText, PR_TRUE);
|
||||
|
||||
result = aContent->AppendChildTo(text, PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLFragmentContentSink::FlushText()
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ function test() {
|
|||
document.open();
|
||||
is(0, 1, "document.open succeeded");
|
||||
} catch (e) {
|
||||
is (e.code, DOMException.INVALID_ACCESS_ERR,
|
||||
is (e.code, DOMException.INVALID_STATE_ERR,
|
||||
"Wrong exception from document.open");
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,15 @@ function test() {
|
|||
document.write("aaa");
|
||||
is(0, 1, "document.write succeeded");
|
||||
} catch (e) {
|
||||
is (e.code, DOMException.INVALID_ACCESS_ERR,
|
||||
is (e.code, DOMException.INVALID_STATE_ERR,
|
||||
"Wrong exception from document.write");
|
||||
}
|
||||
|
||||
try {
|
||||
document.writeln("aaa");
|
||||
is(0, 1, "document.write succeeded");
|
||||
} catch (e) {
|
||||
is (e.code, DOMException.INVALID_STATE_ERR,
|
||||
"Wrong exception from document.write");
|
||||
}
|
||||
|
||||
|
@ -44,7 +52,7 @@ function test() {
|
|||
document.close();
|
||||
is(0, 1, "document.close succeeded");
|
||||
} catch (e) {
|
||||
is (e.code, DOMException.INVALID_ACCESS_ERR,
|
||||
is (e.code, DOMException.INVALID_STATE_ERR,
|
||||
"Wrong exception from document.close");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
extern "C" {
|
||||
#include "sydneyaudio/sydney_audio.h"
|
||||
}
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
using mozilla::TimeStamp;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gAudioStreamLog = nsnull;
|
||||
|
@ -68,7 +71,8 @@ nsAudioStream::nsAudioStream() :
|
|||
mAudioHandle(0),
|
||||
mRate(0),
|
||||
mChannels(0),
|
||||
mFormat(FORMAT_S16_LE)
|
||||
mFormat(FORMAT_S16_LE),
|
||||
mPaused(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -110,13 +114,14 @@ void nsAudioStream::Shutdown()
|
|||
mAudioHandle = nsnull;
|
||||
}
|
||||
|
||||
void nsAudioStream::Write(const void* aBuf, PRUint32 aCount)
|
||||
void nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aCount % mChannels == 0,
|
||||
"Buffer size must be divisible by channel count");
|
||||
NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
|
||||
|
||||
PRUint32 offset = mBufferOverflow.Length();
|
||||
PRInt32 count = aCount + offset;
|
||||
PRUint32 count = aCount + offset;
|
||||
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
@ -168,21 +173,28 @@ void nsAudioStream::Write(const void* aBuf, PRUint32 aCount)
|
|||
}
|
||||
}
|
||||
|
||||
PRInt32 available = Available();
|
||||
if (!aBlocking) {
|
||||
// We're running in non-blocking mode, crop the data to the amount
|
||||
// which is available in the audio buffer, and save the rest for
|
||||
// subsequent calls.
|
||||
PRUint32 available = Available();
|
||||
if (available < count) {
|
||||
mBufferOverflow.AppendElements(s_data.get() + available, (count - available));
|
||||
count = available;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
|
||||
s_data.get(), count * sizeof(short)) != SA_SUCCESS) {
|
||||
s_data.get(),
|
||||
count * sizeof(short)) != SA_SUCCESS)
|
||||
{
|
||||
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 nsAudioStream::Available()
|
||||
PRUint32 nsAudioStream::Available()
|
||||
{
|
||||
// If the audio backend failed to open, lie and say we'll accept some
|
||||
// data.
|
||||
|
@ -225,7 +237,7 @@ void nsAudioStream::Pause()
|
|||
{
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
mPaused = PR_TRUE;
|
||||
sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
|
||||
}
|
||||
|
||||
|
@ -233,14 +245,14 @@ void nsAudioStream::Resume()
|
|||
{
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
mPaused = PR_FALSE;
|
||||
sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
|
||||
}
|
||||
|
||||
float nsAudioStream::GetPosition()
|
||||
PRInt64 nsAudioStream::GetPosition()
|
||||
{
|
||||
if (!mAudioHandle)
|
||||
return -1.0;
|
||||
return -1;
|
||||
|
||||
sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
|
||||
#if defined(XP_WIN)
|
||||
|
@ -249,9 +261,9 @@ float nsAudioStream::GetPosition()
|
|||
PRInt64 position = 0;
|
||||
if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
|
||||
positionType, &position) == SA_SUCCESS) {
|
||||
return (position / float(mRate) / mChannels / sizeof(short));
|
||||
return ((1000 * position) / mRate / mChannels / sizeof(short));
|
||||
}
|
||||
|
||||
return -1.0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,11 +76,14 @@ class nsAudioStream
|
|||
// Write sound data to the audio hardware. aBuf is an array of samples in
|
||||
// the format specified by mFormat of length aCount. aCount should be
|
||||
// evenly divisible by the number of channels in this audio stream.
|
||||
void Write(const void* aBuf, PRUint32 aCount);
|
||||
// When aBlocking is PR_TRUE, we'll block until the write has completed,
|
||||
// otherwise we'll buffer any data we can't write immediately, and write
|
||||
// it in a later call.
|
||||
void Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
|
||||
|
||||
// Return the number of sound samples that can be written to the audio device
|
||||
// without blocking.
|
||||
PRInt32 Available();
|
||||
PRUint32 Available();
|
||||
|
||||
// Set the current volume of the audio playback. This is a value from
|
||||
// 0 (meaning muted) to 1 (meaning full volume).
|
||||
|
@ -95,9 +98,12 @@ class nsAudioStream
|
|||
// Resume audio playback
|
||||
void Resume();
|
||||
|
||||
// Return the position in seconds of the sample being played by the
|
||||
// Return the position in milliseconds of the sample being played by the
|
||||
// audio hardware.
|
||||
float GetPosition();
|
||||
PRInt64 GetPosition();
|
||||
|
||||
// Returns PR_TRUE when the audio stream is paused.
|
||||
PRBool IsPaused() { return mPaused; }
|
||||
|
||||
private:
|
||||
double mVolume;
|
||||
|
@ -112,5 +118,8 @@ class nsAudioStream
|
|||
// backend, the remaining samples are stored in this variable. They
|
||||
// will be written on the next Write() request.
|
||||
nsTArray<short> mBufferOverflow;
|
||||
|
||||
// PR_TRUE if this audio stream is paused.
|
||||
PRPackedBool mPaused;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -49,11 +49,15 @@ LIBXUL_LIBRARY = 1
|
|||
EXPORTS += \
|
||||
nsChannelReader.h \
|
||||
nsOggDecoder.h \
|
||||
nsOggCodecState.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsChannelReader.cpp \
|
||||
nsOggDecoder.cpp \
|
||||
nsOggCodecState.cpp \
|
||||
nsOggReader.cpp \
|
||||
nsOggPlayStateMachine.cpp \
|
||||
$(NULL)
|
||||
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** 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 code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Double <chris.double@double.co.nz>
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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 ***** */
|
||||
#include "nsDebug.h"
|
||||
#include "nsOggCodecState.h"
|
||||
#include "nsOggDecoder.h"
|
||||
#include <string.h>
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsOggHacks.h"
|
||||
|
||||
/*
|
||||
The maximum height and width of the video. Used for
|
||||
sanitizing the memory allocation of the RGB buffer.
|
||||
The maximum resolution we anticipate encountering in the
|
||||
wild is 2160p - 3840x2160 pixels.
|
||||
*/
|
||||
#define MAX_VIDEO_WIDTH 4000
|
||||
#define MAX_VIDEO_HEIGHT 3000
|
||||
|
||||
// Adds two 64bit numbers, retuns PR_TRUE if addition succeeded, or PR_FALSE
|
||||
// if addition would result in an overflow.
|
||||
static PRBool AddOverflow(PRInt64 a, PRInt64 b, PRInt64& aResult);
|
||||
|
||||
// 64 bit integer multiplication with overflow checking. Returns PR_TRUE
|
||||
// if the multiplication was successful, or PR_FALSE if the operation resulted
|
||||
// in an integer overflow.
|
||||
static PRBool MulOverflow(PRInt64 a, PRInt64 b, PRInt64& aResult);
|
||||
|
||||
// Defined in nsOggReader.cpp.
|
||||
extern PRBool MulOverflow32(PRUint32 a, PRUint32 b, PRUint32& aResult);
|
||||
|
||||
|
||||
nsOggCodecState*
|
||||
nsOggCodecState::Create(ogg_page* aPage)
|
||||
{
|
||||
nsAutoPtr<nsOggCodecState> codecState;
|
||||
if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
|
||||
codecState = new nsTheoraState(aPage);
|
||||
} else if (aPage->body_len > 6 && memcmp(aPage->body+1, "vorbis", 6) == 0) {
|
||||
codecState = new nsVorbisState(aPage);
|
||||
} else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
|
||||
codecState = new nsSkeletonState(aPage);
|
||||
} else {
|
||||
codecState = new nsOggCodecState(aPage);
|
||||
}
|
||||
return codecState->nsOggCodecState::Init() ? codecState.forget() : nsnull;
|
||||
}
|
||||
|
||||
nsOggCodecState::nsOggCodecState(ogg_page* aBosPage) :
|
||||
mPacketCount(0),
|
||||
mSerial(ogg_page_serialno(aBosPage)),
|
||||
mActive(PR_FALSE),
|
||||
mDoneReadingHeaders(PR_FALSE)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsOggCodecState);
|
||||
memset(&mState, 0, sizeof(ogg_stream_state));
|
||||
}
|
||||
|
||||
nsOggCodecState::~nsOggCodecState() {
|
||||
MOZ_COUNT_DTOR(nsOggCodecState);
|
||||
int ret = ogg_stream_clear(&mState);
|
||||
NS_ASSERTION(ret == 0, "ogg_stream_clear failed");
|
||||
}
|
||||
|
||||
nsresult nsOggCodecState::Reset() {
|
||||
if (ogg_stream_reset(&mState) != 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mBuffer.Erase();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool nsOggCodecState::Init() {
|
||||
int ret = ogg_stream_init(&mState, mSerial);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
void nsPageQueue::Append(ogg_page* aPage) {
|
||||
ogg_page* p = new ogg_page();
|
||||
p->header_len = aPage->header_len;
|
||||
p->body_len = aPage->body_len;
|
||||
p->header = new unsigned char[p->header_len + p->body_len];
|
||||
p->body = p->header + p->header_len;
|
||||
memcpy(p->header, aPage->header, p->header_len);
|
||||
memcpy(p->body, aPage->body, p->body_len);
|
||||
nsDeque::Push(p);
|
||||
}
|
||||
|
||||
PRBool nsOggCodecState::PageInFromBuffer() {
|
||||
if (mBuffer.IsEmpty())
|
||||
return PR_FALSE;
|
||||
ogg_page *p = mBuffer.PeekFront();
|
||||
int ret = ogg_stream_pagein(&mState, p);
|
||||
NS_ENSURE_TRUE(ret == 0, PR_FALSE);
|
||||
mBuffer.PopFront();
|
||||
delete p->header;
|
||||
delete p;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsTheoraState::nsTheoraState(ogg_page* aBosPage) :
|
||||
nsOggCodecState(aBosPage),
|
||||
mSetup(0),
|
||||
mCtx(0),
|
||||
mFrameDuration(0),
|
||||
mFrameRate(0),
|
||||
mAspectRatio(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsTheoraState);
|
||||
th_info_init(&mInfo);
|
||||
th_comment_init(&mComment);
|
||||
}
|
||||
|
||||
nsTheoraState::~nsTheoraState() {
|
||||
MOZ_COUNT_DTOR(nsTheoraState);
|
||||
th_setup_free(mSetup);
|
||||
th_decode_free(mCtx);
|
||||
th_comment_clear(&mComment);
|
||||
th_info_clear(&mInfo);
|
||||
}
|
||||
|
||||
PRBool nsTheoraState::Init() {
|
||||
if (!mActive)
|
||||
return PR_FALSE;
|
||||
mCtx = th_decode_alloc(&mInfo, mSetup);
|
||||
if (mCtx == NULL) {
|
||||
return mActive = PR_FALSE;
|
||||
}
|
||||
|
||||
PRUint32 n = mInfo.fps_numerator;
|
||||
PRUint32 d = mInfo.fps_denominator;
|
||||
|
||||
mFrameRate = (n == 0 || d == 0) ?
|
||||
0.0 : static_cast<float>(n) / static_cast<float>(d);
|
||||
|
||||
PRUint32 c;
|
||||
if (!MulOverflow32(1000, d, c)) {
|
||||
return mActive = PR_FALSE;
|
||||
}
|
||||
mFrameDuration = c / n;
|
||||
|
||||
n = mInfo.aspect_numerator;
|
||||
d = mInfo.aspect_denominator;
|
||||
mAspectRatio = (n == 0 || d == 0) ?
|
||||
1.0 : static_cast<float>(n) / static_cast<float>(d);
|
||||
|
||||
// Ensure the frame isn't larger than our prescribed maximum.
|
||||
PRUint32 pixels;
|
||||
if (!MulOverflow32(mInfo.pic_width, mInfo.pic_height, pixels) ||
|
||||
pixels > MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT ||
|
||||
pixels == 0)
|
||||
{
|
||||
return mActive = PR_FALSE;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsTheoraState::DecodeHeader(ogg_packet* aPacket)
|
||||
{
|
||||
mPacketCount++;
|
||||
int ret = th_decode_headerin(&mInfo,
|
||||
&mComment,
|
||||
&mSetup,
|
||||
aPacket);
|
||||
|
||||
// We must determine when we've read the last header packet.
|
||||
// th_decode_headerin() does not tell us when it's read the last header, so
|
||||
// we must keep track of the headers externally.
|
||||
//
|
||||
// There are 3 header packets, the Identification, Comment, and Setup
|
||||
// headers, which must be in that order. If they're out of order, the file
|
||||
// is invalid. If we've successfully read a header, and it's the setup
|
||||
// header, then we're done reading headers. The first byte of each packet
|
||||
// determines it's type as follows:
|
||||
// 0x80 -> Identification header
|
||||
// 0x81 -> Comment header
|
||||
// 0x82 -> Setup header
|
||||
// See http://www.theora.org/doc/Theora.pdf Chapter 6, "Bitstream Headers",
|
||||
// for more details of the Ogg/Theora containment scheme.
|
||||
PRBool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x82;
|
||||
if (ret < 0 || mPacketCount > 3) {
|
||||
// We've received an error, or the first three packets weren't valid
|
||||
// header packets, assume bad input, and don't activate the bitstream.
|
||||
mDoneReadingHeaders = PR_TRUE;
|
||||
} else if (ret > 0 && isSetupHeader && mPacketCount == 3) {
|
||||
// Successfully read the three header packets.
|
||||
mDoneReadingHeaders = PR_TRUE;
|
||||
mActive = PR_TRUE;
|
||||
}
|
||||
return mDoneReadingHeaders;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsTheoraState::Time(PRInt64 granulepos) {
|
||||
if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
|
||||
return -1;
|
||||
}
|
||||
PRInt64 t = 0;
|
||||
PRInt64 frameno = th_granule_frame(mCtx, granulepos);
|
||||
if (!AddOverflow(frameno, 1, t))
|
||||
return -1;
|
||||
if (!MulOverflow(t, 1000, t))
|
||||
return -1;
|
||||
if (!MulOverflow(t, mInfo.fps_denominator, t))
|
||||
return -1;
|
||||
return t / mInfo.fps_numerator;
|
||||
}
|
||||
|
||||
PRInt64 nsTheoraState::StartTime(PRInt64 granulepos) {
|
||||
if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
|
||||
return -1;
|
||||
}
|
||||
PRInt64 t = 0;
|
||||
PRInt64 frameno = th_granule_frame(mCtx, granulepos);
|
||||
if (!MulOverflow(frameno, 1000, t))
|
||||
return -1;
|
||||
if (!MulOverflow(t, mInfo.fps_denominator, t))
|
||||
return -1;
|
||||
return t / mInfo.fps_numerator;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsTheoraState::MaxKeyframeOffset()
|
||||
{
|
||||
// Determine the maximum time in milliseconds by which a key frame could
|
||||
// offset for the theora bitstream. Theora granulepos encode time as:
|
||||
// ((key_frame_number << granule_shift) + frame_offset).
|
||||
// Therefore the maximum possible time by which any frame could be offset
|
||||
// from a keyframe is the duration of (1 << granule_shift) - 1) frames.
|
||||
PRInt64 frameDuration;
|
||||
PRInt64 keyframeDiff;
|
||||
|
||||
PRInt64 shift = mInfo.keyframe_granule_shift;
|
||||
|
||||
// Max number of frames keyframe could possibly be offset.
|
||||
keyframeDiff = (1 << shift) - 1;
|
||||
|
||||
// Length of frame in ms.
|
||||
PRInt64 d = 0; // d will be 0 if multiplication overflows.
|
||||
MulOverflow(1000, mInfo.fps_denominator, d);
|
||||
frameDuration = d / mInfo.fps_numerator;
|
||||
|
||||
// Total time in ms keyframe can be offset from any given frame.
|
||||
return frameDuration * keyframeDiff;
|
||||
}
|
||||
|
||||
nsresult nsVorbisState::Reset()
|
||||
{
|
||||
nsresult res = NS_OK;
|
||||
if (mActive && vorbis_synthesis_restart(&mDsp) != 0) {
|
||||
res = NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_FAILED(nsOggCodecState::Reset())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
nsVorbisState::nsVorbisState(ogg_page* aBosPage) :
|
||||
nsOggCodecState(aBosPage)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsVorbisState);
|
||||
vorbis_info_init(&mInfo);
|
||||
vorbis_comment_init(&mComment);
|
||||
memset(&mDsp, 0, sizeof(vorbis_dsp_state));
|
||||
memset(&mBlock, 0, sizeof(vorbis_block));
|
||||
}
|
||||
|
||||
nsVorbisState::~nsVorbisState() {
|
||||
MOZ_COUNT_DTOR(nsVorbisState);
|
||||
vorbis_block_clear(&mBlock);
|
||||
vorbis_dsp_clear(&mDsp);
|
||||
vorbis_info_clear(&mInfo);
|
||||
vorbis_comment_clear(&mComment);
|
||||
}
|
||||
|
||||
PRBool nsVorbisState::DecodeHeader(ogg_packet* aPacket) {
|
||||
mPacketCount++;
|
||||
int ret = vorbis_synthesis_headerin(&mInfo,
|
||||
&mComment,
|
||||
aPacket);
|
||||
// We must determine when we've read the last header packet.
|
||||
// vorbis_synthesis_headerin() does not tell us when it's read the last
|
||||
// header, so we must keep track of the headers externally.
|
||||
//
|
||||
// There are 3 header packets, the Identification, Comment, and Setup
|
||||
// headers, which must be in that order. If they're out of order, the file
|
||||
// is invalid. If we've successfully read a header, and it's the setup
|
||||
// header, then we're done reading headers. The first byte of each packet
|
||||
// determines it's type as follows:
|
||||
// 0x1 -> Identification header
|
||||
// 0x3 -> Comment header
|
||||
// 0x5 -> Setup header
|
||||
// For more details of the Vorbis/Ogg containment scheme, see the Vorbis I
|
||||
// Specification, Chapter 4, Codec Setup and Packet Decode:
|
||||
// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-580004
|
||||
|
||||
PRBool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x5;
|
||||
|
||||
if (ret < 0 || mPacketCount > 3) {
|
||||
// We've received an error, or the first three packets weren't valid
|
||||
// header packets, assume bad input, and don't activate the bitstream.
|
||||
mDoneReadingHeaders = PR_TRUE;
|
||||
} else if (ret == 0 && isSetupHeader && mPacketCount == 3) {
|
||||
// Successfully read the three header packets, activate the bitstream.
|
||||
mDoneReadingHeaders = PR_TRUE;
|
||||
mActive = PR_TRUE;
|
||||
}
|
||||
return mDoneReadingHeaders;
|
||||
}
|
||||
|
||||
PRBool nsVorbisState::Init()
|
||||
{
|
||||
if (!mActive)
|
||||
return PR_FALSE;
|
||||
|
||||
int ret = vorbis_synthesis_init(&mDsp, &mInfo);
|
||||
if (ret != 0) {
|
||||
NS_WARNING("vorbis_synthesis_init() failed initializing vorbis bitstream");
|
||||
return mActive = PR_FALSE;
|
||||
}
|
||||
ret = vorbis_block_init(&mDsp, &mBlock);
|
||||
if (ret != 0) {
|
||||
NS_WARNING("vorbis_block_init() failed initializing vorbis bitstream");
|
||||
if (mActive) {
|
||||
vorbis_dsp_clear(&mDsp);
|
||||
}
|
||||
return mActive = PR_FALSE;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRInt64 nsVorbisState::Time(PRInt64 granulepos) {
|
||||
if (granulepos == -1 || !mActive || mDsp.vi->rate == 0) {
|
||||
return -1;
|
||||
}
|
||||
PRInt64 t = 0;
|
||||
MulOverflow(1000, granulepos, t);
|
||||
return t / mDsp.vi->rate;
|
||||
}
|
||||
|
||||
nsSkeletonState::nsSkeletonState(ogg_page* aBosPage)
|
||||
: nsOggCodecState(aBosPage)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsSkeletonState);
|
||||
}
|
||||
|
||||
nsSkeletonState::~nsSkeletonState()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsSkeletonState);
|
||||
}
|
||||
|
||||
PRBool nsSkeletonState::DecodeHeader(ogg_packet* aPacket)
|
||||
{
|
||||
if (aPacket->e_o_s) {
|
||||
mActive = PR_TRUE;
|
||||
mDoneReadingHeaders = PR_TRUE;
|
||||
}
|
||||
return mDoneReadingHeaders;
|
||||
}
|
||||
|
||||
// Adds two 64bit numbers, retuns PR_TRUE if addition succeeded, or PR_FALSE
|
||||
// if addition would result in an overflow.
|
||||
static PRBool AddOverflow(PRInt64 a, PRInt64 b, PRInt64& aResult) {
|
||||
if (b < 1) {
|
||||
if (PR_INT64_MIN - b <= a) {
|
||||
aResult = a + b;
|
||||
return PR_TRUE;
|
||||
}
|
||||
} else if (PR_INT64_MAX - b >= a) {
|
||||
aResult = a + b;
|
||||
return PR_TRUE;
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// 64 bit integer multiplication with overflow checking. Returns PR_TRUE
|
||||
// if the multiplication was successful, or PR_FALSE if the operation resulted
|
||||
// in an integer overflow.
|
||||
static PRBool MulOverflow(PRInt64 a, PRInt64 b, PRInt64& aResult) {
|
||||
// We break a multiplication a * b into of sign_a * sign_b * abs(a) * abs(b)
|
||||
//
|
||||
// This is equivalent to:
|
||||
//
|
||||
// (sign_a * sign_b) * ((a_hi * 2^32) + a_lo) * ((b_hi * 2^32) + b_lo)
|
||||
//
|
||||
// Which is equivalent to:
|
||||
//
|
||||
// (sign_a * sign_b) *
|
||||
// ((a_hi * b_hi << 64) +
|
||||
// (a_hi * b_lo << 32) + (a_lo * b_hi << 32) +
|
||||
// a_lo * b_lo)
|
||||
//
|
||||
// So to check if a*b overflows, we must check each sub part of the above
|
||||
// sum.
|
||||
//
|
||||
// Note: -1 * PR_INT64_MIN == PR_INT64_MIN ; we can't negate PR_INT64_MIN!
|
||||
// Note: Shift of negative numbers is undefined.
|
||||
//
|
||||
// Figure out the sign after multiplication. Then we can just work with
|
||||
// unsigned numbers.
|
||||
PRInt64 sign = (!(a < 0) == !(b < 0)) ? 1 : -1;
|
||||
|
||||
PRInt64 abs_a = (a < 0) ? -a : a;
|
||||
PRInt64 abs_b = (b < 0) ? -b : b;
|
||||
|
||||
if (abs_a < 0) {
|
||||
NS_ASSERTION(a == PR_INT64_MIN, "How else can this happen?");
|
||||
if (b == 0 || b == 1) {
|
||||
aResult = a * b;
|
||||
return PR_TRUE;
|
||||
} else {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (abs_b < 0) {
|
||||
NS_ASSERTION(b == PR_INT64_MIN, "How else can this happen?");
|
||||
if (a == 0 || a == 1) {
|
||||
aResult = a * b;
|
||||
return PR_TRUE;
|
||||
} else {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(abs_a >= 0 && abs_b >= 0, "abs values must be non-negative");
|
||||
|
||||
PRInt64 a_hi = abs_a >> 32;
|
||||
PRInt64 a_lo = abs_a & 0xFFFFFFFF;
|
||||
PRInt64 b_hi = abs_b >> 32;
|
||||
PRInt64 b_lo = abs_b & 0xFFFFFFFF;
|
||||
|
||||
NS_ASSERTION((a_hi<<32) + a_lo == abs_a, "Partition must be correct");
|
||||
NS_ASSERTION((b_hi<<32) + b_lo == abs_b, "Partition must be correct");
|
||||
|
||||
// In the sub-equation (a_hi * b_hi << 64), if a_hi or b_hi
|
||||
// are non-zero, this will overflow as it's shifted by 64.
|
||||
// Abort if this overflows.
|
||||
if (a_hi != 0 && b_hi != 0) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// We can now assume that either a_hi or b_hi is 0.
|
||||
NS_ASSERTION(a_hi == 0 || b_hi == 0, "One of these must be 0");
|
||||
|
||||
// Next we calculate:
|
||||
// (a_hi * b_lo << 32) + (a_lo * b_hi << 32)
|
||||
// We can factor this as:
|
||||
// (a_hi * b_lo + a_lo * b_hi) << 32
|
||||
PRInt64 q = a_hi * b_lo + a_lo * b_hi;
|
||||
if (q > PR_INT32_MAX) {
|
||||
// q will overflow when we shift by 32; abort.
|
||||
return PR_FALSE;
|
||||
}
|
||||
q <<= 32;
|
||||
|
||||
// Both a_lo and b_lo are less than INT32_MAX, so can't overflow.
|
||||
PRUint64 lo = a_lo * b_lo;
|
||||
if (lo > PR_INT64_MAX) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Add the final result. We must check for overflow during addition.
|
||||
if (!AddOverflow(q, static_cast<PRInt64>(lo), aResult)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
aResult *= sign;
|
||||
NS_ASSERTION(a * b == aResult, "We didn't overflow, but result is wrong!");
|
||||
return PR_TRUE;
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: ML 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 code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Double <chris.double@double.co.nz>
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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 ***** */
|
||||
#if !defined(nsOggCodecState_h_)
|
||||
#define nsOggCodecState_h_
|
||||
|
||||
#include <ogg/ogg.h>
|
||||
#include <theora/theoradec.h>
|
||||
#include <vorbis/codec.h>
|
||||
#include <nsDeque.h>
|
||||
|
||||
class OggPageDeallocator : public nsDequeFunctor {
|
||||
virtual void* operator() (void* aPage) {
|
||||
ogg_page* p = static_cast<ogg_page*>(aPage);
|
||||
delete p->header;
|
||||
delete p;
|
||||
return nsnull;
|
||||
}
|
||||
};
|
||||
|
||||
// A queue of ogg_pages. When we read a page, and it's not from the bitstream
|
||||
// which we're looking for a page for, we buffer the page in the nsOggCodecState,
|
||||
// rather than pushing it immediately into the ogg_stream_state object. This
|
||||
// is because if we're skipping up to the next keyframe in very large frame
|
||||
// sized videos, there may be several megabytes of data between keyframes,
|
||||
// and the ogg_stream_state would end up resizing its buffer every time we
|
||||
// added a new 4K page to the bitstream, which kills performance on Windows.
|
||||
class nsPageQueue : private nsDeque {
|
||||
public:
|
||||
nsPageQueue() : nsDeque(new OggPageDeallocator()) {}
|
||||
~nsPageQueue() { Erase(); }
|
||||
PRBool IsEmpty() { return nsDeque::GetSize() == 0; }
|
||||
void Append(ogg_page* aPage);
|
||||
ogg_page* PopFront() { return static_cast<ogg_page*>(nsDeque::PopFront()); }
|
||||
ogg_page* PeekFront() { return static_cast<ogg_page*>(nsDeque::PeekFront()); }
|
||||
void Erase() { nsDeque::Erase(); }
|
||||
};
|
||||
|
||||
// Encapsulates the data required for decoding an ogg bitstream and for
|
||||
// converting granulepos to timestamps.
|
||||
class nsOggCodecState {
|
||||
public:
|
||||
// Ogg types we know about
|
||||
enum CodecType {
|
||||
TYPE_VORBIS=0,
|
||||
TYPE_THEORA=1,
|
||||
TYPE_SKELETON=2,
|
||||
TYPE_UNKNOWN=3
|
||||
};
|
||||
|
||||
public:
|
||||
nsOggCodecState(ogg_page* aBosPage);
|
||||
virtual ~nsOggCodecState();
|
||||
|
||||
// Factory for creating nsCodecStates.
|
||||
static nsOggCodecState* Create(ogg_page* aPage);
|
||||
|
||||
virtual CodecType GetType() { return TYPE_UNKNOWN; }
|
||||
|
||||
// Reads a header packet. Returns PR_TRUE when last header has been read.
|
||||
virtual PRBool DecodeHeader(ogg_packet* aPacket) {
|
||||
return (mDoneReadingHeaders = PR_TRUE);
|
||||
}
|
||||
|
||||
// Returns the end time that a granulepos represents.
|
||||
virtual PRInt64 Time(PRInt64 granulepos) { return -1; }
|
||||
|
||||
// Returns the start time that a granulepos represents.
|
||||
virtual PRInt64 StartTime(PRInt64 granulepos) { return -1; }
|
||||
|
||||
// Initializes the codec state.
|
||||
virtual PRBool Init();
|
||||
|
||||
// Returns PR_TRUE when this bitstream has finished reading all its
|
||||
// header packets.
|
||||
PRBool DoneReadingHeaders() { return mDoneReadingHeaders; }
|
||||
|
||||
// Deactivates the bitstream. Only the primary video and audio bitstreams
|
||||
// should be active.
|
||||
void Deactivate() { mActive = PR_FALSE; }
|
||||
|
||||
// Resets decoding state.
|
||||
virtual nsresult Reset();
|
||||
|
||||
// Clones a page and adds it to our buffer of pages which we'll insert to
|
||||
// the bitstream at a later time (using PageInFromBuffer()). Memory stored in
|
||||
// cloned pages is freed when Reset() or PageInFromBuffer() are called.
|
||||
inline void AddToBuffer(ogg_page* aPage) { mBuffer.Append(aPage); }
|
||||
|
||||
// Returns PR_TRUE if we had a buffered page and we successfully inserted it
|
||||
// into the bitstream.
|
||||
PRBool PageInFromBuffer();
|
||||
|
||||
public:
|
||||
|
||||
// Number of packets read.
|
||||
PRUint64 mPacketCount;
|
||||
|
||||
// Serial number of the bitstream.
|
||||
PRUint32 mSerial;
|
||||
|
||||
// Ogg specific state.
|
||||
ogg_stream_state mState;
|
||||
|
||||
// Buffer of pages which we've not yet inserted into the ogg_stream_state.
|
||||
nsPageQueue mBuffer;
|
||||
|
||||
// Is the bitstream active; whether we're decoding and playing this bitstream.
|
||||
PRPackedBool mActive;
|
||||
|
||||
// PR_TRUE when all headers packets have been read.
|
||||
PRPackedBool mDoneReadingHeaders;
|
||||
};
|
||||
|
||||
class nsVorbisState : public nsOggCodecState {
|
||||
public:
|
||||
nsVorbisState(ogg_page* aBosPage);
|
||||
virtual ~nsVorbisState();
|
||||
|
||||
virtual CodecType GetType() { return TYPE_VORBIS; }
|
||||
virtual PRBool DecodeHeader(ogg_packet* aPacket);
|
||||
virtual PRInt64 Time(PRInt64 granulepos);
|
||||
virtual PRBool Init();
|
||||
virtual nsresult Reset();
|
||||
|
||||
vorbis_info mInfo;
|
||||
vorbis_comment mComment;
|
||||
vorbis_dsp_state mDsp;
|
||||
vorbis_block mBlock;
|
||||
};
|
||||
|
||||
class nsTheoraState : public nsOggCodecState {
|
||||
public:
|
||||
nsTheoraState(ogg_page* aBosPage);
|
||||
virtual ~nsTheoraState();
|
||||
|
||||
virtual CodecType GetType() { return TYPE_THEORA; }
|
||||
virtual PRBool DecodeHeader(ogg_packet* aPacket);
|
||||
virtual PRInt64 Time(PRInt64 granulepos);
|
||||
virtual PRInt64 StartTime(PRInt64 granulepos);
|
||||
virtual PRBool Init();
|
||||
|
||||
// Returns the maximum number of milliseconds which a keyframe can be offset
|
||||
// from any given interframe.
|
||||
PRInt64 MaxKeyframeOffset();
|
||||
|
||||
th_info mInfo;
|
||||
th_comment mComment;
|
||||
th_setup_info *mSetup;
|
||||
th_dec_ctx* mCtx;
|
||||
|
||||
// Frame duration in ms.
|
||||
PRUint32 mFrameDuration;
|
||||
|
||||
// Number of frames per second.
|
||||
float mFrameRate;
|
||||
|
||||
float mAspectRatio;
|
||||
};
|
||||
|
||||
class nsSkeletonState : public nsOggCodecState {
|
||||
public:
|
||||
nsSkeletonState(ogg_page* aBosPage);
|
||||
virtual ~nsSkeletonState();
|
||||
virtual CodecType GetType() { return TYPE_SKELETON; }
|
||||
virtual PRBool DecodeHeader(ogg_packet* aPacket);
|
||||
virtual PRInt64 Time(PRInt64 granulepos) { return -1; }
|
||||
virtual PRBool Init() { return PR_TRUE; }
|
||||
};
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Chris Double <chris.double@double.co.nz>
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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
|
||||
|
@ -36,55 +37,67 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/*
|
||||
Each video element has two threads. The first thread, called the Decode thread,
|
||||
owns the resources for downloading and reading the video file. It goes through the
|
||||
file, prcessing any decoded theora and vorbis data. It handles the sending of the
|
||||
audio data to the sound device and the presentation of the video data at the correct
|
||||
frame rate.
|
||||
Each video element has three threads.
|
||||
|
||||
The second thread is the step decode thread. It uses OggPlay to decode the video and
|
||||
audio data. It indirectly uses an nsMediaStream to do the file reading and seeking via
|
||||
Oggplay.
|
||||
1) The state machine thread owns the resources for downloading and reading
|
||||
the media file. It controls the lifetime of the other two threads
|
||||
and renders the video data at the correct time during playback.
|
||||
|
||||
All file reads and seeks must occur on these two threads. Synchronisation is done via
|
||||
liboggplay internal mutexes to ensure that access to the liboggplay structures is
|
||||
done correctly in the presence of the threads.
|
||||
2) The Audio thread writes the decoded audio data to the audio
|
||||
hardware. This is done in a seperate thread to ensure that the
|
||||
audio hardware gets a constant stream of data without
|
||||
interruption due to decoding or diplay. At some point
|
||||
libsydneyaudio will be refactored to have a callback interface
|
||||
where it asks for data and an extra thread will no longer be
|
||||
needed.
|
||||
|
||||
The step decode thread is created and destroyed in the decode thread. When decoding
|
||||
needs to be done it is created and event dispatched to it to start the decode loop.
|
||||
This event exits when decoding is completed or no longer required (during seeking
|
||||
or shutdown).
|
||||
3) The decode thread. This thread reads from the media stream and decodes
|
||||
the Theora and Vorbis data. It places the decoded data in a queue
|
||||
for the other threads to pull from.
|
||||
|
||||
When the decode thread is created an event is dispatched to it. The event
|
||||
runs for the lifetime of the playback of the resource. The decode thread
|
||||
synchronises with the main thread via a single monitor held by the
|
||||
nsOggDecoder object.
|
||||
All file reads and seeks must occur on either the state machine thread
|
||||
or the decode thread. Synchronisation is done via a monitor owned by
|
||||
nsOggDecoder.
|
||||
|
||||
The event contains a Run method which consists of an infinite loop
|
||||
that checks the state that the state machine is in and processes
|
||||
operations on that state.
|
||||
The decode thread and the audio thread are created and destroyed in the
|
||||
state machine thread. When playback needs to occur they are created and
|
||||
events dispatched to them to start them. These events exit when
|
||||
decoding is completed or no longer required (during seeking or
|
||||
shutdown).
|
||||
|
||||
The nsOggDecodeStateMachine class is the event that gets dispatched to
|
||||
the decode thread. It has the following states:
|
||||
All threads have one event that is dispatched to it and that event
|
||||
runs for the lifetime of the playback of the resource. State shared
|
||||
between them is synchronised with the main thread via a monitor
|
||||
held by the nsOggDecoder object. The decode thread also has its own
|
||||
monitor to ensure that its internal state is independent of the other
|
||||
threads, and to ensure that it's not hogging the monitor while decoding.
|
||||
|
||||
The events consist of a Run method which is an infinite loop that
|
||||
perform the threads operation and checks the state that the state
|
||||
machine is in and processes operations on that state.
|
||||
|
||||
The nsOggPlayStateMachine class is the event that gets dispatched to
|
||||
the state machine thread. It has the following states:
|
||||
|
||||
DECODING_METADATA
|
||||
The Ogg headers are being loaded, and things like framerate, etc are
|
||||
being determined, and the first frame of audio/video data is being decoded.
|
||||
DECODING
|
||||
Video/Audio frames are being decoded.
|
||||
The decode and audio threads are started and video frames displayed at
|
||||
the required time.
|
||||
SEEKING
|
||||
A seek operation is in progress.
|
||||
BUFFERING
|
||||
Decoding is paused while data is buffered for smooth playback.
|
||||
COMPLETED
|
||||
The resource has completed decoding.
|
||||
The resource has completed decoding, but not finished playback.
|
||||
SHUTDOWN
|
||||
The decoder object is about to be destroyed.
|
||||
|
||||
The following result in state transitions.
|
||||
|
||||
Shutdown()
|
||||
Clean up any resources the nsOggDecodeStateMachine owns.
|
||||
Clean up any resources the nsOggPlayStateMachine owns.
|
||||
Decode()
|
||||
Start decoding video frames.
|
||||
Buffer
|
||||
|
@ -163,7 +176,7 @@ player SHUTDOWN decoder SHUTDOWN
|
|||
The general sequence of events with these objects is:
|
||||
|
||||
1) The video element calls Load on nsMediaDecoder. This creates the
|
||||
decode thread and starts the channel for downloading the file. It
|
||||
state machine thread and starts the channel for downloading the file. It
|
||||
instantiates and starts the Decode state machine. The high level
|
||||
LOADING state is entered, which results in the decode state machine
|
||||
to start decoding metadata. These are the headers that give the
|
||||
|
@ -188,66 +201,52 @@ The general sequence of events with these objects is:
|
|||
video, if the correct frame time comes around and the decoder
|
||||
play state is PLAYING.
|
||||
|
||||
a/v synchronisation is done by a combination of liboggplay and the
|
||||
Decoder state machine. liboggplay ensures that a decoded frame of data
|
||||
has both the audio samples and the YUV data for that period of time.
|
||||
a/v synchronisation is handled by the stete machine thread. It examines the
|
||||
audio playback time and compares this to the next frame in the queue
|
||||
of frames. If it is time to play the video frame it is then displayed.
|
||||
|
||||
When a frame is decoded by the decode state machine it converts the
|
||||
YUV encoded video to RGB and copies the sound data to an internal
|
||||
FrameData object. This is stored in a queue of available decoded frames.
|
||||
Included in the FrameData object is the time that that frame should
|
||||
be displayed.
|
||||
Frame skipping is done in the following ways:
|
||||
|
||||
The display state machine keeps track of the time since the last frame it
|
||||
played. After decoding a frame it checks if it is time to display the next
|
||||
item in the decoded frame queue. If so, it pops the item off the queue
|
||||
and displays it.
|
||||
1) The state machine thread will skip all frames in the video queue whose
|
||||
display time is less than the current audio time. This ensures
|
||||
the correct frame for the current time is always displayed.
|
||||
|
||||
Ideally a/v sync would take into account the actual audio clock of the
|
||||
audio hardware for the sync rather than using the system clock.
|
||||
Unfortunately getting valid time data out of the audio hardware has proven
|
||||
to be unreliable across platforms (and even distributions in Linux) depending
|
||||
on audio hardware, audio backend etc. The current approach works fine in practice
|
||||
and is a compromise until this issue can be sorted. The plan is to eventually
|
||||
move to synchronising using the audio hardware.
|
||||
2) The decode thread will stop decoding interframes and read to the
|
||||
next keyframe if it determines that decoding the remaining
|
||||
interframes will cause playback issues. It detects this by:
|
||||
a) If the amount of audio data in the audio queue drops
|
||||
below a threshold whereby audio may start to skip.
|
||||
b) If the video queue drops below a threshold where it
|
||||
will be decoding video data that won't be displayed due
|
||||
to the decode thread dropping the frame immediately.
|
||||
|
||||
To prevent audio skipping and framerate dropping it is very important to
|
||||
make sure no blocking occurs during the decoding process and minimise
|
||||
expensive time operations at the time a frame is to be displayed. This is
|
||||
managed by immediately converting video data to RGB on decode (an expensive
|
||||
operation to do at frame display time) and checking if the sound device will
|
||||
not block before writing sound data to it.
|
||||
YCbCr conversion is done on the decode thread when it is time to display
|
||||
the video frame. This means frames that are skipped will not have the
|
||||
YCbCr conversion done, improving playback.
|
||||
|
||||
Shutdown needs to ensure that the event posted to the decode
|
||||
thread is completed. The decode thread can potentially block internally
|
||||
inside liboggplay when reading, seeking, or its internal buffers containing
|
||||
decoded data are full. When blocked in this manner a call from the main thread
|
||||
to Shutdown() will hang.
|
||||
The decode thread pushes decoded audio and videos frames into two
|
||||
separate queues - one for audio and one for video. These are kept
|
||||
separate to make it easy to constantly feed audio data to the sound
|
||||
hardware while allowing frame skipping of video data. These queues are
|
||||
threadsafe, and neither the decode, audio, or state machine thread should
|
||||
be able to monopolize them, and cause starvation of the other threads.
|
||||
|
||||
This is fixed with a protocol to ensure that the decode event cleanly
|
||||
completes. The nsMediaStream that the nsChannelReader uses has a
|
||||
Cancel() method. Calling this before Shutdown() will close any
|
||||
internal streams or listeners resulting in blocked i/o completing with
|
||||
an error, and all future i/o on the stream having an error.
|
||||
Both queues are bounded by a maximum size. When this size is reached
|
||||
the decode thread will no longer decode video or audio depending on the
|
||||
queue that has reached the threshold.
|
||||
|
||||
This causes the decode thread to exit and Shutdown() can occur.
|
||||
During playback the audio thread will be idle (via a Wait() on the
|
||||
monitor) if the audio queue is empty. Otherwise it constantly pops an
|
||||
item off the queue and plays it with a blocking write to the audio
|
||||
hardware (via nsAudioStream and libsydneyaudio).
|
||||
|
||||
If the decode thread is seeking then the same Cancel() operation
|
||||
causes an error to be returned from the seek call to liboggplay which
|
||||
exits out of the seek operation, and stops the seek state running on the
|
||||
decode thread.
|
||||
|
||||
If the decode thread is blocked due to internal decode buffers being
|
||||
full, it is unblocked during the shutdown process by calling
|
||||
oggplay_prepare_for_close.
|
||||
|
||||
In practice the OggPlay internal buffer should never fill as we retrieve and
|
||||
process the frame immediately on decoding.
|
||||
The decode thread idles if the video queue is empty or if it is
|
||||
not yet time to display the next frame.
|
||||
|
||||
The Shutdown method on nsOggDecoder can spin the event loop as it waits
|
||||
for threads to complete. Spinning the event loop is a bad thing to happen
|
||||
during certain times like destruction of the media element. To work around
|
||||
this the Shutdown method does nothing by queue an event to the main thread
|
||||
this the Shutdown method does nothing but queue an event to the main thread
|
||||
to perform the actual Shutdown. This way the shutdown can occur at a safe
|
||||
time.
|
||||
|
||||
|
@ -263,25 +262,27 @@ when destroying the nsOggDecoder object.
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsChannelReader.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsSize.h"
|
||||
#include "prlog.h"
|
||||
#include "prmon.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxRect.h"
|
||||
#include "oggplay/oggplay.h"
|
||||
#include "nsMediaStream.h"
|
||||
#include "nsMediaDecoder.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
using mozilla::Monitor;
|
||||
|
||||
class nsAudioStream;
|
||||
class nsOggDecodeStateMachine;
|
||||
class nsOggStepDecodeEvent;
|
||||
class nsOggPlayStateMachine;
|
||||
class nsOggReader;
|
||||
|
||||
class nsOggDecoder : public nsMediaDecoder
|
||||
{
|
||||
friend class nsOggDecodeStateMachine;
|
||||
friend class nsOggStepDecodeEvent;
|
||||
friend class nsOggReader;
|
||||
friend class nsOggPlayStateMachine;
|
||||
|
||||
// ISupports
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -336,7 +337,8 @@ class nsOggDecoder : public nsMediaDecoder
|
|||
virtual void NotifySuspendedStatusChanged();
|
||||
virtual void NotifyBytesDownloaded();
|
||||
virtual void NotifyDownloadEnded(nsresult aStatus);
|
||||
// Called by nsChannelReader on the decoder thread
|
||||
// Called by the decode thread to keep track of the number of bytes read
|
||||
// from the resource.
|
||||
void NotifyBytesConsumed(PRInt64 aBytes);
|
||||
|
||||
// Called when the video file has completed downloading.
|
||||
|
@ -366,9 +368,6 @@ class nsOggDecoder : public nsMediaDecoder
|
|||
// Return PR_TRUE if seeking is supported.
|
||||
virtual PRBool GetSeekable();
|
||||
|
||||
// Returns the channel reader.
|
||||
nsChannelReader* GetReader() { return mReader; }
|
||||
|
||||
virtual Statistics GetStatistics();
|
||||
|
||||
// Suspend any media downloads that are in progress. Called by the
|
||||
|
@ -384,23 +383,25 @@ class nsOggDecoder : public nsMediaDecoder
|
|||
// Tells our nsMediaStream to put all loads in the background.
|
||||
virtual void MoveLoadsToBackground();
|
||||
|
||||
// Stop the state machine thread and drop references to the thread,
|
||||
// state machine and channel reader.
|
||||
// Stop the state machine thread and drop references to the thread and
|
||||
// state machine.
|
||||
void Stop();
|
||||
|
||||
// Called by the state machine to notify the decoder that the duration
|
||||
// has changed.
|
||||
void DurationChanged();
|
||||
|
||||
protected:
|
||||
|
||||
// Returns the monitor for other threads to synchronise access to
|
||||
// state.
|
||||
PRMonitor* GetMonitor()
|
||||
{
|
||||
Monitor& GetMonitor() {
|
||||
return mMonitor;
|
||||
}
|
||||
|
||||
// Return the current state. Can be called on any thread. If called from
|
||||
// a non-main thread, the decoder monitor must be held.
|
||||
PlayState GetState()
|
||||
{
|
||||
PlayState GetState() {
|
||||
return mPlayState;
|
||||
}
|
||||
|
||||
|
@ -496,8 +497,8 @@ private:
|
|||
// time of the last decoded video frame).
|
||||
nsChannelStatistics mPlaybackStatistics;
|
||||
|
||||
// Thread to handle decoding of Ogg data.
|
||||
nsCOMPtr<nsIThread> mDecodeThread;
|
||||
// Thread to manage playback state machine.
|
||||
nsCOMPtr<nsIThread> mStateMachineThread;
|
||||
|
||||
// The current playback position of the media resource in units of
|
||||
// seconds. This is updated approximately at the framerate of the
|
||||
|
@ -506,14 +507,12 @@ private:
|
|||
float mCurrentTime;
|
||||
|
||||
// Volume that playback should start at. 0.0 = muted. 1.0 = full
|
||||
// volume. Readable/Writeable from the main thread. Read from the
|
||||
// audio thread when it is first started to get the initial volume
|
||||
// level.
|
||||
// volume. Readable/Writeable from the main thread.
|
||||
float mInitialVolume;
|
||||
|
||||
// Position to seek to when the seek notification is received by the
|
||||
// decoding thread. Written by the main thread and read via the
|
||||
// decoding thread. Synchronised using mPlayStateMonitor. If the
|
||||
// decode thread. Written by the main thread and read via the
|
||||
// decode thread. Synchronised using mMonitor. If the
|
||||
// value is negative then no seek has been requested. When a seek is
|
||||
// started this is reset to negative.
|
||||
float mRequestedSeekTime;
|
||||
|
@ -531,26 +530,20 @@ private:
|
|||
* The following member variables can be accessed from any thread.
|
||||
******/
|
||||
|
||||
// The state machine object for handling the decoding via
|
||||
// oggplay. It is safe to call methods of this object from other
|
||||
// threads. Its internal data is synchronised on a monitor. The
|
||||
// lifetime of this object is after mPlayState is LOADING and before
|
||||
// mPlayState is SHUTDOWN. It is safe to access it during this
|
||||
// period.
|
||||
nsCOMPtr<nsOggDecodeStateMachine> mDecodeStateMachine;
|
||||
// The state machine object for handling the decoding. It is safe to
|
||||
// call methods of this object from other threads. Its internal data
|
||||
// is synchronised on a monitor. The lifetime of this object is
|
||||
// after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
|
||||
// is safe to access it during this period.
|
||||
nsCOMPtr<nsOggPlayStateMachine> mDecodeStateMachine;
|
||||
|
||||
// OggPlay object used to read data from a channel. Created on main
|
||||
// thread. Passed to liboggplay and the locking for multithreaded
|
||||
// access is handled by that library. Some methods are called from
|
||||
// the decoder thread, and the state machine for that thread keeps
|
||||
// a pointer to this reader. This is safe as the only methods called
|
||||
// are threadsafe (via the threadsafe nsMediaStream).
|
||||
nsAutoPtr<nsChannelReader> mReader;
|
||||
// Stream of media data.
|
||||
nsAutoPtr<nsMediaStream> mStream;
|
||||
|
||||
// Monitor for detecting when the video play state changes. A call
|
||||
// to Wait on this monitor will block the thread until the next
|
||||
// state change.
|
||||
PRMonitor* mMonitor;
|
||||
Monitor mMonitor;
|
||||
|
||||
// Set to one of the valid play states. It is protected by the
|
||||
// monitor mMonitor. This monitor must be acquired when reading or
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: ML 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 code.
|
||||
*
|
||||
* 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):
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef nsOggHacks_h
|
||||
#define nsOggHacks_h
|
||||
|
||||
// This file contains stuff we'd rather put elsewhere, but which is
|
||||
// dependent on other changes which we don't want to wait for. We plan to
|
||||
// remove this file in the near future.
|
||||
|
||||
|
||||
// This belongs in prtypes.h
|
||||
/************************************************************************
|
||||
* MACROS: PR_INT64_MAX
|
||||
* PR_INT64_MIN
|
||||
* PR_UINT64_MAX
|
||||
* DESCRIPTION:
|
||||
* The maximum and minimum values of a PRInt64 or PRUint64.
|
||||
************************************************************************/
|
||||
|
||||
#define PR_INT64_MAX (~((PRInt64)(1) << 63))
|
||||
#define PR_INT64_MIN (-PR_INT64_MAX - 1)
|
||||
#define PR_UINT64_MAX (~(PRUint64)(0))
|
||||
|
||||
|
||||
// This belongs in xpcom/monitor/Monitor.h, once we've made
|
||||
// mozilla::Monitor non-reentrant.
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* MonitorAutoExit
|
||||
* Exit the Monitor when it enters scope, and enters it when it leaves
|
||||
* scope.
|
||||
*
|
||||
* MUCH PREFERRED to bare calls to Monitor.Exit and Enter.
|
||||
*/
|
||||
class NS_COM_GLUE NS_STACK_CLASS MonitorAutoExit
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* The constructor releases the given lock. The destructor
|
||||
* acquires the lock. The lock must be held before constructing
|
||||
* this object!
|
||||
*
|
||||
* @param aMonitor A valid mozilla::Monitor* returned by
|
||||
* mozilla::Monitor::NewMonitor. It must be
|
||||
* already locked.
|
||||
**/
|
||||
MonitorAutoExit(mozilla::Monitor &aMonitor) :
|
||||
mMonitor(&aMonitor)
|
||||
{
|
||||
NS_ASSERTION(mMonitor, "null monitor");
|
||||
mMonitor->AssertCurrentThreadIn();
|
||||
mMonitor->Exit();
|
||||
}
|
||||
|
||||
~MonitorAutoExit(void)
|
||||
{
|
||||
mMonitor->Enter();
|
||||
}
|
||||
|
||||
private:
|
||||
MonitorAutoExit();
|
||||
MonitorAutoExit(const MonitorAutoExit&);
|
||||
MonitorAutoExit& operator =(const MonitorAutoExit&);
|
||||
static void* operator new(size_t) CPP_THROW_NEW;
|
||||
static void operator delete(void*);
|
||||
|
||||
mozilla::Monitor* mMonitor;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,419 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: ML 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 code.
|
||||
*
|
||||
* 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):
|
||||
* Chris Double <chris.double@double.co.nz>
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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 ***** */
|
||||
#if !defined(nsOggPlayStateMachine_h__)
|
||||
#define nsOggPlayStateMachine_h__
|
||||
|
||||
#include "prmem.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsOggReader.h"
|
||||
#include "nsOggDecoder.h"
|
||||
#include "nsHTMLMediaElement.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
using mozilla::TimeDuration;
|
||||
using mozilla::TimeStamp;
|
||||
|
||||
class nsOggDecoder;
|
||||
|
||||
// Checks if we're on a specific thread or not. Used in assertions to
|
||||
// verify thread safety.
|
||||
static inline PRBool IsThread(nsIThread* aThread) {
|
||||
return NS_GetCurrentThread() == aThread;
|
||||
}
|
||||
|
||||
/*
|
||||
The playback state machine class. This manages the decoding in the
|
||||
nsOggReader on the decode thread, seeking and in-sync-playback on the
|
||||
state machine thread, and controls the audio "push" thread.
|
||||
|
||||
All internal state is synchronised via the decoder monitor. NotifyAll
|
||||
on the monitor is called when the state of the state machine is changed
|
||||
by the main thread. The following changes to state cause a notify:
|
||||
|
||||
mState and data related to that state changed (mSeekTime, etc)
|
||||
Ogg Metadata Loaded
|
||||
First Frame Loaded
|
||||
Frame decoded
|
||||
data pushed or popped from the video and audio queues
|
||||
|
||||
See nsOggDecoder.h for more details.
|
||||
*/
|
||||
class nsOggPlayStateMachine : public nsRunnable
|
||||
{
|
||||
public:
|
||||
// Enumeration for the valid states
|
||||
enum State {
|
||||
DECODER_STATE_DECODING_METADATA,
|
||||
DECODER_STATE_DECODING,
|
||||
DECODER_STATE_SEEKING,
|
||||
DECODER_STATE_BUFFERING,
|
||||
DECODER_STATE_COMPLETED,
|
||||
DECODER_STATE_SHUTDOWN
|
||||
};
|
||||
|
||||
nsOggPlayStateMachine(nsOggDecoder* aDecoder);
|
||||
~nsOggPlayStateMachine();
|
||||
|
||||
// Initializes the state machine, returns NS_OK on success, or
|
||||
// NS_ERROR_FAILURE on failure.
|
||||
nsresult Init();
|
||||
|
||||
// Cause state transitions. These methods obtain the decoder monitor
|
||||
// to synchronise the change of state, and to notify other threads
|
||||
// that the state has changed.
|
||||
void Shutdown();
|
||||
void Decode();
|
||||
|
||||
// Seeks to aTime seconds.
|
||||
void Seek(float aTime);
|
||||
|
||||
// State machine thread run function. Polls the state, sends frames to be
|
||||
// displayed at appropriate times, and generally manages the decode.
|
||||
NS_IMETHOD Run();
|
||||
|
||||
// This is called on the state machine thread and audio thread.
|
||||
// The decoder monitor must be obtained before calling this.
|
||||
PRBool HasAudio() const {
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
return mInfo.mHasAudio;
|
||||
}
|
||||
|
||||
// This is called on the state machine thread and audio thread.
|
||||
// The decoder monitor must be obtained before calling this.
|
||||
PRBool HasVideo() const {
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
return mInfo.mHasVideo;
|
||||
}
|
||||
|
||||
// Returns the current playback position in seconds.
|
||||
// Called from the main thread to get the current frame time. The decoder
|
||||
// monitor must be obtained before calling this.
|
||||
float GetCurrentTime();
|
||||
|
||||
// Called from the main thread to get the duration. The decoder monitor
|
||||
// must be obtained before calling this. It is in units of milliseconds.
|
||||
PRInt64 GetDuration();
|
||||
|
||||
// Called from the main thread to set the duration of the media resource
|
||||
// if it is able to be obtained via HTTP headers. The decoder monitor
|
||||
// must be obtained before calling this.
|
||||
void SetDuration(PRInt64 aDuration);
|
||||
|
||||
// Called from the main thread to set whether the media resource can
|
||||
// be seeked. The decoder monitor must be obtained before calling this.
|
||||
void SetSeekable(PRBool aSeekable);
|
||||
|
||||
// Set the audio volume. The decoder monitor must be obtained before
|
||||
// calling this.
|
||||
void SetVolume(float aVolume);
|
||||
|
||||
// Clear the flag indicating that a playback position change event
|
||||
// is currently queued. This is called from the main thread and must
|
||||
// be called with the decode monitor held.
|
||||
void ClearPositionChangeFlag();
|
||||
|
||||
// Should be called by main thread.
|
||||
PRBool HaveNextFrameData() const {
|
||||
PRUint32 audioQueueSize = mReader->mAudioQueue.GetSize();
|
||||
return (mReader->mVideoQueue.GetSize() > 0 &&
|
||||
(!HasAudio() || audioQueueSize > 0)) ||
|
||||
audioQueueSize > 0;
|
||||
}
|
||||
|
||||
// Must be called with the decode monitor held.
|
||||
PRBool IsBuffering() const {
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
|
||||
return mState == nsOggPlayStateMachine::DECODER_STATE_BUFFERING;
|
||||
}
|
||||
|
||||
// Must be called with the decode monitor held.
|
||||
PRBool IsSeeking() const {
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
|
||||
return mState == nsOggPlayStateMachine::DECODER_STATE_SEEKING;
|
||||
}
|
||||
|
||||
// Functions used by assertions to ensure we're calling things
|
||||
// on the appropriate threads.
|
||||
PRBool OnStateMachineThread() {
|
||||
return IsThread(mDecoder->mStateMachineThread);
|
||||
}
|
||||
|
||||
PRBool OnDecodeThread() {
|
||||
return IsThread(mDecodeThread);
|
||||
}
|
||||
|
||||
PRBool OnAudioThread() {
|
||||
return IsThread(mAudioThread);
|
||||
}
|
||||
|
||||
// Decode loop, called on the decode thread.
|
||||
void DecodeLoop();
|
||||
|
||||
// The decoder object that created this state machine. The decoder
|
||||
// always outlives us since it controls our lifetime. This is accessed
|
||||
// read only on the AV, state machine, audio and main thread.
|
||||
nsOggDecoder* mDecoder;
|
||||
|
||||
// Update the playback position. This can result in a timeupdate event
|
||||
// and an invalidate of the frame being dispatched asynchronously if
|
||||
// there is no such event currently queued.
|
||||
// Only called on the decoder thread. Must be called with
|
||||
// the decode monitor held.
|
||||
void UpdatePlaybackPosition(PRInt64 aTime);
|
||||
|
||||
nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus();
|
||||
|
||||
// The decoder monitor must be obtained before modifying this state.
|
||||
// NotifyAll on the monitor must be called when the state is changed by
|
||||
// the main thread so the decoder thread can wake up.
|
||||
// Accessed on state machine, audio, main, and AV thread.
|
||||
State mState;
|
||||
|
||||
private:
|
||||
|
||||
// Waits on the decoder Monitor for aMs. If the decoder monitor is awoken
|
||||
// by a Notify() call, we'll continue waiting, unless we've moved into
|
||||
// shutdown state. This enables us to ensure that we wait for a specified
|
||||
// time, and that the myriad of Notify()s we do an the decoder monitor
|
||||
// don't cause the audio thread to be starved. The decoder monitor must
|
||||
// be locked.
|
||||
void Wait(PRUint32 aMs);
|
||||
|
||||
// Dispatches an asynchronous event to update the media element's ready state.
|
||||
void UpdateReadyState();
|
||||
|
||||
// Resets playback timing data. Called when we seek, on the state machine
|
||||
// thread.
|
||||
void ResetPlayback();
|
||||
|
||||
// Returns the audio clock, if we have audio, or -1 if we don't.
|
||||
// Called on the state machine thread.
|
||||
PRInt64 GetAudioClock();
|
||||
|
||||
// Returns the presentation time of the first sample or frame in the media.
|
||||
// If the media has video, it returns the first video frame. The decoder
|
||||
// monitor must be held with exactly one lock count. Called on the state
|
||||
// machine thread.
|
||||
VideoData* FindStartTime();
|
||||
|
||||
// Finds the end time of the last page in the Ogg file, storing the value
|
||||
// in mEndTime if successful. The decoder must be held with exactly one lock
|
||||
// count. Called on the state machine thread.
|
||||
void FindEndTime();
|
||||
|
||||
// Performs YCbCr to RGB conversion, and pushes the image down the
|
||||
// rendering pipeline. Called on the state machine thread.
|
||||
void RenderVideoFrame(VideoData* aData);
|
||||
|
||||
// If we have video, display a video frame if it's time for display has
|
||||
// arrived, otherwise sleep until it's time for the next sample. Update
|
||||
// the current frame time as appropriate, and trigger ready state update.
|
||||
// The decoder monitor must be held with exactly one lock count. Called
|
||||
// on the state machine thread.
|
||||
void AdvanceFrame();
|
||||
|
||||
// Stops the decode threads. The decoder monitor must be held with exactly
|
||||
// one lock count. Called on the state machine thread.
|
||||
void StopDecodeThreads();
|
||||
|
||||
// Starts the decode threads. The decoder monitor must be held with exactly
|
||||
// one lock count. Called on the state machine thread.
|
||||
nsresult StartDecodeThreads();
|
||||
|
||||
// Reads the Ogg headers using the nsOggReader, and initializes playback.
|
||||
// Called on the state machine thread. The decoder monitor must be held with
|
||||
// exactly one lock count.
|
||||
void LoadOggHeaders();
|
||||
|
||||
// The main loop for the audio thread. Sent to the thread as
|
||||
// an NS_NEW_RUNNABLE_METHOD. This continually does blocking writes to
|
||||
// to audio stream to play audio data.
|
||||
void AudioLoop();
|
||||
|
||||
// Stop or pause playback of media. This has two modes, denoted by
|
||||
// aMode being either AUDIO_PAUSE or AUDIO_SHUTDOWN.
|
||||
//
|
||||
// AUDIO_PAUSE: Suspends the audio stream to be resumed later.
|
||||
// This does not close the OS based audio stream
|
||||
//
|
||||
// AUDIO_SHUTDOWN: Closes and destroys the audio stream and
|
||||
// releases any OS resources.
|
||||
//
|
||||
// The decoder monitor must be held with exactly one lock count. Called
|
||||
// on the state machine thread.
|
||||
enum eStopMode {AUDIO_PAUSE, AUDIO_SHUTDOWN};
|
||||
void StopPlayback(eStopMode aMode);
|
||||
|
||||
// Resume playback of media. Must be called with the decode monitor held.
|
||||
// This resumes a paused audio stream. The decoder monitor must be held with
|
||||
// exactly one lock count. Called on the state machine thread.
|
||||
void StartPlayback();
|
||||
|
||||
// Returns PR_TRUE if we're currently playing. The decoder monitor must
|
||||
// be held.
|
||||
PRBool IsPlaying();
|
||||
|
||||
// Stores presentation info about required for playback of the media.
|
||||
nsOggInfo mInfo;
|
||||
|
||||
// Monitor on mAudioStream. This monitor must be held in order to delete
|
||||
// or use the audio stream. This stops us destroying the audio stream
|
||||
// while it's being used on another thread (typically when it's being
|
||||
// written to on the audio thread).
|
||||
Monitor mAudioMonitor;
|
||||
|
||||
// The reader, don't call its methods with the decoder monitor held.
|
||||
// This is created in the play state machine's constructor, and destroyed
|
||||
// in the play state machine's destructor.
|
||||
nsAutoPtr<nsOggReader> mReader;
|
||||
|
||||
// The size of the decoded YCbCr frame.
|
||||
// Accessed on state machine thread.
|
||||
PRUint32 mCbCrSize;
|
||||
|
||||
// Accessed on state machine thread.
|
||||
nsAutoArrayPtr<unsigned char> mCbCrBuffer;
|
||||
|
||||
// Thread for pushing audio onto the audio hardware.
|
||||
// The "audio push thread".
|
||||
nsCOMPtr<nsIThread> mAudioThread;
|
||||
|
||||
// Thread for decoding video in background. The "decode thread".
|
||||
nsCOMPtr<nsIThread> mDecodeThread;
|
||||
|
||||
// The time that playback started from the system clock. This is used
|
||||
// for timing the display of audio frames when there's no audio.
|
||||
// Accessed only via the state machine thread.
|
||||
TimeStamp mPlayStartTime;
|
||||
|
||||
// The amount of time we've spent playing already the media. The current
|
||||
// playback position is therefore (mPlayDuration + (now - mPlayStartTime)).
|
||||
// Accessed only via the state machine thread.
|
||||
TimeDuration mPlayDuration;
|
||||
|
||||
// Time that buffering started. Used for buffering timeout and only
|
||||
// accessed on the state machine thread.
|
||||
TimeStamp mBufferingStart;
|
||||
|
||||
// Download position where we should stop buffering. Only
|
||||
// accessed on the state machine thread.
|
||||
PRInt64 mBufferingEndOffset;
|
||||
|
||||
// Start time of the media, in milliseconds. This is the presentation
|
||||
// time of the first sample decoded from the media, and is used to calculate
|
||||
// duration and as a bounds for seeking. Accessed on state machine and
|
||||
// main thread. Access controlled by decoder monitor.
|
||||
PRInt64 mStartTime;
|
||||
|
||||
// Time of the last page in the media, in milliseconds. This is the
|
||||
// end time of the last sample in the media. Accessed on state
|
||||
// machine and main thread. Access controlled by decoder monitor.
|
||||
PRInt64 mEndTime;
|
||||
|
||||
// Position to seek to in milliseconds when the seek state transition occurs.
|
||||
// The decoder monitor lock must be obtained before reading or writing
|
||||
// this value. Accessed on main and state machine thread.
|
||||
PRInt64 mSeekTime;
|
||||
|
||||
// The audio stream resource. Used on the state machine, audio, and main
|
||||
// threads. You must hold the mAudioMonitor, and must NOT hold the decoder
|
||||
// monitor when using the audio stream!
|
||||
nsAutoPtr<nsAudioStream> mAudioStream;
|
||||
|
||||
// The time of the current frame in milliseconds. This is referenced from
|
||||
// 0 which is the initial playback position. Set by the state machine
|
||||
// thread, and read-only from the main thread to get the current
|
||||
// time value. Synchronised via decoder monitor.
|
||||
PRInt64 mCurrentFrameTime;
|
||||
|
||||
// The presentation time of the first audio sample that was played. We can
|
||||
// add this to the audio stream position to determine the current audio time.
|
||||
// Accessed on audio and state machine thread. Synchronized by decoder monitor.
|
||||
PRInt64 mAudioStartTime;
|
||||
|
||||
// The end time of the last audio sample that's been pushed onto the audio
|
||||
// hardware. This will approximately be the end time of the audio stream,
|
||||
// unless another sample is pushed to the hardware.
|
||||
PRInt64 mAudioEndTime;
|
||||
|
||||
// The presentation time of the last video frame which has been displayed.
|
||||
// Accessed from the state machine thread.
|
||||
PRInt64 mVideoFrameTime;
|
||||
|
||||
// Volume of playback. 0.0 = muted. 1.0 = full volume. Read/Written
|
||||
// from the state machine and main threads. Synchronised via decoder
|
||||
// monitor.
|
||||
float mVolume;
|
||||
|
||||
// PR_TRUE if the media resource can be seeked. Accessed from the state
|
||||
// machine and main threads. Synchronised via decoder monitor.
|
||||
PRPackedBool mSeekable;
|
||||
|
||||
// PR_TRUE if an event to notify about a change in the playback
|
||||
// position has been queued, but not yet run. It is set to PR_FALSE when
|
||||
// the event is run. This allows coalescing of these events as they can be
|
||||
// produced many times per second. Synchronised via decoder monitor.
|
||||
// Accessed on main and state machine threads.
|
||||
PRPackedBool mPositionChangeQueued;
|
||||
|
||||
// PR_TRUE if the audio playback thread has finished. It is finished
|
||||
// when either all the audio samples in the Vorbis bitstream have completed
|
||||
// playing, or we've moved into shutdown state, and the threads are to be
|
||||
// destroyed. Written by the audio playback thread and read and written by
|
||||
// the state machine thread. Synchronised via decoder monitor.
|
||||
PRPackedBool mAudioCompleted;
|
||||
|
||||
// PR_TRUE if the decode thread has indicated that we need to buffer.
|
||||
// Accessed by the decode thread and the state machine thread.
|
||||
// Synchronised via the decoder monitor.
|
||||
PRPackedBool mBufferExhausted;
|
||||
|
||||
// PR_TRUE if mDuration has a value obtained from an HTTP header.
|
||||
// Accessed on the state machine thread.
|
||||
PRPackedBool mGotDurationFromHeader;
|
||||
|
||||
// PR_FALSE while decode threads should be running. Accessed on audio,
|
||||
// state machine and decode threads. Syncrhonised by decoder monitor.
|
||||
PRPackedBool mStopDecodeThreads;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,531 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: ML 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 code.
|
||||
*
|
||||
* 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):
|
||||
* Chris Double <chris.double@double.co.nz>
|
||||
* Chris Pearce <chris@pearce.org.nz>
|
||||
*
|
||||
* 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 ***** */
|
||||
#if !defined(nsOggReader_h_)
|
||||
#define nsOggReader_h_
|
||||
|
||||
#include <nsDeque.h>
|
||||
#include "nsOggCodecState.h"
|
||||
#include <ogg/ogg.h>
|
||||
#include <theora/theoradec.h>
|
||||
#include <vorbis/codec.h>
|
||||
#include "prmon.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsRect.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
class nsOggPlayStateMachine;
|
||||
|
||||
using mozilla::Monitor;
|
||||
using mozilla::MonitorAutoEnter;
|
||||
using mozilla::TimeDuration;
|
||||
using mozilla::TimeStamp;
|
||||
|
||||
// Holds chunk a decoded sound samples.
|
||||
class SoundData {
|
||||
public:
|
||||
SoundData(PRInt64 aTime,
|
||||
PRInt64 aDuration,
|
||||
PRUint32 aSamples,
|
||||
float* aData,
|
||||
PRUint32 aChannels)
|
||||
: mTime(aTime),
|
||||
mDuration(aDuration),
|
||||
mSamples(aSamples),
|
||||
mAudioData(aData),
|
||||
mChannels(aChannels)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SoundData);
|
||||
}
|
||||
|
||||
SoundData(PRInt64 aDuration,
|
||||
PRUint32 aSamples,
|
||||
float* aData,
|
||||
PRUint32 aChannels)
|
||||
: mTime(-1),
|
||||
mDuration(aDuration),
|
||||
mSamples(aSamples),
|
||||
mAudioData(aData),
|
||||
mChannels(aChannels)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SoundData);
|
||||
}
|
||||
|
||||
~SoundData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SoundData);
|
||||
}
|
||||
|
||||
PRUint32 AudioDataLength() {
|
||||
return mChannels * mSamples;
|
||||
}
|
||||
|
||||
PRInt64 mTime; // Start time of samples in ms.
|
||||
const PRInt64 mDuration; // In ms.
|
||||
const PRUint32 mSamples;
|
||||
const PRUint32 mChannels;
|
||||
nsAutoArrayPtr<float> mAudioData;
|
||||
};
|
||||
|
||||
// Holds a decoded Theora frame, in YCbCr format. These are queued in the reader.
|
||||
class VideoData {
|
||||
public:
|
||||
|
||||
// Constructs a VideoData object. Makes a copy of YCbCr data in aBuffer.
|
||||
// This may return nsnull if we run out of memory when allocating buffers
|
||||
// to store the frame.
|
||||
static VideoData* Create(PRInt64 aTime,
|
||||
th_ycbcr_buffer aBuffer,
|
||||
PRBool aKeyframe,
|
||||
PRInt64 aGranulepos);
|
||||
|
||||
// Constructs a duplicate VideoData object. This intrinsically tells the
|
||||
// player that it does not need to update the displayed frame when this
|
||||
// frame is played; this frame is identical to the previous.
|
||||
static VideoData* CreateDuplicate(PRInt64 aTime,
|
||||
PRInt64 aGranulepos)
|
||||
{
|
||||
return new VideoData(aTime, aGranulepos);
|
||||
}
|
||||
|
||||
~VideoData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(VideoData);
|
||||
for (PRUint32 i = 0; i < 3; ++i) {
|
||||
delete mBuffer[i].data;
|
||||
}
|
||||
}
|
||||
|
||||
// Start time of frame in milliseconds.
|
||||
PRInt64 mTime;
|
||||
PRInt64 mGranulepos;
|
||||
|
||||
th_ycbcr_buffer mBuffer;
|
||||
|
||||
// When PR_TRUE, denotes that this frame is identical to the frame that
|
||||
// came before; it's a duplicate. mBuffer will be empty.
|
||||
PRPackedBool mDuplicate;
|
||||
PRPackedBool mKeyframe;
|
||||
|
||||
private:
|
||||
VideoData(PRInt64 aTime, PRInt64 aGranulepos) :
|
||||
mTime(aTime),
|
||||
mGranulepos(aGranulepos),
|
||||
mDuplicate(PR_TRUE),
|
||||
mKeyframe(PR_FALSE)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
memset(&mBuffer, 0, sizeof(th_ycbcr_buffer));
|
||||
}
|
||||
|
||||
VideoData(PRInt64 aTime,
|
||||
PRBool aKeyframe,
|
||||
PRInt64 aGranulepos)
|
||||
: mTime(aTime),
|
||||
mGranulepos(aGranulepos),
|
||||
mDuplicate(PR_FALSE),
|
||||
mKeyframe(aKeyframe)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Thread and type safe wrapper around nsDeque.
|
||||
template <class T>
|
||||
class MediaQueueDeallocator : public nsDequeFunctor {
|
||||
virtual void* operator() (void* anObject) {
|
||||
delete static_cast<T*>(anObject);
|
||||
return nsnull;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class MediaQueue : private nsDeque {
|
||||
public:
|
||||
|
||||
MediaQueue()
|
||||
: nsDeque(new MediaQueueDeallocator<T>()),
|
||||
mMonitor("mediaqueue"),
|
||||
mEndOfStream(0)
|
||||
{}
|
||||
|
||||
~MediaQueue() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
inline PRInt32 GetSize() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return nsDeque::GetSize();
|
||||
}
|
||||
|
||||
inline void Push(T* aItem) {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
nsDeque::Push(aItem);
|
||||
}
|
||||
|
||||
inline void PushFront(T* aItem) {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
nsDeque::PushFront(aItem);
|
||||
}
|
||||
|
||||
inline T* Pop() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return static_cast<T*>(nsDeque::Pop());
|
||||
}
|
||||
|
||||
inline T* PopFront() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return static_cast<T*>(nsDeque::PopFront());
|
||||
}
|
||||
|
||||
inline T* Peek() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return static_cast<T*>(nsDeque::Peek());
|
||||
}
|
||||
|
||||
inline T* PeekFront() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return static_cast<T*>(nsDeque::PeekFront());
|
||||
}
|
||||
|
||||
inline void Empty() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
nsDeque::Empty();
|
||||
}
|
||||
|
||||
inline void Erase() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
nsDeque::Erase();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
while (GetSize() > 0) {
|
||||
T* x = PopFront();
|
||||
delete x;
|
||||
}
|
||||
mEndOfStream = PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool AtEndOfStream() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return GetSize() == 0 && mEndOfStream;
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
mEndOfStream = PR_TRUE;
|
||||
}
|
||||
|
||||
// Returns the approximate number of milliseconds of samples in the queue.
|
||||
PRInt64 Duration() {
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
if (GetSize() < 2) {
|
||||
return 0;
|
||||
}
|
||||
T* last = Peek();
|
||||
T* first = PeekFront();
|
||||
return last->mTime - first->mTime;
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor mMonitor;
|
||||
|
||||
// PR_TRUE when we've decoded the last packet in the bitstream for which
|
||||
// we're queueing sample-data.
|
||||
PRBool mEndOfStream;
|
||||
};
|
||||
|
||||
// Represents a section of contiguous media, with a start and end offset,
|
||||
// and the timestamps of the start and end of that range. Used to denote the
|
||||
// extremities of a range to seek in.
|
||||
class ByteRange {
|
||||
public:
|
||||
ByteRange() :
|
||||
mOffsetStart(0),
|
||||
mOffsetEnd(0),
|
||||
mTimeStart(0),
|
||||
mTimeEnd(0)
|
||||
{}
|
||||
|
||||
ByteRange(PRInt64 aOffsetStart,
|
||||
PRInt64 aOffsetEnd,
|
||||
PRInt64 aTimeStart,
|
||||
PRInt64 aTimeEnd)
|
||||
: mOffsetStart(aOffsetStart),
|
||||
mOffsetEnd(aOffsetEnd),
|
||||
mTimeStart(aTimeStart),
|
||||
mTimeEnd(aTimeEnd)
|
||||
{}
|
||||
|
||||
PRBool IsNull() {
|
||||
return mOffsetStart == 0 &&
|
||||
mOffsetEnd == 0 &&
|
||||
mTimeStart == 0 &&
|
||||
mTimeEnd == 0;
|
||||
}
|
||||
|
||||
PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
|
||||
PRInt64 mTimeStart, mTimeEnd; // in ms.
|
||||
};
|
||||
|
||||
// Stores info relevant to presenting media samples.
|
||||
class nsOggInfo {
|
||||
public:
|
||||
nsOggInfo()
|
||||
: mFramerate(0.0),
|
||||
mAspectRatio(1.0),
|
||||
mCallbackPeriod(1),
|
||||
mAudioRate(0),
|
||||
mAudioChannels(0),
|
||||
mFrame(0,0),
|
||||
mHasAudio(PR_FALSE),
|
||||
mHasVideo(PR_FALSE)
|
||||
{}
|
||||
|
||||
// Frames per second.
|
||||
float mFramerate;
|
||||
|
||||
// Aspect ratio, as stored in the video header packet.
|
||||
float mAspectRatio;
|
||||
|
||||
// Length of a video frame in milliseconds, or the callback period if
|
||||
// there's no audio.
|
||||
PRUint32 mCallbackPeriod;
|
||||
|
||||
// Samples per second.
|
||||
PRUint32 mAudioRate;
|
||||
|
||||
// Number of audio channels.
|
||||
PRUint32 mAudioChannels;
|
||||
|
||||
// Dimensions of the video frame.
|
||||
nsIntSize mFrame;
|
||||
|
||||
// The picture region inside the video frame to be displayed.
|
||||
nsIntRect mPicture;
|
||||
|
||||
// The offset of the first non-header page in the file, in bytes.
|
||||
// Used to seek to the start of the media.
|
||||
PRInt64 mDataOffset;
|
||||
|
||||
// PR_TRUE if we have an active audio bitstream.
|
||||
PRPackedBool mHasAudio;
|
||||
|
||||
// PR_TRUE if we have an active video bitstream.
|
||||
PRPackedBool mHasVideo;
|
||||
};
|
||||
|
||||
// Encapsulates the decoding and reading of Ogg data. Reading can be done
|
||||
// on either the state machine thread (when loading and seeking) or on
|
||||
// the reader thread (when it's reading and decoding). The reader encapsulates
|
||||
// the reading state and maintains it's own monitor to ensure thread safety
|
||||
// and correctness. Never hold the nsOggDecoder's monitor when calling into
|
||||
// this class.
|
||||
class nsOggReader : public nsRunnable {
|
||||
public:
|
||||
nsOggReader(nsOggPlayStateMachine* aStateMachine);
|
||||
~nsOggReader();
|
||||
|
||||
PRBool HasAudio()
|
||||
{
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return mVorbisState != 0 && mVorbisState->mActive;
|
||||
}
|
||||
|
||||
PRBool HasVideo()
|
||||
{
|
||||
MonitorAutoEnter mon(mMonitor);
|
||||
return mTheoraState != 0 && mTheoraState->mActive;
|
||||
}
|
||||
|
||||
// Read header data for all bitstreams in the Ogg file. Fills aInfo with
|
||||
// the data required to present the media. Returns NS_OK on success,
|
||||
// or NS_ERROR_FAILURE on failure.
|
||||
nsresult ReadOggHeaders(nsOggInfo& aInfo);
|
||||
|
||||
// Stores the presentation time of the first sample in the stream in
|
||||
// aOutStartTime, and returns the first video sample, if we have video.
|
||||
VideoData* FindStartTime(PRInt64 aOffset,
|
||||
PRInt64& aOutStartTime);
|
||||
|
||||
// Returns the end time of the last page which occurs before aEndOffset.
|
||||
// This will not read past aEndOffset. Returns -1 on failure.
|
||||
PRInt64 FindEndTime(PRInt64 aEndOffset);
|
||||
|
||||
// Decodes one Vorbis page, enqueuing the audio data in mAudioQueue.
|
||||
// Returns PR_TRUE when there's more audio to decode, PR_FALSE if the
|
||||
// audio is finished, end of file has been reached, or an un-recoverable
|
||||
// read error has occured.
|
||||
PRBool DecodeAudioPage();
|
||||
|
||||
// Reads and decodes one video frame. If the Theora granulepos has not
|
||||
// been captured, it may read several packets until one with a granulepos
|
||||
// has been captured, to ensure that all packets read have valid time info.
|
||||
// Packets with a timestamp less than aTimeThreshold will be decoded (unless
|
||||
// they're not keyframes and aKeyframeSkip is PR_TRUE), but will not be
|
||||
// added to the queue.
|
||||
PRBool DecodeVideoPage(PRBool &aKeyframeSkip,
|
||||
PRInt64 aTimeThreshold);
|
||||
|
||||
// Moves the decode head to aTime milliseconds. aStartTime and aEndTime
|
||||
// denote the start and end times of the media.
|
||||
nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime);
|
||||
|
||||
// Queue of audio samples. This queue is threadsafe.
|
||||
MediaQueue<SoundData> mAudioQueue;
|
||||
|
||||
// Queue of video samples. This queue is threadsafe.
|
||||
MediaQueue<VideoData> mVideoQueue;
|
||||
|
||||
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
||||
// on failure.
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
|
||||
// Ogg reader decode function. Matches DecodeVideoPage() and
|
||||
// DecodeAudioPage().
|
||||
typedef PRBool (nsOggReader::*DecodeFn)();
|
||||
|
||||
// Calls aDecodeFn on *this until aQueue has a sample, whereupon
|
||||
// we return the first sample.
|
||||
template<class Data>
|
||||
Data* DecodeToFirstData(DecodeFn aDecodeFn,
|
||||
MediaQueue<Data>& aQueue);
|
||||
|
||||
// Wrapper so that DecodeVideoPage(PRBool&,PRInt64) can be called from
|
||||
// DecodeToFirstData().
|
||||
PRBool DecodeVideoPage() {
|
||||
PRBool f = PR_FALSE;
|
||||
return DecodeVideoPage(f, 0);
|
||||
}
|
||||
|
||||
// Decodes one packet of Vorbis data, storing the resulting chunks of
|
||||
// PCM samples in aChunks.
|
||||
nsresult DecodeVorbis(nsTArray<SoundData*>& aChunks,
|
||||
ogg_packet* aPacket);
|
||||
|
||||
// May return NS_ERROR_OUT_OF_MEMORY.
|
||||
nsresult DecodeTheora(nsTArray<VideoData*>& aFrames,
|
||||
ogg_packet* aPacket);
|
||||
|
||||
// Resets all state related to decoding, emptying all buffers etc.
|
||||
nsresult ResetDecode();
|
||||
|
||||
// Read a page of data from the Ogg file. Returns the offset of the start
|
||||
// of the page, or -1 if the page read failed.
|
||||
PRInt64 ReadOggPage(ogg_page* aPage);
|
||||
|
||||
// Read a packet for an Ogg bitstream/codec state. Returns PR_TRUE on
|
||||
// success, or PR_FALSE if the read failed.
|
||||
PRBool ReadOggPacket(nsOggCodecState* aCodecState, ogg_packet* aPacket);
|
||||
|
||||
// Performs a seek bisection to move the media stream's read cursor to the
|
||||
// last ogg page boundary which has end time before aTarget ms on both the
|
||||
// Theora and Vorbis bitstreams. Limits its search to data inside aRange;
|
||||
// i.e. it will only read inside of the aRange's start and end offsets.
|
||||
// aFuzz is the number of ms of leniency we'll allow; we'll terminate the
|
||||
// seek when we land in the range (aTime - aFuzz, aTime) ms.
|
||||
nsresult SeekBisection(PRInt64 aTarget,
|
||||
const ByteRange& aRange,
|
||||
PRUint32 aFuzz);
|
||||
|
||||
// Fills aRanges with ByteRanges denoting the sections of the media which
|
||||
// have been downloaded and are stored in the media cache. The reader
|
||||
// monitor must must be held with exactly one lock count. The nsMediaStream
|
||||
// must be pinned while calling this.
|
||||
nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges);
|
||||
|
||||
// Returns the range in which you should perform a seek bisection if
|
||||
// you wish to seek to aTarget ms, given the known (buffered) byte ranges
|
||||
// in aRanges. If aExact is PR_TRUE, we only return an exact copy of a
|
||||
// range in which aTarget lies, or a null range if aTarget isn't contained
|
||||
// in any of the (buffered) ranges. Otherwise, when aExact is PR_FALSE,
|
||||
// we'll construct the smallest possible range we can, based on the times
|
||||
// and byte offsets known in aRanges. We can then use this to minimize our
|
||||
// bisection's search space when the target isn't in a known buffered range.
|
||||
ByteRange GetSeekRange(const nsTArray<ByteRange>& aRanges,
|
||||
PRInt64 aTarget,
|
||||
PRInt64 aStartTime,
|
||||
PRInt64 aEndTime,
|
||||
PRBool aExact);
|
||||
|
||||
// The lock which we hold whenever we read or decode. This ensures the thread
|
||||
// safety of the reader and its data fields.
|
||||
Monitor mMonitor;
|
||||
|
||||
// Reference to the owning player state machine object. Do not hold the
|
||||
// reader's monitor when accessing the player.
|
||||
nsOggPlayStateMachine* mPlayer;
|
||||
|
||||
// Maps Ogg serialnos to nsOggStreams.
|
||||
nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
|
||||
|
||||
// Decode state of the Theora bitstream we're decoding, if we have video.
|
||||
nsTheoraState* mTheoraState;
|
||||
|
||||
// Decode state of the Vorbis bitstream we're decoding, if we have audio.
|
||||
nsVorbisState* mVorbisState;
|
||||
|
||||
// Ogg decoding state.
|
||||
ogg_sync_state mOggState;
|
||||
|
||||
// The offset of the end of the last page we've read, or the start of
|
||||
// the page we're about to read.
|
||||
PRInt64 mPageOffset;
|
||||
|
||||
// The offset of the start of the first non-header page in the file.
|
||||
// Used to seek to media start time.
|
||||
PRInt64 mDataOffset;
|
||||
|
||||
// The granulepos of the last decoded Theora frame.
|
||||
PRInt64 mTheoraGranulepos;
|
||||
|
||||
// The granulepos of the last decoded Vorbis sample.
|
||||
PRInt64 mVorbisGranulepos;
|
||||
|
||||
// Number of milliseconds of data video/audio data held in a frame.
|
||||
PRUint32 mCallbackPeriod;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -104,6 +104,7 @@ _TEST_FILES = \
|
|||
test_playback_errors.html \
|
||||
test_reactivate.html \
|
||||
test_readyState.html \
|
||||
test_seek.html \
|
||||
test_seek2.html \
|
||||
test_volume.html \
|
||||
use_large_cache.js \
|
||||
|
@ -113,23 +114,17 @@ _TEST_FILES = \
|
|||
# test_resume.html \
|
||||
#
|
||||
|
||||
ifneq ($(OS_ARCH),WINNT)
|
||||
# These tests are disabled on windows until we
|
||||
# figure out the random failures. See bug 475369.
|
||||
_TEST_FILES += \
|
||||
test_seek.html \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
# Ogg sample files
|
||||
_TEST_FILES += \
|
||||
320x240.ogv \
|
||||
320x240.allow-origin.ogv \
|
||||
320x240.allow-origin.ogv^headers^ \
|
||||
448636.ogv \
|
||||
audio-overhang.ogg \
|
||||
beta-phrasebook.ogg \
|
||||
bogus.ogv \
|
||||
bug495129.ogv \
|
||||
bug495794.ogg \
|
||||
bug461281.ogg \
|
||||
bug482461.ogv \
|
||||
bug498380.ogv \
|
||||
|
@ -157,6 +152,7 @@ _TEST_FILES += \
|
|||
short-video.ogv \
|
||||
small-shot.ogg \
|
||||
sound.ogg \
|
||||
video-overhang.ogg \
|
||||
$(NULL)
|
||||
|
||||
# Wave sample files
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -40,7 +40,7 @@ var gPlayTests = [
|
|||
// Ogg stream with eof marker
|
||||
{ name:"bug461281.ogg", type:"application/ogg" },
|
||||
// oggz-chop stream
|
||||
{ name:"bug482461.ogv", type:"video/ogg", duration:4.24 },
|
||||
{ name:"bug482461.ogv", type:"video/ogg", duration:4.34 },
|
||||
// With first frame a "duplicate" (empty) frame.
|
||||
{ name:"bug500311.ogv", type:"video/ogg", duration:1.96 },
|
||||
// Small audio file
|
||||
|
@ -50,9 +50,10 @@ var gPlayTests = [
|
|||
// First Theora data packet is zero bytes.
|
||||
{ name:"bug504613.ogv", type:"video/ogg" },
|
||||
// Multiple audio streams.
|
||||
{ name:"bug516323.ogv", type:"video/ogg", duration:4.424 },
|
||||
{ name:"bug516323.ogv", type:"video/ogg", duration:4.208 },
|
||||
|
||||
// Encoded with vorbis beta1, includes unusually sized codebooks
|
||||
{ name:"beta-phrasebook.ogg", type:"audio/ogg", duration:4 },
|
||||
{ name:"beta-phrasebook.ogg", type:"audio/ogg", duration:4.01 },
|
||||
// Small file, only 1 frame with audio only.
|
||||
{ name:"bug520493.ogg", type:"audio/ogg", duration:0.458 },
|
||||
// Small file with vorbis comments with 0 length values and names.
|
||||
|
@ -61,13 +62,17 @@ var gPlayTests = [
|
|||
// Various weirdly formed Ogg files
|
||||
{ name:"bug499519.ogv", type:"video/ogg", duration:0.24 },
|
||||
{ name:"bug506094.ogv", type:"video/ogg", duration:0 },
|
||||
{ name:"bug501279.ogg", type:"audio/ogg", duration:0 },
|
||||
{ name:"bug498855-1.ogv", type:"video/ogg", duration:0.2 },
|
||||
{ name:"bug498855-2.ogv", type:"video/ogg", duration:0.2 },
|
||||
{ name:"bug498855-3.ogv", type:"video/ogg", duration:0.2 },
|
||||
{ name:"bug504644.ogv", type:"video/ogg", duration:1.56 },
|
||||
{ name:"bug498855-1.ogv", type:"video/ogg", duration:0.24 },
|
||||
{ name:"bug498855-2.ogv", type:"video/ogg", duration:0.24 },
|
||||
{ name:"bug498855-3.ogv", type:"video/ogg", duration:0.24 },
|
||||
{ name:"bug504644.ogv", type:"video/ogg", duration:1.6 },
|
||||
{ name:"chain.ogv", type:"video/ogg", duration:Number.NaN },
|
||||
{ name:"bug523816.ogv", type:"video/ogg", duration:0.5 },
|
||||
{ name:"bug523816.ogv", type:"video/ogg", duration:0.533 },
|
||||
{ name:"bug495129.ogv", type:"video/ogg", duration:2.41 },
|
||||
{ name:"bug498380.ogv", type:"video/ogg" },
|
||||
{ name:"bug495794.ogg", type:"audio/ogg", duration:0.3},
|
||||
{ name:"audio-overhang.ogg", type:"audio/ogg", duration:2.3},
|
||||
{ name:"video-overhang.ogg", type:"audio/ogg", duration:3.966},
|
||||
|
||||
{ name:"bogus.duh", type:"bogus/duh" }
|
||||
];
|
||||
|
@ -82,9 +87,8 @@ var gErrorTests = [
|
|||
{ name:"bogus.wav", type:"audio/x-wav" },
|
||||
{ name:"bogus.ogv", type:"video/ogg" },
|
||||
{ name:"448636.ogv", type:"video/ogg" },
|
||||
{ name:"bug495129.ogv", type:"video/ogg", duration:2.52 },
|
||||
{ name:"bug504843.ogv", type:"video/ogg", duration:1.233 },
|
||||
{ name:"bug498380.ogv", type:"video/ogg" },
|
||||
{ name:"bug504843.ogv", type:"video/ogg" },
|
||||
{ name:"bug501279.ogg", type:"audio/ogg" },
|
||||
{ name:"bogus.duh", type:"bogus/duh" }
|
||||
];
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ function startTest() {
|
|||
if (completed)
|
||||
return false;
|
||||
var v = document.getElementById('v');
|
||||
is(Math.round(v.duration*1000), 3966, "Check duration of video: " + v.duration);
|
||||
is(Math.round(v.duration*1000), 3999, "Check duration of video: " + v.duration);
|
||||
completed = true;
|
||||
clearTimeout(timeout);
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -158,7 +158,7 @@ function checkState() {
|
|||
"Ready state of " + gMedia.readyState + " was leaked");
|
||||
test_is(gMedia.seeking, false, "Seeking leaked");
|
||||
test_is(gMedia.currentTime, 0, "Leaked currentTime");
|
||||
test_is(gMedia.duration, 0, "Leaked duration");
|
||||
test_ok(isNaN(gMedia.duration), "Leaked duration");
|
||||
test_is(gMedia.paused, true, "Paused leaked");
|
||||
test_is(gMedia.ended, false, "Ended leaked");
|
||||
test_is(gMedia.autoplay, false, "Autoplay leaked");
|
||||
|
|
|
@ -30,7 +30,9 @@ function startTests() {
|
|||
continue;
|
||||
|
||||
v.src = test.name;
|
||||
v.name = test.name;
|
||||
var check = function(test, v) { return function() {
|
||||
is(test.name, v.name, "Name should match test.name #1");
|
||||
checkMetadata(test.name, v, test);
|
||||
}}(test, v);
|
||||
var noLoad = function(test, v) { return function() {
|
||||
|
@ -39,8 +41,9 @@ function startTests() {
|
|||
var checkEnded = function(test, v) { return function() {
|
||||
if (test.duration) {
|
||||
ok(Math.abs(v.currentTime - test.duration) < 0.1,
|
||||
test.name + " current time at end: " + v.currentTime);
|
||||
test.name + " current time at end: " + v.currentTime + " should be: " + test.duration);
|
||||
}
|
||||
is(test.name, v.name, "Name should match test.name #2");
|
||||
is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
|
||||
ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
|
||||
ok(v.ended, test.name + " checking playback has ended");
|
||||
|
@ -50,6 +53,7 @@ function startTests() {
|
|||
}
|
||||
}}(test, v);
|
||||
var checkSuspended = function(test, v) { return function() {
|
||||
is(test.name, v.name, "Name should match test.name #3");
|
||||
if (v.seenSuspend)
|
||||
return;
|
||||
|
||||
|
@ -61,12 +65,13 @@ function startTests() {
|
|||
}
|
||||
}}(test, v);
|
||||
v.prevTime = 0;
|
||||
var timeUpdate = function(v) { return function() {
|
||||
var timeUpdate = function(test, v) { return function() {
|
||||
is(test.name, v.name, "Name should match test.name #4");
|
||||
ok(v.prevTime <= v.currentTime,
|
||||
test.name + " time should run forwards: p=" +
|
||||
v.prevTime + " c=" + v.currentTime);
|
||||
v.prevTime = v.currentTime;
|
||||
}}(v);
|
||||
}}(test, v);
|
||||
v.addEventListener("load", noLoad, false);
|
||||
v.addEventListener("loadedmetadata", check, false);
|
||||
v.addEventListener("timeupdate", timeUpdate, false);
|
||||
|
|
|
@ -29,10 +29,8 @@ function timeUpdated() {
|
|||
|
||||
var v = document.getElementById('v');
|
||||
ok(v.currentTime > lastTime,
|
||||
"Check currentTime of " +
|
||||
v.currentTime +
|
||||
" is greater than last time of " +
|
||||
lastTime);
|
||||
"currentTime (" + v.currentTime +
|
||||
") should be greater than last time (" + lastTime + ")");
|
||||
lastTime = v.currentTime;
|
||||
return false;
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
|
@ -648,7 +648,7 @@ nsWaveStateMachine::Run()
|
|||
PRUint32 lengthInSamples = got / sampleSize;
|
||||
|
||||
monitor.Exit();
|
||||
mAudioStream->Write(buf.get(), lengthInSamples);
|
||||
mAudioStream->Write(buf.get(), lengthInSamples, PR_FALSE);
|
||||
monitor.Enter();
|
||||
|
||||
FirePositionChanged(PR_FALSE);
|
||||
|
|
|
@ -124,36 +124,7 @@ public:
|
|||
PRUint32 Serial() const { return mSerial; }
|
||||
void SetSerial(PRUint32 aIndex) { mSerial = aIndex; }
|
||||
|
||||
nsrefcnt AddRef()
|
||||
{
|
||||
if (mRefCnt == PR_UINT32_MAX) {
|
||||
NS_WARNING("refcount overflow, leaking nsSMILInstanceTime");
|
||||
return mRefCnt;
|
||||
}
|
||||
NS_ASSERT_OWNINGTHREAD(_class);
|
||||
NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(),
|
||||
"nsSMILInstanceTime addref isn't thread-safe!");
|
||||
++mRefCnt;
|
||||
NS_LOG_ADDREF(this, mRefCnt, "nsSMILInstanceTime", sizeof(*this));
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
nsrefcnt Release()
|
||||
{
|
||||
if (mRefCnt == PR_UINT32_MAX) {
|
||||
NS_WARNING("refcount overflow, leaking nsSMILInstanceTime");
|
||||
return mRefCnt;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(),
|
||||
"nsSMILInstanceTime release isn't thread-safe!");
|
||||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "nsSMILInstanceTime");
|
||||
if (mRefCnt == 0) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
NS_INLINE_DECL_REFCOUNTING(nsSMILInstanceTime)
|
||||
|
||||
protected:
|
||||
void SetBaseInterval(nsSMILInterval* aBaseInterval);
|
||||
|
@ -162,9 +133,6 @@ protected:
|
|||
|
||||
nsSMILTimeValue mTime;
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
// Internal flags used for represent behaviour of different instance times`
|
||||
enum {
|
||||
// Indicates if this instance time should be removed when the owning timed
|
||||
|
|
|
@ -1132,7 +1132,7 @@ MappedAttrParser::ParseMappedAttrValue(nsIAtom* aMappedAttrName,
|
|||
nsCSSProps::LookupProperty(nsAtomString(aMappedAttrName));
|
||||
PRBool changed; // outparam for ParseProperty. (ignored)
|
||||
mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI,
|
||||
mNodePrincipal, mDecl, &changed);
|
||||
mNodePrincipal, mDecl, &changed, PR_FALSE);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICSSStyleRule>
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "nsSVGValue.h"
|
||||
#include <math.h>
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
const double radPerDegree = 2.0*3.1415926535 / 360.0;
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
|
||||
#include "nsIDOMSVGMatrix.h"
|
||||
#include "gfxMatrix.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
nsresult
|
||||
NS_NewSVGMatrix(nsIDOMSVGMatrix** result,
|
||||
|
|
|
@ -24,6 +24,7 @@ load 415192-1.xul
|
|||
load 420233-1.xhtml
|
||||
load 421997-1.xhtml
|
||||
load 432813-1.xhtml
|
||||
load 454820-1.html
|
||||
load 460665-1.xhtml
|
||||
load 464863-1.xhtml
|
||||
load 472260-1.xhtml
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "nsClassHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class nsXBLPrototypeBinding;
|
||||
class nsIContent;
|
||||
|
@ -77,24 +78,7 @@ public:
|
|||
* which are queued to fire their constructors.
|
||||
*/
|
||||
|
||||
nsrefcnt AddRef()
|
||||
{
|
||||
++mRefCnt;
|
||||
NS_LOG_ADDREF(this, mRefCnt, "nsXBLBinding", sizeof(nsXBLBinding));
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
nsrefcnt Release()
|
||||
{
|
||||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "nsXBLBinding");
|
||||
if (mRefCnt == 0) {
|
||||
mRefCnt = 1;
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
NS_INLINE_DECL_REFCOUNTING(nsXBLBinding)
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding)
|
||||
|
||||
|
@ -172,7 +156,6 @@ public:
|
|||
// MEMBER VARIABLES
|
||||
protected:
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
|
||||
nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
|
||||
nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче