Merge from mozilla-central.

This commit is contained in:
David Anderson 2012-02-02 15:06:21 -08:00
commit d7f19cfeca
387 changed files with 10215 additions and 3491 deletions

View File

@ -105,7 +105,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
roles::PUSHBUTTON,
kUseMapRole,
eNoValue,
eClickAction,
ePressAction,
eNoLiveAttr,
kNoReqStates,
eARIAPressed
@ -219,15 +219,6 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
eNoLiveAttr,
kNoReqStates
},
{
"label",
roles::LABEL,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
kNoReqStates
},
{
"link",
roles::LINK,

View File

@ -78,6 +78,7 @@ enum EActionRule
eNoAction,
eActivateAction,
eClickAction,
ePressAction,
eCheckUncheckAction,
eExpandAction,
eJumpAction,

View File

@ -1828,6 +1828,10 @@ nsAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
aName.AssignLiteral("click");
return NS_OK;
case ePressAction:
aName.AssignLiteral("press");
return NS_OK;
case eCheckUncheckAction:
if (states & states::CHECKED)
aName.AssignLiteral("uncheck");

View File

@ -49,6 +49,15 @@
new scrollingChecker(getAccessible("bottom1"))
]
},
{ // jump again (test for bug 437607)
ID: "anchor1",
actionName: "jump",
actionIndex: 0,
events: CLICK_EVENTS,
eventSeq: [
new scrollingChecker(getAccessible("bottom1"))
]
},
{
ID: "anchor2",
actionName: "jump",

View File

@ -27,7 +27,7 @@
},
{
ID: "button",
actionName: "click",
actionName: "press",
events: CLICK_EVENTS
},
{

View File

@ -49,6 +49,22 @@ function currentTabDocument()
return currentBrowser().contentDocument;
}
/**
* Return browser element of the tab at the given index.
*/
function browserAt(aIndex)
{
return tabBrowser().getBrowserAtIndex(aIndex);
}
/**
* Return DOM document of the tab at the given index.
*/
function tabDocumentAt(aIndex)
{
return browserAt(aIndex).contentDocument;
}
/**
* Return input element of address bar.
*/

View File

@ -82,6 +82,7 @@ _TEST_FILES =\
test_menu.xul \
test_mutation.html \
test_mutation.xhtml \
test_scroll.xul \
test_selection_aria.html \
test_selection.html \
test_selection.xul \

View File

@ -1,13 +1,6 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
@ -16,7 +9,7 @@
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
src="chrome://mochikit/content/chrome-harness.js"/>
<script type="application/javascript"
src="../common.js" />
@ -26,95 +19,74 @@
src="../states.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"/>
src="../browser.js"></script>
<script type="application/javascript">
<![CDATA[
////////////////////////////////////////////////////////////////////////////
// Hacks to make xul:tabbrowser work
const Ci = Components.interfaces;
const CC = Components.classes;
Components.utils.import("resource://gre/modules/Services.jsm");
var handleDroppedLink = null;
var XULBrowserWindow = {
isBusy: false,
setOverLink: function (link, b) {
}
};
var gURLBar = {
focused: false
};
var gFindBarInitialized = false;
function goSetCommandEnabled() {}
////////////////////////////////////////////////////////////////////////////
// Tests
function getTabDocument()
function getAnchorJumpInTabDocument(aTabIdx)
{
return getNode("tabBrowser").selectedBrowser.contentDocument;
var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
return tabDoc.querySelector("a[name='link1']");
}
function getAnchorJumpInTabDocument()
function loadTab(aURL)
{
return getTabDocument().querySelector("a[name='link1']");
}
function loadTab(aTabBrowserID, aURL)
{
function loadTabChecker()
{
this.type = EVENT_REORDER;
this.match = function loadTabChecker_match(aEvent)
{
var target = aEvent.accessible;
if (target.role == ROLE_INTERNAL_FRAME &&
target.parent.parent == getAccessible(getNode(aTabBrowserID).mTabBox.tabpanels)) {
return true;
}
return false;
}
}
this.eventSeq = [ new loadTabChecker() ];
this.eventSeq = [
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
];
this.invoke = function loadTab_invoke()
{
getNode(aTabBrowserID).loadURI(aURL);
tabBrowser().loadURI(aURL);
}
this.getID = function loadTab_getID()
{
return "load tab " + aURL + " for " + prettyName(aTabBrowserID);
return "load tab: " + aURL;
}
}
function advanceFocusIntoTab(aTabBrowserID)
function loadTabInBackground(aURL)
{
this.eventSeq = [
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument, 1)
];
this.invoke = function loadTabInBackground_invoke()
{
tabBrowser().loadOneTab(aURL, null, "", null, true);
}
this.getID = function loadTabInBackground_getID()
{
return "load tab in background: " + aURL;
}
}
function switchToBackgroundTab()
{
this.eventSeq = [
new focusChecker(getTabDocument),
new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
];
this.invoke = function advanceFocusIntoTab_invoke()
this.invoke = function switchToBackgroundTab_invoke()
{
var tabDoc = getAccessible(getTabDocument());
tabDoc.takeFocus();
tabBrowser().selectTabAtIndex(1);
}
this.getID = function advanceFocusIntoTab_getID()
this.getID = function switchToBackgroundTab_getID()
{
return "advance focus into loaded tab";
return "switch to background tab";
}
}
@ -138,28 +110,22 @@
var url = rootDir + "scroll.html#link1";
gQueue = new eventQueue();
gQueue.push(new loadTab("tabBrowser", url));
gQueue.push(new advanceFocusIntoTab("tabBrowser"));
gQueue.push(new loadTab(url));
gQueue.push(new loadTabInBackground(url));
gQueue.push(new switchToBackgroundTab());
gQueue.onFinish = function() { closeBrowserWindow(); }
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
openBrowserWindow(doTest);
]]>
</script>
<hbox flex="1" style="overflow: auto;">
<vbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=437607"
title="Clicking the 'Skip to main content' link once works, second time fails to initiate a V cursor jump">
Mozilla Bug 437607
</a><br/>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=519303"
title="Same page links to targets with content fires scrolling start accessible event on leaf text node">
Mozilla Bug 519303
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=691734"
title="Make sure scrolling start event is fired when document receive focus">
@ -173,33 +139,6 @@
</pre>
</body>
<vbox flex="1">
<!-- Hack to make xul:tabbrowser work -->
<menubar>
<menu label="menu">
<menupopup>
<menuitem label="close window hook" id="menu_closeWindow"/>
<menuitem label="close hook" id="menu_close"/>
</menupopup>
</menu>
</menubar>
<keyset>
<key id="key_close"/>
</keyset>
<hbox>
<tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
tabbrowser="tabBrowser"
flex="1">
<tab class="tabbrowser-tab" selected="true" label="tab"/>
</tabs>
</hbox>
<tabbrowser id="tabBrowser"
type="content-primary"
tabcontainer="tabbrowser-tabs"
flex="1"/>
</vbox>
<toolbar id="addon-bar"/>
</hbox>
<vbox id="eventdump"></vbox>
</vbox>
</window>

View File

@ -131,7 +131,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289
<tr><td>hi<td></tr></table>
<!-- test gEmptyRoleMap -->
<table role="label">
<table role="button">
<tr>
<td id="cell">cell</td>
</tr>

View File

@ -37,15 +37,15 @@
testChildAtPoint(txt, -10000, 10000, false, null);
testChildAtPoint(txt, -10000, 10000, true, null);
// Not specific case, point is inside of label accessible.
var label = getAccessible("label");
var labelText = label.firstChild;
testChildAtPoint(label, 1, 1, false, labelText);
testChildAtPoint(label, 1, 1, true, labelText);
// Not specific case, point is inside of btn accessible.
var btn = getAccessible("btn");
var btnText = btn.firstChild;
testChildAtPoint(btn, 1, 1, false, btnText);
testChildAtPoint(btn, 1, 1, true, btnText);
// Not specific case, point is outside of label accessible.
testChildAtPoint(label, -1, 1, false, null);
testChildAtPoint(label, -1, 1, true, null);
// Not specific case, point is outside of btn accessible.
testChildAtPoint(btn, -1, 1, false, null);
testChildAtPoint(btn, -1, 1, true, null);
// Out of flow accessible testing, do not return out of flow accessible
// because it's not a child of the accessible even visually it is.
@ -78,7 +78,7 @@
<div role="listitem" id="listitem"><span role="image" id="image">img</span>item</div>
</div>
<span role="label">label1</span><span role="label" id="label">label2</span>
<span role="button">button1</span><span role="button" id="btn">button2</span>
<span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span>

View File

@ -38,7 +38,6 @@
#filter substitution
pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
pref("general.useragent.compatMode.firefox", true);
pref("browser.chromeURL", "chrome://browser/content/");
#ifdef MOZ_OFFICIAL_BRANDING
pref("browser.homescreenURL", "file:///system/home/homescreen.html");
@ -394,6 +393,15 @@ pref("layers.acceleration.force-enabled", true);
pref("dom.screenEnabledProperty.enabled", true);
pref("dom.screenBrightnessProperty.enabled", true);
// handle links targeting new windows
// 1=current window/tab, 2=new window, 3=new tab in most recent window
pref("browser.link.open_newwindow", 3);
// 0: no restrictions - divert everything
// 1: don't divert window.open at all
// 2: don't divert window.open with features
pref("browser.link.open_newwindow.restriction", 0);
// Enable browser frame
pref("dom.mozBrowserFramesEnabled", true);
pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666");
@ -401,6 +409,7 @@ pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666");
// Temporary permission hack for WebSMS
pref("dom.sms.enabled", true);
pref("dom.sms.whitelist", "file://,http://localhost:6666");
// Ignore X-Frame-Options headers.
pref("b2g.ignoreXFrameOptions", true);

View File

@ -47,18 +47,16 @@ const LocalFile = CC('@mozilla.org/file/local;1',
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyGetter(Services, 'env', function() {
return Cc['@mozilla.org/process/environment;1']
.getService(Ci.nsIEnvironment);
});
XPCOMUtils.defineLazyGetter(Services, 'ss', function() {
return Cc['@mozilla.org/content/style-sheet-service;1']
.getService(Ci.nsIStyleSheetService);
});
XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
return Cc['@mozilla.org/focus-manager;1']
.getService(Ci.nsIFocusManager);
});
// In order to use http:// scheme instead of file:// scheme
// (that is much more restricted) the following code kick-off
@ -76,7 +74,7 @@ function startupHttpd(baseDir, port) {
// FIXME Bug 707625
// until we have a proper security model, add some rights to
// the pre-installed web applications
// the pre-installed web applications
function addPermissions(urls) {
let permissions = [
'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app'
@ -84,7 +82,7 @@ function addPermissions(urls) {
urls.forEach(function(url) {
let uri = Services.io.newURI(url, null, null);
let allow = Ci.nsIPermissionManager.ALLOW_ACTION;
permissions.forEach(function(permission) {
Services.perms.add(uri, permission, allow);
});
@ -96,9 +94,9 @@ var shell = {
// FIXME/bug 678695: this should be a system setting
preferredScreenBrightness: 1.0,
get home() {
delete this.home;
return this.home = document.getElementById('homescreen');
get contentBrowser() {
delete this.contentBrowser;
return this.contentBrowser = document.getElementById('homescreen');
},
get homeURL() {
@ -131,7 +129,7 @@ var shell = {
window.controllers.appendController(this);
window.addEventListener('keypress', this);
window.addEventListener('MozApplicationManifest', this);
this.home.addEventListener('load', this, true);
this.contentBrowser.addEventListener('load', this, true);
try {
Services.io.offline = false;
@ -156,7 +154,15 @@ var shell = {
return alert(msg);
}
let browser = this.home;
// Load webapi+apps.js as a frame script
let frameScriptUrl = 'chrome://browser/content/webapi.js';
try {
messageManager.loadFrameScript(frameScriptUrl, true);
} catch (e) {
dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n');
}
let browser = this.contentBrowser;
browser.homePage = homeURL;
browser.goHome();
},
@ -187,7 +193,7 @@ var shell = {
doCommand: function shell_doCommand(cmd) {
switch (cmd) {
case 'cmd_close':
this.home.contentWindow.postMessage('appclose', '*');
content.postMessage('appclose', '*');
break;
}
},
@ -197,7 +203,7 @@ var shell = {
case 'keypress':
switch (evt.keyCode) {
case evt.DOM_VK_HOME:
this.sendEvent(this.home.contentWindow, 'home');
this.sendEvent(content, 'home');
break;
case evt.DOM_VK_SLEEP:
this.toggleScreen();
@ -205,7 +211,7 @@ var shell = {
let details = {
'enabled': screen.mozEnabled
};
this.sendEvent(this.home.contentWindow, 'sleep', details);
this.sendEvent(content, 'sleep', details);
break;
case evt.DOM_VK_ESCAPE:
if (evt.defaultPrevented)
@ -215,8 +221,12 @@ var shell = {
}
break;
case 'load':
this.home.removeEventListener('load', this, true);
this.contentBrowser.removeEventListener('load', this, true);
this.turnScreenOn();
let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
chromeWindow.browserDOMWindow = new nsBrowserAccess();
this.sendEvent(window, 'ContentStart');
break;
case 'MozApplicationManifest':
@ -229,7 +239,7 @@ var shell = {
if (!documentElement)
return;
let manifest = documentElement.getAttribute("manifest");
let manifest = documentElement.getAttribute('manifest');
if (!manifest)
return;
@ -273,78 +283,47 @@ var shell = {
turnScreenOn: function shell_turnScreenOn() {
screen.mozEnabled = true;
screen.mozBrightness = this.preferredScreenBrightness;
},
};
(function VirtualKeyboardManager() {
let activeElement = null;
let isKeyboardOpened = false;
let constructor = {
handleEvent: function vkm_handleEvent(evt) {
let contentWindow = shell.home.contentWindow.wrappedJSObject;
switch (evt.type) {
case 'ContentStart':
contentWindow.navigator.mozKeyboard = new MozKeyboard();
break;
case 'keypress':
if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened)
return;
shell.sendEvent(contentWindow, 'hideime');
isKeyboardOpened = false;
evt.preventDefault();
evt.stopPropagation();
break;
case 'mousedown':
if (evt.target != activeElement || isKeyboardOpened)
return;
let type = activeElement.type;
shell.sendEvent(contentWindow, 'showime', { type: type });
isKeyboardOpened = true;
break;
}
},
observe: function vkm_observe(subject, topic, data) {
let contentWindow = shell.home.contentWindow;
let shouldOpen = parseInt(data);
if (shouldOpen && !isKeyboardOpened) {
activeElement = Services.fm.focusedElement;
if (!activeElement)
return;
let type = activeElement.type;
shell.sendEvent(contentWindow, 'showime', { type: type });
} else if (!shouldOpen && isKeyboardOpened) {
shell.sendEvent(contentWindow, 'hideime');
}
isKeyboardOpened = shouldOpen;
}
};
Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false);
['ContentStart', 'keypress', 'mousedown'].forEach(function vkm_events(type) {
window.addEventListener(type, constructor, true);
});
})();
function MozKeyboard() {
}
MozKeyboard.prototype = {
sendKey: function mozKeyboardSendKey(keyCode, charCode) {
charCode = (charCode == undefined) ? keyCode : charCode;
var utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
['keydown', 'keypress', 'keyup'].forEach(function sendKeyEvents(type) {
utils.sendKeyEvent(type, keyCode, charCode, null);
});
}
};
function nsBrowserAccess() {
}
nsBrowserAccess.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
openURI: function openURI(uri, opener, where, context) {
// TODO This should be replaced by an 'open-browser-window' intent
let contentWindow = content.wrappedJSObject;
if (!('getApplicationManager' in contentWindow))
return null;
let applicationManager = contentWindow.getApplicationManager();
if (!applicationManager)
return null;
let url = uri ? uri.spec : 'about:blank';
let window = applicationManager.launch(url, where);
return window.contentWindow;
},
openURIInFrame: function openURIInFrame(uri, opener, where, context) {
throw new Error('Not Implemented');
},
isTabContentWindow: function isTabContentWindow(contentWindow) {
return contentWindow == window;
}
};
// Pipe `console` log messages to the nsIConsoleService which writes them
// to logcat.
Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) {
let message = subject.wrappedJSObject;
let prefix = "Content JS " + message.level.toUpperCase() +
" at " + message.filename + ":" + message.lineNumber +
" in " + (message.functionName || "anonymous") + ": ";
Services.console.logStringMessage(prefix + Array.join(message.arguments, " "));
}, "console-api-log-event", false);

View File

@ -66,12 +66,12 @@
events: ['mousedown', 'mousemove', 'mouseup', 'click', 'unload'],
start: function teh_start() {
this.events.forEach((function(evt) {
shell.home.addEventListener(evt, this, true);
shell.contentBrowser.addEventListener(evt, this, true);
}).bind(this));
},
stop: function teh_stop() {
this.events.forEach((function(evt) {
shell.home.removeEventListener(evt, this, true);
shell.contentBrowser.removeEventListener(evt, this, true);
}).bind(this));
},
handleEvent: function teh_handleEvent(evt) {
@ -139,12 +139,7 @@
return;
case 'click':
if (!isNewTouchAction) {
debug('click: cancel');
evt.preventDefault();
evt.stopPropagation();
} else {
if (isNewTouchAction) {
// Mouse events has been cancelled so dispatch a sequence
// of events to where touchend has been fired
if (preventMouseEvents) {

1791
b2g/chrome/content/webapi.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ chrome.jar:
content/touch.js (content/touch.js)
content/commandUtil.js (content/commandUtil.js)
content/httpd.js (content/httpd.js)
content/webapi.js (content/webapi.js)
content/content.css (content/content.css)
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml

View File

@ -38,7 +38,8 @@
MOZ_APP_BASENAME=B2G
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=11.0a1
MOZ_APP_VERSION=13.0a1
MOZ_APP_UA_NAME=Firefox
MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official

View File

@ -1112,10 +1112,10 @@ pref("prompts.tab_modal.enabled", true);
pref("browser.panorama.animate_zoom", true);
// Defines the url to be used for new tabs.
pref("browser.newtab.url", "about:blank");
pref("browser.newtab.url", "about:newtab");
// Toggles the content of 'about:newtab'. Shows the grid when enabled.
pref("browser.newtabpage.enabled", false);
pref("browser.newtabpage.enabled", true);
// Enable the DOM full-screen API.
pref("full-screen-api.enabled", true);

View File

@ -5455,7 +5455,7 @@ var TabsInTitlebar = {
if (!this._draghandle) {
let tmp = {};
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
this._draghandle = new tmp.WindowDraggingElement(tabsToolbar, window);
this._draghandle = new tmp.WindowDraggingElement(tabsToolbar);
this._draghandle.mouseDownCheck = function () {
return !this._dragBindingAlive && TabsInTitlebar.enabled;
};

View File

@ -5,7 +5,9 @@ const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
Cu.import("resource:///modules/NewTabUtils.jsm");
let tmp = {};
Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
let NewTabUtils = tmp.NewTabUtils;
registerCleanupFunction(function () {
reset();

View File

@ -693,7 +693,10 @@ var gCookiesWindow = {
}
else {
var rangeCount = seln.getRangeCount();
for (var i = 0; i < rangeCount; ++i) {
// Traverse backwards through selections to avoid messing
// up the indices when they are deleted.
// See bug 388079.
for (var i = rangeCount - 1; i >= 0; --i) {
var min = {}; var max = {};
seln.getRangeAt(i, min, max);
nextSelected = min.value;

View File

@ -160,6 +160,7 @@ _BROWSER_TEST_FILES = \
browser_687710_2.js \
browser_694378.js \
browser_705597.js \
browser_707862.js \
$(NULL)
ifneq ($(OS_ARCH),Darwin)

View File

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tabState = {
entries: [{url: "about:home", children: [{url: "about:mozilla"}]}]
};
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab("about:blank");
registerCleanupFunction(function () gBrowser.removeTab(tab));
let browser = tab.linkedBrowser;
whenBrowserLoaded(browser, function () {
ss.setTabState(tab, JSON.stringify(tabState));
let sessionHistory = browser.sessionHistory;
let entry = sessionHistory.getEntryAtIndex(0, false);
whenChildCount(entry, 1, function () {
whenChildCount(entry, 2, function () {
whenBrowserLoaded(browser, function () {
let sessionHistory = browser.sessionHistory;
let entry = sessionHistory.getEntryAtIndex(0, false);
whenChildCount(entry, 0, finish);
});
// reload the browser to deprecate the subframes
browser.reload();
});
// create a dynamic subframe
let doc = browser.contentDocument;
let iframe = doc.createElement("iframe");
iframe.setAttribute("src", "about:mozilla");
doc.body.appendChild(iframe);
});
});
}
function whenBrowserLoaded(aBrowser, aCallback) {
aBrowser.addEventListener("load", function onLoad() {
aBrowser.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}, true);
}
function whenChildCount(aEntry, aChildCount, aCallback) {
if (aEntry.childCount == aChildCount)
aCallback();
else
executeSoon(function () whenChildCount(aEntry, aChildCount, aCallback));
}

View File

@ -68,6 +68,18 @@ var gSetBackground = {
if (this._screenWidth / this._screenHeight >= 1.6)
document.getElementById("monitor").setAttribute("aspectratio", "16:10");
#ifdef XP_WIN
// hide fill + fit options if <win7 since don't work
var version = Components.classes["@mozilla.org/system-info;1"]
.getService(Ci.nsIPropertyBag2)
.getProperty("version");
var isWindows7OrHigher = (parseFloat(version) >= 6.1);
if (!isWindows7OrHigher) {
document.getElementById("fillPosition").hidden = true;
document.getElementById("fitPosition").hidden = true;
}
#endif
// make sure that the correct dimensions will be used
setTimeout(function(self) {
self.init(window.arguments[0]);
@ -198,6 +210,39 @@ var gSetBackground = {
var x = (this._screenWidth - this._image.naturalWidth) / 2;
var y = (this._screenHeight - this._image.naturalHeight) / 2;
ctx.drawImage(this._image, x, y);
break;
case "FILL":
//Try maxing width first, overflow height
var widthRatio = this._screenWidth / this._image.naturalWidth;
var width = this._image.naturalWidth * widthRatio;
var height = this._image.naturalHeight * widthRatio;
if (height < this._screenHeight) {
//height less than screen, max height and overflow width
var heightRatio = this._screenHeight / this._image.naturalHeight;
width = this._image.naturalWidth * heightRatio;
height = this._image.naturalHeight * heightRatio;
}
var x = (this._screenWidth - width) / 2;
var y = (this._screenHeight - height) / 2;
ctx.drawImage(this._image, x, y, width, height);
break;
case "FIT":
//Try maxing width first, top and bottom borders
var widthRatio = this._screenWidth / this._image.naturalWidth;
var width = this._image.naturalWidth * widthRatio;
var height = this._image.naturalHeight * widthRatio;
var x = 0;
var y = (this._screenHeight - height) / 2;
if (height > this._screenHeight) {
//height overflow, maximise height, side borders
var heightRatio = this._screenHeight / this._image.naturalHeight;
width = this._image.naturalWidth * heightRatio;
height = this._image.naturalHeight * heightRatio;
x = (this._screenWidth - width) / 2;
y = 0;
}
ctx.drawImage(this._image, x, y, width, height);
break;
}
}
};

View File

@ -78,6 +78,8 @@
<menuitem label="&center.label;" value="CENTER"/>
<menuitem label="&tile.label;" value="TILE"/>
<menuitem label="&stretch.label;" value="STRETCH"/>
<menuitem label="&fill.label;" value="FILL" id="fillPosition"/>
<menuitem label="&fit.label;" value="FIT" id="fitPosition"/>
</menupopup>
</menulist>
<spacer flex="1"/>

View File

@ -81,6 +81,7 @@ interface nsIShellService : nsISupports
const long BACKGROUND_STRETCH = 2;
const long BACKGROUND_CENTER = 3;
const long BACKGROUND_FILL = 4;
const long BACKGROUND_FIT = 5;
/**
* Sets the desktop background image using either the HTML <IMG>

View File

@ -427,6 +427,10 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
options.Assign("wallpaper");
else if (aPosition == BACKGROUND_STRETCH)
options.Assign("stretched");
else if (aPosition == BACKGROUND_FILL)
options.Assign("zoom");
else if (aPosition == BACKGROUND_FIT)
options.Assign("scaled");
else
options.Assign("centered");

View File

@ -640,6 +640,14 @@ nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
style.AssignLiteral("2");
tile.AssignLiteral("0");
break;
case BACKGROUND_FILL:
style.AssignLiteral("10");
tile.AssignLiteral("0");
break;
case BACKGROUND_FIT:
style.AssignLiteral("6");
tile.AssignLiteral("0");
break;
}
rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);

View File

@ -52,7 +52,8 @@ function onPageLoad() {
checkWallpaper(Ci.nsIShellService.BACKGROUND_TILE, "wallpaper");
checkWallpaper(Ci.nsIShellService.BACKGROUND_STRETCH, "stretched");
checkWallpaper(Ci.nsIShellService.BACKGROUND_CENTER, "centered");
checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "centered");
checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "zoom");
checkWallpaper(Ci.nsIShellService.BACKGROUND_FIT, "scaled");
// Restore GConf and wallpaper

View File

@ -1,7 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource:///modules/PageThumbs.jsm");
let tmp = {};
Cu.import("resource:///modules/PageThumbs.jsm", tmp);
let PageThumbs = tmp.PageThumbs;
let PageThumbsCache = tmp.PageThumbsCache;
registerCleanupFunction(function () {
while (gBrowser.tabs.length > 1)

View File

@ -2,7 +2,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
let tmp = {};
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
let LayoutHelpers = tmp.LayoutHelpers;
function init(callback) {
let iframe = gBrowser.ownerDocument.createElement("iframe");

View File

@ -5,7 +5,9 @@
// - https://github.com/mozilla/gcli/blob/master/docs/index.md
// - https://wiki.mozilla.org/DevTools/Features/GCLI
Components.utils.import("resource:///modules/gcli.jsm");
let tmp = {};
Components.utils.import("resource:///modules/gcli.jsm", tmp);
let gcli = tmp.gcli;
let hud;
let gcliterm;

View File

@ -2,6 +2,8 @@
<!ENTITY tile.label "Tile">
<!ENTITY center.label "Center">
<!ENTITY stretch.label "Stretch">
<!ENTITY fill.label "Fill">
<!ENTITY fit.label "Fit">
<!ENTITY preview.label "Preview">
<!ENTITY color.label "Color:">
<!ENTITY setDesktopBackground.title "Set Desktop Background">

View File

@ -14,8 +14,10 @@ class DeviceManagerADB(DeviceManager):
self.retries = 0
self._sock = None
self.useRunAs = False
self.haveRoot = False
self.useZip = False
self.packageName = None
self.tempDir = None
if packageName == None:
if os.getenv('USER'):
packageName = 'org.mozilla.fennec_' + os.getenv('USER')
@ -36,18 +38,30 @@ class DeviceManagerADB(DeviceManager):
self.verifyZip()
except:
self.useZip = False
try:
def verifyRoot():
# a test to see if we have root privs
files = self.listFiles("/data/data")
if (len(files) == 1):
if (files[0].find("Permission denied") != -1):
print "NOT running as root"
raise Exception("not running as root")
self.haveRoot = True
try:
verifyRoot()
except:
try:
self.checkCmd(["root"])
# The root command does not fail even if ADB cannot get
# root rights (e.g. due to production builds), so we have
# to check again ourselves that we have root now.
verifyRoot()
except:
print "restarting as root failed"
if (self.useRunAs):
print "restarting as root failed, but run-as available"
else:
print "restarting as root failed"
# external function
# returns:
@ -58,9 +72,12 @@ class DeviceManagerADB(DeviceManager):
if (os.name == "nt"):
destname = destname.replace('\\', '/')
if (self.useRunAs):
remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
if self.useDDCopy:
self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
else:
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
self.checkCmd(["shell", "rm", remoteTmpFile])
else:
self.checkCmd(["push", os.path.realpath(localname), destname])
@ -329,7 +346,26 @@ class DeviceManagerADB(DeviceManager):
# TODO: add debug flags and allow for printing stdout
# self.runCmd(["pull", remoteFile, localFile])
try:
self.runCmd(["pull", remoteFile, localFile]).stdout.read()
# First attempt to pull file regularly
outerr = self.runCmd(["pull", remoteFile, localFile]).communicate()
# Now check stderr for errors
errl = outerr[1].splitlines()
if (len(errl) == 1):
if (((errl[0].find("Permission denied") != -1)
or (errl[0].find("does not exist") != -1))
and self.useRunAs):
# If we lack permissions to read but have run-as, then we should try
# to copy the file to a world-readable location first before attempting
# to pull it again.
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
self.runCmd(["pull", remoteTmpFile, localFile]).stdout.read()
# Clean up temporary file
self.checkCmdAs(["shell", "rm", remoteTmpFile])
f = open(localFile)
ret = f.read()
f.close()
@ -422,6 +458,23 @@ class DeviceManagerADB(DeviceManager):
self.mkDir(testRoot)
return testRoot
# Gets the temporary directory we are using on this device
# base on our device root, ensuring also that it exists.
#
# internal function
# returns:
# success: path for temporary directory
# failure: None
def getTempDir(self):
# Cache result to speed up operations depending
# on the temporary directory.
if self.tempDir == None:
self.tempDir = self.getDeviceRoot() + "/tmp"
if (not self.dirExists(self.tempDir)):
return self.mkDir(self.tempDir)
return self.tempDir
# Either we will have /tests/fennec or /tests/firefox but we will never have
# both. Return the one that exists
# TODO: ensure we can support org.mozilla.firefox
@ -543,6 +596,11 @@ class DeviceManagerADB(DeviceManager):
return ret
def runCmd(self, args):
# If we are not root but have run-as, and we're trying to execute
# a shell command then using run-as is the best we can do
if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(0, "adb")
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -553,6 +611,11 @@ class DeviceManagerADB(DeviceManager):
return self.runCmd(args)
def checkCmd(self, args):
# If we are not root but have run-as, and we're trying to execute
# a shell command then using run-as is the best we can do
if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(0, "adb")
return subprocess.check_call(args)
@ -591,6 +654,11 @@ class DeviceManagerADB(DeviceManager):
if (re.search('Usage', data)):
return True
else:
data = self.runCmd(["shell", "dd", "-"]).stdout.read()
if (re.search('unknown operand', data)):
print "'cp' not found, but 'dd' was found as a replacement"
self.useDDCopy = True
return True
print "unable to execute 'cp' on device; consider installing busybox from Android Market"
return False
@ -605,12 +673,14 @@ class DeviceManagerADB(DeviceManager):
self.useRunAs = False
devroot = self.getDeviceRoot()
if (packageName and self.isCpAvailable() and devroot):
self.tmpDir = devroot + "/tmp"
if (not self.dirExists(self.tmpDir)):
self.mkDir(self.tmpDir)
tmpDir = self.getTempDir()
self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"])
self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"])
self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
if self.useDDCopy:
self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
else:
self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
if (self.fileExists(devroot + "/sanity/tmpfile")):
print "will execute commands via run-as " + packageName
self.packageName = packageName

View File

@ -134,11 +134,15 @@ public class FennecNativeDriver implements Driver {
private void getGeckoInfo() {
View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
if (geckoLayout != null) {
geckoTop = geckoLayout.getTop();
geckoLeft = geckoLayout.getLeft();
int[] pos = new int[2];
geckoLayout.getLocationOnScreen(pos);
geckoTop = pos[1];
geckoLeft = pos[0];
geckoWidth = geckoLayout.getWidth();
geckoHeight = geckoLayout.getHeight();
geckoInfo = true;
} else {
throw new RoboCopException("Unable to find view gecko_layout");
}
}

View File

@ -160,6 +160,7 @@ MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
MOZ_OGG = @MOZ_OGG@
MOZ_RAW = @MOZ_RAW@
MOZ_SYDNEYAUDIO = @MOZ_SYDNEYAUDIO@
MOZ_CUBEB = @MOZ_CUBEB@
MOZ_WAVE = @MOZ_WAVE@
MOZ_MEDIA = @MOZ_MEDIA@
MOZ_VORBIS = @MOZ_VORBIS@

View File

@ -1050,4 +1050,5 @@ tremor/ivorbiscodec.h
ogg/ogg.h
ogg/os_types.h
nestegg/nestegg.h
cubeb/cubeb.h
#endif

View File

@ -460,6 +460,9 @@ case "$target" in
if test -z "$ANDROID_PACKAGE_NAME" ; then
ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)'
fi
if test -z "$MOZ_MOBILE_COMPAT" ; then
MOZ_MOBILE_COMPAT='All'
fi
AC_DEFINE(ANDROID)
AC_DEFINE_UNQUOTED(ANDROID_VERSION, $android_version)
@ -481,6 +484,7 @@ AC_SUBST(ANDROID_PLATFORM)
AC_SUBST(ANDROID_SDK)
AC_SUBST(ANDROID_PLATFORM_TOOLS)
AC_SUBST(ANDROID_PACKAGE_NAME)
AC_SUBST(MOZ_MOBILE_COMPAT)
AC_SUBST(OBJCOPY)
dnl ========================================================
@ -4582,6 +4586,7 @@ MOZ_AUTH_EXTENSION=1
MOZ_OGG=1
MOZ_RAW=
MOZ_SYDNEYAUDIO=
MOZ_CUBEB=
MOZ_VORBIS=
MOZ_TREMOR=
MOZ_WAVE=1
@ -5592,6 +5597,7 @@ MOZ_ARG_DISABLE_BOOL(ogg,
if test -n "$MOZ_OGG"; then
AC_DEFINE(MOZ_OGG)
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
case "$target_cpu" in
arm*)
@ -5709,6 +5715,7 @@ AC_SUBST(MOZ_LIBVPX_LIBS)
if test -n "$MOZ_WEBM" -a -z "$MOZ_NATIVE_LIBVPX"; then
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
case "$target_cpu" in
arm*)
@ -5820,17 +5827,29 @@ MOZ_ARG_DISABLE_BOOL(wave,
if test -n "$MOZ_WAVE"; then
AC_DEFINE(MOZ_WAVE)
MOZ_SYDNEYAUDIO=1
MOZ_CUBEB=1
MOZ_MEDIA=1
fi
dnl ========================================================
dnl = Handle dependent SYDNEYAUDIO and MEDIA defines
dnl = Handle dependent SYDNEYAUDIO, CUBEB, and MEDIA defines
dnl ========================================================
if test -n "$MOZ_SYDNEYAUDIO"; then
AC_DEFINE(MOZ_SYDNEYAUDIO)
fi
if test -n "$MOZ_CUBEB"; then
case "$target" in
*-mingw*)
AC_DEFINE(MOZ_CUBEB)
;;
*)
dnl Other targets will be enabled soon.
;;
esac
fi
if test -n "$MOZ_MEDIA"; then
AC_DEFINE(MOZ_MEDIA)
fi
@ -8692,6 +8711,7 @@ AC_SUBST(MOZ_APP_EXTRA_LIBS)
AC_SUBST(MOZ_MEDIA)
AC_SUBST(MOZ_SYDNEYAUDIO)
AC_SUBST(MOZ_CUBEB)
AC_SUBST(MOZ_WAVE)
AC_SUBST(MOZ_VORBIS)
AC_SUBST(MOZ_TREMOR)

View File

@ -1617,6 +1617,8 @@ public:
}
}
virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const = 0;
private:
PRUint64 mWarnedAbout;

View File

@ -9140,3 +9140,18 @@ nsDocument::GetMozVisibilityState(nsAString& aState)
aState.AssignASCII(states[mVisibilityState]);
return NS_OK;
}
static size_t
SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
nsMallocSizeOfFun aMallocSizeOf,
void* aData)
{
return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
}
/* virtual */ size_t
nsDocument::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const
{
return mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aMallocSizeOf);
}

View File

@ -991,6 +991,8 @@ public:
// Posts an event to call UpdateVisibilityState
virtual void PostVisibilityUpdateEvent();
virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const;
protected:
friend class nsNodeUtils;

View File

@ -866,8 +866,6 @@ nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope)
nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
JS_SetVersion(cx, JSVERSION_LATEST);
JS_SetErrorReporter(cx, ContentScriptErrorReporter);

View File

@ -91,9 +91,9 @@ using namespace mozilla;
#define TRUE_OR_FAIL_WEBSOCKET(x, ret) \
PR_BEGIN_MACRO \
if (NS_UNLIKELY(!(x))) { \
NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
FailConnection(); \
return ret; \
NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \
return ret; \
} \
PR_END_MACRO
@ -102,7 +102,7 @@ using namespace mozilla;
nsresult __rv = res; \
if (NS_FAILED(__rv)) { \
NS_ENSURE_SUCCESS_BODY(res, ret) \
FailConnection(); \
FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \
return ret; \
} \
PR_END_MACRO
@ -159,7 +159,8 @@ nsWebSocket::PrintErrorOnConsole(const char *aBundleURI,
// when this is called the browser side wants no more part of it
nsresult
nsWebSocket::CloseConnection()
nsWebSocket::CloseConnection(PRUint16 aReasonCode,
const nsACString& aReasonString)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
if (mDisconnected)
@ -172,7 +173,7 @@ nsWebSocket::CloseConnection()
if (mReadyState == nsIWebSocket::CONNECTING) {
SetReadyState(nsIWebSocket::CLOSED);
if (mChannel) {
mChannel->Close(mClientReasonCode, mClientReason);
mChannel->Close(aReasonCode, aReasonString);
}
Disconnect();
return NS_OK;
@ -186,7 +187,7 @@ nsWebSocket::CloseConnection()
return NS_OK;
}
return mChannel->Close(mClientReasonCode, mClientReason);
return mChannel->Close(aReasonCode, aReasonString);
}
nsresult
@ -219,12 +220,13 @@ nsWebSocket::ConsoleError()
nsresult
nsWebSocket::FailConnection()
nsWebSocket::FailConnection(PRUint16 aReasonCode,
const nsACString& aReasonString)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
ConsoleError();
nsresult rv = CloseConnection();
nsresult rv = CloseConnection(aReasonCode, aReasonString);
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
@ -322,7 +324,7 @@ nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode)
if (mDisconnected)
return NS_OK;
mClosedCleanly = NS_SUCCEEDED(aStatusCode);
mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
if (aStatusCode == NS_BASE_STREAM_CLOSED &&
mReadyState >= nsIWebSocket::CLOSING) {
@ -360,10 +362,16 @@ nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode,
const nsACString &aReason)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
mServerReasonCode = aCode;
CopyUTF8toUTF16(aReason, mServerReason);
CloseConnection(); /* reciprocate! */
// store code/string for onclose DOM event
mCloseEventCode = aCode;
CopyUTF8toUTF16(aReason, mCloseEventReason);
// Send reciprocal Close frame.
// 5.5.1: "When sending a Close frame in response, the endpoint typically
// echos the status code it received"
CloseConnection(aCode, aReason);
return NS_OK;
}
@ -406,10 +414,9 @@ nsWebSocket::GetInterface(const nsIID &aIID, void **aResult)
nsWebSocket::nsWebSocket() : mKeepingAlive(false),
mCheckMustKeepAlive(true),
mTriggeredCloseEvent(false),
mClosedCleanly(false),
mDisconnected(false),
mClientReasonCode(0),
mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
mCloseEventWasClean(false),
mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
mReadyState(nsIWebSocket::CONNECTING),
mOutgoingBufferedAmount(0),
mBinaryType(WS_BINARY_TYPE_BLOB),
@ -623,7 +630,7 @@ public:
~nsAutoCloseWS()
{
if (!mWebSocket->mChannel) {
mWebSocket->CloseConnection();
mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
}
}
private:
@ -906,9 +913,9 @@ nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
mReadyState = aNewReadyState;
// The close event must be dispatched asynchronously.
rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mClosedCleanly,
mServerReasonCode,
mServerReason),
rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mCloseEventWasClean,
mCloseEventCode,
mCloseEventReason),
NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch the close event");
@ -1413,29 +1420,27 @@ nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
// the reason code is optional, but if provided it must be in a specific range
PRUint16 closeCode = 0;
if (argc >= 1) {
if (code != 1000 && (code < 3000 || code > 4999))
if (code != 1000 && (code < 3000 || code > 4999)) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
closeCode = code;
}
nsCAutoString utf8Reason;
nsCAutoString closeReason;
if (argc >= 2) {
if (ContainsUnpairedSurrogates(reason))
if (ContainsUnpairedSurrogates(reason)) {
return NS_ERROR_DOM_SYNTAX_ERR;
CopyUTF16toUTF8(reason, utf8Reason);
}
CopyUTF16toUTF8(reason, closeReason);
// The API requires the UTF-8 string to be 123 or less bytes
if (utf8Reason.Length() > 123)
if (closeReason.Length() > 123) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
}
// Format checks for reason and code both passed, they can now be assigned.
if (argc >= 1)
mClientReasonCode = code;
if (argc >= 2)
mClientReason = utf8Reason;
if (mReadyState == nsIWebSocket::CLOSING ||
mReadyState == nsIWebSocket::CLOSED) {
return NS_OK;
@ -1446,12 +1451,12 @@ nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
// before calling it
nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
FailConnection();
FailConnection(closeCode, closeReason);
return NS_OK;
}
// mReadyState == nsIWebSocket::OPEN
CloseConnection();
CloseConnection(closeCode, closeReason);
return NS_OK;
}
@ -1594,8 +1599,7 @@ nsWebSocket::Cancel(nsresult aStatus)
return NS_OK;
ConsoleError();
mClientReasonCode = nsIWebSocketChannel::CLOSE_GOING_AWAY;
return CloseConnection();
return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
}
NS_IMETHODIMP

View File

@ -111,9 +111,11 @@ protected:
nsresult ParseURL(const nsString& aURL);
nsresult EstablishConnection();
// these three methods when called can release the WebSocket object
nsresult FailConnection();
nsresult CloseConnection();
// These methods when called can release the WebSocket object
nsresult FailConnection(PRUint16 reasonCode,
const nsACString& aReasonString = EmptyCString());
nsresult CloseConnection(PRUint16 reasonCode,
const nsACString& aReasonString = EmptyCString());
nsresult Disconnect();
nsresult ConsoleError();
@ -166,13 +168,12 @@ protected:
bool mKeepingAlive;
bool mCheckMustKeepAlive;
bool mTriggeredCloseEvent;
bool mClosedCleanly;
bool mDisconnected;
nsCString mClientReason;
nsString mServerReason;
PRUint16 mClientReasonCode;
PRUint16 mServerReasonCode;
// Set attributes of DOM 'onclose' message
bool mCloseEventWasClean;
nsString mCloseEventReason;
PRUint16 mCloseEventCode;
nsCString mAsciiHost; // hostname
PRUint32 mPort;

View File

@ -216,6 +216,28 @@ nsHTMLCanvasElement::ToDataURL(const nsAString& aType, nsIVariant* aParams,
return ToDataURLImpl(aType, aParams, aDataURL);
}
// nsHTMLCanvasElement::mozFetchAsStream
NS_IMETHODIMP
nsHTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
const nsAString& aType)
{
if (!nsContentUtils::IsCallerChrome())
return NS_ERROR_FAILURE;
nsresult rv;
bool fellBackToPNG = false;
nsCOMPtr<nsIInputStream> inputData;
rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return aCallback->OnInputStreamReady(asyncData);
}
nsresult
nsHTMLCanvasElement::ExtractData(const nsAString& aType,
const nsAString& aOptions,

View File

@ -2087,10 +2087,6 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
{
NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up");
nsCAutoString src;
GetCurrentSpec(src);
printf("*** nsHTMLElement::FinishDecoderSetup() mDecoder=%p stream=%p src=%s\n",
aDecoder, aDecoder->GetStream(), src.get());
mDecoder = aDecoder;
AddMediaElementToURITable();
@ -2490,15 +2486,7 @@ ImageContainer* nsHTMLMediaElement::GetImageContainer()
if (!video)
return nsnull;
nsRefPtr<LayerManager> manager =
nsContentUtils::PersistentLayerManagerForDocument(OwnerDoc());
if (!manager)
return nsnull;
mImageContainer = manager->CreateImageContainer();
if (manager->IsCompositingCheap()) {
mImageContainer->SetDelayedConversion(true);
}
mImageContainer = LayerManager::CreateImageContainer();
return mImageContainer;
}

View File

@ -60,6 +60,11 @@ extern "C" {
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
#if defined(MOZ_CUBEB)
#include "nsAutoRef.h"
#include "cubeb/cubeb.h"
#endif
using namespace mozilla;
#if defined(XP_MACOSX)
@ -78,6 +83,10 @@ using mozilla::TimeStamp;
PRLogModuleInfo* gAudioStreamLog = nsnull;
#endif
#if defined(MOZ_CUBEB)
static cubeb* gCubebContext;
#endif
static const PRUint32 FAKE_BUFFER_SIZE = 176400;
// Number of milliseconds per second.
@ -170,9 +179,9 @@ class AudioInitEvent : public nsRunnable
{
ContentChild * cpc = ContentChild::GetSingleton();
NS_ASSERTION(cpc, "Content Protocol is NULL!");
mOwner->mAudioChild = static_cast<AudioChild*> (cpc->SendPAudioConstructor(mOwner->mChannels,
mOwner->mRate,
mOwner->mFormat));
mOwner->mAudioChild = static_cast<AudioChild*>(cpc->SendPAudioConstructor(mOwner->mChannels,
mOwner->mRate,
mOwner->mFormat));
return NS_OK;
}
@ -316,42 +325,78 @@ class AudioShutdownEvent : public nsRunnable
};
#endif
static mozilla::Mutex* gVolumeScaleLock = nsnull;
#define PREF_VOLUME_SCALE "media.volume_scale"
#define PREF_USE_CUBEB "media.use_cubeb"
static mozilla::Mutex* gAudioPrefsLock = nsnull;
static double gVolumeScale = 1.0;
static bool gUseCubeb = false;
static int VolumeScaleChanged(const char* aPref, void *aClosure) {
nsAdoptingString value = Preferences::GetString("media.volume_scale");
mozilla::MutexAutoLock lock(*gVolumeScaleLock);
if (value.IsEmpty()) {
gVolumeScale = 1.0;
} else {
NS_ConvertUTF16toUTF8 utf8(value);
gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
static int PrefChanged(const char* aPref, void* aClosure)
{
if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
nsAdoptingString value = Preferences::GetString(aPref);
mozilla::MutexAutoLock lock(*gAudioPrefsLock);
if (value.IsEmpty()) {
gVolumeScale = 1.0;
} else {
NS_ConvertUTF16toUTF8 utf8(value);
gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
}
} else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
bool value = Preferences::GetBool(aPref, true);
mozilla::MutexAutoLock lock(*gAudioPrefsLock);
gUseCubeb = value;
}
return 0;
}
static double GetVolumeScale() {
mozilla::MutexAutoLock lock(*gVolumeScaleLock);
static double GetVolumeScale()
{
mozilla::MutexAutoLock lock(*gAudioPrefsLock);
return gVolumeScale;
}
#if defined(MOZ_CUBEB)
static bool GetUseCubeb()
{
mozilla::MutexAutoLock lock(*gAudioPrefsLock);
return gUseCubeb;
}
#endif
void nsAudioStream::InitLibrary()
{
#ifdef PR_LOGGING
gAudioStreamLog = PR_NewLogModule("nsAudioStream");
#endif
gVolumeScaleLock = new mozilla::Mutex("nsAudioStream::gVolumeScaleLock");
VolumeScaleChanged(nsnull, nsnull);
Preferences::RegisterCallback(VolumeScaleChanged, "media.volume_scale");
gAudioPrefsLock = new mozilla::Mutex("nsAudioStream::gAudioPrefsLock");
PrefChanged(PREF_VOLUME_SCALE, nsnull);
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
#if defined(MOZ_CUBEB)
PrefChanged(PREF_USE_CUBEB, nsnull);
Preferences::RegisterCallback(PrefChanged, PREF_USE_CUBEB);
if (cubeb_init(&gCubebContext, "nsAudioStream") != 0) {
NS_WARNING("cubeb_init failed");
}
#endif
}
void nsAudioStream::ShutdownLibrary()
{
Preferences::UnregisterCallback(VolumeScaleChanged, "media.volume_scale");
delete gVolumeScaleLock;
gVolumeScaleLock = nsnull;
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
#if defined(MOZ_CUBEB)
Preferences::UnregisterCallback(PrefChanged, PREF_USE_CUBEB);
#endif
delete gAudioPrefsLock;
gAudioPrefsLock = nsnull;
#if defined(MOZ_CUBEB)
if (gCubebContext) {
cubeb_destroy(gCubebContext);
gCubebContext = nsnull;
}
#endif
}
nsIThread *
@ -365,16 +410,6 @@ nsAudioStream::GetThread()
return mAudioPlaybackThread;
}
nsAudioStream* nsAudioStream::AllocateStream()
{
#if defined(REMOTE_AUDIO)
if (XRE_GetProcessType() == GeckoProcessType_Content) {
return new nsRemotedAudioStream();
}
#endif
return new nsNativeAudioStream();
}
class AsyncShutdownPlaybackThread : public nsRunnable
{
public:
@ -770,3 +805,398 @@ nsRemotedAudioStream::IsPaused()
}
#endif
#if defined(MOZ_CUBEB)
template <>
class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
{
public:
static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
};
class nsBufferedAudioStream : public nsAudioStream
{
public:
NS_DECL_ISUPPORTS
nsBufferedAudioStream();
~nsBufferedAudioStream();
nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
void Shutdown();
nsresult Write(const void* aBuf, PRUint32 aFrames);
PRUint32 Available();
void SetVolume(double aVolume);
void Drain();
void Pause();
void Resume();
PRInt64 GetPosition();
PRInt64 GetPositionInFrames();
bool IsPaused();
PRInt32 GetMinWriteSize();
private:
static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
{
return static_cast<nsBufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
}
static int StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
{
return static_cast<nsBufferedAudioStream*>(aThis)->StateCallback(aState);
}
long DataCallback(void* aBuffer, long aFrames);
int StateCallback(cubeb_state aState);
// Shared implementation of underflow adjusted position calculation.
// Caller must own the monitor.
PRInt64 GetPositionInFramesUnlocked();
// The monitor is held to protect all access to member variables. Write()
// waits while mBuffer is full; DataCallback() notifies as it consumes
// data from mBuffer. Drain() waits while mState is DRAINING;
// StateCallback() notifies when mState is DRAINED.
Monitor mMonitor;
// Sum of silent frames written when DataCallback requests more frames
// than are available in mBuffer.
PRUint64 mLostFrames;
// Temporary audio buffer. Filled by Write() and consumed by
// DataCallback(). Once mBufferLimit is reached, Write() blocks until
// sufficient space becomes available in mBuffer. The buffer and buffer
// limit deal in bytes, not frames.
nsTArray<PRUint8> mBuffer;
PRUint32 mBufferLimit;
// Software volume level. Applied during the servicing of DataCallback().
double mVolume;
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
// nsAutoRef's destructor.
nsAutoRef<cubeb_stream> mCubebStream;
PRInt32 mRate;
PRInt32 mChannels;
SampleFormat mFormat;
PRUint32 mBytesPerFrame;
enum StreamState {
INITIALIZED, // Initialized, playback has not begun.
STARTED, // Started by a call to Write() (iff INITIALIZED) or Resume().
STOPPED, // Stopped by a call to Pause().
DRAINING, // Drain requested. DataCallback will indicate end of stream
// once the remaining contents of mBuffer are requested by
// cubeb, after which StateCallback will indicate drain
// completion.
DRAINED // StateCallback has indicated that the drain is complete.
};
StreamState mState;
// Arbitrary default stream latency. The higher this value, the longer stream
// volume changes will take to become audible.
static const unsigned int DEFAULT_LATENCY_MS = 100;
};
#endif
nsAudioStream* nsAudioStream::AllocateStream()
{
#if defined(REMOTE_AUDIO)
if (XRE_GetProcessType() == GeckoProcessType_Content) {
return new nsRemotedAudioStream();
}
#endif
#if defined(MOZ_CUBEB)
if (GetUseCubeb()) {
return new nsBufferedAudioStream();
}
#endif
return new nsNativeAudioStream();
}
#if defined(MOZ_CUBEB)
nsBufferedAudioStream::nsBufferedAudioStream()
: mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0), mRate(0), mChannels(0),
mBytesPerFrame(0), mState(INITIALIZED)
{
}
nsBufferedAudioStream::~nsBufferedAudioStream()
{
Shutdown();
}
NS_IMPL_THREADSAFE_ISUPPORTS0(nsBufferedAudioStream)
nsresult
nsBufferedAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
{
if (!gCubebContext || aNumChannels < 0 || aRate < 0) {
return NS_ERROR_FAILURE;
}
mRate = aRate;
mChannels = aNumChannels;
mFormat = aFormat;
cubeb_stream_params params;
params.rate = aRate;
params.channels = aNumChannels;
switch (aFormat) {
case FORMAT_S16_LE:
params.format = CUBEB_SAMPLE_S16LE;
mBytesPerFrame = sizeof(short) * aNumChannels;
break;
case FORMAT_FLOAT32:
params.format = CUBEB_SAMPLE_FLOAT32LE;
mBytesPerFrame = sizeof(float) * aNumChannels;
break;
default:
return NS_ERROR_FAILURE;
}
{
cubeb_stream* stream;
if (cubeb_stream_init(gCubebContext, &stream, "nsBufferedAudioStream", params,
DEFAULT_LATENCY_MS, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
mCubebStream.own(stream);
}
}
if (!mCubebStream) {
return NS_ERROR_FAILURE;
}
// Limit mBuffer to one second of audio. This value is arbitrary, and was
// selected based on the observed behaviour of the existing nsAudioStream
// implementations.
mBufferLimit = aRate * mBytesPerFrame;
NS_ABORT_IF_FALSE(mBufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
// Pre-allocate the buffer. nsTArray::RemoveElementsAt shrinks the buffer
// only if its length reaches zero, so allocator thrashing should be
// minimal.
mBuffer.SetCapacity(mBufferLimit);
return NS_OK;
}
void
nsBufferedAudioStream::Shutdown()
{
if (mCubebStream) {
cubeb_stream_stop(mCubebStream);
mCubebStream.reset();
}
}
nsresult
nsBufferedAudioStream::Write(const void* aBuf, PRUint32 aFrames)
{
MonitorAutoLock mon(mMonitor);
if (!mCubebStream) {
return NS_ERROR_FAILURE;
}
NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state.");
const PRUint8* src = static_cast<const PRUint8*>(aBuf);
PRUint32 bytesToCopy = aFrames * mBytesPerFrame;
while (bytesToCopy > 0) {
NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
PRUint32 available = NS_MIN(bytesToCopy, mBufferLimit - mBuffer.Length());
NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames.");
mBuffer.AppendElements(src, available);
src += available;
bytesToCopy -= available;
if (mState != STARTED && cubeb_stream_start(mCubebStream) == CUBEB_OK) {
mState = STARTED;
}
if (bytesToCopy > 0) {
mon.Wait();
}
}
return NS_OK;
}
PRUint32
nsBufferedAudioStream::Available()
{
MonitorAutoLock mon(mMonitor);
NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
return (mBufferLimit - mBuffer.Length()) / mBytesPerFrame;
}
PRInt32 nsBufferedAudioStream::GetMinWriteSize()
{
return 1;
}
void
nsBufferedAudioStream::SetVolume(double aVolume)
{
MonitorAutoLock mon(mMonitor);
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
mVolume = aVolume;
}
void
nsBufferedAudioStream::Drain()
{
MonitorAutoLock mon(mMonitor);
if (mState != STARTED) {
return;
}
mState = DRAINING;
while (mState != DRAINED) {
mon.Wait();
}
}
void
nsBufferedAudioStream::Pause()
{
MonitorAutoLock mon(mMonitor);
if (!mCubebStream || mState != STARTED) {
return;
}
if (cubeb_stream_stop(mCubebStream) == CUBEB_OK) {
mState = STOPPED;
}
}
void
nsBufferedAudioStream::Resume()
{
MonitorAutoLock mon(mMonitor);
if (!mCubebStream || mState != STOPPED) {
return;
}
if (cubeb_stream_start(mCubebStream) == CUBEB_OK) {
mState = STARTED;
}
}
PRInt64 nsBufferedAudioStream::GetPosition()
{
MonitorAutoLock mon(mMonitor);
PRInt64 frames = GetPositionInFramesUnlocked();
if (frames >= 0) {
return USECS_PER_S * frames / mRate;
}
return -1;
}
PRInt64
nsBufferedAudioStream::GetPositionInFrames()
{
MonitorAutoLock mon(mMonitor);
return GetPositionInFramesUnlocked();
}
PRInt64
nsBufferedAudioStream::GetPositionInFramesUnlocked()
{
mMonitor.AssertCurrentThreadOwns();
if (!mCubebStream) {
return -1;
}
uint64_t position = 0;
if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
return -1;
}
// Adjust the reported position by the number of silent frames written
// during stream underruns.
PRInt64 adjustedPosition = 0;
if (position >= mLostFrames) {
adjustedPosition = position - mLostFrames;
}
return adjustedPosition;
}
bool
nsBufferedAudioStream::IsPaused()
{
MonitorAutoLock mon(mMonitor);
return mState == STOPPED;
}
template<typename T>
void
SampleCopy(void* aDst, const PRUint8* aSrc, PRUint32 aSamples, double aVolume)
{
const T* src = reinterpret_cast<const T*>(aSrc);
double scaled_volume = GetVolumeScale() * aVolume;
T* dst = static_cast<T*>(aDst);
for (PRUint32 i = 0; i < aSamples; ++i) {
dst[i] = T(src[i] * scaled_volume);
}
}
long
nsBufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
{
MonitorAutoLock mon(mMonitor);
PRUint32 bytesWanted = aFrames * mBytesPerFrame;
// Adjust bytesWanted to fit what is available in mBuffer.
PRUint32 available = NS_MIN(bytesWanted, mBuffer.Length());
NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
// Copy each sample from mBuffer to aBuffer, adjusting the volume during the copy.
PRUint32 samplesToCopy = available / mBytesPerFrame * mChannels;
switch (mFormat) {
case FORMAT_S16_LE:
SampleCopy<PRInt16>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
break;
case FORMAT_FLOAT32:
SampleCopy<float>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
break;
default:
return -1;
}
// Remove copied data from the temporary audio buffer.
mBuffer.RemoveElementsAt(0, available);
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
// Notify any blocked Write() call that more space is available in mBuffer.
mon.NotifyAll();
// Calculate remaining bytes requested by caller. If the stream is not
// draining an underrun has occurred, so fill the remaining buffer with
// silence.
bytesWanted -= available;
if (mState != DRAINING) {
memset(static_cast<PRUint8*>(aBuffer) + available, 0, bytesWanted);
mLostFrames += bytesWanted / mBytesPerFrame;
bytesWanted = 0;
}
return aFrames - (bytesWanted / mBytesPerFrame);
}
int
nsBufferedAudioStream::StateCallback(cubeb_state aState)
{
if (aState == CUBEB_STATE_DRAINED) {
MonitorAutoLock mon(mMonitor);
mState = DRAINED;
mon.NotifyAll();
}
return CUBEB_OK;
}
#endif

View File

@ -233,7 +233,7 @@ const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100;
// Detect if we arrive in some sort of undetected recursive syncbase dependency
// relationship
const PRUint16 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20;
const PRUint8 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20;
//----------------------------------------------------------------------
// Ctor, dtor
@ -252,6 +252,7 @@ nsSMILTimedElement::nsSMILTimedElement()
mSeekState(SEEK_NOT_SEEKING),
mDeferIntervalUpdates(false),
mDoDeferredUpdate(false),
mDeleteCount(0),
mUpdateIntervalRecursionDepth(0)
{
mSimpleDur.SetIndefinite();
@ -1960,12 +1961,29 @@ nsSMILTimedElement::UpdateCurrentInterval(bool aForceChangeNotice)
if (mElementState == STATE_STARTUP)
return;
// Although SMIL gives rules for detecting cycles in change notifications,
// some configurations can lead to create-delete-create-delete-etc. cycles
// which SMIL does not consider.
//
// In order to provide consistent behavior in such cases, we detect two
// deletes in a row and then refuse to create any further intervals. That is,
// we say the configuration is invalid.
if (mDeleteCount > 1) {
// When we update the delete count we also set the state to post active, so
// if we're not post active here then something other than
// UpdateCurrentInterval has updated the element state in between and all
// bets are off.
NS_ABORT_IF_FALSE(mElementState == STATE_POSTACTIVE,
"Expected to be in post-active state after performing double delete");
return;
}
// Check that we aren't stuck in infinite recursion updating some syncbase
// dependencies. Generally such situations should be detected in advance and
// the chain broken in a sensible and predictable manner, so if we're hitting
// this assertion we need to work out how to detect the case that's causing
// it. In release builds, just bail out before we overflow the stack.
AutoRestore<PRUint16> depthRestorer(mUpdateIntervalRecursionDepth);
AutoRestore<PRUint8> depthRestorer(mUpdateIntervalRecursionDepth);
if (++mUpdateIntervalRecursionDepth > sMaxUpdateIntervalRecursionDepth) {
NS_ABORT_IF_FALSE(false,
"Update current interval recursion depth exceeded threshold");
@ -2026,6 +2044,8 @@ nsSMILTimedElement::UpdateCurrentInterval(bool aForceChangeNotice)
// sample (along with firing end events, clearing intervals etc.)
RegisterMilestone();
} else if (mElementState == STATE_WAITING) {
AutoRestore<PRUint8> deleteCountRestorer(mDeleteCount);
++mDeleteCount;
mElementState = STATE_POSTACTIVE;
ResetCurrentInterval();
}

View File

@ -625,13 +625,13 @@ protected:
// Used to batch updates to the timing model
class AutoIntervalUpdateBatcher;
bool mDeferIntervalUpdates;
bool mDoDeferredUpdate; // Set if an update to the current interval
// was requested while mDeferIntervalUpdates
// was set
bool mDoDeferredUpdate; // Set if an update to the current interval was
// requested while mDeferIntervalUpdates was set
// Recursion depth checking
PRUint16 mUpdateIntervalRecursionDepth;
static const PRUint16 sMaxUpdateIntervalRecursionDepth;
PRUint8 mDeleteCount;
PRUint8 mUpdateIntervalRecursionDepth;
static const PRUint8 sMaxUpdateIntervalRecursionDepth;
};
#endif // NS_SMILTIMEDELEMENT_H_

View File

@ -4927,7 +4927,7 @@ GenerateNormal(float *N, const PRUint8 *data, PRInt32 stride,
PRInt32 x, PRInt32 y, float surfaceScale)
{
// See this for source of constants:
// http://www.w3.org/TR/SVG11/filters.html#feDiffuseLighting
// http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
static const PRInt8 Kx[3][3][3][3] =
{ { { { 0, 0, 0}, { 0, -2, 2}, { 0, -1, 1} },
{ { 0, 0, 0}, {-2, 0, 2}, {-1, 0, 1} },

View File

@ -655,8 +655,16 @@ nsSHEntry::RemoveChild(nsISHEntry * aChild)
childRemoved = mChildren.ReplaceObjectAt(nsnull, index);
}
}
if (childRemoved)
if (childRemoved) {
aChild->SetParent(nsnull);
// reduce the child count, i.e. remove empty children at the end
for (PRInt32 i = mChildren.Count() - 1; i >= 0 && !mChildren[i]; --i) {
if (!mChildren.RemoveObjectAt(i)) {
break;
}
}
}
return NS_OK;
}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<script>
var collection = document.images;
var other = document.embeds;
var options = document.createElement("select").options;
collection.toString;
options.selectedIndex;
Object.getPrototypeOf(collection).item = {};
other.toString;
collection.toString;
options.selectedIndex;
options.toString;
</script>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<script>
var collection = document.images;
var other = document.embeds;
var options = document.createElement("select").options;
collection.toString;
options.selectedIndex;
Object.defineProperty(Object.getPrototypeOf(collection),
"item",
{ value: {}, enumerable: true, configurable: true });
other.toString;
collection.toString;
options.selectedIndex;
options.toString;
</script>

View File

@ -0,0 +1,4 @@
<!DOCTYPE html>
<script>
document.createElement("p").constructor = function(){};
</script>

View File

@ -30,6 +30,9 @@ load 637116.html
load 666869.html
load 675621-1.html
load 693894.html
load 693811-1.html
load 693811-2.html
load 693811-3.html
load 695867.html
load 697643.html
load 706283-1.html

View File

@ -88,14 +88,22 @@ AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
return true;
}
static void
CollectWindowMemoryUsage(nsGlobalWindow *aWindow,
nsIMemoryMultiReporterCallback *aCb,
nsISupports *aClosure)
struct WindowTotals
{
NS_NAMED_LITERAL_CSTRING(kWindowDesc,
"Memory used by a window and the DOM within it.");
WindowTotals() : mDom(0), mStyleSheets(0) {}
size_t mDom;
size_t mStyleSheets;
};
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WindowStyleSheetsMallocSizeOf,
"window/style-sheets")
static void
CollectWindowReports(nsGlobalWindow *aWindow,
WindowTotals *aWindowTotals,
nsIMemoryMultiReporterCallback *aCb,
nsISupports *aClosure)
{
// DOM window objects fall into one of three categories:
// - "active" windows are currently either displayed in an active
// tab, or a child of such a window.
@ -119,7 +127,7 @@ CollectWindowMemoryUsage(nsGlobalWindow *aWindow,
// The path we give to the reporter callback for inner windows are
// as follows:
//
// explicit/dom/window-objects/<category>/top=<top-outer-id> (inner=<top-inner-id>)/inner-window(id=<id>, uri=<uri>)
// explicit/dom+style/window-objects/<category>/top=<top-outer-id> (inner=<top-inner-id>)/inner-window(id=<id>, uri=<uri>)
//
// Where:
// - <category> is active, cached, or other, as described above.
@ -138,62 +146,82 @@ CollectWindowMemoryUsage(nsGlobalWindow *aWindow,
//
// For outer windows we simply use:
//
// explicit/dom/window-objects/<category>/outer-windows
// explicit/dom+style/window-objects/<category>/outer-windows
//
// Which gives us simple counts of how many outer windows (and their
// combined sizes) per category.
nsCAutoString str("explicit/dom/window-objects/");
nsCAutoString windowPath("explicit/dom+style/window-objects/");
nsIDocShell *docShell = aWindow->GetDocShell();
nsGlobalWindow *top = aWindow->GetTop();
PRInt64 windowSize = aWindow->SizeOf();
PRInt64 windowDOMSize = aWindow->SizeOf();
PRInt64 styleSheetsSize = aWindow->SizeOfStyleSheets(WindowStyleSheetsMallocSizeOf);
if (docShell && aWindow->IsFrozen()) {
str += NS_LITERAL_CSTRING("cached/");
windowPath += NS_LITERAL_CSTRING("cached/");
} else if (docShell) {
str += NS_LITERAL_CSTRING("active/");
windowPath += NS_LITERAL_CSTRING("active/");
} else {
str += NS_LITERAL_CSTRING("other/");
windowPath += NS_LITERAL_CSTRING("other/");
}
if (aWindow->IsInnerWindow()) {
str += NS_LITERAL_CSTRING("top=");
windowPath += NS_LITERAL_CSTRING("top=");
if (top) {
str.AppendInt(top->WindowID());
windowPath.AppendInt(top->WindowID());
nsGlobalWindow *topInner = top->GetCurrentInnerWindowInternal();
if (topInner) {
str += NS_LITERAL_CSTRING(" (inner=");
str.AppendInt(topInner->WindowID());
str += NS_LITERAL_CSTRING(")");
windowPath += NS_LITERAL_CSTRING(" (inner=");
windowPath.AppendInt(topInner->WindowID());
windowPath += NS_LITERAL_CSTRING(")");
}
} else {
str += NS_LITERAL_CSTRING("none");
windowPath += NS_LITERAL_CSTRING("none");
}
str += NS_LITERAL_CSTRING("/inner-window(id=");
str.AppendInt(aWindow->WindowID());
str += NS_LITERAL_CSTRING(", uri=");
windowPath += NS_LITERAL_CSTRING("/inner-window(id=");
windowPath.AppendInt(aWindow->WindowID());
windowPath += NS_LITERAL_CSTRING(", uri=");
if (!AppendWindowURI(aWindow, str)) {
str += NS_LITERAL_CSTRING("[system]");
if (!AppendWindowURI(aWindow, windowPath)) {
windowPath += NS_LITERAL_CSTRING("[system]");
}
str += NS_LITERAL_CSTRING(")");
windowPath += NS_LITERAL_CSTRING(")");
} else {
// Combine all outer windows per section (active/cached/other) as
// they basically never contain anything of interest, and are
// always pretty much the same size.
str += NS_LITERAL_CSTRING("outer-windows");
windowPath += NS_LITERAL_CSTRING("outer-windows");
}
aCb->Callback(EmptyCString(), str, nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, windowSize, kWindowDesc,
aClosure);
if (windowDOMSize > 0) {
nsCAutoString domPath(windowPath);
domPath += "/dom";
NS_NAMED_LITERAL_CSTRING(kWindowDesc,
"Memory used by a window and the DOM within it.");
aCb->Callback(EmptyCString(), domPath, nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, windowDOMSize, kWindowDesc,
aClosure);
aWindowTotals->mDom += windowDOMSize;
}
if (styleSheetsSize > 0) {
nsCAutoString styleSheetsPath(windowPath);
styleSheetsPath += "/style-sheets";
NS_NAMED_LITERAL_CSTRING(kStyleSheetsDesc,
"Memory used by style sheets within a window.");
aCb->Callback(EmptyCString(), styleSheetsPath,
nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, styleSheetsSize,
kStyleSheetsDesc, aClosure);
aWindowTotals->mStyleSheets += styleSheetsSize;
}
}
typedef nsTArray< nsRefPtr<nsGlobalWindow> > WindowArray;
@ -223,10 +251,27 @@ nsDOMMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
// Collect window memory usage.
nsRefPtr<nsGlobalWindow> *w = windows.Elements();
nsRefPtr<nsGlobalWindow> *end = w + windows.Length();
WindowTotals windowTotals;
for (; w != end; ++w) {
CollectWindowMemoryUsage(*w, aCb, aClosure);
CollectWindowReports(*w, &windowTotals, aCb, aClosure);
}
NS_NAMED_LITERAL_CSTRING(kDomTotalWindowsDesc,
"Memory used for the DOM within windows. This is the sum of all windows' "
"'dom' numbers.");
aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("dom-total-window"),
nsIMemoryReporter::KIND_OTHER,
nsIMemoryReporter::UNITS_BYTES, windowTotals.mDom,
kDomTotalWindowsDesc, aClosure);
NS_NAMED_LITERAL_CSTRING(kLayoutTotalWindowStyleSheetsDesc,
"Memory used for style sheets within windows. This is the sum of all windows' "
"'style-sheets' numbers.");
aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("style-sheets-total-window"),
nsIMemoryReporter::KIND_OTHER,
nsIMemoryReporter::UNITS_BYTES, windowTotals.mStyleSheets,
kLayoutTotalWindowStyleSheetsDesc, aClosure);
return NS_OK;
}

View File

@ -817,7 +817,7 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener,
}
#endif
nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS);
nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS, nsGCNormal, true);
nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls);
return NS_OK;

View File

@ -10296,6 +10296,16 @@ nsGlobalWindow::SizeOf() const
return size;
}
size_t
nsGlobalWindow::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const
{
size_t n = 0;
if (IsInnerWindow() && mDoc) {
n += mDoc->SizeOfStyleSheets(aMallocSizeOf);
}
return n;
}
// nsGlobalChromeWindow implementation
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)

View File

@ -576,6 +576,7 @@ public:
}
PRInt64 SizeOf() const;
size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const;
void UnmarkGrayTimers();
private:

View File

@ -135,6 +135,8 @@ static PRLogModuleInfo* gJSDiagnostics;
// doing the first GC.
#define NS_FIRST_GC_DELAY 10000 // ms
#define NS_FULL_GC_DELAY 60000 // ms
// The amount of time we wait between a request to CC (after GC ran)
// and doing the actual CC.
#define NS_CC_DELAY 5000 // ms
@ -148,6 +150,7 @@ static PRLogModuleInfo* gJSDiagnostics;
// if you add statics here, add them to the list in nsJSRuntime::Startup
static nsITimer *sGCTimer;
static nsITimer *sFullGCTimer;
static nsITimer *sShrinkGCBuffersTimer;
static nsITimer *sCCTimer;
@ -166,6 +169,7 @@ static bool sLoadingInProgress;
static PRUint32 sCCollectedWaitingForGC;
static bool sPostGCEventsToConsole;
static bool sDisableExplicitCompartmentGC;
static PRUint32 sCCTimerFireCount = 0;
static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX;
static PRUint32 sMaxForgetSkippableTime = 0;
@ -173,9 +177,15 @@ static PRUint32 sTotalForgetSkippableTime = 0;
static PRUint32 sRemovedPurples = 0;
static PRUint32 sForgetSkippableBeforeCC = 0;
static PRUint32 sPreviousSuspectedCount = 0;
static PRUint32 sCompartmentGCCount = 0;
static nsJSContext* sTopEvaluator = nsnull;
static bool sContextDeleted = false;
static bool sPreviousWasChromeCompGC = false;
static bool sCleanupSinceLastGC = true;
PRUint32 nsJSContext::sGlobalGCEpoch = 0;
nsScriptNameSpaceManager *gNameSpaceManager;
static nsIJSRuntimeService *sRuntimeService;
@ -216,7 +226,8 @@ nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
if (sGCOnMemoryPressure) {
nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking);
nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking,
true);
nsJSContext::CycleCollectNow();
}
return NS_OK;
@ -938,6 +949,8 @@ static const char js_pccounts_content_str[] = JS_OPTIONS_DOT_STR "pccounts.con
static const char js_pccounts_chrome_str[] = JS_OPTIONS_DOT_STR "pccounts.chrome";
static const char js_jit_hardening_str[] = JS_OPTIONS_DOT_STR "jit_hardening";
static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
static const char js_disable_explicit_compartment_gc[] =
JS_OPTIONS_DOT_STR "disable_explicit_compartment_gc";
int
nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
@ -947,6 +960,8 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
sDisableExplicitCompartmentGC =
Preferences::GetBool(js_disable_explicit_compartment_gc);
bool strict = Preferences::GetBool(js_strict_option_str);
if (strict)
@ -1048,6 +1063,9 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
nsJSContext::nsJSContext(JSRuntime *aRuntime)
: mGCOnDestruction(true),
mChromeComp(false),
mGlobalGCEpoch(0),
mEvaluationCount(0),
mExecuteDepth(0)
{
@ -1098,6 +1116,9 @@ nsJSContext::~nsJSContext()
DestroyJSContext();
--sContextCount;
if (sTopEvaluator == this) {
sTopEvaluator = nsnull;
}
if (!sContextCount && sDidShutdown) {
// The last context is being deleted, and we're already in the
@ -1124,6 +1145,7 @@ nsJSContext::DestroyJSContext()
js_options_dot_str, this);
if (mGCOnDestruction) {
sContextDeleted = true;
PokeGC(js::gcreason::NSJSCONTEXT_DESTROY);
}
@ -2237,6 +2259,7 @@ nsJSContext::CreateNativeGlobalForInner(
{
nsIXPConnect *xpc = nsContentUtils::XPConnect();
PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
mChromeComp = aIsChrome;
nsCOMPtr<nsIPrincipal> systemPrincipal;
if (aIsChrome) {
@ -3159,6 +3182,12 @@ nsJSContext::ScriptEvaluated(bool aTerminated)
if (aTerminated && mExecuteDepth == 0 && !JS_IsRunning(mContext)) {
mOperationCallbackTime = 0;
mModalStateTime = 0;
IncreaseEvaluationCount(this);
if (EvaluationCount(sTopEvaluator) < EvaluationCount(this) &&
(!mChromeComp || !sPreviousWasChromeCompGC)) {
sTopEvaluator = this;
}
}
}
@ -3231,9 +3260,20 @@ nsJSContext::ScriptExecuted()
return NS_OK;
}
void
FullGCTimerFired(nsITimer* aTimer, void* aClosure)
{
NS_RELEASE(sFullGCTimer);
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason),
nsGCNormal, true);
}
//static
void
nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind)
nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, PRUint32 aGckind,
bool aGlobal)
{
NS_TIME_FUNCTION_MIN(1.0);
SAMPLE_LABEL("GC", "GarbageCollectNow");
@ -3250,9 +3290,44 @@ nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind)
sPendingLoadCount = 0;
sLoadingInProgress = false;
if (nsContentUtils::XPConnect()) {
nsContentUtils::XPConnect()->GarbageCollect(reason, gckind);
if (!nsContentUtils::XPConnect()) {
return;
}
// Use compartment GC when we're not asked to do a shrinking GC nor
// global GC and compartment GC has been called less than 10 times after
// the previous global GC. If a top level browsing context has been
// deleted, we do a global GC.
if (!sDisableExplicitCompartmentGC &&
aGckind != nsGCShrinking && !aGlobal &&
EvaluationCount(sTopEvaluator) > 0 &&
!sContextDeleted && sCompartmentGCCount < 10) {
nsJSContext* top = sTopEvaluator;
sTopEvaluator = nsnull;
ResetEvaluationCount(top);
JSContext* cx = top->GetNativeContext();
if (cx) {
JSObject* global = top->GetNativeGlobal();
if (global) {
if (!sFullGCTimer) {
CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
}
if (sFullGCTimer) {
sFullGCTimer->Cancel();
js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER;
sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
reinterpret_cast<void *>(reason),
NS_FULL_GC_DELAY,
nsITimer::TYPE_ONE_SHOT);
}
JSCompartment* comp = js::GetObjectCompartment(global);
js::CompartmentGCForReason(cx, comp, aReason);
return;
}
}
}
nsContentUtils::XPConnect()->GarbageCollect(aReason, aGckind);
}
//static
@ -3356,7 +3431,8 @@ GCTimerFired(nsITimer *aTimer, void *aClosure)
NS_RELEASE(sGCTimer);
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCNormal);
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason),
nsGCNormal, false);
}
void
@ -3524,6 +3600,16 @@ nsJSContext::KillGCTimer()
}
}
void
nsJSContext::KillFullGCTimer()
{
if (sFullGCTimer) {
sFullGCTimer->Cancel();
NS_RELEASE(sFullGCTimer);
}
}
//static
void
nsJSContext::KillShrinkGCBuffersTimer()
@ -3552,8 +3638,8 @@ nsJSContext::GC(js::gcreason::Reason aReason)
PokeGC(aReason);
}
static void
DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
void
nsJSContext::DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
{
NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
@ -3579,6 +3665,19 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
sCCollectedWaitingForGC = 0;
sCleanupSinceLastGC = false;
if (!comp) {
sPreviousWasChromeCompGC = false;
sContextDeleted = false;
sCompartmentGCCount = 0;
++sGlobalGCEpoch;
KillFullGCTimer();
} else {
// Only every other compartment GC is allowed to collect a chrome
// compartment. Otherwise we'd collect chrome compartment all the time.
sPreviousWasChromeCompGC = xpc::AccessCheck::isChrome(comp);
++sCompartmentGCCount;
}
if (sGCTimer) {
// If we were waiting for a GC to happen, kill the timer.
nsJSContext::KillGCTimer();
@ -3699,13 +3798,14 @@ void
nsJSRuntime::Startup()
{
// initialize all our statics, so that we can restart XPCOM
sGCTimer = sCCTimer = nsnull;
sGCTimer = sFullGCTimer = sCCTimer = nsnull;
sGCHasRun = false;
sLastCCEndTime = 0;
sPendingLoadCount = 0;
sLoadingInProgress = false;
sCCollectedWaitingForGC = 0;
sPostGCEventsToConsole = false;
sDisableExplicitCompartmentGC = false;
gNameSpaceManager = nsnull;
sRuntimeService = nsnull;
sRuntime = nsnull;
@ -3862,7 +3962,7 @@ nsJSRuntime::Init()
// Let's make sure that our main thread is the same as the xpcom main thread.
NS_ASSERTION(NS_IsMainThread(), "bad");
::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback);
::JS_SetGCFinishedCallback(sRuntime, nsJSContext::DOMGCFinishedCallback);
JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
@ -3947,6 +4047,7 @@ void
nsJSRuntime::Shutdown()
{
nsJSContext::KillGCTimer();
nsJSContext::KillFullGCTimer();
nsJSContext::KillShrinkGCBuffersTimer();
nsJSContext::KillCCTimer();

View File

@ -181,7 +181,9 @@ public:
static void LoadStart();
static void LoadEnd();
static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal);
static void GarbageCollectNow(js::gcreason::Reason aReason,
PRUint32 aGckind,
bool aGlobal);
static void ShrinkGCBuffersNow();
// If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
// called even if the previous collection was GC.
@ -190,6 +192,7 @@ public:
static void PokeGC(js::gcreason::Reason aReason);
static void KillGCTimer();
static void KillFullGCTimer();
static void PokeShrinkGCBuffers();
static void KillShrinkGCBuffersTimer();
@ -208,6 +211,29 @@ public:
JSObject* global = JS_GetGlobalObject(mContext);
return global ? mGlobalObjectRef.get() : nsnull;
}
static PRUint32 EvaluationCount(nsJSContext* aCx)
{
return aCx && aCx->mGlobalGCEpoch == sGlobalGCEpoch ?
aCx->mEvaluationCount : 0;
}
static void IncreaseEvaluationCount(nsJSContext* aCx)
{
if (aCx->mGlobalGCEpoch != sGlobalGCEpoch) {
aCx->mEvaluationCount = 0;
aCx->mGlobalGCEpoch = sGlobalGCEpoch;
}
++(aCx->mEvaluationCount);
}
static void ResetEvaluationCount(nsJSContext* aCx)
{
aCx->mEvaluationCount = 0;
}
static void DOMGCFinishedCallback(JSRuntime* aRt, JSCompartment* aComp,
const char* aStatus);
protected:
nsresult InitializeExternalClasses();
@ -296,7 +322,9 @@ private:
bool mScriptsEnabled;
bool mGCOnDestruction;
bool mProcessingScriptTag;
bool mChromeComp;
PRUint32 mGlobalGCEpoch;
PRUint32 mEvaluationCount;
PRUint32 mExecuteDepth;
PRUint32 mDefaultJSOptions;
PRTime mOperationCallbackTime;
@ -308,6 +336,8 @@ private:
// context does. It is eventually collected by the cycle collector.
nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
static PRUint32 sGlobalGCEpoch;
static int JSOptionChangedCallback(const char *pref, void *data);
static JSBool DOMOperationCallback(JSContext *cx);

View File

@ -54,6 +54,7 @@
interface nsIDOMFile;
interface nsIVariant;
interface nsIInputStreamCallback;
[scriptable, uuid(8cddbc86-f384-40ac-835b-fe3e00630cad)]
interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
@ -82,5 +83,10 @@ interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
// A Mozilla-only extension to get a canvas context backed by double-buffered
// shared memory. Only privileged callers can call this.
nsISupports MozGetIPCContext(in DOMString contextId);
// A Mozilla-only extension that returns the canvas' image data as a data
// stream in the desired image format.
void mozFetchAsStream(in nsIInputStreamCallback callback,
[optional] in DOMString type);
};

View File

@ -800,14 +800,14 @@ ContentChild::GetIndexedDBPath()
bool
ContentChild::RecvGarbageCollect()
{
nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC);
nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true);
return true;
}
bool
ContentChild::RecvCycleCollect()
{
nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC);
nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true);
nsJSContext::CycleCollectNow();
return true;
}

View File

@ -138,6 +138,7 @@ endif
LOCAL_INCLUDES += \
-DSK_BUILD_FOR_ANDROID_NDK \
-I$(topsrcdir)/widget/android \
-I$(topsrcdir)/xpcom/base/ \
-I$(topsrcdir)/gfx/skia/include/core \
-I$(topsrcdir)/gfx/skia/include/config \

View File

@ -306,7 +306,6 @@ anp_audio_start(ANPAudioTrack* s)
if (s->keepGoing) {
// we are already playing. Ignore.
LOG("anp_audio_start called twice!");
return;
}
@ -359,7 +358,14 @@ anp_audio_isStopped(ANPAudioTrack* s)
return s->isStopped;
}
void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) {
uint32_t
anp_audio_trackLatency(ANPAudioTrack* s) {
// Bug 721835
NOT_IMPLEMENTED();
return 1;
}
void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, newTrack);
ASSIGN(i, deleteTrack);
@ -368,3 +374,14 @@ void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) {
ASSIGN(i, stop);
ASSIGN(i, isStopped);
}
void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, newTrack);
ASSIGN(i, deleteTrack);
ASSIGN(i, start);
ASSIGN(i, pause);
ASSIGN(i, stop);
ASSIGN(i, isStopped);
ASSIGN(i, trackLatency);
}

View File

@ -36,8 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include "android_npapi.h"
#include <stdlib.h>
#include "android_npapi.h"
#include "nsAutoPtr.h"
#include "nsISupportsImpl.h"
@ -53,7 +53,8 @@
"!!!!!!!!!!!!!! %s not implemented %s, %d", \
__PRETTY_FUNCTION__, __FILE__, __LINE__); \
void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i);
void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i);
void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i);
void InitBitmapInterface(ANPBitmapInterfaceV0 *i);
void InitCanvasInterface(ANPCanvasInterfaceV0 *i);
void InitEventInterface(ANPEventInterfaceV0 *i);
@ -63,5 +64,13 @@ void InitPaintInterface(ANPPaintInterfaceV0 *i);
void InitPathInterface(ANPPathInterfaceV0 *i);
void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i);
void InitSystemInterface(ANPSystemInterfaceV0 *i);
void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i);
void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i);
void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i);
void InitWindowInterface(ANPWindowInterfaceV0 *i);
void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i);
void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i);
void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i);
void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i);
void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i);
void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i);

View File

@ -0,0 +1,85 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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 Android NPAPI support code
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Willcox <jwillcox@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// must include config.h first for webkit to fiddle with new/delete
#include <android/log.h>
#include "AndroidBridge.h"
#include "AndroidMediaLayer.h"
#include "ANPBase.h"
#include "nsIPluginInstanceOwner.h"
#include "nsPluginInstanceOwner.h"
#include "nsNPAPIPluginInstance.h"
#include "gfxRect.h"
using namespace mozilla;
using namespace mozilla;
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name
static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return NULL;
}
ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent();
owner->Invalidate();
return window;
}
static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return;
}
owner->Layer()->SetInverted(isContentInverted);
}
void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) {
ASSIGN(i, acquireNativeWindow);
ASSIGN(i, invertPluginContent);
}

View File

@ -0,0 +1,65 @@
/* The Original Code is Android NPAPI support code
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Willcox <jwillcox@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <dlfcn.h>
#include <android/log.h>
#include "AndroidBridge.h"
#include "ANPBase.h"
#include "GLContextProvider.h"
#include "nsNPAPIPluginInstance.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#define ASSIGN(obj, name) (obj)->name = anp_opengl_##name
using namespace mozilla;
using namespace mozilla::gl;
static ANPEGLContext anp_opengl_acquireContext(NPP inst) {
// Bug 687267
NOT_IMPLEMENTED();
return NULL;
}
static ANPTextureInfo anp_opengl_lockTexture(NPP instance) {
ANPTextureInfo info = { 0, 0, 0, 0 };
NOT_IMPLEMENTED();
return info;
}
static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) {
NOT_IMPLEMENTED();
}
static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) {
NOT_IMPLEMENTED();
}
///////////////////////////////////////////////////////////////////////////////
void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) {
ASSIGN(i, acquireContext);
ASSIGN(i, lockTexture);
ASSIGN(i, releaseTexture);
ASSIGN(i, invertPluginContent);
}

View File

@ -62,6 +62,12 @@ anp_system_getApplicationDataDirectory()
return dir;
}
const char*
anp_system_getApplicationDataDirectory(NPP instance)
{
return anp_system_getApplicationDataDirectory();
}
jclass anp_system_loadJavaClass(NPP instance, const char* className)
{
LOG("%s", __PRETTY_FUNCTION__);
@ -88,8 +94,27 @@ jclass anp_system_loadJavaClass(NPP instance, const char* className)
return reinterpret_cast<jclass>(obj);
}
void anp_system_setPowerState(NPP instance, ANPPowerState powerState)
{
NOT_IMPLEMENTED();
}
void InitSystemInterface(ANPSystemInterfaceV0 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, getApplicationDataDirectory);
ASSIGN(i, loadJavaClass);
}
void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, getApplicationDataDirectory);
ASSIGN(i, loadJavaClass);
ASSIGN(i, setPowerState);
}
void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, getApplicationDataDirectory);
ASSIGN(i, loadJavaClass);
ASSIGN(i, setPowerState);
}

View File

@ -0,0 +1,109 @@
/* The Original Code is Android NPAPI support code
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* James Willcox <jwillcox@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <android/log.h>
#include "ANPBase.h"
#include "AndroidMediaLayer.h"
#include "nsIPluginInstanceOwner.h"
#include "nsPluginInstanceOwner.h"
#include "nsNPAPIPluginInstance.h"
#include "gfxRect.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#define ASSIGN(obj, name) (obj)->name = anp_video_##name
using namespace mozilla;
static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return NULL;
}
return owner->Layer();
}
static void Invalidate(NPP instance) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner)))
return;
owner->Invalidate();
}
static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) {
AndroidMediaLayer* layer = GetLayerForInstance(instance);
if (!layer)
return NULL;
return layer->RequestNativeWindowForVideo();
}
static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window,
const ANPRectF* dimensions) {
AndroidMediaLayer* layer = GetLayerForInstance(instance);
if (!layer)
return;
gfxRect rect(dimensions->left, dimensions->top,
dimensions->right - dimensions->left,
dimensions->bottom - dimensions->top);
layer->SetNativeWindowDimensions(window, rect);
Invalidate(instance);
}
static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) {
AndroidMediaLayer* layer = GetLayerForInstance(instance);
if (!layer)
return;
layer->ReleaseNativeWindowForVideo(window);
Invalidate(instance);
}
static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) {
// Bug 722682
NOT_IMPLEMENTED();
}
///////////////////////////////////////////////////////////////////////////////
void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) {
ASSIGN(i, acquireNativeWindow);
ASSIGN(i, setWindowDimensions);
ASSIGN(i, releaseNativeWindow);
}
void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) {
ASSIGN(i, acquireNativeWindow);
ASSIGN(i, setWindowDimensions);
ASSIGN(i, releaseNativeWindow);
ASSIGN(i, setFramerateCallback);
}

View File

@ -39,10 +39,16 @@
#include "assert.h"
#include "ANPBase.h"
#include <android/log.h>
#include "AndroidBridge.h"
#include "nsNPAPIPluginInstance.h"
#include "nsIPluginInstanceOwner.h"
#include "nsPluginInstanceOwner.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#define ASSIGN(obj, name) (obj)->name = anp_window_##name
using namespace mozilla;
void
anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count)
{
@ -79,6 +85,32 @@ anp_window_requestCenterFitZoom(NPP instance)
NOT_IMPLEMENTED();
}
ANPRectI
anp_window_visibleRect(NPP instance)
{
ANPRectI rect = { 0, 0, 0, 0 };
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return rect;
}
nsIntRect visibleRect = owner->GetVisibleRect();
rect.left = visibleRect.x;
rect.top = visibleRect.y;
rect.right = visibleRect.x + visibleRect.width;
rect.bottom = visibleRect.y + visibleRect.height;
return rect;
}
void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation)
{
NOT_IMPLEMENTED();
}
void InitWindowInterface(ANPWindowInterfaceV0 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, setVisibleRects);
@ -89,3 +121,26 @@ void InitWindowInterface(ANPWindowInterfaceV0 *i) {
ASSIGN(i, requestCenterFitZoom);
}
void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, setVisibleRects);
ASSIGN(i, clearVisibleRects);
ASSIGN(i, showKeyboard);
ASSIGN(i, requestFullScreen);
ASSIGN(i, exitFullScreen);
ASSIGN(i, requestCenterFitZoom);
ASSIGN(i, visibleRect);
}
void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) {
_assert(i->inSize == sizeof(*i));
ASSIGN(i, setVisibleRects);
ASSIGN(i, clearVisibleRects);
ASSIGN(i, showKeyboard);
ASSIGN(i, requestFullScreen);
ASSIGN(i, exitFullScreen);
ASSIGN(i, requestCenterFitZoom);
ASSIGN(i, visibleRect);
ASSIGN(i, requestFullScreenOrientation);
}

View File

@ -63,12 +63,17 @@ CPPSRCS += ANPAudio.cpp \
ANPWindow.cpp \
ANPBitmap.cpp \
ANPLog.cpp \
ANPNativeWindow.cpp \
ANPSurface.cpp \
ANPVideo.cpp \
ANPOpenGL.cpp \
$(NULL)
LOCAL_INCLUDES += \
-I$(topsrcdir)/widget/android \
-I$(topsrcdir)/dom/plugins/base \
-I$(topsrcdir)/dom/plugins/base/android/include \
-I$(topsrcdir)/gfx/gl \
$(MOZ_CAIRO_CFLAGS) \
$(NULL)

View File

@ -36,8 +36,9 @@
#define android_npapi_H
#include <stdint.h>
#include "npapi.h"
#include <jni.h>
#include "npapi.h"
#include "GLDefs.h"
///////////////////////////////////////////////////////////////////////////////
// General types
@ -120,6 +121,16 @@ typedef uint32_t ANPMatrixFlag;
#define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010)
#define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011)
#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012)
#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013)
#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014)
#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015)
#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016)
#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017)
#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018)
#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019)
#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020)
/** queries for the drawing models supported on this device.
NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits)
@ -180,6 +191,7 @@ enum ANPDrawingModels {
surface object.
*/
kSurface_ANPDrawingModel = 1 << 1,
kOpenGL_ANPDrawingModel = 1 << 2,
};
typedef int32_t ANPDrawingModel;
@ -678,6 +690,25 @@ struct ANPWindowInterfaceV0 : ANPInterface {
void (*requestCenterFitZoom)(NPP instance);
};
struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 {
/** Returns a rectangle representing the visible area of the plugin on
screen. The coordinates are relative to the size of the plugin in the
document and therefore will never be negative or exceed the plugin's size.
*/
ANPRectI (*visibleRect)(NPP instance);
};
typedef int32_t ANPScreenOrientation;
struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 {
/** Called when the plugin wants to specify a particular screen orientation
when entering into full screen mode. The orientation must be set prior
to entering into full screen. After entering full screen any subsequent
changes will be updated the next time the plugin goes full screen.
*/
void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation);
};
///////////////////////////////////////////////////////////////////////////////
enum ANPSampleFormats {
@ -762,6 +793,12 @@ struct ANPAudioTrackInterfaceV0 : ANPInterface {
bool (*isStopped)(ANPAudioTrack*);
};
struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 {
/** Returns the track's latency in milliseconds. */
uint32_t (*trackLatency)(ANPAudioTrack*);
};
///////////////////////////////////////////////////////////////////////////////
// DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent
@ -922,12 +959,16 @@ struct ANPEvent {
// use based on the value in model
union {
ANPBitmap bitmap;
struct {
int32_t width;
int32_t height;
} surfaceSize;
} data;
} draw;
int32_t other[8];
} data;
};
struct ANPEventInterfaceV0 : ANPInterface {
/** Post a copy of the specified event to the plugin. The event will be
delivered to the plugin in its main thread (the thread that receives
@ -976,4 +1017,117 @@ typedef uint32_t uint32;
typedef int16_t int16;
typedef uint16_t uint16;
/**
* TODO should we not use EGL and GL data types for ABI safety?
*/
struct ANPTextureInfo {
GLuint textureId;
uint32_t width;
uint32_t height;
GLenum internalFormat;
};
typedef void* ANPEGLContext;
struct ANPOpenGLInterfaceV0 : ANPInterface {
ANPEGLContext (*acquireContext)(NPP instance);
ANPTextureInfo (*lockTexture)(NPP instance);
void (*releaseTexture)(NPP instance, const ANPTextureInfo*);
/**
* Invert the contents of the plugin on the y-axis.
* default is to not be inverted (i.e. use OpenGL coordinates)
*/
void (*invertPluginContent)(NPP instance, bool isContentInverted);
};
enum ANPPowerStates {
kDefault_ANPPowerState = 0,
kScreenOn_ANPPowerState = 1
};
typedef int32_t ANPPowerState;
struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 {
void (*setPowerState)(NPP instance, ANPPowerState powerState);
};
struct ANPSystemInterfaceV2 : ANPInterface {
/** Return the path name for the current Application's plugin data directory,
or NULL if not supported. This directory will change depending on whether
or not the plugin is found within an incognito tab.
*/
const char* (*getApplicationDataDirectory)(NPP instance);
// redeclaration of existing features
jclass (*loadJavaClass)(NPP instance, const char* className);
void (*setPowerState)(NPP instance, ANPPowerState powerState);
};
typedef void* ANPNativeWindow;
struct ANPVideoInterfaceV0 : ANPInterface {
/**
* Constructs a new native window to be used for rendering video content.
*
* Subsequent calls will produce new windows, but may also return NULL after
* n attempts if the browser has reached it's limit. Further, if the browser
* is unable to acquire the window quickly it may also return NULL in order
* to not prevent the plugin from executing. A subsequent call will then
* return the window if it is avaiable.
*
* NOTE: The hardware may fail if you try to decode more than the allowable
* number of videos supported on that device.
*/
ANPNativeWindow (*acquireNativeWindow)(NPP instance);
/**
* Sets the rectangle that specifies where the video content is to be drawn.
* The dimensions are in document space. Further, if the rect is NULL the
* browser will not attempt to draw the window, therefore do not set the
* dimensions until you queue the first buffer in the window.
*/
void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions);
/**
*/
void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window);
};
/** Called to notify the plugin that a video frame has been composited by the
* browser for display. This will be called in a separate thread and as such
* you cannot call releaseNativeWindow from the callback.
*
* The timestamp is in nanoseconds, and is monotonically increasing.
*/
typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp);
struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 {
/** Set a callback to be notified when an ANPNativeWindow is composited by
* the browser.
*/
void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc);
};
struct ANPNativeWindowInterfaceV0 : ANPInterface {
/**
* Constructs a new native window to be used for rendering plugin content.
*
* Subsequent calls will return the original constructed window. Further, if
* the browser is unable to acquire the window quickly it may return NULL in
* order to not block the plugin indefinitely. A subsequent call will then
* return the window if it is available.
*/
ANPNativeWindow (*acquireNativeWindow)(NPP instance);
/**
* Invert the contents of the plugin on the y-axis.
* default is to not be inverted (e.g. use OpenGL coordinates)
*/
void (*invertPluginContent)(NPP instance, bool isContentInverted);
};
#endif

View File

@ -132,9 +132,10 @@ using mozilla::plugins::PluginModuleParent;
#endif
#ifdef MOZ_WIDGET_ANDROID
#include <android/log.h>
#include "android_npapi.h"
#include "ANPBase.h"
#include "AndroidBridge.h"
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#endif
@ -2348,7 +2349,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
case kAudioTrackInterfaceV0_ANPGetValue: {
LOG("get audio interface");
ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result;
InitAudioTrackInterface(i);
InitAudioTrackInterfaceV0(i);
return NPERR_NO_ERROR;
}
@ -2363,7 +2364,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
LOG("get system interface");
ANPSystemInterfaceV0* i = reinterpret_cast<ANPSystemInterfaceV0*>(result);
InitSystemInterface(i);
LOG("done system interface");
return NPERR_NO_ERROR;
}
@ -2395,6 +2395,71 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
*i = reinterpret_cast<int32_t>(ret);
return NPERR_NO_ERROR;
}
case kAudioTrackInterfaceV1_ANPGetValue: {
LOG("get audio interface v1");
ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result;
InitAudioTrackInterfaceV1(i);
return NPERR_NO_ERROR;
}
case kNativeWindowInterfaceV0_ANPGetValue: {
LOG("get native window interface v0");
ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result;
InitNativeWindowInterface(i);
return NPERR_NO_ERROR;
}
case kOpenGLInterfaceV0_ANPGetValue: {
LOG("get openGL interface");
ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result;
InitOpenGLInterface(i);
return NPERR_NO_ERROR;
}
case kWindowInterfaceV1_ANPGetValue: {
LOG("get Window interface V1");
ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result;
InitWindowInterfaceV1(i);
return NPERR_NO_ERROR;
}
case kWindowInterfaceV2_ANPGetValue: {
LOG("get Window interface V2");
ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result;
InitWindowInterfaceV2(i);
return NPERR_NO_ERROR;
}
case kVideoInterfaceV0_ANPGetValue: {
LOG("get video interface");
ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result;
InitVideoInterfaceV0(i);
return NPERR_NO_ERROR;
}
case kVideoInterfaceV1_ANPGetValue: {
LOG("get video interface");
ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result;
InitVideoInterfaceV1(i);
return NPERR_NO_ERROR;
}
case kSystemInterfaceV1_ANPGetValue: {
LOG("get system interface v1");
ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(result);
InitSystemInterfaceV1(i);
return NPERR_NO_ERROR;
}
case kSystemInterfaceV2_ANPGetValue: {
LOG("get system interface v2");
ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(result);
InitSystemInterfaceV2(i);
return NPERR_NO_ERROR;
}
#endif
// we no longer hand out any XPCOM objects

View File

@ -150,6 +150,8 @@ public:
#ifdef MOZ_WIDGET_ANDROID
PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
void SetANPDrawingModel(PRUint32 aModel);
// This stuff is for kSurface_ANPDrawingModel
void* GetJavaSurface();
void SetJavaSurface(void* aSurface);
void RequestJavaSurface();

View File

@ -122,6 +122,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#include "ANPBase.h"
#include "android_npapi.h"
#include "AndroidBridge.h"
#include "AndroidMediaLayer.h"
using namespace mozilla::dom;
#include <android/log.h>
@ -262,23 +263,14 @@ nsPluginInstanceOwner::UseAsyncRendering()
if (mUseAsyncRendering) {
return true;
}
nsRefPtr<ImageContainer> container;
if (mObjectFrame) {
container = mObjectFrame->GetImageContainer();
}
#endif
bool useAsyncRendering;
bool result = (mInstance &&
NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncRendering)) &&
useAsyncRendering &&
#ifdef XP_MACOSX
container &&
container->GetBackendType() ==
LayerManager::LAYERS_OPENGL
#else
(!mPluginWindow ||
useAsyncRendering
#ifndef XP_MACOSX
&& (!mPluginWindow ||
mPluginWindow->type == NPWindowTypeDrawable)
#endif
);
@ -348,8 +340,9 @@ nsPluginInstanceOwner::nsPluginInstanceOwner()
mWaitingForPaint = false;
#ifdef MOZ_WIDGET_ANDROID
mPluginViewAdded = false;
mLastPluginRect = gfxRect(0, 0, 0, 0);
mOnScreen = false;
mInverted = false;
mLayer = new AndroidMediaLayer();
#endif
}
@ -400,6 +393,13 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner()
PLUG_DeletePluginNativeWindow(mPluginWindow);
mPluginWindow = nsnull;
#ifdef MOZ_WIDGET_ANDROID
if (mLayer) {
delete mLayer;
mLayer = nsnull;
}
#endif
if (mInstance) {
mInstance->InvalidateOwner();
}
@ -1683,6 +1683,43 @@ void nsPluginInstanceOwner::ScrollPositionDidChange(nscoord aX, nscoord aY)
}
#ifdef MOZ_WIDGET_ANDROID
void nsPluginInstanceOwner::SendSize(int width, int height)
{
if (!mInstance)
return;
PRInt32 model = mInstance->GetANPDrawingModel();
if (model != kOpenGL_ANPDrawingModel)
return;
ANPEvent event;
event.inSize = sizeof(ANPEvent);
event.eventType = kDraw_ANPEventType;
event.data.draw.model = kOpenGL_ANPDrawingModel;
event.data.draw.data.surfaceSize.width = width;
event.data.draw.data.surfaceSize.height = height;
mInstance->HandleEvent(&event, nsnull);
}
void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen)
{
if (!mInstance)
return;
if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) {
ANPEvent event;
event.inSize = sizeof(ANPEvent);
event.eventType = kLifecycle_ANPEventType;
event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction;
mInstance->HandleEvent(&event, nsnull);
mOnScreen = onScreen;
}
}
bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
{
void* javaSurface = mInstance->GetJavaSurface();
@ -1691,11 +1728,6 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
return false;
}
if (aRect.IsEqualEdges(mLastPluginRect)) {
// Already added and in position, no work to do
return true;
}
JNIEnv* env = GetJNIForThread();
if (!env)
return false;
@ -1738,26 +1770,16 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
aRect.height);
#endif
if (!mPluginViewAdded) {
ANPEvent event;
event.inSize = sizeof(ANPEvent);
event.eventType = kLifecycle_ANPEventType;
event.data.lifecycle.action = kOnScreen_ANPLifecycleAction;
mInstance->HandleEvent(&event, nsnull);
mPluginViewAdded = true;
}
SendOnScreenEvent(true);
return true;
}
void nsPluginInstanceOwner::RemovePluginView()
{
if (!mInstance || !mObjectFrame | !mPluginViewAdded)
if (!mInstance || !mObjectFrame | !mOnScreen)
return;
mPluginViewAdded = false;
void* surface = mInstance->GetJavaSurface();
if (!surface)
return;
@ -1773,13 +1795,17 @@ void nsPluginInstanceOwner::RemovePluginView()
"removePluginView",
"(Landroid/view/View;)V");
env->CallStaticVoidMethod(cls, method, surface);
ANPEvent event;
event.inSize = sizeof(ANPEvent);
event.eventType = kLifecycle_ANPEventType;
event.data.lifecycle.action = kOffScreen_ANPLifecycleAction;
mInstance->HandleEvent(&event, nsnull);
SendOnScreenEvent(false);
}
void nsPluginInstanceOwner::Invalidate() {
NPRect rect;
rect.left = rect.top = 0;
rect.right = mPluginWindow->width;
rect.bottom = mPluginWindow->height;
InvalidateRect(&rect);
}
#endif
nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
@ -2861,15 +2887,21 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext,
if (model == kSurface_ANPDrawingModel) {
if (!AddPluginView(aFrameRect)) {
NPRect rect;
rect.left = rect.top = 0;
rect.right = aFrameRect.width;
rect.bottom = aFrameRect.height;
InvalidateRect(&rect);
Invalidate();
}
return;
}
if (model == kOpenGL_ANPDrawingModel) {
// FIXME: this is gross
float zoomLevel = aFrameRect.width / (float)mPluginWindow->width;
mLayer->UpdatePosition(aFrameRect, zoomLevel);
SendOnScreenEvent(true);
SendSize((int)aFrameRect.width, (int)aFrameRect.height);
return;
}
if (model != kBitmap_ANPDrawingModel)
return;
@ -3566,8 +3598,16 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow)
} else {
mPluginWindow->clipRect.right = 0;
mPluginWindow->clipRect.bottom = 0;
#ifdef MOZ_WIDGET_ANDROID
RemovePluginView();
#if 0 //MOZ_WIDGET_ANDROID
if (mInstance) {
PRInt32 model = mInstance->GetANPDrawingModel();
if (model == kSurface_ANPDrawingModel) {
RemovePluginView();
} else if (model == kOpenGL_ANPDrawingModel) {
HidePluginLayer();
}
}
#endif
}

View File

@ -94,6 +94,12 @@ class gfxXlibSurface;
#include <os2.h>
#endif
#ifdef MOZ_WIDGET_ANDROID
namespace mozilla {
class AndroidMediaLayer;
}
#endif
// X.h defines KeyPress
#ifdef KeyPress
#undef KeyPress
@ -286,6 +292,26 @@ public:
void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect);
bool UseAsyncRendering();
#ifdef MOZ_WIDGET_ANDROID
nsIntRect GetVisibleRect() {
return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height);
}
void SetInverted(bool aInverted) {
mInverted = aInverted;
}
bool Inverted() {
return mInverted;
}
mozilla::AndroidMediaLayer* Layer() {
return mLayer;
}
void Invalidate();
#endif
private:
@ -298,11 +324,18 @@ private:
}
void FixUpURLS(const nsString &name, nsAString &value);
#ifdef ANDROID
#ifdef MOZ_WIDGET_ANDROID
void SendSize(int width, int height);
void SendOnScreenEvent(bool onScreen);
bool AddPluginView(const gfxRect& aRect);
void RemovePluginView();
bool mPluginViewAdded;
gfxRect mLastPluginRect;
bool mOnScreen;
bool mInverted;
// For kOpenGL_ANPDrawingModel
mozilla::AndroidMediaLayer *mLayer;
#endif
nsPluginNativeWindow *mPluginWindow;

View File

@ -647,9 +647,6 @@ PluginInstanceParent::GetImage(ImageContainer* aContainer, Image** aImage)
#ifdef XP_MACOSX
if (ioSurface) {
format = Image::MAC_IO_SURFACE;
if (!aContainer->Manager()) {
return NS_ERROR_FAILURE;
}
}
#endif

View File

@ -83,6 +83,7 @@ EXTRA_COMPONENTS = \
EXTRA_JS_MODULES = \
ril_consts.js \
ril_worker.js \
systemlibs.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -47,7 +47,7 @@ Cu.import("resource://gre/modules/Services.jsm");
var RIL = {};
Cu.import("resource://gre/modules/ril_consts.js", RIL);
const DEBUG = true; // set to false to suppress debug messages
const DEBUG = false; // set to true to see debug messages
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
@ -241,7 +241,9 @@ RadioInterfaceLayer.prototype = {
handleCallStateChange: function handleCallStateChange(call) {
debug("handleCallStateChange: " + JSON.stringify(call));
call.state = convertRILCallState(call.state);
if (call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) {
if (call.state == nsIRadioInterfaceLayer.CALL_STATE_DIALING ||
call.state == nsIRadioInterfaceLayer.CALL_STATE_RINGING ||
call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) {
// This is now the active call.
this._activeCall = call;
}

View File

@ -62,7 +62,7 @@
"use strict";
importScripts("ril_consts.js");
importScripts("ril_consts.js", "systemlibs.js");
let DEBUG = false;
@ -72,6 +72,8 @@ const UINT16_SIZE = 2;
const UINT32_SIZE = 4;
const PARCEL_SIZE_SIZE = UINT32_SIZE;
let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = false;
/**
* This object contains helpers buffering incoming data & deconstructing it
* into parcels as well as buffering outgoing data & constructing parcels.
@ -432,7 +434,6 @@ let Buf = {
/**
* Process one parcel.
*/
processParcel: function processParcel() {
let response_type = this.readUint32();
let length = this.readIncoming - UINT32_SIZE;
@ -445,20 +446,24 @@ let Buf = {
request_type = this.tokenRequestMap[token];
if (error) {
//TODO
debug("Received error " + error + " for solicited parcel type " +
request_type);
if (DEBUG) {
debug("Received error " + error + " for solicited parcel type " +
request_type);
}
return;
}
debug("Solicited response for request type " + request_type +
", token " + token);
if (DEBUG) {
debug("Solicited response for request type " + request_type +
", token " + token);
}
delete this.tokenRequestMap[token];
this.lastSolicitedToken = token;
} else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
request_type = this.readUint32();
length -= UINT32_SIZE;
debug("Unsolicited response for request type " + request_type);
if (DEBUG) debug("Unsolicited response for request type " + request_type);
} else {
debug("Unknown response type: " + response_type);
if (DEBUG) debug("Unknown response type: " + response_type);
return;
}
@ -483,9 +488,8 @@ let Buf = {
},
/**
* Communication with the RIL IPC thread.
* Communicate with the RIL IPC thread.
*/
sendParcel: function sendParcel() {
// Compute the size of the parcel and write it to the front of the parcel
// where we left room for it. Note that he parcel size does not include
@ -496,7 +500,7 @@ let Buf = {
// This assumes that postRILMessage will make a copy of the ArrayBufferView
// right away!
let parcel = this.outgoingBytes.subarray(0, this.outgoingIndex);
debug("Outgoing parcel: " + Array.slice(parcel));
if (DEBUG) debug("Outgoing parcel: " + Array.slice(parcel));
postRILMessage(parcel);
this.outgoingIndex = PARCEL_SIZE_SIZE;
},
@ -516,6 +520,25 @@ let Buf = {
*/
let RIL = {
/**
* Set quirk flags based on the RIL model detected. Note that this
* requires the RIL being "warmed up" first, which happens when on
* an incoming or outgoing call.
*/
rilQuirksInitialized: false,
initRILQuirks: function initRILQuirks() {
// The Samsung Galaxy S2 I-9100 radio sends an extra Uint32 in the
// call state.
let model_id = libcutils.property_get("ril.model_id");
if (DEBUG) debug("Detected RIL model " + model_id);
if (model_id == "I9100") {
if (DEBUG) debug("Enabling RILQUIRKS_CALLSTATE_EXTRA_UINT32 for I9100.");
RILQUIRKS_CALLSTATE_EXTRA_UINT32 = true;
}
this.rilQuirksInitialized = true;
},
/**
* Retrieve the ICC's status.
*
@ -865,7 +888,7 @@ let RIL = {
handleParcel: function handleParcel(request_type, length) {
let method = this[request_type];
if (typeof method == "function") {
debug("Handling parcel as " + method.name);
if (DEBUG) debug("Handling parcel as " + method.name);
method.call(this, length);
}
}
@ -915,6 +938,10 @@ RIL[REQUEST_CHANGE_SIM_PIN] = function REQUEST_CHANGE_SIM_PIN() {
RIL[REQUEST_CHANGE_SIM_PIN2] = null;
RIL[REQUEST_ENTER_NETWORK_DEPERSONALIZATION] = null;
RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
if (!this.rilQuirksInitialized) {
this.initRILQuirks();
}
let calls_length = 0;
// The RIL won't even send us the length integer if there are no active calls.
// So only read this integer if the parcel actually has it.
@ -928,22 +955,24 @@ RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
let calls = {};
for (let i = 0; i < calls_length; i++) {
let call = {
state: Buf.readUint32(), // CALL_STATE_*
callIndex: Buf.readUint32(), // GSM index (1-based)
toa: Buf.readUint32(),
isMpty: Boolean(Buf.readUint32()),
isMT: Boolean(Buf.readUint32()),
als: Buf.readUint32(),
isVoice: Boolean(Buf.readUint32()),
isVoicePrivacy: Boolean(Buf.readUint32()),
somethingOrOther: Buf.readUint32(), //XXX TODO whatziz? not in ril.h, but it's in the output...
number: Buf.readString(), //TODO munge with TOA
numberPresentation: Buf.readUint32(), // CALL_PRESENTATION_*
name: Buf.readString(),
namePresentation: Buf.readUint32(),
uusInfo: null
};
let call = {};
call.state = Buf.readUint32(); // CALL_STATE_*
call.callIndex = Buf.readUint32(); // GSM index (1-based)
call.toa = Buf.readUint32();
call.isMpty = Boolean(Buf.readUint32());
call.isMT = Boolean(Buf.readUint32());
call.als = Buf.readUint32();
call.isVoice = Boolean(Buf.readUint32());
call.isVoicePrivacy = Boolean(Buf.readUint32());
if (RILQUIRKS_CALLSTATE_EXTRA_UINT32) {
Buf.readUint32();
}
call.number = Buf.readString(); //TODO munge with TOA
call.numberPresentation = Buf.readUint32(); // CALL_PRESENTATION_*
call.name = Buf.readString();
call.namePresentation = Buf.readUint32();
call.uusInfo = null;
let uusInfoPresent = Buf.readUint32();
if (uusInfoPresent == 1) {
call.uusInfo = {
@ -952,6 +981,7 @@ RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
userData: null //XXX TODO byte array?!?
};
}
calls[call.callIndex] = call;
}
Phone.onCurrentCalls(calls);
@ -1218,9 +1248,6 @@ RIL[UNSOLICITED_RESEND_INCALL_MUTE] = null;
*/
let Phone = {
//XXX TODO beware, this is just demo code. It's still missing
// communication with the UI thread.
/**
* One of the RADIO_STATE_* constants.
*/
@ -1313,7 +1340,9 @@ let Phone = {
*/
onRadioStateChanged: function onRadioStateChanged(newState) {
debug("Radio state changed from " + this.radioState + " to " + newState);
if (DEBUG) {
debug("Radio state changed from " + this.radioState + " to " + newState);
}
if (this.radioState == newState) {
// No change in state, return.
return;
@ -1455,13 +1484,13 @@ let Phone = {
},
onNetworkStateChanged: function onNetworkStateChanged() {
debug("Network state changed, re-requesting phone state.");
if (DEBUG) debug("Network state changed, re-requesting phone state.");
this.requestNetworkInfo();
},
onICCStatus: function onICCStatus(iccStatus) {
if (DEBUG) {
debug("iccStatus: " + JSON.stringify(iccStatus));
debug("iccStatus: " + JSON.stringify(iccStatus));
}
this.iccStatus = iccStatus;

View File

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const SYSTEM_PROPERTY_KEY_MAX = 32;
const SYSTEM_PROPERTY_VALUE_MAX = 92;
/**
* Expose some system-level functions.
*/
let libcutils = (function() {
let lib;
try {
lib = ctypes.open("libcutils.so");
} catch(ex) {
// Return a fallback option in case libcutils.so isn't present (e.g.
// when building Firefox with MOZ_B2G_RIL.
dump("Could not load libcutils.so. Using fake propdb.");
let fake_propdb = Object.create(null);
return {
property_get: function fake_property_get(key, defaultValue) {
if (key in fake_propdb) {
return fake_propdb[key];
}
return defaultValue === undefined ? null : defaultValue;
},
property_set: function fake_property_set(key, value) {
fake_propdb[key] = value;
}
};
}
let c_property_get = lib.declare("property_get", ctypes.default_abi,
ctypes.int, // return value: length
ctypes.char.ptr, // key
ctypes.char.ptr, // value
ctypes.char.ptr); // default
let c_property_set = lib.declare("property_set", ctypes.default_abi,
ctypes.int, // return value: success
ctypes.char.ptr, // key
ctypes.char.ptr); // value
let c_value_buf = ctypes.char.array(SYSTEM_PROPERTY_VALUE_MAX)();
return {
/**
* Get a system property.
*
* @param key
* Name of the property
* @param defaultValue [optional]
* Default value to return if the property isn't set (default: null)
*/
property_get: function property_get(key, defaultValue) {
if (defaultValue === undefined) {
defaultValue = null;
}
c_property_get(key, c_value_buf, defaultValue);
return c_value_buf.readString();
},
/**
* Set a system property
*
* @param key
* Name of the property
* @param value
* Value to set the property to.
*/
property_set: function property_set(key, value) {
let rv = c_property_set(key, value);
if (rv) {
throw Error('libcutils.property_set("' + key + '", "' + value +
'") failed with error ' + rv);
}
}
};
})();

View File

@ -293,6 +293,8 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
JS_SetGCParameter(runtime, JSGC_MAX_BYTES,
aWorkerPrivate->GetJSRuntimeHeapSize());
JS_SetNativeStackQuota(runtime, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
JSContext* workerCx = JS_NewContext(runtime, 0);
if (!workerCx) {
JS_DestroyRuntime(runtime);
@ -306,8 +308,6 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
JS_SetOperationCallback(workerCx, OperationCallback);
JS_SetNativeStackQuota(workerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
NS_ASSERTION((aWorkerPrivate->GetJSContextOptions() &
kRequiredJSContextOptions) == kRequiredJSContextOptions,
"Somehow we lost our required options!");

View File

@ -155,6 +155,8 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
dest->swap(rawSupports);
}
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
class WorkerMemoryReporter : public nsIMemoryMultiReporter
{
WorkerPrivate* mWorkerPrivate;
@ -232,7 +234,7 @@ public:
{
AssertIsOnMainThread();
JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
JS::RuntimeStats rtStats(JsWorkerMallocSizeOf, xpc::GetCompartmentName,
xpc::DestroyCompartmentName);
nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
if (NS_FAILED(rv)) {
@ -1523,7 +1525,7 @@ public:
JSAutoSuspendRequest asr(aCx);
*mSucceeded = mIsQuick
? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf)
? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), JsWorkerMallocSizeOf)
: JS::CollectRuntimeStats(JS_GetRuntime(aCx), static_cast<JS::RuntimeStats*>(mData));
{

View File

@ -9,9 +9,17 @@
android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
#endif
>
<uses-sdk android:minSdkVersion="5"
android:targetSdkVersion="11"/>
#ifdef MOZ_TABLETS_ONLY
<supports-screens android:smallScreens="false"
android:normalScreens="false"
android:largeScreens="false"
android:xlargeScreens="true" />
#endif
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1792,5 +1792,5 @@ public class GeckoAppShell
}
// This is only used in Native Fennec.
public static void preventPanning() { }
public static void setPreventPanning(final boolean aPreventPanning) { }
}

View File

@ -79,7 +79,8 @@ MIN_CPU_VERSION=5
endif
ifeq (,$(ANDROID_VERSION_CODE))
ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
# increment the version code by 1 so xul fennec will win any compatability ties
ANDROID_VERSION_CODE=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10` + 1 | bc)
endif
DEFINES += \
@ -94,6 +95,11 @@ DEFINES += \
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
$(NULL)
MOZ_MOBILE_COMPAT = @MOZ_MOBILE_COMPAT@
ifeq (Tablets,$(MOZ_MOBILE_COMPAT))
DEFINES += -DMOZ_TABLETS_ONLY=1
endif
GARBAGE += \
AndroidManifest.xml \
classes.dex \

View File

@ -1108,8 +1108,6 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c
return true;
}
const bool firstTime = (mOffscreenDrawFBO == 0 && mOffscreenReadFBO == 0);
GLuint curBoundFramebufferDraw = 0;
GLuint curBoundFramebufferRead = 0;
GLuint curBoundRenderbuffer = 0;
@ -1412,8 +1410,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c
#ifdef DEBUG
if (DebugMode()) {
printf_stderr("%s %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n",
firstTime ? "Created" : "Resized",
printf_stderr("Resized %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n",
mOffscreenActualSize.width, mOffscreenActualSize.height,
mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha,
mActualFormat.depth, mActualFormat.stencil, mActualFormat.samples);
@ -1429,13 +1426,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c
BindReadFBO(curBoundFramebufferRead);
fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
// -don't- restore the viewport the first time through this, since
// the previous one isn't valid.
if (firstTime)
fViewport(0, 0, aSize.width, aSize.height); // XXX This is coming out in 711642
else
fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
return true;
}

275
gfx/layers/ImageLayers.cpp Normal file
View File

@ -0,0 +1,275 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bas Schouten <bschouten@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/ipc/Shmem.h"
#include "ImageLayers.h"
#include "gfxImageSurface.h"
#include "yuv_convert.h"
#ifdef XP_MACOSX
#include "nsCoreAnimationSupport.h"
#endif
using namespace mozilla::ipc;
namespace mozilla {
namespace layers {
already_AddRefed<Image>
ImageFactory::CreateImage(const Image::Format *aFormats,
PRUint32 aNumFormats,
const gfxIntSize &,
BufferRecycleBin *aRecycleBin)
{
if (!aNumFormats) {
return nsnull;
}
nsRefPtr<Image> img;
if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
img = new PlanarYCbCrImage(aRecycleBin);
} else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
img = new CairoImage();
#ifdef XP_MACOSX
} else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) {
img = new MacIOSurfaceImage();
#endif
}
return img.forget();
}
BufferRecycleBin::BufferRecycleBin()
: mLock("mozilla.layers.BufferRecycleBin.mLock")
{
}
void
BufferRecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize)
{
MutexAutoLock lock(mLock);
if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
mRecycledBuffers.Clear();
}
mRecycledBufferSize = aSize;
mRecycledBuffers.AppendElement(aBuffer);
}
PRUint8*
BufferRecycleBin::GetBuffer(PRUint32 aSize)
{
MutexAutoLock lock(mLock);
if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
return new PRUint8[aSize];
PRUint32 last = mRecycledBuffers.Length() - 1;
PRUint8* result = mRecycledBuffers[last].forget();
mRecycledBuffers.RemoveElementAt(last);
return result;
}
ImageContainer::~ImageContainer()
{
}
already_AddRefed<Image>
ImageContainer::CreateImage(const Image::Format *aFormats,
PRUint32 aNumFormats)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin);
}
void
ImageContainer::SetCurrentImage(Image *aImage)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mActiveImage = aImage;
CurrentImageChanged();
}
already_AddRefed<gfxASurface>
ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return nsnull;
}
*aSize = mActiveImage->GetSize();
return mActiveImage->GetAsSurface();
}
gfxIntSize
ImageContainer::GetCurrentSize()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return gfxIntSize(0,0);
}
return mActiveImage->GetSize();
}
PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
: Image(nsnull, PLANAR_YCBCR)
, mBufferSize(0)
, mRecycleBin(aRecycleBin)
{
}
PlanarYCbCrImage::~PlanarYCbCrImage()
{
if (mBuffer) {
mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
}
}
PRUint8*
PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize)
{
return mRecycleBin->GetBuffer(aSize);
}
void
PlanarYCbCrImage::CopyData(const Data& aData)
{
mData = aData;
mData.mYStride = mData.mYSize.width;
mData.mCbCrStride = mData.mCbCrSize.width;
// update buffer size
mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
mData.mYStride * mData.mYSize.height;
// get new buffer
mBuffer = AllocateBuffer(mBufferSize);
if (!mBuffer)
return;
mData.mYChannel = mBuffer;
mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
for (int i = 0; i < mData.mYSize.height; i++) {
memcpy(mData.mYChannel + i * mData.mYStride,
aData.mYChannel + i * aData.mYStride,
mData.mYStride);
}
for (int i = 0; i < mData.mCbCrSize.height; i++) {
memcpy(mData.mCbChannel + i * mData.mCbCrStride,
aData.mCbChannel + i * aData.mCbCrStride,
mData.mCbCrStride);
memcpy(mData.mCrChannel + i * mData.mCbCrStride,
aData.mCrChannel + i * aData.mCbCrStride,
mData.mCbCrStride);
}
mSize = aData.mPicSize;
}
void
PlanarYCbCrImage::SetData(const Data &aData)
{
CopyData(aData);
}
already_AddRefed<gfxASurface>
PlanarYCbCrImage::GetAsSurface()
{
if (mSurface) {
nsRefPtr<gfxASurface> result = mSurface.get();
return result.forget();
}
nsRefPtr<gfxImageSurface> imageSurface =
new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24);
gfx::YUVType type =
gfx::TypeFromSize(mData.mYSize.width,
mData.mYSize.height,
mData.mCbCrSize.width,
mData.mCbCrSize.height);
// Convert from YCbCr to RGB now
gfx::ConvertYCbCrToRGB32(mData.mYChannel,
mData.mCbChannel,
mData.mCrChannel,
imageSurface->Data(),
mData.mPicX,
mData.mPicY,
mData.mPicSize.width,
mData.mPicSize.height,
mData.mYStride,
mData.mCbCrStride,
imageSurface->Stride(),
type);
mSurface = imageSurface;
return imageSurface.forget().get();
}
#ifdef XP_MACOSX
void
MacIOSurfaceImage::SetData(const Data& aData)
{
mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID());
mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight());
}
already_AddRefed<gfxASurface>
MacIOSurfaceImage::GetAsSurface()
{
return mIOSurface->GetAsSurface();
}
void
MacIOSurfaceImage::Update(ImageContainer* aContainer)
{
if (mUpdateCallback) {
mUpdateCallback(aContainer, mPluginInstanceOwner);
}
}
#endif
}
}

View File

@ -46,8 +46,12 @@
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Mutex.h"
#include "gfxPlatform.h"
class nsIOSurface;
#ifdef XP_MACOSX
#include "nsIOSurface.h"
#endif
namespace mozilla {
namespace layers {
@ -60,6 +64,14 @@ enum StereoMode {
STEREO_MODE_TOP_BOTTOM
};
struct ImageBackendData
{
virtual ~ImageBackendData() {}
protected:
ImageBackendData() {}
};
/**
* A class representing a buffer of pixel data. The data can be in one
* of various formats including YCbCr.
@ -104,8 +116,7 @@ public:
CAIRO_SURFACE,
/**
* The MAC_IO_SURFACE format creates a MacIOSurfaceImage. This
* is only supported on Mac with OpenGL layers.
* The MAC_IO_SURFACE format creates a MacIOSurfaceImage.
*
* It wraps an IOSurface object and binds it directly to a GL texture.
*/
@ -115,16 +126,106 @@ public:
Format GetFormat() { return mFormat; }
void* GetImplData() { return mImplData; }
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
virtual gfxIntSize GetSize() = 0;
ImageBackendData* GetBackendData(LayerManager::LayersBackend aBackend)
{ return mBackendData[aBackend]; }
void SetBackendData(LayerManager::LayersBackend aBackend, ImageBackendData* aData)
{ mBackendData[aBackend] = aData; }
protected:
Image(void* aImplData, Format aFormat) :
mImplData(aImplData),
mFormat(aFormat)
{}
nsAutoPtr<ImageBackendData> mBackendData[LayerManager::LAYERS_LAST];
void* mImplData;
Format mFormat;
};
/**
* A RecycleBin is owned by an ImageContainer. We store buffers in it that we
* want to recycle from one image to the next.It's a separate object from
* ImageContainer because images need to store a strong ref to their RecycleBin
* and we must avoid creating a reference loop between an ImageContainer and
* its active image.
*/
class BufferRecycleBin {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin)
typedef mozilla::gl::GLContext GLContext;
public:
BufferRecycleBin();
void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize);
// Returns a recycled buffer of the right size, or allocates a new buffer.
PRUint8* GetBuffer(PRUint32 aSize);
private:
typedef mozilla::Mutex Mutex;
// This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
// and mRecycledTextureSizes
Mutex mLock;
// We should probably do something to prune this list on a timer so we don't
// eat excess memory while video is paused...
nsTArray<nsAutoArrayPtr<PRUint8> > mRecycledBuffers;
// This is only valid if mRecycledBuffers is non-empty
PRUint32 mRecycledBufferSize;
};
/**
* Returns true if aFormat is in the given format array.
*/
static inline bool
FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
Image::Format aFormat)
{
for (PRUint32 i = 0; i < aNumFormats; ++i) {
if (aFormats[i] == aFormat) {
return true;
}
}
return false;
}
/**
* A class that manages Image creation for a LayerManager. The only reason
* we need a separate class here is that LayerMananers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to create images from any thread, to facilitate video playback
* without involving the main thread, for example.
* Different layer managers can implement child classes of this making it
* possible to create layer manager specific images.
* This class is not meant to be used directly but rather can be set on an
* image container. This is usually done by the layer system internally and
* not explicitly by users. For PlanarYCbCr or Cairo images the default
* implementation will creates images whose data lives in system memory, for
* MacIOSurfaces the default implementation will be a simple nsIOSurface
* wrapper.
*/
class THEBES_API ImageFactory
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
protected:
friend class ImageContainer;
ImageFactory() {}
virtual ~ImageFactory() {}
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats,
const gfxIntSize &aScaleHint,
BufferRecycleBin *aRecycleBin);
};
/**
* A class that manages Images for an ImageLayer. The only reason
* we need a separate class here is that ImageLayers aren't threadsafe
@ -139,10 +240,12 @@ public:
ImageContainer() :
mReentrantMonitor("ImageContainer.mReentrantMonitor"),
mPaintCount(0),
mPreviousImagePainted(false)
mPreviousImagePainted(false),
mImageFactory(new ImageFactory()),
mRecycleBin(new BufferRecycleBin())
{}
virtual ~ImageContainer() {}
~ImageContainer();
/**
* Create an Image in one of the given formats.
@ -152,8 +255,8 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats) = 0;
already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats);
/**
* Set an Image as the current image to display. The Image must have
@ -163,13 +266,7 @@ public:
*
* The Image data must not be modified after this method is called!
*/
virtual void SetCurrentImage(Image* aImage) = 0;
/**
* Ask any PlanarYCbCr images created by this container to delay
* YUV -> RGB conversion until draw time. See PlanarYCbCrImage::SetDelayedConversion.
*/
virtual void SetDelayedConversion(bool aDelayed) {}
void SetCurrentImage(Image* aImage);
/**
* Get the current Image.
@ -181,7 +278,13 @@ public:
* Implementations must call CurrentImageChanged() while holding
* mReentrantMonitor.
*/
virtual already_AddRefed<Image> GetCurrentImage() = 0;
already_AddRefed<Image> GetCurrentImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<Image> retval = mActiveImage;
return retval.forget();
}
/**
* Get the current image as a gfxASurface. This is useful for fallback
@ -198,32 +301,14 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult) = 0;
/**
* Returns the layer manager for this container. This can only
* be used on the main thread, since layer managers should only be
* accessed on the main thread.
*/
LayerManager* Manager()
{
NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread");
return mManager;
}
already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult);
/**
* Returns the size of the image in pixels.
* Can be called on any thread. This method takes mReentrantMonitor when accessing
* thread-shared state.
*/
virtual gfxIntSize GetCurrentSize() = 0;
/**
* Set a new layer manager for this image container. It must be
* either of the same type as the container's current layer manager,
* or null. TRUE is returned on success. Main thread only.
*/
virtual bool SetLayerManager(LayerManager *aManager) = 0;
gfxIntSize GetCurrentSize();
/**
* Sets a size that the image is expected to be rendered at.
@ -232,14 +317,14 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { }
void SetScaleHint(const gfxIntSize& aScaleHint)
{ mScaleHint = aScaleHint; }
/**
* Get the layer manager type this image container was created with,
* presumably its users might want to do something special if types do not
* match. Can be called on any thread.
*/
virtual LayerManager::LayersBackend GetBackendType() = 0;
void SetImageFactory(ImageFactory *aFactory)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mImageFactory = aFactory ? aFactory : new ImageFactory();
}
/**
* Returns the time at which the currently contained image was first
@ -285,19 +370,11 @@ public:
protected:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
LayerManager* mManager;
// ReentrantMonitor to protect thread safe access to the "current
// image", and any other state which is shared between threads.
ReentrantMonitor mReentrantMonitor;
ImageContainer(LayerManager* aManager) :
mManager(aManager),
mReentrantMonitor("ImageContainer.mReentrantMonitor"),
mPaintCount(0),
mPreviousImagePainted(false)
{}
// Performs necessary housekeeping to ensure the painted frame statistics
// are accurate. Must be called by SetCurrentImage() implementations with
// mReentrantMonitor held.
@ -307,6 +384,8 @@ protected:
mPaintTime = TimeStamp();
}
nsRefPtr<Image> mActiveImage;
// Number of contained images that have been painted at least once. It's up
// to the ImageContainer implementation to ensure accesses to this are
// threadsafe.
@ -318,6 +397,15 @@ protected:
// Denotes whether the previous image was painted.
bool mPreviousImagePainted;
// This is the image factory used by this container, layer managers using
// this container can set an alternative image factory that will be used to
// create images for this container.
nsRefPtr<ImageFactory> mImageFactory;
gfxIntSize mScaleHint;
nsRefPtr<BufferRecycleBin> mRecycleBin;
};
/**
@ -332,8 +420,6 @@ public:
*/
void SetContainer(ImageContainer* aContainer)
{
NS_ASSERTION(!aContainer->Manager() || aContainer->Manager() == Manager(),
"ImageContainer must have the same manager as the ImageLayer");
mContainer = aContainer;
}
/**
@ -422,13 +508,13 @@ public:
MAX_DIMENSION = 16384
};
~PlanarYCbCrImage();
/**
* This makes a copy of the data buffers.
* XXX Eventually we will change this to not make a copy of the data,
* Right now it doesn't matter because the BasicLayer implementation
* does YCbCr conversion here anyway.
* This makes a copy of the data buffers, in order to support functioning
* in all different layer managers.
*/
virtual void SetData(const Data& aData) = 0;
virtual void SetData(const Data& aData);
/**
* Ask this Image to not convert YUV to RGB during SetData, and make
@ -440,19 +526,14 @@ public:
/**
* Grab the original YUV data. This is optional.
*/
virtual const Data* GetData() { return nsnull; }
virtual const Data* GetData() { return &mData; }
/**
* Make a copy of the YCbCr data.
* Make a copy of the YCbCr data into local storage.
*
* @param aDest Data object to store the plane data in.
* @param aDestSize Size of the Y plane that was copied.
* @param aDestBufferSize Number of bytes allocated for storage.
* @param aData Input image data.
* @return Raw data pointer for the planes or nsnull on failure.
*/
PRUint8 *CopyData(Data& aDest, gfxIntSize& aDestSize,
PRUint32& aDestBufferSize, const Data& aData);
void CopyData(const Data& aData);
/**
* Return a buffer to store image data in.
@ -464,15 +545,31 @@ public:
/**
* Return the number of bytes of heap memory used to store this image.
*/
virtual PRUint32 GetDataSize() = 0;
virtual PRUint32 GetDataSize() { return mBufferSize; }
protected:
PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {}
already_AddRefed<gfxASurface> GetAsSurface();
virtual gfxIntSize GetSize() { return mSize; }
void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; }
// XXX - not easy to protect these sadly.
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferSize;
Data mData;
gfxIntSize mSize;
gfxImageFormat mOffscreenFormat;
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
nsRefPtr<BufferRecycleBin> mRecycleBin;
PlanarYCbCrImage(BufferRecycleBin *aRecycleBin);
};
/**
* Currently, the data in a CairoImage surface is treated as being in the
* device output color space.
* device output color space. This class is very simple as all backends
* have to know about how to deal with drawing a cairo image.
*/
class THEBES_API CairoImage : public Image {
public:
@ -486,10 +583,26 @@ public:
* to the surface (which will eventually be released on the main thread).
* The surface must not be modified after this call!!!
*/
virtual void SetData(const Data& aData) = 0;
void SetData(const Data& aData)
{
mSurface = aData.mSurface;
mSize = aData.mSize;
}
protected:
CairoImage(void* aImplData) : Image(aImplData, CAIRO_SURFACE) {}
virtual already_AddRefed<gfxASurface> GetAsSurface()
{
NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
nsRefPtr<gfxASurface> surface = mSurface.get();
return surface.forget();
}
gfxIntSize GetSize() { return mSize; }
CairoImage() : Image(NULL, CAIRO_SURFACE) {}
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
gfxIntSize mSize;
};
#ifdef XP_MACOSX
@ -499,12 +612,27 @@ public:
nsIOSurface* mIOSurface;
};
MacIOSurfaceImage()
: Image(NULL, MAC_IO_SURFACE)
, mSize(0, 0)
, mPluginInstanceOwner(NULL)
, mUpdateCallback(NULL)
, mDestroyCallback(NULL)
{}
virtual ~MacIOSurfaceImage()
{
if (mDestroyCallback) {
mDestroyCallback(mPluginInstanceOwner);
}
}
/**
* This can only be called on the main thread. It may add a reference
* to the surface (which will eventually be released on the main thread).
* The surface must not be modified after this call!!!
*/
virtual void SetData(const Data& aData) = 0;
virtual void SetData(const Data& aData);
/**
* Temporary hacks to force plugin drawing during an empty transaction.
@ -512,12 +640,38 @@ public:
* when async plugin rendering is complete.
*/
typedef void (*UpdateSurfaceCallback)(ImageContainer* aContainer, void* aInstanceOwner);
virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner) = 0;
typedef void (*DestroyCallback)(void* aInstanceOwner);
virtual void SetDestroyCallback(DestroyCallback aCallback) = 0;
virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner)
{
mUpdateCallback = aCallback;
mPluginInstanceOwner = aInstanceOwner;
}
protected:
MacIOSurfaceImage(void* aImplData) : Image(aImplData, MAC_IO_SURFACE) {}
typedef void (*DestroyCallback)(void* aInstanceOwner);
virtual void SetDestroyCallback(DestroyCallback aCallback)
{
mDestroyCallback = aCallback;
}
virtual gfxIntSize GetSize()
{
return mSize;
}
nsIOSurface* GetIOSurface()
{
return mIOSurface;
}
void Update(ImageContainer* aContainer);
virtual already_AddRefed<gfxASurface> GetAsSurface();
private:
gfxIntSize mSize;
nsRefPtr<nsIOSurface> mIOSurface;
void* mPluginInstanceOwner;
UpdateSurfaceCallback mUpdateCallback;
DestroyCallback mDestroyCallback;
};
#endif

View File

@ -229,6 +229,13 @@ LayerManager::Mutated(Layer* aLayer)
}
#endif // DEBUG
already_AddRefed<ImageContainer>
LayerManager::CreateImageContainer()
{
nsRefPtr<ImageContainer> container = new ImageContainer();
return container.forget();
}
//--------------------------------------------------
// Layer
@ -513,54 +520,7 @@ ContainerLayer::DidInsertChild(Layer* aLayer)
mMayHaveReadbackChild = true;
}
}
PRUint8*
PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize)
{
const fallible_t fallible = fallible_t();
return new (fallible) PRUint8[aSize];
}
PRUint8*
PlanarYCbCrImage::CopyData(Data& aDest, gfxIntSize& aDestSize,
PRUint32& aDestBufferSize, const Data& aData)
{
aDest = aData;
aDest.mYStride = aDest.mYSize.width;
aDest.mCbCrStride = aDest.mCbCrSize.width;
// update buffer size
aDestBufferSize = aDest.mCbCrStride * aDest.mCbCrSize.height * 2 +
aDest.mYStride * aDest.mYSize.height;
// get new buffer
PRUint8* buffer = AllocateBuffer(aDestBufferSize);
if (!buffer)
return nsnull;
aDest.mYChannel = buffer;
aDest.mCbChannel = aDest.mYChannel + aDest.mYStride * aDest.mYSize.height;
aDest.mCrChannel = aDest.mCbChannel + aDest.mCbCrStride * aDest.mCbCrSize.height;
for (int i = 0; i < aDest.mYSize.height; i++) {
memcpy(aDest.mYChannel + i * aDest.mYStride,
aData.mYChannel + i * aData.mYStride,
aDest.mYStride);
}
for (int i = 0; i < aDest.mCbCrSize.height; i++) {
memcpy(aDest.mCbChannel + i * aDest.mCbCrStride,
aData.mCbChannel + i * aData.mCbCrStride,
aDest.mCbCrStride);
memcpy(aDest.mCrChannel + i * aDest.mCbCrStride,
aData.mCrChannel + i * aData.mCbCrStride,
aDest.mCbCrStride);
}
aDestSize = aData.mPicSize;
return buffer;
}
void
LayerManager::StartFrameTimeRecording()
{

View File

@ -49,6 +49,7 @@
#include "gfxColor.h"
#include "gfxPattern.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/TimeStamp.h"
@ -428,9 +429,9 @@ public:
virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nsnull; }
/**
* Can be called anytime
* Can be called anytime, from any thread.
*/
virtual already_AddRefed<ImageContainer> CreateImageContainer() = 0;
static already_AddRefed<ImageContainer> CreateImageContainer();
/**
* Type of layer manager his is. This is to be used sparsely in order to

View File

@ -82,14 +82,9 @@ CPPSRCS = \
LayerManagerOGL.cpp \
ThebesLayerOGL.cpp \
LayerSorter.cpp \
ImageLayers.cpp \
$(NULL)
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
CMMSRCS = \
MacIOSurfaceImageOGL.mm \
$(NULL)
endif
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
ifdef MOZ_ENABLE_D3D9_LAYER
EXPORTS += \

View File

@ -56,106 +56,70 @@ using mozilla::ReentrantMonitor;
namespace mozilla {
namespace layers {
/**
* All our images can yield up a cairo surface and their size.
*/
class BasicImageImplData {
class BasicPlanarYCbCrImage : public PlanarYCbCrImage
{
public:
/**
* This must be called on the main thread.
*/
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
: PlanarYCbCrImage(aRecycleBin)
, mScaleHint(aScaleHint)
, mOffscreenFormat(aOffscreenFormat)
{}
gfxIntSize GetSize() { return mSize; }
protected:
gfxIntSize mSize;
};
/**
* Since BasicLayers only paint on the main thread, handling a CairoImage
* is extremely simple. We just hang on to a reference to the surface and
* return that surface when BasicImageLayer::Paint asks for it via
* BasicImageContainer::GetAsSurface.
*/
class BasicCairoImage : public CairoImage, public BasicImageImplData {
public:
BasicCairoImage() : CairoImage(static_cast<BasicImageImplData*>(this)) {}
virtual void SetData(const Data& aData)
~BasicPlanarYCbCrImage()
{
mSurface = aData.mSurface;
mSize = aData.mSize;
if (mDecodedBuffer) {
// Right now this only happens if the Image was never drawn, otherwise
// this will have been tossed away at surface destruction.
mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride);
}
}
virtual already_AddRefed<gfxASurface> GetAsSurface()
{
NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
nsRefPtr<gfxASurface> surface = mSurface.get();
return surface.forget();
}
protected:
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
};
/**
* We handle YCbCr by converting to RGB when the image is initialized
* (which should be done off the main thread). The RGB results are stored
* in a memory buffer and converted to a cairo surface lazily.
*/
class BasicPlanarYCbCrImage : public PlanarYCbCrImage, public BasicImageImplData {
typedef gfxASurface::gfxImageFormat gfxImageFormat;
public:
/**
* aScaleHint is a size that the image is expected to be rendered at.
* This is a hint for image backends to optimize scaling.
*/
BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint) :
PlanarYCbCrImage(static_cast<BasicImageImplData*>(this)),
mScaleHint(aScaleHint),
mOffscreenFormat(gfxASurface::ImageFormatUnknown),
mDelayedConversion(false)
{}
virtual void SetData(const Data& aData);
virtual void SetDelayedConversion(bool aDelayed) { mDelayedConversion = aDelayed; }
already_AddRefed<gfxASurface> GetAsSurface();
virtual already_AddRefed<gfxASurface> GetAsSurface();
private:
gfxIntSize mScaleHint;
gfxImageFormat mOffscreenFormat;
int mStride;
nsAutoArrayPtr<PRUint8> mDecodedBuffer;
};
const Data* GetData() { return &mData; }
class BasicImageFactory : public ImageFactory
{
public:
BasicImageFactory() {}
void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; }
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats,
const gfxIntSize &aScaleHint,
BufferRecycleBin *aRecycleBin)
{
if (!aNumFormats) {
return nsnull;
}
PRUint32 GetDataSize() { return mBuffer ? mDelayedConversion ? mBufferSize : mSize.height * mStride : 0; }
nsRefPtr<Image> image;
if (aFormats[0] == Image::PLANAR_YCBCR) {
image = new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
return image.forget();
}
protected:
nsAutoArrayPtr<PRUint8> mBuffer;
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
gfxIntSize mScaleHint;
PRInt32 mStride;
gfxImageFormat mOffscreenFormat;
Data mData;
PRUint32 mBufferSize;
bool mDelayedConversion;
return ImageFactory::CreateImage(aFormats, aNumFormats, aScaleHint, aRecycleBin);
}
};
void
BasicPlanarYCbCrImage::SetData(const Data& aData)
{
PlanarYCbCrImage::SetData(aData);
// Do some sanity checks to prevent integer overflow
if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
NS_ERROR("Illegal image source width or height");
return;
}
if (mDelayedConversion) {
mBuffer = CopyData(mData, mSize, mBufferSize, aData);
return;
}
gfxASurface::gfxImageFormat format = GetOffscreenFormat();
gfxIntSize size(mScaleHint);
@ -167,13 +131,13 @@ BasicPlanarYCbCrImage::SetData(const Data& aData)
}
mStride = gfxASurface::FormatStrideForWidth(format, size.width);
mBuffer = AllocateBuffer(size.height * mStride);
if (!mBuffer) {
mDecodedBuffer = AllocateBuffer(size.height * mStride);
if (!mDecodedBuffer) {
// out of memory
return;
}
gfxUtils::ConvertYCbCrToRGB(aData, format, size, mBuffer, mStride);
gfxUtils::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer, mStride);
SetOffscreenFormat(format);
mSize = size;
}
@ -196,22 +160,20 @@ BasicPlanarYCbCrImage::GetAsSurface()
return result.forget();
}
// XXX: If we forced delayed conversion, are we ever going to hit this?
// We may need to implement the conversion here.
if (!mBuffer || mDelayedConversion) {
return nsnull;
if (!mDecodedBuffer) {
return PlanarYCbCrImage::GetAsSurface();
}
gfxASurface::gfxImageFormat format = GetOffscreenFormat();
nsRefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface(mBuffer, mSize, mStride, format);
new gfxImageSurface(mDecodedBuffer, mSize, mStride, format);
if (!imgSurface || imgSurface->CairoStatus() != 0) {
return nsnull;
}
// Pass ownership of the buffer to the surface
imgSurface->SetData(&imageSurfaceDataKey, mBuffer.forget(), DestroyBuffer);
imgSurface->SetData(&imageSurfaceDataKey, mDecodedBuffer.forget(), DestroyBuffer);
nsRefPtr<gfxASurface> result = imgSurface.get();
#if defined(XP_MACOSX)
@ -221,144 +183,20 @@ BasicPlanarYCbCrImage::GetAsSurface()
result = quartzSurface.forget();
}
#endif
mSurface = result.get();
mSurface = result;
return result.forget();
}
/**
* Our image container is very simple. It's really just a factory
* for the image objects. We use a ReentrantMonitor to synchronize access to
* mImage.
*/
class BasicImageContainer : public ImageContainer {
public:
typedef gfxASurface::gfxImageFormat gfxImageFormat;
BasicImageContainer() :
ImageContainer(nsnull),
mScaleHint(-1, -1),
mOffscreenFormat(gfxASurface::ImageFormatUnknown),
mDelayed(false)
{}
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats);
virtual void SetDelayedConversion(bool aDelayed) { mDelayed = aDelayed; }
virtual void SetCurrentImage(Image* aImage);
virtual already_AddRefed<Image> GetCurrentImage();
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSize);
virtual gfxIntSize GetCurrentSize();
virtual bool SetLayerManager(LayerManager *aManager);
virtual void SetScaleHint(const gfxIntSize& aScaleHint);
void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_BASIC; }
protected:
nsRefPtr<Image> mImage;
gfxIntSize mScaleHint;
gfxImageFormat mOffscreenFormat;
bool mDelayed;
};
/**
* Returns true if aFormat is in the given format array.
*/
static bool
FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
Image::Format aFormat)
ImageFactory*
BasicLayerManager::GetImageFactory()
{
for (PRUint32 i = 0; i < aNumFormats; ++i) {
if (aFormats[i] == aFormat) {
return true;
}
}
return false;
}
already_AddRefed<Image>
BasicImageContainer::CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats)
{
nsRefPtr<Image> image;
// Prefer cairo surfaces because they're native for us
if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
image = new BasicCairoImage();
} else if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
image = new BasicPlanarYCbCrImage(mScaleHint);
static_cast<BasicPlanarYCbCrImage*>(image.get())->SetOffscreenFormat(mOffscreenFormat);
static_cast<BasicPlanarYCbCrImage*>(image.get())->SetDelayedConversion(mDelayed);
}
return image.forget();
}
void
BasicImageContainer::SetCurrentImage(Image* aImage)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mImage = aImage;
CurrentImageChanged();
}
already_AddRefed<Image>
BasicImageContainer::GetCurrentImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<Image> image = mImage;
return image.forget();
}
static BasicImageImplData*
ToImageData(Image* aImage)
{
return static_cast<BasicImageImplData*>(aImage->GetImplData());
}
already_AddRefed<gfxASurface>
BasicImageContainer::GetCurrentAsSurface(gfxIntSize* aSizeResult)
{
NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mImage) {
return nsnull;
}
*aSizeResult = ToImageData(mImage)->GetSize();
return ToImageData(mImage)->GetAsSurface();
}
gfxIntSize
BasicImageContainer::GetCurrentSize()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return !mImage ? gfxIntSize(0,0) : ToImageData(mImage)->GetSize();
}
void BasicImageContainer::SetScaleHint(const gfxIntSize& aScaleHint)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mScaleHint = aScaleHint;
}
bool
BasicImageContainer::SetLayerManager(LayerManager *aManager)
{
if (aManager &&
aManager->GetBackendType() != LayerManager::LAYERS_BASIC)
{
return false;
if (!mFactory) {
mFactory = new BasicImageFactory();
}
return true;
}
already_AddRefed<ImageContainer>
BasicLayerManager::CreateImageContainer()
{
nsRefPtr<ImageContainer> container = new BasicImageContainer();
static_cast<BasicImageContainer*>(container.get())->
SetOffscreenFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());
return container.forget();
return mFactory.get();
}
}

View File

@ -899,6 +899,8 @@ BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext,
if (!mContainer)
return nsnull;
mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nsnull : BasicManager()->GetImageFactory());
nsRefPtr<Image> image = mContainer->GetCurrentImage();
nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&mSize);

View File

@ -131,9 +131,10 @@ public:
virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
virtual already_AddRefed<ImageLayer> CreateImageLayer();
virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
virtual already_AddRefed<ImageContainer> CreateImageContainer();
virtual already_AddRefed<ColorLayer> CreateColorLayer();
virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
virtual ImageFactory *GetImageFactory();
virtual already_AddRefed<ShadowThebesLayer> CreateShadowThebesLayer()
{ return nsnull; }
virtual already_AddRefed<ShadowContainerLayer> CreateShadowContainerLayer()
@ -207,6 +208,8 @@ protected:
nsRefPtr<gfxContext> mDefaultTarget;
// The context to draw into.
nsRefPtr<gfxContext> mTarget;
// Image factory we use.
nsRefPtr<ImageFactory> mFactory;
// Cached surface for double buffering
gfxCachedTempSurface mCachedSurface;
@ -276,54 +279,4 @@ private:
}
}
/**
* We need to be able to hold a reference to a gfxASurface from Image
* subclasses. This is potentially a problem since Images can be addrefed
* or released off the main thread. We can ensure that we never AddRef
* a gfxASurface off the main thread, but we might want to Release due
* to an Image being destroyed off the main thread.
*
* We use nsCountedRef<nsMainThreadSurfaceRef> to reference the
* gfxASurface. When AddRefing, we assert that we're on the main thread.
* When Releasing, if we're not on the main thread, we post an event to
* the main thread to do the actual release.
*/
class nsMainThreadSurfaceRef;
template <>
class nsAutoRefTraits<nsMainThreadSurfaceRef> {
public:
typedef gfxASurface* RawRef;
/**
* The XPCOM event that will do the actual release on the main thread.
*/
class SurfaceReleaser : public nsRunnable {
public:
SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
NS_IMETHOD Run() {
mRef->Release();
return NS_OK;
}
RawRef mRef;
};
static RawRef Void() { return nsnull; }
static void Release(RawRef aRawRef)
{
if (NS_IsMainThread()) {
aRawRef->Release();
return;
}
nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
NS_DispatchToMainThread(runnable);
}
static void AddRef(RawRef aRawRef)
{
NS_ASSERTION(NS_IsMainThread(),
"Can only add a reference on the main thread");
aRawRef->AddRef();
}
};
#endif /* GFX_BASICLAYERS_H */

View File

@ -100,102 +100,6 @@ SurfaceToTexture(ID3D10Device *aDevice,
return texture.forget();
}
ImageContainerD3D10::ImageContainerD3D10(ID3D10Device1 *aDevice)
: ImageContainer(nsnull)
, mDevice(aDevice)
{
}
already_AddRefed<Image>
ImageContainerD3D10::CreateImage(const Image::Format *aFormats,
PRUint32 aNumFormats)
{
if (!aNumFormats) {
return nsnull;
}
nsRefPtr<Image> img;
if (aFormats[0] == Image::PLANAR_YCBCR) {
img = new PlanarYCbCrImageD3D10(mDevice);
} else if (aFormats[0] == Image::CAIRO_SURFACE) {
img = new CairoImageD3D10(mDevice);
}
return img.forget();
}
void
ImageContainerD3D10::SetCurrentImage(Image *aImage)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mActiveImage = aImage;
CurrentImageChanged();
}
already_AddRefed<Image>
ImageContainerD3D10::GetCurrentImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<Image> retval = mActiveImage;
return retval.forget();
}
already_AddRefed<gfxASurface>
ImageContainerD3D10::GetCurrentAsSurface(gfxIntSize *aSize)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return nsnull;
}
if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D10 *yuvImage =
static_cast<PlanarYCbCrImageD3D10*>(mActiveImage.get());
if (yuvImage->HasData()) {
*aSize = yuvImage->mSize;
}
} else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
CairoImageD3D10 *cairoImage =
static_cast<CairoImageD3D10*>(mActiveImage.get());
*aSize = cairoImage->mSize;
}
return static_cast<ImageD3D10*>(mActiveImage->GetImplData())->GetAsSurface();
}
gfxIntSize
ImageContainerD3D10::GetCurrentSize()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return gfxIntSize(0,0);
}
if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D10 *yuvImage =
static_cast<PlanarYCbCrImageD3D10*>(mActiveImage.get());
if (!yuvImage->HasData()) {
return gfxIntSize(0,0);
}
return yuvImage->mSize;
} else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
CairoImageD3D10 *cairoImage =
static_cast<CairoImageD3D10*>(mActiveImage.get());
return cairoImage->mSize;
}
return gfxIntSize(0,0);
}
bool
ImageContainerD3D10::SetLayerManager(LayerManager *aManager)
{
if (aManager->GetBackendType() == LayerManager::LAYERS_D3D10) {
return true;
}
return false;
}
Layer*
ImageLayerD3D10::GetLayer()
{
@ -218,51 +122,39 @@ ImageLayerD3D10::RenderLayer()
ID3D10EffectTechnique *technique;
if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D10 ||
image->GetFormat() == Image::CAIRO_SURFACE)
if (image->GetFormat() == Image::CAIRO_SURFACE)
{
gfxIntSize size;
bool hasAlpha;
nsRefPtr<ID3D10ShaderResourceView> srView;
CairoImage *cairoImage =
static_cast<CairoImage*>(image.get());
if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D10)
{
nsRefPtr<gfxASurface> surf = GetContainer()->GetCurrentAsSurface(&size);
nsRefPtr<ID3D10Texture2D> texture = SurfaceToTexture(device(), surf, size);
if (!texture) {
NS_WARNING("Failed to create texture for surface.");
return;
}
hasAlpha = surf->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA;
device()->CreateShaderResourceView(texture, NULL, getter_AddRefs(srView));
} else {
ImageContainerD3D10 *container =
static_cast<ImageContainerD3D10*>(GetContainer());
if (container->device() != device()) {
container->SetDevice(device());
}
// image->GetFormat() == Image::CAIRO_SURFACE
CairoImageD3D10 *cairoImage =
static_cast<CairoImageD3D10*>(image.get());
if (cairoImage->mDevice != device()) {
// This shader resource view was for an old device! Can't draw that
// now.
return;
}
srView = cairoImage->mSRView;
hasAlpha = cairoImage->mHasAlpha;
size = cairoImage->mSize;
if (!cairoImage->mSurface) {
return;
}
if (hasAlpha) {
if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D10)) {
nsAutoPtr<CairoD3D10BackendData> dat = new CairoD3D10BackendData();
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
if (dat->mTexture) {
device()->CreateShaderResourceView(dat->mTexture, NULL, getter_AddRefs(dat->mSRView));
cairoImage->SetBackendData(LayerManager::LAYERS_D3D10, dat.forget());
}
}
CairoD3D10BackendData *data =
static_cast<CairoD3D10BackendData*>(cairoImage->GetBackendData(LayerManager::LAYERS_D3D10));
if (!data) {
return;
}
nsRefPtr<ID3D10Device> dev;
data->mTexture->GetDevice(getter_AddRefs(dev));
if (dev != device()) {
return;
}
if (cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
if (mFilter == gfxPattern::FILTER_NEAREST) {
technique = effect()->GetTechniqueByName("RenderRGBALayerPremulPoint");
} else {
@ -276,29 +168,38 @@ ImageLayerD3D10::RenderLayer()
}
}
if (srView) {
effect()->GetVariableByName("tRGB")->AsShaderResource()->SetResource(srView);
}
effect()->GetVariableByName("tRGB")->AsShaderResource()->SetResource(data->mSRView);
effect()->GetVariableByName("vLayerQuad")->AsVector()->SetFloatVector(
ShaderConstantRectD3D10(
(float)0,
(float)0,
(float)size.width,
(float)size.height)
(float)cairoImage->mSize.width,
(float)cairoImage->mSize.height)
);
} else if (image->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D10 *yuvImage =
static_cast<PlanarYCbCrImageD3D10*>(image.get());
PlanarYCbCrImage *yuvImage =
static_cast<PlanarYCbCrImage*>(image.get());
if (!yuvImage->HasData()) {
if (!yuvImage->mBufferSize) {
return;
}
if (yuvImage->mDevice != device()) {
// These shader resources were created for an old device! Can't draw
// that here.
return;
if (!yuvImage->GetBackendData(LayerManager::LAYERS_D3D10)) {
AllocateTexturesYCbCr(yuvImage);
}
PlanarYCbCrD3D10BackendData *data =
static_cast<PlanarYCbCrD3D10BackendData*>(yuvImage->GetBackendData(LayerManager::LAYERS_D3D10));
if (!data) {
return;
}
nsRefPtr<ID3D10Device> dev;
data->mYTexture->GetDevice(getter_AddRefs(dev));
if (dev != device()) {
return;
}
// TODO: At some point we should try to deal with mFilter here, you don't
@ -308,9 +209,9 @@ ImageLayerD3D10::RenderLayer()
technique = effect()->GetTechniqueByName("RenderYCbCrLayer");
effect()->GetVariableByName("tY")->AsShaderResource()->SetResource(yuvImage->mYView);
effect()->GetVariableByName("tCb")->AsShaderResource()->SetResource(yuvImage->mCbView);
effect()->GetVariableByName("tCr")->AsShaderResource()->SetResource(yuvImage->mCrView);
effect()->GetVariableByName("tY")->AsShaderResource()->SetResource(data->mYView);
effect()->GetVariableByName("tCb")->AsShaderResource()->SetResource(data->mCbView);
effect()->GetVariableByName("tCr")->AsShaderResource()->SetResource(data->mCrView);
/*
* Send 3d control data and metadata to NV3DVUtils
@ -341,7 +242,7 @@ ImageLayerD3D10::RenderLayer()
if (yuvImage->mData.mStereoMode != STEREO_MODE_MONO) {
// Dst resource is optional
GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->mSize.width,
(unsigned int)yuvImage->mSize.height, (HANDLE)(yuvImage->mYTexture), (HANDLE)(NULL));
(unsigned int)yuvImage->mSize.height, (HANDLE)(data->mYTexture), (HANDLE)(NULL));
}
}
@ -373,146 +274,49 @@ ImageLayerD3D10::RenderLayer()
GetContainer()->NotifyPaintedImage(image);
}
PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice)
: PlanarYCbCrImage(static_cast<ImageD3D10*>(this))
, mBufferSize(0)
, mDevice(aDevice)
, mHasData(false)
void ImageLayerD3D10::AllocateTexturesYCbCr(PlanarYCbCrImage *aImage)
{
}
nsAutoPtr<PlanarYCbCrD3D10BackendData> backendData =
new PlanarYCbCrD3D10BackendData;
void
PlanarYCbCrImageD3D10::SetData(const PlanarYCbCrImage::Data &aData)
{
mBuffer = CopyData(mData, mSize, mBufferSize, aData);
PlanarYCbCrImage::Data &data = aImage->mData;
AllocateTextures();
mHasData = true;
}
void
PlanarYCbCrImageD3D10::AllocateTextures()
{
D3D10_SUBRESOURCE_DATA dataY;
D3D10_SUBRESOURCE_DATA dataCb;
D3D10_SUBRESOURCE_DATA dataCr;
CD3D10_TEXTURE2D_DESC descY(DXGI_FORMAT_R8_UNORM,
mData.mYSize.width,
mData.mYSize.height, 1, 1);
data.mYSize.width,
data.mYSize.height, 1, 1);
CD3D10_TEXTURE2D_DESC descCbCr(DXGI_FORMAT_R8_UNORM,
mData.mCbCrSize.width,
mData.mCbCrSize.height, 1, 1);
data.mCbCrSize.width,
data.mCbCrSize.height, 1, 1);
descY.Usage = descCbCr.Usage = D3D10_USAGE_IMMUTABLE;
dataY.pSysMem = mData.mYChannel;
dataY.SysMemPitch = mData.mYStride;
dataCb.pSysMem = mData.mCbChannel;
dataCb.SysMemPitch = mData.mCbCrStride;
dataCr.pSysMem = mData.mCrChannel;
dataCr.SysMemPitch = mData.mCbCrStride;
dataY.pSysMem = data.mYChannel;
dataY.SysMemPitch = data.mYStride;
dataCb.pSysMem = data.mCbChannel;
dataCb.SysMemPitch = data.mCbCrStride;
dataCr.pSysMem = data.mCrChannel;
dataCr.SysMemPitch = data.mCbCrStride;
HRESULT hr = mDevice->CreateTexture2D(&descY, &dataY, getter_AddRefs(mYTexture));
HRESULT hr = device()->CreateTexture2D(&descY, &dataY, getter_AddRefs(backendData->mYTexture));
if (!FAILED(hr)) {
hr = mDevice->CreateTexture2D(&descCbCr, &dataCb, getter_AddRefs(mCbTexture));
hr = device()->CreateTexture2D(&descCbCr, &dataCb, getter_AddRefs(backendData->mCbTexture));
}
if (!FAILED(hr)) {
hr = mDevice->CreateTexture2D(&descCbCr, &dataCr, getter_AddRefs(mCrTexture));
hr = device()->CreateTexture2D(&descCbCr, &dataCr, getter_AddRefs(backendData->mCrTexture));
}
if (FAILED(hr)) {
LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D10::AllocateTextures(): Failed to create texture"),
hr);
return;
}
mDevice->CreateShaderResourceView(mYTexture, NULL, getter_AddRefs(mYView));
mDevice->CreateShaderResourceView(mCbTexture, NULL, getter_AddRefs(mCbView));
mDevice->CreateShaderResourceView(mCrTexture, NULL, getter_AddRefs(mCrView));
}
device()->CreateShaderResourceView(backendData->mYTexture, NULL, getter_AddRefs(backendData->mYView));
device()->CreateShaderResourceView(backendData->mCbTexture, NULL, getter_AddRefs(backendData->mCbView));
device()->CreateShaderResourceView(backendData->mCrTexture, NULL, getter_AddRefs(backendData->mCrView));
already_AddRefed<gfxASurface>
PlanarYCbCrImageD3D10::GetAsSurface()
{
nsRefPtr<gfxImageSurface> imageSurface =
new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24);
gfx::YUVType type =
gfx::TypeFromSize(mData.mYSize.width,
mData.mYSize.height,
mData.mCbCrSize.width,
mData.mCbCrSize.height);
// Convert from YCbCr to RGB now
gfx::ConvertYCbCrToRGB32(mData.mYChannel,
mData.mCbChannel,
mData.mCrChannel,
imageSurface->Data(),
mData.mPicX,
mData.mPicY,
mData.mPicSize.width,
mData.mPicSize.height,
mData.mYStride,
mData.mCbCrStride,
imageSurface->Stride(),
type);
return imageSurface.forget().get();
}
CairoImageD3D10::~CairoImageD3D10()
{
}
void
CairoImageD3D10::SetData(const CairoImage::Data &aData)
{
mSize = aData.mSize;
NS_ASSERTION(aData.mSurface->GetContentType() != gfxASurface::CONTENT_ALPHA,
"Invalid content type passed to CairoImageD3D10.");
mTexture = SurfaceToTexture(mDevice, aData.mSurface, mSize);
if (!mTexture) {
NS_WARNING("Failed to create texture for CairoImage.");
return;
}
if (aData.mSurface->GetContentType() == gfxASurface::CONTENT_COLOR) {
mHasAlpha = false;
} else {
mHasAlpha = true;
}
mDevice->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
}
already_AddRefed<gfxASurface>
CairoImageD3D10::GetAsSurface()
{
nsRefPtr<ID3D10Texture2D> surfTexture;
// Make a copy of the texture since our current texture is not suitable for
// drawing with Direct2D because it is immutable and cannot be bound as a
// render target.
D3D10_TEXTURE2D_DESC texDesc;
mTexture->GetDesc(&texDesc);
texDesc.Usage = D3D10_USAGE_DEFAULT;
texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
texDesc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE;
HRESULT hr = mDevice->CreateTexture2D(&texDesc, NULL, getter_AddRefs(surfTexture));
if (FAILED(hr)) {
LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("CairoImageD3D10::GetAsSurface(): Failed to create texture"),
hr);
return nsnull;
}
mDevice->CopyResource(surfTexture, mTexture);
nsRefPtr<gfxASurface> surf =
new gfxD2DSurface(surfTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA :
gfxASurface::CONTENT_COLOR);
return surf.forget();
aImage->SetBackendData(LayerManager::LAYERS_D3D10, backendData.forget());
}
} /* layers */

View File

@ -45,35 +45,6 @@
namespace mozilla {
namespace layers {
class THEBES_API ImageContainerD3D10 : public ImageContainer
{
public:
ImageContainerD3D10(ID3D10Device1 *aDevice);
virtual ~ImageContainerD3D10() {}
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats);
virtual void SetCurrentImage(Image* aImage);
virtual already_AddRefed<Image> GetCurrentImage();
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSize);
virtual gfxIntSize GetCurrentSize();
virtual bool SetLayerManager(LayerManager *aManager);
virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_D3D10; }
ID3D10Device1 *device() { return mDevice; }
void SetDevice(ID3D10Device1 *aDevice) { mDevice = aDevice; }
private:
nsRefPtr<Image> mActiveImage;
nsRefPtr<ID3D10Device1> mDevice;
};
class THEBES_API ImageLayerD3D10 : public ImageLayer,
public LayerD3D10
{
@ -89,70 +60,24 @@ public:
virtual Layer* GetLayer();
virtual void RenderLayer();
void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage);
};
class THEBES_API ImageD3D10
struct PlanarYCbCrD3D10BackendData : public ImageBackendData
{
public:
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
};
class THEBES_API PlanarYCbCrImageD3D10 : public PlanarYCbCrImage,
public ImageD3D10
{
public:
PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice);
~PlanarYCbCrImageD3D10() {}
virtual void SetData(const Data &aData);
/*
* Upload the data from out mData into our textures. For now we use this to
* make sure the textures are created and filled on the main thread.
*/
void AllocateTextures();
bool HasData() { return mHasData; }
PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; }
virtual already_AddRefed<gfxASurface> GetAsSurface();
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferSize;
nsRefPtr<ID3D10Device1> mDevice;
Data mData;
gfxIntSize mSize;
nsRefPtr<ID3D10Texture2D> mYTexture;
nsRefPtr<ID3D10Texture2D> mCrTexture;
nsRefPtr<ID3D10Texture2D> mCbTexture;
nsRefPtr<ID3D10ShaderResourceView> mYView;
nsRefPtr<ID3D10ShaderResourceView> mCbView;
nsRefPtr<ID3D10ShaderResourceView> mCrView;
bool mHasData;
};
class THEBES_API CairoImageD3D10 : public CairoImage,
public ImageD3D10
struct CairoD3D10BackendData : public ImageBackendData
{
public:
CairoImageD3D10(ID3D10Device1 *aDevice)
: CairoImage(static_cast<ImageD3D10*>(this))
, mDevice(aDevice)
, mHasAlpha(true)
{ }
~CairoImageD3D10();
virtual void SetData(const Data &aData);
virtual already_AddRefed<gfxASurface> GetAsSurface();
nsRefPtr<ID3D10Device1> mDevice;
nsRefPtr<ID3D10Texture2D> mTexture;
nsRefPtr<ID3D10ShaderResourceView> mSRView;
gfxIntSize mSize;
bool mHasAlpha;
};
} /* layers */

View File

@ -421,13 +421,6 @@ LayerManagerD3D10::CreateReadbackLayer()
return layer.forget();
}
already_AddRefed<ImageContainer>
LayerManagerD3D10::CreateImageContainer()
{
nsRefPtr<ImageContainer> layer = new ImageContainerD3D10(mDevice);
return layer.forget();
}
static void ReleaseTexture(void *texture)
{
static_cast<ID3D10Texture2D*>(texture)->Release();

View File

@ -161,8 +161,6 @@ public:
virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
virtual already_AddRefed<ImageContainer> CreateImageContainer();
virtual already_AddRefed<gfxASurface>
CreateOptimalSurface(const gfxIntSize &aSize,
gfxASurface::gfxImageFormat imageFormat);

View File

@ -137,101 +137,160 @@ SurfaceToTexture(IDirect3DDevice9 *aDevice,
return texture.forget();
}
ImageContainerD3D9::ImageContainerD3D9(IDirect3DDevice9 *aDevice)
: ImageContainer(nsnull)
, mDevice(aDevice)
static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage,
IDirect3DDevice9 *aDevice,
LayerManagerD3D9 *aManager)
{
}
nsAutoPtr<PlanarYCbCrD3D9BackendData> backendData =
new PlanarYCbCrD3D9BackendData;
already_AddRefed<Image>
ImageContainerD3D9::CreateImage(const Image::Format *aFormats,
PRUint32 aNumFormats)
{
if (!aNumFormats) {
return nsnull;
}
nsRefPtr<Image> img;
if (aFormats[0] == Image::PLANAR_YCBCR) {
img = new PlanarYCbCrImageD3D9();
} else if (aFormats[0] == Image::CAIRO_SURFACE) {
img = new CairoImageD3D9(mDevice);
}
return img.forget();
}
PlanarYCbCrImage::Data &data = aImage->mData;
void
ImageContainerD3D9::SetCurrentImage(Image *aImage)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
D3DLOCKED_RECT lockrectY;
D3DLOCKED_RECT lockrectCb;
D3DLOCKED_RECT lockrectCr;
PRUint8* src;
PRUint8* dest;
mActiveImage = aImage;
CurrentImageChanged();
}
nsRefPtr<IDirect3DSurface9> tmpSurfaceY;
nsRefPtr<IDirect3DSurface9> tmpSurfaceCb;
nsRefPtr<IDirect3DSurface9> tmpSurfaceCr;
already_AddRefed<Image>
ImageContainerD3D9::GetCurrentImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<IDirect3DDevice9Ex> deviceEx;
aDevice->QueryInterface(IID_IDirect3DDevice9Ex,
getter_AddRefs(deviceEx));
nsRefPtr<Image> retval = mActiveImage;
return retval.forget();
}
bool isD3D9Ex = deviceEx;
already_AddRefed<gfxASurface>
ImageContainerD3D9::GetCurrentAsSurface(gfxIntSize *aSize)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return nsnull;
}
if (isD3D9Ex) {
nsRefPtr<IDirect3DTexture9> tmpYTexture;
nsRefPtr<IDirect3DTexture9> tmpCbTexture;
nsRefPtr<IDirect3DTexture9> tmpCrTexture;
// D3D9Ex does not support the managed pool, could use dynamic textures
// here. But since an Image is immutable static textures are probably a
// better idea.
if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D9 *yuvImage =
static_cast<PlanarYCbCrImageD3D9*>(mActiveImage.get());
if (yuvImage->HasData()) {
*aSize = yuvImage->mSize;
HRESULT hr;
hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(backendData->mYTexture), NULL);
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(backendData->mCbTexture), NULL);
}
} else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
CairoImageD3D9 *cairoImage =
static_cast<CairoImageD3D9*>(mActiveImage.get());
*aSize = cairoImage->GetSize();
}
return static_cast<ImageD3D9*>(mActiveImage->GetImplData())->GetAsSurface();
}
gfxIntSize
ImageContainerD3D9::GetCurrentSize()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mActiveImage) {
return gfxIntSize(0,0);
}
if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D9 *yuvImage =
static_cast<PlanarYCbCrImageD3D9*>(mActiveImage.get());
if (!yuvImage->HasData()) {
return gfxIntSize(0,0);
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(backendData->mCrTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpYTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpCbTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpCrTexture), NULL);
}
return yuvImage->mSize;
} else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
CairoImageD3D9 *cairoImage =
static_cast<CairoImageD3D9*>(mActiveImage.get());
return cairoImage->GetSize();
if (FAILED(hr)) {
aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (isD3D9Ex)"),
hr);
return;
}
tmpYTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceY));
tmpCbTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCb));
tmpCrTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCr));
tmpSurfaceY->LockRect(&lockrectY, NULL, 0);
tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0);
tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0);
} else {
HRESULT hr;
hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(backendData->mYTexture), NULL);
if (!FAILED(hr)) {
aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(backendData->mCbTexture), NULL);
}
if (!FAILED(hr)) {
aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(backendData->mCrTexture), NULL);
}
if (FAILED(hr)) {
aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (!isD3D9Ex)"),
hr);
return;
}
/* lock the entire texture */
backendData->mYTexture->LockRect(0, &lockrectY, NULL, 0);
backendData->mCbTexture->LockRect(0, &lockrectCb, NULL, 0);
backendData->mCrTexture->LockRect(0, &lockrectCr, NULL, 0);
}
return gfxIntSize(0,0);
}
src = data.mYChannel;
//FIX cast
dest = (PRUint8*)lockrectY.pBits;
bool
ImageContainerD3D9::SetLayerManager(LayerManager *aManager)
{
if (aManager->GetBackendType() == LayerManager::LAYERS_D3D9) {
return true;
// copy over data
for (int h=0; h<data.mYSize.height; h++) {
memcpy(dest, src, data.mYSize.width);
dest += lockrectY.Pitch;
src += data.mYStride;
}
return false;
src = data.mCbChannel;
//FIX cast
dest = (PRUint8*)lockrectCb.pBits;
// copy over data
for (int h=0; h<data.mCbCrSize.height; h++) {
memcpy(dest, src, data.mCbCrSize.width);
dest += lockrectCb.Pitch;
src += data.mCbCrStride;
}
src = data.mCrChannel;
//FIX cast
dest = (PRUint8*)lockrectCr.pBits;
// copy over data
for (int h=0; h<data.mCbCrSize.height; h++) {
memcpy(dest, src, data.mCbCrSize.width);
dest += lockrectCr.Pitch;
src += data.mCbCrStride;
}
if (isD3D9Ex) {
tmpSurfaceY->UnlockRect();
tmpSurfaceCb->UnlockRect();
tmpSurfaceCr->UnlockRect();
nsRefPtr<IDirect3DSurface9> dstSurface;
backendData->mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL);
backendData->mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL);
backendData->mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL);
} else {
backendData->mYTexture->UnlockRect(0);
backendData->mCbTexture->UnlockRect(0);
backendData->mCrTexture->UnlockRect(0);
}
aImage->SetBackendData(LayerManager::LAYERS_D3D9, backendData.forget());
}
Layer*
@ -254,8 +313,37 @@ ImageLayerD3D9::RenderLayer()
SetShaderTransformAndOpacity();
if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D9)
if (image->GetFormat() == Image::CAIRO_SURFACE)
{
CairoImage *cairoImage =
static_cast<CairoImage*>(image.get());
if (!cairoImage->mSurface) {
return;
}
if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D9)) {
nsAutoPtr<CairoD3D9BackendData> dat = new CairoD3D9BackendData();
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
if (dat->mTexture) {
cairoImage->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget());
}
}
CairoD3D9BackendData *data =
static_cast<CairoD3D9BackendData*>(cairoImage->GetBackendData(LayerManager::LAYERS_D3D9));
if (!data) {
return;
}
nsRefPtr<IDirect3DDevice9> dev;
data->mTexture->GetDevice(getter_AddRefs(dev));
if (dev != device()) {
return;
}
gfxIntSize size;
nsRefPtr<gfxASurface> surface =
GetContainer()->GetCurrentAsSurface(&size);
@ -275,16 +363,40 @@ ImageLayerD3D9::RenderLayer()
mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
}
if (mFilter == gfxPattern::FILTER_NEAREST) {
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
}
device()->SetTexture(0, texture);
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
} else if (image->GetFormat() == Image::PLANAR_YCBCR) {
PlanarYCbCrImageD3D9 *yuvImage =
static_cast<PlanarYCbCrImageD3D9*>(image.get());
if (mFilter == gfxPattern::FILTER_NEAREST) {
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
}
} else {
PlanarYCbCrImage *yuvImage =
static_cast<PlanarYCbCrImage*>(image.get());
if (!yuvImage->HasData()) {
if (!yuvImage->mBufferSize) {
return;
}
if (!yuvImage->GetBackendData(LayerManager::LAYERS_D3D9)) {
AllocateTexturesYCbCr(yuvImage, device(), mD3DManager);
}
PlanarYCbCrD3D9BackendData *data =
static_cast<PlanarYCbCrD3D9BackendData*>(yuvImage->GetBackendData(LayerManager::LAYERS_D3D9));
if (!data) {
return;
}
nsRefPtr<IDirect3DDevice9> dev;
data->mYTexture->GetDevice(getter_AddRefs(dev));
if (dev != device()) {
return;
}
yuvImage->AllocateTextures(device());
device()->SetVertexShaderConstantF(CBvLayerQuad,
ShaderConstantRect(0,
@ -336,300 +448,26 @@ ImageLayerD3D9::RenderLayer()
nsRefPtr<IDirect3DSurface9> renderTarget;
device()->GetRenderTarget(0, getter_AddRefs(renderTarget));
mD3DManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->mSize.width,
(unsigned int)yuvImage->mSize.height, (HANDLE)(yuvImage->mYTexture), (HANDLE)(renderTarget));
(unsigned int)yuvImage->mSize.height, (HANDLE)(data->mYTexture), (HANDLE)(renderTarget));
}
}
// Linear scaling is default here, adhering to mFilter is difficult since
// presumably even with point filtering we'll still want chroma upsampling
// to be linear. In the current approach we can't.
device()->SetTexture(0, yuvImage->mYTexture);
device()->SetTexture(1, yuvImage->mCbTexture);
device()->SetTexture(2, yuvImage->mCrTexture);
device()->SetTexture(0, data->mYTexture);
device()->SetTexture(1, data->mCbTexture);
device()->SetTexture(2, data->mCrTexture);
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
device()->SetVertexShaderConstantF(CBvTextureCoords,
ShaderConstantRect(0, 0, 1.0f, 1.0f), 1);
} else if (image->GetFormat() == Image::CAIRO_SURFACE) {
CairoImageD3D9 *cairoImage =
static_cast<CairoImageD3D9*>(image.get());
ImageContainerD3D9 *container =
static_cast<ImageContainerD3D9*>(GetContainer());
if (container->device() != device()) {
// Ensure future images get created with the right device.
container->SetDevice(device());
}
if (cairoImage->device() != device()) {
cairoImage->SetDevice(device());
}
device()->SetVertexShaderConstantF(CBvLayerQuad,
ShaderConstantRect(0,
0,
cairoImage->GetSize().width,
cairoImage->GetSize().height),
1);
if (cairoImage->HasAlpha()) {
mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
} else {
mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
}
if (mFilter == gfxPattern::FILTER_NEAREST) {
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
}
device()->SetTexture(0, cairoImage->GetOrCreateTexture());
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
if (mFilter == gfxPattern::FILTER_NEAREST) {
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
}
}
GetContainer()->NotifyPaintedImage(image);
}
PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9()
: PlanarYCbCrImage(static_cast<ImageD3D9*>(this))
, mBufferSize(0)
, mHasData(false)
{
}
void
PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData)
{
mBuffer = CopyData(mData, mSize, mBufferSize, aData);
mHasData = true;
}
void
PlanarYCbCrImageD3D9::AllocateTextures(IDirect3DDevice9 *aDevice)
{
D3DLOCKED_RECT lockrectY;
D3DLOCKED_RECT lockrectCb;
D3DLOCKED_RECT lockrectCr;
PRUint8* src;
PRUint8* dest;
nsRefPtr<IDirect3DSurface9> tmpSurfaceY;
nsRefPtr<IDirect3DSurface9> tmpSurfaceCb;
nsRefPtr<IDirect3DSurface9> tmpSurfaceCr;
nsRefPtr<IDirect3DDevice9Ex> deviceEx;
aDevice->QueryInterface(IID_IDirect3DDevice9Ex,
getter_AddRefs(deviceEx));
bool isD3D9Ex = deviceEx;
if (isD3D9Ex) {
nsRefPtr<IDirect3DTexture9> tmpYTexture;
nsRefPtr<IDirect3DTexture9> tmpCbTexture;
nsRefPtr<IDirect3DTexture9> tmpCrTexture;
// D3D9Ex does not support the managed pool, could use dynamic textures
// here. But since an Image is immutable static textures are probably a
// better idea.
HRESULT hr;
hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(mYTexture), NULL);
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(mCbTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_DEFAULT,
getter_AddRefs(mCrTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpYTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpCbTexture), NULL);
}
if (!FAILED(hr)) {
hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM,
getter_AddRefs(tmpCrTexture), NULL);
}
if (FAILED(hr)) {
mManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (isD3D9Ex)"),
hr);
return;
}
tmpYTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceY));
tmpCbTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCb));
tmpCrTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCr));
tmpSurfaceY->LockRect(&lockrectY, NULL, 0);
tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0);
tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0);
} else {
HRESULT hr;
hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(mYTexture), NULL);
if (!FAILED(hr)) {
aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(mCbTexture), NULL);
}
if (!FAILED(hr)) {
aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height,
1, 0, D3DFMT_L8, D3DPOOL_MANAGED,
getter_AddRefs(mCrTexture), NULL);
}
if (FAILED(hr)) {
mManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (!isD3D9Ex)"),
hr);
return;
}
/* lock the entire texture */
mYTexture->LockRect(0, &lockrectY, NULL, 0);
mCbTexture->LockRect(0, &lockrectCb, NULL, 0);
mCrTexture->LockRect(0, &lockrectCr, NULL, 0);
}
src = mData.mYChannel;
//FIX cast
dest = (PRUint8*)lockrectY.pBits;
// copy over data
for (int h=0; h<mData.mYSize.height; h++) {
memcpy(dest, src, mData.mYSize.width);
dest += lockrectY.Pitch;
src += mData.mYStride;
}
src = mData.mCbChannel;
//FIX cast
dest = (PRUint8*)lockrectCb.pBits;
// copy over data
for (int h=0; h<mData.mCbCrSize.height; h++) {
memcpy(dest, src, mData.mCbCrSize.width);
dest += lockrectCb.Pitch;
src += mData.mCbCrStride;
}
src = mData.mCrChannel;
//FIX cast
dest = (PRUint8*)lockrectCr.pBits;
// copy over data
for (int h=0; h<mData.mCbCrSize.height; h++) {
memcpy(dest, src, mData.mCbCrSize.width);
dest += lockrectCr.Pitch;
src += mData.mCbCrStride;
}
if (isD3D9Ex) {
tmpSurfaceY->UnlockRect();
tmpSurfaceCb->UnlockRect();
tmpSurfaceCr->UnlockRect();
nsRefPtr<IDirect3DSurface9> dstSurface;
mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL);
mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL);
mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
aDevice->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL);
} else {
mYTexture->UnlockRect(0);
mCbTexture->UnlockRect(0);
mCrTexture->UnlockRect(0);
}
}
void
PlanarYCbCrImageD3D9::FreeTextures()
{
}
already_AddRefed<gfxASurface>
PlanarYCbCrImageD3D9::GetAsSurface()
{
nsRefPtr<gfxImageSurface> imageSurface =
new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24);
gfx::YUVType type =
gfx::TypeFromSize(mData.mYSize.width,
mData.mYSize.height,
mData.mCbCrSize.width,
mData.mCbCrSize.height);
// Convert from YCbCr to RGB now
gfx::ConvertYCbCrToRGB32(mData.mYChannel,
mData.mCbChannel,
mData.mCrChannel,
imageSurface->Data(),
mData.mPicX,
mData.mPicY,
mData.mPicSize.width,
mData.mPicSize.height,
mData.mYStride,
mData.mCbCrStride,
imageSurface->Stride(),
type);
return imageSurface.forget().get();
}
CairoImageD3D9::~CairoImageD3D9()
{
}
void
CairoImageD3D9::SetDevice(IDirect3DDevice9 *aDevice)
{
mTexture = NULL;
mDevice = aDevice;
}
void
CairoImageD3D9::SetData(const CairoImage::Data &aData)
{
mSize = aData.mSize;
mCachedSurface = aData.mSurface;
mTexture = NULL;
}
IDirect3DTexture9*
CairoImageD3D9::GetOrCreateTexture()
{
if (mTexture)
return mTexture;
mTexture = SurfaceToTexture(mDevice, mCachedSurface, mSize);
// We need to keep our cached surface around in case the device changes.
return mTexture;
}
already_AddRefed<gfxASurface>
CairoImageD3D9::GetAsSurface()
{
nsRefPtr<gfxASurface> surface = mCachedSurface;
return surface.forget();
}
ShadowImageLayerD3D9::ShadowImageLayerD3D9(LayerManagerD3D9* aManager)
: ShadowImageLayer(aManager, nsnull)
, LayerD3D9(aManager)
@ -675,7 +513,7 @@ ShadowImageLayerD3D9::Swap(const SharedImage& aNewFront,
data.mPicY = 0;
if (!mYCbCrImage) {
mYCbCrImage = new PlanarYCbCrImageD3D9();
mYCbCrImage = new PlanarYCbCrImage(new BufferRecycleBin());
}
mYCbCrImage->SetData(data);
@ -710,12 +548,25 @@ ShadowImageLayerD3D9::RenderLayer()
if (mBuffer) {
mBuffer->RenderTo(mD3DManager, GetEffectiveVisibleRegion());
} else if (mYCbCrImage) {
if (!mYCbCrImage->HasData()) {
if (!mYCbCrImage->mBufferSize) {
return;
}
if (!mYCbCrImage->GetBackendData(LayerManager::LAYERS_D3D9)) {
AllocateTexturesYCbCr(mYCbCrImage, device(), mD3DManager);
}
PlanarYCbCrD3D9BackendData *data =
static_cast<PlanarYCbCrD3D9BackendData*>(mYCbCrImage->GetBackendData(LayerManager::LAYERS_D3D9));
if (!data) {
return;
}
if (!mYCbCrImage->mBufferSize) {
return;
}
mYCbCrImage->AllocateTextures(device());
SetShaderTransformAndOpacity();
device()->SetVertexShaderConstantF(CBvLayerQuad,
@ -737,9 +588,9 @@ ShadowImageLayerD3D9::RenderLayer()
// Linear scaling is default here, adhering to mFilter is difficult since
// presumably even with point filtering we'll still want chroma upsampling
// to be linear. In the current approach we can't.
device()->SetTexture(0, mYCbCrImage->mYTexture);
device()->SetTexture(1, mYCbCrImage->mCbTexture);
device()->SetTexture(2, mYCbCrImage->mCrTexture);
device()->SetTexture(0, data->mYTexture);
device()->SetTexture(1, data->mCbTexture);
device()->SetTexture(2, data->mCrTexture);
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
} else {

Some files were not shown because too many files have changed in this diff Show More