mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and inbound
This commit is contained in:
commit
7ba642a842
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -1205,6 +1205,10 @@ nsBrowserAccess.prototype = {
|
||||
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
get contentWindow() {
|
||||
return Browser.selectedBrowser.contentWindow;
|
||||
}
|
||||
};
|
||||
|
||||
|
45
configure.in
45
configure.in
@ -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
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMComment
|
||||
// Empty interface
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMText
|
||||
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>
|
@ -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;
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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]();
|
||||
|
@ -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]
|
||||
|
@ -24,6 +24,15 @@ public:
|
||||
virtual ~WebGL1Context();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT WebGLContext
|
||||
|
||||
virtual bool IsWebGL2() const MOZ_OVERRIDE
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT nsWrapperCache
|
||||
|
||||
|
61
content/canvas/src/WebGL2Context.cpp
Normal file
61
content/canvas/src/WebGL2Context.cpp
Normal 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);
|
||||
}
|
||||
|
64
content/canvas/src/WebGL2Context.h
Normal file
64
content/canvas/src/WebGL2Context.h
Normal 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
|
@ -153,6 +153,8 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) = 0;
|
||||
|
||||
virtual bool IsWebGL2() const = 0;
|
||||
|
||||
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
|
||||
|
||||
// nsICanvasRenderingContextInternal
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ if CONFIG['MOZ_WEBGL']:
|
||||
'WebGLActiveInfo.cpp',
|
||||
'WebGLBuffer.cpp',
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGLContext.cpp',
|
||||
'WebGLContextGL.cpp',
|
||||
'WebGLContextUtils.cpp',
|
||||
|
@ -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);
|
||||
|
||||
|
@ -401,6 +401,9 @@ void
|
||||
AudioContext::UnregisterPannerNode(PannerNode* aNode)
|
||||
{
|
||||
mPannerNodes.RemoveEntry(aNode);
|
||||
if (mListener) {
|
||||
mListener->UnregisterPannerNode(aNode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -127,6 +127,7 @@ public:
|
||||
}
|
||||
|
||||
void RegisterPannerNode(PannerNode* aPannerNode);
|
||||
void UnregisterPannerNode(PannerNode* aPannerNode);
|
||||
|
||||
private:
|
||||
void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 \
|
||||
|
21
content/media/webaudio/test/test_bug894150.html
Normal file
21
content/media/webaudio/test/test_bug894150.html
Normal 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>
|
@ -48,6 +48,7 @@ public:
|
||||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMText
|
||||
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMProcessingInstruction
|
||||
NS_DECL_NSIDOMPROCESSINGINSTRUCTION
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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' ],
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -253,6 +253,9 @@
|
||||
},
|
||||
openDialog: function(aType, aName, aFeatures, aArguments, aFrameElement) {
|
||||
alert(aType + ", " + aName + ", " + aFeatures + ", " + aArguments + ", " + aFrameElement);
|
||||
},
|
||||
get contentWindow() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
12
dom/webidl/WebGL2RenderingContext.webidl
Normal file
12
dom/webidl/WebGL2RenderingContext.webidl
Normal 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 {
|
||||
|
||||
};
|
||||
|
@ -389,6 +389,7 @@ endif
|
||||
ifdef MOZ_WEBGL
|
||||
webidl_files += \
|
||||
WebGLRenderingContext.webidl \
|
||||
WebGL2RenderingContext.webidl \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -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)
|
||||
|
11
gfx/2d/2D.h
11
gfx/2d/2D.h
@ -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
916
gfx/2d/DrawTargetD2D1.cpp
Normal 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
214
gfx/2d/DrawTargetD2D1.h
Normal 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_ */
|
@ -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)
|
||||
{
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class DrawTargetD2D;
|
||||
friend class DrawTargetD2D1;
|
||||
|
||||
mutable RefPtr<ID2D1GradientStopCollection> mStopCollection;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -57,6 +57,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class DrawTargetD2D;
|
||||
friend class DrawTargetD2D1;
|
||||
|
||||
RefPtr<IDWriteRenderingParams> mParams;
|
||||
};
|
||||
|
163
gfx/2d/SourceSurfaceD2D1.cpp
Normal file
163
gfx/2d/SourceSurfaceD2D1.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
85
gfx/2d/SourceSurfaceD2D1.h
Normal file
85
gfx/2d/SourceSurfaceD2D1.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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 += [
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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!
|
||||
|
@ -434,7 +434,7 @@ JavaScriptChild::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, boo
|
||||
return fail(cx, rs);
|
||||
|
||||
*result = !!extensible;
|
||||
return true;
|
||||
return ok(rs);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
4
js/src/jit-test/tests/parallelarray/bug894782.js
Normal file
4
js/src/jit-test/tests/parallelarray/bug894782.js
Normal file
@ -0,0 +1,4 @@
|
||||
// Don't crash
|
||||
|
||||
print(ParallelArray())
|
||||
String(Object.create(ParallelArray(8077, function() {})))
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 \
|
||||
|
60
layout/style/test/redundant_font_download.sjs
Normal file
60
layout/style/test/redundant_font_download.sjs
Normal 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);
|
||||
}
|
130
layout/style/test/test_redundant_font_download.html
Normal file
130
layout/style/test/test_redundant_font_download.html
Normal 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>
|
@ -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);
|
||||
|
||||
|
@ -2492,6 +2492,10 @@ nsBrowserAccess.prototype = {
|
||||
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return BrowserApp.getBrowserForWindow(aWindow) != null;
|
||||
},
|
||||
|
||||
get contentWindow() {
|
||||
return BrowserApp.selectedBrowser.contentWindow;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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), "/");
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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^/");
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user