Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2013-07-17 15:46:40 +01:00
commit 7ba642a842
149 changed files with 3265 additions and 937 deletions

View File

@ -11,7 +11,7 @@ typedef long AccessibleTextBoundary;
interface nsIAccessible;
interface nsIPersistentProperties;
[scriptable, uuid(0f4633b1-550c-4b50-8c04-0eb1005eef2f)]
[scriptable, uuid(43d81eb0-1215-4dc4-9226-a4355bd2d20d)]
interface nsIAccessibleText : nsISupports
{
// In parameters for character offsets:
@ -27,7 +27,6 @@ interface nsIAccessibleText : nsISupports
const AccessibleTextBoundary BOUNDARY_SENTENCE_END = 4; // don't use, deprecated
const AccessibleTextBoundary BOUNDARY_LINE_START = 5;
const AccessibleTextBoundary BOUNDARY_LINE_END = 6;
const AccessibleTextBoundary BOUNDARY_ATTRIBUTE_RANGE = 7;
/**
* The current current caret offset.

View File

@ -878,186 +878,6 @@ HyperTextAccessible::FindLineBoundary(int32_t aOffset,
return -1;
}
/*
Gets the specified text relative to aBoundaryType, which means:
BOUNDARY_CHAR The character before/at/after the offset is returned.
BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
*/
nsresult
HyperTextAccessible::GetTextHelper(EGetTextType aType,
AccessibleTextBoundary aBoundaryType,
int32_t aOffset,
int32_t* aStartOffset, int32_t* aEndOffset,
nsAString& aText)
{
aText.Truncate();
NS_ENSURE_ARG_POINTER(aStartOffset);
NS_ENSURE_ARG_POINTER(aEndOffset);
*aStartOffset = *aEndOffset = 0;
int32_t offset = ConvertMagicOffset(aOffset);
if (offset < 0)
return NS_ERROR_INVALID_ARG;
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && offset > 0 &&
(aBoundaryType == BOUNDARY_LINE_START ||
aBoundaryType == BOUNDARY_LINE_END)) {
// It is the same character offset when the caret is visually at
// the very end of a line or the start of a new line. Getting text at
// the line should provide the line with the visual caret,
// otherwise screen readers will announce the wrong line as the user
// presses up or down arrow and land at the end of a line.
nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
if (frameSelection &&
frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
-- offset; // We are at the start of a line
}
}
nsSelectionAmount amount;
bool needsStart = false;
switch (aBoundaryType) {
case BOUNDARY_WORD_START:
needsStart = true;
amount = eSelectWord;
break;
case BOUNDARY_WORD_END:
amount = eSelectWord;
break;
case BOUNDARY_LINE_START:
// Newlines are considered at the end of a line. Since getting
// the BOUNDARY_LINE_START gets the text from the line-start to the next
// line-start, the newline is included at the end of the string.
needsStart = true;
amount = eSelectLine;
break;
case BOUNDARY_LINE_END:
// Newlines are considered at the end of a line. Since getting
// the BOUNDARY_END_START gets the text from the line-end to the next
//line-end, the newline is included at the beginning of the string.
amount = eSelectLine;
break;
case BOUNDARY_ATTRIBUTE_RANGE:
{
nsresult rv = GetTextAttributes(false, offset,
aStartOffset, aEndOffset, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
return GetText(*aStartOffset, *aEndOffset, aText);
}
default: // Note, sentence support is deprecated and falls through to here
return NS_ERROR_INVALID_ARG;
}
int32_t startOffset = offset + (aBoundaryType == BOUNDARY_LINE_END); // Avoid getting the previous line
int32_t endOffset = startOffset;
// Convert offsets to frame-relative
nsRefPtr<Accessible> startAcc;
nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
nullptr, getter_AddRefs(startAcc));
if (!startFrame) {
int32_t textLength = CharacterCount();
if (aBoundaryType == BOUNDARY_LINE_START && offset > 0 && offset == textLength) {
// Asking for start of line, while on last character
if (startAcc)
startFrame = startAcc->GetFrame();
}
if (!startFrame) {
return offset > textLength ? NS_ERROR_FAILURE : NS_OK;
}
else {
// We're on the last continuation since we're on the last character
startFrame = startFrame->GetLastContinuation();
}
}
int32_t finalStartOffset = 0, finalEndOffset = 0;
EWordMovementType wordMovementType = needsStart ? eStartWord : eEndWord;
nsIPresShell* presShell = mDoc->PresShell();
// If aType == eGetAt we'll change both the start and end offset from
// the original offset
if (aType == eGetAfter) {
finalStartOffset = offset;
}
else {
finalStartOffset = GetRelativeOffset(presShell, startFrame, startOffset,
startAcc,
(amount == eSelectLine ? eSelectBeginLine : amount),
eDirPrevious, needsStart,
wordMovementType);
NS_ENSURE_TRUE(finalStartOffset >= 0, NS_ERROR_FAILURE);
}
if (aType == eGetBefore) {
finalEndOffset = offset;
}
else {
// Start moving forward from the start so that we don't get
// 2 words/lines if the offset occurred on whitespace boundary
// Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
// For BOUNDARY_LINE_END, make sure we start of this line
startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END);
nsRefPtr<Accessible> endAcc;
nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
nullptr, getter_AddRefs(endAcc));
if (endAcc && endAcc->Role() == roles::STATICTEXT) {
// Static text like list bullets will ruin our forward calculation,
// since the caret cannot be in the static text. Start just after the static text.
startOffset = endOffset = finalStartOffset +
(aBoundaryType == BOUNDARY_LINE_END) +
nsAccUtils::TextLength(endAcc);
endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
nullptr, getter_AddRefs(endAcc));
}
if (!endFrame) {
return NS_ERROR_FAILURE;
}
finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc,
(amount == eSelectLine ? eSelectEndLine : amount),
eDirNext, needsStart, wordMovementType);
NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE);
if (finalEndOffset == offset) {
if (aType == eGetAt && amount == eSelectWord) {
// Fix word error for the first character in word: PeekOffset() will return the previous word when
// offset points to the first character of the word, but accessibility APIs want the current word
// that the first character is in
return GetTextHelper(eGetAfter, aBoundaryType, offset,
aStartOffset, aEndOffset, aText);
}
int32_t textLength = CharacterCount();
if (finalEndOffset < textLength) {
// This happens sometimes when current character at finalStartOffset
// is an embedded object character representing another hypertext, that
// the AT really needs to dig into separately
++ finalEndOffset;
}
}
}
*aStartOffset = finalStartOffset;
*aEndOffset = finalEndOffset;
NS_ASSERTION((finalStartOffset < offset && finalEndOffset >= offset) || aType != eGetBefore, "Incorrect results for GetTextHelper");
NS_ASSERTION((finalStartOffset <= offset && finalEndOffset > offset) || aType == eGetBefore, "Incorrect results for GetTextHelper");
GetPosAndText(finalStartOffset, finalEndOffset, &aText);
return NS_OK;
}
/**
* nsIAccessibleText impl.
*/
@ -1120,10 +940,6 @@ HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
*aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default:
return NS_ERROR_INVALID_ARG;
}
@ -1177,10 +993,6 @@ HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
*aEndOffset = FindLineBoundary(offset, eThisLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAt, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default:
return NS_ERROR_INVALID_ARG;
}
@ -1243,10 +1055,6 @@ HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
*aEndOffset = FindLineBoundary(offset, eNextLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAfter, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default:
return NS_ERROR_INVALID_ARG;
}

View File

@ -334,22 +334,8 @@ protected:
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType = eDefaultBehavior);
/*
* This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
* @param aType, eGetBefore, eGetAt, eGetAfter
* @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
* @param aOffset, offset into the hypertext to start from
* @param *aStartOffset, the resulting start offset for the returned substring
* @param *aEndOffset, the resulting end offset for the returned substring
* @param aText, the resulting substring
* @return success/failure code
*/
nsresult GetTextHelper(EGetTextType aType, AccessibleTextBoundary aBoundaryType,
int32_t aOffset, int32_t *aStartOffset, int32_t *aEndOffset,
nsAString & aText);
/**
* Used by GetTextHelper() to move backward/forward from a given point
* Used by FindOffset() to move backward/forward from a given point
* by word/line/etc.
*
* @param aPresShell the current presshell we're moving in

View File

@ -6,7 +6,6 @@ const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START;
const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END;
const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START;
const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END;
const BOUNDARY_ATTRIBUTE_RANGE = nsIAccessibleText.BOUNDARY_ATTRIBUTE_RANGE;
const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT;
const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET;
@ -585,7 +584,5 @@ function boundaryToString(aBoundaryType)
return "line start";
case BOUNDARY_LINE_END:
return "line end";
case BOUNDARY_ATTRIBUTE_RANGE:
return "attr range";
}
}

View File

@ -217,14 +217,21 @@ var gPluginHandler = {
},
handleEvent : function(event) {
let plugin = event.target;
let doc = plugin.ownerDocument;
// We're expecting the target to be a plugin.
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
let plugin;
let doc;
let eventType = event.type;
if (eventType === "PluginRemoved") {
doc = event.target;
}
else {
plugin = event.target;
doc = plugin.ownerDocument;
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
}
if (eventType == "PluginBindingAttached") {
// The plugin binding fires this event when it is created.
// As an untrusted event, ensure that this object actually has a binding
@ -304,6 +311,7 @@ var gPluginHandler = {
break;
case "PluginInstantiated":
case "PluginRemoved":
this._showClickToPlayNotification(browser);
break;
}
@ -686,18 +694,12 @@ var gPluginHandler = {
switch (aNewState) {
case "allownow":
if (aPluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
return;
}
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000;
break;
case "allowalways":
if (aPluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
return;
}
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
expireTime = Date.now() +
@ -705,25 +707,28 @@ var gPluginHandler = {
break;
case "block":
if (aPluginInfo.fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
return;
}
permission = Ci.nsIPermissionManager.PROMPT_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
expireTime = 0;
break;
// In case a plugin has already been allowed in another tab, the "continue allowing" button
// shouldn't change any permissions but should run the plugin-enablement code below.
case "continue":
break;
default:
Cu.reportError(Error("Unexpected plugin state: " + aNewState));
return;
}
let browser = aNotification.browser;
Services.perms.add(browser.currentURI, aPluginInfo.permissionString,
permission, expireType, expireTime);
if (aNewState != "continue") {
Services.perms.add(browser.currentURI, aPluginInfo.permissionString,
permission, expireType, expireTime);
if (aNewState == "block") {
return;
if (aNewState == "block") {
return;
}
}
// Manually activate the plugins that would have been automatically
@ -766,8 +771,7 @@ var gPluginHandler = {
fallbackType == plugin.PLUGIN_BLOCKLISTED;
});
let dismissed = notification ? notification.dismissed : true;
// Always show the doorhanger if the anchor is not available.
if (!isElementVisible(gURLBar) || aPrimaryPlugin)
if (aPrimaryPlugin)
dismissed = false;
let primaryPluginPermission = null;

View File

@ -755,6 +755,7 @@ var gBrowserInit = {
gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true);
gBrowser.addEventListener("PluginRemoved", gPluginHandler, true);
gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
@ -4432,6 +4433,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function (aWindow) {
return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
},
get contentWindow() {
return gBrowser.contentWindow;
}
}

View File

@ -1574,12 +1574,19 @@
this.appendChild(item);
this._items.push(item);
}
if (this.notification.options.centerActions.length == 1) {
this._setState(this._states.SINGLE);
} else if (this.notification.options.primaryPlugin) {
this._setState(this._states.MULTI_COLLAPSED);
} else {
this._setState(this._states.MULTI_EXPANDED);
switch (this.notification.options.centerActions.length) {
case 0:
PopupNotifications._dismiss();
break;
case 1:
this._setState(this._states.SINGLE);
break;
default:
if (this.notification.options.primaryPlugin) {
this._setState(this._states.MULTI_COLLAPSED);
} else {
this._setState(this._states.MULTI_EXPANDED);
}
}
]]></constructor>
<method name="_setState">
@ -1645,7 +1652,7 @@
button2 = {
label: "pluginContinue.label",
accesskey: "pluginContinue.accesskey",
action: "_cancel",
action: "_singleContinue",
default: true
};
switch (action.blocklistState) {
@ -1819,6 +1826,14 @@
this._cancel();
]]></body>
</method>
<method name="_singleContinue">
<body><![CDATA[
gPluginHandler._updatePluginPermission(this.notification,
this.notification.options.centerActions[0],
"continue");
this._cancel();
]]></body>
</method>
<method name="_multiAccept">
<body><![CDATA[
for (let item of this._items) {

View File

@ -1205,6 +1205,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function(aWindow) {
return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
},
get contentWindow() {
return Browser.selectedBrowser.contentWindow;
}
};

View File

@ -4293,7 +4293,6 @@ NS_PRINTING=1
MOZ_PDF_PRINTING=
MOZ_DISABLE_CRYPTOLEGACY=
NSS_DISABLE_DBM=
NECKO_WIFI=1
NECKO_COOKIES=1
NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
USE_ARM_KUSER=
@ -4323,11 +4322,6 @@ case "${target}" in
fi
NSS_DISABLE_DBM=1
if test -z "$gonkdir"; then
NECKO_WIFI=
else
NECKO_WIFI=1
fi
MOZ_THEME_FASTSTRIPE=1
MOZ_TREE_FREETYPE=1
MOZ_MEMORY=1
@ -8124,6 +8118,13 @@ dnl ========================================================
dnl Graphics checks.
dnl ========================================================
if test "${OS_TARGET}" = "WINNT"; then
if $PERL -e "exit($MOZ_WINSDK_MAXVER < 0x06020000)"; then
MOZ_ENABLE_DIRECT2D1_1=1
AC_SUBST(MOZ_ENABLE_DIRECT2D1_1)
fi
fi
if test "${OS_TARGET}" = "WINNT" -o "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
case "${target_cpu}" in
i*86*|x86_64|arm)
@ -8471,28 +8472,36 @@ done
dnl
dnl option to disable necko's wifi scanner
dnl
case "$OS_TARGET" in
Android)
if test -n "$gonkdir"; then
NECKO_WIFI=1
fi
;;
Darwin|SunOS|WINNT)
NECKO_WIFI=1
;;
Linux)
if test -z "$MOZ_ENABLE_DBUS"; then
AC_MSG_ERROR([Necko WiFi scanning needs DBus on your platform, remove --disable-dbus or use --disable-necko-wifi])
fi
NECKO_WIFI=1
NECKO_WIFI_DBUS=1
;;
esac
MOZ_ARG_DISABLE_BOOL(necko-wifi,
[ --disable-necko-wifi Disable necko wifi scanner],
NECKO_WIFI=,
NECKO_WIFI=1)
if test "$OS_ARCH" = "OS2"; then
dnl OS/2 implementation of Necko-WiFi support will be added in bug 506566
NECKO_WIFI=
fi
if test "$NECKO_WIFI" -a \
"$OS_ARCH" != "Linux" -a \
"$OS_ARCH" != "Darwin" -a \
"$OS_ARCH" != "SunOS" -a \
"$OS_ARCH" != "WINNT"; then
AC_MSG_ERROR([Necko WiFi scanning not supported on your platform, use --disable-necko-wifi])
fi
if test "$NECKO_WIFI"; then
AC_DEFINE(NECKO_WIFI)
_NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_WIFI"
fi
AC_SUBST(NECKO_WIFI)
AC_SUBST(NECKO_WIFI_DBUS)
dnl
dnl option to disable cookies

View File

@ -46,6 +46,7 @@ public:
// nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMComment
// Empty interface

View File

@ -169,7 +169,7 @@ public:
// WebIDL API
// Our XPCOM GetData is just fine for WebIDL
void SetData(const nsAString& aData, mozilla::ErrorResult& rv)
virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv)
{
rv = SetData(aData);
}

View File

@ -196,26 +196,36 @@ CheckPluginStopEvent::Run()
*/
class nsSimplePluginEvent : public nsRunnable {
public:
nsSimplePluginEvent(nsIContent* aContent, const nsAString &aEvent)
: mContent(aContent),
mEvent(aEvent)
{}
nsSimplePluginEvent(nsIContent* aTarget, const nsAString &aEvent)
: mTarget(aTarget)
, mDocument(aTarget->GetCurrentDoc())
, mEvent(aEvent)
{
}
nsSimplePluginEvent(nsIDocument* aTarget, const nsAString& aEvent)
: mTarget(aTarget)
, mDocument(aTarget)
, mEvent(aEvent)
{
}
~nsSimplePluginEvent() {}
NS_IMETHOD Run();
private:
nsCOMPtr<nsIContent> mContent;
nsCOMPtr<nsISupports> mTarget;
nsCOMPtr<nsIDocument> mDocument;
nsString mEvent;
};
NS_IMETHODIMP
nsSimplePluginEvent::Run()
{
LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mContent.get(),
LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mTarget.get(),
mEvent.get()));
nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
nsContentUtils::DispatchTrustedEvent(mDocument, mTarget,
mEvent, true, true);
return NS_OK;
}
@ -674,7 +684,9 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
/// would keep the docshell around, but trash the frameloader
UnloadObject();
}
nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent->GetCurrentDoc(),
NS_LITERAL_STRING("PluginRemoved"));
NS_DispatchToCurrentThread(ev);
}
nsObjectLoadingContent::nsObjectLoadingContent()

View File

@ -53,6 +53,7 @@ public:
// nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMText
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)

View File

@ -638,6 +638,7 @@ MOCHITEST_FILES_C= \
test_bug890580.html \
test_declare_stylesheet_obsolete.html \
variable_style_sheet.sjs \
test_processing_instruction_update_stylesheet.xhtml \
$(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec

View File

@ -27,8 +27,15 @@ function make_object()
o.b = true;
o.s = "hello";
o.x = { i: 10 };
o.f = function () { return 99; }
o.f = function () { return 99; };
// Doing anything with this Proxy will throw.
var throwing = new Proxy({}, new Proxy({}, {
get: function (trap) { throw trap; }
}));
return { "data": o,
"throwing": throwing,
"document": content.document
};
}

View File

@ -37,6 +37,39 @@
ok(data.b === false, "boolean property");
ok(data.s === "bye", "string property");
ok(data.x === null, "nested property");
let throwing = message.objects.throwing;
// Based on the table on:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
let tests = [
() => Object.getOwnPropertyDescriptor(throwing, 'test'),
() => Object.getOwnPropertyNames(throwing),
() => Object.defineProperty(throwing, 'test', {value: 1}),
() => delete throwing.test,
() => "test" in throwing,
() => Object.prototype.hasOwnProperty.call(throwing, 'test'),
() => throwing.test,
() => { throwing.test = 1 },
// () => { for (let prop in throwing) {} }, Bug 783829
() => { for (let prop of throwing) {} },
() => Object.keys(throwing),
() => Function.prototype.call.call(throwing),
() => new throwing,
() => Object.preventExtensions(throwing),
() => Object.freeze(throwing),
() => Object.seal(throwing),
]
for (let test of tests) {
let threw = false;
try {
test()
} catch (e) {
threw = true;
}
ok(threw, "proxy operation threw exception");
}
}
function recvAsyncMessage(message) {
@ -57,7 +90,7 @@
run_tests("inprocess");
return;
}
finish();
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="data:text/css;charset=UTF-8,p{color:red}" type="text/css"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=888864
-->
<head>
<title>Test for Bug 888864</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
<![CDATA[
/** Test for Bug 888864 **/
SimpleTest.waitForExplicitFinish();
function changeColorAndRun(callback) {
var piNode = document.firstChild;
piNode.data = 'href="data:text/css;charset=UTF-8,p{color:green}" type="text/css"';
piNode.addEventListener("load", callback);
}
function runTest() {
var previousColor = window.getComputedStyle(document.getElementById("display")).
getPropertyValue("color");
changeColorAndRun(function() {
var afterChange = window.getComputedStyle(document.getElementById("display")).
getPropertyValue("color");
isnot(previousColor, afterChange,
"Color of the p element should change.");
SimpleTest.finish();
});
}
]]>
</script>
</head>
<body onload="runTest();">
<p id="display">This changes color</p>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=888864">Mozilla Bug 888864</a>
<pre id="test">
</pre>
</body>
</html>

View File

@ -3,6 +3,8 @@
* 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/. */
Components.utils.import("resource://testing-common/httpd.js");
const nsIDocumentEncoder = Components.interfaces.nsIDocumentEncoder;
const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;

View File

@ -4,9 +4,10 @@
Components.utils.import("resource://testing-common/httpd.js");
var server = null;
var server = new HttpServer();
server.start(-1);
const SERVER_PORT = 4444;
const SERVER_PORT = server.identity.primaryPort;
const HTTP_BASE = "http://localhost:" + SERVER_PORT;
const redirectPath = "/redirect";
const headerCheckPath = "/headerCheck";
@ -36,10 +37,8 @@ function headerCheckHandler(metadata, response) {
}
function run_test() {
var server = new HttpServer();
server.registerPathHandler(redirectPath, redirectHandler);
server.registerPathHandler(headerCheckPath, headerCheckHandler);
server.start(SERVER_PORT);
do_test_pending();
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]

View File

@ -5,7 +5,6 @@ const Cu = Components.utils;
const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import("resource://testing-common/httpd.js");
var httpserv = null;

View File

@ -10,16 +10,14 @@ const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
// load the HTTP server
Cu.import("resource://testing-common/httpd.js");
var httpServer = new HttpServer();
httpServer.start(-1);
var testsToFinish = 0;
const REPORT_SERVER_PORT = 9000;
const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
const REPORT_SERVER_URI = "http://localhost";
const REPORT_SERVER_PATH = "/report";
var httpServer = null;
var testsToFinish = 0;
/**
* Construct a callback that listens to a report submission and either passes
* or fails a test based on what it gets.
@ -99,9 +97,6 @@ function run_test() {
":" + REPORT_SERVER_PORT +
"/foo/self");
httpServer = new HttpServer();
httpServer.start(REPORT_SERVER_PORT);
// test that inline script violations cause a report.
makeTest(0, {"blocked-uri": "self"}, false,
function(csp) {

View File

@ -11,13 +11,11 @@ const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
// load the HTTP server
Cu.import("resource://testing-common/httpd.js");
var httpServer = new HttpServer();
httpServer.start(-1);
const POLICY_FROM_URI = "default-src 'self'; img-src *";
const POLICY_PORT = 9000;
const POLICY_PORT = httpServer.identity.primaryPort;
const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy";
const POLICY_URI_RELATIVE = "/policy";
@ -1048,7 +1046,6 @@ function run_test() {
}
//server.registerDirectory("/", nsILocalFileForBasePath);
httpServer.registerPathHandler("/policy", policyresponder);
httpServer.start(POLICY_PORT);
for(let i in tests) {
tests[i]();

View File

@ -8,6 +8,7 @@ tail =
[test_csputils.js]
[test_cspreports.js]
[test_error_codes.js]
run-sequentially = Hardcoded 4444 port.
[test_thirdpartyutil.js]
[test_xhr_standalone.js]
[test_xmlserializer.js]

View File

@ -24,6 +24,15 @@ public:
virtual ~WebGL1Context();
// -------------------------------------------------------------------------
// IMPLEMENT WebGLContext
virtual bool IsWebGL2() const MOZ_OVERRIDE
{
return false;
}
// -------------------------------------------------------------------------
// IMPLEMENT nsWrapperCache

View File

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#include "WebGL2Context.h"
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
#include "mozilla/Telemetry.h"
using namespace mozilla;
// -----------------------------------------------------------------------------
// CONSTRUCTOR & DESTRUCTOR
WebGL2Context::WebGL2Context()
: WebGLContext()
{
MOZ_ASSERT(IsSupported(), "not supposed to create a WebGL2Context"
"context when not supported");
}
WebGL2Context::~WebGL2Context()
{
}
// -----------------------------------------------------------------------------
// STATIC FUNCTIONS
bool
WebGL2Context::IsSupported()
{
#ifdef RELEASE_BUILD
return false;
#else
return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
#endif
}
WebGL2Context*
WebGL2Context::Create()
{
#ifdef RELEASE_BUILD
return nullptr;
#else
return new WebGL2Context();
#endif
}
// -----------------------------------------------------------------------------
// IMPLEMENT nsWrapperCache
JSObject*
WebGL2Context::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
{
return dom::WebGL2RenderingContextBinding::Wrap(cx, scope, this);
}

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#ifndef WEBGL2CONTEXT_H_
#define WEBGL2CONTEXT_H_
#include "WebGLContext.h"
namespace mozilla {
class WebGL2Context
: public WebGLContext
{
// -----------------------------------------------------------------------------
// PUBLIC
public:
// -------------------------------------------------------------------------
// DESTRUCTOR
virtual ~WebGL2Context();
// -------------------------------------------------------------------------
// STATIC FUNCTIONS
static bool IsSupported();
static WebGL2Context* Create();
// -------------------------------------------------------------------------
// IMPLEMENT WebGLContext
virtual bool IsWebGL2() const MOZ_OVERRIDE
{
return true;
}
// -------------------------------------------------------------------------
// IMPLEMENT nsWrapperCache
virtual JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
// -----------------------------------------------------------------------------
// PRIVATE
private:
// -------------------------------------------------------------------------
// CONSTRUCTOR
WebGL2Context();
};
} // namespace mozilla
#endif

View File

@ -153,6 +153,8 @@ public:
virtual JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> scope) = 0;
virtual bool IsWebGL2() const = 0;
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
// nsICanvasRenderingContextInternal

View File

@ -2113,7 +2113,18 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv)
case LOCAL_GL_RENDERER:
return StringValue(cx, "Mozilla", rv);
case LOCAL_GL_VERSION:
return StringValue(cx, "WebGL 1.0", rv);
{
const char* version = 0;
if (IsWebGL2()) {
version = "WebGL 2.0";
} else {
version = "WebGL 1.0";
}
MOZ_ASSERT(version != 0);
return StringValue(cx, version, rv);
}
case LOCAL_GL_SHADING_LANGUAGE_VERSION:
return StringValue(cx, "WebGL GLSL ES 1.0", rv);

View File

@ -4,7 +4,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMWebGLRenderingContext.h"
#include "WebGL2Context.h"
#define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; }
DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext)
WebGL2Context * WebGL2Context::Create()
{
return nullptr;
}

View File

@ -29,6 +29,7 @@ if CONFIG['MOZ_WEBGL']:
'WebGLActiveInfo.cpp',
'WebGLBuffer.cpp',
'WebGL1Context.cpp',
'WebGL2Context.cpp',
'WebGLContext.cpp',
'WebGLContextGL.cpp',
'WebGLContextUtils.cpp',

View File

@ -31,6 +31,8 @@
#include "nsNetUtil.h"
#include "nsStreamUtils.h"
#include "../canvas/src/WebGL2Context.h"
using namespace mozilla::layers;
NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
@ -682,6 +684,20 @@ HTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
ctx.forget(aContext);
return NS_OK;
}
else if (WebGL2Context::IsSupported() &&
aContextId.EqualsLiteral("experimental-webgl2"))
{
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
nsRefPtr<WebGL2Context> ctx = WebGL2Context::Create();
if (ctx == nullptr) {
return NS_ERROR_NOT_IMPLEMENTED;
}
ctx->SetCanvasElement(this);
ctx.forget(aContext);
return NS_OK;
}
NS_ConvertUTF16toUTF8 ctxId(aContextId);

View File

@ -401,6 +401,9 @@ void
AudioContext::UnregisterPannerNode(PannerNode* aNode)
{
mPannerNodes.RemoveEntry(aNode);
if (mListener) {
mListener->UnregisterPannerNode(aNode);
}
}
void

View File

@ -52,6 +52,11 @@ AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
UpdatePannersVelocity();
}
void AudioListener::UnregisterPannerNode(PannerNode* aPannerNode)
{
mPanners.RemoveElement(aPannerNode);
}
void
AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
{

View File

@ -127,6 +127,7 @@ public:
}
void RegisterPannerNode(PannerNode* aPannerNode);
void UnregisterPannerNode(PannerNode* aPannerNode);
private:
void SendDoubleParameterToStream(uint32_t aIndex, double aValue);

View File

@ -212,6 +212,14 @@ PannerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
return PannerNodeBinding::Wrap(aCx, aScope, this);
}
void PannerNode::DestroyMediaStream()
{
if (Context()) {
Context()->UnregisterPannerNode(this);
}
AudioNode::DestroyMediaStream();
}
// Those three functions are described in the spec.
float
PannerNodeEngine::LinearGainFunction(float aDistance)

View File

@ -31,9 +31,12 @@ public:
explicit PannerNode(AudioContext* aContext);
virtual ~PannerNode();
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
virtual void DestroyMediaStream() MOZ_OVERRIDE;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode)

View File

@ -27,6 +27,7 @@ MOCHITEST_FILES := \
test_bug867203.html \
test_bug875221.html \
test_bug875402.html \
test_bug894150.html \
test_analyserNode.html \
test_AudioBuffer.html \
test_AudioContext.html \

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test whether we can create an AudioContext interface</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
var ac = AudioContext();
ac.createPanner();
var listener = ac.listener;
SpecialPowers.forceGC();
SpecialPowers.forceCC();
listener.setOrientation(0, 0, -1, 0, 0, 0);
ok(true, "No crashes should happen!");
</script>
</body>

View File

@ -48,6 +48,7 @@ public:
// nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMText
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)

View File

@ -30,6 +30,7 @@ public:
// nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMProcessingInstruction
NS_DECL_NSIDOMPROCESSINGINSTRUCTION

View File

@ -63,6 +63,16 @@ public:
// nsStyleLinkElement
NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE;
virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv) MOZ_OVERRIDE
{
nsGenericDOMDataNode::SetData(aData, rv);
if (rv.Failed()) {
return;
}
UpdateStyleSheetInternal(nullptr, true);
}
using ProcessingInstruction::SetData; // Prevent hiding overloaded virtual function.
protected:
nsCOMPtr<nsIURI> mOverriddenBaseURI;

View File

@ -12,7 +12,7 @@
interface nsIDocShellTreeItem;
[scriptable, uuid(932f9f93-8e21-4728-a527-cafc64b4d831)]
[scriptable, uuid(6cd89e60-1060-491e-8c31-ce969435ec56)]
interface nsIDocShellTreeOwner : nsISupports
{
/*
@ -64,6 +64,9 @@ interface nsIDocShellTreeOwner : nsISupports
*/
readonly attribute nsIDocShellTreeItem primaryContentShell;
[implicit_jscontext]
readonly attribute jsval contentWindow;
/*
Tells the tree owner to size its window or parent window in such a way
that the shell passed along will be the size specified.

View File

@ -84,7 +84,7 @@ WebappsRegistry.prototype = {
}
} catch(e) {
throw new Components.Exception(
"INVALID_URL: '" + aURL, Cr.NS_ERROR_FAILURE
"INVALID_URL: '" + aURL + "'", Cr.NS_ERROR_FAILURE
);
}

View File

@ -3284,7 +3284,7 @@ nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
mAudioContexts.AppendElement(aAudioContext);
nsIDocShell* docShell = GetDocShell();
if (docShell && !docShell->GetAllowMedia()) {
if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
aAudioContext->Mute();
}
}
@ -3293,7 +3293,9 @@ void
nsPIDOMWindow::MuteAudioContexts()
{
for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
mAudioContexts[i]->Mute();
if (!mAudioContexts[i]->IsOffline()) {
mAudioContexts[i]->Mute();
}
}
}
@ -3301,7 +3303,9 @@ void
nsPIDOMWindow::UnmuteAudioContexts()
{
for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
mAudioContexts[i]->Unmute();
if (!mAudioContexts[i]->IsOffline()) {
mAudioContexts[i]->Unmute();
}
}
}
@ -3618,6 +3622,28 @@ nsGlobalWindow::GetContent(nsIDOMWindow** aContent)
return NS_OK;
}
NS_IMETHODIMP
nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::Value* aVal)
{
nsCOMPtr<nsIDOMWindow> content;
nsresult rv = GetContent(getter_AddRefs(content));
NS_ENSURE_SUCCESS(rv, rv);
if (content || !nsContentUtils::IsCallerChrome() || !IsChromeWindow()) {
JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
if (content && global) {
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
return nsContentUtils::WrapNative(aCx, global, content, aVal,
getter_AddRefs(wrapper));
}
return NS_ERROR_FAILURE;
}
// Something tries to get .content on a ChromeWindow, try to fetch the CPOW.
nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
return treeOwner->GetContentWindow(aCx, aVal);
}
NS_IMETHODIMP
nsGlobalWindow::GetPrompter(nsIPrompt** aPrompt)

View File

@ -1287,8 +1287,16 @@ DOMInterfaces = {
},
'WebGLRenderingContext': {
'nativeType': 'mozilla::WebGL1Context',
'headerFile': 'WebGL1Context.h',
'nativeType': 'mozilla::WebGLContext',
'headerFile': 'WebGLContext.h',
'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension',
'getAttachedShaders' ],
'implicitJSContext': [ 'getSupportedExtensions' ],
},
'WebGL2RenderingContext': {
'nativeType': 'mozilla::WebGLContext',
'headerFile': 'WebGLContext.h',
'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension',
'getAttachedShaders' ],
'implicitJSContext': [ 'getSupportedExtensions' ],

View File

@ -9,7 +9,7 @@ interface nsIDOMWindow;
interface nsIURI;
interface nsIFrameLoaderOwner;
[scriptable, uuid(3ab89888-eb41-4dc8-b347-115555f47c80)]
[scriptable, uuid(e420bd32-b8c4-4b47-8cca-09e0bddbb0c3)]
/**
* The C++ source has access to the browser script source through
@ -92,5 +92,12 @@ interface nsIBrowserDOMWindow : nsISupports
* currently open tab in this toplevel browser window.
*/
boolean isTabContentWindow(in nsIDOMWindow aWindow);
/**
* The contentWindow property of the currently selected browser.
* This is used to implement .content in remote-Firefox.
*/
readonly attribute jsval contentWindow;
};

View File

@ -5,7 +5,7 @@
#include "domstubs.idl"
[scriptable, uuid(35b653f4-e679-4843-8391-89cb2f5a9ba4)]
[scriptable, uuid(f28c92a2-302a-4448-b589-46af599de352)]
interface nsIDOMJSWindow : nsISupports
{
void dump(in DOMString str);
@ -66,4 +66,7 @@ interface nsIDOMJSWindow : nsISupports
* This property is "replaceable" in JavaScript.
*/
readonly attribute nsIDOMWindow frames;
[implicit_jscontext, binaryname(ScriptableContent)]
readonly attribute jsval content;
};

View File

@ -25,7 +25,7 @@ interface nsIVariant;
* @see <http://www.whatwg.org/html/#window>
*/
[scriptable, uuid(be62660a-e3f6-409c-a4a9-378364a9526f)]
[scriptable, uuid(db8ea3c8-6997-460a-8715-0a1cbf20f15d)]
interface nsIDOMWindow : nsISupports
{
// the current browsing context
@ -360,7 +360,7 @@ interface nsIDOMWindow : nsISupports
void sizeToContent();
/* [replaceable] content */
readonly attribute nsIDOMWindow content;
[noscript] readonly attribute nsIDOMWindow content;
/* [replaceable] prompter */
[noscript] readonly attribute nsIPrompt prompter;

View File

@ -1181,10 +1181,13 @@ PreloadSlowThings()
}
bool
ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID)
ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
const nsCString& name, const nsCString& UAName)
{
mAppInfo.version.Assign(version);
mAppInfo.buildID.Assign(buildID);
mAppInfo.name.Assign(name);
mAppInfo.UAName.Assign(UAName);
// If we're part of the mozbrowser machinery, go ahead and start
// preloading things. We can only do this for mozbrowser because
// PreloadSlowThings() may set the docshell of the first TabChild

View File

@ -60,6 +60,8 @@ public:
{
nsCString version;
nsCString buildID;
nsCString name;
nsCString UAName;
};
bool Init(MessageLoop* aIOLoop,
@ -194,7 +196,8 @@ public:
virtual bool RecvGarbageCollect();
virtual bool RecvCycleCollect();
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID);
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
const nsCString& name, const nsCString& UAName);
virtual bool RecvLastPrivateDocShellDestroyed();

View File

@ -1133,9 +1133,11 @@ ContentParent::ContentParent(mozIApplication* aApp,
if (gAppData) {
nsCString version(gAppData->version);
nsCString buildID(gAppData->buildID);
nsCString name(gAppData->name);
nsCString UAName(gAppData->UAName);
//Sending all information to content process
unused << SendAppInfo(version, buildID);
unused << SendAppInfo(version, buildID, name, UAName);
}
}

View File

@ -258,7 +258,7 @@ child:
*/
ActivateA11y();
AppInfo(nsCString version, nsCString buildID);
AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName);
// Notify child that last-pb-context-exited notification was observed
LastPrivateDocShellDestroyed();

View File

@ -253,6 +253,9 @@
},
openDialog: function(aType, aName, aFeatures, aArguments, aFrameElement) {
alert(aType + ", " + aName + ", " + aFeatures + ", " + aArguments + ", " + aFrameElement);
},
get contentWindow() {
return null;
}
}

View File

@ -1192,9 +1192,11 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs
uint32_t flags = 0;
if (!CallNP_Initialize(flags, error)) {
mShutdown = true;
return NS_ERROR_FAILURE;
}
else if (*error != NPERR_NO_ERROR) {
mShutdown = true;
return NS_OK;
}
@ -1220,8 +1222,14 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
flags |= kAllowAsyncDrawing;
#endif
if (!CallNP_Initialize(flags, error))
if (!CallNP_Initialize(flags, error)) {
mShutdown = true;
return NS_ERROR_FAILURE;
}
if (*error != NPERR_NO_ERROR) {
mShutdown = true;
return NS_OK;
}
#if defined XP_WIN
// Send the info needed to join the chrome process's audio session to the

View File

@ -0,0 +1,12 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/.
*
* This IDL depend on WebGLRenderingContext.webidl
*/
interface WebGL2RenderingContext : WebGLRenderingContext {
};

View File

@ -389,6 +389,7 @@ endif
ifdef MOZ_WEBGL
webidl_files += \
WebGLRenderingContext.webidl \
WebGL2RenderingContext.webidl \
$(NULL)
endif

View File

@ -353,7 +353,7 @@ nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
{
NS_ENSURE_ARG_POINTER(aShell);
if(mTreeOwner)
if (mTreeOwner)
return mTreeOwner->GetPrimaryContentShell(aShell);
*aShell = (mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell);
@ -362,6 +362,15 @@ nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
return NS_OK;
}
NS_IMETHODIMP
nsDocShellTreeOwner::GetContentWindow(JSContext* aCx, JS::Value* aVal)
{
if (mTreeOwner)
return mTreeOwner->GetContentWindow(aCx, aVal);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
int32_t aCX, int32_t aCY)

View File

@ -35,6 +35,8 @@ typedef _cairo_scaled_font cairo_scaled_font_t;
struct ID3D10Device1;
struct ID3D10Texture2D;
struct ID3D11Device;
struct ID2D1Device;
struct IDWriteRenderingParams;
class GrContext;
@ -989,6 +991,11 @@ public:
static void SetDirect3D10Device(ID3D10Device1 *aDevice);
static ID3D10Device1 *GetDirect3D10Device();
#ifdef USE_D2D1_1
static void SetDirect3D11Device(ID3D11Device *aDevice);
static ID3D11Device *GetDirect3D11Device();
static ID2D1Device *GetD2D1Device();
#endif
static TemporaryRef<GlyphRenderingOptions>
CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
@ -999,6 +1006,10 @@ public:
private:
static ID3D10Device1 *mD3D10Device;
#ifdef USE_D2D1_1
static ID3D11Device *mD3D11Device;
static ID2D1Device *mD2D1Device;
#endif
#endif
static DrawEventRecorder *mRecorder;

916
gfx/2d/DrawTargetD2D1.cpp Normal file
View File

@ -0,0 +1,916 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#include "DrawTargetD2D1.h"
#include "DrawTargetD2D.h"
#include "GradientStopsD2D.h"
#include "SourceSurfaceD2D1.h"
#include "SourceSurfaceD2D.h"
#include "RadialGradientEffectD2D1.h"
#include "HelpersD2D.h"
#include "Tools.h"
using namespace std;
namespace mozilla {
namespace gfx {
uint64_t DrawTargetD2D1::mVRAMUsageDT;
uint64_t DrawTargetD2D1::mVRAMUsageSS;
ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
ID2D1Factory1 *D2DFactory1()
{
return DrawTargetD2D1::factory();
}
DrawTargetD2D1::DrawTargetD2D1()
: mClipsArePushed(false)
{
}
DrawTargetD2D1::~DrawTargetD2D1()
{
PopAllClips();
mDC->EndDraw();
}
TemporaryRef<SourceSurface>
DrawTargetD2D1::Snapshot()
{
if (mSnapshot) {
return mSnapshot;
}
PopAllClips();
mDC->Flush();
mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
return mSnapshot;
}
void
DrawTargetD2D1::Flush()
{
mDC->Flush();
}
void
DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions)
{
Matrix mat;
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP);
if (!image) {
gfxWarning() << *this << ": Unable to get D2D image for surface.";
return;
}
PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
D2D1_RECT_F samplingBounds;
if (aSurfOptions.mSamplingBounds == SAMPLING_BOUNDED) {
samplingBounds = D2DRect(aSource);
} else {
samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height));
}
Float xScale = aDest.width / aSource.width;
Float yScale = aDest.height / aSource.height;
RefPtr<ID2D1ImageBrush> brush;
// Here we scale the source pattern up to the size and position where we want
// it to be.
Matrix transform;
transform.Translate(aDest.x, aDest.y);
transform.Scale(xScale, yScale);
mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds),
D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
byRef(brush));
mDC->FillRectangle(D2DRect(aDest), brush);
FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
}
void
DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator)
{
MarkChanged();
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
Matrix mat;
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP);
if (!mat.IsIdentity()) {
gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces.";
return;
}
// Step 1, create the shadow effect.
RefPtr<ID2D1Effect> shadowEffect;
mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect));
shadowEffect->SetInput(0, image);
shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
// Step 2, move the shadow effect into place.
RefPtr<ID2D1Effect> affineTransformEffect;
mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect));
affineTransformEffect->SetInputEffect(0, shadowEffect);
D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y);
affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix);
// Step 3, create an effect that combines shadow and bitmap in one image.
RefPtr<ID2D1Effect> compositeEffect;
mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect));
compositeEffect->SetInputEffect(0, affineTransformEffect);
compositeEffect->SetInput(1, image);
compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator));
D2D1_POINT_2F surfPoint = D2DPoint(aDest);
mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
}
void
DrawTargetD2D1::ClearRect(const Rect &aRect)
{
MarkChanged();
mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
mDC->Clear();
mDC->PopAxisAlignedClip();
}
void
DrawTargetD2D1::MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
const DrawOptions &aOptions)
{
RefPtr<ID2D1Bitmap> bitmap;
RefPtr<ID2D1Image> image = GetImageForSurface(aMask, Matrix(), EXTEND_CLAMP);
PrepareForDrawing(aOptions.mCompositionOp, aSource);
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
IntSize size = aMask->GetSize();
Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
image->QueryInterface((ID2D1Bitmap**)&bitmap);
if (!bitmap) {
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
return;
}
Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
FinalizeDrawing(aOptions.mCompositionOp, aSource);
}
void
DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination)
{
MarkChanged();
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
Matrix mat;
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP);
if (!mat.IsIdentity()) {
gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface.";
return;
}
mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y),
Float(aSourceRect.XMost()), Float(aSourceRect.YMost())),
D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
}
void
DrawTargetD2D1::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
mDC->FillRectangle(D2DRect(aRect), brush);
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions)
{
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions)
{
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions)
{
if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
return;
}
const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
return;
}
const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
mDC->FillGeometry(d2dPath->mGeometry, brush);
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions,
const GlyphRenderingOptions *aRenderingOptions)
{
if (aFont->GetType() != FONT_DWRITE) {
gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
return;
}
ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
IDWriteRenderingParams *params = nullptr;
if (aRenderingOptions) {
if (aRenderingOptions->GetType() != FONT_DWRITE) {
gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
// This should never happen.
MOZ_ASSERT(false);
} else {
params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderingOptions)->mParams;
}
}
AntialiasMode aaMode = font->GetDefaultAAMode();
if (aOptions.mAntialiasMode != AA_DEFAULT) {
aaMode = aOptions.mAntialiasMode;
}
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
bool forceClearType = false;
if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA &&
aOptions.mCompositionOp == OP_OVER && aaMode == AA_SUBPIXEL) {
forceClearType = true;
}
D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
switch (aaMode) {
case AA_NONE:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
break;
case AA_GRAY:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
break;
case AA_SUBPIXEL:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
break;
default:
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
}
if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
mFormat != FORMAT_B8G8R8X8 && !forceClearType) {
d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
}
mDC->SetTextAntialiasMode(d2dAAMode);
if (params != mTextRenderingParams) {
mDC->SetTextRenderingParams(params);
mTextRenderingParams = params;
}
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
AutoDWriteGlyphRun autoRun;
DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
if (brush) {
mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
}
FinalizeDrawing(aOptions.mCompositionOp, aPattern);
}
void
DrawTargetD2D1::Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions)
{
PrepareForDrawing(aOptions.mCompositionOp, aSource);
RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f);
mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
D2D1::IdentityMatrix(),
1.0f, mask),
nullptr);
Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
Matrix mat = mTransform;
mat.Invert();
mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
mDC->PopLayer();
FinalizeDrawing(aOptions.mCompositionOp, aSource);
}
void
DrawTargetD2D1::PushClip(const Path *aPath)
{
if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
return;
}
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
PushedClip clip;
clip.mTransform = D2DMatrix(mTransform);
clip.mPath = pathD2D;
pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
mPushedClips.push_back(clip);
// The transform of clips is relative to the world matrix, since we use the total
// transform for the clips, make the world matrix identity.
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
if (mClipsArePushed) {
PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
}
}
void
DrawTargetD2D1::PushClipRect(const Rect &aRect)
{
if (!mTransform.IsRectilinear()) {
// Whoops, this isn't a rectangle in device space, Direct2D will not deal
// with this transform the way we want it to.
// See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
pathBuilder->MoveTo(aRect.TopLeft());
pathBuilder->LineTo(aRect.TopRight());
pathBuilder->LineTo(aRect.BottomRight());
pathBuilder->LineTo(aRect.BottomLeft());
pathBuilder->Close();
RefPtr<Path> path = pathBuilder->Finish();
return PushClip(path);
}
PushedClip clip;
Rect rect = mTransform.TransformBounds(aRect);
IntRect intRect;
clip.mIsPixelAligned = rect.ToIntRect(&intRect);
// Do not store the transform, just store the device space rectangle directly.
clip.mBounds = D2DRect(rect);
mPushedClips.push_back(clip);
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
if (mClipsArePushed) {
mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
}
}
void
DrawTargetD2D1::PopClip()
{
if (mClipsArePushed) {
if (mPushedClips.back().mPath) {
mDC->PopLayer();
} else {
mDC->PopAxisAlignedClip();
}
}
mPushedClips.pop_back();
}
TemporaryRef<SourceSurface>
DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat) const
{
RefPtr<ID2D1Bitmap1> bitmap;
HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride,
D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)),
byRef(bitmap));
if (!bitmap) {
return nullptr;
}
return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize);
}
TemporaryRef<DrawTarget>
DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
if (!dt->Init(aSize, aFormat)) {
return nullptr;
}
return dt;
}
TemporaryRef<PathBuilder>
DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
{
RefPtr<ID2D1PathGeometry> path;
HRESULT hr = factory()->CreatePathGeometry(byRef(path));
if (FAILED(hr)) {
gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hr;
return nullptr;
}
RefPtr<ID2D1GeometrySink> sink;
hr = path->Open(byRef(sink));
if (FAILED(hr)) {
gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hr;
return nullptr;
}
if (aFillRule == FILL_WINDING) {
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
}
return new PathBuilderD2D(sink, path, aFillRule);
}
TemporaryRef<GradientStops>
DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
{
D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
for (uint32_t i = 0; i < aNumStops; i++) {
stops[i].position = rawStops[i].offset;
stops[i].color = D2DColor(rawStops[i].color);
}
RefPtr<ID2D1GradientStopCollection> stopCollection;
HRESULT hr =
mDC->CreateGradientStopCollection(stops, aNumStops,
D2D1_GAMMA_2_2, D2DExtend(aExtendMode),
byRef(stopCollection));
delete [] stops;
if (FAILED(hr)) {
gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hr;
return nullptr;
}
return new GradientStopsD2D(stopCollection);
}
bool
DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
{
HRESULT hr;
hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC));
if (FAILED(hr)) {
gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext.";
return false;
}
D2D1_BITMAP_PROPERTIES1 props;
props.dpiX = 96;
props.dpiY = 96;
props.pixelFormat = D2DPixelFormat(aFormat);
props.colorContext = nullptr;
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap));
if (FAILED(hr)) {
gfxWarning() << *this << ": Error " << hr << " failed to create new CommandList.";
return false;
}
mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
mDC->SetTarget(mBitmap);
mDC->BeginDraw();
mFormat = aFormat;
mSize = aSize;
return true;
}
/**
* Private helpers.
*/
uint32_t
DrawTargetD2D1::GetByteSize() const
{
return mSize.width * mSize.height * BytesPerPixel(mFormat);
}
ID2D1Factory1*
DrawTargetD2D1::factory()
{
if (mFactory) {
return mFactory;
}
HRESULT hr = D2DFactory()->QueryInterface((ID2D1Factory1**)&mFactory);
if (FAILED(hr)) {
return nullptr;
}
RadialGradientEffectD2D1::Register(mFactory);
return mFactory;
}
void
DrawTargetD2D1::MarkChanged()
{
if (mSnapshot) {
if (mSnapshot->hasOneRef()) {
// Just destroy it, since no-one else knows about it.
mSnapshot = nullptr;
} else {
mSnapshot->DrawTargetWillChange();
// The snapshot will no longer depend on this target.
MOZ_ASSERT(!mSnapshot);
}
}
if (mDependentTargets.size()) {
// Copy mDependentTargets since the Flush()es below will modify it.
TargetSet tmpTargets = mDependentTargets;
for (TargetSet::iterator iter = tmpTargets.begin();
iter != tmpTargets.end(); iter++) {
(*iter)->Flush();
}
// The Flush() should have broken all dependencies on this target.
MOZ_ASSERT(!mDependentTargets.size());
}
}
void
DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
{
MarkChanged();
// It's important to do this before FlushTransformToDC! As this will cause
// the transform to become dirty.
if (!mClipsArePushed) {
mClipsArePushed = true;
PushClipsToDC(mDC);
}
FlushTransformToDC();
if (aOp == OP_OVER && IsPatternSupportedByD2D(aPattern)) {
return;
}
mDC->SetTarget(mTempBitmap);
mDC->Clear(D2D1::ColorF(0, 0));
}
void
DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
{
bool patternSupported = IsPatternSupportedByD2D(aPattern);
if (aOp == OP_OVER && patternSupported) {
return;
}
RefPtr<ID2D1Image> image;
mDC->GetTarget(byRef(image));
mDC->SetTarget(mBitmap);
if (patternSupported) {
mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
return;
}
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
RefPtr<ID2D1Effect> radialGradientEffect;
mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect));
const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION,
static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection);
radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y));
radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y));
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1);
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform));
radialGradientEffect->SetInput(0, image);
mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
}
void
DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
{
if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) {
aSource->mDrawTarget->mDependentTargets.insert(this);
mDependingOnTargets.insert(aSource->mDrawTarget);
}
}
void
DrawTargetD2D1::PopAllClips()
{
if (mClipsArePushed) {
PopClipsFromDC(mDC);
mClipsArePushed = false;
}
}
void
DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
{
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
iter != mPushedClips.end(); iter++) {
if (iter->mPath) {
PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform);
} else {
mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
}
}
}
void
DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
{
for (int i = mPushedClips.size() - 1; i >= 0; i--) {
if (mPushedClips[i].mPath) {
aDC->PopLayer();
} else {
aDC->PopAxisAlignedClip();
}
}
}
TemporaryRef<ID2D1Brush>
DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
{
if (!IsPatternSupportedByD2D(aPattern)) {
RefPtr<ID2D1SolidColorBrush> colBrush;
mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
return colBrush;
}
if (aPattern.GetType() == PATTERN_COLOR) {
RefPtr<ID2D1SolidColorBrush> colBrush;
Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g,
color.b, color.a),
D2D1::BrushProperties(aAlpha),
byRef(colBrush));
return colBrush;
} else if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) {
RefPtr<ID2D1LinearGradientBrush> gradBrush;
const LinearGradientPattern *pat =
static_cast<const LinearGradientPattern*>(&aPattern);
GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
if (!stops) {
gfxDebug() << "No stops specified for gradient pattern.";
return nullptr;
}
if (pat->mBegin == pat->mEnd) {
RefPtr<ID2D1SolidColorBrush> colBrush;
uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
mDC->CreateSolidColorBrush(d2dStops.back().color,
D2D1::BrushProperties(aAlpha),
byRef(colBrush));
return colBrush;
}
mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
D2DPoint(pat->mEnd)),
D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
stops->mStopCollection,
byRef(gradBrush));
return gradBrush;
} else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
RefPtr<ID2D1RadialGradientBrush> gradBrush;
const RadialGradientPattern *pat =
static_cast<const RadialGradientPattern*>(&aPattern);
GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
if (!stops) {
gfxDebug() << "No stops specified for gradient pattern.";
return nullptr;
}
// This will not be a complex radial gradient brush.
mDC->CreateRadialGradientBrush(
D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2),
D2DPoint(pat->mCenter1 - pat->mCenter2),
pat->mRadius2, pat->mRadius2),
D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
stops->mStopCollection,
byRef(gradBrush));
return gradBrush;
} else if (aPattern.GetType() == PATTERN_SURFACE) {
const SurfacePattern *pat =
static_cast<const SurfacePattern*>(&aPattern);
if (!pat->mSurface) {
gfxDebug() << "No source surface specified for surface pattern";
return nullptr;
}
Matrix mat = pat->mMatrix;
RefPtr<ID2D1ImageBrush> imageBrush;
RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode);
mDC->CreateImageBrush(image,
D2D1::ImageBrushProperties(D2D1::RectF(0, 0,
Float(pat->mSurface->GetSize().width),
Float(pat->mSurface->GetSize().height)),
D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode),
D2DInterpolationMode(pat->mFilter)),
D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
byRef(imageBrush));
return imageBrush;
}
gfxWarning() << "Invalid pattern type detected.";
return nullptr;
}
TemporaryRef<ID2D1Image>
DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
ExtendMode aExtendMode)
{
RefPtr<ID2D1Image> image;
switch (aSurface->GetType()) {
case SURFACE_D2D1_1_IMAGE:
{
SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
image = surf->GetImage();
AddDependencyOnSource(surf);
}
break;
default:
{
RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
if (!dataSurf) {
gfxWarning() << "Invalid surface type.";
return nullptr;
}
image = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode,
aSourceTransform, mDC);
return image;
}
break;
}
return image;
}
void
DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
{
D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) {
options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
}
mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry,
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
1.0, nullptr, options), nullptr);
}
}
}

214
gfx/2d/DrawTargetD2D1.h Normal file
View File

@ -0,0 +1,214 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef MOZILLA_GFX_DRAWTARGETD2D1_H_
#define MOZILLA_GFX_DRAWTARGETD2D1_H_
#include "2D.h"
#include <d3d11.h>
#include <d2d1_1.h>
#include "PathD2D.h"
#include "HelpersD2D.h"
#include <vector>
#include <sstream>
#ifdef _MSC_VER
#include <hash_set>
#else
#include <unordered_set>
#endif
struct IDWriteFactory;
namespace mozilla {
namespace gfx {
class SourceSurfaceD2D1;
class GradientStopsD2D;
class ScaledFontDWrite;
const int32_t kLayerCacheSize1 = 5;
class DrawTargetD2D1 : public DrawTarget
{
public:
DrawTargetD2D1();
virtual ~DrawTargetD2D1();
virtual BackendType GetType() const { return BACKEND_DIRECT2D1_1; }
virtual TemporaryRef<SourceSurface> Snapshot();
virtual IntSize GetSize() { return mSize; }
virtual void Flush();
virtual void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions);
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator);
virtual void ClearRect(const Rect &aRect);
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
const DrawOptions &aOptions = DrawOptions());
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination);
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions());
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions());
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions());
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions());
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions());
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions(),
const GlyphRenderingOptions *aRenderingOptions = nullptr);
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions = DrawOptions());
virtual void PushClip(const Path *aPath);
virtual void PushClipRect(const Rect &aRect);
virtual void PopClip();
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat) const;
virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const { return nullptr; }
virtual TemporaryRef<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { return nullptr; }
virtual TemporaryRef<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
ExtendMode aExtendMode = EXTEND_CLAMP) const;
virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
bool Init(const IntSize &aSize, SurfaceFormat aFormat);
uint32_t GetByteSize() const;
static ID2D1Factory1 *factory();
static void CleanupD2D();
static IDWriteFactory *GetDWriteFactory();
operator std::string() const {
std::stringstream stream;
stream << "DrawTargetD2D 1.1 (" << this << ")";
return stream.str();
}
static uint64_t mVRAMUsageDT;
static uint64_t mVRAMUsageSS;
private:
friend class SourceSurfaceD2D1;
#ifdef _MSC_VER
typedef stdext::hash_set<DrawTargetD2D1*> TargetSet;
#else
typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
#endif
// This function will mark the surface as changing, and make sure any
// copy-on-write snapshots are notified.
void MarkChanged();
void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
void FlushTransformToDC() {
if (mTransformDirty) {
mDC->SetTransform(D2DMatrix(mTransform));
mTransformDirty = false;
}
}
void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
void PopAllClips();
void PushClipsToDC(ID2D1DeviceContext *aDC);
void PopClipsFromDC(ID2D1DeviceContext *aDC);
TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
ExtendMode aExtendMode);
void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
IntSize mSize;
RefPtr<ID3D11Device> mDevice;
RefPtr<ID3D11Texture2D> mTexture;
// This is only valid if mCurrentClippedGeometry is non-null. And will
// only be the intersection of all pixel-aligned retangular clips. This is in
// device space.
IntRect mCurrentClipBounds;
mutable RefPtr<ID2D1DeviceContext> mDC;
RefPtr<ID2D1Bitmap1> mBitmap;
RefPtr<ID2D1Bitmap1> mTempBitmap;
// We store this to prevent excessive SetTextRenderingParams calls.
RefPtr<IDWriteRenderingParams> mTextRenderingParams;
// List of pushed clips.
struct PushedClip
{
D2D1_RECT_F mBounds;
union {
// If mPath is non-NULL, the mTransform member will be used, otherwise
// the mIsPixelAligned member is valid.
D2D1_MATRIX_3X2_F mTransform;
bool mIsPixelAligned;
};
RefPtr<PathD2D> mPath;
};
std::vector<PushedClip> mPushedClips;
// The latest snapshot of this surface. This needs to be told when this
// target is modified. We keep it alive as a cache.
RefPtr<SourceSurfaceD2D1> mSnapshot;
// A list of targets we need to flush when we're modified.
TargetSet mDependentTargets;
// A list of targets which have this object in their mDependentTargets set
TargetSet mDependingOnTargets;
// True of the current clip stack is pushed to the main RT.
bool mClipsArePushed;
static ID2D1Factory1 *mFactory;
static IDWriteFactory *mDWriteFactory;
};
}
}
#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */

View File

@ -34,8 +34,12 @@
#ifdef WIN32
#include "DrawTargetD2D.h"
#ifdef USE_D2D1_1
#include "DrawTargetD2D1.h"
#endif
#include "ScaledFontDWrite.h"
#include <d3d10_1.h>
#include "HelpersD2D.h"
#endif
#include "DrawTargetDual.h"
@ -151,6 +155,10 @@ int sGfxLogLevel = LOG_DEBUG;
#ifdef WIN32
ID3D10Device1 *Factory::mD3D10Device;
#ifdef USE_D2D1_1
ID3D11Device *Factory::mD3D11Device;
ID2D1Device *Factory::mD2D1Device;
#endif
#endif
DrawEventRecorder *Factory::mRecorder;
@ -185,6 +193,17 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor
}
break;
}
#ifdef USE_D2D1_1
case BACKEND_DIRECT2D1_1:
{
RefPtr<DrawTargetD2D1> newTarget;
newTarget = new DrawTargetD2D1();
if (newTarget->Init(aSize, aFormat)) {
retVal = newTarget;
}
break;
}
#endif
#elif defined XP_MACOSX
case BACKEND_COREGRAPHICS:
case BACKEND_COREGRAPHICS_ACCELERATED:
@ -427,6 +446,32 @@ Factory::GetDirect3D10Device()
return mD3D10Device;
}
#ifdef USE_D2D1_1
void
Factory::SetDirect3D11Device(ID3D11Device *aDevice)
{
mD3D11Device = aDevice;
RefPtr<ID2D1Factory1> factory = D2DFactory1();
RefPtr<IDXGIDevice> device;
aDevice->QueryInterface((IDXGIDevice**)byRef(device));
factory->CreateDevice(device, &mD2D1Device);
}
ID3D11Device*
Factory::GetDirect3D11Device()
{
return mD3D11Device;
}
ID2D1Device*
Factory::GetD2D1Device()
{
return mD2D1Device;
}
#endif
TemporaryRef<GlyphRenderingOptions>
Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams)
{

View File

@ -24,6 +24,7 @@ public:
private:
friend class DrawTargetD2D;
friend class DrawTargetD2D1;
mutable RefPtr<ID2D1GradientStopCollection> mStopCollection;
};

View File

@ -6,7 +6,11 @@
#ifndef MOZILLA_GFX_HELPERSD2D_H_
#define MOZILLA_GFX_HELPERSD2D_H_
#ifndef USE_D2D1_1
#include "moz-d2d1-1.h"
#else
#include <d2d1_1.h>
#endif
#include <vector>
@ -26,6 +30,10 @@ namespace gfx {
ID2D1Factory* D2DFactory();
#ifdef USE_D2D1_1
ID2D1Factory1* D2DFactory1();
#endif
static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
{
return D2D1::Point2F(aPoint.x, aPoint.y);
@ -68,6 +76,18 @@ static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter)
}
}
#ifdef USE_D2D1_1
static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter)
{
switch (aFilter) {
case FILTER_POINT:
return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
default:
return D2D1_INTERPOLATION_MODE_LINEAR;
}
}
#endif
static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
{
switch (aMode) {
@ -152,6 +172,38 @@ static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat));
}
#ifdef USE_D2D1_1
static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
{
switch(aOp) {
case OP_OVER:
return D2D1_COMPOSITE_MODE_SOURCE_OVER;
case OP_ADD:
return D2D1_COMPOSITE_MODE_PLUS;
case OP_ATOP:
return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
case OP_OUT:
return D2D1_COMPOSITE_MODE_SOURCE_OUT;
case OP_IN:
return D2D1_COMPOSITE_MODE_SOURCE_IN;
case OP_SOURCE:
return D2D1_COMPOSITE_MODE_SOURCE_COPY;
case OP_DEST_IN:
return D2D1_COMPOSITE_MODE_DESTINATION_IN;
case OP_DEST_OUT:
return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
case OP_DEST_OVER:
return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
case OP_DEST_ATOP:
return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
case OP_XOR:
return D2D1_COMPOSITE_MODE_XOR;
default:
return D2D1_COMPOSITE_MODE_SOURCE_OVER;
}
}
#endif
static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
{
if (aPattern.GetType() != PATTERN_RADIAL_GRADIENT) {

View File

@ -42,6 +42,11 @@ DEFINES += -DWIN32 -DINITGUID
ifdef MOZ_ENABLE_SKIA
DEFINES += -DSKIA_IMPLEMENTATION=1 -DGR_IMPLEMENTATION=1
endif
# For Direct2D 1.1 we require WINSDK_MAXVER 0x06020000 or higher.
ifdef MOZ_ENABLE_DIRECT2D1_1
DEFINES += -DUSE_D2D1_1
endif
endif
include $(topsrcdir)/config/rules.mk

View File

@ -6,8 +6,9 @@
#ifndef MOZILLA_GFX_PATHD2D_H_
#define MOZILLA_GFX_PATHD2D_H_
#include <d2d1.h>
#include "2D.h"
#include "moz-d2d1-1.h"
namespace mozilla {
namespace gfx {
@ -90,6 +91,7 @@ public:
private:
friend class DrawTargetD2D;
friend class DrawTargetD2D1;
mutable RefPtr<ID2D1PathGeometry> mGeometry;
bool mEndedActive;

View File

@ -57,6 +57,7 @@ public:
private:
friend class DrawTargetD2D;
friend class DrawTargetD2D1;
RefPtr<IDWriteRenderingParams> mParams;
};

View File

@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#include "SourceSurfaceD2D1.h"
#include "DrawTargetD2D1.h"
#include "Tools.h"
namespace mozilla {
namespace gfx {
SourceSurfaceD2D1::SourceSurfaceD2D1(ID2D1Image *aImage, ID2D1DeviceContext *aDC,
SurfaceFormat aFormat, const IntSize &aSize,
DrawTargetD2D1 *aDT)
: mImage(aImage)
, mDC(aDC)
, mDrawTarget(aDT)
{
aImage->QueryInterface((ID2D1Bitmap1**)byRef(mRealizedBitmap));
mFormat = aFormat;
mSize = aSize;
}
SourceSurfaceD2D1::~SourceSurfaceD2D1()
{
}
TemporaryRef<DataSourceSurface>
SourceSurfaceD2D1::GetDataSurface()
{
EnsureRealizedBitmap();
RefPtr<ID2D1Bitmap1> softwareBitmap;
D2D1_BITMAP_PROPERTIES1 props;
props.dpiX = 96;
props.dpiY = 96;
props.pixelFormat = D2DPixelFormat(mFormat);
props.colorContext = nullptr;
props.bitmapOptions = D2D1_BITMAP_OPTIONS_CANNOT_DRAW |
D2D1_BITMAP_OPTIONS_CPU_READ;
mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(softwareBitmap));
D2D1_POINT_2U point = D2D1::Point2U(0, 0);
D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
softwareBitmap->CopyFromBitmap(&point, mRealizedBitmap, &rect);
return new DataSourceSurfaceD2D1(softwareBitmap, mFormat);
}
void
SourceSurfaceD2D1::EnsureRealizedBitmap()
{
if (mRealizedBitmap) {
return;
}
RefPtr<ID2D1DeviceContext> dc;
Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, byRef(dc));
D2D1_BITMAP_PROPERTIES1 props;
props.dpiX = 96;
props.dpiY = 96;
props.pixelFormat = D2DPixelFormat(mFormat);
props.colorContext = nullptr;
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
dc->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
dc->SetTarget(mRealizedBitmap);
dc->BeginDraw();
dc->DrawImage(mImage);
dc->EndDraw();
}
void
SourceSurfaceD2D1::DrawTargetWillChange()
{
// At this point in time this should always be true here.
MOZ_ASSERT(mRealizedBitmap);
RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap;
D2D1_BITMAP_PROPERTIES1 props;
props.dpiX = 96;
props.dpiY = 96;
props.pixelFormat = D2DPixelFormat(mFormat);
props.colorContext = nullptr;
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap));
D2D1_POINT_2U point = D2D1::Point2U(0, 0);
D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height);
mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect);
mImage = mRealizedBitmap;
DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat);
mDrawTarget = nullptr;
// We now no longer depend on the source surface content remaining the same.
MarkIndependent();
}
void
SourceSurfaceD2D1::MarkIndependent()
{
if (mDrawTarget) {
MOZ_ASSERT(mDrawTarget->mSnapshot == this);
mDrawTarget->mSnapshot = nullptr;
mDrawTarget = nullptr;
}
}
DataSourceSurfaceD2D1::DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat)
: mBitmap(aMappableBitmap)
, mFormat(aFormat)
, mMapped(false)
{
}
DataSourceSurfaceD2D1::~DataSourceSurfaceD2D1()
{
if (mMapped) {
mBitmap->Unmap();
}
}
IntSize
DataSourceSurfaceD2D1::GetSize() const
{
D2D1_SIZE_F size = mBitmap->GetSize();
return IntSize(int32_t(size.width), int32_t(size.height));
}
uint8_t*
DataSourceSurfaceD2D1::GetData()
{
EnsureMapped();
return mMap.bits;
}
int32_t
DataSourceSurfaceD2D1::Stride()
{
EnsureMapped();
return mMap.pitch;
}
void
DataSourceSurfaceD2D1::EnsureMapped()
{
if (mMapped) {
return;
}
mBitmap->Map(D2D1_MAP_OPTIONS_READ, &mMap);
mMapped = true;
}
}
}

View File

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef MOZILLA_GFX_SOURCESURFACED2DTARGET_H_
#define MOZILLA_GFX_SOURCESURFACED2DTARGET_H_
#include "2D.h"
#include "HelpersD2D.h"
#include <vector>
#include <d3d11.h>
#include <d2d1_1.h>
namespace mozilla {
namespace gfx {
class DrawTargetD2D1;
class SourceSurfaceD2D1 : public SourceSurface
{
public:
SourceSurfaceD2D1(ID2D1Image* aImage, ID2D1DeviceContext *aDC,
SurfaceFormat aFormat, const IntSize &aSize,
DrawTargetD2D1 *aDT = nullptr);
~SourceSurfaceD2D1();
virtual SurfaceType GetType() const { return SURFACE_D2D1_1_IMAGE; }
virtual IntSize GetSize() const { return mSize; }
virtual SurfaceFormat GetFormat() const { return mFormat; }
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
ID2D1Image *GetImage() { return mImage; }
private:
friend class DrawTargetD2D1;
void EnsureRealizedBitmap();
// This function is called by the draw target this texture belongs to when
// it is about to be changed. The texture will be required to make a copy
// of itself when this happens.
void DrawTargetWillChange();
// This will mark the surface as no longer depending on its drawtarget,
// this may happen on destruction or copying.
void MarkIndependent();
RefPtr<ID2D1Image> mImage;
// This may be null if we were created for a non-bitmap image and have not
// had a reason yet to realize ourselves.
RefPtr<ID2D1Bitmap1> mRealizedBitmap;
RefPtr<ID2D1DeviceContext> mDC;
SurfaceFormat mFormat;
IntSize mSize;
RefPtr<DrawTargetD2D1> mDrawTarget;
};
class DataSourceSurfaceD2D1 : public DataSourceSurface
{
public:
DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat);
~DataSourceSurfaceD2D1();
virtual SurfaceType GetType() const { return SURFACE_DATA; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const { return mFormat; }
virtual uint8_t *GetData();
virtual int32_t Stride();
private:
friend class SourceSurfaceD2DTarget;
void EnsureMapped();
mutable RefPtr<ID2D1Bitmap1> mBitmap;
SurfaceFormat mFormat;
D2D1_MAPPED_RECT mMap;
bool mMapped;
};
}
}
#endif /* MOZILLA_GFX_SOURCESURFACED2D2TARGET_H_ */

View File

@ -27,6 +27,7 @@ enum SurfaceType
SURFACE_COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
SURFACE_SKIA, /* Surface wrapping a Skia bitmap */
SURFACE_DUAL_DT, /* Snapshot of a dual drawtarget */
SURFACE_D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
SURFACE_RECORDING /* Surface used for recording */
};
@ -50,7 +51,8 @@ enum BackendType
BACKEND_COREGRAPHICS_ACCELERATED,
BACKEND_CAIRO,
BACKEND_SKIA,
BACKEND_RECORDING
BACKEND_RECORDING,
BACKEND_DIRECT2D1_1
};
enum FontType

View File

@ -49,9 +49,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'PathD2D.cpp',
'ScaledFontDWrite.cpp',
]
if CONFIG['MOZ_WINSDK_MAXVER'] >= '0x06020000':
if CONFIG['MOZ_ENABLE_DIRECT2D1_1']:
CPP_SOURCES += [
'RadialGradientEffectD2D1.cpp'
'RadialGradientEffectD2D1.cpp',
'DrawTargetD2D1.cpp',
'SourceSurfaceD2D1.cpp'
]
if CONFIG['MOZ_ENABLE_SKIA']:
CPP_SOURCES += [

View File

@ -346,13 +346,28 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfxIntSize& aSize,
SurfaceDescriptor* aBuffer)
{
// Check for Nexus S to disable gralloc. We only check for this on ICS or
// earlier, in hopes that JB will work.
// Check for devices that have problems with gralloc. We only check for
// this on ICS or earlier, in hopes that JB will work.
#if ANDROID_VERSION <= 15
char propValue[PROPERTY_VALUE_MAX];
property_get("ro.product.device", propValue, "None");
if (strcmp("crespo",propValue) == 0) {
NS_WARNING("Nexus S has issues with gralloc, falling back to shmem");
static bool checkedDevice = false;
static bool disableGralloc = false;
if (!checkedDevice) {
char propValue[PROPERTY_VALUE_MAX];
property_get("ro.product.device", propValue, "None");
if (strcmp("crespo",propValue) == 0) {
NS_WARNING("Nexus S has issues with gralloc, falling back to shmem");
disableGralloc = true;
} else if (strcmp("peak", propValue) == 0) {
NS_WARNING("Geeksphone Peak has issues with gralloc, falling back to shmem");
disableGralloc = true;
}
checkedDevice = true;
}
if (disableGralloc) {
return false;
}
#endif

View File

@ -133,6 +133,8 @@ GetBackendName(mozilla::gfx::BackendType aBackend)
return "skia";
case mozilla::gfx::BACKEND_RECORDING:
return "recording";
case mozilla::gfx::BACKEND_DIRECT2D1_1:
return "direct2d 1.1";
case mozilla::gfx::BACKEND_NONE:
return "none";
}

View File

@ -45,7 +45,7 @@ static uint64_t sFontSetGeneration = 0;
gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
uint32_t aStretch,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
@ -60,6 +60,8 @@ gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSr
mSrcIndex = 0;
mWeight = aWeight;
mStretch = aStretch;
// XXX Currently, we don't distinguish 'italic' and 'oblique' styles;
// we need to fix this. (Bug 543715)
mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
mFeatureSettings.AppendElements(aFeatureSettings);
mLanguageOverride = aLanguageOverride;
@ -70,6 +72,29 @@ gfxProxyFontEntry::~gfxProxyFontEntry()
{
}
bool
gfxProxyFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
gfxSparseBitSet *aUnicodeRanges)
{
// XXX font entries don't distinguish italic from oblique (bug 543715)
bool isItalic =
(aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
return mWeight == aWeight &&
mStretch == aStretch &&
mItalic == isItalic &&
mFeatureSettings == aFeatureSettings &&
mLanguageOverride == aLanguageOverride &&
mSrcList == aFontFaceSrcList;
// XXX once we support unicode-range (bug 475891),
// we'll need to compare that here as well
}
gfxFont*
gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
{
@ -91,14 +116,12 @@ gfxFontEntry*
gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
uint32_t aStretch,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
const nsString& aLanguageOverride,
gfxSparseBitSet *aUnicodeRanges)
{
gfxProxyFontEntry *proxyEntry = nullptr;
nsAutoString key(aFamilyName);
ToLowerCase(key);
@ -115,10 +138,42 @@ gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
mFontFamilies.Put(key, family);
}
// construct a new face and add it into the family
uint32_t languageOverride =
gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride);
proxyEntry =
// If there's already a proxy in the family whose descriptors all match,
// we can just move it to the end of the list instead of adding a new
// face that will always "shadow" the old one.
// Note that we can't do this for "real" (non-proxy) entries, even if the
// style descriptors match, as they might have had a different source list,
// but we no longer have the old source list available to check.
nsTArray<nsRefPtr<gfxFontEntry> >& fontList = family->GetFontList();
for (uint32_t i = 0, count = fontList.Length(); i < count; i++) {
if (!fontList[i]->mIsProxy) {
continue;
}
gfxProxyFontEntry *existingProxyEntry =
static_cast<gfxProxyFontEntry*>(fontList[i].get());
if (!existingProxyEntry->Matches(aFontFaceSrcList,
aWeight, aStretch, aItalicStyle,
aFeatureSettings, languageOverride,
aUnicodeRanges)) {
continue;
}
// We've found an entry that matches the new face exactly, so advance
// it to the end of the list.
// (Hold a strong reference while doing this, in case the only thing
// keeping the proxyFontEntry alive is the reference from family!)
nsRefPtr<gfxFontEntry> ref(existingProxyEntry);
family->RemoveFontEntry(existingProxyEntry);
family->AddFontEntry(existingProxyEntry);
return existingProxyEntry;
}
// construct a new face and add it into the family
gfxProxyFontEntry *proxyEntry =
new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch,
aItalicStyle,
aFeatureSettings,

View File

@ -41,6 +41,20 @@ struct gfxFontFaceSrc {
nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url
};
inline bool
operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b)
{
bool equals;
return (a.mIsLocal && b.mIsLocal &&
a.mLocalName == b.mLocalName) ||
(!a.mIsLocal && !b.mIsLocal &&
a.mUseOriginPrincipal == b.mUseOriginPrincipal &&
a.mFormatFlags == b.mFormatFlags &&
NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals &&
NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) && equals &&
a.mOriginPrincipal->Equals(b.mOriginPrincipal));
}
// Subclassed to store platform-specific code cleaned out when font entry is
// deleted.
// Lifetime: from when platform font is created until it is deactivated.
@ -168,13 +182,14 @@ public:
// add in a font face
// weight, stretch - 0 == unknown, [1, 9] otherwise
// weight - 0 == unknown, [100, 900] otherwise (multiples of 100)
// stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED]
// italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL
// TODO: support for unicode ranges not yet implemented
gfxFontEntry *AddFontFace(const nsAString& aFamilyName,
const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
uint32_t aStretch,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
const nsString& aLanguageOverride,
@ -428,7 +443,7 @@ class gfxProxyFontEntry : public gfxFontEntry {
public:
gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
uint32_t aStretch,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
@ -436,6 +451,15 @@ public:
virtual ~gfxProxyFontEntry();
// Return whether the entry matches the given list of attributes
bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
int32_t aStretch,
uint32_t aItalicStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
gfxSparseBitSet *aUnicodeRanges);
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
// note that code depends on the ordering of these values!

View File

@ -434,7 +434,7 @@ JavaScriptChild::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, boo
return fail(cx, rs);
*result = !!extensible;
return true;
return ok(rs);
}
bool

View File

@ -4098,12 +4098,16 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets,
callInfo.pushFormals(dispatchBlock);
// Patch any InlinePropertyTable to only contain functions that are inlineable.
// Also guarantee that the table uses functions from |targets| instead of |originals|.
//
// Note that we trim using originals, as callsite clones are not user
// visible. We don't patch the entries inside the table with the cloned
// targets, as the entries should only be used for comparison.
//
// The InlinePropertyTable will also be patched at the end to exclude native functions
// that vetoed inlining.
if (maybeCache) {
InlinePropertyTable *propTable = maybeCache->propTable();
propTable->trimToAndMaybePatchTargets(targets, originals);
propTable->trimToTargets(originals);
if (propTable->numEntries() == 0)
maybeCache = NULL;
}
@ -4153,6 +4157,10 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets,
// Inline each of the inlineable targets.
JS_ASSERT(targets.length() == originals.length());
for (uint32_t i = 0; i < targets.length(); i++) {
// When original != target, the target is a callsite clone. The
// original should be used for guards, and the target should be the
// actual function inlined.
JSFunction *original = &originals[i]->as<JSFunction>();
JSFunction *target = &targets[i]->as<JSFunction>();
// Target must be inlineable.
@ -4160,7 +4168,7 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets,
continue;
// Target must be reachable by the MDispatchInstruction.
if (maybeCache && !maybeCache->propTable()->hasFunction(target)) {
if (maybeCache && !maybeCache->propTable()->hasFunction(original)) {
choiceSet[i] = false;
continue;
}
@ -6727,18 +6735,25 @@ IonBuilder::jsop_setelem()
return jsop_setelem_typed(arrayType, SetElem_Normal,
object, index, value);
if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value)) {
if (ElementAccessIsDenseNative(object, index)) {
if (ElementAccessIsDenseNative(object, index)) {
do {
if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value))
break;
if (!object->resultTypeSet())
break;
types::StackTypeSet::DoubleConversion conversion =
object->resultTypeSet()->convertDoubleElements(cx);
// If AmbiguousDoubleConversion, only handle int32 values for now.
if (conversion != types::StackTypeSet::AmbiguousDoubleConversion ||
value->type() == MIRType_Int32)
if (conversion == types::StackTypeSet::AmbiguousDoubleConversion &&
value->type() != MIRType_Int32)
{
return jsop_setelem_dense(conversion, SetElem_Normal, object, index, value);
break;
}
}
return jsop_setelem_dense(conversion, SetElem_Normal, object, index, value);
} while(false);
}
if (object->type() == MIRType_Magic)

View File

@ -2293,8 +2293,7 @@ InlinePropertyTable::trimTo(AutoObjectVector &targets, Vector<bool> &choiceSet)
}
void
InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets,
AutoObjectVector &originals)
InlinePropertyTable::trimToTargets(AutoObjectVector &targets)
{
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases",
(int)numEntries());
@ -2302,12 +2301,8 @@ InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets,
size_t i = 0;
while (i < numEntries()) {
bool foundFunc = false;
// Compare using originals, but if we find a matching function,
// patch it to the target, which might be a clone.
for (size_t j = 0; j < originals.length(); j++) {
if (entries_[i]->func == originals[j]) {
if (entries_[i]->func != targets[j])
entries_[i] = new Entry(entries_[i]->typeObj, &targets[j]->as<JSFunction>());
for (size_t j = 0; j < targets.length(); j++) {
if (entries_[i]->func == targets[j]) {
foundFunc = true;
break;
}

View File

@ -5464,7 +5464,7 @@ class InlinePropertyTable : public TempObject
void trimTo(AutoObjectVector &targets, Vector<bool> &choiceSet);
// Ensure that the InlinePropertyTable's domain is a subset of |targets|.
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals);
void trimToTargets(AutoObjectVector &targets);
};
class MGetPropertyCache

View File

@ -0,0 +1,4 @@
// Don't crash
print(ParallelArray())
String(Object.create(ParallelArray(8077, function() {})))

View File

@ -7206,12 +7206,11 @@ JS_GetScriptedGlobal(JSContext *cx)
JS_PUBLIC_API(JSBool)
JS_PreventExtensions(JSContext *cx, JS::HandleObject obj)
{
JSBool extensible;
if (!JS_IsExtensible(cx, obj, &extensible))
return JS_TRUE;
if (extensible)
return JS_TRUE;
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return true;
return JSObject::preventExtensions(cx, obj);
}

View File

@ -970,14 +970,9 @@ TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
// But if we don't have too many properties yet, don't do anything. The
// idea here is that normal object initialization should not trigger
// deoptimization in most cases, while actual usage as a hashmap should.
// Except for vanilla objects and arrays work around bug 894447 for now
// by deoptimizing more eagerly. Since in those cases we often have a
// pc-keyed TypeObject, this is ok.
TypeObject* type = obj->type();
if (!obj->is<JSObject>() && !obj->is<ArrayObject>() &&
type->getPropertyCount() < 8) {
if (type->getPropertyCount() < 8)
return;
}
MarkTypeObjectUnknownProperties(cx, type);
}
}

View File

@ -172,6 +172,7 @@ static bool compileOnly = false;
static bool fuzzingSafe = false;
#ifdef DEBUG
static bool dumpEntrainedVariables = false;
static bool OOM_printAllocationCount = false;
#endif
@ -428,6 +429,12 @@ RunFile(JSContext *cx, Handle<JSObject*> obj, const char *filename, FILE *file,
RootedScript script(cx);
script = JS::Compile(cx, obj, options, file);
#ifdef DEBUG
if (dumpEntrainedVariables)
AnalyzeEntrainedVariables(cx, script);
#endif
JS_SetOptions(cx, oldopts);
JS_ASSERT_IF(!script, gGotError);
if (script && !compileOnly) {
@ -5069,6 +5076,11 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
#endif /* JS_ION */
#ifdef DEBUG
if (op->getBoolOption("dump-entrained-variables"))
dumpEntrainedVariables = true;
#endif
/* |scriptArgs| gets bound on the global before any code is run. */
if (!BindScriptArgs(cx, obj, op))
return EXIT_FAILURE;
@ -5302,6 +5314,10 @@ main(int argc, char **argv, char **envp)
"to test JIT codegen (no-op on platforms other than x86).")
|| !op.addBoolOption('\0', "fuzzing-safe", "Don't expose functions that aren't safe for "
"fuzzers to call")
#ifdef DEBUG
|| !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are "
"unnecessarily entrained by inner functions")
#endif
#ifdef JSGC_GENERATIONAL
|| !op.addBoolOption('\0', "no-ggc", "Disable Generational GC")
#endif

View File

@ -2192,3 +2192,175 @@ js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame)
ScopeIter si(frame, cx);
return GetDebugScope(cx, si);
}
#ifdef DEBUG
typedef HashSet<PropertyName *> PropertyNameSet;
static bool
RemoveReferencedNames(JSContext *cx, HandleScript script, PropertyNameSet &remainingNames)
{
// Remove from remainingNames --- the closure variables in some outer
// script --- any free variables in this script. This analysis isn't perfect:
//
// - It will not account for free variables in an inner script which are
// actually accessing some name in an intermediate script between the
// inner and outer scripts. This can cause remainingNames to be an
// underapproximation.
//
// - It will not account for new names introduced via eval. This can cause
// remainingNames to be an overapproximation. This would be easy to fix
// but is nice to have as the eval will probably not access these
// these names and putting eval in an inner script is bad news if you
// care about entraining variables unnecessarily.
for (jsbytecode *pc = script->code;
pc != script->code + script->length;
pc += GetBytecodeLength(pc))
{
PropertyName *name;
switch (JSOp(*pc)) {
case JSOP_NAME:
case JSOP_CALLNAME:
case JSOP_SETNAME:
name = script->getName(pc);
break;
case JSOP_GETALIASEDVAR:
case JSOP_CALLALIASEDVAR:
case JSOP_SETALIASEDVAR:
name = ScopeCoordinateName(cx, script, pc);
break;
default:
name = NULL;
break;
}
if (name)
remainingNames.remove(name);
}
if (script->hasObjects()) {
ObjectArray *objects = script->objects();
for (size_t i = 0; i < objects->length; i++) {
JSObject *obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
JSFunction *fun = &obj->as<JSFunction>();
RootedScript innerScript(cx, fun->getOrCreateScript(cx));
if (!innerScript)
return false;
if (!RemoveReferencedNames(cx, innerScript, remainingNames))
return false;
}
}
}
return true;
}
static bool
AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript)
{
PropertyNameSet remainingNames(cx);
if (!remainingNames.init())
return false;
for (BindingIter bi(script); bi; bi++) {
if (bi->aliased()) {
PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name());
if (!p && !remainingNames.add(p, bi->name()))
return false;
}
}
if (!RemoveReferencedNames(cx, innerScript, remainingNames))
return false;
if (!remainingNames.empty()) {
Sprinter buf(cx);
if (!buf.init())
return false;
buf.printf("Script ");
if (JSAtom *name = script->function()->displayAtom()) {
buf.putString(name);
buf.printf(" ");
}
buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno);
if (JSAtom *name = innerScript->function()->displayAtom()) {
buf.putString(name);
buf.printf(" ");
}
buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno);
for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
buf.printf(" ");
buf.putString(r.front());
}
printf("%s\n", buf.string());
}
if (innerScript->hasObjects()) {
ObjectArray *objects = innerScript->objects();
for (size_t i = 0; i < objects->length; i++) {
JSObject *obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
JSFunction *fun = &obj->as<JSFunction>();
RootedScript innerInnerScript(cx, fun->nonLazyScript());
if (!AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
return false;
}
}
}
return true;
}
// Look for local variables in script or any other script inner to it, which are
// part of the script's call object and are unnecessarily entrained by their own
// inner scripts which do not refer to those variables. An example is:
//
// function foo() {
// var a, b;
// function bar() { return a; }
// function baz() { return b; }
// }
//
// |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
bool
js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script)
{
if (!script->hasObjects())
return true;
ObjectArray *objects = script->objects();
for (size_t i = 0; i < objects->length; i++) {
JSObject *obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
JSFunction *fun = &obj->as<JSFunction>();
RootedScript innerScript(cx, fun->getOrCreateScript(cx));
if (!innerScript)
return false;
if (script->function() && script->function()->isHeavyweight()) {
if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
return false;
}
if (!AnalyzeEntrainedVariables(cx, innerScript))
return false;
}
}
return true;
}
#endif

View File

@ -747,6 +747,11 @@ StaticBlockObject::enclosingBlock() const
return obj && obj->is<StaticBlockObject>() ? &obj->as<StaticBlockObject>() : NULL;
}
#ifdef DEBUG
bool
AnalyzeEntrainedVariables(JSContext *cx, HandleScript script);
#endif
} // namespace js
#endif /* vm_ScopeObject_h */

View File

@ -413,39 +413,54 @@ static PLDHashOperator DetachFontEntries(const nsAString& aKey,
return PL_DHASH_NEXT;
}
static PLDHashOperator RemoveIfEmpty(const nsAString& aKey,
nsRefPtr<gfxMixedFontFamily>& aFamily,
void* aUserArg)
{
return aFamily->GetFontList().Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
}
bool
nsUserFontSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
{
bool modified = false;
// destroy any current loaders, as the entries they refer to
// may be about to get replaced
if (mLoaders.Count() > 0) {
modified = true; // trigger reflow so that any necessary downloads
// will be reinitiated
}
mLoaders.EnumerateEntries(DestroyIterator, nullptr);
// The @font-face rules that make up the user font set have changed,
// so we need to update the set. However, we want to preserve existing
// font entries wherever possible, so that we don't discard and then
// re-download resources in the (common) case where at least some of the
// same rules are still present.
nsTArray<FontFaceRuleRecord> oldRules;
mRules.SwapElements(oldRules);
// destroy the font family records; we need to re-create them
// because we might end up with faces in a different order,
// even if they're the same font entries as before
// Remove faces from the font family records; we need to re-insert them
// because we might end up with faces in a different order even if they're
// the same font entries as before. (The order can affect font selection
// where multiple faces match the requested style, perhaps with overlapping
// unicode-range coverage.)
mFontFamilies.Enumerate(DetachFontEntries, nullptr);
mFontFamilies.Clear();
for (uint32_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
// insert each rule into our list, migrating old font entries if possible
// Insert each rule into our list, migrating old font entries if possible
// rather than creating new ones; set modified to true if we detect
// that rule ordering has changed, or if a new entry is created
// that rule ordering has changed, or if a new entry is created.
InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified);
}
// if any rules are left in the old list, note that the set has changed
// Remove any residual families that have no font entries (i.e., they were
// not defined at all by the updated set of @font-face rules).
mFontFamilies.Enumerate(RemoveIfEmpty, nullptr);
// If any rules are left in the old list, note that the set has changed
// (even if the new set was built entirely by migrating old font entries).
if (oldRules.Length() > 0) {
modified = true;
// any in-progress loaders for obsolete rules should be cancelled
// Any in-progress loaders for obsolete rules should be cancelled,
// as the resource being downloaded will no longer be required.
// We need to explicitly remove any loaders here, otherwise the loaders
// will keep their "orphaned" font entries alive until they complete,
// even after the oldRules array is deleted.
size_t count = oldRules.Length();
for (size_t i = 0; i < count; ++i) {
gfxFontEntry *fe = oldRules[i].mFontEntry;
@ -515,7 +530,7 @@ nsUserFontSet::InsertRule(nsCSSFontFaceRule *aRule, uint8_t aSheetType,
// this is a new rule:
uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
uint32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
nsString languageOverride;

View File

@ -145,6 +145,8 @@ MOCHITEST_FILES = test_acid3_test46.html \
test_property_database.html \
test_priority_preservation.html \
test_property_syntax_errors.html \
test_redundant_font_download.html \
redundant_font_download.sjs \
test_rem_unit.html \
test_rule_insertion.html \
test_rule_serialization.html \

View File

@ -0,0 +1,60 @@
const BinaryOutputStream =
Components.Constructor("@mozilla.org/binaryoutputstream;1",
"nsIBinaryOutputStream",
"setOutputStream");
// this is simply a hex dump of a red square .PNG image
const RED_SQUARE =
[
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00,
0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x20, 0x08, 0x02, 0x00, 0x00, 0x00, 0xFC,
0x18, 0xED, 0xA3, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, 0x00, 0x28,
0x49, 0x44, 0x41, 0x54, 0x48, 0xC7, 0xED, 0xCD, 0x41, 0x0D,
0x00, 0x00, 0x08, 0x04, 0xA0, 0xD3, 0xFE, 0x9D, 0x35, 0x85,
0x0F, 0x37, 0x28, 0x40, 0x4D, 0x6E, 0x75, 0x04, 0x02, 0x81,
0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0xC1, 0x93, 0x60, 0x01,
0xA3, 0xC4, 0x01, 0x3F, 0x58, 0x1D, 0xEF, 0x27, 0x00, 0x00,
0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
];
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
response.setHeader("Cache-Control", "no-cache");
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/plain", false);
var log = getState("bug-879963-request-log") || "";
var stream = new BinaryOutputStream(response.bodyOutputStream);
if (query["q"] == "init") {
log = "init"; // initialize the log, and return a PNG image
response.setHeader("Content-Type", "image/png", false);
stream.writeByteArray(RED_SQUARE, RED_SQUARE.length);
} else if (query["q"] == "image") {
log = log + ";" + query["q"];
response.setHeader("Content-Type", "image/png", false);
stream.writeByteArray(RED_SQUARE, RED_SQUARE.length);
} else if (query["q"] == "font") {
log = log + ";" + query["q"];
// we don't provide a real font; that's ok, OTS will just reject it
response.write("Junk");
} else if (query["q"] == "report") {
// don't include the actual "report" request in the log we return
response.write(log);
} else {
log = log + ";" + query["q"];
response.setStatusLine(request.httpVersion, 404, "Not Found");
}
setState("bug-879963-request-log", log);
}

View File

@ -0,0 +1,130 @@
<!DOCTYPE HTML>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=879963 -->
<head>
<meta charset="utf-8">
<title>Test for bug 879963</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<!-- Two <style> elements with identical @font-face rules.
Although multiple @font-face at-rules for the same family are legal,
and add faces to the family, we should NOT download the same resource
twice just because we have a duplicate face entry. -->
<style type="text/css">
@font-face {
font-family: foo;
src: url("redundant_font_download.sjs?q=font");
}
.test {
font-family: foo;
}
</style>
<style type="text/css">
@font-face {
font-family: foo;
src: url("redundant_font_download.sjs?q=font");
}
.test {
font-family: foo;
}
</style>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=879963">Mozilla Bug 879963</a>
<div>
<!-- the 'init' request returns an image (just so we can see it's working)
and initializes the request logging in our sjs server -->
<img src="redundant_font_download.sjs?q=init">
</div>
<div id="test">
Test
</div>
<div>
<img id="image2" src="">
</div>
<script type="application/javascript">
// helper to retrieve the server's request log as text
function getRequestLog() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "redundant_font_download.sjs?q=report", false);
xmlHttp.send(null);
return xmlHttp.responseText;
}
// retrieve just the most recent request the server has seen
function getLastRequest() {
return getRequestLog().split(";").pop();
}
// poll the server at intervals of 'delay' ms until it has seen a specific request,
// or until maxTime ms have passed
function waitForRequest(request, delay, maxTime, func) {
timeLimit = Date.now() + maxTime;
var intervalId = window.setInterval(function() {
var req = getLastRequest();
if (req == request || Date.now() > timeLimit) {
window.clearInterval(intervalId);
func();
}
}.bind(this), delay);
}
// initially disable the second of the <style> elements,
// so we only have a single copy of the font-face
document.getElementsByTagName("style")[1].disabled = true;
SimpleTest.waitForExplicitFinish();
// We perform a series of actions that trigger requests to the .sjs server,
// and poll the server's request log at each stage to check that it has
// seen the request we expected before we proceed to the next step.
function startTest() {
is(getRequestLog(), "init", "request log has been initialized");
// this should trigger a font download
document.getElementById("test").className = "test";
// wait to confirm that the server has received the request
waitForRequest("font", 10, 1000, continueTest1);
}
function continueTest1() {
is(getRequestLog(), "init;font", "server received font request");
// trigger a request for the second image, to provide an explicit
// delimiter in the log before we enable the second @font-face rule
document.getElementById("image2").src = "redundant_font_download.sjs?q=image";
waitForRequest("image", 10, 1000, continueTest2);
}
function continueTest2() {
is(getRequestLog(), "init;font;image", "server received image request");
// enable the second <style> element: we should NOT see a second font request,
// we expect waitForRequest to time out instead
document.getElementsByTagName("style")[1].disabled = false;
waitForRequest("font", 10, 1000, continueTest3);
}
function continueTest3() {
is(getRequestLog(), "init;font;image", "should NOT have re-requested the font");
SimpleTest.finish();
}
startTest();
</script>
</body>
</html>

View File

@ -82,7 +82,7 @@ var WebrtcUI = {
browser.ownerDocument.defaultView.navigator.mozGetUserMediaDevices(
function (devices) {
WebrtcUI.prompt(browser, callID, params.audio, params.video, devices);
WebrtcUI.prompt(windowID, callID, params.audio, params.video, devices);
},
function (error) {
Cu.reportError(error);
@ -156,7 +156,7 @@ var WebrtcUI = {
}
},
prompt: function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
prompt: function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) {
let audioDevices = [];
let videoDevices = [];
for (let device of aDevices) {
@ -183,7 +183,8 @@ var WebrtcUI = {
else
return;
let host = aBrowser.contentDocument.documentURIObject.asciiHost;
let contentWindow = Services.wm.getOuterWindowWithId(aWindowID);
let host = contentWindow.document.documentURIObject.host;
let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host;
let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1);

View File

@ -2492,6 +2492,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function(aWindow) {
return BrowserApp.getBrowserForWindow(aWindow) != null;
},
get contentWindow() {
return BrowserApp.selectedBrowser.contentWindow;
}
};

View File

@ -8,21 +8,28 @@
* Basic functionality test, from the client programmer's POV.
*/
var tests =
[
new Test("http://localhost:4444/objHandler",
null, start_objHandler, null),
new Test("http://localhost:4444/functionHandler",
null, start_functionHandler, null),
new Test("http://localhost:4444/nonexistent-path",
null, start_non_existent_path, null),
new Test("http://localhost:4444/lotsOfHeaders",
null, start_lots_of_headers, null),
XPCOMUtils.defineLazyGetter(this, "port", function() {
return srv.identity.primaryPort;
});
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test("http://localhost:" + port + "/objHandler",
null, start_objHandler, null),
new Test("http://localhost:" + port + "/functionHandler",
null, start_functionHandler, null),
new Test("http://localhost:" + port + "/nonexistent-path",
null, start_non_existent_path, null),
new Test("http://localhost:" + port + "/lotsOfHeaders",
null, start_lots_of_headers, null),
];
});
var srv;
function run_test()
{
var srv = createServer();
srv = createServer();
// base path
// XXX should actually test this works with a file by comparing streams!
@ -36,7 +43,7 @@ function run_test()
srv.registerPathHandler("/functionHandler", functionHandler);
srv.registerPathHandler("/lotsOfHeaders", lotsOfHeadersHandler);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}
@ -118,13 +125,13 @@ var objHandler =
var body = "Request (slightly reformatted):\n\n";
body += metadata.method + " " + metadata.path;
do_check_eq(metadata.port, 4444);
do_check_eq(metadata.port, port);
if (metadata.queryString)
body += "?" + metadata.queryString;
body += " HTTP/" + metadata.httpVersion + "\n";
var headEnum = metadata.headers;
while (headEnum.hasMoreElements())
{
@ -150,7 +157,7 @@ function functionHandler(metadata, response)
response.setStatusLine("1.1", 404, "Page Not Found");
response.setHeader("foopy", "quux-baz", false);
do_check_eq(metadata.port, 4444);
do_check_eq(metadata.port, port);
do_check_eq(metadata.host, "localhost");
do_check_eq(metadata.path.charAt(0), "/");

View File

@ -10,15 +10,13 @@
* octal number.
*/
const PORT = 4444;
var srv;
function run_test()
{
srv = createServer();
srv.registerPathHandler("/content-length", contentLength);
srv.start(PORT);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}
@ -44,10 +42,12 @@ function contentLength(request, response)
* BEGIN TESTS *
***************/
var tests = [
new Test("http://localhost:4444/content-length",
init_content_length),
];
XPCOMUtils.defineLazyGetter(this, 'tests', function() {
return [
new Test("http://localhost:" + srv.identity.primaryPort + "/content-length",
init_content_length),
];
});
function init_content_length(ch)
{

View File

@ -7,10 +7,13 @@
// checks if a byte range request and non-byte range request retrieve the
// correct data.
const PREFIX = "http://localhost:4444";
var srv;
XPCOMUtils.defineLazyGetter(this, "PREFIX", function() {
return "http://localhost:" + srv.identity.primaryPort;
});
var tests =
[
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test(PREFIX + "/range.txt",
init_byterange, start_byterange, stop_byterange),
new Test(PREFIX + "/range.txt",
@ -40,14 +43,15 @@ var tests =
new Test(PREFIX + "/range.txt",
null, start_normal, stop_normal)
];
});
function run_test()
{
var srv = createServer();
srv = createServer();
var dir = do_get_file("data/ranges/");
srv.registerDirectory("/", dir);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}

View File

@ -5,31 +5,35 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// exercises support for mod_cern_meta-style header/status line modification
var srv;
const PREFIX = "http://localhost:4444";
XPCOMUtils.defineLazyGetter(this, 'PREFIX', function() {
return "http://localhost:" + srv.identity.primaryPort;
});
var tests =
[
new Test(PREFIX + "/test_both.html",
null, start_testBoth, null),
new Test(PREFIX + "/test_ctype_override.txt",
null, start_test_ctype_override_txt, null),
new Test(PREFIX + "/test_status_override.html",
null, start_test_status_override_html, null),
new Test(PREFIX + "/test_status_override_nodesc.txt",
null, start_test_status_override_nodesc_txt, null),
new Test(PREFIX + "/caret_test.txt^",
null, start_caret_test_txt_, null)
XPCOMUtils.defineLazyGetter(this, 'tests', function() {
return [
new Test(PREFIX + "/test_both.html",
null, start_testBoth, null),
new Test(PREFIX + "/test_ctype_override.txt",
null, start_test_ctype_override_txt, null),
new Test(PREFIX + "/test_status_override.html",
null, start_test_status_override_html, null),
new Test(PREFIX + "/test_status_override_nodesc.txt",
null, start_test_status_override_nodesc_txt, null),
new Test(PREFIX + "/caret_test.txt^",
null, start_caret_test_txt_, null)
];
});
function run_test()
{
var srv = createServer();
srv = createServer();
var cernDir = do_get_file("data/cern_meta/");
srv.registerDirectory("/", cernDir);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}

View File

@ -10,6 +10,10 @@
var srv, dir, dirEntries;
XPCOMUtils.defineLazyGetter(this, 'BASE_URL', function() {
return "http://localhost:" + srv.identity.primaryPort + "/";
});
function run_test()
{
createTestDirectory();
@ -20,7 +24,7 @@ function run_test()
var nameDir = do_get_file("data/name-scheme/");
srv.registerDirectory("/bar/", nameDir);
srv.start(4444);
srv.start(-1);
function done()
{
@ -255,38 +259,32 @@ function makeFile(name, isDirectory, parentDir, lst)
* TESTS *
*********/
var tests = [];
var test;
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test(BASE_URL, null, start, stopRootDirectory),
new Test(BASE_URL + "foo/", null, start, stopFooDirectory),
new Test(BASE_URL + "bar/folder^/", null, start, stopTrailingCaretDirectory),
];
});
// check top-level directory listing
test = new Test("http://localhost:4444/",
null, start, stopRootDirectory),
tests.push(test);
function start(ch)
{
do_check_eq(ch.getResponseHeader("Content-Type"), "text/html;charset=utf-8");
}
function stopRootDirectory(ch, cx, status, data)
{
dataCheck(data, "http://localhost:4444/", "/", dirEntries[0]);
dataCheck(data, BASE_URL, "/", dirEntries[0]);
}
// check non-top-level, too
test = new Test("http://localhost:4444/foo/",
null, start, stopFooDirectory),
tests.push(test);
function stopFooDirectory(ch, cx, status, data)
{
dataCheck(data, "http://localhost:4444/foo/", "/foo/", dirEntries[1]);
dataCheck(data, BASE_URL + "foo/", "/foo/", dirEntries[1]);
}
// trailing-caret leaf with hidden files
test = new Test("http://localhost:4444/bar/folder^/",
null, start, stopTrailingCaretDirectory),
tests.push(test);
function stopTrailingCaretDirectory(ch, cx, status, data)
{
hiddenDataCheck(data, "http://localhost:4444/bar/folder^/", "/bar/folder^/");
hiddenDataCheck(data, BASE_URL + "bar/folder^/", "/bar/folder^/");
}

View File

@ -7,23 +7,26 @@
// in its original incarnation, the server didn't like empty response-bodies;
// see the comment in _end for details
var tests =
[
new Test("http://localhost:4444/empty-body-unwritten",
null, ensureEmpty, null),
new Test("http://localhost:4444/empty-body-written",
null, ensureEmpty, null),
var srv;
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test("http://localhost:" + srv.identity.primaryPort + "/empty-body-unwritten",
null, ensureEmpty, null),
new Test("http://localhost:" + srv.identity.primaryPort + "/empty-body-written",
null, ensureEmpty, null),
];
});
function run_test()
{
var srv = createServer();
srv = createServer();
// register a few test paths
srv.registerPathHandler("/empty-body-unwritten", emptyBodyUnwritten);
srv.registerPathHandler("/empty-body-written", emptyBodyWritten);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}

View File

@ -7,15 +7,18 @@
// Request handlers may throw exceptions, and those exception should be caught
// by the server and converted into the proper error codes.
var tests =
[
new Test("http://localhost:4444/throws/exception",
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test("http://localhost:" + srv.identity.primaryPort + "/throws/exception",
null, start_throws_exception, succeeded),
new Test("http://localhost:4444/this/file/does/not/exist/and/404s",
new Test("http://localhost:" + srv.identity.primaryPort +
"/this/file/does/not/exist/and/404s",
null, start_nonexistent_404_fails_so_400, succeeded),
new Test("http://localhost:4444/attempts/404/fails/so/400/fails/so/500s",
new Test("http://localhost:" + srv.identity.primaryPort +
"/attempts/404/fails/so/400/fails/so/500s",
register400Handler, start_multiple_exceptions_500, succeeded),
];
});
var srv;
@ -26,7 +29,7 @@ function run_test()
srv.registerErrorHandler(404, throwsException);
srv.registerPathHandler("/throws/exception", throwsException);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}

View File

@ -6,15 +6,15 @@
// test that special headers are sent as an array of headers with the same name
const PORT = 4444;
var srv;
function run_test()
{
var srv;
srv;
srv = createServer();
srv.registerPathHandler("/path-handler", pathHandler);
srv.start(PORT);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}
@ -47,9 +47,12 @@ function pathHandler(request, response)
* BEGIN TESTS *
***************/
var tests = [
new Test("http://localhost:4444/path-handler",
null, check)];
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test("http://localhost:" + srv.identity.primaryPort + "/path-handler",
null, check)
];
});
function check(ch, cx)
{

View File

@ -8,42 +8,47 @@
// htaccess-like functionality without the need to explicitly disable display
// of such files
const PREFIX = "http://localhost:4444";
var srv;
var tests =
[
new Test(PREFIX + "/bar.html^",
XPCOMUtils.defineLazyGetter(this, "PREFIX", function() {
return "http://localhost:" + srv.identity.primaryPort;
});
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test(PREFIX + "/bar.html^",
null, start_bar_html_, null),
new Test(PREFIX + "/foo.html^",
new Test(PREFIX + "/foo.html^",
null, start_foo_html_, null),
new Test(PREFIX + "/normal-file.txt",
new Test(PREFIX + "/normal-file.txt",
null, start_normal_file_txt, null),
new Test(PREFIX + "/folder^/file.txt",
new Test(PREFIX + "/folder^/file.txt",
null, start_folder__file_txt, null),
new Test(PREFIX + "/foo/bar.html^",
new Test(PREFIX + "/foo/bar.html^",
null, start_bar_html_, null),
new Test(PREFIX + "/foo/foo.html^",
new Test(PREFIX + "/foo/foo.html^",
null, start_foo_html_, null),
new Test(PREFIX + "/foo/normal-file.txt",
new Test(PREFIX + "/foo/normal-file.txt",
null, start_normal_file_txt, null),
new Test(PREFIX + "/foo/folder^/file.txt",
new Test(PREFIX + "/foo/folder^/file.txt",
null, start_folder__file_txt, null),
new Test(PREFIX + "/end-caret^/bar.html^",
new Test(PREFIX + "/end-caret^/bar.html^",
null, start_bar_html_, null),
new Test(PREFIX + "/end-caret^/foo.html^",
new Test(PREFIX + "/end-caret^/foo.html^",
null, start_foo_html_, null),
new Test(PREFIX + "/end-caret^/normal-file.txt",
new Test(PREFIX + "/end-caret^/normal-file.txt",
null, start_normal_file_txt, null),
new Test(PREFIX + "/end-caret^/folder^/file.txt",
new Test(PREFIX + "/end-caret^/folder^/file.txt",
null, start_folder__file_txt, null)
];
];
});
function run_test()
{
var srv = createServer();
srv = createServer();
// make sure underscores work in directories "mounted" in directories with
// folders starting with _
@ -52,7 +57,7 @@ function run_test()
srv.registerDirectory("/foo/", nameDir);
srv.registerDirectory("/end-caret^/", nameDir);
srv.start(4444);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}

View File

@ -8,15 +8,18 @@
* Tests for correct behavior of asynchronous responses.
*/
const PORT = 4444;
const PREPATH = "http://localhost:" + PORT;
XPCOMUtils.defineLazyGetter(this, "PREPATH", function() {
return "http://localhost:" + srv.identity.primaryPort;
});
var srv;
function run_test()
{
var srv = createServer();
srv = createServer();
for (var path in handlers)
srv.registerPathHandler(path, handlers[path]);
srv.start(PORT);
srv.start(-1);
runHttpTests(tests, testComplete(srv));
}
@ -26,8 +29,18 @@ function run_test()
* BEGIN TESTS *
***************/
var test;
var tests = [];
XPCOMUtils.defineLazyGetter(this, "tests", function() {
return [
new Test(PREPATH + "/handleSync", null, start_handleSync, null),
new Test(PREPATH + "/handleAsync1", null, start_handleAsync1,
stop_handleAsync1),
new Test(PREPATH + "/handleAsync2", init_handleAsync2, start_handleAsync2,
stop_handleAsync2),
new Test(PREPATH + "/handleAsyncOrdering", null, null,
stop_handleAsyncOrdering)
];
});
var handlers = {};
function handleSync(request, response)
@ -54,11 +67,6 @@ function start_handleSync(ch, cx)
do_check_eq(ch.responseStatusText, "handleSync pass");
}
test = new Test(PREPATH + "/handleSync",
null, start_handleSync, null),
tests.push(test);
function handleAsync1(request, response)
{
response.setStatusLine(request.httpVersion, 500, "Old status line!");
@ -125,11 +133,6 @@ function stop_handleAsync1(ch, cx, status, data)
do_check_eq(data.length, 0);
}
test = new Test(PREPATH + "/handleAsync1",
null, start_handleAsync1, stop_handleAsync1),
tests.push(test);
const startToHeaderDelay = 500;
const startToFinishedDelay = 750;
@ -233,11 +236,6 @@ function stop_handleAsync2(ch, cx, status, data)
do_check_eq(String.fromCharCode.apply(null, data), "BODY");
}
test = new Test(PREPATH + "/handleAsync2",
init_handleAsync2, start_handleAsync2, stop_handleAsync2);
tests.push(test);
/*
* Tests that accessing output stream *before* calling processAsync() works
* correctly, sending written data immediately as it is written, not buffering
@ -304,7 +302,3 @@ function stop_handleAsyncOrdering(ch, cx, status, data)
do_throw("value " + v + " at index " + index + " should be zero");
});
}
test = new Test(PREPATH + "/handleAsyncOrdering",
null, null, stop_handleAsyncOrdering);
tests.push(test);

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