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 \
nsIContentSerializer.h \
nsIXPathEvaluatorInternal.h \
mozISanitizingSerializer.h \
nsCaseTreatment.h \
nsContentCID.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
CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \
nsAttrAndChildArray.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
{
public:
JSScript *script;
jsbytecode *pc;
/* Type set receiving the result of the arithmetic. */
TypeSet *target;
/* For addition operations, the other operand. */
TypeSet *other;
TypeConstraintArith(TypeSet *target, TypeSet *other)
: TypeConstraint("arith"), target(target), other(other)
TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
: TypeConstraint("arith"), script(script), pc(pc), target(target), other(other)
{
JS_ASSERT(target);
}
@ -694,9 +697,9 @@ public:
};
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. */
@ -1299,27 +1302,29 @@ TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
} else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN |
TYPE_FLAG_ANYOBJECT) ||
other->getObjectCount() != 0) {
TYPE_FLAG_ANYOBJECT)) {
target->addType(cx, Type::DoubleType());
} else if (other->getObjectCount() != 0) {
TypeDynamicResult(cx, script, pc, Type::DoubleType());
}
} else if (type.isPrimitive(JSVAL_TYPE_STRING)) {
target->addType(cx, Type::StringType());
} else {
if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN |
TYPE_FLAG_ANYOBJECT) ||
other->getObjectCount() != 0) {
target->addType(cx, Type::Int32Type());
}
if (other->hasAnyFlag(TYPE_FLAG_DOUBLE))
target->addType(cx, Type::DoubleType());
} else if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) {
target->addType(cx, Type::DoubleType());
} else if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN |
TYPE_FLAG_ANYOBJECT)) {
target->addType(cx, Type::Int32Type());
} else if (other->getObjectCount() != 0) {
TypeDynamicResult(cx, script, pc, Type::Int32Type());
}
} else {
if (type.isUnknown())
target->addType(cx, Type::UnknownType());
else if (type.isPrimitive(JSVAL_TYPE_DOUBLE))
target->addType(cx, Type::DoubleType());
else if (!type.isAnyObject() && type.isObject())
TypeDynamicResult(cx, script, pc, Type::Int32Type());
else
target->addType(cx, Type::Int32Type());
}
@ -3616,10 +3621,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_LOCALDEC: {
uint32_t slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot)) {
poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
} else if (slot < TotalSlots(script)) {
TypeSet *types = TypeScript::SlotTypes(script, slot);
types->addArith(cx, types);
types->addArith(cx, script, pc, types);
types->addSubset(cx, &pushed[0]);
} else {
pushed[0].addType(cx, Type::UnknownType());
@ -3702,21 +3707,21 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
break;
case JSOP_ADD:
poppedTypes(pc, 0)->addArith(cx, &pushed[0], poppedTypes(pc, 1));
poppedTypes(pc, 1)->addArith(cx, &pushed[0], poppedTypes(pc, 0));
poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 1));
poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 0));
break;
case JSOP_SUB:
case JSOP_MUL:
case JSOP_MOD:
case JSOP_DIV:
poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
poppedTypes(pc, 1)->addArith(cx, &pushed[0]);
poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0]);
break;
case JSOP_NEG:
case JSOP_POS:
poppedTypes(pc, 0)->addArith(cx, &pushed[0]);
poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
break;
case JSOP_LAMBDA:

View File

@ -441,7 +441,8 @@ class TypeSet
void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
TypeSet *objectTypes, TypeSet *valueTypes);
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 addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
Type type, TypeSet *types = NULL);

View File

@ -6247,7 +6247,6 @@ dumpValue(const Value &v)
#ifdef DEBUG
switch (v.whyMagic()) {
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_NO_ITER_VALUE: fprintf(stderr, " no iter value"); 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 };
/*****************************************************************************/
/* 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 */
#endif /* __cplusplus */

View File

@ -275,7 +275,6 @@ typedef uint64_t JSValueShiftedTag;
typedef enum JSWhyMagic
{
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
* to JS_EnumerateState, which really means the object can be
* enumerated like a native object. */
@ -289,6 +288,7 @@ typedef enum JSWhyMagic
JS_UNASSIGNED_ARGUMENTS, /* the initial value of callobj.arguments */
JS_OPTIMIZED_ARGUMENTS, /* optimized-away 'arguments' value */
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 */
} JSWhyMagic;

View File

@ -864,47 +864,6 @@ class GetPropCompiler : public PICStubCompiler
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()
{
Assembler masm;
@ -1883,21 +1842,14 @@ GetPropMaybeCached(VMFrame &f, ic::PICInfo *pic, bool cached)
}
if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() ||
(obj->isArguments() && !obj->asArguments().hasOverriddenLength()) ||
obj->isString()) {
if (obj->isArray() || obj->isString()) {
GetPropCompiler cc(f, script, obj, *pic, NULL, stub);
if (obj->isArray()) {
LookupStatus status = cc.generateArrayLengthStub();
if (status == Lookup_Error)
THROW();
f.regs.sp[-1].setNumber(obj->getArrayLength());
} else if (obj->isArguments()) {
LookupStatus status = cc.generateArgsLengthStub();
if (status == Lookup_Error)
THROW();
f.regs.sp[-1].setInt32(int32_t(obj->asArguments().initialLength()));
} else if (obj->isString()) {
} else {
LookupStatus status = cc.generateStringObjLengthStub();
if (status == Lookup_Error)
THROW();
@ -2368,158 +2320,6 @@ GetElementIC::attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyN
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
LookupStatus
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))
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
/*
* 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 attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name,
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 disable(VMFrame &f, const char *reason);
LookupStatus error(JSContext *cx);

View File

@ -89,39 +89,42 @@ ArgumentsObject::data() const
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 &
ArgumentsObject::element(uint32_t i) const
{
JS_ASSERT(i < initialLength());
JS_ASSERT(!isElementDeleted(i));
return data()->slots[i];
}
inline const js::Value *
ArgumentsObject::elements() const
{
return Valueify(data()->slots);
}
inline void
ArgumentsObject::setElement(uint32_t i, const js::Value &v)
{
JS_ASSERT(i < initialLength());
JS_ASSERT(!isElementDeleted(i));
data()->slots[i] = v;
}
inline bool
ArgumentsObject::getElement(uint32_t i, Value *vp)
{
if (i >= initialLength())
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))
if (i >= initialLength() || isElementDeleted(i))
return false;
/*
@ -133,6 +136,8 @@ ArgumentsObject::getElement(uint32_t i, Value *vp)
JS_ASSERT_IF(isStrictArguments(), !fp);
if (fp)
*vp = fp->canonicalActualArg(i);
else
*vp = element(i);
return true;
}
@ -144,8 +149,6 @@ struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
ArgumentsObject &argsobj;
Value *dst;
bool operator()(uint32_t argi, Value *src) {
if (argsobj.element(argi).isMagic(JS_ARGS_HOLE))
return false;
*dst++ = *src;
return true;
}
@ -159,21 +162,18 @@ ArgumentsObject::getElements(uint32_t start, uint32_t count, Value *vp)
JS_ASSERT(start + count >= start);
uint32_t length = initialLength();
if (start > length || start + count > length)
if (start > length || start + count > length || isAnyElementDeleted())
return false;
StackFrame *fp = maybeStackFrame();
/* If there's no stack frame for this, argument values are in elements(). */
if (!fp) {
const Value *srcbeg = elements() + start;
const Value *srcbeg = Valueify(data()->slots) + start;
const Value *srcend = srcbeg + count;
const Value *src = srcbeg;
for (Value *dst = vp; src < srcend; ++dst, ++src) {
if (src->isMagic(JS_ARGS_HOLE))
return false;
for (Value *dst = vp; src < srcend; ++dst, ++src)
*dst = *src;
}
return true;
}
@ -209,7 +209,7 @@ NormalArgumentsObject::callee() const
inline void
NormalArgumentsObject::clearCallee()
{
data()->callee.set(compartment(), MagicValue(JS_ARGS_HOLE));
data()->callee.set(compartment(), MagicValue(JS_OVERWRITTEN_CALLEE));
}
} // namespace js

View File

@ -56,12 +56,14 @@ using namespace js::gc;
struct PutArg
{
PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {}
HeapValue *dst;
PutArg(JSCompartment *comp, ArgumentsObject &argsobj)
: compartment(comp), argsobj(argsobj), dst(argsobj.data()->slots) {}
JSCompartment *compartment;
bool operator()(unsigned, Value *src) {
JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined());
if (!dst->isMagic(JS_ARGS_HOLE))
ArgumentsObject &argsobj;
HeapValue *dst;
bool operator()(unsigned i, Value *src) {
JS_ASSERT(dst->isUndefined());
if (!argsobj.isElementDeleted(i))
dst->set(compartment, *src);
++dst;
return true;
@ -75,7 +77,7 @@ js_PutArgsObject(StackFrame *fp)
if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.maybeStackFrame() == fp);
JSCompartment *comp = fp->scopeChain().compartment();
fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots));
fp->forEachCanonicalActualArg(PutArg(comp, argsobj));
argsobj.setStackFrame(NULL);
} else {
JS_ASSERT(!argsobj.maybeStackFrame());
@ -108,14 +110,20 @@ ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee)
if (!emptyArgumentsShape)
return NULL;
ArgumentsData *data = (ArgumentsData *)
cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
unsigned numDeletedWords = NumWordsForBitArrayOfLength(argc);
unsigned numBytes = offsetof(ArgumentsData, slots) +
numDeletedWords * sizeof(size_t) +
argc * sizeof(Value);
ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes);
if (!data)
return NULL;
data->callee.init(ObjectValue(callee));
for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++)
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. */
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.
*/
if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
fp->forEachCanonicalActualArg(PutArg(cx->compartment, *argsobj));
else
argsobj->setStackFrame(fp);
@ -166,7 +174,7 @@ ArgumentsObject::createUnexpected(JSContext *cx, StackFrame *fp)
if (!argsobj)
return NULL;
fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
fp->forEachCanonicalActualArg(PutArg(cx->compartment, *argsobj));
return argsobj;
}
@ -176,8 +184,10 @@ args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
ArgumentsObject &argsobj = obj->asArguments();
if (JSID_IS_INT(id)) {
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength())
argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE));
if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
argsobj.setElement(arg, UndefinedValue());
argsobj.markElementDeleted(arg);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
argsobj.markLengthOverridden();
} 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.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
if (StackFrame *fp = argsobj.maybeStackFrame())
*vp = fp->canonicalActualArg(arg);
else
@ -212,7 +221,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = argsobj.callee();
if (!v.isMagic(JS_ARGS_HOLE))
if (!v.isMagic(JS_OVERWRITTEN_CALLEE))
*vp = v;
}
return true;
@ -267,7 +276,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_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;
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))
return true;
if (argsobj.callee().isMagic(JS_ARGS_HOLE))
if (argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE))
return true;
}
@ -295,17 +304,29 @@ NormalArgumentsObject::optimizedGetElem(JSContext *cx, StackFrame *fp, const Val
{
JS_ASSERT(!fp->hasArgsObj());
/* Fast path: no need to convert to id when elem is already an int in range. */
if (elem.isInt32()) {
int32_t i = elem.toInt32();
if (i >= 0 && uint32_t(i) < fp->numActualArgs()) {
*vp = fp->canonicalActualArg(elem.toInt32());
*vp = fp->canonicalActualArg(i);
return true;
}
}
/* Slow path: create and canonicalize an id, then emulate args_resolve. */
jsid id;
if (!ValueToId(cx, elem, &id))
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)) {
*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.
*/
unsigned arg = unsigned(JSID_TO_INT(id));
if (arg < argsobj.initialLength()) {
const Value &v = argsobj.element(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
*vp = argsobj.element(arg);
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!argsobj.hasOverriddenLength())
@ -418,7 +436,7 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObje
if (JSID_IS_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;
attrs |= JSPROP_ENUMERATE;

View File

@ -23,6 +23,7 @@
*
* Contributor(s):
* 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
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -43,30 +44,8 @@
#include "jsfun.h"
#ifdef JS_POLYIC
class GetPropCompiler;
#endif
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
* corresponding and that function itself. It is used to store arguments[i]
@ -82,6 +61,12 @@ struct ArgumentsData
*/
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
* 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 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. */
static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
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 initData(ArgumentsData *data);
static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
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. */
static bool create(JSContext *cx, StackFrame *fp);
@ -214,8 +188,25 @@ class ArgumentsObject : public JSObject
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 *elements() const;
inline void setElement(uint32_t i, const js::Value &v);
/* The stack frame for this ArgumentsObject, if the frame is still active. */

View File

@ -73,7 +73,6 @@
#include "nsIXBLService.h"
#include "nsCaret.h"
#include "nsPlainTextSerializer.h"
#include "mozSanitizingSerializer.h"
#include "nsXMLContentSerializer.h"
#include "nsXHTMLContentSerializer.h"
#include "nsRuleNode.h"
@ -514,7 +513,6 @@ MAKE_CTOR(CreateXMLContentSerializer, nsIContentSerializer, NS_NewXML
MAKE_CTOR(CreateHTMLContentSerializer, nsIContentSerializer, NS_NewHTMLContentSerializer)
MAKE_CTOR(CreateXHTMLContentSerializer, nsIContentSerializer, NS_NewXHTMLContentSerializer)
MAKE_CTOR(CreatePlainTextSerializer, nsIContentSerializer, NS_NewPlainTextSerializer)
MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer, NS_NewSanitizingHTMLSerializer)
MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService)
MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy)
#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_HTMLCONTENTSERIALIZER_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_SCRIPTABLEUNESCAPEHTML_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_XHTMLCONTENTSERIALIZER_CID, false, NULL, CreateXHTMLContentSerializer },
{ &kNS_PLAINTEXTSERIALIZER_CID, false, NULL, CreatePlainTextSerializer },
{ &kMOZ_SANITIZINGHTMLSERIALIZER_CID, false, NULL, CreateSanitizingHTMLSerializer },
{ &kNS_PARSERUTILS_CID, false, NULL, nsParserUtilsConstructor },
{ &kNS_SCRIPTABLEUNESCAPEHTML_CID, false, NULL, nsParserUtilsConstructor },
{ &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 "application/vnd.mozilla.xul+xml", &kNS_XMLCONTENTSERIALIZER_CID },
{ NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/plain", &kNS_PLAINTEXTSERIALIZER_CID },
{ MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID, &kMOZ_SANITIZINGHTMLSERIALIZER_CID },
{ NS_PARSERUTILS_CONTRACTID, &kNS_PARSERUTILS_CID },
{ NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID, &kNS_SCRIPTABLEUNESCAPEHTML_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.ViewSwitcher.ViewFactory;
public class BrowserToolbar extends LinearLayout {
private static final String LOGTAG = "GeckoToolbar";
public class BrowserToolbar {
private static final String LOGTAG = "GeckoToolbar";
private LinearLayout mLayout;
private Button mAwesomeBar;
private ImageButton mTabs;
public ImageButton mFavicon;
@ -90,16 +91,19 @@ public class BrowserToolbar extends LinearLayout {
private int mCount;
public BrowserToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
public BrowserToolbar(Context context) {
mContext = context;
}
public void from(LinearLayout layout) {
mLayout = layout;
mTitleCanExpand = true;
// Get the device's highlight color
TypedArray typedArray;
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 {
ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, android.R.style.TextAppearance);
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);
typedArray.recycle();
}
public void init() {
mAwesomeBar = (Button) findViewById(R.id.awesome_bar);
mAwesomeBar = (Button) mLayout.findViewById(R.id.awesome_bar);
mAwesomeBar.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
onAwesomeBarSearch();
}
});
Resources resources = getResources();
Resources resources = mContext.getResources();
mPadding = new int[] { mAwesomeBar.getPaddingLeft(),
mAwesomeBar.getPaddingTop(),
@ -132,7 +133,7 @@ public class BrowserToolbar extends LinearLayout {
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() {
public void onClick(View v) {
if (Tabs.getInstance().getCount() > 1)
@ -145,7 +146,7 @@ public class BrowserToolbar extends LinearLayout {
mCounterColor = 0xFFC7D1DB;
mTabsCount = (TextSwitcher) findViewById(R.id.tabs_count);
mTabsCount = (TextSwitcher) mLayout.findViewById(R.id.tabs_count);
mTabsCount.removeAllViews();
mTabsCount.setFactory(new ViewFactory() {
public View makeView() {
@ -169,18 +170,18 @@ public class BrowserToolbar extends LinearLayout {
mTabsCount.setText("0");
mCount = 0;
mFavicon = (ImageButton) findViewById(R.id.favicon);
mSiteSecurity = (ImageButton) findViewById(R.id.site_security);
mFavicon = (ImageButton) mLayout.findViewById(R.id.favicon);
mSiteSecurity = (ImageButton) mLayout.findViewById(R.id.site_security);
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() {
public void onClick(View v) {
doStop();
}
});
mShadow = (ImageView) findViewById(R.id.shadow);
mShadow = (ImageView) mLayout.findViewById(R.id.shadow);
mHandler = new Handler();
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() {
if (Build.VERSION.SDK_INT >= 11)
GeckoActionBar.show(GeckoApp.mAppContext);
else
setVisibility(View.VISIBLE);
mLayout.setVisibility(View.VISIBLE);
}
public void hide() {
if (Build.VERSION.SDK_INT >= 11)
GeckoActionBar.hide(GeckoApp.mAppContext);
else
setVisibility(View.GONE);
mLayout.setVisibility(View.GONE);
}
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
public void refreshActionBar() {
if (Build.VERSION.SDK_INT >= 11) {
mBrowserToolbar = (BrowserToolbar) getLayoutInflater().inflate(R.layout.browser_toolbar, null);
mBrowserToolbar.init();
LinearLayout actionBar = (LinearLayout) getLayoutInflater().inflate(R.layout.browser_toolbar, null);
mBrowserToolbar.from(actionBar);
mBrowserToolbar.refresh();
GeckoActionBar.setBackgroundDrawable(this, getResources().getDrawable(R.drawable.gecko_actionbar_bg));
GeckoActionBar.setDisplayOptions(this, ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM |
ActionBar.DISPLAY_SHOW_HOME |
ActionBar.DISPLAY_SHOW_TITLE |
ActionBar.DISPLAY_USE_LOGO);
GeckoActionBar.setCustomView(this, mBrowserToolbar);
GeckoActionBar.setCustomView(this, actionBar);
}
}
@ -1633,12 +1629,16 @@ abstract public class GeckoApp
setContentView(R.layout.gecko_app);
LinearLayout actionBar;
if (Build.VERSION.SDK_INT >= 11) {
mBrowserToolbar = (BrowserToolbar) GeckoActionBar.getCustomView(this);
actionBar = (LinearLayout) GeckoActionBar.getCustomView(this);
} 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
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (LinearLayout) findViewById(R.id.main_layout);
@ -1670,7 +1670,6 @@ abstract public class GeckoApp
checkAndLaunchUpdate();
}
mBrowserToolbar.init();
mBrowserToolbar.setTitle(mLastTitle);
String passedUri = null;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -117,6 +117,20 @@ nsHtml5TreeOpExecutor::WillParse()
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
NS_IMETHODIMP
nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
@ -143,7 +157,9 @@ nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
GetParser()->DropStreamParser();
// 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) {
// We never saw the body, and layout never got started. Force
@ -274,12 +290,12 @@ nsHtml5TreeOpExecutor::UpdateChildCounts()
// No-op
}
void
nsHtml5TreeOpExecutor::MarkAsBroken()
nsresult
nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mRunsToCompletion, "Fragment parsers can't be broken!");
mBroken = true;
mBroken = aReason;
if (mStreamParser) {
mStreamParser->Terminate();
}
@ -293,6 +309,7 @@ nsHtml5TreeOpExecutor::MarkAsBroken()
NS_WARNING("failed to dispatch executor flush event");
}
}
return aReason;
}
nsresult

View File

@ -129,14 +129,17 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
bool mCallContinueInterruptedParsingIfEnabled;
/**
* True if this parser should refuse to process any more input.
* Currently, the only way a parser can break is if it drops some input
* due to a memory allocation failure. In such a case, the whole parser
* needs to be marked as broken, because some input has been lost and
* parsing more input could lead to a DOM where pieces of HTML source
* Non-NS_OK if this parser should refuse to process any more input.
* For example, the parser needs to be marked as broken if it drops some
* input due to a memory allocation failure. In such a case, the whole
* parser needs to be marked as broken, because some input has been lost
* and parsing more input could lead to a DOM where pieces of HTML source
* 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:
@ -153,14 +156,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
/**
*
*/
NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
"Script global object not ready");
mDocument->AddObserver(this);
WillBuildModelImpl();
GetDocument()->BeginLoad();
return NS_OK;
}
NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
/**
* Emits EOF.
@ -263,13 +259,16 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
/**
* Marks this parser as broken and tells the stream parser (if any) to
* 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!");
return mBroken;
}

View File

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

View File

@ -4,12 +4,16 @@
#include "nsISupports.idl"
interface nsIDOMElement;
interface nsIDOMDocumentFragment;
interface nsIURI;
/**
* Non-Web HTML parser functionality to Firefox extensions and XULRunner apps.
* Don't use this from within Gecko--use nsContentUtils, nsTreeSanitizer, etc.
* directly instead.
*/
[scriptable, uuid(290f49bb-0619-4bda-8006-ab31bec7231a)]
[scriptable, uuid(a1101145-0025-411e-8873-fdf57bf28128)]
interface nsIParserUtils : nsISupports
{
@ -100,6 +104,22 @@ interface nsIParserUtils : nsISupports
AString convertToPlainText(in AString src,
in unsigned long flags,
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++

View File

@ -41,7 +41,7 @@ interface nsIDOMDocumentFragment;
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)]
interface nsIScriptableUnescapeHTML : nsISupports
@ -52,16 +52,20 @@ interface nsIScriptableUnescapeHTML : nsISupports
* nsIDocumentEncoder::OutputSelectionOnly |
* nsIDocumentEncoder::OutputAbsoluteLinks, 0).
*
* You should most likely call nsIParserUtils::convertToPlainText()
* instead of calling this method.
* You should call nsIParserUtils::convertToPlainText() instead of calling
* this method.
*
* @param src The HTML string to convert to plain text.
*/
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 isXML true if |fragment| is XML and false if HTML
* @param baseURI the base URL for this fragment

View File

@ -79,9 +79,9 @@ static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
NS_IMETHODIMP
nsParserUtils::ConvertToPlainText(const nsAString& aFromStr,
PRUint32 aFlags,
PRUint32 aWrapCol,
nsAString& aToStr)
PRUint32 aFlags,
PRUint32 aWrapCol,
nsAString& aToStr)
{
return nsContentUtils::ConvertToPlainText(aFromStr,
aToStr,
@ -142,15 +142,28 @@ nsParserUtils::Sanitize(const nsAString& aFromStr,
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
nsParserUtils::ParseFragment(const nsAString& aFragment,
bool aIsXML,
nsIURI* aBaseURI,
nsIDOMElement* aContextElement,
nsIDOMDocumentFragment** aReturn)
bool aIsXML,
nsIURI* aBaseURI,
nsIDOMElement* aContextElement,
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);
*aReturn = nsnull;
@ -239,7 +252,7 @@ nsParserUtils::ParseFragment(const nsAString& aFragment,
}
}
if (fragment) {
nsTreeSanitizer sanitizer;
nsTreeSanitizer sanitizer(aFlags);
sanitizer.Sanitize(fragment);
}
}

View File

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

View File

@ -149,12 +149,11 @@ var qaTools = {
return newArray;
},
writeSafeHTML : function(elementID, htmlstr) {
document.getElementById(elementID).innerHTML = ""; //clear it.
var gUnescapeHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"].getService(Components.interfaces.nsIScriptableUnescapeHTML);
document.getElementById(elementID).textContent = ""; //clear it.
var utils = Components.classes["@mozilla.org/parserutils;1"].getService(Components.interfaces.nsIParserUtils);
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);
},
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 ARRAY_CONTRACTID = "@mozilla.org/array;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;
@ -644,14 +644,16 @@ function TextConstruct() {
this.base = null;
this.type = "text";
this.text = null;
this.unescapeHTML = Cc[UNESCAPE_CONTRACTID].
getService(Ci.nsIScriptableUnescapeHTML);
this.parserUtils = Cc[PARSERUTILS_CONTRACTID].getService(Ci.nsIParserUtils);
}
TextConstruct.prototype = {
plainText: function TC_plainText() {
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;
},
@ -672,8 +674,8 @@ TextConstruct.prototype = {
else
return null;
return this.unescapeHTML.parseFragment(this.text, isXML,
this.base, element);
return this.parserUtils.parseFragment(this.text, 0, isXML,
this.base, element);
},
// XPCOM stuff

View File

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