Merge last green changeset from inbound to mozilla-central

This commit is contained in:
Matt Brubeck 2012-03-21 11:58:42 -07:00
commit c51963cc5b
36 changed files with 369 additions and 1400 deletions

View File

@ -66,7 +66,6 @@ nsIScriptElement.h \
nsIStyleSheetLinkingElement.h \ nsIStyleSheetLinkingElement.h \
nsIContentSerializer.h \ nsIContentSerializer.h \
nsIXPathEvaluatorInternal.h \ nsIXPathEvaluatorInternal.h \
mozISanitizingSerializer.h \
nsCaseTreatment.h \ nsCaseTreatment.h \
nsContentCID.h \ nsContentCID.h \
nsCopySupport.h \ nsCopySupport.h \

View File

@ -1,128 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org HTML Sanitizer code.
*
* The Initial Developer of the Original Code is
* Ben Bucksch <mozilla@bucksch.org>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Netscape
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* Cleans up HTML source from unwanted tags/attributes
This class implements a content sink, which takes a parsed HTML document
and removes all tags and attributes that are not explicitly allowed.
This may improve the viewing experience of the user and/or the
security/privacy.
What is allowed is defined by a string (format described before the
implementation of |mozHTMLSanitizer::ParsePrefs()|). The sytnax of the
definition is not very rich - you can only (dis)allow certain tags and
attributes, but not where they may appear. (This makes the implementation
much more simple.) E.g. it is impossible to disallow ordinary text as a
direct child of the <head> node or to disallow multiple <head> nodes.
We also remove some known bad attribute values like javascript: URLs.
Draconian attitude.
Currently, the output of this class is unparsed (!) HTML source, which
means that each document has to go through the parser twice. Of course,
that is a performance killer. There are some reasons for for me doing it
that way:
* There is, to my knowledge, no interface to hook up such modifiers
in the document display data flow. We have a nice interface for doing
the modifications (the DOM), but no place to get the DOM and to invoke
this code. As I don't want to hack this directly into the html sink,
I'd have to create a generic interface first, which is too much work for
me at the moment.
* It is quite easy to hook up modifiers for the (unparsed) data stream,
both in netwerk (for the browser) and esp. in libmime (for Mailnews).
* It seems like the safest method - it is easier to debug (you have the
HTML source output to check) and is less prone to security-relevant bugs
and regressions, because in the case of a bug, it will probably fall back
to not outputting, which is safer than erring on the side of letting
something slip through (most of the alternative approaches listed below
are probably vulnerable to the latter).
* It should be possible to later change this class to output a parsed HTML
document.
So, in other words, I had the choice between better design and better
performance. I choose design. Bad performance has an effect on the users
of this class only, while bad design has an effect on all users and
programmers.
That being said, I have some ideas, how do make it much more efficient, but
they involve hacking core code.
* At some point when we have DOM, but didn't do anything with it yet
(in particular, didn't load any external objects or ran any javascript),
walk the DOM and delete everything the user doesn't explicitly like.
* There's this nice GetPref() in the HTMLContentSink. It isn't used exactly
as I would like to, but that should be doable. Bascially, before
processing any tag (e.g. in OpenContainer or AddLeaf), ask that
function, if the tag is allowed. If not, just return.
In any case, there's the problem, how the users of the renderer
(e.g. Mailnews) can tell it to use the sanitizer and which tags are
allowed (the browser may want to allow more tags than Mailnews).
That probably means that I have to hack into the docshell (incl. its
interface) or similar, which I would really like to avoid.
Any ideas appreciated.
*/
#ifndef _mozISanitizingSerializer_h__
#define _mozISanitizingSerializer_h__
#include "nsISupports.h"
class nsAString;
#define MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID "@mozilla.org/layout/htmlsanitizer;1"
/* starting interface: nsIContentSerializer */
#define MOZ_ISANITIZINGHTMLSERIALIZER_IID_STR "feca3c34-205e-4ae5-bd1c-03c686ff012b"
#define MOZ_ISANITIZINGHTMLSERIALIZER_IID \
{0xfeca3c34, 0x205e, 0x4ae5, \
{ 0xbd, 0x1c, 0x03, 0xc6, 0x86, 0xff, 0x01, 0x2b }}
class mozISanitizingHTMLSerializer : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(MOZ_ISANITIZINGHTMLSERIALIZER_IID)
NS_IMETHOD Initialize(nsAString* aOutString,
PRUint32 aFlags,
const nsAString& allowedTags) = 0;
// This function violates string ownership rules, see impl.
};
NS_DEFINE_STATIC_IID_ACCESSOR(mozISanitizingHTMLSerializer,
MOZ_ISANITIZINGHTMLSERIALIZER_IID)
#endif

View File

@ -77,7 +77,6 @@ EXPORTS_mozilla/dom = \
LOCAL_INCLUDES = -I$(srcdir)/js/xpconnect/src LOCAL_INCLUDES = -I$(srcdir)/js/xpconnect/src
CPPSRCS = \ CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \ nsAtomListUtils.cpp \
nsAttrAndChildArray.cpp \ nsAttrAndChildArray.cpp \
nsAttrValue.cpp \ nsAttrValue.cpp \

View File

@ -1,684 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et 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 HTML Sanitizer code.
*
* The Initial Developer of the Original Code is
* Ben Bucksch <mozilla@bucksch.org>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Netscape
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* A serializer and content sink that removes potentially insecure or
* otherwise dangerous or offending HTML (eg for display of HTML
* e-mail attachments or something).
*/
/* I used nsPlaintextSerializer as base for this class. I don't understand
all of the functions in the beginning. Possible that I fail to do
something or do something useless.
I am not proud about the implementation here at all.
Feel free to fix it :-).
*/
#include "mozSanitizingSerializer.h"
#include "nsIServiceManager.h"
#include "nsIDOMElement.h"
#include "nsTextFragment.h"
#include "nsContentUtils.h"
#include "nsReadableUtils.h"
#include "plstr.h"
#include "nsIProperties.h"
#include "nsUnicharUtils.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsEscape.h"
#include "mozilla/dom/Element.h"
using namespace mozilla::dom;
static inline PRUnichar* escape(const nsString& source)
{
return nsEscapeHTML2(source.get(), source.Length());
}
/* XXX: |printf|s in some error conditions. They are intended as information
for the user, because they complain about malformed pref values.
Not sure, if popping up dialog boxes is the right thing for such code
(and if so, how to do it).
*/
#define TEXT_REMOVED "&lt;Text removed&gt;"
#define TEXT_BREAKER "|"
nsresult NS_NewSanitizingHTMLSerializer(nsIContentSerializer** aSerializer)
{
mozSanitizingHTMLSerializer* it = new mozSanitizingHTMLSerializer();
NS_ADDREF(it);
*aSerializer = it;
return NS_OK;
}
mozSanitizingHTMLSerializer::mozSanitizingHTMLSerializer()
: mSkipLevel(0),
mAllowedTags(30) // Just some initial buffer size
{
mOutputString = nsnull;
}
mozSanitizingHTMLSerializer::~mozSanitizingHTMLSerializer()
{
#ifdef DEBUG_BenB
printf("Output:\n%s\n", NS_LossyConvertUTF16toASCII(*mOutputString).get());
#endif
mAllowedTags.Enumerate(ReleaseProperties);
}
//<copy from="xpcom/ds/nsProperties.cpp">
bool
mozSanitizingHTMLSerializer::ReleaseProperties(nsHashKey* key, void* data,
void* closure)
{
nsIProperties* prop = (nsIProperties*)data;
NS_IF_RELEASE(prop);
return true;
}
//</copy>
NS_IMPL_ISUPPORTS4(mozSanitizingHTMLSerializer,
nsIContentSerializer,
nsIContentSink,
nsIHTMLContentSink,
mozISanitizingHTMLSerializer)
NS_IMETHODIMP
mozSanitizingHTMLSerializer::Init(PRUint32 aFlags, PRUint32 dummy,
const char* aCharSet, bool aIsCopying,
bool aIsWholeDocument)
{
NS_ENSURE_TRUE(nsContentUtils::GetParserService(), NS_ERROR_UNEXPECTED);
return NS_OK;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::Initialize(nsAString* aOutString,
PRUint32 aFlags,
const nsAString& allowedTags)
{
nsresult rv = Init(aFlags, 0, nsnull, false, false);
NS_ENSURE_SUCCESS(rv, rv);
// XXX This is wrong. It violates XPCOM string ownership rules.
// We're only getting away with this because instances of this
// class are restricted to single function scope.
// (Comment copied from nsPlaintextSerializer)
mOutputString = aOutString;
ParsePrefs(allowedTags);
return NS_OK;
}
// This is not used within the class, but maybe called from somewhere else?
NS_IMETHODIMP
mozSanitizingHTMLSerializer::Flush(nsAString& aStr)
{
#ifdef DEBUG_BenB
printf("Flush: -%s-", NS_LossyConvertUTF16toASCII(aStr).get());
#endif
Write(aStr);
return NS_OK;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::AppendDocumentStart(nsIDocument *aDocument,
nsAString& aStr)
{
return NS_OK;
}
void
mozSanitizingHTMLSerializer::Write(const nsAString& aString)
{
mOutputString->Append(aString);
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::IsEnabled(PRInt32 aTag, bool* aReturn)
{
*aReturn = false;
return NS_OK;
}
/**
* Returns true, if the id represents a container
*/
bool
mozSanitizingHTMLSerializer::IsContainer(PRInt32 aId)
{
bool isContainer = false;
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
parserService->IsContainer(aId, isContainer);
}
return isContainer;
}
/* XXX I don't really know, what these functions do, but they seem to be
needed ;-). Mostly copied from nsPlaintextSerializer. */
/* akk says:
"I wonder if the sanitizing class could inherit from nsHTMLSerializer,
so that at least these methods that none of us understand only have to be
written once?" */
// static
PRInt32
mozSanitizingHTMLSerializer::GetIdForContent(nsIContent* aContent)
{
if (!aContent->IsHTML()) {
return eHTMLTag_unknown;
}
nsIParserService* parserService = nsContentUtils::GetParserService();
return parserService ? parserService->HTMLAtomTagToId(aContent->Tag()) :
eHTMLTag_unknown;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::AppendText(nsIContent* aText,
PRInt32 aStartOffset,
PRInt32 aEndOffset,
nsAString& aStr)
{
nsresult rv = NS_OK;
mOutputString = &aStr;
nsAutoString linebuffer;
rv = DoAddLeaf(eHTMLTag_text, linebuffer);
return rv;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::AppendElementStart(Element* aElement,
Element* aOriginalElement,
nsAString& aStr)
{
NS_ENSURE_ARG(aElement);
mElement = aElement;
mOutputString = &aStr;
PRInt32 id = GetIdForContent(mElement);
bool isContainer = IsContainer(id);
nsresult rv;
if (isContainer) {
rv = DoOpenContainer(id);
}
else {
rv = DoAddLeaf(id, EmptyString());
}
mElement = nsnull;
mOutputString = nsnull;
return rv;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::AppendElementEnd(Element* aElement,
nsAString& aStr)
{
NS_ENSURE_ARG(aElement);
mElement = aElement;
mOutputString = &aStr;
PRInt32 id = GetIdForContent(mElement);
bool isContainer = IsContainer(id);
nsresult rv = NS_OK;
if (isContainer) {
rv = DoCloseContainer(id);
}
mElement = nsnull;
mOutputString = nsnull;
return rv;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::OpenContainer(const nsIParserNode& aNode)
{
PRInt32 type = aNode.GetNodeType();
mParserNode = const_cast<nsIParserNode *>(&aNode);
return DoOpenContainer(type);
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::CloseContainer(const nsHTMLTag aTag)
{
return DoCloseContainer(aTag);
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::AddLeaf(const nsIParserNode& aNode)
{
eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
const nsAString& text = aNode.GetText();
mParserNode = const_cast<nsIParserNode*>(&aNode);
return DoAddLeaf(type, text);
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::SetDocumentCharset(nsACString& aCharset)
{
// No idea, if this works - it isn't invoked by |TestOutput|.
Write(NS_LITERAL_STRING("\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=")
/* Danger: breaking the line within the string literal, like
"foo"\n"bar", breaks win32! */
+ nsAdoptingString(escape(NS_ConvertASCIItoUTF16(aCharset)))
+ NS_LITERAL_STRING("\">\n"));
return NS_OK;
}
NS_IMETHODIMP
mozSanitizingHTMLSerializer::OpenHead()
{
// XXX We don't have a parser node here, is it okay to ignore this?
// return OpenContainer(aNode);
return NS_OK;
}
// Here comes the actual code...
nsresult
mozSanitizingHTMLSerializer::DoOpenContainer(PRInt32 aTag)
{
eHTMLTags type = (eHTMLTags)aTag;
if (mSkipLevel == 0 && IsAllowedTag(type))
{
nsIParserService* parserService = nsContentUtils::GetParserService();
if (!parserService)
return NS_ERROR_OUT_OF_MEMORY;
const PRUnichar* tag_name = parserService->HTMLIdToStringTag(aTag);
NS_ENSURE_TRUE(tag_name, NS_ERROR_INVALID_POINTER);
Write(NS_LITERAL_STRING("<") + nsDependentString(tag_name));
// Attributes
if (mParserNode)
{
PRInt32 count = mParserNode->GetAttributeCount();
for (PRInt32 i = 0; i < count; i++)
{
const nsAString& key = mParserNode->GetKeyAt(i);
if(IsAllowedAttribute(type, key))
{
// Ensure basic sanity of value
nsAutoString value(mParserNode->GetValueAt(i));
// SanitizeAttrValue() modifies |value|
if (NS_SUCCEEDED(SanitizeAttrValue(type, key, value)))
{
// Write out
Write(NS_LITERAL_STRING(" "));
Write(key); // I get an infinive loop with | + key + | !!!
Write(NS_LITERAL_STRING("=\"") + value + NS_LITERAL_STRING("\""));
}
}
}
}
Write(NS_LITERAL_STRING(">"));
}
else if (mSkipLevel != 0 || type == eHTMLTag_script || type == eHTMLTag_style)
++mSkipLevel;
else
Write(NS_LITERAL_STRING(" "));
return NS_OK;
}
nsresult
mozSanitizingHTMLSerializer::DoCloseContainer(PRInt32 aTag)
{
eHTMLTags type = (eHTMLTags)aTag;
if (mSkipLevel == 0 && IsAllowedTag(type)) {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (!parserService)
return NS_ERROR_OUT_OF_MEMORY;
const PRUnichar* tag_name = parserService->HTMLIdToStringTag(aTag);
NS_ENSURE_TRUE(tag_name, NS_ERROR_INVALID_POINTER);
Write(NS_LITERAL_STRING("</") + nsDependentString(tag_name)
+ NS_LITERAL_STRING(">"));
}
else if (mSkipLevel == 0)
Write(NS_LITERAL_STRING(" "));
else
--mSkipLevel;
return NS_OK;
}
nsresult
mozSanitizingHTMLSerializer::DoAddLeaf(PRInt32 aTag,
const nsAString& aText)
{
if (mSkipLevel != 0)
return NS_OK;
eHTMLTags type = (eHTMLTags)aTag;
nsresult rv = NS_OK;
if (type == eHTMLTag_whitespace ||
type == eHTMLTag_newline)
{
Write(aText); // sure to be safe?
}
else if (type == eHTMLTag_text)
{
nsAutoString text(aText);
if(NS_SUCCEEDED(SanitizeTextNode(text)))
Write(text);
else
Write(NS_LITERAL_STRING(TEXT_REMOVED)); // Does not happen (yet)
NS_ENSURE_SUCCESS(rv, rv);
}
else if (type == eHTMLTag_entity)
{
Write(NS_LITERAL_STRING("&"));
Write(aText); // sure to be safe?
// using + operator here might give an infinitive loop, see above.
// not adding ";", because Gecko delivers that as part of |aText| (freaky)
}
else
{
DoOpenContainer(type);
}
return rv;
}
/**
Similar to SanitizeAttrValue.
*/
nsresult
mozSanitizingHTMLSerializer::SanitizeTextNode(nsString& aText /*inout*/)
{
aText.Adopt(escape(aText));
return NS_OK;
}
/**
Ensures basic sanity of attribute value.
This function also (tries to :-( ) makes sure, that no
unwanted / dangerous URLs appear in the document
(like javascript: and data:).
Pass the value as |aValue| arg. It will be modified in-place.
If the value is not allowed at all, we return with NS_ERROR_ILLEGAL_VALUE.
In that case, do not use the |aValue|, but output nothing.
*/
nsresult
mozSanitizingHTMLSerializer::SanitizeAttrValue(nsHTMLTag aTag,
const nsAString& anAttrName,
nsString& aValue /*inout*/)
{
/* First, cut the attribute to 1000 chars.
Attributes with values longer than 1000 chars seem bogus,
considering that we don't support any JS. The longest attributes
I can think of are URLs, and URLs with 1000 chars are likely to be
bogus, too. */
aValue = Substring(aValue, 0, 1000);
//aValue.Truncate(1000); //-- this cuts half of the document !!?!!
aValue.Adopt(escape(aValue));
/* Check some known bad stuff. Add more!
I don't care too much, if it happens to trigger in some innocent cases
(like <img alt="Statistical data: Mortage rates and newspapers">) -
security first. */
if (aValue.Find("javascript:") != kNotFound ||
aValue.Find("data:") != kNotFound ||
aValue.Find("base64") != kNotFound)
return NS_ERROR_ILLEGAL_VALUE;
// Check img src scheme
if (aTag == eHTMLTag_img &&
anAttrName.LowerCaseEqualsLiteral("src"))
{
nsresult rv;
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString scheme;
rv = ioService->ExtractScheme(NS_LossyConvertUTF16toASCII(aValue), scheme);
NS_ENSURE_SUCCESS(rv, rv);
if (!scheme.Equals("cid", nsCaseInsensitiveCStringComparator()))
return NS_ERROR_ILLEGAL_VALUE;
}
#ifdef DEBUG_BenB
printf("attribute value for %s: -%s-\n",
NS_LossyConvertUTF16toASCII(anAttrName).get(),
NS_LossyConvertUTF16toASCII(aValue).get());
#endif
return NS_OK;
}
/**
*/
bool
mozSanitizingHTMLSerializer::IsAllowedTag(nsHTMLTag aTag)
{
nsPRUint32Key tag_key(aTag);
#ifdef DEBUG_BenB
printf("IsAllowedTag %d: %s\n",
aTag,
mAllowedTags.Exists(&tag_key)?"yes":"no");
#endif
return mAllowedTags.Exists(&tag_key);
}
/**
*/
bool
mozSanitizingHTMLSerializer::IsAllowedAttribute(nsHTMLTag aTag,
const nsAString& anAttributeName)
{
#ifdef DEBUG_BenB
printf("IsAllowedAttribute %d, -%s-\n",
aTag,
NS_LossyConvertUTF16toASCII(anAttributeName).get());
#endif
nsresult rv;
nsPRUint32Key tag_key(aTag);
nsIProperties* attr_bag = (nsIProperties*)mAllowedTags.Get(&tag_key);
NS_ENSURE_TRUE(attr_bag, false);
bool allowed;
nsCAutoString attr;
ToLowerCase(NS_ConvertUTF16toUTF8(anAttributeName), attr);
rv = attr_bag->Has(attr.get(), &allowed);
if (NS_FAILED(rv))
return false;
#ifdef DEBUG_BenB
printf(" Allowed: %s\n", allowed?"yes":"no");
#endif
return allowed;
}
/**
aPref is a long string, which holds an exhaustive list of allowed tags
and attributes. All other tags and attributes will be removed.
aPref has the format
"html head body ul ol li a(href,name,title) img(src,alt,title) #text"
i.e.
- tags are separated by whitespace
- the attribute list follows the tag directly in brackets
- the attributes are separated by commas.
There is no way to express further restrictions, like "no text inside the
<head> element". This is so to considerably reduce the complexity of the
pref and this implementation.
Update: Akk told me that I might be able to use DTD classes. Later(TM)...
*/
nsresult
mozSanitizingHTMLSerializer::ParsePrefs(const nsAString& aPref)
{
char* pref = ToNewCString(aPref);
char* tags_lasts;
for (char* iTag = PL_strtok_r(pref, " ", &tags_lasts);
iTag;
iTag = PL_strtok_r(NULL, " ", &tags_lasts))
{
ParseTagPref(nsCAutoString(iTag));
}
delete[] pref;
return NS_OK;
}
/**
Parses e.g. "a(href,title)" (but not several tags at once).
*/
nsresult
mozSanitizingHTMLSerializer::ParseTagPref(const nsCAutoString& tagpref)
{
nsIParserService* parserService = nsContentUtils::GetParserService();
if (!parserService)
return NS_ERROR_OUT_OF_MEMORY;
// Parsing tag
PRInt32 bracket = tagpref.FindChar('(');
if (bracket == 0)
{
printf(" malformed pref: %s\n", tagpref.get());
return NS_ERROR_CANNOT_CONVERT_DATA;
}
nsAutoString tag;
CopyUTF8toUTF16(StringHead(tagpref, bracket), tag);
// Create key
PRInt32 tag_id = parserService->HTMLStringTagToId(tag);
if (tag_id == eHTMLTag_userdefined)
{
printf(" unknown tag <%s>, won't add.\n",
NS_ConvertUTF16toUTF8(tag).get());
return NS_ERROR_CANNOT_CONVERT_DATA;
}
nsPRUint32Key tag_key(tag_id);
if (mAllowedTags.Exists(&tag_key))
{
printf(" duplicate tag: %s\n", NS_ConvertUTF16toUTF8(tag).get());
return NS_ERROR_CANNOT_CONVERT_DATA;
}
if (bracket == kNotFound)
/* There are no attributes in the pref. So, allow none; only the tag
itself */
{
mAllowedTags.Put(&tag_key, 0);
}
else
{
// Attributes
// where is the macro for non-fatal errors in opt builds?
if(tagpref[tagpref.Length() - 1] != ')' ||
tagpref.Length() < PRUint32(bracket) + 3)
{
printf(" malformed pref: %s\n", tagpref.get());
return NS_ERROR_CANNOT_CONVERT_DATA;
}
nsCOMPtr<nsIProperties> attr_bag =
do_CreateInstance(NS_PROPERTIES_CONTRACTID);
NS_ENSURE_TRUE(attr_bag, NS_ERROR_INVALID_POINTER);
nsCAutoString attrList;
attrList.Append(Substring(tagpref,
bracket + 1,
tagpref.Length() - 2 - bracket));
char* attrs_lasts;
for (char* iAttr = PL_strtok_r(attrList.BeginWriting(),
",", &attrs_lasts);
iAttr;
iAttr = PL_strtok_r(NULL, ",", &attrs_lasts))
{
attr_bag->Set(iAttr, 0);
}
nsIProperties* attr_bag_raw = attr_bag;
NS_ADDREF(attr_bag_raw);
mAllowedTags.Put(&tag_key, attr_bag_raw);
}
return NS_OK;
}
/*
might be useful:
htmlparser/public/nsHTMLTokens.h for tag categories
*/

View File

@ -1,158 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et 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 HTML Sanitizer code.
*
* The Initial Developer of the Original Code is
* Ben Bucksch <mozilla@bucksch.org>.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Netscape
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* A serializer and content sink that removes potentially insecure or
* otherwise dangerous or offending HTML (eg for display of HTML
* e-mail attachments or something).
*/
#ifndef mozSanitizingSerializer_h__
#define mozSanitizingSerializer_h__
#include "mozISanitizingSerializer.h"
#include "nsIContentSerializer.h"
#include "nsIHTMLContentSink.h"
#include "nsHTMLTags.h"
#include "nsCOMPtr.h"
#include "nsIParserService.h"
#include "nsIContent.h"
#include "nsIAtom.h"
#include "nsString.h"
#include "nsIParser.h"
#include "nsHashtable.h"
class mozSanitizingHTMLSerializer : public nsIContentSerializer,
public nsIHTMLContentSink,
public mozISanitizingHTMLSerializer
{
public:
mozSanitizingHTMLSerializer();
virtual ~mozSanitizingHTMLSerializer();
static bool ReleaseProperties(nsHashKey* key, void* data, void* closure);
NS_DECL_ISUPPORTS
// nsIContentSerializer
NS_IMETHOD Init(PRUint32 flags, PRUint32 dummy, const char* aCharSet,
bool aIsCopying, bool aIsWholeDocument);
NS_IMETHOD AppendText(nsIContent* aText, PRInt32 aStartOffset,
PRInt32 aEndOffset, nsAString& aStr);
NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection,
PRInt32 aStartOffset, PRInt32 aEndOffset,
nsAString& aStr)
{ return NS_OK; }
NS_IMETHOD AppendProcessingInstruction(nsIContent* aPI,
PRInt32 aStartOffset,
PRInt32 aEndOffset,
nsAString& aStr)
{ return NS_OK; }
NS_IMETHOD AppendComment(nsIContent* aComment, PRInt32 aStartOffset,
PRInt32 aEndOffset, nsAString& aStr)
{ return NS_OK; }
NS_IMETHOD AppendDoctype(nsIContent *aDoctype, nsAString& aStr)
{ return NS_OK; }
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
mozilla::dom::Element* aOriginalElement,
nsAString& aStr);
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
nsAString& aStr);
NS_IMETHOD Flush(nsAString& aStr);
NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
nsAString& aStr);
// nsIContentSink
NS_IMETHOD WillParse(void) { return NS_OK; }
NS_IMETHOD WillInterrupt(void) { return NS_OK; }
NS_IMETHOD WillResume(void) { return NS_OK; }
NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
virtual void FlushPendingNotifications(mozFlushType aType) { }
NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
virtual nsISupports *GetTarget() { return nsnull; }
// nsIHTMLContentSink
NS_IMETHOD OpenHead();
NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn);
NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
// nsISanitizingHTMLSerializer
NS_IMETHOD Initialize(nsAString* aOutString,
PRUint32 aFlags, const nsAString& allowedTags);
protected:
nsresult ParsePrefs(const nsAString& aPref);
nsresult ParseTagPref(const nsCAutoString& tagpref);
bool IsAllowedTag(nsHTMLTag aTag);
bool IsAllowedAttribute(nsHTMLTag aTag, const nsAString& anAttributeName);
nsresult SanitizeAttrValue(nsHTMLTag aTag, const nsAString& attr_name,
nsString& value /*inout*/);
nsresult SanitizeTextNode(nsString& value /*inout*/);
bool IsContainer(PRInt32 aId);
static PRInt32 GetIdForContent(nsIContent* aContent);
nsresult GetParserService(nsIParserService** aParserService);
nsresult DoOpenContainer(PRInt32 aTag);
nsresult DoCloseContainer(PRInt32 aTag);
nsresult DoAddLeaf(PRInt32 aTag, const nsAString& aText);
void Write(const nsAString& aString);
protected:
PRInt32 mFlags;
PRUint32 mSkipLevel;
nsHashtable mAllowedTags;
nsRefPtr<mozilla::dom::Element> mElement;
nsAString* mOutputString;
nsIParserNode* mParserNode;
nsCOMPtr<nsIParserService> mParserService;
};
nsresult
NS_NewSanitizingHTMLSerializer(nsIContentSerializer** aSerializer);
#endif

View File

@ -0,0 +1,6 @@
function f(s) {
return arguments[s];
}
for (var i = 0; i < 10; ++i)
assertEq(f(String(i+1), 0,1,2,3,4,5,6,7,8,9), i);

View File

@ -0,0 +1,6 @@
function getArgs() { return arguments; }
var a1 = getArgs(1);
var a2 = getArgs(1);
a1.__proto__ = a2;
delete a1[0];
a1[0];

View File

@ -678,14 +678,17 @@ TypeSet::addCall(JSContext *cx, TypeCallsite *site)
class TypeConstraintArith : public TypeConstraint class TypeConstraintArith : public TypeConstraint
{ {
public: public:
JSScript *script;
jsbytecode *pc;
/* Type set receiving the result of the arithmetic. */ /* Type set receiving the result of the arithmetic. */
TypeSet *target; TypeSet *target;
/* For addition operations, the other operand. */ /* For addition operations, the other operand. */
TypeSet *other; TypeSet *other;
TypeConstraintArith(TypeSet *target, TypeSet *other) TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
: TypeConstraint("arith"), target(target), other(other) : TypeConstraint("arith"), script(script), pc(pc), target(target), other(other)
{ {
JS_ASSERT(target); JS_ASSERT(target);
} }
@ -694,9 +697,9 @@ public:
}; };
void void
TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other) TypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
{ {
add(cx, cx->typeLifoAlloc().new_<TypeConstraintArith>(target, other)); add(cx, cx->typeLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
} }
/* Subset constraint which transforms primitive values into appropriate objects. */ /* Subset constraint which transforms primitive values into appropriate objects. */
@ -1299,27 +1302,29 @@ TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
} else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) { } else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN | TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN |
TYPE_FLAG_ANYOBJECT) || TYPE_FLAG_ANYOBJECT)) {
other->getObjectCount() != 0) {
target->addType(cx, Type::DoubleType()); target->addType(cx, Type::DoubleType());
} else if (other->getObjectCount() != 0) {
TypeDynamicResult(cx, script, pc, Type::DoubleType());
} }
} else if (type.isPrimitive(JSVAL_TYPE_STRING)) { } else if (type.isPrimitive(JSVAL_TYPE_STRING)) {
target->addType(cx, Type::StringType()); target->addType(cx, Type::StringType());
} else { } else if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) {
if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | target->addType(cx, Type::DoubleType());
TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN | } else if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
TYPE_FLAG_ANYOBJECT) || TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN |
other->getObjectCount() != 0) { TYPE_FLAG_ANYOBJECT)) {
target->addType(cx, Type::Int32Type()); target->addType(cx, Type::Int32Type());
} } else if (other->getObjectCount() != 0) {
if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) TypeDynamicResult(cx, script, pc, Type::Int32Type());
target->addType(cx, Type::DoubleType());
} }
} else { } else {
if (type.isUnknown()) if (type.isUnknown())
target->addType(cx, Type::UnknownType()); target->addType(cx, Type::UnknownType());
else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) else if (type.isPrimitive(JSVAL_TYPE_DOUBLE))
target->addType(cx, Type::DoubleType()); target->addType(cx, Type::DoubleType());
else if (!type.isAnyObject() && type.isObject())
TypeDynamicResult(cx, script, pc, Type::Int32Type());
else else
target->addType(cx, Type::Int32Type()); target->addType(cx, Type::Int32Type());
} }
@ -3616,10 +3621,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_LOCALDEC: { case JSOP_LOCALDEC: {
uint32_t slot = GetBytecodeSlot(script, pc); uint32_t slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot)) { if (trackSlot(slot)) {
poppedTypes(pc, 0)->addArith(cx, &pushed[0]); poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
} else if (slot < TotalSlots(script)) { } else if (slot < TotalSlots(script)) {
TypeSet *types = TypeScript::SlotTypes(script, slot); TypeSet *types = TypeScript::SlotTypes(script, slot);
types->addArith(cx, types); types->addArith(cx, script, pc, types);
types->addSubset(cx, &pushed[0]); types->addSubset(cx, &pushed[0]);
} else { } else {
pushed[0].addType(cx, Type::UnknownType()); pushed[0].addType(cx, Type::UnknownType());
@ -3702,21 +3707,21 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
break; break;
case JSOP_ADD: case JSOP_ADD:
poppedTypes(pc, 0)->addArith(cx, &pushed[0], poppedTypes(pc, 1)); poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 1));
poppedTypes(pc, 1)->addArith(cx, &pushed[0], poppedTypes(pc, 0)); poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 0));
break; break;
case JSOP_SUB: case JSOP_SUB:
case JSOP_MUL: case JSOP_MUL:
case JSOP_MOD: case JSOP_MOD:
case JSOP_DIV: case JSOP_DIV:
poppedTypes(pc, 0)->addArith(cx, &pushed[0]); poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
poppedTypes(pc, 1)->addArith(cx, &pushed[0]); poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0]);
break; break;
case JSOP_NEG: case JSOP_NEG:
case JSOP_POS: case JSOP_POS:
poppedTypes(pc, 0)->addArith(cx, &pushed[0]); poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
break; break;
case JSOP_LAMBDA: case JSOP_LAMBDA:

View File

@ -441,7 +441,8 @@ class TypeSet
void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
TypeSet *objectTypes, TypeSet *valueTypes); TypeSet *objectTypes, TypeSet *valueTypes);
void addCall(JSContext *cx, TypeCallsite *site); void addCall(JSContext *cx, TypeCallsite *site);
void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL); void addArith(JSContext *cx, JSScript *script, jsbytecode *pc,
TypeSet *target, TypeSet *other = NULL);
void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target); void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
Type type, TypeSet *types = NULL); Type type, TypeSet *types = NULL);

View File

@ -6247,7 +6247,6 @@ dumpValue(const Value &v)
#ifdef DEBUG #ifdef DEBUG
switch (v.whyMagic()) { switch (v.whyMagic()) {
case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break; case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break; case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break; case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break; case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;

View File

@ -297,6 +297,66 @@ UnsignedPtrDiff(const void *bigger, const void *smaller)
*/ */
enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false }; enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false };
/*****************************************************************************/
/* A bit array is an array of bits represented by an array of words (size_t). */
static inline unsigned
NumWordsForBitArrayOfLength(size_t length)
{
return (length + (JS_BITS_PER_WORD - 1)) / JS_BITS_PER_WORD;
}
static inline unsigned
BitArrayIndexToWordIndex(size_t length, size_t bitIndex)
{
unsigned wordIndex = bitIndex / JS_BITS_PER_WORD;
JS_ASSERT(wordIndex < length);
return wordIndex;
}
static inline size_t
BitArrayIndexToWordMask(size_t i)
{
return size_t(1) << (i % JS_BITS_PER_WORD);
}
static inline bool
IsBitArrayElementSet(size_t *array, size_t length, size_t i)
{
return array[BitArrayIndexToWordIndex(length, i)] & BitArrayIndexToWordMask(i);
}
static inline bool
IsAnyBitArrayElementSet(size_t *array, size_t length)
{
unsigned numWords = NumWordsForBitArrayOfLength(length);
for (unsigned i = 0; i < numWords; ++i) {
if (array[i])
return true;
}
return false;
}
static inline void
SetBitArrayElement(size_t *array, size_t length, size_t i)
{
array[BitArrayIndexToWordIndex(length, i)] |= BitArrayIndexToWordMask(i);
}
static inline void
ClearBitArrayElement(size_t *array, size_t length, size_t i)
{
array[BitArrayIndexToWordIndex(length, i)] &= ~BitArrayIndexToWordMask(i);
}
static inline void
ClearAllBitArrayElements(size_t *array, size_t length)
{
for (unsigned i = 0; i < length; ++i)
array[i] = 0;
}
} /* namespace js */ } /* namespace js */
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -275,7 +275,6 @@ typedef uint64_t JSValueShiftedTag;
typedef enum JSWhyMagic typedef enum JSWhyMagic
{ {
JS_ARRAY_HOLE, /* a hole in a dense array */ JS_ARRAY_HOLE, /* a hole in a dense array */
JS_ARGS_HOLE, /* a hole in the args object's array */
JS_NATIVE_ENUMERATE, /* indicates that a custom enumerate hook forwarded JS_NATIVE_ENUMERATE, /* indicates that a custom enumerate hook forwarded
* to JS_EnumerateState, which really means the object can be * to JS_EnumerateState, which really means the object can be
* enumerated like a native object. */ * enumerated like a native object. */
@ -289,6 +288,7 @@ typedef enum JSWhyMagic
JS_UNASSIGNED_ARGUMENTS, /* the initial value of callobj.arguments */ JS_UNASSIGNED_ARGUMENTS, /* the initial value of callobj.arguments */
JS_OPTIMIZED_ARGUMENTS, /* optimized-away 'arguments' value */ JS_OPTIMIZED_ARGUMENTS, /* optimized-away 'arguments' value */
JS_IS_CONSTRUCTING, /* magic value passed to natives to indicate construction */ JS_IS_CONSTRUCTING, /* magic value passed to natives to indicate construction */
JS_OVERWRITTEN_CALLEE, /* arguments.callee has been overwritten */
JS_GENERIC_MAGIC /* for local use */ JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic; } JSWhyMagic;

View File

@ -864,47 +864,6 @@ class GetPropCompiler : public PICStubCompiler
repatcher.relink(pic.slowPathCall, target); repatcher.relink(pic.slowPathCall, target);
} }
LookupStatus generateArgsLengthStub()
{
Assembler masm;
Jump notArgs = masm.guardShape(pic.objReg, obj);
masm.load32(Address(pic.objReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), pic.objReg);
masm.move(pic.objReg, pic.shapeReg);
Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), pic.objReg);
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
Jump done = masm.jump();
pic.updatePCCounters(f, masm);
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.chunk())) ||
!buffer.verifyRange(f.chunk())) {
return disable("code memory is out of range");
}
buffer.link(notArgs, pic.slowPathStart);
buffer.link(overridden, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel start = buffer.finalize(f);
JaegerSpew(JSpew_PICs, "generate args length stub at %p\n",
start.executableAddress());
patchPreviousToHere(start);
disable("args length done");
return Lookup_Cacheable;
}
LookupStatus generateArrayLengthStub() LookupStatus generateArrayLengthStub()
{ {
Assembler masm; Assembler masm;
@ -1883,21 +1842,14 @@ GetPropMaybeCached(VMFrame &f, ic::PICInfo *pic, bool cached)
} }
if (!f.regs.sp[-1].isPrimitive()) { if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject(); JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() || if (obj->isArray() || obj->isString()) {
(obj->isArguments() && !obj->asArguments().hasOverriddenLength()) ||
obj->isString()) {
GetPropCompiler cc(f, script, obj, *pic, NULL, stub); GetPropCompiler cc(f, script, obj, *pic, NULL, stub);
if (obj->isArray()) { if (obj->isArray()) {
LookupStatus status = cc.generateArrayLengthStub(); LookupStatus status = cc.generateArrayLengthStub();
if (status == Lookup_Error) if (status == Lookup_Error)
THROW(); THROW();
f.regs.sp[-1].setNumber(obj->getArrayLength()); f.regs.sp[-1].setNumber(obj->getArrayLength());
} else if (obj->isArguments()) { } else {
LookupStatus status = cc.generateArgsLengthStub();
if (status == Lookup_Error)
THROW();
f.regs.sp[-1].setInt32(int32_t(obj->asArguments().initialLength()));
} else if (obj->isString()) {
LookupStatus status = cc.generateStringObjLengthStub(); LookupStatus status = cc.generateStringObjLengthStub();
if (status == Lookup_Error) if (status == Lookup_Error)
THROW(); THROW();
@ -2368,158 +2320,6 @@ GetElementIC::attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyN
return Lookup_Cacheable; return Lookup_Cacheable;
} }
LookupStatus
GetElementIC::attachArguments(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp)
{
JSContext *cx = f.cx;
if (!v.isInt32())
return disable(f, "arguments object with non-integer key");
if (op == JSOP_CALLELEM)
return disable(f, "arguments object with call");
JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32);
Assembler masm;
Jump shapeGuard = masm.testObjClass(Assembler::NotEqual, objReg, typeReg, obj->getClass());
masm.move(objReg, typeReg);
masm.load32(Address(objReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)),
objReg);
Jump overridden = masm.branchTest32(Assembler::NonZero, objReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), objReg);
Jump outOfBounds;
if (idRemat.isConstant()) {
outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32()));
} else {
outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg());
}
masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg);
if (idRemat.isConstant()) {
Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value));
masm.loadTypeTag(slot, objReg);
} else {
BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE,
offsetof(ArgumentsData, slots));
masm.loadTypeTag(slot, objReg);
}
Jump holeCheck = masm.branchPtr(Assembler::Equal, objReg, ImmType(JSVAL_TYPE_MAGIC));
masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::STACK_FRAME_SLOT)), objReg);
Jump liveArguments = masm.branchPtr(Assembler::NotEqual, objReg, ImmPtr(0));
masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg);
if (idRemat.isConstant()) {
Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value));
masm.loadValueAsComponents(slot, typeReg, objReg);
} else {
BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE,
offsetof(ArgumentsData, slots));
masm.loadValueAsComponents(slot, typeReg, objReg);
}
Jump done = masm.jump();
liveArguments.linkTo(masm.label(), &masm);
masm.move(objReg, typeReg);
Address fun(typeReg, StackFrame::offsetOfExec());
masm.loadPtr(fun, objReg);
Address nargs(objReg, offsetof(JSFunction, nargs));
masm.load16(nargs, objReg);
Jump notFormalArg;
if (idRemat.isConstant())
notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32()));
else
notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg());
masm.lshift32(Imm32(3), objReg); /* nargs << 3 == nargs * sizeof(Value) */
masm.subPtr(objReg, typeReg); /* fp - numFormalArgs => start of formal args */
Label loadFromStack = masm.label();
masm.move(typeReg, objReg);
if (idRemat.isConstant()) {
Address frameEntry(objReg, v.toInt32() * sizeof(Value));
masm.loadValueAsComponents(frameEntry, typeReg, objReg);
} else {
BaseIndex frameEntry(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE);
masm.loadValueAsComponents(frameEntry, typeReg, objReg);
}
Jump done2 = masm.jump();
notFormalArg.linkTo(masm.label(), &masm);
masm.push(typeReg);
Address argsObject(typeReg, StackFrame::offsetOfArgsObj());
masm.loadPtr(argsObject, typeReg);
masm.load32(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)),
typeReg);
masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), typeReg);
/* This basically does fp - (numFormalArgs + numActualArgs + 2) */
masm.addPtr(typeReg, objReg);
masm.addPtr(Imm32(2), objReg);
masm.lshiftPtr(Imm32(3), objReg);
masm.pop(typeReg);
masm.subPtr(objReg, typeReg);
masm.jump(loadFromStack);
updatePCCounters(f, masm);
PICLinker buffer(masm, *this);
if (!buffer.init(cx))
return error(cx);
if (!buffer.verifyRange(f.chunk()))
return disable(f, "code memory is out of range");
buffer.link(shapeGuard, slowPathStart);
buffer.link(overridden, slowPathStart);
buffer.link(outOfBounds, slowPathStart);
buffer.link(holeCheck, slowPathStart);
buffer.link(done, fastPathRejoin);
buffer.link(done2, fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
JaegerSpew(JSpew_PICs, "generated getelem arguments stub at %p\n", cs.executableAddress());
Repatcher repatcher(f.chunk());
repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), cs);
JS_ASSERT(!shouldPatchUnconditionalShapeGuard());
JS_ASSERT(!inlineShapeGuardPatched);
inlineShapeGuardPatched = true;
stubsGenerated++;
if (stubsGenerated == MAX_GETELEM_IC_STUBS)
disable(f, "max stubs reached");
disable(f, "generated arguments stub");
if (!obj->getGeneric(cx, id, vp))
return Lookup_Error;
return Lookup_Cacheable;
}
#if defined JS_METHODJIT_TYPED_ARRAY #if defined JS_METHODJIT_TYPED_ARRAY
LookupStatus LookupStatus
GetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp) GetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp)
@ -2627,9 +2427,6 @@ GetElementIC::update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *
if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy))
return attachGetProp(f, obj, v, JSID_TO_ATOM(id)->asPropertyName(), vp); return attachGetProp(f, obj, v, JSID_TO_ATOM(id)->asPropertyName(), vp);
if (obj->isArguments())
return attachArguments(f, obj, v, id, vp);
#if defined JS_METHODJIT_TYPED_ARRAY #if defined JS_METHODJIT_TYPED_ARRAY
/* /*
* Typed array ICs can make stub calls, and need to know which registers * Typed array ICs can make stub calls, and need to know which registers

View File

@ -301,7 +301,6 @@ struct GetElementIC : public BasePolyIC {
LookupStatus update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp); LookupStatus update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name, LookupStatus attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name,
Value *vp); Value *vp);
LookupStatus attachArguments(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp); LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus disable(VMFrame &f, const char *reason); LookupStatus disable(VMFrame &f, const char *reason);
LookupStatus error(JSContext *cx); LookupStatus error(JSContext *cx);

View File

@ -89,39 +89,42 @@ ArgumentsObject::data() const
return reinterpret_cast<js::ArgumentsData *>(getFixedSlot(DATA_SLOT).toPrivate()); return reinterpret_cast<js::ArgumentsData *>(getFixedSlot(DATA_SLOT).toPrivate());
} }
inline bool
ArgumentsObject::isElementDeleted(uint32_t i) const
{
return IsBitArrayElementSet(data()->deletedBits, initialLength(), i);
}
inline bool
ArgumentsObject::isAnyElementDeleted() const
{
return IsAnyBitArrayElementSet(data()->deletedBits, initialLength());
}
inline void
ArgumentsObject::markElementDeleted(uint32_t i)
{
SetBitArrayElement(data()->deletedBits, initialLength(), i);
}
inline const js::Value & inline const js::Value &
ArgumentsObject::element(uint32_t i) const ArgumentsObject::element(uint32_t i) const
{ {
JS_ASSERT(i < initialLength()); JS_ASSERT(!isElementDeleted(i));
return data()->slots[i]; return data()->slots[i];
} }
inline const js::Value *
ArgumentsObject::elements() const
{
return Valueify(data()->slots);
}
inline void inline void
ArgumentsObject::setElement(uint32_t i, const js::Value &v) ArgumentsObject::setElement(uint32_t i, const js::Value &v)
{ {
JS_ASSERT(i < initialLength()); JS_ASSERT(!isElementDeleted(i));
data()->slots[i] = v; data()->slots[i] = v;
} }
inline bool inline bool
ArgumentsObject::getElement(uint32_t i, Value *vp) ArgumentsObject::getElement(uint32_t i, Value *vp)
{ {
if (i >= initialLength()) if (i >= initialLength() || isElementDeleted(i))
return false;
*vp = element(i);
/*
* If the argument was overwritten, it could be in any object slot, so we
* can't optimize.
*/
if (vp->isMagic(JS_ARGS_HOLE))
return false; return false;
/* /*
@ -133,6 +136,8 @@ ArgumentsObject::getElement(uint32_t i, Value *vp)
JS_ASSERT_IF(isStrictArguments(), !fp); JS_ASSERT_IF(isStrictArguments(), !fp);
if (fp) if (fp)
*vp = fp->canonicalActualArg(i); *vp = fp->canonicalActualArg(i);
else
*vp = element(i);
return true; return true;
} }
@ -144,8 +149,6 @@ struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
ArgumentsObject &argsobj; ArgumentsObject &argsobj;
Value *dst; Value *dst;
bool operator()(uint32_t argi, Value *src) { bool operator()(uint32_t argi, Value *src) {
if (argsobj.element(argi).isMagic(JS_ARGS_HOLE))
return false;
*dst++ = *src; *dst++ = *src;
return true; return true;
} }
@ -159,21 +162,18 @@ ArgumentsObject::getElements(uint32_t start, uint32_t count, Value *vp)
JS_ASSERT(start + count >= start); JS_ASSERT(start + count >= start);
uint32_t length = initialLength(); uint32_t length = initialLength();
if (start > length || start + count > length) if (start > length || start + count > length || isAnyElementDeleted())
return false; return false;
StackFrame *fp = maybeStackFrame(); StackFrame *fp = maybeStackFrame();
/* If there's no stack frame for this, argument values are in elements(). */ /* If there's no stack frame for this, argument values are in elements(). */
if (!fp) { if (!fp) {
const Value *srcbeg = elements() + start; const Value *srcbeg = Valueify(data()->slots) + start;
const Value *srcend = srcbeg + count; const Value *srcend = srcbeg + count;
const Value *src = srcbeg; const Value *src = srcbeg;
for (Value *dst = vp; src < srcend; ++dst, ++src) { for (Value *dst = vp; src < srcend; ++dst, ++src)
if (src->isMagic(JS_ARGS_HOLE))
return false;
*dst = *src; *dst = *src;
}
return true; return true;
} }
@ -209,7 +209,7 @@ NormalArgumentsObject::callee() const
inline void inline void
NormalArgumentsObject::clearCallee() NormalArgumentsObject::clearCallee()
{ {
data()->callee.set(compartment(), MagicValue(JS_ARGS_HOLE)); data()->callee.set(compartment(), MagicValue(JS_OVERWRITTEN_CALLEE));
} }
} // namespace js } // namespace js

View File

@ -56,12 +56,14 @@ using namespace js::gc;
struct PutArg struct PutArg
{ {
PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {} PutArg(JSCompartment *comp, ArgumentsObject &argsobj)
HeapValue *dst; : compartment(comp), argsobj(argsobj), dst(argsobj.data()->slots) {}
JSCompartment *compartment; JSCompartment *compartment;
bool operator()(unsigned, Value *src) { ArgumentsObject &argsobj;
JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined()); HeapValue *dst;
if (!dst->isMagic(JS_ARGS_HOLE)) bool operator()(unsigned i, Value *src) {
JS_ASSERT(dst->isUndefined());
if (!argsobj.isElementDeleted(i))
dst->set(compartment, *src); dst->set(compartment, *src);
++dst; ++dst;
return true; return true;
@ -75,7 +77,7 @@ js_PutArgsObject(StackFrame *fp)
if (argsobj.isNormalArguments()) { if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.maybeStackFrame() == fp); JS_ASSERT(argsobj.maybeStackFrame() == fp);
JSCompartment *comp = fp->scopeChain().compartment(); JSCompartment *comp = fp->scopeChain().compartment();
fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots)); fp->forEachCanonicalActualArg(PutArg(comp, argsobj));
argsobj.setStackFrame(NULL); argsobj.setStackFrame(NULL);
} else { } else {
JS_ASSERT(!argsobj.maybeStackFrame()); JS_ASSERT(!argsobj.maybeStackFrame());
@ -108,14 +110,20 @@ ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee)
if (!emptyArgumentsShape) if (!emptyArgumentsShape)
return NULL; return NULL;
ArgumentsData *data = (ArgumentsData *) unsigned numDeletedWords = NumWordsForBitArrayOfLength(argc);
cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value)); unsigned numBytes = offsetof(ArgumentsData, slots) +
numDeletedWords * sizeof(size_t) +
argc * sizeof(Value);
ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes);
if (!data) if (!data)
return NULL; return NULL;
data->callee.init(ObjectValue(callee)); data->callee.init(ObjectValue(callee));
for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++) for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++)
vp->init(UndefinedValue()); vp->init(UndefinedValue());
data->deletedBits = (size_t *)(data->slots + argc);
ClearAllBitArrayElements(data->deletedBits, numDeletedWords);
/* We have everything needed to fill in the object, so make the object. */ /* We have everything needed to fill in the object, so make the object. */
JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL); JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL);
@ -151,7 +159,7 @@ ArgumentsObject::create(JSContext *cx, StackFrame *fp)
* to retrieve up-to-date parameter values. * to retrieve up-to-date parameter values.
*/ */
if (argsobj->isStrictArguments()) if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots)); fp->forEachCanonicalActualArg(PutArg(cx->compartment, *argsobj));
else else
argsobj->setStackFrame(fp); argsobj->setStackFrame(fp);
@ -166,7 +174,7 @@ ArgumentsObject::createUnexpected(JSContext *cx, StackFrame *fp)
if (!argsobj) if (!argsobj)
return NULL; return NULL;
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots)); fp->forEachCanonicalActualArg(PutArg(cx->compartment, *argsobj));
return argsobj; return argsobj;
} }
@ -176,8 +184,10 @@ args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
ArgumentsObject &argsobj = obj->asArguments(); ArgumentsObject &argsobj = obj->asArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id)); unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE)); argsobj.setElement(arg, UndefinedValue());
argsobj.markElementDeleted(arg);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
argsobj.markLengthOverridden(); argsobj.markLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
@ -199,8 +209,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
* prototype to point to another Arguments object with a bigger argc. * prototype to point to another Arguments object with a bigger argc.
*/ */
unsigned arg = unsigned(JSID_TO_INT(id)); unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) { if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = argsobj.maybeStackFrame()) if (StackFrame *fp = argsobj.maybeStackFrame())
*vp = fp->canonicalActualArg(arg); *vp = fp->canonicalActualArg(arg);
else else
@ -212,7 +221,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
} else { } else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)); JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = argsobj.callee(); const Value &v = argsobj.callee();
if (!v.isMagic(JS_ARGS_HOLE)) if (!v.isMagic(JS_OVERWRITTEN_CALLEE))
*vp = v; *vp = v;
} }
return true; return true;
@ -267,7 +276,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id)); uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE)) if (arg >= argsobj.initialLength() || argsobj.isElementDeleted(arg))
return true; return true;
attrs |= JSPROP_ENUMERATE; attrs |= JSPROP_ENUMERATE;
@ -278,7 +287,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
return true; return true;
if (argsobj.callee().isMagic(JS_ARGS_HOLE)) if (argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE))
return true; return true;
} }
@ -295,17 +304,29 @@ NormalArgumentsObject::optimizedGetElem(JSContext *cx, StackFrame *fp, const Val
{ {
JS_ASSERT(!fp->hasArgsObj()); JS_ASSERT(!fp->hasArgsObj());
/* Fast path: no need to convert to id when elem is already an int in range. */
if (elem.isInt32()) { if (elem.isInt32()) {
int32_t i = elem.toInt32(); int32_t i = elem.toInt32();
if (i >= 0 && uint32_t(i) < fp->numActualArgs()) { if (i >= 0 && uint32_t(i) < fp->numActualArgs()) {
*vp = fp->canonicalActualArg(elem.toInt32()); *vp = fp->canonicalActualArg(i);
return true; return true;
} }
} }
/* Slow path: create and canonicalize an id, then emulate args_resolve. */
jsid id; jsid id;
if (!ValueToId(cx, elem, &id)) if (!ValueToId(cx, elem, &id))
return false; return false;
id = js_CheckForStringIndex(id);
if (JSID_IS_INT(id)) {
int32_t i = JSID_TO_INT(id);
if (i >= 0 && uint32_t(i) < fp->numActualArgs()) {
*vp = fp->canonicalActualArg(i);
return true;
}
}
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
*vp = Int32Value(fp->numActualArgs()); *vp = Int32Value(fp->numActualArgs());
@ -363,11 +384,8 @@ StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
* prototype to point to another Arguments object with a bigger argc. * prototype to point to another Arguments object with a bigger argc.
*/ */
unsigned arg = unsigned(JSID_TO_INT(id)); unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) { if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
const Value &v = argsobj.element(arg); *vp = argsobj.element(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
} else { } else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)); JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!argsobj.hasOverriddenLength()) if (!argsobj.hasOverriddenLength())
@ -418,7 +436,7 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObje
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32_t arg = uint32_t(JSID_TO_INT(id)); uint32_t arg = uint32_t(JSID_TO_INT(id));
if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE)) if (arg >= argsobj.initialLength() || argsobj.isElementDeleted(arg))
return true; return true;
attrs |= JSPROP_ENUMERATE; attrs |= JSPROP_ENUMERATE;

View File

@ -23,6 +23,7 @@
* *
* Contributor(s): * Contributor(s):
* Jeff Walden <jwalden+code@mit.edu> (original author) * Jeff Walden <jwalden+code@mit.edu> (original author)
* Luke Wagner <luke@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@ -43,30 +44,8 @@
#include "jsfun.h" #include "jsfun.h"
#ifdef JS_POLYIC
class GetPropCompiler;
#endif
namespace js { namespace js {
#ifdef JS_POLYIC
struct VMFrame;
namespace mjit {
namespace ic {
struct PICInfo;
struct GetElementIC;
/* Aargh, Windows. */
#ifdef GetProp
#undef GetProp
#endif
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *pic);
}
}
#endif
struct EmptyShape;
/* /*
* ArgumentsData stores the initial indexed arguments provided to the * ArgumentsData stores the initial indexed arguments provided to the
* corresponding and that function itself. It is used to store arguments[i] * corresponding and that function itself. It is used to store arguments[i]
@ -82,6 +61,12 @@ struct ArgumentsData
*/ */
HeapValue callee; HeapValue callee;
/*
* Pointer to an array of bits indicating, for every argument in 'slots',
* whether the element has been deleted. See isElementDeleted comment.
*/
size_t *deletedBits;
/* /*
* Values of the arguments for this object, or MagicValue(JS_ARGS_HOLE) if * Values of the arguments for this object, or MagicValue(JS_ARGS_HOLE) if
* the indexed argument has been modified. * the indexed argument has been modified.
@ -147,29 +132,18 @@ class ArgumentsObject : public JSObject
static const uint32_t DATA_SLOT = 1; static const uint32_t DATA_SLOT = 1;
static const uint32_t STACK_FRAME_SLOT = 2; static const uint32_t STACK_FRAME_SLOT = 2;
public:
static const uint32_t RESERVED_SLOTS = 3;
static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
private:
/* Lower-order bit stolen from the length slot. */ /* Lower-order bit stolen from the length slot. */
static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1; static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
static const uint32_t PACKED_BITS_COUNT = 1; static const uint32_t PACKED_BITS_COUNT = 1;
/*
* Need access to DATA_SLOT, INITIAL_LENGTH_SLOT, LENGTH_OVERRIDDEN_BIT, and
* PACKED_BIT_COUNT.
*/
#ifdef JS_POLYIC
friend class ::GetPropCompiler;
friend struct mjit::ic::GetElementIC;
#endif
void initInitialLength(uint32_t length); void initInitialLength(uint32_t length);
void initData(ArgumentsData *data); void initData(ArgumentsData *data);
static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee); static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
public: public:
static const uint32_t RESERVED_SLOTS = 3;
static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
/* Create an arguments object for a frame that is expecting them. */ /* Create an arguments object for a frame that is expecting them. */
static bool create(JSContext *cx, StackFrame *fp); static bool create(JSContext *cx, StackFrame *fp);
@ -214,8 +188,25 @@ class ArgumentsObject : public JSObject
inline js::ArgumentsData *data() const; inline js::ArgumentsData *data() const;
/*
* Because the arguments object is a real object, its elements may be
* deleted. This is implemented by setting a 'deleted' flag for the arg
* which is read by argument object resolve and getter/setter hooks.
*
* NB: an element, once deleted, stays deleted. Thus:
*
* function f(x) { delete arguments[0]; arguments[0] = 42; return x }
* assertEq(f(1), 1);
*
* This works because, once a property is deleted from an arguments object,
* it gets regular properties with regular getters/setters that don't alias
* ArgumentsData::slots.
*/
inline bool isElementDeleted(uint32_t i) const;
inline bool isAnyElementDeleted() const;
inline void markElementDeleted(uint32_t i);
inline const js::Value &element(uint32_t i) const; inline const js::Value &element(uint32_t i) const;
inline const js::Value *elements() const;
inline void setElement(uint32_t i, const js::Value &v); inline void setElement(uint32_t i, const js::Value &v);
/* The stack frame for this ArgumentsObject, if the frame is still active. */ /* The stack frame for this ArgumentsObject, if the frame is still active. */

View File

@ -73,7 +73,6 @@
#include "nsIXBLService.h" #include "nsIXBLService.h"
#include "nsCaret.h" #include "nsCaret.h"
#include "nsPlainTextSerializer.h" #include "nsPlainTextSerializer.h"
#include "mozSanitizingSerializer.h"
#include "nsXMLContentSerializer.h" #include "nsXMLContentSerializer.h"
#include "nsXHTMLContentSerializer.h" #include "nsXHTMLContentSerializer.h"
#include "nsRuleNode.h" #include "nsRuleNode.h"
@ -514,7 +513,6 @@ MAKE_CTOR(CreateXMLContentSerializer, nsIContentSerializer, NS_NewXML
MAKE_CTOR(CreateHTMLContentSerializer, nsIContentSerializer, NS_NewHTMLContentSerializer) MAKE_CTOR(CreateHTMLContentSerializer, nsIContentSerializer, NS_NewHTMLContentSerializer)
MAKE_CTOR(CreateXHTMLContentSerializer, nsIContentSerializer, NS_NewXHTMLContentSerializer) MAKE_CTOR(CreateXHTMLContentSerializer, nsIContentSerializer, NS_NewXHTMLContentSerializer)
MAKE_CTOR(CreatePlainTextSerializer, nsIContentSerializer, NS_NewPlainTextSerializer) MAKE_CTOR(CreatePlainTextSerializer, nsIContentSerializer, NS_NewPlainTextSerializer)
MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer, NS_NewSanitizingHTMLSerializer)
MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService) MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService)
MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy) MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy)
#ifdef MOZ_XUL #ifdef MOZ_XUL
@ -711,7 +709,6 @@ NS_DEFINE_NAMED_CID(NS_XMLCONTENTSERIALIZER_CID);
NS_DEFINE_NAMED_CID(NS_XHTMLCONTENTSERIALIZER_CID); NS_DEFINE_NAMED_CID(NS_XHTMLCONTENTSERIALIZER_CID);
NS_DEFINE_NAMED_CID(NS_HTMLCONTENTSERIALIZER_CID); NS_DEFINE_NAMED_CID(NS_HTMLCONTENTSERIALIZER_CID);
NS_DEFINE_NAMED_CID(NS_PLAINTEXTSERIALIZER_CID); NS_DEFINE_NAMED_CID(NS_PLAINTEXTSERIALIZER_CID);
NS_DEFINE_NAMED_CID(MOZ_SANITIZINGHTMLSERIALIZER_CID);
NS_DEFINE_NAMED_CID(NS_PARSERUTILS_CID); NS_DEFINE_NAMED_CID(NS_PARSERUTILS_CID);
NS_DEFINE_NAMED_CID(NS_SCRIPTABLEUNESCAPEHTML_CID); NS_DEFINE_NAMED_CID(NS_SCRIPTABLEUNESCAPEHTML_CID);
NS_DEFINE_NAMED_CID(NS_XBLSERVICE_CID); NS_DEFINE_NAMED_CID(NS_XBLSERVICE_CID);
@ -987,7 +984,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kNS_HTMLCONTENTSERIALIZER_CID, false, NULL, CreateHTMLContentSerializer }, { &kNS_HTMLCONTENTSERIALIZER_CID, false, NULL, CreateHTMLContentSerializer },
{ &kNS_XHTMLCONTENTSERIALIZER_CID, false, NULL, CreateXHTMLContentSerializer }, { &kNS_XHTMLCONTENTSERIALIZER_CID, false, NULL, CreateXHTMLContentSerializer },
{ &kNS_PLAINTEXTSERIALIZER_CID, false, NULL, CreatePlainTextSerializer }, { &kNS_PLAINTEXTSERIALIZER_CID, false, NULL, CreatePlainTextSerializer },
{ &kMOZ_SANITIZINGHTMLSERIALIZER_CID, false, NULL, CreateSanitizingHTMLSerializer },
{ &kNS_PARSERUTILS_CID, false, NULL, nsParserUtilsConstructor }, { &kNS_PARSERUTILS_CID, false, NULL, nsParserUtilsConstructor },
{ &kNS_SCRIPTABLEUNESCAPEHTML_CID, false, NULL, nsParserUtilsConstructor }, { &kNS_SCRIPTABLEUNESCAPEHTML_CID, false, NULL, nsParserUtilsConstructor },
{ &kNS_XBLSERVICE_CID, false, NULL, CreateXBLService }, { &kNS_XBLSERVICE_CID, false, NULL, CreateXBLService },
@ -1135,7 +1131,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/html", &kNS_HTMLCONTENTSERIALIZER_CID }, { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/html", &kNS_HTMLCONTENTSERIALIZER_CID },
{ NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "application/vnd.mozilla.xul+xml", &kNS_XMLCONTENTSERIALIZER_CID }, { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "application/vnd.mozilla.xul+xml", &kNS_XMLCONTENTSERIALIZER_CID },
{ NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/plain", &kNS_PLAINTEXTSERIALIZER_CID }, { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/plain", &kNS_PLAINTEXTSERIALIZER_CID },
{ MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID, &kMOZ_SANITIZINGHTMLSERIALIZER_CID },
{ NS_PARSERUTILS_CONTRACTID, &kNS_PARSERUTILS_CID }, { NS_PARSERUTILS_CONTRACTID, &kNS_PARSERUTILS_CID },
{ NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID, &kNS_SCRIPTABLEUNESCAPEHTML_CID }, { NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID, &kNS_SCRIPTABLEUNESCAPEHTML_CID },
{ "@mozilla.org/xbl;1", &kNS_XBLSERVICE_CID }, { "@mozilla.org/xbl;1", &kNS_XBLSERVICE_CID },

View File

@ -64,8 +64,9 @@ import android.widget.TextView;
import android.widget.TextSwitcher; import android.widget.TextSwitcher;
import android.widget.ViewSwitcher.ViewFactory; import android.widget.ViewSwitcher.ViewFactory;
public class BrowserToolbar extends LinearLayout { public class BrowserToolbar {
private static final String LOGTAG = "GeckoToolbar"; private static final String LOGTAG = "GeckoToolbar";
private LinearLayout mLayout;
private Button mAwesomeBar; private Button mAwesomeBar;
private ImageButton mTabs; private ImageButton mTabs;
public ImageButton mFavicon; public ImageButton mFavicon;
@ -90,16 +91,19 @@ public class BrowserToolbar extends LinearLayout {
private int mCount; private int mCount;
public BrowserToolbar(Context context, AttributeSet attrs) { public BrowserToolbar(Context context) {
super(context, attrs);
mContext = context; mContext = context;
}
public void from(LinearLayout layout) {
mLayout = layout;
mTitleCanExpand = true; mTitleCanExpand = true;
// Get the device's highlight color // Get the device's highlight color
TypedArray typedArray; TypedArray typedArray;
if (Build.VERSION.SDK_INT >= 11) { if (Build.VERSION.SDK_INT >= 11) {
typedArray = context.obtainStyledAttributes(new int[] { android.R.attr.textColorHighlight }); typedArray = mContext.obtainStyledAttributes(new int[] { android.R.attr.textColorHighlight });
} else { } else {
ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, android.R.style.TextAppearance); ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, android.R.style.TextAppearance);
typedArray = wrapper.getTheme().obtainStyledAttributes(new int[] { android.R.attr.textColorHighlight }); typedArray = wrapper.getTheme().obtainStyledAttributes(new int[] { android.R.attr.textColorHighlight });
@ -107,17 +111,14 @@ public class BrowserToolbar extends LinearLayout {
mColor = typedArray.getColor(typedArray.getIndex(0), 0); mColor = typedArray.getColor(typedArray.getIndex(0), 0);
typedArray.recycle(); typedArray.recycle();
} mAwesomeBar = (Button) mLayout.findViewById(R.id.awesome_bar);
public void init() {
mAwesomeBar = (Button) findViewById(R.id.awesome_bar);
mAwesomeBar.setOnClickListener(new Button.OnClickListener() { mAwesomeBar.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
onAwesomeBarSearch(); onAwesomeBarSearch();
} }
}); });
Resources resources = getResources(); Resources resources = mContext.getResources();
mPadding = new int[] { mAwesomeBar.getPaddingLeft(), mPadding = new int[] { mAwesomeBar.getPaddingLeft(),
mAwesomeBar.getPaddingTop(), mAwesomeBar.getPaddingTop(),
@ -132,7 +133,7 @@ public class BrowserToolbar extends LinearLayout {
mAwesomeBar.setPadding(mPadding[0], mPadding[1], mPadding[2], mPadding[3]); mAwesomeBar.setPadding(mPadding[0], mPadding[1], mPadding[2], mPadding[3]);
mTabs = (ImageButton) findViewById(R.id.tabs); mTabs = (ImageButton) mLayout.findViewById(R.id.tabs);
mTabs.setOnClickListener(new Button.OnClickListener() { mTabs.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
if (Tabs.getInstance().getCount() > 1) if (Tabs.getInstance().getCount() > 1)
@ -145,7 +146,7 @@ public class BrowserToolbar extends LinearLayout {
mCounterColor = 0xFFC7D1DB; mCounterColor = 0xFFC7D1DB;
mTabsCount = (TextSwitcher) findViewById(R.id.tabs_count); mTabsCount = (TextSwitcher) mLayout.findViewById(R.id.tabs_count);
mTabsCount.removeAllViews(); mTabsCount.removeAllViews();
mTabsCount.setFactory(new ViewFactory() { mTabsCount.setFactory(new ViewFactory() {
public View makeView() { public View makeView() {
@ -169,18 +170,18 @@ public class BrowserToolbar extends LinearLayout {
mTabsCount.setText("0"); mTabsCount.setText("0");
mCount = 0; mCount = 0;
mFavicon = (ImageButton) findViewById(R.id.favicon); mFavicon = (ImageButton) mLayout.findViewById(R.id.favicon);
mSiteSecurity = (ImageButton) findViewById(R.id.site_security); mSiteSecurity = (ImageButton) mLayout.findViewById(R.id.site_security);
mProgressSpinner = (AnimationDrawable) resources.getDrawable(R.drawable.progress_spinner); mProgressSpinner = (AnimationDrawable) resources.getDrawable(R.drawable.progress_spinner);
mStop = (ImageButton) findViewById(R.id.stop); mStop = (ImageButton) mLayout.findViewById(R.id.stop);
mStop.setOnClickListener(new Button.OnClickListener() { mStop.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
doStop(); doStop();
} }
}); });
mShadow = (ImageView) findViewById(R.id.shadow); mShadow = (ImageView) mLayout.findViewById(R.id.shadow);
mHandler = new Handler(); mHandler = new Handler();
mSlideUpIn = new TranslateAnimation(0, 0, 40, 0); mSlideUpIn = new TranslateAnimation(0, 0, 40, 0);
@ -329,18 +330,26 @@ public class BrowserToolbar extends LinearLayout {
} }
} }
public void setVisibility(int visibility) {
mLayout.setVisibility(visibility);
}
public void requestFocusFromTouch() {
mLayout.requestFocusFromTouch();
}
public void show() { public void show() {
if (Build.VERSION.SDK_INT >= 11) if (Build.VERSION.SDK_INT >= 11)
GeckoActionBar.show(GeckoApp.mAppContext); GeckoActionBar.show(GeckoApp.mAppContext);
else else
setVisibility(View.VISIBLE); mLayout.setVisibility(View.VISIBLE);
} }
public void hide() { public void hide() {
if (Build.VERSION.SDK_INT >= 11) if (Build.VERSION.SDK_INT >= 11)
GeckoActionBar.hide(GeckoApp.mAppContext); GeckoActionBar.hide(GeckoApp.mAppContext);
else else
setVisibility(View.GONE); mLayout.setVisibility(View.GONE);
} }
public void refresh() { public void refresh() {

View File

@ -1592,15 +1592,11 @@ abstract public class GeckoApp
// The ActionBar needs to be refreshed on rotation as different orientation uses different resources // The ActionBar needs to be refreshed on rotation as different orientation uses different resources
public void refreshActionBar() { public void refreshActionBar() {
if (Build.VERSION.SDK_INT >= 11) { if (Build.VERSION.SDK_INT >= 11) {
mBrowserToolbar = (BrowserToolbar) getLayoutInflater().inflate(R.layout.browser_toolbar, null); LinearLayout actionBar = (LinearLayout) getLayoutInflater().inflate(R.layout.browser_toolbar, null);
mBrowserToolbar.init(); mBrowserToolbar.from(actionBar);
mBrowserToolbar.refresh(); mBrowserToolbar.refresh();
GeckoActionBar.setBackgroundDrawable(this, getResources().getDrawable(R.drawable.gecko_actionbar_bg)); GeckoActionBar.setBackgroundDrawable(this, getResources().getDrawable(R.drawable.gecko_actionbar_bg));
GeckoActionBar.setDisplayOptions(this, ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM | GeckoActionBar.setCustomView(this, actionBar);
ActionBar.DISPLAY_SHOW_HOME |
ActionBar.DISPLAY_SHOW_TITLE |
ActionBar.DISPLAY_USE_LOGO);
GeckoActionBar.setCustomView(this, mBrowserToolbar);
} }
} }
@ -1633,12 +1629,16 @@ abstract public class GeckoApp
setContentView(R.layout.gecko_app); setContentView(R.layout.gecko_app);
LinearLayout actionBar;
if (Build.VERSION.SDK_INT >= 11) { if (Build.VERSION.SDK_INT >= 11) {
mBrowserToolbar = (BrowserToolbar) GeckoActionBar.getCustomView(this); actionBar = (LinearLayout) GeckoActionBar.getCustomView(this);
} else { } else {
mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar); actionBar = (LinearLayout) findViewById(R.id.browser_toolbar);
} }
mBrowserToolbar = new BrowserToolbar(mAppContext);
mBrowserToolbar.from(actionBar);
// setup gecko layout // setup gecko layout
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (LinearLayout) findViewById(R.id.main_layout); mMainLayout = (LinearLayout) findViewById(R.id.main_layout);
@ -1670,7 +1670,6 @@ abstract public class GeckoApp
checkAndLaunchUpdate(); checkAndLaunchUpdate();
} }
mBrowserToolbar.init();
mBrowserToolbar.setTitle(mLastTitle); mBrowserToolbar.setTitle(mLastTitle);
String passedUri = null; String passedUri = null;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.mozilla.gecko.BrowserToolbar xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/browser_toolbar" android:id="@+id/browser_toolbar"
style="@style/BrowserToolbar"> style="@style/BrowserToolbar">
<RelativeLayout android:id="@+id/address_bar" <RelativeLayout android:id="@+id/address_bar"
style="@style/AddressBar" style="@style/AddressBar"
@ -81,4 +81,4 @@
</RelativeLayout> </RelativeLayout>
</org.mozilla.gecko.BrowserToolbar> </LinearLayout>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.mozilla.gecko.BrowserToolbar xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/browser_toolbar" android:id="@+id/browser_toolbar"
style="@style/BrowserToolbar"> style="@style/BrowserToolbar">
<RelativeLayout android:id="@+id/address_bar" <RelativeLayout android:id="@+id/address_bar"
style="@style/AddressBar" style="@style/AddressBar"
@ -82,4 +82,4 @@
</RelativeLayout> </RelativeLayout>
</org.mozilla.gecko.BrowserToolbar> </LinearLayout>

View File

@ -29,6 +29,7 @@
</style> </style>
<style name="Gecko.App"> <style name="Gecko.App">
<item name="android:windowBackground">@drawable/abouthome_bg_repeat</item>
<item name="android:actionBarStyle">@style/ActionBar.GeckoApp</item> <item name="android:actionBarStyle">@style/ActionBar.GeckoApp</item>
</style> </style>

View File

@ -27,7 +27,9 @@
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
</style> </style>
<style name="Gecko.App"/> <style name="Gecko.App">
<item name="android:windowBackground">@drawable/abouthome_bg_repeat</item>
</style>
<style name="Gecko.Light.AwesomeBar"/> <style name="Gecko.Light.AwesomeBar"/>

View File

@ -245,12 +245,12 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
bool aLastCall, bool aLastCall,
nsDTDMode aMode) // ignored nsDTDMode aMode) // ignored
{ {
if (mExecutor->IsBroken()) { nsresult rv;
return NS_ERROR_OUT_OF_MEMORY; if (NS_FAILED(rv = mExecutor->IsBroken())) {
return rv;
} }
if (aSourceBuffer.Length() > PR_INT32_MAX) { if (aSourceBuffer.Length() > PR_INT32_MAX) {
mExecutor->MarkAsBroken(); return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
} }
// Maintain a reference to ourselves so we don't go away // Maintain a reference to ourselves so we don't go away
@ -446,8 +446,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer(); heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
if (!heapBuffer) { if (!heapBuffer) {
// Allocation failed. The parser is now broken. // Allocation failed. The parser is now broken.
mExecutor->MarkAsBroken(); return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
} }
} }

View File

@ -947,10 +947,10 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
nsHtml5OwningUTF16Buffer::FalliblyCreate( nsHtml5OwningUTF16Buffer::FalliblyCreate(
NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE); NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
if (!newBuf) { if (!newBuf) {
mExecutor->MarkAsBroken(); // marks this stream parser as terminated, // marks this stream parser as terminated,
// which prevents entry to code paths that // which prevents entry to code paths that
// would use mFirstBuffer or mLastBuffer. // would use mFirstBuffer or mLastBuffer.
return NS_ERROR_OUT_OF_MEMORY; return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
} }
NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?"); NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?");
NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?"); NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?");
@ -1143,8 +1143,9 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
PRUint32 aSourceOffset, PRUint32 aSourceOffset,
PRUint32 aLength) PRUint32 aLength)
{ {
if (mExecutor->IsBroken()) { nsresult rv;
return NS_ERROR_OUT_OF_MEMORY; if (NS_FAILED(rv = mExecutor->IsBroken())) {
return rv;
} }
NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream."); NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
@ -1152,10 +1153,9 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
const mozilla::fallible_t fallible = mozilla::fallible_t(); const mozilla::fallible_t fallible = mozilla::fallible_t();
nsAutoArrayPtr<PRUint8> data(new (fallible) PRUint8[aLength]); nsAutoArrayPtr<PRUint8> data(new (fallible) PRUint8[aLength]);
if (!data) { if (!data) {
mExecutor->MarkAsBroken(); return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
} }
nsresult rv = aInStream->Read(reinterpret_cast<char*>(data.get()), rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
aLength, &totalRead); aLength, &totalRead);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?"); NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");

View File

@ -117,6 +117,20 @@ nsHtml5TreeOpExecutor::WillParse()
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
NS_IMETHODIMP
nsHtml5TreeOpExecutor::WillBuildModel(nsDTDMode aDTDMode)
{
if (mDocShell && !GetDocument()->GetScriptGlobalObject()) {
// Not loading as data but script global object not ready
return MarkAsBroken(NS_ERROR_DOM_INVALID_STATE_ERR);
}
mDocument->AddObserver(this);
WillBuildModelImpl();
GetDocument()->BeginLoad();
return NS_OK;
}
// This is called when the tree construction has ended // This is called when the tree construction has ended
NS_IMETHODIMP NS_IMETHODIMP
nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated) nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
@ -143,7 +157,9 @@ nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
GetParser()->DropStreamParser(); GetParser()->DropStreamParser();
// This comes from nsXMLContentSink and nsHTMLContentSink // This comes from nsXMLContentSink and nsHTMLContentSink
DidBuildModelImpl(aTerminated); // If this parser has been marked as broken, treat the end of parse as
// forced termination.
DidBuildModelImpl(aTerminated || IsBroken());
if (!mLayoutStarted) { if (!mLayoutStarted) {
// We never saw the body, and layout never got started. Force // We never saw the body, and layout never got started. Force
@ -274,12 +290,12 @@ nsHtml5TreeOpExecutor::UpdateChildCounts()
// No-op // No-op
} }
void nsresult
nsHtml5TreeOpExecutor::MarkAsBroken() nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason)
{ {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mRunsToCompletion, "Fragment parsers can't be broken!"); NS_ASSERTION(!mRunsToCompletion, "Fragment parsers can't be broken!");
mBroken = true; mBroken = aReason;
if (mStreamParser) { if (mStreamParser) {
mStreamParser->Terminate(); mStreamParser->Terminate();
} }
@ -293,6 +309,7 @@ nsHtml5TreeOpExecutor::MarkAsBroken()
NS_WARNING("failed to dispatch executor flush event"); NS_WARNING("failed to dispatch executor flush event");
} }
} }
return aReason;
} }
nsresult nsresult

View File

@ -129,14 +129,17 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
bool mCallContinueInterruptedParsingIfEnabled; bool mCallContinueInterruptedParsingIfEnabled;
/** /**
* True if this parser should refuse to process any more input. * Non-NS_OK if this parser should refuse to process any more input.
* Currently, the only way a parser can break is if it drops some input * For example, the parser needs to be marked as broken if it drops some
* due to a memory allocation failure. In such a case, the whole parser * input due to a memory allocation failure. In such a case, the whole
* needs to be marked as broken, because some input has been lost and * parser needs to be marked as broken, because some input has been lost
* parsing more input could lead to a DOM where pieces of HTML source * and parsing more input could lead to a DOM where pieces of HTML source
* that weren't supposed to become scripts become scripts. * that weren't supposed to become scripts become scripts.
*
* Since NS_OK is actually 0, zeroing operator new takes care of
* initializing this.
*/ */
bool mBroken; nsresult mBroken;
public: public:
@ -153,14 +156,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
/** /**
* *
*/ */
NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) { NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
"Script global object not ready");
mDocument->AddObserver(this);
WillBuildModelImpl();
GetDocument()->BeginLoad();
return NS_OK;
}
/** /**
* Emits EOF. * Emits EOF.
@ -263,13 +259,16 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
/** /**
* Marks this parser as broken and tells the stream parser (if any) to * Marks this parser as broken and tells the stream parser (if any) to
* terminate. * terminate.
*
* @return aReason for convenience
*/ */
void MarkAsBroken(); nsresult MarkAsBroken(nsresult aReason);
/** /**
* Checks if this parser is broken. * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0)
* value if broken.
*/ */
inline bool IsBroken() { inline nsresult IsBroken() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return mBroken; return mBroken;
} }

View File

@ -588,7 +588,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
return AppendToDocument(asContent, aBuilder); return AppendToDocument(asContent, aBuilder);
} }
case eTreeOpMarkAsBroken: { case eTreeOpMarkAsBroken: {
aBuilder->MarkAsBroken(); aBuilder->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
return rv; return rv;
} }
case eTreeOpRunScript: { case eTreeOpRunScript: {

View File

@ -4,12 +4,16 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsIDOMElement;
interface nsIDOMDocumentFragment;
interface nsIURI;
/** /**
* Non-Web HTML parser functionality to Firefox extensions and XULRunner apps. * Non-Web HTML parser functionality to Firefox extensions and XULRunner apps.
* Don't use this from within Gecko--use nsContentUtils, nsTreeSanitizer, etc. * Don't use this from within Gecko--use nsContentUtils, nsTreeSanitizer, etc.
* directly instead. * directly instead.
*/ */
[scriptable, uuid(290f49bb-0619-4bda-8006-ab31bec7231a)] [scriptable, uuid(a1101145-0025-411e-8873-fdf57bf28128)]
interface nsIParserUtils : nsISupports interface nsIParserUtils : nsISupports
{ {
@ -100,6 +104,22 @@ interface nsIParserUtils : nsISupports
AString convertToPlainText(in AString src, AString convertToPlainText(in AString src,
in unsigned long flags, in unsigned long flags,
in unsigned long wrapCol); in unsigned long wrapCol);
/**
* Parses markup into a sanitized document fragment.
*
* @param fragment the input markup
* @param flags sanitization option flags defined above
* @param isXML true if |fragment| is XML and false if HTML
* @param baseURI the base URL for this fragment
* @param element the context node for the fragment parsing algorithm
*/
nsIDOMDocumentFragment parseFragment(in AString fragment,
in unsigned long flags,
in boolean isXML,
in nsIURI baseURI,
in nsIDOMElement element);
}; };
%{ C++ %{ C++

View File

@ -41,7 +41,7 @@ interface nsIDOMDocumentFragment;
interface nsIURI; interface nsIURI;
/** /**
* A utility class for HTML parsing in the feed processor. * This interface is OBSOLETE and exists solely for legacy extensions.
*/ */
[scriptable, uuid(3ab244a9-f09d-44da-9e3f-ee4d67367f2d)] [scriptable, uuid(3ab244a9-f09d-44da-9e3f-ee4d67367f2d)]
interface nsIScriptableUnescapeHTML : nsISupports interface nsIScriptableUnescapeHTML : nsISupports
@ -52,16 +52,20 @@ interface nsIScriptableUnescapeHTML : nsISupports
* nsIDocumentEncoder::OutputSelectionOnly | * nsIDocumentEncoder::OutputSelectionOnly |
* nsIDocumentEncoder::OutputAbsoluteLinks, 0). * nsIDocumentEncoder::OutputAbsoluteLinks, 0).
* *
* You should most likely call nsIParserUtils::convertToPlainText() * You should call nsIParserUtils::convertToPlainText() instead of calling
* instead of calling this method. * this method.
* *
* @param src The HTML string to convert to plain text. * @param src The HTML string to convert to plain text.
*/ */
AString unescape(in AString src); AString unescape(in AString src);
/** /**
* Parses markup into a sanitized document fragment. * Parses markup into a sanitized document fragment. This is equivalent to
* calling nsIParserUtils::parseFragment(fragment, 0, isXML, baseURI,
* element).
* *
* You should call nsIParserUtils::parseFragment() instead of calling this
* method.
* @param fragment the input markup * @param fragment the input markup
* @param isXML true if |fragment| is XML and false if HTML * @param isXML true if |fragment| is XML and false if HTML
* @param baseURI the base URL for this fragment * @param baseURI the base URL for this fragment

View File

@ -79,9 +79,9 @@ static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
NS_IMETHODIMP NS_IMETHODIMP
nsParserUtils::ConvertToPlainText(const nsAString& aFromStr, nsParserUtils::ConvertToPlainText(const nsAString& aFromStr,
PRUint32 aFlags, PRUint32 aFlags,
PRUint32 aWrapCol, PRUint32 aWrapCol,
nsAString& aToStr) nsAString& aToStr)
{ {
return nsContentUtils::ConvertToPlainText(aFromStr, return nsContentUtils::ConvertToPlainText(aFromStr,
aToStr, aToStr,
@ -142,15 +142,28 @@ nsParserUtils::Sanitize(const nsAString& aFromStr,
return encoder->EncodeToString(aToStr); return encoder->EncodeToString(aToStr);
} }
// The feed version of nsContentUtils::CreateContextualFragment It
// creates a fragment, but doesn't go to all the effort to preserve
// context like innerHTML does, because feed DOMs shouldn't have that.
NS_IMETHODIMP NS_IMETHODIMP
nsParserUtils::ParseFragment(const nsAString& aFragment, nsParserUtils::ParseFragment(const nsAString& aFragment,
bool aIsXML, bool aIsXML,
nsIURI* aBaseURI, nsIURI* aBaseURI,
nsIDOMElement* aContextElement, nsIDOMElement* aContextElement,
nsIDOMDocumentFragment** aReturn) nsIDOMDocumentFragment** aReturn)
{
return nsParserUtils::ParseFragment(aFragment,
0,
aIsXML,
aBaseURI,
aContextElement,
aReturn);
}
NS_IMETHODIMP
nsParserUtils::ParseFragment(const nsAString& aFragment,
PRUint32 aFlags,
bool aIsXML,
nsIURI* aBaseURI,
nsIDOMElement* aContextElement,
nsIDOMDocumentFragment** aReturn)
{ {
NS_ENSURE_ARG(aContextElement); NS_ENSURE_ARG(aContextElement);
*aReturn = nsnull; *aReturn = nsnull;
@ -239,7 +252,7 @@ nsParserUtils::ParseFragment(const nsAString& aFragment,
} }
} }
if (fragment) { if (fragment) {
nsTreeSanitizer sanitizer; nsTreeSanitizer sanitizer(aFlags);
sanitizer.Sanitize(fragment); sanitizer.Sanitize(fragment);
} }
} }

View File

@ -41,7 +41,7 @@
#include "nsIParserUtils.h" #include "nsIParserUtils.h"
class nsParserUtils : public nsIScriptableUnescapeHTML, class nsParserUtils : public nsIScriptableUnescapeHTML,
public nsIParserUtils public nsIParserUtils
{ {
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS

View File

@ -149,12 +149,11 @@ var qaTools = {
return newArray; return newArray;
}, },
writeSafeHTML : function(elementID, htmlstr) { writeSafeHTML : function(elementID, htmlstr) {
document.getElementById(elementID).innerHTML = ""; //clear it. document.getElementById(elementID).textContent = ""; //clear it.
var gUnescapeHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"].getService(Components.interfaces.nsIScriptableUnescapeHTML); var utils = Components.classes["@mozilla.org/parserutils;1"].getService(Components.interfaces.nsIParserUtils);
var context = document.getElementById(elementID); var context = document.getElementById(elementID);
var fragment = gUnescapeHTML.parseFragment(htmlstr, false, null, context); var fragment = utils.parseFragment(htmlstr, 0, false, null, context);
context.appendChild(fragment); context.appendChild(fragment);
}, },
assignLinkHandlers : function(node) { assignLinkHandlers : function(node) {

View File

@ -75,7 +75,7 @@ const IO_CONTRACTID = "@mozilla.org/network/io-service;1"
const BAG_CONTRACTID = "@mozilla.org/hash-property-bag;1" const BAG_CONTRACTID = "@mozilla.org/hash-property-bag;1"
const ARRAY_CONTRACTID = "@mozilla.org/array;1"; const ARRAY_CONTRACTID = "@mozilla.org/array;1";
const SAX_CONTRACTID = "@mozilla.org/saxparser/xmlreader;1"; const SAX_CONTRACTID = "@mozilla.org/saxparser/xmlreader;1";
const UNESCAPE_CONTRACTID = "@mozilla.org/feed-unescapehtml;1"; const PARSERUTILS_CONTRACTID = "@mozilla.org/parserutils;1";
var gIoService = null; var gIoService = null;
@ -644,14 +644,16 @@ function TextConstruct() {
this.base = null; this.base = null;
this.type = "text"; this.type = "text";
this.text = null; this.text = null;
this.unescapeHTML = Cc[UNESCAPE_CONTRACTID]. this.parserUtils = Cc[PARSERUTILS_CONTRACTID].getService(Ci.nsIParserUtils);
getService(Ci.nsIScriptableUnescapeHTML);
} }
TextConstruct.prototype = { TextConstruct.prototype = {
plainText: function TC_plainText() { plainText: function TC_plainText() {
if (this.type != "text") { if (this.type != "text") {
return this.unescapeHTML.unescape(stripTags(this.text)); return this.parserUtils.convertToPlainText(stripTags(this.text),
Ci.nsIDocumentEncoder.OutputSelectionOnly |
Ci.nsIDocumentEncoder.OutputAbsoluteLinks,
0);
} }
return this.text; return this.text;
}, },
@ -672,8 +674,8 @@ TextConstruct.prototype = {
else else
return null; return null;
return this.unescapeHTML.parseFragment(this.text, isXML, return this.parserUtils.parseFragment(this.text, 0, isXML,
this.base, element); this.base, element);
}, },
// XPCOM stuff // XPCOM stuff

View File

@ -22,9 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=675492
/** Test for Bug 675492 **/ /** Test for Bug 675492 **/
Components Components
.classes["@mozilla.org/feed-unescapehtml;1"] .classes["@mozilla.org/parserutils;1"]
.getService(Components.interfaces.nsIScriptableUnescapeHTML) .getService(Components.interfaces.nsIParserUtils)
.parseFragment("<p>test</p>", false, null, document.createElementNS("http://www.w3.org/1999/xhtml", "body")); .parseFragment("<p>test</p>", 0, false, null, document.createElementNS("http://www.w3.org/1999/xhtml", "body"));
ok(true, "No crash!"); ok(true, "No crash!");
]]> ]]>
</script> </script>