Merge mozilla-central into mozilla-inbound

This commit is contained in:
Ehsan Akhgari 2012-04-24 21:29:44 -04:00
commit 166aca09f9
108 changed files with 4354 additions and 25250 deletions

View File

@ -79,3 +79,4 @@ bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
0000000000000000000000000000000000000000 AURORA_BASE_20120131
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
357da346ceb705d196a46574804c7c4ec44ac186 FIREFOX_AURORA_14_BASE

2
b2g/confvars.sh Normal file → Executable file
View File

@ -38,7 +38,7 @@
MOZ_APP_BASENAME=B2G
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=14.0a1
MOZ_APP_VERSION=15.0a1
MOZ_APP_UA_NAME=Firefox
MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial

View File

@ -55,7 +55,7 @@
<Description>
<em:id>{3c2e2abc-06d4-11e1-ac3b-374f68613e61}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_VERSION@</em:maxVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
</Description>

View File

@ -7,7 +7,7 @@ export MOZILLA_OFFICIAL=1
mk_add_options MOZ_MAKE_FLAGS=-j1
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
. $topsrcdir/build/win32/mozconfig.vs2010
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

View File

@ -3,4 +3,4 @@ ac_add_options --enable-update-packaging
ac_add_options --enable-official-branding
ac_add_options --with-l10n-base=../../l10n-central
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
. $topsrcdir/build/win32/mozconfig.vs2010

View File

@ -16,7 +16,7 @@ export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS=-j1
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
. $topsrcdir/build/win32/mozconfig.vs2010
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

View File

@ -12,7 +12,7 @@ export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
. $topsrcdir/build/win32/mozconfig.vs2010
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

View File

@ -13,4 +13,4 @@ mk_add_options MOZ_MAKE_FLAGS=-j1
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
. $topsrcdir/build/win64/mozconfig.vs2010

View File

@ -22,4 +22,4 @@ mk_add_options MOZ_MAKE_FLAGS=-j1
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
. $topsrcdir/browser/config/mozconfigs/win64/vs2010-mozconfig
. $topsrcdir/build/win64/mozconfig.vs2010

View File

@ -1 +1 @@
14.0a1
15.0a1

View File

@ -55,7 +55,7 @@
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_VERSION@</em:maxVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
</Description>

View File

@ -198,6 +198,9 @@ build_all_dep: alldep
build_all_depend: alldep
clobber clobber_all: clean
# helper target for mobile
build_and_deploy: build package install
# Do everything from scratch
everything: clean build

View File

@ -59,6 +59,7 @@ MOZ_PROFILE_MIGRATOR = @MOZ_PROFILE_MIGRATOR@
MOZ_EXTENSION_MANAGER = @MOZ_EXTENSION_MANAGER@
MOZ_APP_UA_NAME = @MOZ_APP_UA_NAME@
MOZ_APP_VERSION = @MOZ_APP_VERSION@
MOZ_APP_MAXVERSION = @MOZ_APP_MAXVERSION@
MOZ_UA_BUILDID = @MOZ_UA_BUILDID@
MOZ_MACBUNDLE_NAME = @MOZ_MACBUNDLE_NAME@
MOZ_APP_STATIC_INI = @MOZ_APP_STATIC_INI@

View File

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
14.0a1
15.0a1

View File

@ -8564,6 +8564,20 @@ if test -z "$MOZ_APP_NAME"; then
MOZ_APP_NAME=`echo $MOZ_APP_BASENAME | tr A-Z a-z`
fi
# For extensions and langpacks, we require a max version that is compatible
# across security releases. MOZ_APP_MAXVERSION is our method for doing that.
# 10.0a1 and 10.0a2 aren't affected
# 10.0 becomes 10.0.*
# 10.0.1 becomes 10.0.*
IS_ALPHA=`echo $MOZ_APP_VERSION | grep a`
if test -z "$IS_ALPHA"; then
changequote(,)
MOZ_APP_MAXVERSION=`echo $MOZ_APP_VERSION | sed "s|\(^[0-9]*.[0-9]*\).*|\1|"`.*
changequote([,])
else
MOZ_APP_MAXVERSION=$MOZ_APP_VERSION
fi
AC_SUBST(MOZ_APP_NAME)
AC_SUBST(MOZ_APP_DISPLAYNAME)
AC_SUBST(MOZ_APP_BASENAME)
@ -8578,6 +8592,7 @@ AC_DEFINE_UNQUOTED(MOZ_APP_UA_NAME, "$MOZ_APP_UA_NAME")
AC_SUBST(MOZ_APP_UA_NAME)
AC_DEFINE_UNQUOTED(MOZ_APP_UA_VERSION, "$MOZ_APP_VERSION")
AC_SUBST(MOZ_APP_VERSION)
AC_SUBST(MOZ_APP_MAXVERSION)
AC_DEFINE_UNQUOTED(MOZ_UA_FIREFOX_VERSION, "$FIREFOX_VERSION")
AC_DEFINE_UNQUOTED(FIREFOX_VERSION,$FIREFOX_VERSION)
AC_SUBST(FIREFOX_VERSION)

View File

@ -444,6 +444,15 @@ public:
*/
static bool ParseIntMarginValue(const nsAString& aString, nsIntMargin& aResult);
/**
* Parse the value of the <font size=""> attribute according to the HTML5
* spec as of April 16, 2012.
*
* @param aValue the value to parse
* @return 1 to 7, or 0 if the value couldn't be parsed
*/
static PRInt32 ParseLegacyFontSize(const nsAString& aValue);
static void Shutdown();
/**

View File

@ -243,6 +243,12 @@ interface nsIDocumentEncoder : nsISupports
*/
const unsigned long OutputDropInvisibleBreak = (1 << 21);
/**
* Don't check for _moz_dirty attributes when deciding whether to
* pretty-print if this flag is set (bug 599983).
*/
const unsigned long OutputIgnoreMozDirty = (1 << 22);
/**
* Initialize with a pointer to the document and the mime type.
* @param aDocument Document to encode.

View File

@ -55,10 +55,10 @@ namespace mozilla {
namespace dom {
Link::Link(Element *aElement)
: mLinkState(defaultState)
, mRegistered(false)
, mElement(aElement)
: mElement(aElement)
, mHistory(services::GetHistoryService())
, mLinkState(defaultState)
, mRegistered(false)
{
NS_ABORT_IF_FALSE(mElement, "Must have an element");
}
@ -75,7 +75,7 @@ Link::GetLinkState() const
"Getting the link state of an unregistered Link!");
NS_ASSERTION(mLinkState != eLinkState_Unknown,
"Getting the link state with an unknown value!");
return mLinkState;
return nsLinkState(mLinkState);
}
void

View File

@ -149,17 +149,17 @@ private:
already_AddRefed<nsIURI> GetURIToMutate();
void SetHrefAttribute(nsIURI *aURI);
nsLinkState mLinkState;
mutable nsCOMPtr<nsIURI> mCachedURI;
bool mRegistered;
Element * const mElement;
// Strong reference to History. The link has to unregister before History
// can disappear.
nsCOMPtr<IHistory> mHistory;
PRUint16 mLinkState;
bool mRegistered;
};
NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID)

View File

@ -1306,6 +1306,59 @@ nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& resul
return true;
}
// static
PRInt32
nsContentUtils::ParseLegacyFontSize(const nsAString& aValue)
{
nsAString::const_iterator iter, end;
aValue.BeginReading(iter);
aValue.EndReading(end);
while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
++iter;
}
if (iter == end) {
return 0;
}
bool relative = false;
bool negate = false;
if (*iter == PRUnichar('-')) {
relative = true;
negate = true;
++iter;
} else if (*iter == PRUnichar('+')) {
relative = true;
++iter;
}
if (*iter < PRUnichar('0') || *iter > PRUnichar('9')) {
return 0;
}
// We don't have to worry about overflow, since we can bail out as soon as
// we're bigger than 7.
PRInt32 value = 0;
while (iter != end && *iter >= PRUnichar('0') && *iter <= PRUnichar('9')) {
value = 10*value + (*iter - PRUnichar('0'));
if (value >= 7) {
break;
}
++iter;
}
if (relative) {
if (negate) {
value = 3 - value;
} else {
value = 3 + value;
}
}
return clamped(value, 1, 7);
}
/* static */
void
nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)

View File

@ -347,8 +347,8 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
--mDisableEntityEncoding;
}
bool forceFormat = content->HasAttr(kNameSpaceID_None,
nsGkAtoms::mozdirty);
bool forceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
content->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
DecrIndentation(name);

View File

@ -565,8 +565,8 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
// The _moz_dirty attribute is emitted by the editor to
// indicate that this element should be pretty printed
// even if we're not in pretty printing mode
aForceFormat = aContent->HasAttr(kNameSpaceID_None,
nsGkAtoms::mozdirty);
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
nsIAtom *name = aContent->Tag();
PRInt32 namespaceID = aContent->GetNameSpaceID();
@ -592,8 +592,8 @@ nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
{
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
aForceFormat = aContent->HasAttr(kNameSpaceID_None,
nsGkAtoms::mozdirty);
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
nsIAtom *name = aContent->Tag();
PRInt32 namespaceID = aContent->GetNameSpaceID();

View File

@ -357,8 +357,8 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
nsresult
nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
bool aForward,
PRUint32* aXPOffset)
bool aForward,
PRUint32* aXPOffset)
{
// XXX This method assumes that the frame boundaries must be cluster
// boundaries. It's false, but no problem now, maybe.
@ -366,7 +366,7 @@ nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
*aXPOffset == 0 || *aXPOffset == aContent->TextLength())
return NS_OK;
NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
NS_ASSERTION(*aXPOffset <= aContent->TextLength(),
"offset is out of range.");
nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();

View File

@ -697,13 +697,24 @@ nsGenericHTMLElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
nsresult rv = docEncoder->NativeInit(doc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in
// the source
nsIDocumentEncoder::OutputRaw);
PRUint32 flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in
// the source
nsIDocumentEncoder::OutputRaw |
// Only check for mozdirty when necessary (bug 599983)
nsIDocumentEncoder::OutputIgnoreMozDirty;
if (IsEditable()) {
nsCOMPtr<nsIEditor> editor;
GetEditorInternal(getter_AddRefs(editor));
if (editor && editor->OutputsMozDirty()) {
flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
}
}
nsresult rv = docEncoder->NativeInit(doc, contentType, flags);
NS_ENSURE_SUCCESS(rv, rv);
if (aIncludeSelf) {

View File

@ -48,6 +48,7 @@
#include "nsRuleData.h"
#include "nsIDocument.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
using namespace mozilla;
@ -116,32 +117,6 @@ NS_IMPL_STRING_ATTR(nsHTMLFontElement, Color, color)
NS_IMPL_STRING_ATTR(nsHTMLFontElement, Face, face)
NS_IMPL_STRING_ATTR(nsHTMLFontElement, Size, size)
static const nsAttrValue::EnumTable kRelFontSizeTable[] = {
{ "-10", -10 },
{ "-9", -9 },
{ "-8", -8 },
{ "-7", -7 },
{ "-6", -6 },
{ "-5", -5 },
{ "-4", -4 },
{ "-3", -3 },
{ "-2", -2 },
{ "-1", -1 },
{ "-0", 0 },
{ "+0", 0 },
{ "+1", 1 },
{ "+2", 2 },
{ "+3", 3 },
{ "+4", 4 },
{ "+5", 5 },
{ "+6", 6 },
{ "+7", 7 },
{ "+8", 8 },
{ "+9", 9 },
{ "+10", 10 },
{ 0 }
};
bool
nsHTMLFontElement::ParseAttribute(PRInt32 aNamespaceID,
@ -151,26 +126,12 @@ nsHTMLFontElement::ParseAttribute(PRInt32 aNamespaceID,
{
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::size) {
nsAutoString tmp(aValue);
tmp.CompressWhitespace(true, true);
PRUnichar ch = tmp.IsEmpty() ? 0 : tmp.First();
if ((ch == '+' || ch == '-')) {
if (aResult.ParseEnumValue(aValue, kRelFontSizeTable, false))
return true;
// truncate after digit, then parse it again.
PRUint32 i;
for (i = 1; i < tmp.Length(); i++) {
ch = tmp.CharAt(i);
if (!nsCRT::IsAsciiDigit(ch)) {
tmp.Truncate(i);
break;
}
}
return aResult.ParseEnumValue(tmp, kRelFontSizeTable, false);
PRInt32 size = nsContentUtils::ParseLegacyFontSize(aValue);
if (size) {
aResult.SetTo(size, &aValue);
return true;
}
return aResult.ParseIntValue(aValue);
return false;
}
if (aAttribute == nsGkAtoms::pointSize ||
aAttribute == nsGkAtoms::fontWeight) {
@ -207,20 +168,10 @@ MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
if (value && value->Type() == nsAttrValue::eInteger)
fontSize->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Point);
else {
// size: int, enum ,
// size: int
value = aAttributes->GetAttr(nsGkAtoms::size);
if (value) {
nsAttrValue::ValueType unit = value->Type();
if (unit == nsAttrValue::eInteger || unit == nsAttrValue::eEnum) {
PRInt32 size;
if (unit == nsAttrValue::eEnum) // int (+/-)
size = value->GetEnumValue() + 3;
else
size = value->GetIntegerValue();
size = clamped(size, 1, 7);
fontSize->SetIntValue(size, eCSSUnit_Enumerated);
}
if (value && value->Type() == nsAttrValue::eInteger) {
fontSize->SetIntValue(value->GetIntegerValue(), eCSSUnit_Enumerated);
}
}
}

View File

@ -301,6 +301,7 @@ _TEST_FILES = \
test_object_plugin_nav.html \
test_bug742030.html \
test_bug742549.html \
test_bug745685.html \
$(NULL)
_BROWSER_TEST_FILES = \

View File

@ -0,0 +1,105 @@
<!doctype html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=745685
-->
<title>Test for Bug 745685</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=745685">Mozilla Bug 745685</a>
<font>Test text</font>
<font size=1>1</font>
<font size=2>2</font>
<font size=3>3</font>
<font size=4>4</font>
<font size=5>5</font>
<font size=6>6</font>
<font size=7>7</font>
<script>
/** Test for Bug 745685 **/
var referenceSizes = {};
for (var i = 1; i <= 7; i++) {
referenceSizes[i] =
getComputedStyle(document.querySelector('[size="' + i + '"]'))
.fontSize;
if (i > 1) {
isnot(referenceSizes[i], referenceSizes[i - 1],
"Sanity check: different <font size>s give different .fontSize");
}
}
function testFontSize(input, expected) {
var font = document.querySelector("font");
font.setAttribute("size", input);
is(font.getAttribute("size"), input,
"Setting doesn't round-trip (.getAttribute)");
is(font.size, input,
"Setting doesn't round-trip (.size)");
is(getComputedStyle(font).fontSize, referenceSizes[expected],
'Incorrect size for "' + input + '" : expected the same as ' + expected);
}
function testFontSizes(input, expected) {
testFontSize(input, expected);
// Leading whitespace
testFontSize(" " + input, expected);
testFontSize("\t" + input, expected);
testFontSize("\n" + input, expected);
testFontSize("\f" + input, expected);
testFontSize("\r" + input, expected);
// Trailing garbage
testFontSize(input + "abcd", expected);
testFontSize(input + ".5", expected);
testFontSize(input + "e2", expected);
}
// Parse error
testFontSizes("", 3);
// No sign
testFontSizes("0", 1);
testFontSizes("1", 1);
testFontSizes("2", 2);
testFontSizes("3", 3);
testFontSizes("4", 4);
testFontSizes("5", 5);
testFontSizes("6", 6);
testFontSizes("7", 7);
testFontSizes("8", 7);
testFontSizes("9", 7);
testFontSizes("10", 7);
testFontSizes("10000000000000000000000", 7);
// Minus sign
testFontSizes("-0", 3);
testFontSizes("-1", 2);
testFontSizes("-2", 1);
testFontSizes("-3", 1);
testFontSizes("-4", 1);
testFontSizes("-5", 1);
testFontSizes("-6", 1);
testFontSizes("-7", 1);
testFontSizes("-8", 1);
testFontSizes("-9", 1);
testFontSizes("-10", 1);
testFontSizes("-10000000000000000000000", 1);
// Plus sign
testFontSizes("+0", 3);
testFontSizes("+1", 4);
testFontSizes("+2", 5);
testFontSizes("+3", 6);
testFontSizes("+4", 7);
testFontSizes("+5", 7);
testFontSizes("+6", 7);
testFontSizes("+7", 7);
testFontSizes("+8", 7);
testFontSizes("+9", 7);
testFontSizes("+10", 7);
testFontSizes("+10000000000000000000000", 7);
// Non-HTML5 whitespace
testFontSize("\b1", 3);
testFontSize("\v1", 3);
testFontSize("\0u00a01", 3);
</script>

View File

@ -2305,7 +2305,7 @@ nsresult
nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
PRInt32 aChange)
{
NS_ASSERTION(mContentEditableCount + aChange >= 0,
NS_ASSERTION(PRInt32(mContentEditableCount) + aChange >= 0,
"Trying to decrement too much.");
mContentEditableCount += aChange;
@ -2877,8 +2877,8 @@ static const char* const gBlocks[] = {
};
static bool
ConvertToMidasInternalCommandInner(const nsAString & inCommandID,
const nsAString & inParam,
ConvertToMidasInternalCommandInner(const nsAString& inCommandID,
const nsAString& inParam,
nsACString& outCommandID,
nsACString& outParam,
bool& outIsBoolean,
@ -2892,8 +2892,7 @@ ConvertToMidasInternalCommandInner(const nsAString & inCommandID,
if (convertedCommandID.LowerCaseEqualsLiteral("usecss")) {
convertedCommandID.Assign("styleWithCSS");
invertBool = true;
}
else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
} else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
convertedCommandID.Assign("contentReadOnly");
invertBool = true;
}
@ -2908,70 +2907,84 @@ ConvertToMidasInternalCommandInner(const nsAString & inCommandID,
}
}
if (found) {
// set outCommandID (what we use internally)
outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
// set outParam & outIsBoolean based on flags from the table
outIsBoolean = gMidasCommandTable[i].convertToBoolean;
if (!aIgnoreParams) {
if (gMidasCommandTable[i].useNewParam) {
outParam.Assign(gMidasCommandTable[i].internalParamString);
}
else {
// handle checking of param passed in
if (outIsBoolean) {
// if this is a boolean value and it's not explicitly false
// (e.g. no value) we default to "true". For old backwards commands
// we invert the check (see bug 301490).
if (invertBool) {
outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
}
else {
outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
}
outParam.Truncate();
}
else {
// check to see if we need to convert the parameter
if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
const PRUnichar *start = inParam.BeginReading();
const PRUnichar *end = inParam.EndReading();
if (start != end && *start == '<' && *(end - 1) == '>') {
++start;
--end;
}
NS_ConvertUTF16toUTF8 convertedParam(Substring(start, end));
PRUint32 j;
for (j = 0; j < ArrayLength(gBlocks); ++j) {
if (convertedParam.Equals(gBlocks[j],
nsCaseInsensitiveCStringComparator())) {
outParam.Assign(gBlocks[j]);
break;
}
}
if (j == ArrayLength(gBlocks)) {
outParam.Truncate();
}
}
else {
CopyUTF16toUTF8(inParam, outParam);
}
}
}
}
} // end else for useNewParam (do convert existing param)
else {
if (!found) {
// reset results if the command is not found in our table
outCommandID.SetLength(0);
outParam.SetLength(0);
outIsBoolean = false;
return false;
}
return found;
// set outCommandID (what we use internally)
outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
// set outParam & outIsBoolean based on flags from the table
outIsBoolean = gMidasCommandTable[i].convertToBoolean;
if (aIgnoreParams) {
// No further work to do
return true;
}
if (gMidasCommandTable[i].useNewParam) {
// Just have to copy it, no checking
outParam.Assign(gMidasCommandTable[i].internalParamString);
return true;
}
// handle checking of param passed in
if (outIsBoolean) {
// If this is a boolean value and it's not explicitly false (e.g. no value)
// we default to "true". For old backwards commands we invert the check (see
// bug 301490).
if (invertBool) {
outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
} else {
outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
}
outParam.Truncate();
return true;
}
// String parameter -- see if we need to convert it (necessary for
// cmd_paragraphState and cmd_fontSize)
if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
const PRUnichar* start = inParam.BeginReading();
const PRUnichar* end = inParam.EndReading();
if (start != end && *start == '<' && *(end - 1) == '>') {
++start;
--end;
}
NS_ConvertUTF16toUTF8 convertedParam(Substring(start, end));
PRUint32 j;
for (j = 0; j < ArrayLength(gBlocks); ++j) {
if (convertedParam.Equals(gBlocks[j],
nsCaseInsensitiveCStringComparator())) {
outParam.Assign(gBlocks[j]);
break;
}
}
if (j == ArrayLength(gBlocks)) {
outParam.Truncate();
}
} else if (outCommandID.EqualsLiteral("cmd_fontSize")) {
// Per editing spec as of April 23, 2012, we need to reject the value if
// it's not a valid floating-point number surrounded by optional whitespace.
// Otherwise, we parse it as a legacy font size. For now, we just parse as
// a legacy font size regardless (matching WebKit) -- bug 747879.
outParam.Truncate();
PRInt32 size = nsContentUtils::ParseLegacyFontSize(inParam);
if (size) {
outParam.AppendInt(size);
}
} else {
CopyUTF16toUTF8(inParam, outParam);
}
return true;
}
static bool
@ -3103,7 +3116,8 @@ nsHTMLDocument::ExecCommand(const nsAString & commandID,
cmdToDispatch, paramStr, isBool, boolVal))
return NS_OK;
if (cmdToDispatch.EqualsLiteral("cmd_paragraphState") && paramStr.IsEmpty()) {
if ((cmdToDispatch.EqualsLiteral("cmd_paragraphState") ||
cmdToDispatch.EqualsLiteral("cmd_fontSize")) && paramStr.IsEmpty()) {
// Invalid value
return NS_OK;
}

View File

@ -49,7 +49,6 @@
#include "MediaResource.h"
#include "nsMathUtils.h"
#include "prlog.h"
#include "nsIPrivateBrowsingService.h"
#include "mozilla/Preferences.h"
#include "FileBlockCache.h"
@ -123,7 +122,7 @@ void nsMediaCacheFlusher::Init()
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(gMediaCacheFlusher, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
observerService->AddObserver(gMediaCacheFlusher, "last-pb-context-exited", true);
}
}
@ -374,8 +373,7 @@ protected:
NS_IMETHODIMP
nsMediaCacheFlusher::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData)
{
if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0 &&
NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
if (strcmp(aTopic, "last-pb-context-exited") == 0) {
nsMediaCache::Flush();
}
return NS_OK;

View File

@ -255,10 +255,10 @@ nsXBLJSClass::Destroy()
// Constructors/Destructors
nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
: mPrototypeBinding(aBinding),
mInsertionPointTable(nsnull),
mIsStyleBinding(true),
mMarkedForDeath(false)
: mIsStyleBinding(true),
mMarkedForDeath(false),
mPrototypeBinding(aBinding),
mInsertionPointTable(nsnull)
{
NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
// Grab a ref to the document info so the prototype binding won't die

View File

@ -156,6 +156,9 @@ public:
// MEMBER VARIABLES
protected:
bool mIsStyleBinding;
bool mMarkedForDeath;
nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
@ -164,9 +167,6 @@ protected:
// A hash from nsIContent* -> (a sorted array of nsXBLInsertionPoint)
nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>* mInsertionPointTable;
bool mIsStyleBinding;
bool mMarkedForDeath;
};
#endif // nsXBLBinding_h_

View File

@ -83,6 +83,7 @@ XPIDLSRCS = \
nsIRefreshURI.idl \
nsIContentViewerContainer.idl \
nsIDocumentLoaderFactory.idl \
nsIPrivacyTransitionObserver.idl \
$(NULL)
EXPORTS = \
@ -114,6 +115,8 @@ CPPSRCS = \
# static lib.
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += \

View File

@ -68,7 +68,8 @@ nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
if (NS_UNLIKELY(!initializedPrefCache)) {
// Lock the pref so that the user's changes to it, if any, are ignored.
nsIPrefBranch *root = Preferences::GetRootBranch();
root->LockPref("b2g.ignoreXFrameOptions");
if (XRE_GetProcessType() != GeckoProcessType_Content)
root->LockPref("b2g.ignoreXFrameOptions");
Preferences::AddBoolVarCache(&sIgnoreXFrameOptions, "b2g.ignoreXFrameOptions");
initializedPrefCache = true;

View File

@ -41,6 +41,7 @@
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Util.h"
#ifdef MOZ_LOGGING
@ -106,6 +107,7 @@
#include "nsIScriptChannel.h"
#include "nsIOfflineCacheUpdate.h"
#include "nsITimedChannel.h"
#include "nsIPrivacyTransitionObserver.h"
#include "nsCPrefetchService.h"
#include "nsJSON.h"
#include "IHistory.h"
@ -714,6 +716,19 @@ ConvertLoadTypeToNavigationType(PRUint32 aLoadType)
static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
static void
IncreasePrivateDocShellCount()
{
gNumberOfPrivateDocShells++;
if (gNumberOfPrivateDocShells > 1 ||
XRE_GetProcessType() != GeckoProcessType_Content) {
return;
}
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
cc->SendPrivateDocShellsExist(true);
}
static void
DecreasePrivateDocShellCount()
{
@ -721,9 +736,16 @@ DecreasePrivateDocShellCount()
gNumberOfPrivateDocShells--;
if (!gNumberOfPrivateDocShells)
{
if (XRE_GetProcessType() == GeckoProcessType_Content) {
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
cc->SendPrivateDocShellsExist(false);
return;
}
nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
if (obsvc)
if (obsvc) {
obsvc->NotifyObservers(nsnull, "last-pb-context-exited", nsnull);
}
}
}
@ -2020,10 +2042,11 @@ nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
NS_IMETHODIMP
nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
{
if (aUsePrivateBrowsing != mInPrivateBrowsing) {
bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
if (changed) {
mInPrivateBrowsing = aUsePrivateBrowsing;
if (aUsePrivateBrowsing) {
gNumberOfPrivateDocShells++;
IncreasePrivateDocShellCount();
} else {
DecreasePrivateDocShellCount();
}
@ -2036,9 +2059,32 @@ nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
shell->SetUsePrivateBrowsing(aUsePrivateBrowsing);
}
}
if (changed) {
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
if (!obs) {
mPrivacyObservers.RemoveElement(ref);
} else {
obs->PrivateModeChanged(aUsePrivateBrowsing);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObserver)
{
nsWeakPtr weakObs = do_GetWeakReference(aObserver);
if (!weakObs) {
return NS_ERROR_NOT_AVAILABLE;
}
return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);

View File

@ -847,6 +847,7 @@ protected:
private:
nsCOMPtr<nsIAtom> mForcedCharset;
nsCOMPtr<nsIAtom> mParentCharset;
nsTObserverArray<nsWeakPtr> mPrivacyObservers;
PRInt32 mParentCharsetSource;
#ifdef DEBUG

View File

@ -71,8 +71,9 @@ interface nsIDOMStorage;
interface nsIPrincipal;
interface nsIWebBrowserPrint;
interface nsIVariant;
interface nsIPrivacyTransitionObserver;
[scriptable, uuid(c7325422-817e-4321-957a-c0bdd764941d)]
[scriptable, uuid(6f60ac96-fa2c-41a5-92b4-29aaadbd7a7b)]
interface nsIDocShell : nsISupports
{
/**
@ -603,6 +604,12 @@ interface nsIDocShell : nsISupports
*/
attribute PRInt32 parentCharsetSource;
/**
* Add an observer to the list of parties to be notified when this docshell's
* private browsing status is changed. |obs| must support weak references.
*/
void addWeakPrivacyTransitionObserver(in nsIPrivacyTransitionObserver obs);
/*
* Is this docshell a browser frame (i.e., does it correspond to an <iframe
* mozbrowser>)? The frameloader is responsible for setting this property

View File

@ -0,0 +1,11 @@
/* 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 "nsISupports.idl"
[scriptable, function, uuid(b4b1449d-0ef0-47f5-b62e-adc57fd49702)]
interface nsIPrivacyTransitionObserver : nsISupports
{
void privateModeChanged(in bool enabled);
};

View File

@ -47,10 +47,14 @@ DIRS += chrome \
navigation \
$(NULL)
XPCSHELL_TESTS = unit
include $(DEPTH)/config/autoconf.mk
XPCSHELL_TESTS = unit
# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
ifneq ($(OS_ARCH),Darwin)
XPCSHELL_TESTS += unit_ipc
endif
ifneq (mobile,$(MOZ_BUILD_APP))
DIRS += browser
endif

View File

@ -0,0 +1,23 @@
if (typeof Cc === "undefined")
Cc = Components.classes;
if (typeof Ci === "undefined")
Ci = Components.interfaces;
function destroy_transient_docshell() {
var docshell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
docshell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = true;
do_test_pending();
do_timeout(0, Components.utils.forceGC);
}
function run_test() {
var obs = {
observe: function(aSubject, aTopic, aData) {
do_check_eq(aTopic, "last-pb-context-exited");
do_test_finished();
}
};
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.addObserver(obs, "last-pb-context-exited", false);
destroy_transient_docshell();
}

View File

@ -0,0 +1,23 @@
var gNotifications = 0;
var observer = {
QueryInterface: function(iid) {
if (Ci.nsIPrivacyTransitionObserver.equals(iid) ||
Ci.nsISupportsWeakReference.equals(iid) ||
Ci.nsISupports.equals(iid))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
privateModeChanged: function(enabled) {
gNotifications++;
}
}
function run_test() {
var docshell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
docshell.addWeakPrivacyTransitionObserver(observer);
docshell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = true;
docshell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = false;
do_check_eq(gNotifications, 2);
}

View File

@ -5,3 +5,5 @@ tail =
[test_bug414201_jfif.js]
[test_bug442584.js]
[test_nsIDownloadHistory.js]
[test_pb_notification.js]
[test_privacy_transition.js]

View File

@ -0,0 +1,20 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
function run_test() {
var notifications = 0;
var obs = {
observe: function(aSubject, aTopic, aData) {
do_check_eq(aTopic, "last-pb-context-exited");
notifications++;
}
};
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.addObserver(obs, "last-pb-context-exited", false);
run_test_in_child("../unit/test_pb_notification.js",
function() {
do_check_eq(notifications, 1);
do_test_finished();
});
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_pb_notification_ipc.js]

View File

@ -98,6 +98,7 @@
#include "mozilla/TimeStamp.h"
#include "nsIDOMTouchEvent.h"
#include "nsIInlineEventHandlers.h"
#include "nsWrapperCacheInlines.h"
// JS includes
#include "jsapi.h"

View File

@ -188,9 +188,11 @@ class CGList(CGThing):
def prepend(self, child):
self.children.insert(0, child)
def declare(self):
return self.joiner.join([child.declare() for child in self.children])
return self.joiner.join([child.declare() for child in self.children
if child is not None])
def define(self):
return self.joiner.join([child.define() for child in self.children])
return self.joiner.join([child.define() for child in self.children
if child is not None])
class CGGeneric(CGThing):
"""
@ -237,7 +239,7 @@ class CGWrapper(CGThing):
"""
def __init__(self, child, pre="", post="", declarePre=None,
declarePost=None, definePre=None, definePost=None,
declareOnly=False):
declareOnly=False, reindent=False):
CGThing.__init__(self)
self.child = child
self.declarePre = declarePre or pre
@ -245,12 +247,25 @@ class CGWrapper(CGThing):
self.definePre = definePre or pre
self.definePost = definePost or post
self.declareOnly = declareOnly
self.reindent = reindent
def declare(self):
return self.declarePre + self.child.declare() + self.declarePost
decl = self.child.declare()
if self.reindent:
# We don't use lineStartDetector because we don't want to
# insert whitespace at the beginning of our _first_ line.
decl = stripTrailingWhitespace(
decl.replace("\n", "\n" + (" " * len(self.declarePre))))
return self.declarePre + decl + self.declarePost
def define(self):
if self.declareOnly:
return ''
return self.definePre + self.child.define() + self.definePost
defn = self.child.define()
if self.reindent:
# We don't use lineStartDetector because we don't want to
# insert whitespace at the beginning of our _first_ line.
defn = stripTrailingWhitespace(
defn.replace("\n", "\n" + (" " * len(self.definePre))))
return self.definePre + defn + self.definePost
class CGNamespace(CGWrapper):
def __init__(self, namespace, child, declareOnly=False):
@ -810,45 +825,54 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
idsToInit.append(props.variableName(False))
if props.hasChromeOnly() and not self.descriptor.workers:
idsToInit.append(props.variableName(True))
initIds = ""
if len(idsToInit) > 0:
init = ' ||\n '.join(["!InitIds(aCx, %s, %s_ids)" % (varname, varname)
for varname in idsToInit])
initIds = CGList(
[CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
varname in idsToInit], ' ||\n')
if len(idsToInit) > 1:
init = '(' + init + ')'
initIds = (" if (%s_ids[0] == JSID_VOID &&\n" +
" %s) {\n" +
" %s_ids[0] = JSID_VOID;\n"
" return NULL;\n"
" }\n\n") % (idsToInit[0], init, idsToInit[0])
initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
initIds = CGList(
[CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
"\n")
initIds = CGWrapper(initIds, pre="if (", post=") {", reindent=True)
initIds = CGList(
[initIds,
CGGeneric((" %s_ids[0] = JSID_VOID;\n"
" return NULL;") % idsToInit[0]),
CGGeneric("}")],
"\n")
else:
initIds = None
getParentProto = (" JSObject* parentProto = %s;\n" +
" if (!parentProto) {\n" +
" return NULL;\n" +
" }") % getParentProto
getParentProto = ("JSObject* parentProto = %s;\n"
"if (!parentProto) {\n"
" return NULL;\n"
"}") % getParentProto
call = """return bindings::CreateInterfaceObjects(aCx, aGlobal, parentProto,
%s, %s,
%%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,
%s);""" % (
call = CGGeneric(("return bindings::CreateInterfaceObjects(aCx, aGlobal, parentProto,\n"
" %s, %s,\n"
" %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,\n"
" %s);") % (
"&PrototypeClass" if needInterfacePrototypeObject else "NULL",
"&InterfaceObjectClass" if needInterfaceObject else "NULL",
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL"))
if self.properties.hasChromeOnly():
if self.descriptor.workers:
accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
else:
accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
chrome = """
if (%s) {
%s
}
""" % (accessCheck, call.replace("\n ", "\n ") % self.properties.variableNames(True))
accessCheck = "if (" + accessCheck + ") {\n"
chrome = CGWrapper(CGGeneric((CGIndenter(call).define() % self.properties.variableNames(True))),
pre=accessCheck, post="\n}")
else:
chrome = ""
return initIds + getParentProto + chrome + "\n " + call % self.properties.variableNames(False)
chrome = None
functionBody = CGList(
[CGGeneric(getParentProto), initIds, chrome,
CGGeneric(call.define() % self.properties.variableNames(False))],
"\n\n")
return CGIndenter(functionBody).define()
class CGGetPerInterfaceObject(CGAbstractMethod):
"""
@ -912,7 +936,7 @@ def CheckPref(descriptor, scopeName, varName, retval, wrapperCache = None):
else:
wrapperCache = ""
return """
if (!%s->ParisBindingsEnabled()) {
if (!%s->ExperimentalBindingsEnabled()) {
%s %s = false;
return %s;
}
@ -1219,8 +1243,8 @@ def getArgumentConversionTemplate(type, descriptor):
nullBehavior = "eNull"
undefinedBehavior = "eNull"
else:
nullBehavior = "eDefaultNullBehavior"
undefinedBehavior = "eDefaultUndefinedBehavior"
nullBehavior = "eStringify"
undefinedBehavior = "eStringify"
return (
" xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
@ -1488,11 +1512,15 @@ def getWrapTemplateForTypeImpl(type, result, descriptorProvider,
if type.isCallback() and not type.isInterface():
# XXXbz we're going to assume that callback types are always
# nullable and always have [TreatNonCallableAsNull] for now.
# See comments in WrapNewBindingObject explaining why we need
# to wrap here.
return """
${jsvalRef} = JS::ObjectOrNullValue(%s);
return JS_WrapValue(cx, ${jsvalPtr});""" % result
if type.tag() == IDLType.Tags.any:
# See comments in WrapNewBindingObject explaining why we need
# to wrap here.
return """
${jsvalRef} = %s;\n
return JS_WrapValue(cx, ${jsvalPtr});""" % result
@ -1736,12 +1764,12 @@ class CGCase(CGList):
def __init__(self, expression, body, fallThrough=False):
CGList.__init__(self, [], "\n")
self.append(CGWrapper(CGGeneric(expression), pre="case ", post=": {"))
if body is not None:
self.append(CGIndenter(body))
bodyList = CGList([body], "\n")
if fallThrough:
self.append(CGIndenter(CGGeneric("/* Fall through */")))
bodyList.append(CGGeneric("/* Fall through */"))
else:
self.append(CGIndenter(CGGeneric("break;")))
bodyList.append(CGGeneric("break;"))
self.append(CGIndenter(bodyList));
self.append(CGGeneric("}"))
class CGMethodCall(CGThing):

View File

@ -36,6 +36,14 @@ public:
mIsNull = false;
}
// For cases when |T| is some type with nontrivial copy behavior, we may want
// to get a reference to our internal copy of T and work with it directly
// instead of relying on the copying version of SetValue().
T& SetValue() {
mIsNull = false;
return mValue;
}
void SetNull() {
mIsNull = true;
}

View File

@ -69,7 +69,8 @@ CreateInterfacePrototypeObject(JSContext* cx, JSObject* global,
JSPropertySpec* properties,
ConstantSpec* constants)
{
JSObject* ourProto = JS_NewObject(cx, protoClass, parentProto, global);
JSObject* ourProto = JS_NewObjectWithUniqueType(cx, protoClass, parentProto,
global);
if (!ourProto) {
return NULL;
}

View File

@ -270,12 +270,12 @@ WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp)
}
}
// Now make sure that |obj| is wrapped for the compartment of |scope|
// correctly. That means entering the compartment of |scope|.
JSAutoEnterCompartment ac;
if (!ac.enter(cx, scope)) {
return false;
}
// When called via XrayWrapper, we end up here while running in the
// chrome compartment. But the obj we have would be created in
// whatever the content compartment is. So at this point we need to
// make sure it's correctly wrapped for the compartment of |scope|.
// cx should already be in the compartment of |scope| here.
MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
*vp = JS::ObjectValue(*obj);
return JS_WrapValue(cx, vp);
}

View File

@ -819,5 +819,13 @@ ContentChild::RecvSetID(const PRUint64 &id)
return true;
}
bool
ContentChild::RecvLastPrivateDocShellDestroyed()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(nsnull, "last-pb-context-exited", nsnull);
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -170,6 +170,8 @@ public:
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID);
virtual bool RecvSetID(const PRUint64 &id);
virtual bool RecvLastPrivateDocShellDestroyed();
#ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; }
#endif

View File

@ -163,6 +163,7 @@ MemoryReportRequestParent::~MemoryReportRequestParent()
}
nsTArray<ContentParent*>* ContentParent::gContentParents;
nsTArray<ContentParent*>* ContentParent::gPrivateContent;
// The first content child has ID 1, so the chrome process can have ID 0.
static PRUint64 gContentChildID = 1;
@ -211,6 +212,7 @@ ContentParent::Init()
obs->AddObserver(this, "memory-pressure", false);
obs->AddObserver(this, "child-gc-request", false);
obs->AddObserver(this, "child-cc-request", false);
obs->AddObserver(this, "last-pb-context-exited", false);
#ifdef ACCESSIBILITY
obs->AddObserver(this, "a11y-init-or-shutdown", false);
#endif
@ -308,6 +310,7 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-gc-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-cc-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "last-pb-context-exited");
#ifdef ACCESSIBILITY
obs->RemoveObserver(static_cast<nsIObserver*>(this), "a11y-init-or-shutdown");
#endif
@ -339,6 +342,14 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
}
}
if (gPrivateContent) {
gPrivateContent->RemoveElement(this);
if (!gPrivateContent->Length()) {
delete gPrivateContent;
gPrivateContent = NULL;
}
}
mIsAlive = false;
if (obs) {
@ -723,6 +734,9 @@ ContentParent::Observe(nsISupports* aSubject,
else if (!strcmp(aTopic, "child-cc-request")){
SendCycleCollect();
}
else if (!strcmp(aTopic, "last-pb-context-exited")) {
unused << SendLastPrivateDocShellDestroyed();
}
#ifdef ACCESSIBILITY
// Make sure accessibility is running in content process when accessibility
// gets initiated in chrome process.
@ -1234,5 +1248,24 @@ ContentParent::RecvScriptError(const nsString& aMessage,
return true;
}
bool
ContentParent::RecvPrivateDocShellsExist(const bool& aExist)
{
if (!gPrivateContent)
gPrivateContent = new nsTArray<ContentParent*>;
if (aExist) {
gPrivateContent->AppendElement(this);
} else {
gPrivateContent->RemoveElement(this);
if (!gPrivateContent->Length()) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(nsnull, "last-pb-context-exited", nsnull);
delete gPrivateContent;
gPrivateContent = NULL;
}
}
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -110,6 +110,7 @@ protected:
private:
static nsTArray<ContentParent*>* gContentParents;
static nsTArray<ContentParent*>* gPrivateContent;
// Hide the raw constructor methods since we don't want client code
// using them.
@ -222,6 +223,8 @@ private:
const PRUint32& aFlags,
const nsCString& aCategory);
virtual bool RecvPrivateDocShellsExist(const bool& aExist);
GeckoChildProcessHost* mSubprocess;
PRInt32 mGeolocationWatchID;

View File

@ -149,6 +149,9 @@ child:
SetID(PRUint64 id);
// Notify child that last-pb-context-exited notification was observed
LastPrivateDocShellDestroyed();
parent:
PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
@ -226,6 +229,9 @@ parent:
sync GetShowPasswordSetting()
returns (bool showPassword);
// Notify the parent of the presence or absence of private docshells
PrivateDocShellsExist(bool aExist);
both:
AsyncMessage(nsString aMessage, nsString aJSON);

View File

@ -54,7 +54,6 @@
#include "nsNPAPIPluginStreamListener.h"
#include "nsIServiceManager.h"
#include "nsThreadUtils.h"
#include "nsIPrivateBrowsingService.h"
#include "mozilla/Preferences.h"
#include "nsIPluginStreamListener.h"
@ -105,6 +104,8 @@
#include "nsJSNPRuntime.h"
#include "nsIHttpAuthManager.h"
#include "nsICookieService.h"
#include "nsILoadContext.h"
#include "nsIDocShell.h"
#include "nsNetUtil.h"
@ -2135,11 +2136,12 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
}
case NPNVprivateModeBool: {
nsCOMPtr<nsIPrivateBrowsingService> pbs = do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
if (pbs) {
bool enabled;
pbs->GetPrivateBrowsingEnabled(&enabled);
*(NPBool*)result = (NPBool)enabled;
nsCOMPtr<nsIDocument> doc = GetDocumentFromNPP(npp);
nsCOMPtr<nsPIDOMWindow> domwindow = do_QueryInterface(doc);
if (domwindow) {
nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell();
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
*(NPBool*)result = (NPBool)(loadContext && loadContext->UsePrivateBrowsing());
return NPERR_NO_ERROR;
}
return NPERR_GENERIC_ERROR;

View File

@ -48,7 +48,6 @@
#include "nsPluginHost.h"
#include "nsPluginSafety.h"
#include "nsPluginLogging.h"
#include "nsIPrivateBrowsingService.h"
#include "nsContentUtils.h"
#include "nsIDocument.h"
@ -152,7 +151,7 @@ nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsIPluginInst
PL_strcpy(mMIMEType, aMIMEType);
}
}
return Start();
}
@ -1143,7 +1142,7 @@ nsNPAPIPluginInstance::GetPluginAPIVersion(PRUint16* version)
}
nsresult
nsNPAPIPluginInstance::PrivateModeStateChanged()
nsNPAPIPluginInstance::PrivateModeStateChanged(bool enabled)
{
if (RUNNING != mRunning)
return NS_OK;
@ -1155,23 +1154,15 @@ nsNPAPIPluginInstance::PrivateModeStateChanged()
NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
if (pluginFunctions->setvalue) {
PluginDestructionGuard guard(this);
nsCOMPtr<nsIPrivateBrowsingService> pbs = do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
if (pbs) {
bool pme = false;
nsresult rv = pbs->GetPrivateBrowsingEnabled(&pme);
if (NS_FAILED(rv))
return rv;
if (!pluginFunctions->setvalue)
return NS_ERROR_FAILURE;
NPError error;
NPBool value = static_cast<NPBool>(pme);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this);
return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
}
}
return NS_ERROR_FAILURE;
PluginDestructionGuard guard(this);
NPError error;
NPBool value = static_cast<NPBool>(enabled);
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this);
return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
}
class DelayUnscheduleEvent : public nsRunnable {

View File

@ -204,7 +204,7 @@ public:
already_AddRefed<nsPIDOMWindow> GetDOMWindow();
nsresult PrivateModeStateChanged();
nsresult PrivateModeStateChanged(bool aEnabled);
nsresult GetDOMElement(nsIDOMElement* *result);

View File

@ -86,7 +86,6 @@
#include "nsIScriptChannel.h"
#include "nsIBlocklistService.h"
#include "nsVersionComparator.h"
#include "nsIPrivateBrowsingService.h"
#include "nsIObjectLoadingContent.h"
#include "nsIWritablePropertyBag2.h"
#include "nsPluginStreamListenerPeer.h"
@ -359,7 +358,6 @@ nsPluginHost::nsPluginHost()
mozilla::services::GetObserverService();
if (obsService) {
obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
obsService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
#ifdef MOZ_WIDGET_ANDROID
obsService->AddObserver(this, "application-foreground", false);
obsService->AddObserver(this, "application-background", false);
@ -2697,19 +2695,20 @@ nsPluginHost::ReadPluginInfo()
return rv;
// kPluginRegistryVersion
PRInt32 vdiff = NS_CompareVersions(values[1], kPluginRegistryVersion);
PRInt32 vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
mozilla::Version version(values[1]);
// If this is a registry from some future version then don't attempt to read it
if (vdiff > 0)
return rv;
// If this is a registry from before the minimum then don't attempt to read it
if (NS_CompareVersions(values[1], kMinimumRegistryVersion) < 0)
if (version < kMinimumRegistryVersion)
return rv;
// Registry v0.10 and upwards includes the plugin version field
bool regHasVersion = NS_CompareVersions(values[1], "0.10") >= 0;
bool regHasVersion = (version >= "0.10");
// Registry v0.13 and upwards includes the architecture
if (NS_CompareVersions(values[1], "0.13") >= 0) {
if (version >= "0.13") {
char* archValues[6];
if (!reader.NextLine()) {
@ -2743,7 +2742,7 @@ nsPluginHost::ReadPluginInfo()
}
// Registry v0.13 and upwards includes the list of invalid plugins
bool hasInvalidPlugins = (NS_CompareVersions(values[1], "0.13") >= 0);
bool hasInvalidPlugins = (version >= "0.13");
if (!ReadSectionHeader(reader, "PLUGINS"))
return rv;
@ -2751,7 +2750,7 @@ nsPluginHost::ReadPluginInfo()
#if defined(XP_MACOSX)
bool hasFullPathInFileNameField = false;
#else
bool hasFullPathInFileNameField = (NS_CompareVersions(values[1], "0.11") < 0);
bool hasFullPathInFileNameField = (version < "0.11");
#endif
while (reader.NextLine()) {
@ -3294,21 +3293,6 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
UnloadPlugins();
sInst->Release();
}
if (!nsCRT::strcmp(NS_PRIVATE_BROWSING_SWITCH_TOPIC, aTopic)) {
// inform all active plugins of changed private mode state
for (PRUint32 i = 0; i < mInstances.Length(); i++) {
mInstances[i]->PrivateModeStateChanged();
}
}
if (!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
// Unload or load plugins as needed
if (mPluginsDisabled) {
UnloadPlugins();
} else {
LoadPlugins();
}
}
#ifdef MOZ_WIDGET_ANDROID
if (!nsCRT::strcmp("application-background", aTopic)) {
for(PRUint32 i = 0; i < mInstances.Length(); i++) {

View File

@ -100,6 +100,7 @@ using mozilla::DefaultXDisplay;
#include "nsIScrollableFrame.h"
#include "nsIImageLoadingContent.h"
#include "nsIObjectLoadingContent.h"
#include "nsIDocShell.h"
#include "nsContentCID.h"
#include "nsWidgetsCID.h"
@ -404,10 +405,12 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner()
}
}
NS_IMPL_ISUPPORTS3(nsPluginInstanceOwner,
NS_IMPL_ISUPPORTS5(nsPluginInstanceOwner,
nsIPluginInstanceOwner,
nsIPluginTagInfo,
nsIDOMEventListener)
nsIDOMEventListener,
nsIPrivacyTransitionObserver,
nsISupportsWeakReference)
nsresult
nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance *aInstance)
@ -427,6 +430,17 @@ nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance *aInstance)
mInstance = aInstance;
nsCOMPtr<nsIDocument> doc;
GetDocument(getter_AddRefs(doc));
if (doc) {
nsCOMPtr<nsPIDOMWindow> domWindow = doc->GetWindow();
if (domWindow) {
nsCOMPtr<nsIDocShell> docShell = domWindow->GetDocShell();
if (docShell)
docShell->AddWeakPrivacyTransitionObserver(this);
}
}
return NS_OK;
}
@ -3798,6 +3812,11 @@ void nsPluginInstanceOwner::FixUpURLS(const nsString &name, nsAString &value)
}
}
NS_IMETHODIMP nsPluginInstanceOwner::PrivateModeChanged(bool aEnabled)
{
return mInstance ? mInstance->PrivateModeStateChanged(aEnabled) : NS_OK;
}
// nsPluginDOMContextMenuListener class implementation
nsPluginDOMContextMenuListener::nsPluginDOMContextMenuListener()

View File

@ -52,10 +52,12 @@
#include "nsCOMPtr.h"
#include "nsIPluginInstanceOwner.h"
#include "nsIPluginTagInfo.h"
#include "nsIPrivacyTransitionObserver.h"
#include "nsIDOMEventListener.h"
#include "nsIScrollPositionListener.h"
#include "nsPluginHost.h"
#include "nsPluginNativeWindow.h"
#include "nsWeakReference.h"
#include "gfxRect.h"
// X.h defines KeyPress
@ -103,7 +105,9 @@ namespace mozilla {
class nsPluginInstanceOwner : public nsIPluginInstanceOwner,
public nsIPluginTagInfo,
public nsIDOMEventListener,
public nsIScrollPositionListener
public nsIScrollPositionListener,
public nsIPrivacyTransitionObserver,
public nsSupportsWeakReference
{
public:
nsPluginInstanceOwner();
@ -111,6 +115,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPLUGININSTANCEOWNER
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
NS_IMETHOD GetURL(const char *aURL, const char *aTarget,
nsIInputStream *aPostStream,

View File

@ -55,7 +55,7 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
[scriptable, uuid(2e14b183-29d4-4282-9475-589277a70654)]
[scriptable, uuid(7861fe14-9977-413f-a893-3e1000c40817)]
interface nsIEditor : nsISupports
{
@ -497,6 +497,12 @@ interface nsIEditor : nsISupports
*/
void deleteNode(in nsIDOMNode child);
/**
* Returns true if markNodeDirty() has any effect. Returns false if
* markNodeDirty() is a no-op.
*/
[notxpcom] boolean outputsMozDirty();
/**
* markNodeDirty() sets a special dirty attribute on the node.
* Usually this will be called immediately after creating a new node.

View File

@ -147,34 +147,28 @@ extern nsIParserService *sParserService;
//---------------------------------------------------------------------------
nsEditor::nsEditor()
: mModCount(0)
: mPlaceHolderName(nsnull)
, mSelState(nsnull)
, mPhonetic(nsnull)
, mModCount(0)
, mFlags(0)
, mUpdateCount(0)
, mSpellcheckCheckboxState(eTriUnset)
, mPlaceHolderTxn(nsnull)
, mPlaceHolderName(nsnull)
, mPlaceHolderBatch(0)
, mSelState(nsnull)
, mSavedSel()
, mRangeUpdater()
, mAction(nsnull)
, mDirection(eNone)
, mIMETextNode(nsnull)
, mHandlingActionCount(0)
, mIMETextOffset(0)
, mIMEBufferLength(0)
, mDirection(eNone)
, mDocDirtyState(-1)
, mSpellcheckCheckboxState(eTriUnset)
, mInIMEMode(false)
, mIsIMEComposing(false)
, mShouldTxnSetSelection(true)
, mDidPreDestroy(false)
, mDidPostCreate(false)
, mDocDirtyState(-1)
, mDocWeak(nsnull)
, mPhonetic(nsnull)
, mHandlingActionCount(0)
, mHandlingTrustedAction(false)
, mDispatchInputEvent(true)
{
//initialize member variables here
}
nsEditor::~nsEditor()
@ -1270,14 +1264,28 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute)
}
bool
nsEditor::OutputsMozDirty()
{
// Return true for Composer (!eEditorAllowInteraction) or mail
// (eEditorMailMask), but false for webpages.
return !(mFlags & nsIPlaintextEditor::eEditorAllowInteraction) ||
(mFlags & nsIPlaintextEditor::eEditorMailMask);
}
NS_IMETHODIMP
nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
{
// mark the node dirty.
nsCOMPtr<nsIContent> element (do_QueryInterface(aNode));
if (element)
// Mark the node dirty, but not for webpages (bug 599983)
if (!OutputsMozDirty()) {
return NS_OK;
}
nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
if (element) {
element->SetAttr(kNameSpaceID_None, nsEditProperty::mozdirty,
EmptyString(), false);
}
return NS_OK;
}

View File

@ -218,8 +218,6 @@ public:
void SwitchTextDirectionTo(PRUint32 aDirection);
protected:
nsCString mContentMIMEType; // MIME type of the doc we are editing.
nsresult DetermineCurrentDirection();
/** create a transaction for setting aAttribute to aValue on aElement
@ -793,59 +791,60 @@ public:
};
protected:
PRUint32 mModCount; // number of modifications (for undo/redo stack)
PRUint32 mFlags; // behavior flags. See nsIPlaintextEditor.idl for the flags we use.
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController
PRInt32 mUpdateCount;
// Spellchecking
enum Tristate {
eTriUnset,
eTriFalse,
eTriTrue
} mSpellcheckCheckboxState;
};
// Spellchecking
nsCString mContentMIMEType; // MIME type of the doc we are editing.
nsCOMPtr<nsIInlineSpellChecker> mInlineSpellChecker;
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction
PRInt32 mPlaceHolderBatch; // nesting count for batching
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsSelectionState mSavedSel; // cached selection for nsAutoSelectionReset
nsRangeUpdater mRangeUpdater; // utility class object for maintaining preserved ranges
nsCOMPtr<mozilla::dom::Element> mRootElement; // cached root node
PRInt32 mAction; // the current editor action
EDirection mDirection; // the current direction of editor action
// data necessary to build IME transactions
nsCOMPtr<mozilla::dom::Element> mRootElement; // cached root node
nsCOMPtr<nsIPrivateTextRangeList> mIMETextRangeList; // IME special selection ranges
nsCOMPtr<nsIDOMCharacterData> mIMETextNode; // current IME text node
PRUint32 mIMETextOffset; // offset in text node where IME comp string begins
PRUint32 mIMEBufferLength; // current length of IME comp string
bool mInIMEMode; // are we inside an IME composition?
bool mIsIMEComposing; // is IME in composition state?
// This is different from mInIMEMode. see Bug 98434.
nsCOMPtr<nsIDOMEventTarget> mEventTarget; // The form field as an event receiver
nsCOMPtr<nsIDOMEventListener> mEventListener;
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsWeakPtr mDocWeak; // weak reference to the nsIDOMDocument
nsIAtom *mPlaceHolderName; // name of placeholder transaction
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
nsString *mPhonetic;
bool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
bool mDidPreDestroy; // whether PreDestroy has been called
bool mDidPostCreate; // whether PostCreate has been called
// various listeners
// various listeners
nsCOMArray<nsIEditActionListener> mActionListeners; // listens to all low level actions on the doc
nsCOMArray<nsIEditorObserver> mEditorObservers; // just notify once per high level change
nsCOMArray<nsIDocumentStateListener> mDocStateListeners;// listen to overall doc state (dirty or not, just created, etc)
PRInt8 mDocDirtyState; // -1 = not initialized
nsWeakPtr mDocWeak; // weak reference to the nsIDOMDocument
// The form field as an event receiver
nsCOMPtr<nsIDOMEventTarget> mEventTarget;
nsSelectionState mSavedSel; // cached selection for nsAutoSelectionReset
nsRangeUpdater mRangeUpdater; // utility class object for maintaining preserved ranges
nsString* mPhonetic;
PRUint32 mModCount; // number of modifications (for undo/redo stack)
PRUint32 mFlags; // behavior flags. See nsIPlaintextEditor.idl for the flags we use.
nsCOMPtr<nsIDOMEventListener> mEventListener;
PRInt32 mUpdateCount;
PRUint32 mHandlingActionCount;
PRInt32 mPlaceHolderBatch; // nesting count for batching
PRInt32 mAction; // the current editor action
PRUint32 mHandlingActionCount;
PRUint32 mIMETextOffset; // offset in text node where IME comp string begins
PRUint32 mIMEBufferLength; // current length of IME comp string
EDirection mDirection; // the current direction of editor action
PRInt8 mDocDirtyState; // -1 = not initialized
PRUint8 mSpellcheckCheckboxState; // a Tristate value
bool mInIMEMode; // are we inside an IME composition?
bool mIsIMEComposing; // is IME in composition state?
// This is different from mInIMEMode. see Bug 98434.
bool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
bool mDidPreDestroy; // whether PreDestroy has been called
bool mDidPostCreate; // whether PostCreate has been called
bool mHandlingTrustedAction;
bool mDispatchInputEvent;

View File

@ -50,6 +50,7 @@ _TEST_FILES = \
test_bug567213.html \
file_bug586662.html \
test_bug586662.html \
test_bug599983.html \
$(NULL)
_CHROME_TEST_FILES = \
@ -57,6 +58,7 @@ _CHROME_TEST_FILES = \
test_bug46555.html \
test_bug646194.xul \
test_dragdrop.html \
test_bug599983.xul \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,16 @@
<!doctype html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=599983
-->
<title>Test for Bug 599983</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=599983">Mozilla Bug 599983</a>
<div contenteditable>foo</div>
<script>
getSelection().selectAllChildren(document.querySelector("div"));
document.execCommand("bold");
is(document.querySelector("[_moz_dirty]"), null,
"No _moz_dirty allowed in webpages");
</script>

View File

@ -0,0 +1,70 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin"
type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=599983
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Mozilla Bug 599983" onload="runTest()">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=599983"
target="_blank">Mozilla Bug 599983</a>
<editor xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="editor"
editortype="html"
src="about:blank" />
</body>
<script type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
const kAllowInteraction = Components.interfaces.nsIPlaintextEditor
.eEditorAllowInteraction;
const kMailMask = Components.interfaces.nsIPlaintextEditor.eEditorMailMask;
function runTest() {
testEditor(false, false);
testEditor(false, true);
testEditor(true, false);
testEditor(true, true);
SimpleTest.finish();
}
function testEditor(setAllowInteraction, setMailMask) {
var desc = " with " + (setAllowInteraction ? "" : "no ") +
"eEditorAllowInteraction and " +
(setMailMask ? "" : "no ") + "eEditorMailMask";
var editorElem = document.getElementById("editor");
var editorObj = editorElem.getEditor(editorElem.contentWindow);
editorObj.flags = (setAllowInteraction ? kAllowInteraction : 0) |
(setMailMask ? kMailMask : 0);
var editorDoc = editorElem.contentDocument;
editorDoc.body.innerHTML = "<p>foo<p>bar";
editorDoc.getSelection().selectAllChildren(editorDoc.body.firstChild);
editorDoc.execCommand("bold");
var createsDirty = !setAllowInteraction || setMailMask;
(createsDirty ? isnot : is)(editorDoc.querySelector("[_moz_dirty]"), null,
"Elements with _moz_dirty" + desc);
// Even if we do create _moz_dirty, we should strip it for innerHTML.
is(editorDoc.body.innerHTML, "<p><b>foo</b></p><p>bar</p>",
"innerHTML" + desc);
}
]]>
</script>
</window>

View File

@ -55,6 +55,7 @@
#include "nsAttrName.h"
#include "nsAutoPtr.h"
#include "mozilla/Preferences.h"
#include "nsContentUtils.h"
using namespace mozilla;
@ -194,6 +195,45 @@ void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutput
}
}
static
void ProcessFontSizeValue(const nsAString* aInputString, nsAString& aOutputString,
const char* aDefaultValueString,
const char* aPrependString, const char* aAppendString)
{
aOutputString.Truncate();
if (aInputString) {
PRInt32 size = nsContentUtils::ParseLegacyFontSize(*aInputString);
switch (size) {
case 0:
// Didn't parse
return;
case 1:
aOutputString.AssignLiteral("x-small");
return;
case 2:
aOutputString.AssignLiteral("small");
return;
case 3:
aOutputString.AssignLiteral("medium");
return;
case 4:
aOutputString.AssignLiteral("large");
return;
case 5:
aOutputString.AssignLiteral("x-large");
return;
case 6:
aOutputString.AssignLiteral("xx-large");
return;
case 7:
// No corresponding CSS size
return;
default:
NS_NOTREACHED("Unexpected return value from ParseLegacyFontSize");
}
}
}
const nsHTMLCSSUtils::CSSEquivTable boldEquivTable[] = {
{ nsHTMLCSSUtils::eCSSEditableProperty_font_weight, ProcessBValue, nsnull, nsnull, nsnull, true, false },
{ nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
@ -229,6 +269,11 @@ const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable[] = {
{ nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
};
const nsHTMLCSSUtils::CSSEquivTable fontSizeEquivTable[] = {
{ nsHTMLCSSUtils::eCSSEditableProperty_font_size, ProcessFontSizeValue, nsnull, nsnull, nsnull, true, false },
{ nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
};
const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable[] = {
{ nsHTMLCSSUtils::eCSSEditableProperty_background_color, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
{ nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
@ -312,21 +357,23 @@ nsHTMLCSSUtils::~nsHTMLCSSUtils()
// Answers true if we have some CSS equivalence for the HTML style defined
// by aProperty and/or aAttribute for the node aNode
bool
nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode * aNode,
nsIAtom * aProperty,
const nsAString * aAttribute)
nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode* aNode,
nsIAtom* aProperty,
const nsAString* aAttribute,
const nsAString* aValue)
{
NS_ASSERTION(aNode, "Shouldn't you pass aNode? - Bug 214025");
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
NS_ENSURE_TRUE(content, false);
return IsCSSEditableProperty(content, aProperty, aAttribute);
return IsCSSEditableProperty(content, aProperty, aAttribute, aValue);
}
bool
nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent* aNode,
nsIAtom* aProperty,
const nsAString* aAttribute)
const nsAString* aAttribute,
const nsAString* aValue)
{
MOZ_ASSERT(aNode);
@ -352,6 +399,16 @@ nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent* aNode,
return true;
}
// FONT SIZE doesn't work if the value is 7
if (nsEditProperty::font == aProperty && aAttribute &&
aAttribute->EqualsLiteral("size")) {
if (!aValue || aValue->IsEmpty()) {
return true;
}
PRInt32 size = nsContentUtils::ParseLegacyFontSize(*aValue);
return size && size != 7;
}
// ALIGN attribute on elements supporting it
if (aAttribute && (aAttribute->EqualsLiteral("align")) &&
(nsEditProperty::div == tagName
@ -853,77 +910,67 @@ nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(dom::Element* aElement,
{
MOZ_ASSERT(aElement);
nsIAtom* tagName = aElement->Tag();
const nsHTMLCSSUtils::CSSEquivTable* equivTable = nsnull;
if (nsEditProperty::b == aHTMLProperty) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, boldEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::i == aHTMLProperty) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, italicEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::u == aHTMLProperty) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, underlineEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::strike == aHTMLProperty) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, strikeEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::tt == aHTMLProperty) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, ttEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute) {
equivTable = boldEquivTable;
} else if (nsEditProperty::i == aHTMLProperty) {
equivTable = italicEquivTable;
} else if (nsEditProperty::u == aHTMLProperty) {
equivTable = underlineEquivTable;
} else if (nsEditProperty::strike == aHTMLProperty) {
equivTable = strikeEquivTable;
} else if (nsEditProperty::tt == aHTMLProperty) {
equivTable = ttEquivTable;
} else if (aAttribute) {
if (nsEditProperty::font == aHTMLProperty &&
aAttribute->EqualsLiteral("color")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontColorEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::font == aHTMLProperty &&
aAttribute->EqualsLiteral("face")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontFaceEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("bgcolor")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, bgcolorEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("background")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, backgroundImageEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("text")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, textColorEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("border")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, borderEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("align")) {
equivTable = fontColorEquivTable;
} else if (nsEditProperty::font == aHTMLProperty &&
aAttribute->EqualsLiteral("face")) {
equivTable = fontFaceEquivTable;
} else if (nsEditProperty::font == aHTMLProperty &&
aAttribute->EqualsLiteral("size")) {
equivTable = fontSizeEquivTable;
} else if (aAttribute->EqualsLiteral("bgcolor")) {
equivTable = bgcolorEquivTable;
} else if (aAttribute->EqualsLiteral("background")) {
equivTable = backgroundImageEquivTable;
} else if (aAttribute->EqualsLiteral("text")) {
equivTable = textColorEquivTable;
} else if (aAttribute->EqualsLiteral("border")) {
equivTable = borderEquivTable;
} else if (aAttribute->EqualsLiteral("align")) {
if (nsEditProperty::table == tagName) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, tableAlignEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::hr == tagName) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, hrAlignEquivTable, aValue, aGetOrRemoveRequest);
}
else if (nsEditProperty::legend == tagName ||
equivTable = tableAlignEquivTable;
} else if (nsEditProperty::hr == tagName) {
equivTable = hrAlignEquivTable;
} else if (nsEditProperty::legend == tagName ||
nsEditProperty::caption == tagName) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, captionAlignEquivTable, aValue, aGetOrRemoveRequest);
}
else {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, textAlignEquivTable, aValue, aGetOrRemoveRequest);
equivTable = captionAlignEquivTable;
} else {
equivTable = textAlignEquivTable;
}
} else if (aAttribute->EqualsLiteral("valign")) {
equivTable = verticalAlignEquivTable;
} else if (aAttribute->EqualsLiteral("nowrap")) {
equivTable = nowrapEquivTable;
} else if (aAttribute->EqualsLiteral("width")) {
equivTable = widthEquivTable;
} else if (aAttribute->EqualsLiteral("height") ||
(nsEditProperty::hr == tagName &&
aAttribute->EqualsLiteral("size"))) {
equivTable = heightEquivTable;
} else if (aAttribute->EqualsLiteral("type") &&
(nsEditProperty::ol == tagName
|| nsEditProperty::ul == tagName
|| nsEditProperty::li == tagName)) {
equivTable = listStyleTypeEquivTable;
}
else if (aAttribute->EqualsLiteral("valign")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, verticalAlignEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("nowrap")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, nowrapEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("width")) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, widthEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("height") ||
(nsEditProperty::hr == tagName && aAttribute->EqualsLiteral("size"))) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, heightEquivTable, aValue, aGetOrRemoveRequest);
}
else if (aAttribute->EqualsLiteral("type") &&
(nsEditProperty::ol == tagName
|| nsEditProperty::ul == tagName
|| nsEditProperty::li == tagName)) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, listStyleTypeEquivTable, aValue, aGetOrRemoveRequest);
}
}
if (equivTable) {
BuildCSSDeclarations(cssPropertyArray, cssValueArray, equivTable,
aValue, aGetOrRemoveRequest);
}
}
@ -939,7 +986,8 @@ nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
{
nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
*aCount = 0;
if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) {
if (!element || !IsCSSEditableProperty(element, aHTMLProperty,
aAttribute, aValue)) {
return NS_OK;
}
@ -1015,7 +1063,8 @@ nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode,
nsCOMPtr<dom::Element> theElement = GetElementContainerOrSelf(aNode);
NS_ENSURE_TRUE(theElement, NS_ERROR_NULL_POINTER);
if (!theElement || !IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
if (!theElement || !IsCSSEditableProperty(theElement, aHTMLProperty,
aAttribute, &aValueString)) {
return NS_OK;
}
@ -1048,17 +1097,16 @@ nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode,
return NS_OK;
}
// Does the node aNode (or his parent if it is not an element node) carries
// the CSS equivalent styles to the HTML style aHTMLProperty/aAttribute/
// aValueString for this node ?
// The value of aStyleType controls the styles we retrieve : specified or
// computed. The return value aIsSet is true is the CSS styles are set.
// Does the node aNode (or its parent, if it's not an element node) have a CSS
// style equivalent to the HTML style aHTMLProperty/aHTMLAttribute/valueString?
// The value of aStyleType controls the styles we retrieve: specified or
// computed. The return value aIsSet is true if the CSS styles are set.
nsresult
nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode *aNode,
nsIAtom *aHTMLProperty,
const nsAString * aHTMLAttribute,
bool & aIsSet,
nsAString & valueString,
const nsAString *aHTMLAttribute,
bool& aIsSet,
nsAString& valueString,
PRUint8 aStyleType)
{
NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
@ -1066,7 +1114,6 @@ nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
nsAutoString htmlValueString(valueString);
aIsSet = false;
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
NS_NAMED_LITERAL_STRING(boldStr, "bold");
do {
valueString.Assign(htmlValueString);
// get the value of the CSS equivalent styles
@ -1075,64 +1122,54 @@ nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
NS_ENSURE_SUCCESS(res, res);
// early way out if we can
if (valueString.IsEmpty()) return NS_OK;
if (valueString.IsEmpty()) {
return NS_OK;
}
if (nsEditProperty::b == aHTMLProperty) {
if (valueString.Equals(boldStr)) {
if (valueString.EqualsLiteral("bold")) {
aIsSet = true;
}
else if (valueString.EqualsLiteral("normal")) {
} else if (valueString.EqualsLiteral("normal")) {
aIsSet = false;
}
else if (valueString.EqualsLiteral("bolder")) {
} else if (valueString.EqualsLiteral("bolder")) {
aIsSet = true;
valueString.Assign(boldStr);
}
else {
valueString.AssignLiteral("bold");
} else {
PRInt32 weight = 0;
PRInt32 errorCode;
nsAutoString value(valueString);
weight = value.ToInteger(&errorCode, 10);
if (400 < weight) {
aIsSet = true;
valueString.Assign(boldStr);
}
else {
valueString.AssignLiteral("bold");
} else {
aIsSet = false;
valueString.AssignLiteral("normal");
}
}
}
else if (nsEditProperty::i == aHTMLProperty) {
} else if (nsEditProperty::i == aHTMLProperty) {
if (valueString.EqualsLiteral("italic") ||
valueString.EqualsLiteral("oblique")) {
aIsSet= true;
aIsSet = true;
}
}
else if (nsEditProperty::u == aHTMLProperty) {
} else if (nsEditProperty::u == aHTMLProperty) {
nsAutoString val;
val.AssignLiteral("underline");
aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
}
else if (nsEditProperty::strike == aHTMLProperty) {
} else if (nsEditProperty::strike == aHTMLProperty) {
nsAutoString val;
val.AssignLiteral("line-through");
aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
}
else if (aHTMLAttribute &&
( (nsEditProperty::font == aHTMLProperty &&
aHTMLAttribute->EqualsLiteral("color")) ||
aHTMLAttribute->EqualsLiteral("bgcolor"))) {
if (htmlValueString.IsEmpty())
} else if (aHTMLAttribute &&
((nsEditProperty::font == aHTMLProperty &&
aHTMLAttribute->EqualsLiteral("color")) ||
aHTMLAttribute->EqualsLiteral("bgcolor"))) {
if (htmlValueString.IsEmpty()) {
aIsSet = true;
else {
} else {
nscolor rgba;
nsAutoString subStr;
htmlValueString.Right(subStr, htmlValueString.Length()-1);
htmlValueString.Right(subStr, htmlValueString.Length() - 1);
if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
NS_HexToRGB(subStr, &rgba)) {
nsAutoString htmlColor, tmpStr;
@ -1154,19 +1191,15 @@ nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
htmlColor.Append(PRUnichar(')'));
aIsSet = htmlColor.Equals(valueString,
nsCaseInsensitiveStringComparator());
}
else
} else {
aIsSet = htmlValueString.Equals(valueString,
nsCaseInsensitiveStringComparator());
}
}
}
else if (nsEditProperty::tt == aHTMLProperty) {
} else if (nsEditProperty::tt == aHTMLProperty) {
aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
}
else if ((nsEditProperty::font == aHTMLProperty) && aHTMLAttribute
&& aHTMLAttribute->EqualsLiteral("face")) {
} else if (nsEditProperty::font == aHTMLProperty && aHTMLAttribute &&
aHTMLAttribute->EqualsLiteral("face")) {
if (!htmlValueString.IsEmpty()) {
const PRUnichar commaSpace[] = { PRUnichar(','), PRUnichar(' '), 0 };
const PRUnichar comma[] = { PRUnichar(','), 0 };
@ -1175,8 +1208,7 @@ nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
valueStringNorm.ReplaceSubstring(commaSpace, comma);
aIsSet = htmlValueString.Equals(valueStringNorm,
nsCaseInsensitiveStringComparator());
}
else {
} else {
// ignore this, it's TT or our default
nsAutoString valueStringLower;
ToLowerCase(valueString, valueStringLower);
@ -1184,21 +1216,30 @@ nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
!valueStringLower.EqualsLiteral("serif");
}
return NS_OK;
}
else if (aHTMLAttribute
&& aHTMLAttribute->EqualsLiteral("align")) {
} else if (nsEditProperty::font == aHTMLProperty && aHTMLAttribute &&
aHTMLAttribute->EqualsLiteral("size")) {
if (htmlValueString.IsEmpty()) {
aIsSet = true;
} else {
PRInt32 size = nsContentUtils::ParseLegacyFontSize(htmlValueString);
aIsSet = (size == 1 && valueString.EqualsLiteral("x-small")) ||
(size == 2 && valueString.EqualsLiteral("small")) ||
(size == 3 && valueString.EqualsLiteral("medium")) ||
(size == 4 && valueString.EqualsLiteral("large")) ||
(size == 5 && valueString.EqualsLiteral("x-large")) ||
(size == 6 && valueString.EqualsLiteral("xx-large"));
}
} else if (aHTMLAttribute && aHTMLAttribute->EqualsLiteral("align")) {
aIsSet = true;
}
else {
} else {
aIsSet = false;
return NS_OK;
}
if (!htmlValueString.IsEmpty()) {
if (htmlValueString.Equals(valueString,
nsCaseInsensitiveStringComparator())) {
aIsSet = true;
}
if (!htmlValueString.IsEmpty() &&
htmlValueString.Equals(valueString,
nsCaseInsensitiveStringComparator())) {
aIsSet = true;
}
if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {

View File

@ -106,10 +106,20 @@ public:
* @return a boolean saying if the tag/attribute has a css equiv
* @param aNode [IN] a DOM node
* @param aProperty [IN] an atom containing a HTML tag name
* @param aAttribute [IN] a string containing the name of a HTML attribute carried by the element above
* @param aAttribute [IN] a string containing the name of a HTML
* attribute carried by the element above
* @param aValue [IN] an optional string containing the attribute's
* HTML value -- this matters for <font size>,
* since size=7 has no CSS equivalent. Make sure
* you pass the HTML value (e.g. "4"), not the
* CSS value (e.g. "large").
*/
bool IsCSSEditableProperty(nsIContent* aNode, nsIAtom* aProperty, const nsAString* aAttribute);
bool IsCSSEditableProperty(nsIDOMNode* aNode, nsIAtom* aProperty, const nsAString* aAttribute);
bool IsCSSEditableProperty(nsIContent* aNode, nsIAtom* aProperty,
const nsAString* aAttribute,
const nsAString* aValue = nsnull);
bool IsCSSEditableProperty(nsIDOMNode* aNode, nsIAtom* aProperty,
const nsAString* aAttribute,
const nsAString* aValue = nsnull);
/** adds/remove a CSS declaration to the STYLE atrribute carried by a given element
*

View File

@ -709,7 +709,7 @@ protected:
nsresult RemoveStyleInside(nsIDOMNode *aNode,
nsIAtom *aProperty,
const nsAString *aAttribute,
bool aChildrenOnly = false);
const bool aChildrenOnly = false);
nsresult RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAString *aAttribute);
bool NodeIsProperty(nsIDOMNode *aNode);
@ -972,6 +972,13 @@ friend class nsTextEditRules;
friend class nsWSRunObject;
friend class nsHTMLEditorEventListener;
private:
// Helper
nsresult SetInlinePropertyOnNodeImpl(nsIDOMNode *aNode,
nsIAtom *aProperty,
const nsAString *aAttribute,
const nsAString *aValue);
};
#endif //nsHTMLEditor_h__

File diff suppressed because it is too large Load Diff

View File

@ -63,6 +63,7 @@ _TEST_FILES = \
test_bug456244.html \
test_bug460740.html \
test_bug478725.html \
test_bug480647.html \
test_bug480972.html \
test_bug484181.html \
test_bug487524.html \

View File

@ -19,7 +19,6 @@ var knownFailures = {
},
'a' : {
'createbookmark-0' : true,
'fontsize-1' : true,
'subscript-1' : true,
'superscript-1' : true,
},
@ -35,15 +34,6 @@ var knownFailures = {
'fontsize-1' : true,
'fontsize-2' : true,
},
'c': {
'fontname-0' : true,
'fontname-2' : true,
'fontname-3' : true,
'fontsize-1' : true,
'fontsize-2' : true,
'forecolor-0' : true,
'forecolor-2' : true,
},
};
function isKnownFailure(type, test, param) {

View File

@ -132,10 +132,9 @@ http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/u
// 'body' (test within a <body contenteditable="true">)
if (UPDATE_TEST_RESULTS) {
var testResults = {};
var newKnownFailures = {value: {}, select: {}};
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
testResults[category.id] = {};
for (var group in results[category.id]) {
switch (group) {
// Skip the known properties
@ -145,7 +144,6 @@ http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/u
case "time":
break;
default:
testResults[category.id][group] = {};
for (var test_id in results[category.id][group]) {
switch (test_id) {
// Skip the known properties
@ -154,15 +152,18 @@ http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/u
case "selscore":
break;
default:
testResults[category.id][group][test_id] = {};
for (var structure in results[category.id][group][test_id]) {
switch (structure) {
// Only look at each test structure
case "dM":
case "body":
case "div":
var row = results[category.id][group][test_id][structure];
testResults[category.id][group][test_id][structure] = row;
if (!results[category.id][group][test_id][structure].valscore) {
newKnownFailures.value[category.id + "-" + group + "-" + test_id + "-" + structure] = true;
}
if (!results[category.id][group][test_id][structure].selscore) {
newKnownFailures.select[category.id + "-" + group + "-" + test_id + "-" + structure] = true;
}
}
}
}
@ -172,7 +173,7 @@ http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/u
}
var resultContainer = document.getElementById("results");
resultContainer.style.display = "";
resultContainer.textContent = JSON.stringify(testResults);
resultContainer.textContent = JSON.stringify(newKnownFailures);
} else {
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
@ -200,57 +201,11 @@ http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/u
case "body":
case "div":
var row = results[category.id][group][test_id][structure];
var expected = TEST_RESULTS[category.id][group][test_id][structure];
var testName = "[" + [category.id, group, test_id, structure].join(", ") + "]";
// There are three possible cases here:
// a) If row.valscore == 1 and expected.valscore == 1, then the test has passed successfully.
// b) If row.valscore == 1 and expected.valscore == 0, a previously failing test has started to pass.
// In this case, the respective currentStatus.js entry should be modified.
// c) If row.valscore == 0 && expected.valscore == 0, then the test is known to fail.
// d) If row.valscore == 0 && expected.valscore == 1, then we've found a regression, which should be fixed.
// The same logic goes for selscore too.
if (row.valscore == 1 && expected.valscore == 1) {
// successful
is(row.valscore, expected.valscore, testName + " passed successfully");
} else if (row.valscore == 1 && expected.valscore == 0) {
// unexpected pass
todo_is(row.valscore, 1, testName + " used to fail, but it just started passing");
// debugging information
info("HTML test result comparison - got " +
row.output + ", expected " + expected.output);
} else if (row.valscore == 0 && expected.valscore == 0) {
// known failure
todo_is(row.valscore, 1, testName + " is known to fail");
} else if (row.valscore == 0 && expected.valscore == 1) {
// regression
is(row.valscore, expected.valscore, testName + " has regressed");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else {
// sanity check: this shouldn't happen!
ok(false, "Unexpected result: row = " + row.toSource() + ", expected: " + expected.toSource());
}
if (row.selscore == 1 && expected.selscore == 1) {
// successful
is(row.selscore, expected.selscore, testName + " passed successfully");
} else if (row.selscore == 1 && expected.selscore == 0) {
// unexpected pass
todo_is(row.selscore, 1, testName + " used to fail, but it just started passing");
// debugging information
info("HTML test result comparison - got " +
row.output + ", expected " + expected.output);
} else if (row.selscore == 0 && expected.selscore == 0) {
// known failure
todo_is(row.selscore, 1, testName + " is known to fail");
} else if (row.selscore == 0 && expected.selscore == 1) {
// regression
is(row.selscore, expected.selscore, testName + " has regressed");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else {
// sanity check: this shouldn't happen!
ok(false, "Unexpected result: row = " + row.toSource() + ", expected: " + expected.toSource());
}
var testName = [category.id, group, test_id, structure].join("-");
(testName in knownFailures.value ? todo_is : is)(
row.valscore, 1, "Browserscope richtext2 value: " + testName);
(testName in knownFailures.select ? todo_is : is)(
row.selscore, 1, "Browserscope richtext2 selection: " + testName);
}
}
}

View File

@ -0,0 +1,141 @@
<!DOCTYPE html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=480647
-->
<title>Test for Bug 480647</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=480647">Mozilla Bug 480647</a>
<div contenteditable></div>
<script>
/** Test for Bug 480647 **/
var div = document.querySelector("div");
function parseFontSize(input, expected) {
parseFontSizeInner(input, expected, is);
}
function parseFontSizeTodo(input, expected) {
parseFontSizeInner(input, expected, todo_is);
}
function parseFontSizeInner(input, expected, fn) {
// First test non-CSS
document.execCommand("styleWithCSS", false, false);
div.innerHTML = "foo";
getSelection().selectAllChildren(div);
document.execCommand("fontSize", false, input);
if (expected === null) {
fn(div.innerHTML, "foo",
'execCommand("fontSize", false, "' + input + '") should be no-op ' +
'(non-CSS)');
} else {
fn(div.innerHTML, '<font size="' + expected + '">foo</font>',
'execCommand("fontSize", false, "' + input + '") should parse to ' +
expected + ' (non-CSS)');
}
// Now test CSS
document.execCommand("styleWithCSS", false, true);
div.innerHTML = "foo";
getSelection().selectAllChildren(div);
document.execCommand("fontSize", false, input);
if (expected === null) {
fn(div.innerHTML, "foo",
'execCommand("fontSize", false, "' + input + '") should be no-op ' +
'(CSS)');
} else if (expected === 7) {
// No CSS support for <font size=7>
fn(div.innerHTML, '<font size="' + expected + '">foo</font>',
'execCommand("fontSize", false, "' + input + '") should parse to ' +
expected + ' (CSS)');
} else {
var cssVal = {
1: "x-small",
2: "small",
3: "medium",
4: "large",
5: "x-large",
6: "xx-large",
}[expected];
fn(div.innerHTML, '<span style="font-size: ' + cssVal + ';">foo</span>',
'execCommand("fontSize", false, "' + input + '") should parse to ' +
expected + ' (CSS)');
}
}
// Parse errors
parseFontSize("", null);
parseFontSize("abc", null);
parseFontSize("larger", null);
parseFontSize("smaller", null);
parseFontSize("xx-small", null);
parseFontSize("x-small", null);
parseFontSize("small", null);
parseFontSize("medium", null);
parseFontSize("large", null);
parseFontSize("x-large", null);
parseFontSize("xx-large", null);
parseFontSize("xxx-large", null);
// Bug 747879
parseFontSizeTodo("1.2em", null);
parseFontSizeTodo("8px", null);
parseFontSizeTodo("-1.2em", null);
parseFontSizeTodo("-8px", null);
parseFontSizeTodo("+1.2em", null);
parseFontSizeTodo("+8px", null);
// Numbers
parseFontSize("0", 1);
parseFontSize("1", 1);
parseFontSize("2", 2);
parseFontSize("3", 3);
parseFontSize("4", 4);
parseFontSize("5", 5);
parseFontSize("6", 6);
parseFontSize("7", 7);
parseFontSize("8", 7);
parseFontSize("9", 7);
parseFontSize("10", 7);
parseFontSize("1000000000000000000000", 7);
parseFontSize("2.72", 2);
parseFontSize("2.72e9", 2);
// Minus sign
parseFontSize("-0", 3);
parseFontSize("-1", 2);
parseFontSize("-2", 1);
parseFontSize("-3", 1);
parseFontSize("-4", 1);
parseFontSize("-5", 1);
parseFontSize("-6", 1);
parseFontSize("-7", 1);
parseFontSize("-8", 1);
parseFontSize("-9", 1);
parseFontSize("-10", 1);
parseFontSize("-1000000000000000000000", 1);
parseFontSize("-1.72", 2);
parseFontSize("-1.72e9", 2);
// Plus sign
parseFontSize("+0", 3);
parseFontSize("+1", 4);
parseFontSize("+2", 5);
parseFontSize("+3", 6);
parseFontSize("+4", 7);
parseFontSize("+5", 7);
parseFontSize("+6", 7);
parseFontSize("+7", 7);
parseFontSize("+8", 7);
parseFontSize("+9", 7);
parseFontSize("+10", 7);
parseFontSize("+1000000000000000000000", 7);
parseFontSize("+1.72", 4);
parseFontSize("+1.72e9", 4);
// Whitespace
parseFontSize(" \t\n\r\f5 \t\n\r\f", 5);
parseFontSize("\u00a05", null);
parseFontSize("\b5", null);
</script>

View File

@ -78,6 +78,10 @@ struct NS_GFX nsFont {
// The variant of the font (normal, small-caps)
PRUint8 variant;
// The decorations on the font (underline, overline,
// line-through). The decorations can be binary or'd together.
PRUint8 decorations;
// The weight of the font; see gfxFontConstants.h.
PRUint16 weight;
@ -85,10 +89,6 @@ struct NS_GFX nsFont {
// constants; see gfxFontConstants.h).
PRInt16 stretch;
// The decorations on the font (underline, overline,
// line-through). The decorations can be binary or'd together.
PRUint8 decorations;
// The logical size of the font, in nscoord units
nscoord size;

View File

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
14.0a1
15.0a1

View File

@ -3993,11 +3993,11 @@ JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *obj, jsid id,
struct JSPropertyDescriptor {
JSObject *obj;
unsigned attrs;
unsigned attrs;
unsigned shortid;
JSPropertyOp getter;
JSStrictPropertyOp setter;
jsval value;
unsigned shortid;
};
/*

View File

@ -53,6 +53,10 @@ extern JSBool
js_Stringify(JSContext *cx, js::Value *vp, JSObject *replacer, js::Value space,
js::StringBuffer &sb);
// Avoid build errors on certain platforms that define these names as constants
#undef STRICT
#undef LEGACY
/*
* The type of JSON decoding to perform. Strict decoding is to-the-spec;
* legacy decoding accepts a few non-JSON syntaxes historically accepted by the

View File

@ -1915,7 +1915,7 @@ AccumulateTelemetryCallback(int id, uint32_t sample)
}
bool XPCJSRuntime::gNewDOMBindingsEnabled;
bool XPCJSRuntime::gParisBindingsEnabled;
bool XPCJSRuntime::gExperimentalBindingsEnabled;
bool PreserveWrapper(JSContext *cx, JSObject *obj)
{
@ -1968,7 +1968,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
DOM_InitInterfaces();
Preferences::AddBoolVarCache(&gNewDOMBindingsEnabled, "dom.new_bindings",
false);
Preferences::AddBoolVarCache(&gParisBindingsEnabled, "dom.paris_bindings",
Preferences::AddBoolVarCache(&gExperimentalBindingsEnabled,
"dom.experimental_bindings",
false);

View File

@ -150,7 +150,7 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(XPCCallContext& ccx,
mPrototypeNoHelper(nsnull),
mScriptObjectPrincipal(nsnull),
mNewDOMBindingsEnabled(ccx.GetRuntime()->NewDOMBindingsEnabled()),
mParisBindingsEnabled(ccx.GetRuntime()->ParisBindingsEnabled())
mExperimentalBindingsEnabled(ccx.GetRuntime()->ExperimentalBindingsEnabled())
{
// add ourselves to the scopes list
{ // scoped lock

View File

@ -795,9 +795,9 @@ public:
return gNewDOMBindingsEnabled;
}
bool ParisBindingsEnabled()
bool ExperimentalBindingsEnabled()
{
return gParisBindingsEnabled;
return gExperimentalBindingsEnabled;
}
size_t SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf);
@ -812,7 +812,7 @@ private:
static void WatchdogMain(void *arg);
static bool gNewDOMBindingsEnabled;
static bool gParisBindingsEnabled;
static bool gExperimentalBindingsEnabled;
static const char* mStrings[IDX_TOTAL_COUNT];
jsid mStrIDs[IDX_TOTAL_COUNT];
@ -1638,9 +1638,9 @@ public:
return mNewDOMBindingsEnabled;
}
JSBool ParisBindingsEnabled()
JSBool ExperimentalBindingsEnabled()
{
return mParisBindingsEnabled;
return mExperimentalBindingsEnabled;
}
protected:
@ -1684,7 +1684,7 @@ private:
nsDataHashtable<nsDepCharHashKey, JSObject*> mCachedDOMPrototypes;
JSBool mNewDOMBindingsEnabled;
JSBool mParisBindingsEnabled;
JSBool mExperimentalBindingsEnabled;
};
/***************************************************************************/

View File

@ -1229,9 +1229,8 @@ static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
static void
LayoutModuleDtor()
{
xpcModuleDtor();
nsScriptSecurityManager::Shutdown();
xpcModuleDtor();
}
static const mozilla::Module kLayoutModule = {

View File

@ -4,8 +4,8 @@
<script type="text/javascript">
var text = '<html><head></head><body style="font-size:16px;">'
+ '<p style="background-color:red;">This paragraph should be red</p>'
+ '<p style="background-color:blue;">This paragraph should be blue</p>'
+ '<p><span style="background-color:red;">This paragraph should be red</span></p>'
+ '<p><span style="background-color:blue;">This paragraph should be blue</span></p>'
+ '<p>This paragraph should not be colored</p>'
+ '</body></html>';

View File

@ -136,12 +136,12 @@ static const nsStyleSet::sheetType gCSSSheetTypes[] = {
nsStyleSet::nsStyleSet()
: mRuleTree(nsnull),
mUnusedRuleNodeCount(0),
mBatching(0),
mInShutdown(false),
mAuthorStyleDisabled(false),
mInReconstruct(false),
mDirty(0)
mDirty(0),
mUnusedRuleNodeCount(0)
{
}

View File

@ -405,6 +405,13 @@ class nsStyleSet
// lexicographic tree of matched rules that style
// contexts use to look up properties.
PRUint16 mBatching;
unsigned mInShutdown : 1;
unsigned mAuthorStyleDisabled: 1;
unsigned mInReconstruct : 1;
unsigned mDirty : 8; // one dirty bit is used per sheet type
PRUint32 mUnusedRuleNodeCount; // used to batch rule node GC
nsTArray<nsStyleContext*> mRoots; // style contexts with no parent
@ -416,18 +423,10 @@ class nsStyleSet
// determining when context-sensitive values are in use.
nsRefPtr<nsInitialStyleRule> mInitialStyleRule;
PRUint16 mBatching;
// Old rule trees, which should only be non-empty between
// BeginReconstruct and EndReconstruct, but in case of bugs that cause
// style contexts to exist too long, may last longer.
nsTArray<nsRuleNode*> mOldRuleTrees;
unsigned mInShutdown : 1;
unsigned mAuthorStyleDisabled: 1;
unsigned mInReconstruct : 1;
unsigned mDirty : 8; // one dirty bit is used per sheet type
};
#ifdef _IMPL_NS_LAYOUT

View File

@ -457,9 +457,9 @@ nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
mBorderImageSource(aSrc.mBorderImageSource),
mBorderRadius(aSrc.mBorderRadius),
mBorderImageSlice(aSrc.mBorderImageSlice),
mBorderImageFill(aSrc.mBorderImageFill),
mBorderImageWidth(aSrc.mBorderImageWidth),
mBorderImageOutset(aSrc.mBorderImageOutset),
mBorderImageFill(aSrc.mBorderImageFill),
mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
mFloatEdge(aSrc.mFloatEdge),
@ -2846,10 +2846,10 @@ nsStyleText::nsStyleText(const nsStyleText& aSource)
mHyphens(aSource.mHyphens),
mTextSizeAdjust(aSource.mTextSizeAdjust),
mTabSize(aSource.mTabSize),
mWordSpacing(aSource.mWordSpacing),
mLetterSpacing(aSource.mLetterSpacing),
mLineHeight(aSource.mLineHeight),
mTextIndent(aSource.mTextIndent),
mWordSpacing(aSource.mWordSpacing),
mTextShadow(aSource.mTextShadow)
{
MOZ_COUNT_CTOR(nsStyleText);

View File

@ -939,14 +939,13 @@ protected:
public:
nsStyleCorners mBorderRadius; // [reset] coord, percent
nsStyleSides mBorderImageSlice; // [reset] factor, percent
PRUint8 mBorderImageFill; // [reset]
nsStyleSides mBorderImageWidth; // [reset] length, factor, percent, auto
nsStyleSides mBorderImageOutset; // [reset] length, factor
PRUint8 mBorderImageFill; // [reset]
PRUint8 mBorderImageRepeatH; // [reset] see nsStyleConsts.h
PRUint8 mBorderImageRepeatV; // [reset]
PRUint8 mFloatEdge; // [reset]
// 8 bits free here
protected:
// mComputedBorder holds the CSS2.1 computed border-width values.
@ -1325,10 +1324,10 @@ struct nsStyleText {
PRUint8 mTextSizeAdjust; // [inherited] see nsStyleConsts.h
PRInt32 mTabSize; // [inherited] see nsStyleConsts.h
nscoord mWordSpacing; // [inherited]
nsStyleCoord mLetterSpacing; // [inherited] coord, normal
nsStyleCoord mLineHeight; // [inherited] coord, factor, normal
nsStyleCoord mTextIndent; // [inherited] coord, percent, calc
nscoord mWordSpacing; // [inherited]
nsRefPtr<nsCSSShadowArray> mTextShadow; // [inherited] NULL in case of a zero-length
@ -1601,12 +1600,12 @@ struct nsStyleDisplay {
// specified, or null to indicate there is no transform. (inherit or
// initial are replaced by an actual list of transform functions, or
// null, as appropriate.) (owned by the style rule)
PRUint8 mBackfaceVisibility;
PRUint8 mTransformStyle;
const nsCSSValueList *mSpecifiedTransform; // [reset]
nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only
nsStyleCoord mChildPerspective; // [reset] coord
nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc
PRUint8 mBackfaceVisibility;
PRUint8 mTransformStyle;
nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
// The number of elements in mTransitions that are not from repeating

2
mobile/android/confvars.sh Normal file → Executable file
View File

@ -38,7 +38,7 @@
MOZ_APP_BASENAME=Fennec
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=14.0a1
MOZ_APP_VERSION=15.0a1
MOZ_APP_UA_NAME=Firefox
MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial

View File

@ -55,7 +55,7 @@
<Description>
<em:id>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_VERSION@</em:maxVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
</Description>

2
mobile/xul/confvars.sh Normal file → Executable file
View File

@ -38,7 +38,7 @@
MOZ_APP_BASENAME=Fennec
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=14.0a1
MOZ_APP_VERSION=15.0a1
MOZ_BRANDING_DIRECTORY=mobile/xul/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/xul/branding/official

View File

@ -55,7 +55,7 @@
<Description>
<em:id>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_VERSION@</em:maxVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
</Description>

View File

@ -646,7 +646,7 @@ pref("dom.min_background_timeout_value", 1000);
// Use the new DOM bindings (only affects any scopes created after the pref is
// changed)
pref("dom.new_bindings", true);
pref("dom.paris_bindings", true);
pref("dom.experimental_bindings", true);
// Parsing perf prefs. For now just mimic what the old code did.
#ifndef XP_WIN

View File

@ -13,6 +13,7 @@
[include:dom/indexedDB/test/unit/xpcshell.ini]
[include:content/xtf/test/unit/xpcshell.ini]
[include:docshell/test/unit/xpcshell.ini]
[include:docshell/test/unit_ipc/xpcshell.ini]
[include:embedding/tests/unit/xpcshell.ini]
[include:toolkit/components/commandlines/test/unit/xpcshell.ini]
[include:toolkit/components/contentprefs/tests/unit/xpcshell.ini]

View File

@ -87,7 +87,7 @@ ThreadActor.prototype = {
_scripts: {},
/**
* Add a debuggee global to the JSInspector.
* Add a debuggee global to the Debugger object.
*/
addDebuggee: function TA_addDebuggee(aGlobal) {
// Use the inspector xpcom component to turn on debugging

View File

@ -0,0 +1,485 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
"use strict";
/**
* Toolkit glue for the remote debugging protocol, loaded into the
* debugging global.
*/
const Ci = Components.interfaces;
const Cc = Components.classes;
const CC = Components.Constructor;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
function dumpn(str) {
if (wantLogging) {
dump("DBG-SERVER: " + str + "\n");
}
}
function dbg_assert(cond, e) {
if (!cond) {
return e;
}
}
loadSubScript.call(this, "chrome://global/content/devtools/dbg-transport.js");
// XPCOM constructors
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
"init");
/***
* Public API
*/
var DebuggerServer = {
_listener: null,
_transportInitialized: false,
xpcInspector: null,
/**
* Initialize the debugger server.
*/
init: function DH_init() {
if (this.initialized) {
return;
}
// Hack: Merely loading jsdebugger.jsm will not work, because it will load
// in the chrome compartment, and then we'd get a cross-compartment wrapper
// of that. The Debugger object must be created in the sandbox compartment,
// that is, this file's compartment.
const init = Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger);
init.addClass(); // adds global variable Debugger to this global.
this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
this.initTransport();
this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
},
/**
* Initialize the debugger server's transport variables. This can be
* in place of init() for cases where the jsdebugger isn't needed.
*/
initTransport: function DH_initTransport() {
if (this._transportInitialized) {
return;
}
this._connections = {};
this._nextConnID = 0;
this._transportInitialized = true;
},
get initialized() { return !!this.xpcInspector; },
/**
* Load a subscript into the debugging global.
*
* @param aURL string A url that will be loaded as a subscript into the
* debugging global. The user must load at least one script
* that implements a createRootActor() function to create the
* server's root actor.
*/
addActors: function DH_addActors(aURL) {
loadSubScript.call(this, aURL);
},
/**
* Install Firefox-specific actors.
*/
addBrowserActors: function DH_addBrowserActors() {
this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
},
/**
* Listens on the given port for remote debugger connections.
*
* @param aPort int
* The port to listen on.
* @param aLocalOnly bool
* If true, server will listen on the loopback device.
*/
openListener: function DH_openListener(aPort, aLocalOnly) {
this._checkInit();
if (this._listener) {
throw "Debugging listener already open.";
}
try {
let socket = new ServerSocket(aPort, aLocalOnly, 4);
socket.asyncListen(this);
this._listener = socket;
} catch (e) {
dumpn("Could not start debugging listener on port " + aPort + ": " + e);
throw Cr.NS_ERROR_NOT_AVAILABLE;
}
return true;
},
/**
* Close a previously-opened TCP listener.
*/
closeListener: function DH_closeListener() {
this._checkInit();
if (!this._listener) {
return false;
}
this._listener.close();
this._listener = null;
return true;
},
/**
* Creates a new connection to the local debugger speaking over an
* nsIPipe.
*
* @returns a client-side DebuggerTransport for communicating with
* the newly-created connection.
*/
connectPipe: function DH_connectPipe() {
this._checkInit();
let toServer = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
toServer.init(true, true, 0, 0, null);
let toClient = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
toClient.init(true, true, 0, 0, null);
let serverTransport = new DebuggerTransport(toServer.inputStream,
toClient.outputStream);
this._onConnection(serverTransport);
return new DebuggerTransport(toClient.inputStream, toServer.outputStream);
},
// nsIServerSocketListener implementation
onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
try {
let input = aTransport.openInputStream(0, 0, 0);
let output = aTransport.openOutputStream(0, 0, 0);
let transport = new DebuggerTransport(input, output);
DebuggerServer._onConnection(transport);
} catch (e) {
dumpn("Couldn't initialize connection: " + e + " - " + e.stack);
}
},
onStopListening: function DH_onStopListening() { },
/**
* Raises an exception if the server has not been properly initialized.
*/
_checkInit: function DH_checkInit() {
if (!this._transportInitialized) {
throw "DebuggerServer has not been initialized.";
}
if (!this.createRootActor) {
throw "Use DebuggerServer.addActors() to add a root actor implementation.";
}
},
/**
* Create a new debugger connection for the given transport. Called
* after connectPipe() or after an incoming socket connection.
*/
_onConnection: function DH_onConnection(aTransport) {
let connID = "conn" + this._nextConnID++ + '.';
let conn = new DebuggerServerConnection(connID, aTransport);
this._connections[connID] = conn;
// Create a root actor for the connection and send the hello packet.
conn.rootActor = this.createRootActor(conn);
conn.addActor(conn.rootActor);
aTransport.send(conn.rootActor.sayHello());
aTransport.ready();
},
/**
* Remove the connection from the debugging server.
*/
_connectionClosed: function DH_connectionClosed(aConnection) {
delete this._connections[aConnection.prefix];
}
};
/**
* Construct an ActorPool.
*
* ActorPools are actorID -> actor mapping and storage. These are
* used to accumulate and quickly dispose of groups of actors that
* share a lifetime.
*/
function ActorPool(aConnection)
{
this.conn = aConnection;
this._cleanups = {};
this._actors = {};
}
ActorPool.prototype = {
/**
* Add an actor to the actor pool. If the actor doesn't have an ID,
* allocate one from the connection.
*
* @param aActor object
* The actor implementation. If the object has a
* 'disconnected' property, it will be called when the actor
* pool is cleaned up.
*/
addActor: function AP_addActor(aActor) {
aActor.conn = this.conn;
if (!aActor.actorID) {
aActor.actorID = this.conn.allocID(aActor.actorPrefix || undefined);
}
if (aActor.registeredPool) {
aActor.registeredPool.removeActor(aActor);
}
aActor.registeredPool = this;
this._actors[aActor.actorID] = aActor;
if (aActor.disconnect) {
this._cleanups[aActor.actorID] = aActor;
}
},
get: function AP_get(aActorID) {
return this._actors[aActorID];
},
has: function AP_has(aActorID) {
return aActorID in this._actors;
},
/**
* Remove an actor from the actor pool.
*/
removeActor: function AP_remove(aActorID) {
delete this._actors[aActorID];
delete this._cleanups[aActorID];
},
/**
* Run all cleanups previously registered with addCleanup.
*/
cleanup: function AP_cleanup() {
for each (let actor in this._cleanups) {
actor.disconnect();
}
this._cleanups = {};
}
}
/**
* Creates a DebuggerServerConnection.
*
* Represents a connection to this debugging global from a client.
* Manages a set of actors and actor pools, allocates actor ids, and
* handles incoming requests.
*
* @param aPrefix string
* All actor IDs created by this connection should be prefixed
* with aPrefix.
* @param aTransport transport
* Packet transport for the debugging protocol.
*/
function DebuggerServerConnection(aPrefix, aTransport)
{
this._prefix = aPrefix;
this._transport = aTransport;
this._transport.hooks = this;
this._nextID = 1;
this._actorPool = new ActorPool(this);
this._extraPools = [];
}
DebuggerServerConnection.prototype = {
_prefix: null,
get prefix() { return this._prefix },
_transport: null,
get transport() { return this._transport },
send: function DSC_send(aPacket) {
this.transport.send(aPacket);
},
allocID: function DSC_allocID(aPrefix) {
return this.prefix + (aPrefix || '') + this._nextID++;
},
/**
* Add a map of actor IDs to the connection.
*/
addActorPool: function DSC_addActorPool(aActorPool) {
this._extraPools.push(aActorPool);
},
/**
* Remove a previously-added pool of actors to the connection.
*/
removeActorPool: function DSC_removeActorPool(aActorPool) {
let index = this._extraPools.splice(this._extraPools.lastIndexOf(aActorPool), 1);
},
/**
* Add an actor to the default actor pool for this connection.
*/
addActor: function DSC_addActor(aActor) {
this._actorPool.addActor(aActor);
},
/**
* Remove an actor to the default actor pool for this connection.
*/
removeActor: function DSC_removeActor(aActor) {
this._actorPool.removeActor(aActor);
},
/**
* Add a cleanup to the default actor pool for this connection.
*/
addCleanup: function DSC_addCleanup(aCleanup) {
this._actorPool.addCleanup(aCleanup);
},
/**
* Look up an actor implementation for an actorID. Will search
* all the actor pools registered with the connection.
*
* @param aActorID string
* Actor ID to look up.
*/
getActor: function DSC_getActor(aActorID) {
if (this._actorPool.has(aActorID)) {
return this._actorPool.get(aActorID);
}
for each (let pool in this._extraPools) {
if (pool.has(aActorID)) {
return pool.get(aActorID);
}
}
if (aActorID === "root") {
return this.rootActor;
}
return null;
},
// Transport hooks.
/**
* Called by DebuggerTransport to dispatch incoming packets as appropriate.
*
* @param aPacket object
* The incoming packet.
*/
onPacket: function DSC_onPacket(aPacket) {
let actor = this.getActor(aPacket.to);
if (!actor) {
this.transport.send({ from: aPacket.to ? aPacket.to : "root",
error: "noSuchActor" });
return;
}
var ret = null;
// Dispatch the request to the actor.
if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
try {
ret = actor.requestTypes[aPacket.type].bind(actor)(aPacket);
} catch(e) {
Cu.reportError(e);
ret = { error: "unknownError",
message: "An unknown error has occurred while processing request." };
}
} else {
ret = { error: "unrecognizedPacketType",
message: 'Actor "' + actor.actorID + '" does not recognize the packet type "' + aPacket.type + '"' };
}
if (!ret) {
// XXX: The actor wasn't ready to reply yet, don't process new
// requests until it does.
return;
}
if (!ret.from) {
ret.from = aPacket.to;
}
this.transport.send(ret);
},
/**
* Called by DebuggerTransport when the underlying stream is closed.
*
* @param aStatus nsresult
* The status code that corresponds to the reason for closing
* the stream.
*/
onClosed: function DSC_onClosed(aStatus) {
dumpn("Cleaning up connection.");
this._actorPool.cleanup();
this._actorPool = null;
this._extraPools.map(function(p) { p.cleanup(); });
this._extraPools = null;
DebuggerServer._connectionClosed(this);
}
};

View File

@ -39,456 +39,38 @@
"use strict";
/**
* Toolkit glue for the remote debugging protocol, loaded into the
* debugging global.
* Loads the remote debugging protocol code into a sandbox, in order to
* shield it from the debuggee. This way, when debugging chrome globals,
* debugger and debuggee will be in separate compartments.
*/
const Ci = Components.interfaces;
const Cc = Components.classes;
const CC = Components.Constructor;
const Cu = Components.utils;
var EXPORTED_SYMBOLS = ["DebuggerServer"];
Cu.import("resource://gre/modules/Services.jsm");
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
function loadSubScript(aURL)
{
try {
let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript(aURL, this);
} catch(e) {
dumpn("Error loading: " + aURL + ": " + e + " - " + e.stack + "\n");
dump("Error loading: " + aURL + ": " + e + " - " + e.stack + "\n");
throw e;
}
}
function dumpn(str) {
if (wantLogging) {
dump("DBG-SERVER: " + str + "\n");
}
}
Cu.import("resource:///modules/devtools/dbg-client.jsm");
function dbg_assert(cond, e) {
if (!cond) {
return e;
}
}
// Load the debugging server in a sandbox with its own compartment.
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
loadSubScript.call(this, "chrome://global/content/devtools/dbg-transport.js");
var gGlobal = Cu.Sandbox(systemPrincipal);
gGlobal.importFunction(loadSubScript);
gGlobal.loadSubScript("chrome://global/content/devtools/dbg-server.js");
// XPCOM constructors
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
"init");
/***
* Public API
*/
var DebuggerServer = {
_listener: null,
_transportInitialized: false,
xpcInspector: null,
/**
* Initialize the debugger server.
*/
init: function DH_init() {
if (this.initialized) {
return;
}
Cu.import("resource://gre/modules/jsdebugger.jsm");
this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
this.initTransport();
this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
},
/**
* Initialize the debugger server's transport variables. This can be
* in place of init() for cases where the jsdebugger isn't needed.
*/
initTransport: function DH_initTransport() {
if (this._transportInitialized) {
return;
}
this._connections = {};
this._nextConnID = 0;
this._transportInitialized = true;
},
get initialized() { return !!this.xpcInspector; },
/**
* Load a subscript into the debugging global.
*
* @param aURL string A url that will be loaded as a subscript into the
* debugging global. The user must load at least one script
* that implements a createRootActor() function to create the
* server's root actor.
*/
addActors: function DH_addActors(aURL) {
loadSubScript.call(this, aURL);
},
/**
* Install Firefox-specific actors.
*/
addBrowserActors: function DH_addBrowserActors() {
this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
},
/**
* Listens on the given port for remote debugger connections.
*
* @param aPort int
* The port to listen on.
* @param aLocalOnly bool
* If true, server will listen on the loopback device.
*/
openListener: function DH_openListener(aPort, aLocalOnly) {
this._checkInit();
if (this._listener) {
throw "Debugging listener already open.";
}
try {
let socket = new ServerSocket(aPort, aLocalOnly, 4);
socket.asyncListen(this);
this._listener = socket;
} catch (e) {
dumpn("Could not start debugging listener on port " + aPort + ": " + e);
throw Cr.NS_ERROR_NOT_AVAILABLE;
}
return true;
},
/**
* Close a previously-opened TCP listener.
*/
closeListener: function DH_closeListener() {
this._checkInit();
if (!this._listener) {
return false;
}
this._listener.close();
this._listener = null;
return true;
},
/**
* Creates a new connection to the local debugger speaking over an
* nsIPipe.
*
* @returns a client-side DebuggerTransport for communicating with
* the newly-created connection.
*/
connectPipe: function DH_connectPipe() {
this._checkInit();
let toServer = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
toServer.init(true, true, 0, 0, null);
let toClient = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
toClient.init(true, true, 0, 0, null);
let serverTransport = new DebuggerTransport(toServer.inputStream,
toClient.outputStream);
this._onConnection(serverTransport);
return new DebuggerTransport(toClient.inputStream, toServer.outputStream);
},
// nsIServerSocketListener implementation
onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
try {
let input = aTransport.openInputStream(0, 0, 0);
let output = aTransport.openOutputStream(0, 0, 0);
let transport = new DebuggerTransport(input, output);
DebuggerServer._onConnection(transport);
} catch (e) {
dumpn("Couldn't initialize connection: " + e + " - " + e.stack);
}
},
onStopListening: function DH_onStopListening() { },
/**
* Raises an exception if the server has not been properly initialized.
*/
_checkInit: function DH_checkInit() {
if (!this._transportInitialized) {
throw "DebuggerServer has not been initialized.";
}
if (!this.createRootActor) {
throw "Use DebuggerServer.addActors() to add a root actor implementation.";
}
},
/**
* Create a new debugger connection for the given transport. Called
* after connectPipe() or after an incoming socket connection.
*/
_onConnection: function DH_onConnection(aTransport) {
let connID = "conn" + this._nextConnID++ + '.';
let conn = new DebuggerServerConnection(connID, aTransport);
this._connections[connID] = conn;
// Create a root actor for the connection and send the hello packet.
conn.rootActor = this.createRootActor(conn);
conn.addActor(conn.rootActor);
aTransport.send(conn.rootActor.sayHello());
aTransport.ready();
},
/**
* Remove the connection from the debugging server.
*/
_connectionClosed: function DH_connectionClosed(aConnection) {
delete this._connections[aConnection.prefix];
}
};
/**
* Construct an ActorPool.
*
* ActorPools are actorID -> actor mapping and storage. These are
* used to accumulate and quickly dispose of groups of actors that
* share a lifetime.
*/
function ActorPool(aConnection)
{
this.conn = aConnection;
this._cleanups = {};
this._actors = {};
}
ActorPool.prototype = {
/**
* Add an actor to the actor pool. If the actor doesn't have an ID,
* allocate one from the connection.
*
* @param aActor object
* The actor implementation. If the object has a
* 'disconnected' property, it will be called when the actor
* pool is cleaned up.
*/
addActor: function AP_addActor(aActor) {
aActor.conn = this.conn;
if (!aActor.actorID) {
aActor.actorID = this.conn.allocID(aActor.actorPrefix || undefined);
}
if (aActor.registeredPool) {
aActor.registeredPool.removeActor(aActor);
}
aActor.registeredPool = this;
this._actors[aActor.actorID] = aActor;
if (aActor.disconnect) {
this._cleanups[aActor.actorID] = aActor;
}
},
get: function AP_get(aActorID) {
return this._actors[aActorID];
},
has: function AP_has(aActorID) {
return aActorID in this._actors;
},
/**
* Remove an actor from the actor pool.
*/
removeActor: function AP_remove(aActorID) {
delete this._actors[aActorID];
delete this._cleanups[aActorID];
},
/**
* Run all cleanups previously registered with addCleanup.
*/
cleanup: function AP_cleanup() {
for each (let actor in this._cleanups) {
actor.disconnect();
}
this._cleanups = {};
}
}
/**
* Creates a DebuggerServerConnection.
*
* Represents a connection to this debugging global from a client.
* Manages a set of actors and actor pools, allocates actor ids, and
* handles incoming requests.
*
* @param aPrefix string
* All actor IDs created by this connection should be prefixed
* with aPrefix.
* @param aTransport transport
* Packet transport for the debugging protocol.
*/
function DebuggerServerConnection(aPrefix, aTransport)
{
this._prefix = aPrefix;
this._transport = aTransport;
this._transport.hooks = this;
this._nextID = 1;
this._actorPool = new ActorPool(this);
this._extraPools = [];
}
DebuggerServerConnection.prototype = {
_prefix: null,
get prefix() { return this._prefix },
_transport: null,
get transport() { return this._transport },
send: function DSC_send(aPacket) {
this.transport.send(aPacket);
},
allocID: function DSC_allocID(aPrefix) {
return this.prefix + (aPrefix || '') + this._nextID++;
},
/**
* Add a map of actor IDs to the connection.
*/
addActorPool: function DSC_addActorPool(aActorPool) {
this._extraPools.push(aActorPool);
},
/**
* Remove a previously-added pool of actors to the connection.
*/
removeActorPool: function DSC_removeActorPool(aActorPool) {
let index = this._extraPools.splice(this._extraPools.lastIndexOf(aActorPool), 1);
},
/**
* Add an actor to the default actor pool for this connection.
*/
addActor: function DSC_addActor(aActor) {
this._actorPool.addActor(aActor);
},
/**
* Remove an actor to the default actor pool for this connection.
*/
removeActor: function DSC_removeActor(aActor) {
this._actorPool.removeActor(aActor);
},
/**
* Add a cleanup to the default actor pool for this connection.
*/
addCleanup: function DSC_addCleanup(aCleanup) {
this._actorPool.addCleanup(aCleanup);
},
/**
* Look up an actor implementation for an actorID. Will search
* all the actor pools registered with the connection.
*
* @param aActorID string
* Actor ID to look up.
*/
getActor: function DSC_getActor(aActorID) {
if (this._actorPool.has(aActorID)) {
return this._actorPool.get(aActorID);
}
for each (let pool in this._extraPools) {
if (pool.has(aActorID)) {
return pool.get(aActorID);
}
}
if (aActorID === "root") {
return this.rootActor;
}
return null;
},
// Transport hooks.
/**
* Called by DebuggerTransport to dispatch incoming packets as appropriate.
*
* @param aPacket object
* The incoming packet.
*/
onPacket: function DSC_onPacket(aPacket) {
let actor = this.getActor(aPacket.to);
if (!actor) {
this.transport.send({ from: aPacket.to ? aPacket.to : "root",
error: "noSuchActor" });
return;
}
var ret = null;
// Dispatch the request to the actor.
if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
try {
ret = actor.requestTypes[aPacket.type].bind(actor)(aPacket);
} catch(e) {
Cu.reportError(e);
ret = { error: "unknownError",
message: "An unknown error has occurred while processing request." };
}
} else {
ret = { error: "unrecognizedPacketType",
message: 'Actor "' + actor.actorID + '" does not recognize the packet type "' + aPacket.type + '"' };
}
if (!ret) {
// XXX: The actor wasn't ready to reply yet, don't process new
// requests until it does.
return;
}
if (!ret.from) {
ret.from = aPacket.to;
}
this.transport.send(ret);
},
/**
* Called by DebuggerTransport when the underlying stream is closed.
*
* @param aStatus nsresult
* The status code that corresponds to the reason for closing
* the stream.
*/
onClosed: function DSC_onClosed(aStatus) {
dumpn("Cleaning up connection.");
this._actorPool.cleanup();
this._actorPool = null;
this._extraPools.map(function(p) { p.cleanup(); });
this._extraPools = null;
DebuggerServer._connectionClosed(this);
}
};
var DebuggerServer = gGlobal.DebuggerServer;

View File

@ -1,4 +1,5 @@
toolkit.jar:
content/global/devtools/dbg-transport.js (debugger/dbg-transport.js)
content/global/devtools/dbg-server.js (debugger/server/dbg-server.js)
content/global/devtools/dbg-script-actors.js (debugger/server/dbg-script-actors.js)
content/global/devtools/dbg-browser-actors.js (debugger/server/dbg-browser-actors.js)

View File

@ -85,6 +85,7 @@ DEFINES += \
-DAB_CD=$(AB_CD) \
-DMOZ_LANGPACK_EID=$(MOZ_LANGPACK_EID) \
-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
-DMOZ_APP_MAXVERSION=$(MOZ_APP_MAXVERSION) \
-DLOCALE_SRCDIR=$(call core_abspath,$(LOCALE_SRCDIR)) \
-DPKG_BASENAME="$(PKG_BASENAME)" \
-DPKG_INST_BASENAME="$(PKG_INST_BASENAME)" \

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